From 0d2513f4cc207a5c8b20219a06ec7e3f797a18a9 Mon Sep 17 00:00:00 2001 From: Jake McGinty Date: Fri, 4 Sep 2015 17:33:22 -0700 Subject: [PATCH 1/8] Large attachment support // FREEBIE --- res/values/strings.xml | 1 + .../securesms/ConversationActivity.java | 102 +++++++----------- .../securesms/components/ThumbnailView.java | 12 ++- .../securesms/mms/AttachmentManager.java | 84 ++++++++++----- .../securesms/mms/AudioSlide.java | 9 +- .../thoughtcrime/securesms/mms/GifSlide.java | 13 +-- .../securesms/mms/ImageSlide.java | 9 +- .../securesms/mms/PushMediaConstraints.java | 6 +- src/org/thoughtcrime/securesms/mms/Slide.java | 19 ++-- .../securesms/mms/VideoSlide.java | 8 +- 10 files changed, 135 insertions(+), 128 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 8da04333f66..28220cceae2 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -126,6 +126,7 @@ Unblock? Are you sure you want to unblock this contact? Unblock + Attachment exceeds size limits for the type of message you\'re sending. Message details diff --git a/src/org/thoughtcrime/securesms/ConversationActivity.java b/src/org/thoughtcrime/securesms/ConversationActivity.java index 3b945074ffc..811b9859bbf 100644 --- a/src/org/thoughtcrime/securesms/ConversationActivity.java +++ b/src/org/thoughtcrime/securesms/ConversationActivity.java @@ -88,6 +88,7 @@ import org.thoughtcrime.securesms.mms.AttachmentTypeSelectorAdapter; import org.thoughtcrime.securesms.mms.MediaConstraints; import org.thoughtcrime.securesms.mms.MediaTooLargeException; +import org.thoughtcrime.securesms.mms.MediaType; import org.thoughtcrime.securesms.mms.MmsMediaConstraints; import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage; import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; @@ -113,6 +114,7 @@ import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.DynamicTheme; import org.thoughtcrime.securesms.util.GroupUtil; +import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.concurrent.ListenableFuture; @@ -288,13 +290,15 @@ public void onActivityResult(int reqCode, int resultCode, Intent data) { switch (reqCode) { case PICK_IMAGE: - addAttachmentImage(masterSecret, data.getData()); + addAttachment(data.getData(), MediaUtil.isGif(MediaUtil.getMimeType(this, data.getData())) + ? MediaType.GIF + : MediaType.IMAGE); break; case PICK_VIDEO: - addAttachmentVideo(data.getData()); + addAttachment(data.getData(), MediaType.VIDEO); break; case PICK_AUDIO: - addAttachmentAudio(data.getData()); + addAttachment(data.getData(), MediaType.AUDIO); break; case PICK_CONTACT_INFO: addAttachmentContactInfo(data.getData()); @@ -308,7 +312,7 @@ public void onActivityResult(int reqCode, int resultCode, Intent data) { break; case TAKE_PHOTO: if (attachmentManager.getCaptureUri() != null) { - addAttachmentImage(masterSecret, attachmentManager.getCaptureUri()); + addAttachment(attachmentManager.getCaptureUri(), MediaType.IMAGE); } break; } @@ -671,9 +675,10 @@ private void initializeDraft() { Uri draftVideo = getIntent().getParcelableExtra(DRAFT_VIDEO_EXTRA); if (draftText != null) composeText.setText(draftText); - if (draftImage != null) addAttachmentImage(masterSecret, draftImage); - if (draftAudio != null) addAttachmentAudio(draftAudio); - if (draftVideo != null) addAttachmentVideo(draftVideo); + + if (draftImage != null) addAttachment(draftImage, MediaType.IMAGE); + else if (draftAudio != null) addAttachment(draftAudio, MediaType.AUDIO); + else if (draftVideo != null) addAttachment(draftVideo, MediaType.VIDEO); if (draftText == null && draftImage == null && draftAudio == null && draftVideo == null) { initializeDraftFromDatabase(); @@ -707,11 +712,11 @@ protected void onPostExecute(List drafts) { if (draft.getType().equals(Draft.TEXT)) { composeText.setText(draft.getValue()); } else if (draft.getType().equals(Draft.IMAGE)) { - addAttachmentImage(masterSecret, Uri.parse(draft.getValue())); + addAttachment(Uri.parse(draft.getValue()), MediaType.IMAGE); } else if (draft.getType().equals(Draft.AUDIO)) { - addAttachmentAudio(Uri.parse(draft.getValue())); + addAttachment(Uri.parse(draft.getValue()), MediaType.AUDIO); } else if (draft.getType().equals(Draft.VIDEO)) { - addAttachmentVideo(Uri.parse(draft.getValue())); + addAttachment(Uri.parse(draft.getValue()), MediaType.VIDEO); } } @@ -917,55 +922,8 @@ private void addAttachment(int type) { } } - private void addAttachmentImage(MasterSecret masterSecret, Uri imageUri) { - try { - attachmentManager.setImage(masterSecret, imageUri); - } catch (IOException | BitmapDecodingException e) { - Log.w(TAG, e); - attachmentManager.clear(); - Toast.makeText(this, R.string.ConversationActivity_sorry_there_was_an_error_setting_your_attachment, - Toast.LENGTH_LONG).show(); - } catch (MediaTooLargeException e) { - attachmentManager.clear(); - Toast.makeText(this, getString(R.string.ConversationActivity_the_gif_you_selected_was_too_big), - Toast.LENGTH_LONG).show(); - Log.w(TAG, e); - } - } - - private void addAttachmentVideo(Uri videoUri) { - try { - attachmentManager.setVideo(videoUri); - } catch (IOException e) { - attachmentManager.clear(); - Toast.makeText(this, R.string.ConversationActivity_sorry_there_was_an_error_setting_your_attachment, - Toast.LENGTH_LONG).show(); - Log.w("ComposeMessageActivity", e); - } catch (MediaTooLargeException e) { - attachmentManager.clear(); - - Toast.makeText(this, getString(R.string.ConversationActivity_sorry_the_selected_video_exceeds_message_size_restrictions, - (MmsMediaConstraints.MAX_MESSAGE_SIZE/1024)), - Toast.LENGTH_LONG).show(); - Log.w("ComposeMessageActivity", e); - } - } - - private void addAttachmentAudio(Uri audioUri) { - try { - attachmentManager.setAudio(audioUri); - } catch (IOException e) { - attachmentManager.clear(); - Toast.makeText(this, R.string.ConversationActivity_sorry_there_was_an_error_setting_your_attachment, - Toast.LENGTH_LONG).show(); - Log.w("ComposeMessageActivity", e); - } catch (MediaTooLargeException e) { - attachmentManager.clear(); - Toast.makeText(this, getString(R.string.ConversationActivity_sorry_the_selected_audio_exceeds_message_size_restrictions, - (MmsMediaConstraints.MAX_MESSAGE_SIZE/1024)), - Toast.LENGTH_LONG).show(); - Log.w("ComposeMessageActivity", e); - } + private void addAttachment(Uri uri, MediaType mediaType) { + attachmentManager.setMedia(uri, mediaType, masterSecret); } private void addAttachmentContactInfo(Uri contactUri) { @@ -1198,8 +1156,14 @@ private void sendMediaMessage(final boolean forceSms) final Context context = getApplicationContext(); SlideDeck slideDeck; - if (attachmentManager.isAttachmentPresent()) slideDeck = new SlideDeck(attachmentManager.getSlideDeck()); - else slideDeck = new SlideDeck(); + if (attachmentManager.isAttachmentPresent()) { + if (!verifyAttachmentAllowed(attachmentManager.getSlideDeck().getThumbnailSlide())) { + return; + } + slideDeck = new SlideDeck(attachmentManager.getSlideDeck()); + } else { + slideDeck = new SlideDeck(); + } OutgoingMediaMessage outgoingMessage = new OutgoingMediaMessage(this, recipients, slideDeck, getMessage(), distributionType); @@ -1273,7 +1237,7 @@ public void onAttachmentDrawerStateChanged(DrawerState drawerState) { @Override public void onImageCapture(@NonNull final byte[] imageBytes) { attachmentManager.setCaptureUri(CaptureProvider.getInstance(this).create(masterSecret, recipients, imageBytes)); - addAttachmentImage(masterSecret, attachmentManager.getCaptureUri()); + addAttachment(attachmentManager.getCaptureUri(), MediaType.IMAGE); quickAttachmentDrawer.hide(false); } @@ -1397,4 +1361,18 @@ public void onAttachmentChanged() { initializeSecurity(); updateToggleButtonState(); } + + @Override + public boolean verifyAttachmentAllowed(final Slide slide) { + MediaConstraints constraints = sendButton.getSelectedTransport().getType() == Type.TEXTSECURE + ? MediaConstraints.PUSH_CONSTRAINTS + : MediaConstraints.MMS_CONSTRAINTS; + + if (!constraints.isSatisfied(this, masterSecret, slide.getPart()) && !constraints.canResize(slide.getPart())) { + Toast.makeText(this, R.string.ConversationActivity_attachment_exceeds_size_limits, Toast.LENGTH_LONG).show(); + return false; + } else { + return true; + } + } } diff --git a/src/org/thoughtcrime/securesms/components/ThumbnailView.java b/src/org/thoughtcrime/securesms/components/ThumbnailView.java index 07b0cc4cd1f..7e89e540423 100644 --- a/src/org/thoughtcrime/securesms/components/ThumbnailView.java +++ b/src/org/thoughtcrime/securesms/components/ThumbnailView.java @@ -205,7 +205,12 @@ public void setDownloadClickListener(ThumbnailClickListener listener) { } public void clear() { - if (isContextValid()) Glide.clear(this); + if (isContextValid()) Glide.clear(image); + if (slideDeckFuture != null) slideDeckFuture.removeListener(slideDeckListener); + slide = null; + slideId = null; + slideDeckFuture = null; + slideDeckListener = null; } public void hideControls(boolean hideControls) { @@ -213,6 +218,11 @@ public void hideControls(boolean hideControls) { if (hideControls) hideProgressWheel(); } + public void showProgressSpinner() { + getProgressWheel().spin(); + getProgressWheel().setVisibility(VISIBLE); + } + @TargetApi(VERSION_CODES.JELLY_BEAN_MR1) private boolean isContextValid() { return !(getContext() instanceof Activity) || diff --git a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java index bb0168b9113..875a36d22d6 100644 --- a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java +++ b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java @@ -21,9 +21,11 @@ import android.content.Context; import android.content.Intent; import android.net.Uri; +import android.os.AsyncTask; import android.os.Build; import android.provider.ContactsContract; import android.provider.MediaStore; +import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.util.Log; import android.view.View; @@ -36,8 +38,6 @@ import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.providers.CaptureProvider; import org.thoughtcrime.securesms.recipients.Recipients; -import org.thoughtcrime.securesms.util.BitmapDecodingException; -import org.thoughtcrime.securesms.util.MediaUtil; import java.io.IOException; @@ -83,34 +83,45 @@ public void cleanup() { captureUri = null; } - public void setImage(MasterSecret masterSecret, Uri image) - throws IOException, BitmapDecodingException, MediaTooLargeException + public void setMedia(@NonNull final Uri uri, + @NonNull final MediaType mediaType, + @NonNull final MasterSecret masterSecret) { - if (MediaUtil.isGif(MediaUtil.getMimeType(context, image))) { - setMedia(new GifSlide(context, masterSecret, image), masterSecret); - } else { - setMedia(new ImageSlide(context, masterSecret, image), masterSecret); - } - } - - public void setVideo(Uri video) throws IOException, MediaTooLargeException { - setMedia(new VideoSlide(context, video)); - } - - public void setAudio(Uri audio) throws IOException, MediaTooLargeException { - setMedia(new AudioSlide(context, audio)); - } + new AsyncTask() { + @Override protected void onPreExecute() { + slideDeck.clear(); + thumbnail.clear(); + cleanup(); + thumbnail.showProgressSpinner(); + attachmentView.setVisibility(View.VISIBLE); + } - public void setMedia(final Slide slide) { - setMedia(slide, null); - } + @Override protected @Nullable Slide doInBackground(Void... params) { + long start = System.currentTimeMillis(); + try { + final Slide slide = mediaType.createSlide(context, masterSecret, uri); + Log.w(TAG, "slide with size " + slide.getPart().getDataSize() + " took " + (System.currentTimeMillis() - start) + "ms"); + return slide; + } catch (IOException ioe) { + Log.w(TAG, ioe); + return null; + } + } - public void setMedia(final Slide slide, @Nullable MasterSecret masterSecret) { - slideDeck.clear(); - slideDeck.addSlide(slide); - attachmentView.setVisibility(View.VISIBLE); - thumbnail.setImageResource(slide, masterSecret); - attachmentListener.onAttachmentChanged(); + @Override protected void onPostExecute(@Nullable final Slide slide) { + if (slide != null && attachmentListener.verifyAttachmentAllowed(slide)) { + slideDeck.addSlide(slide); + attachmentView.setVisibility(View.VISIBLE); + thumbnail.setImageResource(slide, masterSecret); + attachmentListener.onAttachmentChanged(); + } else { + attachmentView.setVisibility(View.GONE); + Toast.makeText(context, + R.string.ConversationActivity_sorry_there_was_an_error_setting_your_attachment, + Toast.LENGTH_SHORT).show(); + } + } + }.execute(); } public boolean isAttachmentPresent() { @@ -193,5 +204,24 @@ public void onClick(View v) { public interface AttachmentListener { void onAttachmentChanged(); + boolean verifyAttachmentAllowed(Slide slide); + } + + public enum MediaType { + IMAGE, GIF, AUDIO, VIDEO; + + public @NonNull Slide createSlide(@NonNull Context context, + @NonNull MasterSecret masterSecret, + @NonNull Uri uri) + throws IOException + { + switch (this) { + case IMAGE: return new ImageSlide(context, masterSecret, uri); + case GIF: return new GifSlide(context, masterSecret, uri); + case AUDIO: return new AudioSlide(context, uri); + case VIDEO: return new VideoSlide(context, uri); + default: throw new AssertionError("unrecognized enum"); + } + } } } diff --git a/src/org/thoughtcrime/securesms/mms/AudioSlide.java b/src/org/thoughtcrime/securesms/mms/AudioSlide.java index 68bc123bc2b..7be0c08e962 100644 --- a/src/org/thoughtcrime/securesms/mms/AudioSlide.java +++ b/src/org/thoughtcrime/securesms/mms/AudioSlide.java @@ -33,7 +33,7 @@ public class AudioSlide extends Slide { - public AudioSlide(Context context, Uri uri) throws IOException, MediaTooLargeException { + public AudioSlide(Context context, Uri uri) throws IOException { super(context, constructPartFromUri(context, uri)); } @@ -56,11 +56,9 @@ public boolean hasAudio() { return ResUtil.getDrawableRes(theme, R.attr.conversation_icon_attach_audio); } - public static PduPart constructPartFromUri(Context context, Uri uri) throws IOException, MediaTooLargeException { + public static PduPart constructPartFromUri(Context context, Uri uri) throws IOException { PduPart part = new PduPart(); - assertMediaSize(context, uri, MmsMediaConstraints.MAX_MESSAGE_SIZE); - Cursor cursor = null; try { @@ -73,8 +71,9 @@ public static PduPart constructPartFromUri(Context context, Uri uri) throws IOEx } finally { if (cursor != null) cursor.close(); - } + } + part.setDataSize(getMediaSize(context, null, uri)); part.setDataUri(uri); part.setContentId((System.currentTimeMillis()+"").getBytes()); part.setName(("Audio" + System.currentTimeMillis()).getBytes()); diff --git a/src/org/thoughtcrime/securesms/mms/GifSlide.java b/src/org/thoughtcrime/securesms/mms/GifSlide.java index 4c87c257424..7483b6a798b 100644 --- a/src/org/thoughtcrime/securesms/mms/GifSlide.java +++ b/src/org/thoughtcrime/securesms/mms/GifSlide.java @@ -16,19 +16,8 @@ public GifSlide(Context context, MasterSecret masterSecret, PduPart part) { super(context, masterSecret, part); } - public GifSlide(Context context, MasterSecret masterSecret, Uri uri) - throws IOException, BitmapDecodingException, MediaTooLargeException - { + public GifSlide(Context context, MasterSecret masterSecret, Uri uri) throws IOException { super(context, masterSecret, uri); - assertMediaSize(); - } - - private void assertMediaSize() throws MediaTooLargeException, IOException { - // TODO move assertion outside of slides and take available transport options into account - assertMediaSize(context, getPart().getDataUri(), MediaConstraints.PUSH_CONSTRAINTS.getGifMaxSize()); - if (!MediaConstraints.PUSH_CONSTRAINTS.isSatisfied(context, masterSecret, part)) { - throw new MediaTooLargeException("Media exceeds maximum message size."); - } } @Override public Uri getThumbnailUri() { diff --git a/src/org/thoughtcrime/securesms/mms/ImageSlide.java b/src/org/thoughtcrime/securesms/mms/ImageSlide.java index 87131f2f3b3..ea8c9d08a8e 100644 --- a/src/org/thoughtcrime/securesms/mms/ImageSlide.java +++ b/src/org/thoughtcrime/securesms/mms/ImageSlide.java @@ -38,8 +38,8 @@ public ImageSlide(Context context, MasterSecret masterSecret, PduPart part) { super(context, masterSecret, part); } - public ImageSlide(Context context, MasterSecret masterSecret, Uri uri) throws IOException, BitmapDecodingException { - super(context, masterSecret, constructPartFromUri(context, uri)); + public ImageSlide(Context context, MasterSecret masterSecret, Uri uri) throws IOException { + super(context, masterSecret, constructPartFromUri(context, masterSecret, uri)); } @Override @@ -63,13 +63,12 @@ public boolean hasImage() { return true; } - private static PduPart constructPartFromUri(Context context, Uri uri) - throws IOException, BitmapDecodingException - { + private static PduPart constructPartFromUri(Context context, MasterSecret masterSecret, Uri uri) throws IOException { PduPart part = new PduPart(); final String mimeType = MediaUtil.getMimeType(context, uri); + part.setDataSize(getMediaSize(context, masterSecret, uri)); part.setDataUri(uri); part.setContentType((mimeType != null ? mimeType : ContentType.IMAGE_JPEG).getBytes()); part.setContentId((System.currentTimeMillis()+"").getBytes()); diff --git a/src/org/thoughtcrime/securesms/mms/PushMediaConstraints.java b/src/org/thoughtcrime/securesms/mms/PushMediaConstraints.java index f59ece7865e..b3b88e27fd4 100644 --- a/src/org/thoughtcrime/securesms/mms/PushMediaConstraints.java +++ b/src/org/thoughtcrime/securesms/mms/PushMediaConstraints.java @@ -27,16 +27,16 @@ public int getImageMaxSize() { @Override public int getGifMaxSize() { - return 1 * MB; + return 3 * MB; } @Override public int getVideoMaxSize() { - return MmsMediaConstraints.MAX_MESSAGE_SIZE; + return 50 * MB; } @Override public int getAudioMaxSize() { - return MmsMediaConstraints.MAX_MESSAGE_SIZE; + return 100 * MB; } } diff --git a/src/org/thoughtcrime/securesms/mms/Slide.java b/src/org/thoughtcrime/securesms/mms/Slide.java index 7f528852926..be7988d3ad0 100644 --- a/src/org/thoughtcrime/securesms/mms/Slide.java +++ b/src/org/thoughtcrime/securesms/mms/Slide.java @@ -90,18 +90,21 @@ public boolean isDraft() { return !getPart().getPartId().isValid(); } - protected static void assertMediaSize(Context context, Uri uri, long max) - throws MediaTooLargeException, IOException - { - InputStream in = context.getContentResolver().openInputStream(uri); - long size = 0; - byte[] buffer = new byte[512]; - int read; + protected static long getMediaSize(Context context, MasterSecret masterSecret, Uri uri) throws IOException { + InputStream in = PartAuthority.getPartStream(context, masterSecret, uri); + if (in == null) throw new IOException("Couldn't obtain input stream."); + + long size = 0; + byte[] buffer = new byte[512]; + int read; while ((read = in.read(buffer)) != -1) { size += read; - if (size > max) throw new MediaTooLargeException("Media exceeds maximum message size."); +// if (size >= limit) break; } + in.close(); + + return size; } @Override diff --git a/src/org/thoughtcrime/securesms/mms/VideoSlide.java b/src/org/thoughtcrime/securesms/mms/VideoSlide.java index d8f4d9b61a0..e32e56d038b 100644 --- a/src/org/thoughtcrime/securesms/mms/VideoSlide.java +++ b/src/org/thoughtcrime/securesms/mms/VideoSlide.java @@ -35,7 +35,7 @@ public class VideoSlide extends Slide { - public VideoSlide(Context context, Uri uri) throws IOException, MediaTooLargeException { + public VideoSlide(Context context, Uri uri) throws IOException { super(context, constructPartFromUri(context, uri)); } @@ -58,9 +58,7 @@ public boolean hasVideo() { return true; } - private static PduPart constructPartFromUri(Context context, Uri uri) - throws IOException, MediaTooLargeException - { + private static PduPart constructPartFromUri(Context context, Uri uri) throws IOException { PduPart part = new PduPart(); ContentResolver resolver = context.getContentResolver(); Cursor cursor = null; @@ -76,7 +74,7 @@ private static PduPart constructPartFromUri(Context context, Uri uri) cursor.close(); } - assertMediaSize(context, uri, MmsMediaConstraints.MAX_MESSAGE_SIZE); + part.setDataSize(getMediaSize(context, null, uri)); part.setDataUri(uri); part.setContentId((System.currentTimeMillis()+"").getBytes()); part.setName(("Video" + System.currentTimeMillis()).getBytes()); From c12191b3e0ec4017747eb8e2874f66db1e8f9137 Mon Sep 17 00:00:00 2001 From: Jake McGinty Date: Fri, 4 Sep 2015 17:54:44 -0700 Subject: [PATCH 2/8] simplify PduPart logic // FREEBIE --- .../securesms/ConversationActivity.java | 2 +- .../securesms/mms/AttachmentManager.java | 13 +++++---- .../securesms/mms/AudioSlide.java | 28 ++----------------- .../securesms/mms/ImageSlide.java | 17 +---------- .../securesms/mms/PushMediaConstraints.java | 2 +- src/org/thoughtcrime/securesms/mms/Slide.java | 25 ++++++++++++++--- .../securesms/mms/VideoSlide.java | 27 ++---------------- .../securesms/util/MediaUtil.java | 6 +++- 8 files changed, 40 insertions(+), 80 deletions(-) diff --git a/src/org/thoughtcrime/securesms/ConversationActivity.java b/src/org/thoughtcrime/securesms/ConversationActivity.java index 811b9859bbf..34a1f65c2eb 100644 --- a/src/org/thoughtcrime/securesms/ConversationActivity.java +++ b/src/org/thoughtcrime/securesms/ConversationActivity.java @@ -85,10 +85,10 @@ import org.thoughtcrime.securesms.database.MmsSmsColumns.Types; import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.mms.AttachmentManager; +import org.thoughtcrime.securesms.mms.AttachmentManager.MediaType; import org.thoughtcrime.securesms.mms.AttachmentTypeSelectorAdapter; import org.thoughtcrime.securesms.mms.MediaConstraints; import org.thoughtcrime.securesms.mms.MediaTooLargeException; -import org.thoughtcrime.securesms.mms.MediaType; import org.thoughtcrime.securesms.mms.MmsMediaConstraints; import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage; import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; diff --git a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java index 875a36d22d6..cffe6709de1 100644 --- a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java +++ b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java @@ -70,6 +70,7 @@ public void clear() { @Override public void onAnimationRepeat(Animation animation) {} @Override public void onAnimationEnd(Animation animation) { slideDeck.clear(); + thumbnail.clear(); attachmentView.setVisibility(View.GONE); attachmentListener.onAttachmentChanged(); } @@ -109,16 +110,16 @@ public void setMedia(@NonNull final Uri uri, } @Override protected void onPostExecute(@Nullable final Slide slide) { - if (slide != null && attachmentListener.verifyAttachmentAllowed(slide)) { - slideDeck.addSlide(slide); - attachmentView.setVisibility(View.VISIBLE); - thumbnail.setImageResource(slide, masterSecret); - attachmentListener.onAttachmentChanged(); - } else { + if (slide == null) { attachmentView.setVisibility(View.GONE); Toast.makeText(context, R.string.ConversationActivity_sorry_there_was_an_error_setting_your_attachment, Toast.LENGTH_SHORT).show(); + } else if (attachmentListener.verifyAttachmentAllowed(slide)) { + slideDeck.addSlide(slide); + attachmentView.setVisibility(View.VISIBLE); + thumbnail.setImageResource(slide, masterSecret); + attachmentListener.onAttachmentChanged(); } } }.execute(); diff --git a/src/org/thoughtcrime/securesms/mms/AudioSlide.java b/src/org/thoughtcrime/securesms/mms/AudioSlide.java index 7be0c08e962..e7f3c5930dc 100644 --- a/src/org/thoughtcrime/securesms/mms/AudioSlide.java +++ b/src/org/thoughtcrime/securesms/mms/AudioSlide.java @@ -29,12 +29,13 @@ import java.io.IOException; +import ws.com.google.android.mms.ContentType; import ws.com.google.android.mms.pdu.PduPart; public class AudioSlide extends Slide { public AudioSlide(Context context, Uri uri) throws IOException { - super(context, constructPartFromUri(context, uri)); + super(context, constructPartFromUri(context, null, uri, ContentType.AUDIO_UNSPECIFIED)); } public AudioSlide(Context context, MasterSecret masterSecret, PduPart part) { @@ -55,29 +56,4 @@ public boolean hasAudio() { public @DrawableRes int getPlaceholderRes(Theme theme) { return ResUtil.getDrawableRes(theme, R.attr.conversation_icon_attach_audio); } - - public static PduPart constructPartFromUri(Context context, Uri uri) throws IOException { - PduPart part = new PduPart(); - - Cursor cursor = null; - - try { - cursor = context.getContentResolver().query(uri, new String[]{Audio.Media.MIME_TYPE}, null, null, null); - - if (cursor != null && cursor.moveToFirst()) - part.setContentType(cursor.getString(0).getBytes()); - else - throw new IOException("Unable to query content type."); - } finally { - if (cursor != null) - cursor.close(); - } - - part.setDataSize(getMediaSize(context, null, uri)); - part.setDataUri(uri); - part.setContentId((System.currentTimeMillis()+"").getBytes()); - part.setName(("Audio" + System.currentTimeMillis()).getBytes()); - - return part; - } } diff --git a/src/org/thoughtcrime/securesms/mms/ImageSlide.java b/src/org/thoughtcrime/securesms/mms/ImageSlide.java index ea8c9d08a8e..65c76ce2a07 100644 --- a/src/org/thoughtcrime/securesms/mms/ImageSlide.java +++ b/src/org/thoughtcrime/securesms/mms/ImageSlide.java @@ -39,7 +39,7 @@ public ImageSlide(Context context, MasterSecret masterSecret, PduPart part) { } public ImageSlide(Context context, MasterSecret masterSecret, Uri uri) throws IOException { - super(context, masterSecret, constructPartFromUri(context, masterSecret, uri)); + super(context, masterSecret, constructPartFromUri(context, masterSecret, uri, ContentType.IMAGE_JPEG)); } @Override @@ -62,19 +62,4 @@ public Uri getThumbnailUri() { public boolean hasImage() { return true; } - - private static PduPart constructPartFromUri(Context context, MasterSecret masterSecret, Uri uri) throws IOException { - PduPart part = new PduPart(); - - final String mimeType = MediaUtil.getMimeType(context, uri); - - part.setDataSize(getMediaSize(context, masterSecret, uri)); - part.setDataUri(uri); - part.setContentType((mimeType != null ? mimeType : ContentType.IMAGE_JPEG).getBytes()); - part.setContentId((System.currentTimeMillis()+"").getBytes()); - part.setName(("Image" + System.currentTimeMillis()).getBytes()); - - return part; - } - } diff --git a/src/org/thoughtcrime/securesms/mms/PushMediaConstraints.java b/src/org/thoughtcrime/securesms/mms/PushMediaConstraints.java index b3b88e27fd4..794759d7f30 100644 --- a/src/org/thoughtcrime/securesms/mms/PushMediaConstraints.java +++ b/src/org/thoughtcrime/securesms/mms/PushMediaConstraints.java @@ -27,7 +27,7 @@ public int getImageMaxSize() { @Override public int getGifMaxSize() { - return 3 * MB; + return 5 * MB; } @Override diff --git a/src/org/thoughtcrime/securesms/mms/Slide.java b/src/org/thoughtcrime/securesms/mms/Slide.java index be7988d3ad0..46105602db8 100644 --- a/src/org/thoughtcrime/securesms/mms/Slide.java +++ b/src/org/thoughtcrime/securesms/mms/Slide.java @@ -21,8 +21,10 @@ import android.net.Uri; import android.support.annotation.DrawableRes; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import org.thoughtcrime.securesms.crypto.MasterSecret; +import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.Util; import java.io.IOException; @@ -100,13 +102,31 @@ protected static long getMediaSize(Context context, MasterSecret masterSecret, U while ((read = in.read(buffer)) != -1) { size += read; -// if (size >= limit) break; } in.close(); return size; } + protected static PduPart constructPartFromUri(@NonNull Context context, + @Nullable MasterSecret masterSecret, + @NonNull Uri uri, + @NonNull String defaultMime) + throws IOException + { + final PduPart part = new PduPart(); + final String mimeType = MediaUtil.getMimeType(context, uri); + final String derivedMimeType = mimeType != null ? mimeType : defaultMime; + + part.setDataSize(getMediaSize(context, masterSecret, uri)); + part.setDataUri(uri); + part.setContentType(derivedMimeType.getBytes()); + part.setContentId((System.currentTimeMillis()+"").getBytes()); + part.setName((MediaUtil.getDiscreteMimeType(derivedMimeType) + System.currentTimeMillis()).getBytes()); + + return part; + } + @Override public boolean equals(Object other) { if (!(other instanceof Slide)) return false; @@ -128,7 +148,4 @@ public int hashCode() { return Util.hashCode(getContentType(), hasAudio(), hasImage(), hasVideo(), isDraft(), getUri(), getThumbnailUri(), getTransferProgress()); } - - - } diff --git a/src/org/thoughtcrime/securesms/mms/VideoSlide.java b/src/org/thoughtcrime/securesms/mms/VideoSlide.java index e32e56d038b..947a3f92ab1 100644 --- a/src/org/thoughtcrime/securesms/mms/VideoSlide.java +++ b/src/org/thoughtcrime/securesms/mms/VideoSlide.java @@ -31,12 +31,13 @@ import java.io.IOException; +import ws.com.google.android.mms.ContentType; import ws.com.google.android.mms.pdu.PduPart; public class VideoSlide extends Slide { public VideoSlide(Context context, Uri uri) throws IOException { - super(context, constructPartFromUri(context, uri)); + super(context, constructPartFromUri(context, null, uri, ContentType.VIDEO_UNSPECIFIED)); } public VideoSlide(Context context, MasterSecret masterSecret, PduPart part) { @@ -57,28 +58,4 @@ public boolean hasImage() { public boolean hasVideo() { return true; } - - private static PduPart constructPartFromUri(Context context, Uri uri) throws IOException { - PduPart part = new PduPart(); - ContentResolver resolver = context.getContentResolver(); - Cursor cursor = null; - - try { - cursor = resolver.query(uri, new String[] {MediaStore.Video.Media.MIME_TYPE}, null, null, null); - if (cursor != null && cursor.moveToFirst()) { - Log.w("VideoSlide", "Setting mime type: " + cursor.getString(0)); - part.setContentType(cursor.getString(0).getBytes()); - } - } finally { - if (cursor != null) - cursor.close(); - } - - part.setDataSize(getMediaSize(context, null, uri)); - part.setDataUri(uri); - part.setContentId((System.currentTimeMillis()+"").getBytes()); - part.setName(("Video" + System.currentTimeMillis()).getBytes()); - - return part; - } } diff --git a/src/org/thoughtcrime/securesms/util/MediaUtil.java b/src/org/thoughtcrime/securesms/util/MediaUtil.java index b04d4f522eb..abbc9351746 100644 --- a/src/org/thoughtcrime/securesms/util/MediaUtil.java +++ b/src/org/thoughtcrime/securesms/util/MediaUtil.java @@ -111,7 +111,11 @@ public static boolean isVideo(PduPart part) { } public static @Nullable String getDiscreteMimeType(@NonNull PduPart part) { - final String[] sections = (Util.toIsoString(part.getContentType()).split("/", 2)); + return getDiscreteMimeType(Util.toIsoString(part.getContentType())); + } + + public static @Nullable String getDiscreteMimeType(@NonNull String mimeType) { + final String[] sections = mimeType.split("/", 2); return sections.length > 1 ? sections[0] : null; } From b0b3a3002d0f8e215c4a7d4c0c2afffa41fbc083 Mon Sep 17 00:00:00 2001 From: Jake McGinty Date: Fri, 4 Sep 2015 18:20:14 -0700 Subject: [PATCH 3/8] address comments round 0 --- .../securesms/ConversationActivity.java | 16 +++++++--------- .../securesms/mms/AttachmentManager.java | 17 +++++++++++------ 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/org/thoughtcrime/securesms/ConversationActivity.java b/src/org/thoughtcrime/securesms/ConversationActivity.java index 34a1f65c2eb..c894150900c 100644 --- a/src/org/thoughtcrime/securesms/ConversationActivity.java +++ b/src/org/thoughtcrime/securesms/ConversationActivity.java @@ -923,7 +923,7 @@ private void addAttachment(int type) { } private void addAttachment(Uri uri, MediaType mediaType) { - attachmentManager.setMedia(uri, mediaType, masterSecret); + attachmentManager.setMedia(masterSecret, uri, mediaType); } private void addAttachmentContactInfo(Uri contactUri) { @@ -1157,7 +1157,10 @@ private void sendMediaMessage(final boolean forceSms) SlideDeck slideDeck; if (attachmentManager.isAttachmentPresent()) { - if (!verifyAttachmentAllowed(attachmentManager.getSlideDeck().getThumbnailSlide())) { + if (!isAttachmentAllowed(attachmentManager.getSlideDeck().getThumbnailSlide())) { + Toast.makeText(context, + R.string.ConversationActivity_attachment_exceeds_size_limits, + Toast.LENGTH_SHORT).show(); return; } slideDeck = new SlideDeck(attachmentManager.getSlideDeck()); @@ -1363,16 +1366,11 @@ public void onAttachmentChanged() { } @Override - public boolean verifyAttachmentAllowed(final Slide slide) { + public boolean isAttachmentAllowed(final Slide slide) { MediaConstraints constraints = sendButton.getSelectedTransport().getType() == Type.TEXTSECURE ? MediaConstraints.PUSH_CONSTRAINTS : MediaConstraints.MMS_CONSTRAINTS; - if (!constraints.isSatisfied(this, masterSecret, slide.getPart()) && !constraints.canResize(slide.getPart())) { - Toast.makeText(this, R.string.ConversationActivity_attachment_exceeds_size_limits, Toast.LENGTH_LONG).show(); - return false; - } else { - return true; - } + return constraints.isSatisfied(this, masterSecret, slide.getPart()) || constraints.canResize(slide.getPart()); } } diff --git a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java index cffe6709de1..bc471b53225 100644 --- a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java +++ b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java @@ -84,9 +84,9 @@ public void cleanup() { captureUri = null; } - public void setMedia(@NonNull final Uri uri, - @NonNull final MediaType mediaType, - @NonNull final MasterSecret masterSecret) + public void setMedia(@NonNull final MasterSecret masterSecret, + @NonNull final Uri uri, + @NonNull final MediaType mediaType) { new AsyncTask() { @Override protected void onPreExecute() { @@ -115,11 +115,16 @@ public void setMedia(@NonNull final Uri uri, Toast.makeText(context, R.string.ConversationActivity_sorry_there_was_an_error_setting_your_attachment, Toast.LENGTH_SHORT).show(); - } else if (attachmentListener.verifyAttachmentAllowed(slide)) { + } else if (attachmentListener.isAttachmentAllowed(slide)) { slideDeck.addSlide(slide); attachmentView.setVisibility(View.VISIBLE); thumbnail.setImageResource(slide, masterSecret); attachmentListener.onAttachmentChanged(); + } else { + attachmentView.setVisibility(View.GONE); + Toast.makeText(context, + R.string.ConversationActivity_attachment_exceeds_size_limits, + Toast.LENGTH_SHORT).show(); } } }.execute(); @@ -204,8 +209,8 @@ public void onClick(View v) { } public interface AttachmentListener { - void onAttachmentChanged(); - boolean verifyAttachmentAllowed(Slide slide); + void onAttachmentChanged(); + boolean isAttachmentAllowed(Slide slide); } public enum MediaType { From ddcc5bcd65721b597622d040195195691290dffa Mon Sep 17 00:00:00 2001 From: Jake McGinty Date: Fri, 4 Sep 2015 18:46:04 -0700 Subject: [PATCH 4/8] one-way listeners --- .../securesms/ConversationActivity.java | 18 ++++--- .../securesms/mms/AttachmentManager.java | 47 +++++++++++++------ 2 files changed, 41 insertions(+), 24 deletions(-) diff --git a/src/org/thoughtcrime/securesms/ConversationActivity.java b/src/org/thoughtcrime/securesms/ConversationActivity.java index c894150900c..2ce4cc45bcd 100644 --- a/src/org/thoughtcrime/securesms/ConversationActivity.java +++ b/src/org/thoughtcrime/securesms/ConversationActivity.java @@ -923,7 +923,7 @@ private void addAttachment(int type) { } private void addAttachment(Uri uri, MediaType mediaType) { - attachmentManager.setMedia(masterSecret, uri, mediaType); + attachmentManager.setMedia(masterSecret, uri, mediaType, getCurrentConstraints()); } private void addAttachmentContactInfo(Uri contactUri) { @@ -1090,6 +1090,12 @@ private String getMessage() throws InvalidMessageException { return rawText; } + private MediaConstraints getCurrentConstraints() { + return sendButton.getSelectedTransport().getType() == Type.TEXTSECURE + ? MediaConstraints.PUSH_CONSTRAINTS + : MediaConstraints.MMS_CONSTRAINTS; + } + private void markThreadAsRead() { new AsyncTask() { @Override @@ -1157,7 +1163,7 @@ private void sendMediaMessage(final boolean forceSms) SlideDeck slideDeck; if (attachmentManager.isAttachmentPresent()) { - if (!isAttachmentAllowed(attachmentManager.getSlideDeck().getThumbnailSlide())) { + if (!attachmentManager.areConstraintsSatisfied(this, masterSecret, getCurrentConstraints())) { Toast.makeText(context, R.string.ConversationActivity_attachment_exceeds_size_limits, Toast.LENGTH_SHORT).show(); @@ -1365,12 +1371,4 @@ public void onAttachmentChanged() { updateToggleButtonState(); } - @Override - public boolean isAttachmentAllowed(final Slide slide) { - MediaConstraints constraints = sendButton.getSelectedTransport().getType() == Type.TEXTSECURE - ? MediaConstraints.PUSH_CONSTRAINTS - : MediaConstraints.MMS_CONSTRAINTS; - - return constraints.isSatisfied(this, masterSecret, slide.getPart()) || constraints.canResize(slide.getPart()); - } } diff --git a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java index bc471b53225..a7e173c6ba1 100644 --- a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java +++ b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java @@ -53,10 +53,10 @@ public class AttachmentManager { private Uri captureUri; public AttachmentManager(Activity view, AttachmentListener listener) { - this.attachmentView = view.findViewById(R.id.attachment_editor); - this.thumbnail = (ThumbnailView)view.findViewById(R.id.attachment_thumbnail); - this.slideDeck = new SlideDeck(); - this.context = view; + this.attachmentView = view.findViewById(R.id.attachment_editor); + this.thumbnail = (ThumbnailView)view.findViewById(R.id.attachment_thumbnail); + this.slideDeck = new SlideDeck(); + this.context = view; this.attachmentListener = listener; thumbnail.setRemoveClickListener(new RemoveButtonListener()); @@ -67,7 +67,9 @@ public void clear() { animation.setDuration(200); animation.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) {} + @Override public void onAnimationRepeat(Animation animation) {} + @Override public void onAnimationEnd(Animation animation) { slideDeck.clear(); thumbnail.clear(); @@ -85,8 +87,9 @@ public void cleanup() { } public void setMedia(@NonNull final MasterSecret masterSecret, - @NonNull final Uri uri, - @NonNull final MediaType mediaType) + @NonNull final Uri uri, + @NonNull final MediaType mediaType, + @NonNull final MediaConstraints constraints) { new AsyncTask() { @Override protected void onPreExecute() { @@ -115,16 +118,16 @@ public void setMedia(@NonNull final MasterSecret masterSecret, Toast.makeText(context, R.string.ConversationActivity_sorry_there_was_an_error_setting_your_attachment, Toast.LENGTH_SHORT).show(); - } else if (attachmentListener.isAttachmentAllowed(slide)) { - slideDeck.addSlide(slide); - attachmentView.setVisibility(View.VISIBLE); - thumbnail.setImageResource(slide, masterSecret); - attachmentListener.onAttachmentChanged(); - } else { + } else if (!areConstraintsSatisfied(context, masterSecret, slide, constraints)) { attachmentView.setVisibility(View.GONE); Toast.makeText(context, R.string.ConversationActivity_attachment_exceeds_size_limits, Toast.LENGTH_SHORT).show(); + } else { + slideDeck.addSlide(slide); + attachmentView.setVisibility(View.VISIBLE); + thumbnail.setImageResource(slide, masterSecret); + attachmentListener.onAttachmentChanged(); } } }.execute(); @@ -200,6 +203,23 @@ private static void selectMediaType(Activity activity, String type, int requestC } } + public boolean areConstraintsSatisfied(final @NonNull Context context, + final @NonNull MasterSecret masterSecret, + final @NonNull MediaConstraints constraints) + { + return areConstraintsSatisfied(context, masterSecret, slideDeck.getThumbnailSlide(), constraints); + } + + private boolean areConstraintsSatisfied(final @NonNull Context context, + final @NonNull MasterSecret masterSecret, + final @Nullable Slide slide, + final @NonNull MediaConstraints constraints) + { + return slide == null || + constraints.isSatisfied(context, masterSecret, slide.getPart()) || + constraints.canResize(slide.getPart()); + } + private class RemoveButtonListener implements View.OnClickListener { @Override public void onClick(View v) { @@ -209,8 +229,7 @@ public void onClick(View v) { } public interface AttachmentListener { - void onAttachmentChanged(); - boolean isAttachmentAllowed(Slide slide); + void onAttachmentChanged(); } public enum MediaType { From 40606d7f67b2738eba1df9d7eac982eae5de0ed8 Mon Sep 17 00:00:00 2001 From: Jake McGinty Date: Mon, 7 Sep 2015 12:14:06 -1000 Subject: [PATCH 5/8] pass in data size to slide constructor remove lots of unnecessary MasterSecret passing too // FREEBIE --- .../securesms/ImageMediaAdapter.java | 2 +- .../securesms/database/MmsDatabase.java | 10 ++---- .../securesms/mms/AttachmentManager.java | 20 ++++++------ .../securesms/mms/AudioSlide.java | 11 +++---- .../thoughtcrime/securesms/mms/GifSlide.java | 12 +++---- .../securesms/mms/ImageSlide.java | 11 +++---- .../securesms/mms/PushMediaConstraints.java | 2 +- src/org/thoughtcrime/securesms/mms/Slide.java | 31 +++---------------- .../thoughtcrime/securesms/mms/SlideDeck.java | 4 +-- .../securesms/mms/VideoSlide.java | 13 +++----- .../securesms/util/MediaUtil.java | 26 +++++++++++++--- 11 files changed, 60 insertions(+), 82 deletions(-) diff --git a/src/org/thoughtcrime/securesms/ImageMediaAdapter.java b/src/org/thoughtcrime/securesms/ImageMediaAdapter.java index 200fe28a86e..6691399db08 100644 --- a/src/org/thoughtcrime/securesms/ImageMediaAdapter.java +++ b/src/org/thoughtcrime/securesms/ImageMediaAdapter.java @@ -74,7 +74,7 @@ public void onBindViewHolder(final ViewHolder viewHolder, final Cursor cursor) { part.setContentType(imageRecord.getContentType().getBytes()); part.setPartId(imageRecord.getPartId()); - Slide slide = MediaUtil.getSlideForPart(getContext(), masterSecret, part, imageRecord.getContentType()); + Slide slide = MediaUtil.getSlideForPart(getContext(), part, imageRecord.getContentType()); if (slide != null) { imageView.setImageResource(slide, masterSecret); } diff --git a/src/org/thoughtcrime/securesms/database/MmsDatabase.java b/src/org/thoughtcrime/securesms/database/MmsDatabase.java index c479b3e52a8..a85c2aa703a 100644 --- a/src/org/thoughtcrime/securesms/database/MmsDatabase.java +++ b/src/org/thoughtcrime/securesms/database/MmsDatabase.java @@ -1094,7 +1094,7 @@ private MediaMmsMessageRecord getMediaMmsMessageRecord(Cursor cursor) { List mismatches = getMismatchedIdentities(mismatchDocument); List networkFailures = getFailures(networkDocument); - ListenableFutureTask slideDeck = getSlideDeck(masterSecret, dateReceived, id); + ListenableFutureTask slideDeck = getSlideDeck(dateReceived, id); return new MediaMmsMessageRecord(context, id, recipients, recipients.getPrimaryRecipient(), addressDeviceId, dateSent, dateReceived, receiptCount, @@ -1159,8 +1159,7 @@ private DisplayRecord.Body getBody(Cursor cursor) { } } - private ListenableFutureTask getSlideDeck(final MasterSecret masterSecret, - final long timestamp, + private ListenableFutureTask getSlideDeck(final long timestamp, final long id) { ListenableFutureTask future = getCachedSlideDeck(timestamp, id); @@ -1172,12 +1171,9 @@ private ListenableFutureTask getSlideDeck(final MasterSecret masterSe Callable task = new Callable() { @Override public SlideDeck call() throws Exception { - if (masterSecret == null) - return null; - PartDatabase partDatabase = DatabaseFactory.getPartDatabase(context); PduBody body = getPartsAsBody(partDatabase.getParts(id)); - SlideDeck slideDeck = new SlideDeck(context, masterSecret, body); + SlideDeck slideDeck = new SlideDeck(context, body); if (!body.containsPushInProgress()) { slideCache.put(timestamp + "::" + id, new SoftReference<>(slideDeck)); diff --git a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java index a7e173c6ba1..b6c5980bc3b 100644 --- a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java +++ b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java @@ -38,6 +38,7 @@ import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.providers.CaptureProvider; import org.thoughtcrime.securesms.recipients.Recipients; +import org.thoughtcrime.securesms.util.MediaUtil; import java.io.IOException; @@ -103,8 +104,9 @@ public void setMedia(@NonNull final MasterSecret masterSecret, @Override protected @Nullable Slide doInBackground(Void... params) { long start = System.currentTimeMillis(); try { - final Slide slide = mediaType.createSlide(context, masterSecret, uri); - Log.w(TAG, "slide with size " + slide.getPart().getDataSize() + " took " + (System.currentTimeMillis() - start) + "ms"); + final long mediaSize = MediaUtil.getMediaSize(context, masterSecret, uri); + final Slide slide = mediaType.createSlide(context, uri, mediaSize); + Log.w(TAG, "slide with size " + mediaSize + " took " + (System.currentTimeMillis() - start) + "ms"); return slide; } catch (IOException ioe) { Log.w(TAG, ioe); @@ -235,16 +237,16 @@ public interface AttachmentListener { public enum MediaType { IMAGE, GIF, AUDIO, VIDEO; - public @NonNull Slide createSlide(@NonNull Context context, - @NonNull MasterSecret masterSecret, - @NonNull Uri uri) + public @NonNull Slide createSlide(@NonNull Context context, + @NonNull Uri uri, + long dataSize) throws IOException { switch (this) { - case IMAGE: return new ImageSlide(context, masterSecret, uri); - case GIF: return new GifSlide(context, masterSecret, uri); - case AUDIO: return new AudioSlide(context, uri); - case VIDEO: return new VideoSlide(context, uri); + case IMAGE: return new ImageSlide(context, uri, dataSize); + case GIF: return new GifSlide(context, uri, dataSize); + case AUDIO: return new AudioSlide(context, uri, dataSize); + case VIDEO: return new VideoSlide(context, uri, dataSize); default: throw new AssertionError("unrecognized enum"); } } diff --git a/src/org/thoughtcrime/securesms/mms/AudioSlide.java b/src/org/thoughtcrime/securesms/mms/AudioSlide.java index e7f3c5930dc..78948e65c27 100644 --- a/src/org/thoughtcrime/securesms/mms/AudioSlide.java +++ b/src/org/thoughtcrime/securesms/mms/AudioSlide.java @@ -18,13 +18,10 @@ import android.content.Context; import android.content.res.Resources.Theme; -import android.database.Cursor; import android.net.Uri; -import android.provider.MediaStore.Audio; import android.support.annotation.DrawableRes; import org.thoughtcrime.securesms.R; -import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.util.ResUtil; import java.io.IOException; @@ -34,12 +31,12 @@ public class AudioSlide extends Slide { - public AudioSlide(Context context, Uri uri) throws IOException { - super(context, constructPartFromUri(context, null, uri, ContentType.AUDIO_UNSPECIFIED)); + public AudioSlide(Context context, Uri uri, long dataSize) throws IOException { + super(context, constructPartFromUri(context, uri, ContentType.AUDIO_UNSPECIFIED, dataSize)); } - public AudioSlide(Context context, MasterSecret masterSecret, PduPart part) { - super(context, masterSecret, part); + public AudioSlide(Context context, PduPart part) { + super(context, part); } @Override diff --git a/src/org/thoughtcrime/securesms/mms/GifSlide.java b/src/org/thoughtcrime/securesms/mms/GifSlide.java index 7483b6a798b..9fcbb65e86e 100644 --- a/src/org/thoughtcrime/securesms/mms/GifSlide.java +++ b/src/org/thoughtcrime/securesms/mms/GifSlide.java @@ -2,22 +2,18 @@ import android.content.Context; import android.net.Uri; -import android.provider.MediaStore.Audio.Media; - -import org.thoughtcrime.securesms.crypto.MasterSecret; -import org.thoughtcrime.securesms.util.BitmapDecodingException; import java.io.IOException; import ws.com.google.android.mms.pdu.PduPart; public class GifSlide extends ImageSlide { - public GifSlide(Context context, MasterSecret masterSecret, PduPart part) { - super(context, masterSecret, part); + public GifSlide(Context context, PduPart part) { + super(context, part); } - public GifSlide(Context context, MasterSecret masterSecret, Uri uri) throws IOException { - super(context, masterSecret, uri); + public GifSlide(Context context, Uri uri, long dataSize) throws IOException { + super(context, uri, dataSize); } @Override public Uri getThumbnailUri() { diff --git a/src/org/thoughtcrime/securesms/mms/ImageSlide.java b/src/org/thoughtcrime/securesms/mms/ImageSlide.java index 65c76ce2a07..1ec867b11d0 100644 --- a/src/org/thoughtcrime/securesms/mms/ImageSlide.java +++ b/src/org/thoughtcrime/securesms/mms/ImageSlide.java @@ -22,9 +22,6 @@ import android.support.annotation.DrawableRes; import org.thoughtcrime.securesms.R; -import org.thoughtcrime.securesms.crypto.MasterSecret; -import org.thoughtcrime.securesms.util.BitmapDecodingException; -import org.thoughtcrime.securesms.util.MediaUtil; import java.io.IOException; @@ -34,12 +31,12 @@ public class ImageSlide extends Slide { private static final String TAG = ImageSlide.class.getSimpleName(); - public ImageSlide(Context context, MasterSecret masterSecret, PduPart part) { - super(context, masterSecret, part); + public ImageSlide(Context context, PduPart part) { + super(context, part); } - public ImageSlide(Context context, MasterSecret masterSecret, Uri uri) throws IOException { - super(context, masterSecret, constructPartFromUri(context, masterSecret, uri, ContentType.IMAGE_JPEG)); + public ImageSlide(Context context, Uri uri, long size) throws IOException { + super(context, constructPartFromUri(context, uri, ContentType.IMAGE_JPEG, size)); } @Override diff --git a/src/org/thoughtcrime/securesms/mms/PushMediaConstraints.java b/src/org/thoughtcrime/securesms/mms/PushMediaConstraints.java index 794759d7f30..d505ebe6e83 100644 --- a/src/org/thoughtcrime/securesms/mms/PushMediaConstraints.java +++ b/src/org/thoughtcrime/securesms/mms/PushMediaConstraints.java @@ -32,7 +32,7 @@ public int getGifMaxSize() { @Override public int getVideoMaxSize() { - return 50 * MB; + return 100 * MB; } @Override diff --git a/src/org/thoughtcrime/securesms/mms/Slide.java b/src/org/thoughtcrime/securesms/mms/Slide.java index 46105602db8..60e925576d6 100644 --- a/src/org/thoughtcrime/securesms/mms/Slide.java +++ b/src/org/thoughtcrime/securesms/mms/Slide.java @@ -34,20 +34,14 @@ public abstract class Slide { - protected final PduPart part; - protected final Context context; - protected MasterSecret masterSecret; + protected final PduPart part; + protected final Context context; public Slide(Context context, @NonNull PduPart part) { this.part = part; this.context = context; } - public Slide(Context context, @NonNull MasterSecret masterSecret, @NonNull PduPart part) { - this(context, part); - this.masterSecret = masterSecret; - } - public String getContentType() { return new String(part.getContentType()); } @@ -92,33 +86,18 @@ public boolean isDraft() { return !getPart().getPartId().isValid(); } - protected static long getMediaSize(Context context, MasterSecret masterSecret, Uri uri) throws IOException { - InputStream in = PartAuthority.getPartStream(context, masterSecret, uri); - if (in == null) throw new IOException("Couldn't obtain input stream."); - - long size = 0; - byte[] buffer = new byte[512]; - int read; - - while ((read = in.read(buffer)) != -1) { - size += read; - } - in.close(); - - return size; - } protected static PduPart constructPartFromUri(@NonNull Context context, - @Nullable MasterSecret masterSecret, @NonNull Uri uri, - @NonNull String defaultMime) + @NonNull String defaultMime, + long dataSize) throws IOException { final PduPart part = new PduPart(); final String mimeType = MediaUtil.getMimeType(context, uri); final String derivedMimeType = mimeType != null ? mimeType : defaultMime; - part.setDataSize(getMediaSize(context, masterSecret, uri)); + part.setDataSize(dataSize); part.setDataUri(uri); part.setContentType(derivedMimeType.getBytes()); part.setContentId((System.currentTimeMillis()+"").getBytes()); diff --git a/src/org/thoughtcrime/securesms/mms/SlideDeck.java b/src/org/thoughtcrime/securesms/mms/SlideDeck.java index 794b63b8953..66afee40be6 100644 --- a/src/org/thoughtcrime/securesms/mms/SlideDeck.java +++ b/src/org/thoughtcrime/securesms/mms/SlideDeck.java @@ -47,10 +47,10 @@ public SlideDeck(SlideDeck copy) { this.slides.addAll(copy.getSlides()); } - public SlideDeck(Context context, MasterSecret masterSecret, PduBody body) { + public SlideDeck(Context context, PduBody body) { for (int i=0;i Date: Mon, 7 Sep 2015 12:47:49 -1000 Subject: [PATCH 6/8] better naming, capture race condition fix --- .../securesms/ConversationActivity.java | 36 +++++++++++-------- .../securesms/mms/AttachmentManager.java | 17 +++++---- 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/src/org/thoughtcrime/securesms/ConversationActivity.java b/src/org/thoughtcrime/securesms/ConversationActivity.java index 2ce4cc45bcd..139abd9a122 100644 --- a/src/org/thoughtcrime/securesms/ConversationActivity.java +++ b/src/org/thoughtcrime/securesms/ConversationActivity.java @@ -290,15 +290,15 @@ public void onActivityResult(int reqCode, int resultCode, Intent data) { switch (reqCode) { case PICK_IMAGE: - addAttachment(data.getData(), MediaUtil.isGif(MediaUtil.getMimeType(this, data.getData())) + setAttachment(data.getData(), MediaUtil.isGif(MediaUtil.getMimeType(this, data.getData())) ? MediaType.GIF : MediaType.IMAGE); break; case PICK_VIDEO: - addAttachment(data.getData(), MediaType.VIDEO); + setAttachment(data.getData(), MediaType.VIDEO); break; case PICK_AUDIO: - addAttachment(data.getData(), MediaType.AUDIO); + setAttachment(data.getData(), MediaType.AUDIO); break; case PICK_CONTACT_INFO: addAttachmentContactInfo(data.getData()); @@ -312,7 +312,7 @@ public void onActivityResult(int reqCode, int resultCode, Intent data) { break; case TAKE_PHOTO: if (attachmentManager.getCaptureUri() != null) { - addAttachment(attachmentManager.getCaptureUri(), MediaType.IMAGE); + setCapturedAttachment(attachmentManager.getCaptureUri(), MediaType.IMAGE); } break; } @@ -676,9 +676,9 @@ private void initializeDraft() { if (draftText != null) composeText.setText(draftText); - if (draftImage != null) addAttachment(draftImage, MediaType.IMAGE); - else if (draftAudio != null) addAttachment(draftAudio, MediaType.AUDIO); - else if (draftVideo != null) addAttachment(draftVideo, MediaType.VIDEO); + if (draftImage != null) setAttachment(draftImage, MediaType.IMAGE); + else if (draftAudio != null) setAttachment(draftAudio, MediaType.AUDIO); + else if (draftVideo != null) setAttachment(draftVideo, MediaType.VIDEO); if (draftText == null && draftImage == null && draftAudio == null && draftVideo == null) { initializeDraftFromDatabase(); @@ -712,11 +712,11 @@ protected void onPostExecute(List drafts) { if (draft.getType().equals(Draft.TEXT)) { composeText.setText(draft.getValue()); } else if (draft.getType().equals(Draft.IMAGE)) { - addAttachment(Uri.parse(draft.getValue()), MediaType.IMAGE); + setAttachment(Uri.parse(draft.getValue()), MediaType.IMAGE); } else if (draft.getType().equals(Draft.AUDIO)) { - addAttachment(Uri.parse(draft.getValue()), MediaType.AUDIO); + setAttachment(Uri.parse(draft.getValue()), MediaType.AUDIO); } else if (draft.getType().equals(Draft.VIDEO)) { - addAttachment(Uri.parse(draft.getValue()), MediaType.VIDEO); + setAttachment(Uri.parse(draft.getValue()), MediaType.VIDEO); } } @@ -922,8 +922,16 @@ private void addAttachment(int type) { } } - private void addAttachment(Uri uri, MediaType mediaType) { - attachmentManager.setMedia(masterSecret, uri, mediaType, getCurrentConstraints()); + private void setCapturedAttachment(Uri uri, MediaType mediaType) { + setMedia(uri, mediaType, true); + } + + private void setAttachment(Uri uri, MediaType mediaType) { + setMedia(uri, mediaType, false); + } + + private void setMedia(Uri uri, MediaType mediaType, boolean isCapture) { + attachmentManager.setMedia(masterSecret, uri, mediaType, getCurrentConstraints(), isCapture); } private void addAttachmentContactInfo(Uri contactUri) { @@ -1245,8 +1253,8 @@ public void onAttachmentDrawerStateChanged(DrawerState drawerState) { @Override public void onImageCapture(@NonNull final byte[] imageBytes) { - attachmentManager.setCaptureUri(CaptureProvider.getInstance(this).create(masterSecret, recipients, imageBytes)); - addAttachment(attachmentManager.getCaptureUri(), MediaType.IMAGE); + setCapturedAttachment(CaptureProvider.getInstance(this).create(masterSecret, recipients, imageBytes), + MediaType.IMAGE); quickAttachmentDrawer.hide(false); } diff --git a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java index b6c5980bc3b..9fc327af5b9 100644 --- a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java +++ b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java @@ -87,18 +87,21 @@ public void cleanup() { captureUri = null; } - public void setMedia(@NonNull final MasterSecret masterSecret, - @NonNull final Uri uri, - @NonNull final MediaType mediaType, - @NonNull final MediaConstraints constraints) + public void setMedia(@NonNull final MasterSecret masterSecret, + @NonNull final Uri uri, + @NonNull final MediaType mediaType, + @NonNull final MediaConstraints constraints, + final boolean isCapture) { new AsyncTask() { @Override protected void onPreExecute() { slideDeck.clear(); thumbnail.clear(); - cleanup(); thumbnail.showProgressSpinner(); attachmentView.setVisibility(View.VISIBLE); + + if (isCapture) captureUri = uri; + if (!uri.equals(captureUri)) cleanup(); } @Override protected @Nullable Slide doInBackground(Void... params) { @@ -165,10 +168,6 @@ public Uri getCaptureUri() { return captureUri; } - public void setCaptureUri(Uri captureUri) { - this.captureUri = captureUri; - } - public void capturePhoto(Activity activity, Recipients recipients, int requestCode) { try { Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); From 321c8c293dcf3543fcd3a296b5f3a797d7ac767b Mon Sep 17 00:00:00 2001 From: Jake McGinty Date: Mon, 7 Sep 2015 15:32:00 -1000 Subject: [PATCH 7/8] aesthetic fix and constraint method refactor --- .../securesms/ConversationActivity.java | 53 ++++++++++--------- .../securesms/mms/AttachmentManager.java | 19 ++----- 2 files changed, 33 insertions(+), 39 deletions(-) diff --git a/src/org/thoughtcrime/securesms/ConversationActivity.java b/src/org/thoughtcrime/securesms/ConversationActivity.java index 139abd9a122..6521b45569c 100644 --- a/src/org/thoughtcrime/securesms/ConversationActivity.java +++ b/src/org/thoughtcrime/securesms/ConversationActivity.java @@ -35,6 +35,7 @@ import android.os.Bundle; import android.provider.ContactsContract; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.support.v4.view.WindowCompat; import android.text.Editable; import android.text.TextWatcher; @@ -290,15 +291,16 @@ public void onActivityResult(int reqCode, int resultCode, Intent data) { switch (reqCode) { case PICK_IMAGE: - setAttachment(data.getData(), MediaUtil.isGif(MediaUtil.getMimeType(this, data.getData())) - ? MediaType.GIF - : MediaType.IMAGE); + setMedia(data.getData(), + MediaUtil.isGif(MediaUtil.getMimeType(this, data.getData())) ? MediaType.GIF + : MediaType.IMAGE, + false); break; case PICK_VIDEO: - setAttachment(data.getData(), MediaType.VIDEO); + setMedia(data.getData(), MediaType.VIDEO, false); break; case PICK_AUDIO: - setAttachment(data.getData(), MediaType.AUDIO); + setMedia(data.getData(), MediaType.AUDIO, false); break; case PICK_CONTACT_INFO: addAttachmentContactInfo(data.getData()); @@ -312,7 +314,7 @@ public void onActivityResult(int reqCode, int resultCode, Intent data) { break; case TAKE_PHOTO: if (attachmentManager.getCaptureUri() != null) { - setCapturedAttachment(attachmentManager.getCaptureUri(), MediaType.IMAGE); + setMedia(attachmentManager.getCaptureUri(), MediaType.IMAGE, true); } break; } @@ -676,9 +678,9 @@ private void initializeDraft() { if (draftText != null) composeText.setText(draftText); - if (draftImage != null) setAttachment(draftImage, MediaType.IMAGE); - else if (draftAudio != null) setAttachment(draftAudio, MediaType.AUDIO); - else if (draftVideo != null) setAttachment(draftVideo, MediaType.VIDEO); + if (draftImage != null) setMedia(draftImage, MediaType.IMAGE, false); + else if (draftAudio != null) setMedia(draftAudio, MediaType.AUDIO, false); + else if (draftVideo != null) setMedia(draftVideo, MediaType.VIDEO, false); if (draftText == null && draftImage == null && draftAudio == null && draftVideo == null) { initializeDraftFromDatabase(); @@ -712,11 +714,11 @@ protected void onPostExecute(List drafts) { if (draft.getType().equals(Draft.TEXT)) { composeText.setText(draft.getValue()); } else if (draft.getType().equals(Draft.IMAGE)) { - setAttachment(Uri.parse(draft.getValue()), MediaType.IMAGE); + setMedia(Uri.parse(draft.getValue()), MediaType.IMAGE, false); } else if (draft.getType().equals(Draft.AUDIO)) { - setAttachment(Uri.parse(draft.getValue()), MediaType.AUDIO); + setMedia(Uri.parse(draft.getValue()), MediaType.AUDIO, false); } else if (draft.getType().equals(Draft.VIDEO)) { - setAttachment(Uri.parse(draft.getValue()), MediaType.VIDEO); + setMedia(Uri.parse(draft.getValue()), MediaType.VIDEO, false); } } @@ -922,16 +924,8 @@ private void addAttachment(int type) { } } - private void setCapturedAttachment(Uri uri, MediaType mediaType) { - setMedia(uri, mediaType, true); - } - - private void setAttachment(Uri uri, MediaType mediaType) { - setMedia(uri, mediaType, false); - } - private void setMedia(Uri uri, MediaType mediaType, boolean isCapture) { - attachmentManager.setMedia(masterSecret, uri, mediaType, getCurrentConstraints(), isCapture); + attachmentManager.setMedia(masterSecret, uri, mediaType, getCurrentMediaConstraints(), isCapture); } private void addAttachmentContactInfo(Uri contactUri) { @@ -1098,12 +1092,20 @@ private String getMessage() throws InvalidMessageException { return rawText; } - private MediaConstraints getCurrentConstraints() { + private MediaConstraints getCurrentMediaConstraints() { return sendButton.getSelectedTransport().getType() == Type.TEXTSECURE ? MediaConstraints.PUSH_CONSTRAINTS : MediaConstraints.MMS_CONSTRAINTS; } + private boolean isCurrentMediaConstraintSatisfied() { + final MediaConstraints constraints = getCurrentMediaConstraints(); + final Slide mediaSlide = attachmentManager.getSlideDeck().getThumbnailSlide(); + return mediaSlide == null || + constraints.isSatisfied(this, masterSecret, mediaSlide.getPart()) || + constraints.canResize(mediaSlide.getPart()); + } + private void markThreadAsRead() { new AsyncTask() { @Override @@ -1171,7 +1173,7 @@ private void sendMediaMessage(final boolean forceSms) SlideDeck slideDeck; if (attachmentManager.isAttachmentPresent()) { - if (!attachmentManager.areConstraintsSatisfied(this, masterSecret, getCurrentConstraints())) { + if (!isCurrentMediaConstraintSatisfied()) { Toast.makeText(context, R.string.ConversationActivity_attachment_exceeds_size_limits, Toast.LENGTH_SHORT).show(); @@ -1253,8 +1255,9 @@ public void onAttachmentDrawerStateChanged(DrawerState drawerState) { @Override public void onImageCapture(@NonNull final byte[] imageBytes) { - setCapturedAttachment(CaptureProvider.getInstance(this).create(masterSecret, recipients, imageBytes), - MediaType.IMAGE); + setMedia(CaptureProvider.getInstance(this).create(masterSecret, recipients, imageBytes), + MediaType.IMAGE, + true); quickAttachmentDrawer.hide(false); } diff --git a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java index 9fc327af5b9..e401918a558 100644 --- a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java +++ b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java @@ -54,10 +54,10 @@ public class AttachmentManager { private Uri captureUri; public AttachmentManager(Activity view, AttachmentListener listener) { - this.attachmentView = view.findViewById(R.id.attachment_editor); - this.thumbnail = (ThumbnailView)view.findViewById(R.id.attachment_thumbnail); - this.slideDeck = new SlideDeck(); - this.context = view; + this.attachmentView = view.findViewById(R.id.attachment_editor); + this.thumbnail = (ThumbnailView) view.findViewById(R.id.attachment_thumbnail); + this.slideDeck = new SlideDeck(); + this.context = view; this.attachmentListener = listener; thumbnail.setRemoveClickListener(new RemoveButtonListener()); @@ -68,9 +68,7 @@ public void clear() { animation.setDuration(200); animation.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) {} - @Override public void onAnimationRepeat(Animation animation) {} - @Override public void onAnimationEnd(Animation animation) { slideDeck.clear(); thumbnail.clear(); @@ -143,7 +141,7 @@ public boolean isAttachmentPresent() { } - public SlideDeck getSlideDeck() { + public @NonNull SlideDeck getSlideDeck() { return slideDeck; } @@ -204,13 +202,6 @@ private static void selectMediaType(Activity activity, String type, int requestC } } - public boolean areConstraintsSatisfied(final @NonNull Context context, - final @NonNull MasterSecret masterSecret, - final @NonNull MediaConstraints constraints) - { - return areConstraintsSatisfied(context, masterSecret, slideDeck.getThumbnailSlide(), constraints); - } - private boolean areConstraintsSatisfied(final @NonNull Context context, final @NonNull MasterSecret masterSecret, final @Nullable Slide slide, From 49ed4d04f3cbe95abc14f99b852d5cbaba31b927 Mon Sep 17 00:00:00 2001 From: Jake McGinty Date: Mon, 7 Sep 2015 15:32:53 -1000 Subject: [PATCH 8/8] get it done --- src/org/thoughtcrime/securesms/util/MediaUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/thoughtcrime/securesms/util/MediaUtil.java b/src/org/thoughtcrime/securesms/util/MediaUtil.java index 0bdf392cd38..9af08d3b81c 100644 --- a/src/org/thoughtcrime/securesms/util/MediaUtil.java +++ b/src/org/thoughtcrime/securesms/util/MediaUtil.java @@ -95,7 +95,7 @@ public static long getMediaSize(Context context, MasterSecret masterSecret, Uri if (in == null) throw new IOException("Couldn't obtain input stream."); long size = 0; - byte[] buffer = new byte[512]; + byte[] buffer = new byte[4096]; int read; while ((read = in.read(buffer)) != -1) {