Commit 5e3938b7 authored by vdveer's avatar vdveer Committed by Bastien Le Querrec

File transfer in secure transport

Closes #365
parent 03c3e9b6
......@@ -74,6 +74,7 @@
<data android:mimeType="image/*" />
<data android:mimeType="text/plain" />
<data android:mimeType="video/*" />
<data android:mimeType="*/*" />
</intent-filter>
</activity>
......
......@@ -40,7 +40,7 @@
<attr name="conversation_attach_video" format="reference"/>
<attr name="conversation_attach_sound" format="reference"/>
<attr name="conversation_attach_contact_info" format="reference"/>
<attr name="conversation_attach" format="reference"/>
<attr name="conversation_attach_file" format="reference"/>
<attr name="emoji_tab_strip_background" format="color" />
<attr name="emoji_tab_indicator" format="color" />
......@@ -71,6 +71,7 @@
<attr name="conversation_icon_attach_audio" format="reference"/>
<attr name="conversation_icon_attach_video" format="reference" />
<attr name="conversation_icon_attach_file" format="reference" />
<attr name="contact_selection_push_user" format="reference|color" />
<attr name="contact_selection_lay_user" format="reference|color" />
......
......@@ -49,6 +49,7 @@
<string name="DraftDatabase_Draft_image_snippet">(image)</string>
<string name="DraftDatabase_Draft_audio_snippet">(audio)</string>
<string name="DraftDatabase_Draft_video_snippet">(video)</string>
<string name="DraftDatabase_Draft_file_snippet">(file)</string>
<!-- AttchmentManager -->
<string name="AttachmentManager_cant_open_media_selection">Can\'t find an app to select media.</string>
......@@ -58,6 +59,7 @@
<string name="AttachmentTypeSelectorAdapter_picture">Picture</string>
<string name="AttachmentTypeSelectorAdapter_video">Video</string>
<string name="AttachmentTypeSelectorAdapter_audio">Audio</string>
<string name="AttachmentTypeSelectorAdapter_file">File</string>
<string name="AttachmentTypeSelectorAdapter_contact">Contact info</string>
<!-- BlockedContactsActivity -->
......@@ -118,6 +120,7 @@
<string name="ConversationActivity_mms_not_supported_title">MMS not supported</string>
<string name="ConversationActivity_mms_not_supported_message">This message cannot be sent since your carrier doesn\'t support MMS.</string>
<string name="ConversationActivity_specify_recipient">Please choose a contact</string>
<string name="ConversationActivity_sorry_no_files_attaching_on_insecure_chat">Sorry, files cannot be attached to insecure conversations.</string>
<string name="ConversationActivity_attachment_exceeds_size_limits">Attachment exceeds size limits.</string>
<!-- ConversationFragment -->
......
......@@ -116,7 +116,7 @@
<item name="conversation_attach_video">@drawable/ic_movie_creation_light</item>
<item name="conversation_attach_sound">@drawable/ic_volume_up_light</item>
<item name="conversation_attach_contact_info">@drawable/ic_account_box_light</item>
<item name="conversation_attach">@drawable/ic_attach_grey600_24dp</item>
<item name="conversation_attach_file">@drawable/ic_file_attachment_light</item>
<item name="emoji_tab_strip_background">@color/gray12</item>
<item name="emoji_tab_indicator">#66555555</item>
......@@ -172,6 +172,7 @@
<item name="conversation_icon_attach_audio">@drawable/ic_audio_light</item>
<item name="conversation_icon_attach_video">@drawable/ic_video_light</item>
<item name="conversation_icon_attach_file">@drawable/ic_file_light</item>
<item name="reminder_header_background">#ff1d85d7</item>
......@@ -248,7 +249,7 @@
<item name="conversation_attach_video">@drawable/ic_movie_creation_dark</item>
<item name="conversation_attach_sound">@drawable/ic_volume_up_dark</item>
<item name="conversation_attach_contact_info">@drawable/ic_account_box_dark</item>
<item name="conversation_attach">@drawable/ic_attach_white_24dp</item>
<item name="conversation_attach_file">@drawable/ic_file_attachment_dark</item>
<item name="emoji_tab_strip_background">@color/gray95</item>
<item name="emoji_tab_indicator">@color/gray65</item>
......@@ -285,6 +286,7 @@
<item name="conversation_icon_attach_audio">@drawable/ic_audio_dark</item>
<item name="conversation_icon_attach_video">@drawable/ic_video_dark</item>
<item name="conversation_icon_attach_file">@drawable/ic_file_dark</item>
<item name="reminder_header_background">@color/smssecure_primary_dark</item>
......
......@@ -147,6 +147,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
public static final String THREAD_ID_EXTRA = "thread_id";
public static final String IS_ARCHIVED_EXTRA = "is_archived";
public static final String TEXT_EXTRA = "draft_text";
public static final String FILENAME_EXTRA = "filename";
public static final String DISTRIBUTION_TYPE_EXTRA = "distribution_type";
private static final int PICK_IMAGE = 1;
......@@ -156,6 +157,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private static final int GROUP_EDIT = 5;
private static final int TAKE_PHOTO = 6;
private static final int ADD_CONTACT = 7;
private static final int PICK_FILE = 8;
private MasterSecret masterSecret;
protected ComposeText composeText;
......@@ -314,6 +316,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
recipients.addListener(this);
fragment.reloadList();
break;
case PICK_FILE:
setMedia(data.getData(), MediaType.FILE);
break;
}
}
......@@ -678,10 +683,21 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private void initializeDraft() {
final String draftText = getIntent().getStringExtra(TEXT_EXTRA);
final Uri draftMedia = getIntent().getData();
final String draftFilename = getIntent().getStringExtra(FILENAME_EXTRA);
final MediaType draftMediaType = MediaType.from(getIntent().getType());
if (draftText != null) composeText.setText(draftText);
if (draftMedia != null && draftMediaType != null) setMedia(draftMedia, draftMediaType);
if (draftMedia != null && draftMediaType != null){
Recipient primaryRecipient = getRecipients() == null ? null : getRecipients().getPrimaryRecipient();
boolean isSecureSmsDestination = isSingleConversation() && SessionUtil.hasSession(this, masterSecret, primaryRecipient);
if (draftMediaType.equals(MediaType.FILE) && !isSecureSmsDestination) {
Toast.makeText(this, R.string.ConversationActivity_sorry_no_files_attaching_on_insecure_chat, Toast.LENGTH_LONG).show();
} else if (draftFilename != null) {
setMedia(draftMedia, draftMediaType, draftFilename);
} else {
setMedia(draftMedia, draftMediaType);
}
}
if (draftText == null && draftMedia == null && draftMediaType == null) {
initializeDraftFromDatabase();
......@@ -720,6 +736,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
setMedia(Uri.parse(draft.getValue()), MediaType.AUDIO);
} else if (draft.getType().equals(Draft.VIDEO)) {
setMedia(Uri.parse(draft.getValue()), MediaType.VIDEO);
} else if (draft.getType().equals(Draft.FILE)) {
setMedia(Uri.parse(draft.getValue()) , MediaType.FILE);
}
}
......@@ -739,7 +757,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} else {
this.isEncryptedConversation = false;
}
attachmentAdapter.setSecureDestination(isSecureSmsDestination);
sendButton.resetAvailableTransports(isMediaMessage);
if (!isSecureSmsDestination ) sendButton.disableTransport(Type.SECURE_SMS);
if (recipients.isGroupRecipient()) sendButton.disableTransport(Type.INSECURE_SMS);
......@@ -806,7 +824,10 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
composeBubble.getBackground().setColorFilter(defaultColor, PorterDuff.Mode.MULTIPLY);
colors.recycle();
attachmentAdapter = new AttachmentTypeSelectorAdapter(this);
Recipient primaryRecipient = getRecipients() == null ? null : getRecipients().getPrimaryRecipient();
boolean isSecureSmsDestination = isSingleConversation() && SessionUtil.hasSession(this, masterSecret, primaryRecipient);
attachmentAdapter = new AttachmentTypeSelectorAdapter(this, isSecureSmsDestination);
attachmentManager = new AttachmentManager(this, this);
SendButtonListener sendButtonListener = new SendButtonListener();
......@@ -952,6 +973,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
AttachmentManager.selectContactInfo(this, PICK_CONTACT_INFO); break;
case AttachmentTypeSelectorAdapter.TAKE_PHOTO:
attachmentManager.capturePhoto(this, TAKE_PHOTO); break;
case AttachmentTypeSelectorAdapter.ADD_FILE:
attachmentManager.selectFile(masterSecret, this, PICK_FILE); break;
}
}
......@@ -959,6 +982,10 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
attachmentManager.setMedia(masterSecret, uri, mediaType, getCurrentMediaConstraints());
}
private void setMedia(Uri uri, MediaType mediaType, String fileName) {
attachmentManager.setMedia(masterSecret, uri, mediaType, getCurrentMediaConstraints(), fileName);
}
private void addAttachmentContactInfo(Uri contactUri) {
ContactAccessor contactDataList = ContactAccessor.getInstance();
ContactData contactData = contactDataList.getContactData(this, contactUri);
......@@ -999,6 +1026,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
for (Slide slide : attachmentManager.buildSlideDeck().getSlides()) {
if (slide.hasAudio()) drafts.add(new Draft(Draft.AUDIO, slide.getUri().toString()));
else if (slide.hasVideo()) drafts.add(new Draft(Draft.VIDEO, slide.getUri().toString()));
else if (slide.hasFile() ) drafts.add(new Draft(Draft.FILE, slide.getUri().toString()));
else if (slide.hasImage()) drafts.add(new Draft(Draft.IMAGE, slide.getUri().toString()));
}
......
......@@ -340,9 +340,9 @@ public class ConversationFragment extends Fragment
SaveAttachmentTask.showWarningDialog(getActivity(), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
for (Slide slide : message.getSlideDeck().getSlides()) {
if (slide.hasImage() || slide.hasVideo() || slide.hasAudio()) {
if (slide.hasImage() || slide.hasVideo() || slide.hasAudio() || slide.hasFile()) {
SaveAttachmentTask saveTask = new SaveAttachmentTask(getActivity(), masterSecret);
saveTask.execute(new Attachment(slide.getUri(), slide.getContentType(), message.getDateReceived()));
saveTask.execute(new Attachment(slide.getUri(), slide.getContentType(), message.getDateReceived(), slide.getFileName()));
return;
}
}
......
......@@ -63,16 +63,20 @@ import org.smssecure.smssecure.protocol.AutoInitiate;
import org.smssecure.smssecure.recipients.Recipient;
import org.smssecure.smssecure.recipients.Recipients;
import org.smssecure.smssecure.util.DateUtils;
import org.smssecure.smssecure.util.SaveAttachmentTask;
import org.smssecure.smssecure.util.TelephonyUtil;
import org.smssecure.smssecure.util.Util;
import org.smssecure.smssecure.util.dualsim.SubscriptionInfoCompat;
import org.smssecure.smssecure.util.dualsim.SubscriptionManagerCompat;
import org.whispersystems.libaxolotl.util.guava.Optional;
import java.util.Date;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import ws.com.google.android.mms.ContentType;
/**
* A view that displays an individual conversation item within a conversation
* thread. Used by ComposeMessageActivity's ListActivity via a ConversationAdapter.
......@@ -525,6 +529,15 @@ public class ConversationItem extends LinearLayout
intent.putExtra(MediaPreviewActivity.DATE_EXTRA, messageRecord.getTimestamp());
context.startActivity(intent);
} else if (slide.hasFile()) {
SaveAttachmentTask.showWarningDialog(context, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
SaveAttachmentTask saveTask = new SaveAttachmentTask(context, masterSecret);
saveTask.execute(new SaveAttachmentTask.Attachment(slide.getUri(), slide.getContentType(), new Date().getTime(), slide.getFileName()));
return;
}
});
} else {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.ConversationItem_view_secure_media_question);
......
......@@ -164,7 +164,7 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity i
ImageRecord record = ImageRecord.from(cursor);
attachments.add(new SaveAttachmentTask.Attachment(record.getAttachment().getDataUri(),
record.getContentType(),
record.getDate()));
record.getDate(), null));
}
return attachments;
......
......@@ -171,7 +171,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
@Override
public void onClick(DialogInterface dialogInterface, int i) {
SaveAttachmentTask saveTask = new SaveAttachmentTask(MediaPreviewActivity.this, masterSecret);
saveTask.execute(new Attachment(mediaUri, mediaType, date));
saveTask.execute(new Attachment(mediaUri, mediaType, date, null));
}
});
}
......
......@@ -31,6 +31,7 @@ import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import org.smssecure.smssecure.attachments.UriAttachment;
import org.smssecure.smssecure.crypto.MasterSecret;
import org.smssecure.smssecure.mms.PartAuthority;
import org.smssecure.smssecure.providers.PersistentBlobProvider;
......@@ -43,6 +44,8 @@ import org.smssecure.smssecure.util.ViewUtil;
import java.io.IOException;
import java.io.InputStream;
import ws.com.google.android.mms.ContentType;
/**
* An activity to quickly share content with contacts
*
......@@ -62,6 +65,7 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
private Uri resolvedExtra;
private String mimeType;
private boolean isPassingAlongMedia;
private String resolvedFilename;
@Override
protected void onPreCreate() {
......@@ -112,7 +116,9 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
isPassingAlongMedia = false;
Uri streamExtra = getIntent().getParcelableExtra(Intent.EXTRA_STREAM);
mimeType = getMimeType(streamExtra);
mimeType = streamExtra != null && !ContentType.isTextType(getMimeType(streamExtra)) ? getMimeType(streamExtra)
: ContentType.SMS_SECURE_FILE;
resolvedFilename = UriAttachment.getFilenameFromUri(streamExtra, context);
if (streamExtra != null && PartAuthority.isLocalUri(streamExtra)) {
isPassingAlongMedia = true;
resolvedExtra = streamExtra;
......@@ -171,6 +177,7 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
final String textExtra = getIntent().getStringExtra(Intent.EXTRA_TEXT);
intent.putExtra(ConversationActivity.TEXT_EXTRA, textExtra);
if (resolvedExtra != null) intent.setDataAndType(resolvedExtra, mimeType);
if (resolvedFilename != null) intent.putExtra(ConversationActivity.FILENAME_EXTRA, resolvedFilename);
return intent;
}
......
......@@ -13,6 +13,7 @@ public abstract class Attachment {
private final String contentType;
private final int transferState;
private final long size;
private final String fileName;
@Nullable
private final String location;
......@@ -28,7 +29,7 @@ public abstract class Attachment {
private Bitmap thumbnail;
public Attachment(@NonNull String contentType, int transferState, long size,
@Nullable String location, @Nullable String key, @Nullable String relay)
@Nullable String location, @Nullable String key, @Nullable String relay, @Nullable String fileName)
{
this.contentType = contentType;
this.transferState = transferState;
......@@ -36,6 +37,7 @@ public abstract class Attachment {
this.location = location;
this.key = key;
this.relay = relay;
this.fileName = fileName;
}
@Nullable
......@@ -85,4 +87,9 @@ public abstract class Attachment {
public Bitmap getThumbnail() {
return thumbnail;
}
@Nullable
public String getFileName(){
return fileName;
}
}
......@@ -13,14 +13,21 @@ public class DatabaseAttachment extends Attachment {
public DatabaseAttachment(AttachmentId attachmentId, long mmsId, boolean hasData,
String contentType, int transferProgress, long size,
String location, String key, String relay)
String location, String key, String relay, String fileName)
{
super(contentType, transferProgress, size, location, key, relay);
super(contentType, transferProgress, size, location, key, relay, fileName);
this.attachmentId = attachmentId;
this.hasData = hasData;
this.mmsId = mmsId;
}
public DatabaseAttachment(AttachmentId attachmentId, long mmsId, boolean hasData,
String contentType, int transferProgress, long size,
String location, String key, String relay)
{
this(attachmentId, mmsId, hasData, contentType, transferProgress, size, location, key, relay, null);
}
@Override
@NonNull
public Uri getDataUri() {
......
package org.smssecure.smssecure.attachments;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.OpenableColumns;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import org.smssecure.smssecure.crypto.MasterSecret;
import org.smssecure.smssecure.util.MediaUtil;
import org.whispersystems.libaxolotl.util.guava.Optional;
import java.io.IOException;
import java.io.InputStream;
import ws.com.google.android.mms.ContentType;
public class UriAttachment extends Attachment {
private static final String TAG = UriAttachment.class.getSimpleName();
private final @NonNull Uri dataUri;
private final @NonNull Uri thumbnailUri;
public UriAttachment(@NonNull Uri uri, @NonNull String contentType, int transferState, long size) {
this(uri, uri, contentType, transferState, size);
public UriAttachment(@NonNull Uri uri, @NonNull String contentType, int transferState, long size, Context context) {
this(uri, uri, contentType, transferState, size, UriAttachment.getFilenameFromUri(uri, context));
}
public UriAttachment(@NonNull Uri uri, @NonNull String contentType, int transferState, long size, String inputFilename) {
this(uri, uri, contentType, transferState, size, inputFilename);
}
public UriAttachment(@NonNull Uri dataUri, @NonNull Uri thumbnailUri,
@NonNull String contentType, int transferState, long size)
@NonNull String contentType, int transferState, long size, @Nullable String fileName)
{
super(contentType, transferState, size, null, null, null);
super(contentType, transferState, size, null, null, null, fileName);
this.dataUri = dataUri;
this.thumbnailUri = thumbnailUri;
if(!ContentType.isVendorFileType(contentType)) {
this.thumbnailUri = thumbnailUri;
} else {
this.thumbnailUri = null;
}
}
@Override
......@@ -49,4 +58,27 @@ public class UriAttachment extends Attachment {
public int hashCode() {
return dataUri.hashCode();
}
public static String getFilenameFromUri(Uri uri, Context context) {
String fileName = null;
if (uri != null && uri.getScheme() != null && uri.getScheme().equals("content")) {
Log.w(TAG, "contenturi: "+uri.toString());
Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
try {
if (cursor != null && cursor.moveToFirst()) {
fileName = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
}
} finally {
if (cursor != null) cursor.close();
}
}
if (fileName == null) {
fileName = uri.getPath();
int cut = fileName != null ? fileName.lastIndexOf('/') : -1;
if (cut != -1) {
fileName = fileName.substring(cut + 1);
}
}
return fileName;
}
}
......@@ -118,9 +118,9 @@ public class ThumbnailView extends FrameLayout {
this.slide = slide;
if (slide.getThumbnailUri() != null) buildThumbnailGlideRequest(slide, masterSecret).into(image);
else if (slide.hasPlaceholder()) buildPlaceholderGlideRequest(slide).into(image);
else Glide.clear(image);
if (slide.getThumbnailUri() != null && !slide.hasFile()) buildThumbnailGlideRequest(slide, masterSecret).into(image);
else if (slide.hasPlaceholder()) buildPlaceholderGlideRequest(slide).into(image);
else Glide.clear(image);
}
public void setImageResource(@NonNull MasterSecret masterSecret, @NonNull Uri uri) {
......
......@@ -65,6 +65,7 @@ public class AttachmentDatabase extends Database {
static final String MMS_ID = "mid";
static final String CONTENT_TYPE = "ct";
static final String NAME = "name";
static final String FILENAME = "filename";
static final String CONTENT_DISPOSITION = "cd";
static final String CONTENT_LOCATION = "cl";
static final String DATA = "_data";
......@@ -85,11 +86,11 @@ public class AttachmentDatabase extends Database {
MMS_ID, CONTENT_TYPE, NAME, CONTENT_DISPOSITION,
CONTENT_LOCATION, DATA, TRANSFER_STATE,
SIZE, THUMBNAIL, THUMBNAIL_ASPECT_RATIO,
UNIQUE_ID};
UNIQUE_ID, FILENAME};
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ROW_ID + " INTEGER PRIMARY KEY, " +
MMS_ID + " INTEGER, " + "seq" + " INTEGER DEFAULT 0, " +
CONTENT_TYPE + " TEXT, " + NAME + " TEXT, " + "chset" + " INTEGER, " +
CONTENT_TYPE + " TEXT, " + NAME + " TEXT, " + FILENAME + " TEXT, " + "chset" + " INTEGER, " +
CONTENT_DISPOSITION + " TEXT, " + "fn" + " TEXT, " + "cid" + " TEXT, " +
CONTENT_LOCATION + " TEXT, " + "ctt_s" + " INTEGER, " +
"ctt_t" + " TEXT, " + "encrypted" + " INTEGER, " +
......@@ -260,6 +261,7 @@ public class AttachmentDatabase extends Database {
values.put(CONTENT_LOCATION, (String)null);
values.put(CONTENT_DISPOSITION, (String)null);
values.put(NAME, (String) null);
values.put(FILENAME, (String) null);
if (database.update(TABLE_NAME, values, PART_ID_WHERE, attachmentId.toStrings()) == 0) {
//noinspection ResultOfMethodCallIgnored
......@@ -305,6 +307,9 @@ public class AttachmentDatabase extends Database {
ContentValues contentValues = new ContentValues();
contentValues.put(SIZE, dataSize);
contentValues.put(CONTENT_TYPE, mediaStream.getMimeType());
if (attachment.getFileName() != null) {
contentValues.put(FILENAME, databaseAttachment.getFileName());
}
database.update(TABLE_NAME, contentValues, PART_ID_WHERE, databaseAttachment.getAttachmentId().toStrings());
......@@ -316,7 +321,8 @@ public class AttachmentDatabase extends Database {
dataSize,
databaseAttachment.getLocation(),
databaseAttachment.getKey(),
databaseAttachment.getRelay());
databaseAttachment.getRelay(),
databaseAttachment.getFileName());
}
......@@ -437,7 +443,8 @@ public class AttachmentDatabase extends Database {
cursor.getLong(cursor.getColumnIndexOrThrow(SIZE)),
cursor.getString(cursor.getColumnIndexOrThrow(CONTENT_LOCATION)),
cursor.getString(cursor.getColumnIndexOrThrow(CONTENT_DISPOSITION)),
cursor.getString(cursor.getColumnIndexOrThrow(NAME)));
cursor.getString(cursor.getColumnIndexOrThrow(NAME)),
cursor.getString(cursor.getColumnIndexOrThrow(FILENAME)));
}
......@@ -463,7 +470,9 @@ public class AttachmentDatabase extends Database {
contentValues.put(CONTENT_LOCATION, attachment.getLocation());
contentValues.put(CONTENT_DISPOSITION, attachment.getKey());
contentValues.put(NAME, attachment.getRelay());
if (attachment.getFileName() != null) {
contentValues.put(FILENAME, attachment.getFileName());
}
if (partData != null) {
contentValues.put(DATA, partData.first.getAbsolutePath());
contentValues.put(SIZE, partData.second);
......