mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-23 18:15:22 +00:00
parent
4f7ac59c6f
commit
551274f167
@ -126,6 +126,7 @@
|
||||
<string name="ConversationActivity_unblock_question">Unblock?</string>
|
||||
<string name="ConversationActivity_are_you_sure_you_want_to_unblock_this_contact">Are you sure you want to unblock this contact?</string>
|
||||
<string name="ConversationActivity_unblock">Unblock</string>
|
||||
<string name="ConversationActivity_attachment_exceeds_size_limits">Attachment exceeds size limits for the type of message you\'re sending.</string>
|
||||
|
||||
<!-- ConversationFragment -->
|
||||
<string name="ConversationFragment_message_details">Message details</string>
|
||||
|
@ -35,6 +35,7 @@ import android.os.Build;
|
||||
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;
|
||||
@ -85,6 +86,7 @@ import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||
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;
|
||||
@ -113,6 +115,7 @@ import org.thoughtcrime.securesms.util.DirectoryHelper;
|
||||
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 +291,16 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
switch (reqCode) {
|
||||
case PICK_IMAGE:
|
||||
addAttachmentImage(masterSecret, data.getData());
|
||||
setMedia(data.getData(),
|
||||
MediaUtil.isGif(MediaUtil.getMimeType(this, data.getData())) ? MediaType.GIF
|
||||
: MediaType.IMAGE,
|
||||
false);
|
||||
break;
|
||||
case PICK_VIDEO:
|
||||
addAttachmentVideo(data.getData());
|
||||
setMedia(data.getData(), MediaType.VIDEO, false);
|
||||
break;
|
||||
case PICK_AUDIO:
|
||||
addAttachmentAudio(data.getData());
|
||||
setMedia(data.getData(), MediaType.AUDIO, false);
|
||||
break;
|
||||
case PICK_CONTACT_INFO:
|
||||
addAttachmentContactInfo(data.getData());
|
||||
@ -308,7 +314,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
break;
|
||||
case TAKE_PHOTO:
|
||||
if (attachmentManager.getCaptureUri() != null) {
|
||||
addAttachmentImage(masterSecret, attachmentManager.getCaptureUri());
|
||||
setMedia(attachmentManager.getCaptureUri(), MediaType.IMAGE, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -671,9 +677,10 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
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) 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();
|
||||
@ -707,11 +714,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
if (draft.getType().equals(Draft.TEXT)) {
|
||||
composeText.setText(draft.getValue());
|
||||
} else if (draft.getType().equals(Draft.IMAGE)) {
|
||||
addAttachmentImage(masterSecret, Uri.parse(draft.getValue()));
|
||||
setMedia(Uri.parse(draft.getValue()), MediaType.IMAGE, false);
|
||||
} else if (draft.getType().equals(Draft.AUDIO)) {
|
||||
addAttachmentAudio(Uri.parse(draft.getValue()));
|
||||
setMedia(Uri.parse(draft.getValue()), MediaType.AUDIO, false);
|
||||
} else if (draft.getType().equals(Draft.VIDEO)) {
|
||||
addAttachmentVideo(Uri.parse(draft.getValue()));
|
||||
setMedia(Uri.parse(draft.getValue()), MediaType.VIDEO, false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -917,55 +924,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
}
|
||||
}
|
||||
|
||||
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 setMedia(Uri uri, MediaType mediaType, boolean isCapture) {
|
||||
attachmentManager.setMedia(masterSecret, uri, mediaType, getCurrentMediaConstraints(), isCapture);
|
||||
}
|
||||
|
||||
private void addAttachmentContactInfo(Uri contactUri) {
|
||||
@ -1132,6 +1092,12 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
return rawText;
|
||||
}
|
||||
|
||||
private MediaConstraints getCurrentMediaConstraints() {
|
||||
return sendButton.getSelectedTransport().getType() == Type.TEXTSECURE
|
||||
? MediaConstraints.PUSH_CONSTRAINTS
|
||||
: MediaConstraints.MMS_CONSTRAINTS;
|
||||
}
|
||||
|
||||
private void markThreadAsRead() {
|
||||
new AsyncTask<Long, Void, Void>() {
|
||||
@Override
|
||||
@ -1198,8 +1164,24 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
final Context context = getApplicationContext();
|
||||
SlideDeck slideDeck;
|
||||
|
||||
if (attachmentManager.isAttachmentPresent()) slideDeck = new SlideDeck(attachmentManager.getSlideDeck());
|
||||
else slideDeck = new SlideDeck();
|
||||
if (attachmentManager.isAttachmentPresent()) {
|
||||
Slide mediaSlide = attachmentManager.getSlideDeck().getThumbnailSlide();
|
||||
MediaConstraints constraints = getCurrentMediaConstraints();
|
||||
|
||||
if (mediaSlide != null &&
|
||||
!constraints.isSatisfied(this, masterSecret, mediaSlide.getPart()) &&
|
||||
!constraints.canResize(mediaSlide.getPart()))
|
||||
{
|
||||
Toast.makeText(context,
|
||||
R.string.ConversationActivity_attachment_exceeds_size_limits,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
slideDeck = new SlideDeck(attachmentManager.getSlideDeck());
|
||||
} else {
|
||||
slideDeck = new SlideDeck();
|
||||
}
|
||||
|
||||
OutgoingMediaMessage outgoingMessage = new OutgoingMediaMessage(this, recipients, slideDeck,
|
||||
getMessage(), distributionType);
|
||||
@ -1272,8 +1254,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
@Override
|
||||
public void onImageCapture(@NonNull final byte[] imageBytes) {
|
||||
attachmentManager.setCaptureUri(CaptureProvider.getInstance(this).create(masterSecret, recipients, imageBytes));
|
||||
addAttachmentImage(masterSecret, attachmentManager.getCaptureUri());
|
||||
setMedia(CaptureProvider.getInstance(this).create(masterSecret, recipients, imageBytes), MediaType.IMAGE, true);
|
||||
quickAttachmentDrawer.hide(false);
|
||||
}
|
||||
|
||||
@ -1397,4 +1378,5 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
initializeSecurity();
|
||||
updateToggleButtonState();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ public class ImageMediaAdapter extends CursorRecyclerViewAdapter<ViewHolder> {
|
||||
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);
|
||||
}
|
||||
|
@ -201,7 +201,12 @@ public class ThumbnailView extends FrameLayout {
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -209,6 +214,11 @@ public class ThumbnailView extends FrameLayout {
|
||||
if (hideControls) hideProgressWheel();
|
||||
}
|
||||
|
||||
public void showProgressSpinner() {
|
||||
getProgressWheel().spin();
|
||||
getProgressWheel().setVisibility(VISIBLE);
|
||||
}
|
||||
|
||||
@TargetApi(VERSION_CODES.JELLY_BEAN_MR1)
|
||||
private boolean isContextValid() {
|
||||
return !(getContext() instanceof Activity) ||
|
||||
|
@ -1094,7 +1094,7 @@ public class MmsDatabase extends MessagingDatabase {
|
||||
List<IdentityKeyMismatch> mismatches = getMismatchedIdentities(mismatchDocument);
|
||||
List<NetworkFailure> networkFailures = getFailures(networkDocument);
|
||||
|
||||
ListenableFutureTask<SlideDeck> slideDeck = getSlideDeck(masterSecret, dateReceived, id);
|
||||
ListenableFutureTask<SlideDeck> slideDeck = getSlideDeck(dateReceived, id);
|
||||
|
||||
return new MediaMmsMessageRecord(context, id, recipients, recipients.getPrimaryRecipient(),
|
||||
addressDeviceId, dateSent, dateReceived, receiptCount,
|
||||
@ -1159,8 +1159,7 @@ public class MmsDatabase extends MessagingDatabase {
|
||||
}
|
||||
}
|
||||
|
||||
private ListenableFutureTask<SlideDeck> getSlideDeck(final MasterSecret masterSecret,
|
||||
final long timestamp,
|
||||
private ListenableFutureTask<SlideDeck> getSlideDeck(final long timestamp,
|
||||
final long id)
|
||||
{
|
||||
ListenableFutureTask<SlideDeck> future = getCachedSlideDeck(timestamp, id);
|
||||
@ -1172,12 +1171,9 @@ public class MmsDatabase extends MessagingDatabase {
|
||||
Callable<SlideDeck> task = new Callable<SlideDeck>() {
|
||||
@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));
|
||||
|
@ -21,9 +21,11 @@ import android.content.ActivityNotFoundException;
|
||||
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,7 +38,6 @@ import org.thoughtcrime.securesms.components.ThumbnailView;
|
||||
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;
|
||||
@ -54,7 +55,7 @@ public class AttachmentManager {
|
||||
|
||||
public AttachmentManager(Activity view, AttachmentListener listener) {
|
||||
this.attachmentView = view.findViewById(R.id.attachment_editor);
|
||||
this.thumbnail = (ThumbnailView)view.findViewById(R.id.attachment_thumbnail);
|
||||
this.thumbnail = (ThumbnailView) view.findViewById(R.id.attachment_thumbnail);
|
||||
this.slideDeck = new SlideDeck();
|
||||
this.context = view;
|
||||
this.attachmentListener = listener;
|
||||
@ -70,6 +71,7 @@ public class AttachmentManager {
|
||||
@Override public void onAnimationRepeat(Animation animation) {}
|
||||
@Override public void onAnimationEnd(Animation animation) {
|
||||
slideDeck.clear();
|
||||
thumbnail.clear();
|
||||
attachmentView.setVisibility(View.GONE);
|
||||
attachmentListener.onAttachmentChanged();
|
||||
}
|
||||
@ -83,34 +85,55 @@ public class AttachmentManager {
|
||||
captureUri = null;
|
||||
}
|
||||
|
||||
public void setImage(MasterSecret masterSecret, Uri image)
|
||||
throws IOException, BitmapDecodingException, MediaTooLargeException
|
||||
public void setMedia(@NonNull final MasterSecret masterSecret,
|
||||
@NonNull final Uri uri,
|
||||
@NonNull final MediaType mediaType,
|
||||
@NonNull final MediaConstraints constraints,
|
||||
final boolean isCapture)
|
||||
{
|
||||
if (MediaUtil.isGif(MediaUtil.getMimeType(context, image))) {
|
||||
setMedia(new GifSlide(context, masterSecret, image), masterSecret);
|
||||
} else {
|
||||
setMedia(new ImageSlide(context, masterSecret, image), masterSecret);
|
||||
}
|
||||
}
|
||||
new AsyncTask<Void, Void, Slide>() {
|
||||
@Override protected void onPreExecute() {
|
||||
slideDeck.clear();
|
||||
thumbnail.clear();
|
||||
thumbnail.showProgressSpinner();
|
||||
attachmentView.setVisibility(View.VISIBLE);
|
||||
|
||||
public void setVideo(Uri video) throws IOException, MediaTooLargeException {
|
||||
setMedia(new VideoSlide(context, video));
|
||||
}
|
||||
if (isCapture) captureUri = uri;
|
||||
if (!uri.equals(captureUri)) cleanup();
|
||||
}
|
||||
|
||||
public void setAudio(Uri audio) throws IOException, MediaTooLargeException {
|
||||
setMedia(new AudioSlide(context, audio));
|
||||
}
|
||||
@Override protected @Nullable Slide doInBackground(Void... params) {
|
||||
long start = System.currentTimeMillis();
|
||||
try {
|
||||
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);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setMedia(final Slide slide) {
|
||||
setMedia(slide, 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) {
|
||||
attachmentView.setVisibility(View.GONE);
|
||||
Toast.makeText(context,
|
||||
R.string.ConversationActivity_sorry_there_was_an_error_setting_your_attachment,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
} 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();
|
||||
}
|
||||
|
||||
public boolean isAttachmentPresent() {
|
||||
@ -118,7 +141,7 @@ public class AttachmentManager {
|
||||
}
|
||||
|
||||
|
||||
public SlideDeck getSlideDeck() {
|
||||
public @NonNull SlideDeck getSlideDeck() {
|
||||
return slideDeck;
|
||||
}
|
||||
|
||||
@ -143,10 +166,6 @@ public class AttachmentManager {
|
||||
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);
|
||||
@ -183,6 +202,16 @@ public class AttachmentManager {
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -194,4 +223,22 @@ public class AttachmentManager {
|
||||
public interface AttachmentListener {
|
||||
void onAttachmentChanged();
|
||||
}
|
||||
|
||||
public enum MediaType {
|
||||
IMAGE, GIF, AUDIO, VIDEO;
|
||||
|
||||
public @NonNull Slide createSlide(@NonNull Context context,
|
||||
@NonNull Uri uri,
|
||||
long dataSize)
|
||||
throws IOException
|
||||
{
|
||||
switch (this) {
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,27 +18,25 @@ package org.thoughtcrime.securesms.mms;
|
||||
|
||||
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;
|
||||
|
||||
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, MediaTooLargeException {
|
||||
super(context, constructPartFromUri(context, uri));
|
||||
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
|
||||
@ -55,30 +53,4 @@ public class AudioSlide extends Slide {
|
||||
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, MediaTooLargeException {
|
||||
PduPart part = new PduPart();
|
||||
|
||||
assertMediaSize(context, uri, MmsMediaConstraints.MAX_MESSAGE_SIZE);
|
||||
|
||||
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.setDataUri(uri);
|
||||
part.setContentId((System.currentTimeMillis()+"").getBytes());
|
||||
part.setName(("Audio" + System.currentTimeMillis()).getBytes());
|
||||
|
||||
return part;
|
||||
}
|
||||
}
|
||||
|
@ -2,33 +2,18 @@ package org.thoughtcrime.securesms.mms;
|
||||
|
||||
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, BitmapDecodingException, MediaTooLargeException
|
||||
{
|
||||
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.");
|
||||
}
|
||||
public GifSlide(Context context, Uri uri, long dataSize) throws IOException {
|
||||
super(context, uri, dataSize);
|
||||
}
|
||||
|
||||
@Override public Uri getThumbnailUri() {
|
||||
|
@ -22,9 +22,6 @@ import android.net.Uri;
|
||||
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 @@ import ws.com.google.android.mms.pdu.PduPart;
|
||||
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, BitmapDecodingException {
|
||||
super(context, masterSecret, constructPartFromUri(context, uri));
|
||||
public ImageSlide(Context context, Uri uri, long size) throws IOException {
|
||||
super(context, constructPartFromUri(context, uri, ContentType.IMAGE_JPEG, size));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -62,20 +59,4 @@ public class ImageSlide extends Slide {
|
||||
public boolean hasImage() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static PduPart constructPartFromUri(Context context, Uri uri)
|
||||
throws IOException, BitmapDecodingException
|
||||
{
|
||||
PduPart part = new PduPart();
|
||||
|
||||
final String mimeType = MediaUtil.getMimeType(context, 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -27,16 +27,16 @@ public class PushMediaConstraints extends MediaConstraints {
|
||||
|
||||
@Override
|
||||
public int getGifMaxSize() {
|
||||
return 1 * MB;
|
||||
return 5 * MB;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVideoMaxSize() {
|
||||
return MmsMediaConstraints.MAX_MESSAGE_SIZE;
|
||||
return 100 * MB;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAudioMaxSize() {
|
||||
return MmsMediaConstraints.MAX_MESSAGE_SIZE;
|
||||
return 100 * MB;
|
||||
}
|
||||
}
|
||||
|
@ -21,8 +21,10 @@ import android.content.res.Resources.Theme;
|
||||
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;
|
||||
@ -32,20 +34,14 @@ import ws.com.google.android.mms.pdu.PduPart;
|
||||
|
||||
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());
|
||||
}
|
||||
@ -90,18 +86,24 @@ public abstract class Slide {
|
||||
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;
|
||||
|
||||
while ((read = in.read(buffer)) != -1) {
|
||||
size += read;
|
||||
if (size > max) throw new MediaTooLargeException("Media exceeds maximum message size.");
|
||||
}
|
||||
protected static PduPart constructPartFromUri(@NonNull Context context,
|
||||
@NonNull Uri uri,
|
||||
@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(dataSize);
|
||||
part.setDataUri(uri);
|
||||
part.setContentType(derivedMimeType.getBytes());
|
||||
part.setContentId((System.currentTimeMillis()+"").getBytes());
|
||||
part.setName((MediaUtil.getDiscreteMimeType(derivedMimeType) + System.currentTimeMillis()).getBytes());
|
||||
|
||||
return part;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -125,7 +127,4 @@ public abstract class Slide {
|
||||
return Util.hashCode(getContentType(), hasAudio(), hasImage(),
|
||||
hasVideo(), isDraft(), getUri(), getThumbnailUri(), getTransferProgress());
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -47,10 +47,10 @@ public class SlideDeck {
|
||||
this.slides.addAll(copy.getSlides());
|
||||
}
|
||||
|
||||
public SlideDeck(Context context, MasterSecret masterSecret, PduBody body) {
|
||||
public SlideDeck(Context context, PduBody body) {
|
||||
for (int i=0;i<body.getPartsNum();i++) {
|
||||
String contentType = Util.toIsoString(body.getPart(i).getContentType());
|
||||
Slide slide = MediaUtil.getSlideForPart(context, masterSecret, body.getPart(i), contentType);
|
||||
Slide slide = MediaUtil.getSlideForPart(context, body.getPart(i), contentType);
|
||||
if (slide != null) slides.add(slide);
|
||||
}
|
||||
}
|
||||
|
@ -16,31 +16,27 @@
|
||||
*/
|
||||
package org.thoughtcrime.securesms.mms;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources.Theme;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.provider.MediaStore;
|
||||
import android.support.annotation.DrawableRes;
|
||||
import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.util.ResUtil;
|
||||
|
||||
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, MediaTooLargeException {
|
||||
super(context, constructPartFromUri(context, uri));
|
||||
public VideoSlide(Context context, Uri uri, long dataSize) throws IOException {
|
||||
super(context, constructPartFromUri(context, uri, ContentType.VIDEO_UNSPECIFIED, dataSize));
|
||||
}
|
||||
|
||||
public VideoSlide(Context context, MasterSecret masterSecret, PduPart part) {
|
||||
super(context, masterSecret, part);
|
||||
public VideoSlide(Context context, PduPart part) {
|
||||
super(context, part);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -57,30 +53,4 @@ public class VideoSlide extends Slide {
|
||||
public boolean hasVideo() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static PduPart constructPartFromUri(Context context, Uri uri)
|
||||
throws IOException, MediaTooLargeException
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
assertMediaSize(context, uri, MmsMediaConstraints.MAX_MESSAGE_SIZE);
|
||||
part.setDataUri(uri);
|
||||
part.setContentId((System.currentTimeMillis()+"").getBytes());
|
||||
part.setName(("Video" + System.currentTimeMillis()).getBytes());
|
||||
|
||||
return part;
|
||||
}
|
||||
}
|
||||
|
@ -66,16 +66,16 @@ public class MediaUtil {
|
||||
return BitmapUtil.createScaledBitmap(context, new DecryptableUri(masterSecret, uri), maxSize, maxSize);
|
||||
}
|
||||
|
||||
public static Slide getSlideForPart(Context context, MasterSecret masterSecret, PduPart part, String contentType) {
|
||||
public static Slide getSlideForPart(Context context, PduPart part, String contentType) {
|
||||
Slide slide = null;
|
||||
if (isGif(contentType)) {
|
||||
slide = new GifSlide(context, masterSecret, part);
|
||||
slide = new GifSlide(context, part);
|
||||
} else if (ContentType.isImageType(contentType)) {
|
||||
slide = new ImageSlide(context, masterSecret, part);
|
||||
slide = new ImageSlide(context, part);
|
||||
} else if (ContentType.isVideoType(contentType)) {
|
||||
slide = new VideoSlide(context, masterSecret, part);
|
||||
slide = new VideoSlide(context, part);
|
||||
} else if (ContentType.isAudioType(contentType)) {
|
||||
slide = new AudioSlide(context, masterSecret, part);
|
||||
slide = new AudioSlide(context, part);
|
||||
}
|
||||
|
||||
return slide;
|
||||
@ -90,6 +90,22 @@ public class MediaUtil {
|
||||
return type;
|
||||
}
|
||||
|
||||
public 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[4096];
|
||||
int read;
|
||||
|
||||
while ((read = in.read(buffer)) != -1) {
|
||||
size += read;
|
||||
}
|
||||
in.close();
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
public static boolean isGif(String contentType) {
|
||||
return !TextUtils.isEmpty(contentType) && contentType.trim().equals("image/gif");
|
||||
}
|
||||
@ -111,7 +127,11 @@ public class MediaUtil {
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user