diff --git a/src/org/thoughtcrime/securesms/ConversationActivity.java b/src/org/thoughtcrime/securesms/ConversationActivity.java index e6ac201ac9..142075b7ac 100644 --- a/src/org/thoughtcrime/securesms/ConversationActivity.java +++ b/src/org/thoughtcrime/securesms/ConversationActivity.java @@ -73,6 +73,7 @@ import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.mms.AttachmentManager; import org.thoughtcrime.securesms.mms.AttachmentTypeSelectorAdapter; import org.thoughtcrime.securesms.mms.MediaTooLargeException; +import org.thoughtcrime.securesms.mms.MmsMediaConstraints; import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage; import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; import org.thoughtcrime.securesms.mms.OutgoingMmsConnection; @@ -848,8 +849,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity Log.w("ComposeMessageActivity", e); } catch (MediaTooLargeException e) { attachmentManager.clear(); + Toast.makeText(this, getString(R.string.ConversationActivity_sorry_the_selected_video_exceeds_message_size_restrictions, - (Slide.MAX_MESSAGE_SIZE/1024)), + (MmsMediaConstraints.MAX_MESSAGE_SIZE/1024)), Toast.LENGTH_LONG).show(); Log.w("ComposeMessageActivity", e); } @@ -866,7 +868,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } catch (MediaTooLargeException e) { attachmentManager.clear(); Toast.makeText(this, getString(R.string.ConversationActivity_sorry_the_selected_audio_exceeds_message_size_restrictions, - (Slide.MAX_MESSAGE_SIZE/1024)), + (MmsMediaConstraints.MAX_MESSAGE_SIZE/1024)), Toast.LENGTH_LONG).show(); Log.w("ComposeMessageActivity", e); } diff --git a/src/org/thoughtcrime/securesms/database/PartDatabase.java b/src/org/thoughtcrime/securesms/database/PartDatabase.java index 624f0ccbc4..1fc0b58ea1 100644 --- a/src/org/thoughtcrime/securesms/database/PartDatabase.java +++ b/src/org/thoughtcrime/securesms/database/PartDatabase.java @@ -461,6 +461,38 @@ public class PartDatabase extends Database { notifyConversationListeners(DatabaseFactory.getMmsDatabase(context).getThreadIdForMessage(messageId)); } + public void updatePartData(MasterSecret masterSecret, PduPart part, InputStream data) + throws MmsException + { + SQLiteDatabase database = databaseHelper.getWritableDatabase(); + Pair partData = writePartData(masterSecret, part, data); + + if (partData == null) throw new MmsException("couldn't update part data"); + + Cursor cursor = null; + try { + cursor = database.query(TABLE_NAME, new String[]{DATA}, ID_WHERE, + new String[]{part.getId()+""}, null, null, null); + + if (cursor != null && cursor.moveToFirst()) { + int dataColumn = cursor.getColumnIndexOrThrow(DATA); + if (!cursor.isNull(dataColumn) && !new File(cursor.getString(dataColumn)).delete()) { + Log.w(TAG, "Couldn't delete old part file"); + } + } + } finally { + if (cursor != null) cursor.close(); + } + ContentValues values = new ContentValues(2); + values.put(DATA, partData.first.getAbsolutePath()); + values.put(SIZE, partData.second); + + part.setDataSize(partData.second); + + database.update(TABLE_NAME, values, ID_WHERE, new String[] {part.getId()+""}); + Log.w(TAG, "updated data for part #" + part.getId()); + } + public void updatePartThumbnail(MasterSecret masterSecret, long partId, PduPart part, InputStream in, float aspectRatio) throws MmsException { diff --git a/src/org/thoughtcrime/securesms/jobs/MmsSendJob.java b/src/org/thoughtcrime/securesms/jobs/MmsSendJob.java index a0d98f3c4b..9bd434cbd7 100644 --- a/src/org/thoughtcrime/securesms/jobs/MmsSendJob.java +++ b/src/org/thoughtcrime/securesms/jobs/MmsSendJob.java @@ -12,11 +12,11 @@ import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.database.NoSuchMessageException; import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement; import org.thoughtcrime.securesms.mms.ApnUnavailableException; +import org.thoughtcrime.securesms.mms.MediaConstraints; import org.thoughtcrime.securesms.mms.MmsRadio; import org.thoughtcrime.securesms.mms.MmsRadioException; import org.thoughtcrime.securesms.mms.MmsSendResult; import org.thoughtcrime.securesms.mms.OutgoingMmsConnection; -import org.thoughtcrime.securesms.mms.PartAuthority; import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.recipients.RecipientFormattingException; import org.thoughtcrime.securesms.recipients.Recipients; @@ -24,21 +24,17 @@ import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException; import org.thoughtcrime.securesms.transport.UndeliverableMessageException; import org.thoughtcrime.securesms.util.Hex; import org.thoughtcrime.securesms.util.NumberUtil; -import org.thoughtcrime.securesms.util.Util; import org.whispersystems.jobqueue.JobParameters; import org.whispersystems.jobqueue.requirements.NetworkRequirement; import org.whispersystems.libaxolotl.NoSessionException; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Arrays; import ws.com.google.android.mms.MmsException; import ws.com.google.android.mms.pdu.EncodedStringValue; -import ws.com.google.android.mms.pdu.PduBody; import ws.com.google.android.mms.pdu.PduComposer; import ws.com.google.android.mms.pdu.PduHeaders; -import ws.com.google.android.mms.pdu.PduPart; import ws.com.google.android.mms.pdu.SendConf; import ws.com.google.android.mms.pdu.SendReq; @@ -69,8 +65,6 @@ public class MmsSendJob extends SendJob { MmsDatabase database = DatabaseFactory.getMmsDatabase(context); SendReq message = database.getOutgoingMessage(masterSecret, messageId); - populatePartData(message.getBody(), masterSecret); - try { MmsSendResult result = deliver(masterSecret, message); @@ -101,24 +95,6 @@ public class MmsSendJob extends SendJob { notifyMediaMessageDeliveryFailed(context, messageId); } - private void populatePartData(PduPart part, MasterSecret masterSecret) throws IOException { - if (part.getDataUri() == null) { - return; - } - - ByteArrayOutputStream os = part.getDataSize() > 0 && part.getDataSize() < Integer.MAX_VALUE - ? new ByteArrayOutputStream((int)part.getDataSize()) - : new ByteArrayOutputStream(); - Util.copy(PartAuthority.getPartStream(context, masterSecret, part.getDataUri()), os); - part.setData(os.toByteArray()); - } - - private void populatePartData(PduBody body, MasterSecret masterSecret) throws IOException { - for (int i=body.getPartsNum()-1; i>=0; i--) { - populatePartData(body.getPart(i), masterSecret); - } - } - public MmsSendResult deliver(MasterSecret masterSecret, SendReq message) throws UndeliverableMessageException, InsecureFallbackApprovalException { @@ -182,13 +158,11 @@ public class MmsSendJob extends SendJob { message.setFrom(new EncodedStringValue(number)); } + prepareMessageMedia(masterSecret, message, MediaConstraints.MMS_CONSTRAINTS, true); + try { OutgoingMmsConnection connection = new OutgoingMmsConnection(context, radio.getApnInformation(), new PduComposer(context, message).make()); - SendConf conf = connection.send(usingMmsRadio, useProxy); - - for (int i=0;i attachments = getAttachments(masterSecret, message); diff --git a/src/org/thoughtcrime/securesms/jobs/PushSendJob.java b/src/org/thoughtcrime/securesms/jobs/PushSendJob.java index d3d4764244..db13866547 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushSendJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushSendJob.java @@ -6,7 +6,10 @@ import android.util.Log; import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement; +import org.thoughtcrime.securesms.mms.MmsMediaConstraints; import org.thoughtcrime.securesms.mms.PartAuthority; +import org.thoughtcrime.securesms.mms.MediaConstraints; +import org.thoughtcrime.securesms.mms.PushMediaConstraints; import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipients; @@ -21,7 +24,6 @@ import org.thoughtcrime.securesms.database.TextSecureDirectory; import org.whispersystems.textsecure.api.push.PushAddress; import org.whispersystems.textsecure.api.util.InvalidNumberException; -import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.LinkedList; diff --git a/src/org/thoughtcrime/securesms/jobs/SendJob.java b/src/org/thoughtcrime/securesms/jobs/SendJob.java index e75db25ec8..5e2d145240 100644 --- a/src/org/thoughtcrime/securesms/jobs/SendJob.java +++ b/src/org/thoughtcrime/securesms/jobs/SendJob.java @@ -1,14 +1,27 @@ package org.thoughtcrime.securesms.jobs; import android.content.Context; +import android.util.Log; import org.thoughtcrime.securesms.BuildConfig; import org.thoughtcrime.securesms.TextSecureExpiredException; import org.thoughtcrime.securesms.crypto.MasterSecret; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.mms.MediaConstraints; +import org.thoughtcrime.securesms.transport.UndeliverableMessageException; +import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.Util; import org.whispersystems.jobqueue.JobParameters; +import java.io.ByteArrayInputStream; +import java.io.IOException; + +import ws.com.google.android.mms.MmsException; +import ws.com.google.android.mms.pdu.PduPart; +import ws.com.google.android.mms.pdu.SendReq; + public abstract class SendJob extends MasterSecretJob { + private final static String TAG = SendJob.class.getSimpleName(); public SendJob(Context context, JobParameters parameters) { super(context, parameters); @@ -26,4 +39,54 @@ public abstract class SendJob extends MasterSecretJob { } protected abstract void onSend(MasterSecret masterSecret) throws Exception; + + protected void prepareMessageMedia(MasterSecret masterSecret, SendReq message, + MediaConstraints constraints, boolean toMemory) + throws IOException, UndeliverableMessageException + { + try { + for (int i = 0; i < message.getBody().getPartsNum(); i++) { + preparePart(masterSecret, constraints, message.getBody().getPart(i), toMemory); + } + } catch (MmsException me) { + throw new UndeliverableMessageException(me); + } + } + + private void preparePart(MasterSecret masterSecret, MediaConstraints constraints, + PduPart part, boolean toMemory) + throws IOException, MmsException, UndeliverableMessageException + { + byte[] resizedData = null; + + if (!constraints.isSatisfied(context, masterSecret, part)) { + if (!constraints.canResize(part)) { + throw new UndeliverableMessageException("Size constraints could not be satisfied."); + } + resizedData = resizePart(masterSecret, constraints, part); + } + + if (toMemory) { + part.setData(resizedData != null ? resizedData : MediaUtil.getPartData(context, masterSecret, part)); + } + + if (resizedData != null) { + part.setDataSize(resizedData.length); + } + } + + private byte[] resizePart(MasterSecret masterSecret, MediaConstraints constraints, + PduPart part) + throws IOException, MmsException + { + Log.w(TAG, "resizing part " + part.getId()); + + final long oldSize = part.getDataSize(); + final byte[] data = constraints.getResizedMedia(context, masterSecret, part); + + DatabaseFactory.getPartDatabase(context).updatePartData(masterSecret, part, new ByteArrayInputStream(data)); + Log.w(TAG, String.format("Resized part %.1fkb => %.1fkb", oldSize / 1024.0, part.getDataSize() / 1024.0)); + + return data; + } } diff --git a/src/org/thoughtcrime/securesms/mms/MediaConstraints.java b/src/org/thoughtcrime/securesms/mms/MediaConstraints.java new file mode 100644 index 0000000000..5841f5cf87 --- /dev/null +++ b/src/org/thoughtcrime/securesms/mms/MediaConstraints.java @@ -0,0 +1,72 @@ +package org.thoughtcrime.securesms.mms; + +import android.content.Context; +import android.net.Uri; +import android.util.Log; +import android.util.Pair; + +import org.thoughtcrime.securesms.crypto.MasterSecret; +import org.thoughtcrime.securesms.util.BitmapDecodingException; +import org.thoughtcrime.securesms.util.BitmapUtil; +import org.thoughtcrime.securesms.util.MediaUtil; + +import java.io.IOException; +import java.io.InputStream; + +import ws.com.google.android.mms.pdu.PduPart; + +public abstract class MediaConstraints { + private static final String TAG = MediaConstraints.class.getSimpleName(); + + public static MediaConstraints MMS_CONSTRAINTS = new MmsMediaConstraints(); + public static MediaConstraints PUSH_CONSTRAINTS = new PushMediaConstraints(); + + public abstract int getImageMaxWidth(); + public abstract int getImageMaxHeight(); + public abstract int getImageMaxSize(); + + public abstract int getVideoMaxSize(); + + public abstract int getAudioMaxSize(); + + public boolean isSatisfied(Context context, MasterSecret masterSecret, PduPart part) { + try { + return (MediaUtil.isImage(part) && part.getDataSize() <= getImageMaxSize() && isWithinBounds(context, masterSecret, part.getDataUri())) || + (MediaUtil.isAudio(part) && part.getDataSize() <= getAudioMaxSize()) || + (MediaUtil.isVideo(part) && part.getDataSize() <= getVideoMaxSize()) || + (!MediaUtil.isImage(part) && !MediaUtil.isAudio(part) && !MediaUtil.isVideo(part)); + } catch (IOException ioe) { + Log.w(TAG, "Failed to determine if media's constraints are satisfied.", ioe); + return false; + } + } + + public boolean isWithinBounds(Context context, MasterSecret masterSecret, Uri uri) throws IOException { + InputStream is = PartAuthority.getPartStream(context, masterSecret, uri); + Pair dimensions = BitmapUtil.getDimensions(is); + return dimensions.first > 0 && dimensions.first <= getImageMaxWidth() && + dimensions.second > 0 && dimensions.second <= getImageMaxHeight(); + } + + public boolean canResize(PduPart part) { + return part != null && MediaUtil.isImage(part); + } + + public byte[] getResizedMedia(Context context, MasterSecret masterSecret, PduPart part) + throws IOException + { + if (!canResize(part) || part.getDataUri() == null) { + throw new UnsupportedOperationException("Cannot resize this content type"); + } + + try { + return BitmapUtil.createScaledBytes(context, masterSecret, part.getDataUri(), + getImageMaxWidth(), + getImageMaxHeight(), + getImageMaxSize()); + } catch (BitmapDecodingException bde) { + throw new IOException(bde); + } + } + +} diff --git a/src/org/thoughtcrime/securesms/mms/MmsMediaConstraints.java b/src/org/thoughtcrime/securesms/mms/MmsMediaConstraints.java new file mode 100644 index 0000000000..45f775f171 --- /dev/null +++ b/src/org/thoughtcrime/securesms/mms/MmsMediaConstraints.java @@ -0,0 +1,31 @@ +package org.thoughtcrime.securesms.mms; + +public class MmsMediaConstraints extends MediaConstraints { + private static final int MAX_IMAGE_DIMEN = 1280; + public static final int MAX_MESSAGE_SIZE = 280 * 1024; + + @Override + public int getImageMaxWidth() { + return MAX_IMAGE_DIMEN; + } + + @Override + public int getImageMaxHeight() { + return MAX_IMAGE_DIMEN; + } + + @Override + public int getImageMaxSize() { + return MAX_MESSAGE_SIZE; + } + + @Override + public int getVideoMaxSize() { + return MAX_MESSAGE_SIZE; + } + + @Override + public int getAudioMaxSize() { + return MAX_MESSAGE_SIZE; + } +} diff --git a/src/org/thoughtcrime/securesms/mms/PushMediaConstraints.java b/src/org/thoughtcrime/securesms/mms/PushMediaConstraints.java new file mode 100644 index 0000000000..6dbe9248d3 --- /dev/null +++ b/src/org/thoughtcrime/securesms/mms/PushMediaConstraints.java @@ -0,0 +1,32 @@ +package org.thoughtcrime.securesms.mms; + +public class PushMediaConstraints extends MediaConstraints { + private static final int MAX_IMAGE_DIMEN = 1280; + private static final int KB = 1024; + private static final int MB = 1024 * KB; + + @Override + public int getImageMaxWidth() { + return MAX_IMAGE_DIMEN; + } + + @Override + public int getImageMaxHeight() { + return MAX_IMAGE_DIMEN; + } + + @Override + public int getImageMaxSize() { + return 300 * KB; + } + + @Override + public int getVideoMaxSize() { + return MmsMediaConstraints.MAX_MESSAGE_SIZE; + } + + @Override + public int getAudioMaxSize() { + return MmsMediaConstraints.MAX_MESSAGE_SIZE; + } +} diff --git a/src/org/thoughtcrime/securesms/mms/Slide.java b/src/org/thoughtcrime/securesms/mms/Slide.java index 7dba4c45e5..a06f0f5342 100644 --- a/src/org/thoughtcrime/securesms/mms/Slide.java +++ b/src/org/thoughtcrime/securesms/mms/Slide.java @@ -36,8 +36,6 @@ import ws.com.google.android.mms.pdu.PduPart; public abstract class Slide { - public static final int MAX_MESSAGE_SIZE = 280 * 1024; - protected final PduPart part; protected final Context context; protected MasterSecret masterSecret; @@ -124,7 +122,7 @@ public abstract class Slide { while ((read = in.read(buffer)) != -1) { size += read; - if (size > MAX_MESSAGE_SIZE) throw new MediaTooLargeException("Media exceeds maximum message size."); + if (size > MmsMediaConstraints.MAX_MESSAGE_SIZE) throw new MediaTooLargeException("Media exceeds maximum message size."); } } } diff --git a/src/org/thoughtcrime/securesms/mms/VideoSlide.java b/src/org/thoughtcrime/securesms/mms/VideoSlide.java index 1f1f045f98..c18a159f0a 100644 --- a/src/org/thoughtcrime/securesms/mms/VideoSlide.java +++ b/src/org/thoughtcrime/securesms/mms/VideoSlide.java @@ -75,10 +75,12 @@ public class VideoSlide extends Slide { return SmilUtil.createMediaElement("video", document, new String(getPart().getName())); } - private static PduPart constructPartFromUri(Context context, Uri uri) throws IOException, MediaTooLargeException { - PduPart part = new PduPart(); + private static PduPart constructPartFromUri(Context context, Uri uri) + throws IOException, MediaTooLargeException + { + PduPart part = new PduPart(); ContentResolver resolver = context.getContentResolver(); - Cursor cursor = null; + Cursor cursor = null; try { cursor = resolver.query(uri, new String[] {MediaStore.Video.Media.MIME_TYPE}, null, null, null); diff --git a/src/org/thoughtcrime/securesms/transport/UndeliverableMessageException.java b/src/org/thoughtcrime/securesms/transport/UndeliverableMessageException.java index 2ba2604d41..02987b1cdb 100644 --- a/src/org/thoughtcrime/securesms/transport/UndeliverableMessageException.java +++ b/src/org/thoughtcrime/securesms/transport/UndeliverableMessageException.java @@ -1,6 +1,6 @@ package org.thoughtcrime.securesms.transport; -public class UndeliverableMessageException extends Throwable { +public class UndeliverableMessageException extends Exception { public UndeliverableMessageException() { } diff --git a/src/org/thoughtcrime/securesms/util/BitmapUtil.java b/src/org/thoughtcrime/securesms/util/BitmapUtil.java index 5f7384f9e2..6fd86507e5 100644 --- a/src/org/thoughtcrime/securesms/util/BitmapUtil.java +++ b/src/org/thoughtcrime/securesms/util/BitmapUtil.java @@ -6,13 +6,10 @@ import android.graphics.Bitmap.CompressFormat; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Matrix; -import android.graphics.Color; import android.graphics.Paint; import android.graphics.PorterDuff; -import android.graphics.PorterDuff.Mode; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; -import android.graphics.RectF; import android.net.Uri; import android.util.Log; import android.util.Pair; @@ -29,9 +26,6 @@ import java.io.InputStream; import com.android.gallery3d.data.Exif; -import org.thoughtcrime.securesms.crypto.MasterSecret; -import org.thoughtcrime.securesms.mms.PartAuthority; - public class BitmapUtil { private static final String TAG = BitmapUtil.class.getSimpleName(); @@ -61,6 +55,8 @@ public class BitmapUtil { quality = Math.max((quality * maxSize) / baos.size(), MIN_COMPRESSION_QUALITY); } while (baos.size() > maxSize && attempts++ < MAX_COMPRESSION_ATTEMPTS); + Log.w(TAG, "createScaledBytes(" + uri + ") -> quality " + Math.min(quality, MAX_COMPRESSION_QUALITY) + ", " + attempts + " attempt(s)"); + bitmap.recycle(); if (baos.size() <= maxSize) return baos.toByteArray(); @@ -216,6 +212,11 @@ public class BitmapUtil { return options; } + public static Pair getDimensions(InputStream inputStream) { + BitmapFactory.Options options = getImageDimensions(inputStream); + return new Pair<>(options.outWidth, options.outHeight); + } + public static Bitmap getCircleCroppedBitmap(Bitmap bitmap) { if (bitmap == null) return null; final int srcSize = Math.min(bitmap.getWidth(), bitmap.getHeight()); diff --git a/src/org/thoughtcrime/securesms/util/MediaUtil.java b/src/org/thoughtcrime/securesms/util/MediaUtil.java index 5f683dd07c..9594ff374a 100644 --- a/src/org/thoughtcrime/securesms/util/MediaUtil.java +++ b/src/org/thoughtcrime/securesms/util/MediaUtil.java @@ -9,7 +9,12 @@ import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.PartDatabase; +import org.thoughtcrime.securesms.mms.MediaConstraints; +import org.thoughtcrime.securesms.mms.PartAuthority; +import org.thoughtcrime.securesms.transport.UndeliverableMessageException; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; @@ -18,6 +23,7 @@ import java.util.concurrent.Callable; import ws.com.google.android.mms.ContentType; import ws.com.google.android.mms.MmsException; import ws.com.google.android.mms.pdu.PduPart; +import ws.com.google.android.mms.pdu.SendReq; public class MediaUtil { private static final String TAG = MediaUtil.class.getSimpleName(); @@ -39,6 +45,16 @@ public class MediaUtil { return data; } + public static byte[] getPartData(Context context, MasterSecret masterSecret, PduPart part) + throws IOException + { + ByteArrayOutputStream os = part.getDataSize() > 0 && part.getDataSize() < Integer.MAX_VALUE + ? new ByteArrayOutputStream((int) part.getDataSize()) + : new ByteArrayOutputStream(); + Util.copy(PartAuthority.getPartStream(context, masterSecret, part.getDataUri()), os); + return os.toByteArray(); + } + private static Bitmap generateImageThumbnail(Context context, MasterSecret masterSecret, Uri uri) throws IOException, BitmapDecodingException, OutOfMemoryError { @@ -46,6 +62,18 @@ public class MediaUtil { return BitmapUtil.createScaledBitmap(context, masterSecret, uri, maxSize, maxSize); } + public static boolean isImage(PduPart part) { + return ContentType.isImageType(Util.toIsoString(part.getContentType())); + } + + public static boolean isAudio(PduPart part) { + return ContentType.isAudioType(Util.toIsoString(part.getContentType())); + } + + public static boolean isVideo(PduPart part) { + return ContentType.isVideoType(Util.toIsoString(part.getContentType())); + } + public static class ThumbnailData { Bitmap bitmap; float aspectRatio; diff --git a/src/ws/com/google/android/mms/pdu/PduPart.java b/src/ws/com/google/android/mms/pdu/PduPart.java index 2a51b0b640..d94602fe90 100644 --- a/src/ws/com/google/android/mms/pdu/PduPart.java +++ b/src/ws/com/google/android/mms/pdu/PduPart.java @@ -20,8 +20,13 @@ package ws.com.google.android.mms.pdu; import android.graphics.Bitmap; import android.net.Uri; +import org.thoughtcrime.securesms.crypto.MasterSecret; +import org.thoughtcrime.securesms.mms.MediaConstraints; +import org.thoughtcrime.securesms.util.BitmapDecodingException; +import org.thoughtcrime.securesms.util.BitmapUtil; import org.thoughtcrime.securesms.util.Util; +import java.io.IOException; import java.util.HashMap; import java.util.Map;