Commit cb006969 authored by Moxie Marlinspike's avatar Moxie Marlinspike Committed by Bastien Le Querrec

Support for receiving arbitrary attachment types

// FREEBIE

Upstream commit: https://github.com/signalapp/Signal-Android/commit/f67eb5f9f3efc688cec1d1690bd379d18e5001b0
parent 16a6238b
......@@ -33,6 +33,17 @@
app:foregroundTintColor="@color/grey_500"
app:backgroundTintColor="@color/white"/>
<org.smssecure.smssecure.components.DocumentView
android:id="@+id/attachment_document"
android:layout_width="210dp"
android:layout_height="wrap_content"
android:visibility="gone"
android:paddingTop="15dp"
android:paddingBottom="15dp"
app:documentWidgetBackground="?conversation_item_bubble_background"
app:documentForegroundTintColor="@color/grey_500"
app:documentBackgroundTintColor="?conversation_item_bubble_background"/>
</org.smssecure.smssecure.components.RemovableMediaView>
</FrameLayout>
......@@ -65,6 +65,11 @@
android:layout_width="210dp"
android:layout_height="wrap_content"/>
<ViewStub android:id="@+id/document_view_stub"
android:layout="@layout/conversation_item_received_document"
android:layout_width="210dp"
android:layout_height="wrap_content"/>
<org.smssecure.smssecure.components.emoji.EmojiTextView
android:id="@+id/conversation_item_body"
android:layout_width="wrap_content"
......
<?xml version="1.0" encoding="utf-8"?>
<org.smssecure.smssecure.components.DocumentView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/document_view"
android:layout_width="210dp"
android:layout_height="wrap_content"
android:visibility="gone"
app:documentForegroundTintColor="@color/white"
app:documentBackgroundTintColor="@color/blue_500"
tools:visibility="visible"/>
......@@ -57,6 +57,11 @@
android:layout_width="210dp"
android:layout_height="wrap_content"/>
<ViewStub android:id="@+id/document_view_stub"
android:layout="@layout/conversation_item_sent_document"
android:layout_width="210dp"
android:layout_height="wrap_content"/>
<org.smssecure.smssecure.components.emoji.EmojiTextView
android:id="@+id/conversation_item_body"
android:layout_width="wrap_content"
......
<?xml version="1.0" encoding="utf-8"?>
<org.smssecure.smssecure.components.DocumentView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/document_view"
android:layout_width="210dp"
android:layout_height="wrap_content"
app:documentForegroundTintColor="@color/grey_500"
app:documentBackgroundTintColor="@color/white"
android:visibility="gone"
tools:visibility="visible"/>
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context="org.smssecure.smssecure.components.DocumentView">
<LinearLayout android:id="@+id/document_container"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:clickable="false"
android:focusable="false"
android:orientation="horizontal">
<org.smssecure.smssecure.components.AnimatingToggle
android:id="@+id/control_toggle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:clickable="false"
android:focusable="false"
android:gravity="center">
<com.pnikosis.materialishprogress.ProgressWheel
android:id="@+id/download_progress"
android:layout_width="48dp"
android:layout_height="48dp"
android:visibility="gone"
android:clickable="false"
android:layout_gravity="center"
app:matProg_barColor="@color/white"
app:matProg_linearProgress="true"
app:matProg_spinSpeed="0.333"
tools:visibility="gone"/>
<FrameLayout android:id="@+id/document_background"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:gravity="center_vertical"
android:background="@drawable/ic_circle_fill_white_48dp"
android:visibility="visible"
android:clickable="false"
android:focusable="false"
tools:backgroundTint="@color/blue_400">
<TextView android:id="@+id/document"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:clickable="false"
android:visibility="visible"
android:background="@drawable/ic_insert_drive_file_white_24dp"
android:textAlignment="center"
android:scaleType="centerInside"
android:textAllCaps="true"
android:textSize="8sp"
android:paddingTop="8dp"
android:typeface="monospace"
tools:visibility="visible"
tools:text="PDF"
tools:textColor="@color/blue_400"/>
</FrameLayout>
<ImageView android:id="@+id/download"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:clickable="true"
android:visibility="gone"
android:background="@drawable/circle_touch_highlight_background"
android:src="@drawable/ic_download_circle_fill_white_48dp"
android:contentDescription="@string/audio_view__download_accessibility_description"/>
</org.smssecure.smssecure.components.AnimatingToggle>
<LinearLayout android:orientation="vertical"
android:layout_marginLeft="7dp"
android:layout_gravity="center_vertical"
android:layout_width="match_parent"
android:focusable="false"
android:clickable="false"
android:layout_height="wrap_content">
<TextView android:id="@+id/file_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textStyle="bold"
android:singleLine="true"
android:maxLines="1"
android:clickable="false"
android:ellipsize="end"
tools:text="The-Anarchist-Tension-by-Alfredo-Bonanno.pdf"/>
<TextView android:id="@+id/file_size"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:textSize="12sp"
android:clickable="false"
tools:text="24kb"/>
</LinearLayout>
</LinearLayout>
</merge>
......@@ -153,4 +153,10 @@
<attr name="backgroundTintColor" format="color" />
</declare-styleable>
<declare-styleable name="DocumentView">
<attr name="documentWidgetBackground" format="color"/>
<attr name="documentForegroundTintColor" format="color" />
<attr name="documentBackgroundTintColor" format="color" />
</declare-styleable>
</resources>
......@@ -61,6 +61,7 @@
<string name="AttachmentTypeSelectorAdapter_picture">Picture</string>
<string name="AttachmentTypeSelectorAdapter_video">Video</string>
<string name="AttachmentTypeSelectorAdapter_audio">Audio</string>
<string name="AttachmentTypeSelectorAdapter_documents">Documents</string>
<string name="AttachmentTypeSelectorAdapter_contact">Contact info</string>
<!-- AudioSlidePlayer -->
......@@ -892,6 +893,8 @@
<!-- transport_selection_list_item -->
<string name="transport_selection_list_item__transport_icon">Transport icon</string>
<string name="SaveAttachmentTask_open_directory">Open Directory</string>
<string name="DocumentView_unknown_file">Unknown file</string>
<!-- EOF -->
......
......@@ -79,11 +79,13 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
private final Map<String,SoftReference<MessageRecord>> messageRecordCache =
Collections.synchronizedMap(new LRUCache<String, SoftReference<MessageRecord>>(MAX_CACHE_SIZE));
private static final int MESSAGE_TYPE_OUTGOING = 0;
private static final int MESSAGE_TYPE_INCOMING = 1;
private static final int MESSAGE_TYPE_UPDATE = 2;
private static final int MESSAGE_TYPE_AUDIO_OUTGOING = 3;
private static final int MESSAGE_TYPE_AUDIO_INCOMING = 4;
private static final int MESSAGE_TYPE_OUTGOING = 0;
private static final int MESSAGE_TYPE_INCOMING = 1;
private static final int MESSAGE_TYPE_UPDATE = 2;
private static final int MESSAGE_TYPE_AUDIO_OUTGOING = 3;
private static final int MESSAGE_TYPE_AUDIO_INCOMING = 4;
private static final int MESSAGE_TYPE_DOCUMENT_OUTGOING = 5;
private static final int MESSAGE_TYPE_DOCUMENT_INCOMING = 6;
private final Set<MessageRecord> batchSelected = Collections.synchronizedSet(new HashSet<MessageRecord>());
......@@ -216,8 +218,10 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
private @LayoutRes int getLayoutForViewType(int viewType) {
switch (viewType) {
case MESSAGE_TYPE_AUDIO_OUTGOING:
case MESSAGE_TYPE_DOCUMENT_OUTGOING:
case MESSAGE_TYPE_OUTGOING: return R.layout.conversation_item_sent;
case MESSAGE_TYPE_AUDIO_INCOMING:
case MESSAGE_TYPE_DOCUMENT_INCOMING:
case MESSAGE_TYPE_INCOMING: return R.layout.conversation_item_received;
case MESSAGE_TYPE_UPDATE: return R.layout.conversation_item_update;
default: throw new IllegalArgumentException("unsupported item view type given to ConversationAdapter");
......@@ -233,6 +237,9 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
} else if (hasAudio(messageRecord)) {
if (messageRecord.isOutgoing()) return MESSAGE_TYPE_AUDIO_OUTGOING;
else return MESSAGE_TYPE_AUDIO_INCOMING;
} else if (hasDocument(messageRecord)) {
if (messageRecord.isOutgoing()) return MESSAGE_TYPE_DOCUMENT_OUTGOING;
else return MESSAGE_TYPE_DOCUMENT_INCOMING;
} else if (messageRecord.isOutgoing()) {
return MESSAGE_TYPE_OUTGOING;
} else {
......@@ -305,6 +312,12 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
((MediaMmsMessageRecord)messageRecord).getSlideDeck().getAudioSlide() != null;
}
private boolean hasDocument(MessageRecord messageRecord) {
return messageRecord.isMms() &&
!messageRecord.isMmsNotification() &&
((MediaMmsMessageRecord)messageRecord).getSlideDeck().getDocumentSlide() != null;
}
@Override
public long getHeaderId(int position) {
if (!isActiveCursor()) return -1;
......
......@@ -381,9 +381,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()) && slide.getUri() != null) {
SaveAttachmentTask saveTask = new SaveAttachmentTask(getActivity(), masterSecret);
saveTask.execute(new Attachment(slide.getUri(), slide.getContentType(), message.getDateReceived()));
if ((slide.hasImage() || slide.hasVideo() || slide.hasAudio() || slide.hasDocument()) && slide.getUri() != null) {
SaveAttachmentTask saveTask = new SaveAttachmentTask(getActivity(), masterSecret, list);
saveTask.execute(new Attachment(slide.getUri(), slide.getContentType(), message.getDateReceived(), slide.getFileName().orNull()));
return;
}
}
......
......@@ -23,8 +23,10 @@ import android.content.Intent;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog;
import android.text.TextUtils;
import android.text.util.Linkify;
......@@ -43,6 +45,7 @@ import android.widget.Toast;
import org.smssecure.smssecure.components.AudioView;
import org.smssecure.smssecure.components.AvatarImageView;
import org.smssecure.smssecure.components.DeliveryStatusView;
import org.smssecure.smssecure.components.DocumentView;
import org.smssecure.smssecure.components.AlertView;
import org.smssecure.smssecure.components.ThumbnailView;
import org.smssecure.smssecure.crypto.KeyExchangeInitiator;
......@@ -115,6 +118,7 @@ public class ConversationItem extends LinearLayout
private @Nullable Recipients conversationRecipients;
private @NonNull ThumbnailView mediaThumbnail;
private @NonNull Stub<AudioView> audioViewStub;
private @NonNull Stub<DocumentView> documentViewStub;
private @NonNull Button mmsDownloadButton;
private @NonNull TextView mmsDownloadingLabel;
......@@ -181,6 +185,7 @@ public class ConversationItem extends LinearLayout
this.bodyBubble = findViewById(R.id.body_bubble);
this.mediaThumbnail = (ThumbnailView) findViewById(R.id.image_view);
this.audioViewStub = new Stub<>((ViewStub) findViewById(R.id.audio_view_stub));
this.documentViewStub = new Stub<>((ViewStub) findViewById(R.id.document_view_stub));
setOnClickListener(new ClickListener(null));
......@@ -261,6 +266,10 @@ public class ConversationItem extends LinearLayout
if (audioViewStub.resolved()) {
setAudioViewTint(messageRecord, conversationRecipients);
}
if (documentViewStub.resolved()) {
setDocumentViewTint(messageRecord, conversationRecipients);
}
}
private void setAudioViewTint(MessageRecord messageRecord, Recipients recipients) {
......@@ -275,6 +284,18 @@ public class ConversationItem extends LinearLayout
}
}
private void setDocumentViewTint(MessageRecord messageRecord, Recipients recipients) {
if (messageRecord.isOutgoing()) {
if (DynamicTheme.LIGHT.equals(SilencePreferences.getTheme(context))) {
documentViewStub.get().setTint(recipients.getColor().toConversationColor(context), defaultBubbleColor);
} else {
documentViewStub.get().setTint(Color.WHITE, defaultBubbleColor);
}
} else {
documentViewStub.get().setTint(Color.WHITE, recipients.getColor().toConversationColor(context));
}
}
private void setInteractionState(MessageRecord messageRecord) {
setSelected(batchSelected.contains(messageRecord));
mediaThumbnail.setFocusable(!shouldInterceptClicks(messageRecord) && batchSelected.isEmpty());
......@@ -286,6 +307,11 @@ public class ConversationItem extends LinearLayout
audioViewStub.get().setClickable(batchSelected.isEmpty());
audioViewStub.get().setEnabled(batchSelected.isEmpty());
}
if (documentViewStub.resolved()) {
documentViewStub.get().setFocusable(!shouldInterceptClicks(messageRecord) && batchSelected.isEmpty());
documentViewStub.get().setClickable(batchSelected.isEmpty());
}
}
private boolean isCaptionlessMms(MessageRecord messageRecord) {
......@@ -304,6 +330,12 @@ public class ConversationItem extends LinearLayout
((MediaMmsMessageRecord)messageRecord).getSlideDeck().getThumbnailSlide() != null;
}
private boolean hasDocument(MessageRecord messageRecord) {
return messageRecord.isMms() &&
!messageRecord.isMmsNotification() &&
((MediaMmsMessageRecord)messageRecord).getSlideDeck().getDocumentSlide() != null;
}
private void setBodyText(MessageRecord messageRecord) {
bodyText.setClickable(false);
bodyText.setFocusable(false);
......@@ -347,6 +379,7 @@ public class ConversationItem extends LinearLayout
} else if (hasAudio(messageRecord)) {
audioViewStub.get().setVisibility(View.VISIBLE);
mediaThumbnail.setVisibility(View.GONE);
if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE);
//noinspection ConstantConditions
audioViewStub.get().setAudio(masterSecret, ((MediaMmsMessageRecord) messageRecord).getSlideDeck().getAudioSlide(), showControls);
......@@ -354,9 +387,22 @@ public class ConversationItem extends LinearLayout
audioViewStub.get().setOnLongClickListener(passthroughClickListener);
bodyText.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
} else if (hasDocument(messageRecord)) {
documentViewStub.get().setVisibility(View.VISIBLE);
mediaThumbnail.setVisibility(View.GONE);
if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE);
//noinspection ConstantConditions
documentViewStub.get().setDocument(((MediaMmsMessageRecord)messageRecord).getSlideDeck().getDocumentSlide(), showControls);
documentViewStub.get().setDocumentClickListener(new ThumbnailClickListener());
documentViewStub.get().setDownloadClickListener(downloadClickListener);
documentViewStub.get().setOnLongClickListener(passthroughClickListener);
bodyText.setLayoutParams(new ActionBar.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
} else if (hasThumbnail(messageRecord)) {
mediaThumbnail.setVisibility(View.VISIBLE);
if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE);
if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE);
if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE);
//noinspection ConstantConditions
mediaThumbnail.setImageResource(masterSecret,
......@@ -365,7 +411,8 @@ public class ConversationItem extends LinearLayout
bodyText.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
} else {
mediaThumbnail.setVisibility(View.GONE);
if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE);
if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE);
if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE);
bodyText.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
}
}
......@@ -416,7 +463,7 @@ public class ConversationItem extends LinearLayout
}
}
}
public void hideClickForDetails(){
indicatorText.setVisibility(View.GONE);
}
......@@ -570,19 +617,6 @@ public class ConversationItem extends LinearLayout
}
private class ThumbnailClickListener implements SlideClickListener {
private void fireIntent(Slide slide) {
Log.w(TAG, "Clicked: " + slide.getUri() + " , " + slide.getContentType());
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(PartAuthority.getAttachmentPublicUri(slide.getUri()), slide.getContentType());
try {
context.startActivity(intent);
} catch (ActivityNotFoundException anfe) {
Log.w(TAG, "No activity existed to view the media.");
Toast.makeText(context, R.string.ConversationItem_unable_to_open_media, Toast.LENGTH_LONG).show();
}
}
public void onClick(final View v, final Slide slide) {
if (shouldInterceptClicks(messageRecord) || !batchSelected.isEmpty()) {
performClick();
......@@ -597,18 +631,18 @@ public class ConversationItem extends LinearLayout
context.startActivity(intent);
} else if (slide.getUri() != null) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.ConversationItem_view_secure_media_question);
builder.setIconAttribute(R.attr.dialog_alert_icon);
builder.setCancelable(true);
builder.setMessage(R.string.ConversationItem_this_media_has_been_stored_in_an_encrypted_database_external_viewer_warning);
builder.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
fireIntent(slide);
}
});
builder.setNegativeButton(R.string.no, null);
builder.show();
Log.w(TAG, "Clicked: " + slide.getUri() + " , " + slide.getContentType());
Uri publicUri = PartAuthority.getAttachmentPublicUri(slide.getUri());
Log.w(TAG, "Public URI: " + publicUri);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(PartAuthority.getAttachmentPublicUri(slide.getUri()), slide.getContentType());
try {
context.startActivity(intent);
} catch (ActivityNotFoundException anfe) {
Log.w(TAG, "No activity existed to view the media.");
Toast.makeText(context, R.string.ConversationItem_unable_to_open_media, Toast.LENGTH_LONG).show();
}
}
}
}
......@@ -650,6 +684,7 @@ public class ConversationItem extends LinearLayout
performClick();
}
}
private class ClickListener implements View.OnClickListener {
private OnClickListener parent;
......
......@@ -393,7 +393,7 @@ public class ConversationListFragment extends Fragment
private void sendMediaDraft(DraftDatabase.Draft draft, long threadId, @Nullable String forcedValue) {
List<Attachment> attachment = new LinkedList<Attachment>();
attachment.add(new UriAttachment(Uri.parse(draft.getValue()), draft.getType() + "/*", AttachmentDatabase.TRANSFER_PROGRESS_DONE, 0));
attachment.add(new UriAttachment(Uri.parse(draft.getValue()), draft.getType() + "/*", AttachmentDatabase.TRANSFER_PROGRESS_DONE, 0, null));
OutgoingMediaMessage message = new OutgoingMediaMessage(recipients,
forcedValue != null ? forcedValue : "",
......
......@@ -67,7 +67,7 @@ public class MediaAdapter extends CursorRecyclerViewAdapter<ViewHolder> {
@Override
public void onBindItemViewHolder(final ViewHolder viewHolder, final @NonNull Cursor cursor) {
final ThumbnailView imageView = viewHolder.imageView;
final MediaRecord mediaRecord = MediaRecord.from(cursor);
final MediaRecord mediaRecord = MediaRecord.from(getContext(), masterSecret, cursor);
Slide slide = MediaUtil.getSlideForAttachment(getContext(), mediaRecord.getAttachment());
......
......@@ -166,10 +166,11 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity i
List<SaveAttachmentTask.Attachment> attachments = new ArrayList<>(cursor.getCount());
while (cursor != null && cursor.moveToNext()) {
MediaRecord record = MediaRecord.from(cursor);
MediaRecord record = MediaRecord.from(c, masterSecret, cursor);
attachments.add(new SaveAttachmentTask.Attachment(record.getAttachment().getDataUri(),
record.getContentType(),
record.getDate()));
record.getDate(),
null));
}
return attachments;
......@@ -179,7 +180,7 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity i
protected void onPostExecute(List<SaveAttachmentTask.Attachment> attachments) {
super.onPostExecute(attachments);
SaveAttachmentTask saveTask = new SaveAttachmentTask(c, masterSecret, attachments.size());
SaveAttachmentTask saveTask = new SaveAttachmentTask(c, masterSecret, gridView, attachments.size());
saveTask.execute(attachments.toArray(new SaveAttachmentTask.Attachment[attachments.size()]));
}
}.execute();
......
......@@ -207,9 +207,9 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
SaveAttachmentTask.showWarningDialog(this, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
SaveAttachmentTask saveTask = new SaveAttachmentTask(MediaPreviewActivity.this, masterSecret);
SaveAttachmentTask saveTask = new SaveAttachmentTask(MediaPreviewActivity.this, masterSecret, image);
long saveDate = (date > 0) ? date : System.currentTimeMillis();
saveTask.execute(new Attachment(mediaUri, mediaType, saveDate));
saveTask.execute(new Attachment(mediaUri, mediaType, saveDate, null));
}
});
}
......
......@@ -13,6 +13,9 @@ public abstract class Attachment {
private final int transferState;
private final long size;
@Nullable
private final String fileName;
@Nullable
private final String location;
......@@ -22,12 +25,13 @@ public abstract class Attachment {
@Nullable
private final String relay;
public Attachment(@NonNull String contentType, int transferState, long size,
public Attachment(@NonNull String contentType, int transferState, long size, @Nullable String fileName,
@Nullable String location, @Nullable String key, @Nullable String relay)
{
this.contentType = contentType;
this.transferState = transferState;
this.size = size;
this.fileName = fileName;
this.location = location;
this.key = key;
this.relay = relay;
......@@ -52,6 +56,11 @@ public abstract class Attachment {
return size;
}
@Nullable
public String getFileName() {
return fileName;
}
@NonNull
public String getContentType() {
return contentType;
......
......@@ -15,9 +15,9 @@ public class DatabaseAttachment extends Attachment {
public DatabaseAttachment(AttachmentId attachmentId, long mmsId,
boolean hasData, boolean hasThumbnail,
String contentType, int transferProgress, long size,
String location, String key, String relay)
String fileName, String location, String key, String relay)
{
super(contentType, transferProgress, size, location, key, relay);
super(contentType, transferProgress, size, fileName, location, key, relay);
this.attachmentId = attachmentId;
this.hasData = hasData;
this.hasThumbnail = hasThumbnail;
......
......@@ -9,14 +9,17 @@ public class UriAttachment extends Attachment {
private final @NonNull Uri dataUri;
private final @Nullable 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,
@Nullable String fileName)
{
this(uri, uri, contentType, transferState, size, fileName);
}
public UriAttachment(@NonNull Uri dataUri, @Nullable 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, fileName, null, null, null);
this.dataUri = dataUri;
this.thumbnailUri = thumbnailUri;
}
......
package org.smssecure.smssecure.components;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.support.annotation.AttrRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import com.pnikosis.materialishprogress.ProgressWheel;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import org.smssecure.smssecure.R;
import org.smssecure.smssecure.database.AttachmentDatabase;
import org.smssecure.smssecure.events.PartProgressEvent;
import org.smssecure.smssecure.mms.DocumentSlide;
import org.smssecure.smssecure.mms.SlideClickListener;
import org.smssecure.smssecure.util.Util;
import org.whispersystems.libsignal.util.guava.Optional;
public class DocumentView extends FrameLayout {
private static final String TAG = DocumentView.class.getSimpleName();
private final @NonNull AnimatingToggle controlToggle;
private final @NonNull ImageView downloadButton;
private final @NonNull ProgressWheel downloadProgress;
private final @NonNull View documentBackground;
private final @NonNull View container;
private final @NonNull TextView fileName;
private final @NonNull TextView fileSize;
private final @NonNull TextView document;
private @Nullable SlideClickListener downloadListener;
private @Nullable SlideClickListener viewListener;
private @Nullable DocumentSlide documentSlide;
public DocumentView(@NonNull Context context) {
this(context, null);
}
public DocumentView(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public DocumentView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
inflate(context, R.layout.document_view, this);
this.container = findViewById(R.id.document_container);
this.controlToggle = (AnimatingToggle) findViewById(R.id.control_toggle);
this.downloadButton = (ImageView) findViewById(R.id.download);
this.downloadProgress = (ProgressWheel) findViewById(R.id.download_progress);
this.fileName = (TextView) findViewById(R.id.file_name);
this.fileSize = (TextView) findViewById(R.id.file_size);
this.documentBackground = findViewById(R.id.document_background);
this.document = (TextView) findViewById(R.id.document);
if (attrs != null) {
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.DocumentView, 0, 0);
setTint(typedArray.getColor(R.styleable.DocumentView_documentForegroundTintColor, Color.WHITE),
typedArray.getColor(R.styleable.DocumentView_documentBackgroundTintColor, Color.WHITE));