mirror of
https://github.com/deltachat/deltachat-android.git
synced 2025-10-03 17:59:39 +02:00
display vcard view type and allow to import/export contacts as vcard (#3043)
* implement Rpc API for parse_vcard * allow to create Recipient from VcardContact * display vcard view-type in incoming/outgoing messages * fix linter warnings in Recipient class * properly show vcard view-type in quoted messages * display fallback avatar in quotes * allow to attach vcard * implement basic click listener * timestamp type got changed from float to int * set stick translation for "contact" * share contact as vcard using the new Rpc.makeVcard API * allow to import contact when clicking vcard
This commit is contained in:
parent
1884f9bc4f
commit
c705790d26
23 changed files with 565 additions and 96 deletions
|
@ -69,6 +69,17 @@
|
|||
android:padding="8dp"
|
||||
android:background="@drawable/message_bubble_background_sent_alone"/>
|
||||
|
||||
<org.thoughtcrime.securesms.components.VcardView
|
||||
android:id="@+id/attachment_vcard"
|
||||
android:layout_width="230dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginRight="10dp"
|
||||
android:padding="8dp"
|
||||
android:background="@drawable/message_bubble_background_sent_alone"/>
|
||||
|
||||
</org.thoughtcrime.securesms.components.RemovableEditableMediaView>
|
||||
|
||||
</FrameLayout>
|
||||
|
|
|
@ -153,6 +153,16 @@
|
|||
android:layout_marginLeft="@dimen/message_bubble_horizontal_padding"
|
||||
android:layout_marginRight="@dimen/message_bubble_horizontal_padding" />
|
||||
|
||||
<ViewStub
|
||||
android:id="@+id/vcard_view_stub"
|
||||
android:layout="@layout/conversation_item_vcard"
|
||||
android:layout_width="210dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/message_bubble_top_padding"
|
||||
android:layout_marginBottom="@dimen/message_bubble_collapsed_footer_padding"
|
||||
android:layout_marginLeft="@dimen/message_bubble_horizontal_padding"
|
||||
android:layout_marginRight="@dimen/message_bubble_horizontal_padding" />
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
android:id="@+id/conversation_item_body"
|
||||
android:layout_width="wrap_content"
|
||||
|
|
|
@ -131,6 +131,16 @@
|
|||
android:layout_marginLeft="@dimen/message_bubble_horizontal_padding"
|
||||
android:layout_marginRight="@dimen/message_bubble_horizontal_padding" />
|
||||
|
||||
<ViewStub
|
||||
android:id="@+id/vcard_view_stub"
|
||||
android:layout="@layout/conversation_item_vcard"
|
||||
android:layout_width="210dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/message_bubble_top_padding"
|
||||
android:layout_marginBottom="@dimen/message_bubble_collapsed_footer_padding"
|
||||
android:layout_marginLeft="@dimen/message_bubble_horizontal_padding"
|
||||
android:layout_marginRight="@dimen/message_bubble_horizontal_padding" />
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
android:id="@+id/conversation_item_body"
|
||||
android:layout_width="wrap_content"
|
||||
|
|
10
res/layout/conversation_item_vcard.xml
Normal file
10
res/layout/conversation_item_vcard.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<org.thoughtcrime.securesms.components.VcardView
|
||||
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/vcard_view"
|
||||
android:layout_width="210dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"/>
|
54
res/layout/vcard_view.xml
Normal file
54
res/layout/vcard_view.xml
Normal file
|
@ -0,0 +1,54 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<org.thoughtcrime.securesms.contacts.ContactSelectionListItem
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="60dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:focusable="true">
|
||||
|
||||
<org.thoughtcrime.securesms.components.AvatarView
|
||||
android:id="@+id/avatar"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp" />
|
||||
|
||||
<LinearLayout android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
android:id="@+id/name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checkMark="?android:attr/listChoiceIndicatorMultiple"
|
||||
android:drawablePadding="5dp"
|
||||
android:ellipsize="marquee"
|
||||
android:singleLine="true"
|
||||
android:fontFamily="sans-serif"
|
||||
android:textSize="16sp"
|
||||
tools:text="Frieeeeeeedrich Nieeeeeeeeeetzsche" />
|
||||
<!-- Attention: Using android:maxLines="1", if the name is an emoji followed by a
|
||||
long word and the chat is muted, then the long word is not shown at all
|
||||
(instead of using `…`). That's why we use android:singleLine="true" -->
|
||||
|
||||
|
||||
<TextView android:id="@+id/addr"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textDirection="ltr"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textSize="14sp"
|
||||
android:fontFamily="sans-serif-light"
|
||||
tools:text="user@example.com" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</org.thoughtcrime.securesms.contacts.ContactSelectionListItem>
|
|
@ -20,6 +20,7 @@ public class DcMsg {
|
|||
public final static int DC_MSG_FILE = 60;
|
||||
public final static int DC_MSG_VIDEOCHAT_INVITATION = 70;
|
||||
public final static int DC_MSG_WEBXDC = 80;
|
||||
public final static int DC_MSG_VCARD = 90;
|
||||
|
||||
public final static int DC_INFO_UNKNOWN = 0;
|
||||
public final static int DC_INFO_GROUP_NAME_CHANGED = 2;
|
||||
|
|
|
@ -8,6 +8,7 @@ import com.google.gson.JsonElement;
|
|||
import com.google.gson.JsonSyntaxException;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
@ -95,6 +96,20 @@ public class Rpc {
|
|||
return gson.fromJson(getResult("get_system_info"), mapType.getType());
|
||||
}
|
||||
|
||||
public List<VcardContact> parseVcard(String path) throws RpcException {
|
||||
TypeToken<List<VcardContact>> listType = new TypeToken<List<VcardContact>>(){};
|
||||
return gson.fromJson(getResult("parse_vcard", path), listType.getType());
|
||||
}
|
||||
|
||||
public String makeVcard(int accountId, int... contacts) throws RpcException {
|
||||
return gson.fromJson(getResult("make_vcard", accountId, contacts), String.class);
|
||||
}
|
||||
|
||||
public List<Integer> importVcard(int accountId, String path) throws RpcException {
|
||||
TypeToken<List<Integer>> listType = new TypeToken<List<Integer>>(){};
|
||||
return gson.fromJson(getResult("import_vcard", accountId, path), listType.getType());
|
||||
}
|
||||
|
||||
public HttpResponse getHttpResponse(int accountId, String url) throws RpcException {
|
||||
return gson.fromJson(getResult("get_http_response", accountId, url), HttpResponse.class);
|
||||
}
|
||||
|
|
60
src/com/b44t/messenger/rpc/VcardContact.java
Normal file
60
src/com/b44t/messenger/rpc/VcardContact.java
Normal file
|
@ -0,0 +1,60 @@
|
|||
package com.b44t.messenger.rpc;
|
||||
|
||||
import android.util.Base64;
|
||||
|
||||
public class VcardContact {
|
||||
// Email address.
|
||||
private final String addr;
|
||||
|
||||
// The contact's name, or the email address if no name was given.
|
||||
private final String displayName;
|
||||
|
||||
// Public PGP key in Base64.
|
||||
private final String key;
|
||||
|
||||
// Profile image in Base64.
|
||||
private final String profileImage;
|
||||
|
||||
// Contact color in HTML color format.
|
||||
private final String color;
|
||||
|
||||
// Last update timestamp.
|
||||
private final int timestamp;
|
||||
|
||||
public VcardContact(String addr, String displayName, String key, String profileImage, String color, int timestamp) {
|
||||
this.addr = addr;
|
||||
this.displayName = displayName;
|
||||
this.key = key;
|
||||
this.profileImage = profileImage;
|
||||
this.color = color;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public String getAddr() {
|
||||
return addr;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public byte[] getKey() {
|
||||
return key == null? null : Base64.decode(key, Base64.NO_WRAP | Base64.NO_PADDING);
|
||||
}
|
||||
|
||||
public boolean hasProfileImage() {
|
||||
return profileImage != null;
|
||||
}
|
||||
|
||||
public byte[] getProfileImage() {
|
||||
return profileImage == null? null : Base64.decode(profileImage, Base64.NO_WRAP | Base64.NO_PADDING);
|
||||
}
|
||||
|
||||
public String getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
public int getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
}
|
|
@ -2,26 +2,17 @@ package org.thoughtcrime.securesms;
|
|||
|
||||
import android.content.Intent;
|
||||
|
||||
import com.b44t.messenger.DcContext;
|
||||
|
||||
import org.thoughtcrime.securesms.connect.DcHelper;
|
||||
|
||||
public class AttachContactActivity extends ContactSelectionActivity {
|
||||
|
||||
public static final String NAME_EXTRA = "name_extra";
|
||||
public static final String ADDR_EXTRA = "addr_extra";
|
||||
public static final String CONTACT_ID_EXTRA = "contact_id_extra";
|
||||
|
||||
@Override
|
||||
public void onContactSelected(int specialId, String addr) {
|
||||
String name = "";
|
||||
DcContext dcContext = DcHelper.getContext(this);
|
||||
int contactId = dcContext.lookupContactIdByAddr(addr);
|
||||
if (contactId != 0) {
|
||||
name = dcContext.getContact(contactId).getDisplayName();
|
||||
}
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra(NAME_EXTRA, name);
|
||||
intent.putExtra(ADDR_EXTRA, addr);
|
||||
int contactId = DcHelper.getContext(this).lookupContactIdByAddr(addr);
|
||||
intent.putExtra(CONTACT_ID_EXTRA, contactId);
|
||||
setResult(RESULT_OK, intent);
|
||||
finish();
|
||||
}
|
||||
|
|
|
@ -71,6 +71,8 @@ import com.b44t.messenger.DcContact;
|
|||
import com.b44t.messenger.DcContext;
|
||||
import com.b44t.messenger.DcEvent;
|
||||
import com.b44t.messenger.DcMsg;
|
||||
import com.b44t.messenger.rpc.Rpc;
|
||||
import com.b44t.messenger.rpc.RpcException;
|
||||
import com.b44t.messenger.util.concurrent.ListenableFuture;
|
||||
import com.b44t.messenger.util.concurrent.SettableFuture;
|
||||
|
||||
|
@ -103,7 +105,6 @@ import org.thoughtcrime.securesms.mms.AttachmentManager.MediaType;
|
|||
import org.thoughtcrime.securesms.mms.AudioSlide;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.mms.PartAuthority;
|
||||
import org.thoughtcrime.securesms.mms.QuoteModel;
|
||||
import org.thoughtcrime.securesms.mms.SlideDeck;
|
||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||
|
@ -125,12 +126,7 @@ import org.thoughtcrime.securesms.video.recode.VideoRecoder;
|
|||
import org.thoughtcrime.securesms.videochat.VideochatUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
|
@ -193,6 +189,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||
private ApplicationContext context;
|
||||
private Recipient recipient;
|
||||
private DcContext dcContext;
|
||||
private Rpc rpc;
|
||||
private DcChat dcChat = new DcChat(0, 0);
|
||||
private int chatId;
|
||||
private final boolean isSecureText = true;
|
||||
|
@ -204,6 +201,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||
protected void onCreate(Bundle state, boolean ready) {
|
||||
this.context = ApplicationContext.getInstance(getApplicationContext());
|
||||
this.dcContext = DcHelper.getContext(context);
|
||||
this.rpc = DcHelper.getRpc(context);
|
||||
|
||||
supportRequestWindowFeature(WindowCompat.FEATURE_ACTION_BAR_OVERLAY);
|
||||
setContentView(R.layout.conversation_activity);
|
||||
|
@ -978,9 +976,18 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||
}
|
||||
|
||||
private void addAttachmentContactInfo(Intent data) {
|
||||
String name = data.getStringExtra(AttachContactActivity.NAME_EXTRA);
|
||||
String mail = data.getStringExtra(AttachContactActivity.ADDR_EXTRA);
|
||||
composeText.append(name + "\n" + mail);
|
||||
int contactId = data.getIntExtra(AttachContactActivity.CONTACT_ID_EXTRA, 0);
|
||||
if (contactId == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
byte[] vcard = rpc.makeVcard(dcContext.getAccountId(), contactId).getBytes();
|
||||
String mimeType = "application/octet-stream";
|
||||
setMedia(PersistentBlobProvider.getInstance().create(this, vcard, mimeType, "vcard.vcf"), MediaType.DOCUMENT);
|
||||
} catch (RpcException e) {
|
||||
Log.e(TAG, "makeVcard() failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isMultiUser() {
|
||||
|
@ -991,39 +998,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||
return dcChat.getVisibility() == DcChat.DC_CHAT_VISIBILITY_ARCHIVED;
|
||||
}
|
||||
|
||||
public static String getRealPathFromAttachment(Context context, Attachment attachment) {
|
||||
try {
|
||||
// get file in the blobdir as `<blobdir>/<name>[-<uniqueNumber>].<ext>`
|
||||
String filename = attachment.getFileName();
|
||||
String ext = "";
|
||||
if(filename==null) {
|
||||
filename = new SimpleDateFormat("yyyy-MM-dd-HH-mm").format(new Date());
|
||||
ext = "." + MediaUtil.getExtensionFromMimeType(attachment.getContentType());
|
||||
}
|
||||
else {
|
||||
int i = filename.lastIndexOf(".");
|
||||
if(i>=0) {
|
||||
ext = filename.substring(i);
|
||||
filename = filename.substring(0, i);
|
||||
}
|
||||
}
|
||||
String path = DcHelper.getBlobdirFile(DcHelper.getContext(context), filename, ext);
|
||||
|
||||
// copy content to this file
|
||||
if(path!=null) {
|
||||
InputStream inputStream = PartAuthority.getAttachmentStream(context, attachment.getDataUri());
|
||||
OutputStream outputStream = new FileOutputStream(path);
|
||||
Util.copy(inputStream, outputStream);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
//////// send message or save draft
|
||||
|
||||
protected static final int ACTION_SEND_OUT = 1;
|
||||
|
@ -1076,7 +1050,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||
} else {
|
||||
msg = new DcMsg(dcContext, DcMsg.DC_MSG_FILE);
|
||||
}
|
||||
String path = getRealPathFromAttachment(this, attachment);
|
||||
String path = attachment.getRealPath(this);
|
||||
msg.setFile(path, null);
|
||||
}
|
||||
}
|
||||
|
@ -1336,7 +1310,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||
private void sendSticker(@NonNull Uri uri, String contentType) {
|
||||
Attachment attachment = new UriAttachment(uri, null, contentType,
|
||||
AttachmentDatabase.TRANSFER_PROGRESS_STARTED, 0, 0, 0, null, null, false);
|
||||
String path = getRealPathFromAttachment(this, attachment);
|
||||
String path = attachment.getRealPath(this);
|
||||
|
||||
Optional<QuoteModel> quote = inputPanel.getQuote();
|
||||
inputPanel.clearQuote();
|
||||
|
|
|
@ -32,16 +32,19 @@ import android.view.View;
|
|||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.DimenRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.b44t.messenger.DcChat;
|
||||
import com.b44t.messenger.DcContact;
|
||||
import com.b44t.messenger.DcMsg;
|
||||
import com.b44t.messenger.rpc.Reactions;
|
||||
import com.b44t.messenger.rpc.RpcException;
|
||||
import com.b44t.messenger.rpc.VcardContact;
|
||||
|
||||
import org.thoughtcrime.securesms.audio.AudioSlidePlayer;
|
||||
import org.thoughtcrime.securesms.components.AudioView;
|
||||
|
@ -51,6 +54,7 @@ import org.thoughtcrime.securesms.components.ConversationItemFooter;
|
|||
import org.thoughtcrime.securesms.components.ConversationItemThumbnail;
|
||||
import org.thoughtcrime.securesms.components.DocumentView;
|
||||
import org.thoughtcrime.securesms.components.QuoteView;
|
||||
import org.thoughtcrime.securesms.components.VcardView;
|
||||
import org.thoughtcrime.securesms.components.WebxdcView;
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiTextView;
|
||||
import org.thoughtcrime.securesms.connect.DcHelper;
|
||||
|
@ -62,6 +66,7 @@ import org.thoughtcrime.securesms.mms.Slide;
|
|||
import org.thoughtcrime.securesms.mms.SlideClickListener;
|
||||
import org.thoughtcrime.securesms.mms.SlideDeck;
|
||||
import org.thoughtcrime.securesms.mms.StickerSlide;
|
||||
import org.thoughtcrime.securesms.mms.VcardSlide;
|
||||
import org.thoughtcrime.securesms.reactions.ReactionsConversationView;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.LongClickMovementMethod;
|
||||
|
@ -71,6 +76,7 @@ import org.thoughtcrime.securesms.util.Util;
|
|||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
import org.thoughtcrime.securesms.util.views.Stub;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -113,6 +119,7 @@ public class ConversationItem extends BaseConversationItem
|
|||
private @NonNull Stub<DocumentView> documentViewStub;
|
||||
private @NonNull Stub<WebxdcView> webxdcViewStub;
|
||||
private Stub<BorderlessImageView> stickerStub;
|
||||
private Stub<VcardView> vcardViewStub;
|
||||
private @Nullable EventListener eventListener;
|
||||
|
||||
private int measureCalls;
|
||||
|
@ -146,6 +153,7 @@ public class ConversationItem extends BaseConversationItem
|
|||
this.documentViewStub = new Stub<>(findViewById(R.id.document_view_stub));
|
||||
this.webxdcViewStub = new Stub<>(findViewById(R.id.webxdc_view_stub));
|
||||
this.stickerStub = new Stub<>(findViewById(R.id.sticker_view_stub));
|
||||
this.vcardViewStub = new Stub<>(findViewById(R.id.vcard_view_stub));
|
||||
this.groupSenderHolder = findViewById(R.id.group_sender_holder);
|
||||
this.quoteView = findViewById(R.id.quote_view);
|
||||
this.container = findViewById(R.id.container);
|
||||
|
@ -301,6 +309,11 @@ public class ConversationItem extends BaseConversationItem
|
|||
webxdcViewStub.get().setFocusable(!shouldInterceptClicks(messageRecord) && batchSelected.isEmpty());
|
||||
webxdcViewStub.get().setClickable(batchSelected.isEmpty());
|
||||
}
|
||||
|
||||
if (vcardViewStub.resolved()) {
|
||||
vcardViewStub.get().setFocusable(!shouldInterceptClicks(messageRecord) && batchSelected.isEmpty());
|
||||
vcardViewStub.get().setClickable(batchSelected.isEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
private void setContentDescription() {
|
||||
|
@ -315,6 +328,8 @@ public class ConversationItem extends BaseConversationItem
|
|||
desc += documentViewStub.get().getDescription() + "\n";
|
||||
} else if (webxdcViewStub.resolved() && webxdcViewStub.get().getVisibility() == View.VISIBLE) {
|
||||
desc += webxdcViewStub.get().getDescription() + "\n";
|
||||
} else if (vcardViewStub.resolved() && vcardViewStub.get().getVisibility() == View.VISIBLE) {
|
||||
desc += vcardViewStub.get().getDescription() + "\n";
|
||||
} else if (mediaThumbnailStub.resolved() && mediaThumbnailStub.get().getVisibility() == View.VISIBLE) {
|
||||
desc += mediaThumbnailStub.get().getDescription() + "\n";
|
||||
} else if (stickerStub.resolved() && stickerStub.get().getVisibility() == View.VISIBLE) {
|
||||
|
@ -362,6 +377,10 @@ public class ConversationItem extends BaseConversationItem
|
|||
return dcMsg.getType()==DcMsg.DC_MSG_WEBXDC;
|
||||
}
|
||||
|
||||
private boolean hasVcard(DcMsg dcMsg) {
|
||||
return dcMsg.getType()==DcMsg.DC_MSG_VCARD;
|
||||
}
|
||||
|
||||
private boolean hasDocument(DcMsg dcMsg) {
|
||||
return dcMsg.getType()==DcMsg.DC_MSG_FILE && !dcMsg.isSetupMessage();
|
||||
}
|
||||
|
@ -463,6 +482,7 @@ public class ConversationItem extends BaseConversationItem
|
|||
if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE);
|
||||
if (webxdcViewStub.resolved()) webxdcViewStub.get().setVisibility(View.GONE);
|
||||
if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE);
|
||||
if (vcardViewStub.resolved()) vcardViewStub.get().setVisibility(View.GONE);
|
||||
|
||||
//noinspection ConstantConditions
|
||||
int duration = messageRecord.getDuration();
|
||||
|
@ -489,6 +509,7 @@ public class ConversationItem extends BaseConversationItem
|
|||
if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE);
|
||||
if (webxdcViewStub.resolved()) webxdcViewStub.get().setVisibility(View.GONE);
|
||||
if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE);
|
||||
if (vcardViewStub.resolved()) vcardViewStub.get().setVisibility(View.GONE);
|
||||
|
||||
//noinspection ConstantConditions
|
||||
documentViewStub.get().setDocument(new DocumentSlide(context, messageRecord));
|
||||
|
@ -508,6 +529,7 @@ public class ConversationItem extends BaseConversationItem
|
|||
if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE);
|
||||
if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE);
|
||||
if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE);
|
||||
if (vcardViewStub.resolved()) vcardViewStub.get().setVisibility(View.GONE);
|
||||
|
||||
webxdcViewStub.get().setWebxdc(messageRecord, context.getString(R.string.webxdc_app));
|
||||
webxdcViewStub.get().setWebxdcClickListener(new ThumbnailClickListener());
|
||||
|
@ -520,12 +542,33 @@ public class ConversationItem extends BaseConversationItem
|
|||
ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
footer.setVisibility(VISIBLE);
|
||||
}
|
||||
else if (hasVcard(messageRecord)) {
|
||||
vcardViewStub.get().setVisibility(View.VISIBLE);
|
||||
if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE);
|
||||
if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE);
|
||||
if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE);
|
||||
if (webxdcViewStub.resolved()) webxdcViewStub.get().setVisibility(View.GONE);
|
||||
if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE);
|
||||
|
||||
vcardViewStub.get().setVcard(glideRequests, new VcardSlide(context, messageRecord), rpc);
|
||||
vcardViewStub.get().setVcardClickListener(new ThumbnailClickListener());
|
||||
vcardViewStub.get().setOnLongClickListener(passthroughClickListener);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
vcardViewStub.get().setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
|
||||
}
|
||||
|
||||
ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
footer.setVisibility(VISIBLE);
|
||||
}
|
||||
else if (hasThumbnail(messageRecord)) {
|
||||
mediaThumbnailStub.get().setVisibility(View.VISIBLE);
|
||||
if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE);
|
||||
if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE);
|
||||
if (webxdcViewStub.resolved()) webxdcViewStub.get().setVisibility(View.GONE);
|
||||
if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE);
|
||||
if (vcardViewStub.resolved()) vcardViewStub.get().setVisibility(View.GONE);
|
||||
|
||||
Slide slide = MediaUtil.getSlideForMsg(context, messageRecord);
|
||||
|
||||
|
@ -566,6 +609,7 @@ public class ConversationItem extends BaseConversationItem
|
|||
if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE);
|
||||
if (webxdcViewStub.resolved()) webxdcViewStub.get().setVisibility(View.GONE);
|
||||
if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE);
|
||||
if (vcardViewStub.resolved()) vcardViewStub.get().setVisibility(View.GONE);
|
||||
|
||||
bodyBubble.setBackgroundColor(Color.TRANSPARENT);
|
||||
|
||||
|
@ -587,6 +631,7 @@ public class ConversationItem extends BaseConversationItem
|
|||
if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE);
|
||||
if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE);
|
||||
if (webxdcViewStub.resolved()) webxdcViewStub.get().setVisibility(View.GONE);
|
||||
if (vcardViewStub.resolved()) vcardViewStub.get().setVisibility(View.GONE);
|
||||
|
||||
ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
|
@ -839,6 +884,7 @@ public class ConversationItem extends BaseConversationItem
|
|||
else if (audioViewStub.resolved()) audioViewStub.get().togglePlay();
|
||||
else if (documentViewStub.resolved()) documentViewStub.get().performClick();
|
||||
else if (webxdcViewStub.resolved()) webxdcViewStub.get().performClick();
|
||||
else if (vcardViewStub.resolved()) vcardViewStub.get().performClick();
|
||||
}
|
||||
|
||||
/// Event handlers
|
||||
|
@ -847,8 +893,37 @@ public class ConversationItem extends BaseConversationItem
|
|||
public void onClick(final View v, final Slide slide) {
|
||||
if (shouldInterceptClicks(messageRecord) || !batchSelected.isEmpty()) {
|
||||
performClick();
|
||||
} else if (messageRecord.getType() == DcMsg.DC_MSG_WEBXDC) {
|
||||
} else if (slide.isWebxdcDocument()) {
|
||||
WebxdcActivity.openWebxdcActivity(context, messageRecord);
|
||||
} else if (slide.isVcard()) {
|
||||
try {
|
||||
String path = slide.asAttachment().getRealPath(context);
|
||||
VcardContact vcardContact = rpc.parseVcard(path).get(0);
|
||||
new AlertDialog.Builder(context)
|
||||
.setMessage(context.getString(R.string.ask_start_chat_with, vcardContact.getDisplayName()))
|
||||
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
|
||||
try {
|
||||
List<Integer> contactIds = rpc.importVcard(dcContext.getAccountId(), path);
|
||||
if (contactIds.size() > 0) {
|
||||
int chatId = dcContext.createChatByContactId(contactIds.get(0));
|
||||
if (chatId != 0) {
|
||||
Intent intent = new Intent(context, ConversationActivity.class);
|
||||
intent.putExtra(ConversationActivity.CHAT_ID_EXTRA, chatId);
|
||||
context.startActivity(intent);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (RpcException e) {
|
||||
Log.e(TAG, "failed to import vCard", e);
|
||||
}
|
||||
Toast.makeText(context, context.getResources().getString(R.string.error), Toast.LENGTH_SHORT).show();
|
||||
})
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show();
|
||||
} catch (RpcException e) {
|
||||
Log.e(TAG, "failed to parse vCard", e);
|
||||
Toast.makeText(context, context.getResources().getString(R.string.error), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
} else if (MediaPreviewActivity.isTypeSupported(slide) && slide.getUri() != null) {
|
||||
Intent intent = new Intent(context, MediaPreviewActivity.class);
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
|
|
|
@ -1,9 +1,21 @@
|
|||
package org.thoughtcrime.securesms.attachments;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.thoughtcrime.securesms.connect.DcHelper;
|
||||
import org.thoughtcrime.securesms.mms.PartAuthority;
|
||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
public abstract class Attachment {
|
||||
|
||||
@NonNull
|
||||
|
@ -88,4 +100,35 @@ public abstract class Attachment {
|
|||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public String getRealPath(Context context) {
|
||||
try {
|
||||
// get file in the blobdir as `<blobdir>/<name>[-<uniqueNumber>].<ext>`
|
||||
String filename = getFileName();
|
||||
String ext = "";
|
||||
if(filename==null) {
|
||||
filename = new SimpleDateFormat("yyyy-MM-dd-HH-mm").format(new Date());
|
||||
ext = "." + MediaUtil.getExtensionFromMimeType(getContentType());
|
||||
}
|
||||
else {
|
||||
int i = filename.lastIndexOf(".");
|
||||
if(i>=0) {
|
||||
ext = filename.substring(i);
|
||||
filename = filename.substring(0, i);
|
||||
}
|
||||
}
|
||||
String path = DcHelper.getBlobdirFile(DcHelper.getContext(context), filename, ext);
|
||||
|
||||
// copy content to this file
|
||||
InputStream inputStream = PartAuthority.getAttachmentStream(context, getDataUri());
|
||||
OutputStream outputStream = new FileOutputStream(path);
|
||||
Util.copy(inputStream, outputStream);
|
||||
|
||||
return path;
|
||||
}
|
||||
catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package org.thoughtcrime.securesms.attachments;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
|
@ -35,4 +37,9 @@ public class DcAttachment extends Attachment {
|
|||
}
|
||||
return getDataUri();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRealPath(Context context) {
|
||||
return dcMsg.getFile();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import android.net.Uri;
|
|||
import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
|
@ -20,11 +21,14 @@ import androidx.annotation.RequiresApi;
|
|||
import com.annimon.stream.Stream;
|
||||
import com.b44t.messenger.DcContact;
|
||||
import com.b44t.messenger.DcMsg;
|
||||
import com.b44t.messenger.rpc.RpcException;
|
||||
import com.b44t.messenger.rpc.VcardContact;
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
|
||||
import org.json.JSONObject;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||
import org.thoughtcrime.securesms.connect.DcHelper;
|
||||
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.mms.Slide;
|
||||
|
@ -185,7 +189,7 @@ public class QuoteView extends FrameLayout implements RecipientForeverObserver {
|
|||
}
|
||||
|
||||
private void setQuoteAttachment(@NonNull GlideRequests glideRequests, @NonNull SlideDeck slideDeck) {
|
||||
List<Slide> thumbnailSlides = Stream.of(slideDeck.getSlides()).filter(s -> s.hasImage() || s.hasVideo() || s.hasSticker() || s.isWebxdcDocument()).limit(1).toList();
|
||||
List<Slide> thumbnailSlides = Stream.of(slideDeck.getSlides()).filter(s -> s.hasImage() || s.hasVideo() || s.hasSticker() || s.isWebxdcDocument() || s.isVcard()).limit(1).toList();
|
||||
List<Slide> audioSlides = Stream.of(slideDeck.getSlides()).filter(s -> s.hasAudio()).limit(1).toList();
|
||||
List<Slide> documentSlides = Stream.of(attachments.getSlides()).filter(Slide::hasDocument).limit(1).toList();
|
||||
|
||||
|
@ -206,7 +210,22 @@ public class QuoteView extends FrameLayout implements RecipientForeverObserver {
|
|||
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
|
||||
.into(thumbnailView);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, "failed to get webxdc icon", e);
|
||||
thumbnailView.setVisibility(GONE);
|
||||
}
|
||||
} else if (thumbnailSlides.get(0).isVcard()) {
|
||||
try {
|
||||
VcardContact vcardContact = DcHelper.getRpc(getContext()).parseVcard(quotedMsg.getFile()).get(0);
|
||||
Recipient recipient = new Recipient(getContext(), vcardContact);
|
||||
glideRequests.load(recipient.getContactPhoto(getContext()))
|
||||
.error(recipient.getFallbackAvatarDrawable(getContext(), false))
|
||||
.centerCrop()
|
||||
.override(getContext().getResources().getDimensionPixelSize(R.dimen.quote_thumb_size))
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||
.into(thumbnailView);
|
||||
} catch (RpcException e) {
|
||||
Log.e(TAG, "failed to parse vCard", e);
|
||||
thumbnailView.setVisibility(GONE);
|
||||
}
|
||||
} else {
|
||||
Uri thumbnailUri = thumbnailSlides.get(0).getUri();
|
||||
|
|
75
src/org/thoughtcrime/securesms/components/VcardView.java
Normal file
75
src/org/thoughtcrime/securesms/components/VcardView.java
Normal file
|
@ -0,0 +1,75 @@
|
|||
package org.thoughtcrime.securesms.components;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.b44t.messenger.rpc.Rpc;
|
||||
import com.b44t.messenger.rpc.RpcException;
|
||||
import com.b44t.messenger.rpc.VcardContact;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.mms.SlideClickListener;
|
||||
import org.thoughtcrime.securesms.mms.VcardSlide;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
|
||||
public class VcardView extends FrameLayout {
|
||||
private static final String TAG = VcardView.class.getSimpleName();
|
||||
|
||||
private final @NonNull AvatarView avatar;
|
||||
private final @NonNull TextView name;
|
||||
private final @NonNull TextView address;
|
||||
|
||||
private @Nullable SlideClickListener viewListener;
|
||||
private @Nullable VcardSlide slide;
|
||||
|
||||
public VcardView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public VcardView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public VcardView(final Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
|
||||
inflate(context, R.layout.vcard_view, this);
|
||||
|
||||
this.avatar = findViewById(R.id.avatar);
|
||||
this.name = findViewById(R.id.name);
|
||||
this.address = findViewById(R.id.addr);
|
||||
|
||||
setOnClickListener(v -> {
|
||||
if (viewListener != null && slide != null) {
|
||||
viewListener.onClick(v, slide);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setVcardClickListener(@Nullable SlideClickListener listener) {
|
||||
this.viewListener = listener;
|
||||
}
|
||||
|
||||
public void setVcard(@NonNull GlideRequests glideRequests, final @NonNull VcardSlide slide, final @NonNull Rpc rpc) {
|
||||
try {
|
||||
VcardContact vcardContact = rpc.parseVcard(slide.asAttachment().getRealPath(getContext())).get(0);
|
||||
name.setText(vcardContact.getDisplayName());
|
||||
address.setText(vcardContact.getAddr());
|
||||
avatar.setAvatar(glideRequests, new Recipient(getContext(), vcardContact), false);
|
||||
this.slide = slide;
|
||||
} catch (RpcException e) {
|
||||
Log.e(TAG, "failed to parse vCard", e);
|
||||
}
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return name.getText() + "\n" + address.getText();
|
||||
}
|
||||
}
|
|
@ -260,6 +260,7 @@ public class DcHelper {
|
|||
dcContext.setStockTranslation(177, context.getString(R.string.reaction_by_other));
|
||||
dcContext.setStockTranslation(190, context.getString(R.string.secure_join_wait));
|
||||
dcContext.setStockTranslation(191, context.getString(R.string.secure_join_wait_timeout));
|
||||
dcContext.setStockTranslation(200, context.getString(R.string.contact));
|
||||
}
|
||||
|
||||
public static File getImexDir() {
|
||||
|
|
|
@ -24,17 +24,21 @@ public class GeneratedContactPhoto implements FallbackContactPhoto {
|
|||
|
||||
@Override
|
||||
public Drawable asDrawable(Context context, int color) {
|
||||
return asDrawable(context, color, true);
|
||||
}
|
||||
|
||||
public Drawable asDrawable(Context context, int color, boolean roundShape) {
|
||||
int targetSize = context.getResources().getDimensionPixelSize(R.dimen.contact_photo_target_size);
|
||||
|
||||
return TextDrawable.builder()
|
||||
.beginConfig()
|
||||
.width(targetSize)
|
||||
.height(targetSize)
|
||||
.textColor(Color.WHITE)
|
||||
.bold()
|
||||
.toUpperCase()
|
||||
.endConfig()
|
||||
.buildRound(getCharacter(name), color);
|
||||
TextDrawable.IShapeBuilder builder = TextDrawable.builder()
|
||||
.beginConfig()
|
||||
.width(targetSize)
|
||||
.height(targetSize)
|
||||
.textColor(Color.WHITE)
|
||||
.bold()
|
||||
.toUpperCase()
|
||||
.endConfig();
|
||||
return roundShape? builder.buildRound(getCharacter(name), color) : builder.buildRect(getCharacter(name), color);
|
||||
}
|
||||
|
||||
private String getCharacter(String name) {
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
package org.thoughtcrime.securesms.contacts.avatars;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.b44t.messenger.rpc.VcardContact;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.MessageDigest;
|
||||
|
||||
public class VcardContactPhoto implements ContactPhoto {
|
||||
private final VcardContact vContact;
|
||||
|
||||
public VcardContactPhoto(VcardContact vContact) {
|
||||
this.vContact = vContact;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream openInputStream(Context context) throws IOException {
|
||||
byte[] blob = vContact.getProfileImage();
|
||||
return (blob == null)? null : new ByteArrayInputStream(blob);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Uri getUri(@NonNull Context context) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isProfilePhoto() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {
|
||||
messageDigest.update(vContact.getAddr().getBytes());
|
||||
messageDigest.update(ByteBuffer.allocate(4).putFloat(vContact.getTimestamp()).array());
|
||||
}
|
||||
}
|
|
@ -41,12 +41,12 @@ import androidx.appcompat.app.AlertDialog;
|
|||
|
||||
import com.b44t.messenger.DcContext;
|
||||
import com.b44t.messenger.DcMsg;
|
||||
import com.b44t.messenger.rpc.RpcException;
|
||||
import com.b44t.messenger.util.concurrent.ListenableFuture;
|
||||
import com.b44t.messenger.util.concurrent.ListenableFuture.Listener;
|
||||
import com.b44t.messenger.util.concurrent.SettableFuture;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.ConversationActivity;
|
||||
import org.thoughtcrime.securesms.MediaPreviewActivity;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.ShareLocationDialog;
|
||||
|
@ -58,6 +58,7 @@ import org.thoughtcrime.securesms.components.AudioView;
|
|||
import org.thoughtcrime.securesms.components.DocumentView;
|
||||
import org.thoughtcrime.securesms.components.RemovableEditableMediaView;
|
||||
import org.thoughtcrime.securesms.components.ThumbnailView;
|
||||
import org.thoughtcrime.securesms.components.VcardView;
|
||||
import org.thoughtcrime.securesms.components.WebxdcView;
|
||||
import org.thoughtcrime.securesms.connect.DcHelper;
|
||||
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
||||
|
@ -93,6 +94,7 @@ public class AttachmentManager {
|
|||
private AudioView audioView;
|
||||
private DocumentView documentView;
|
||||
private WebxdcView webxdcView;
|
||||
private VcardView vcardView;
|
||||
//private SignalMapView mapView;
|
||||
|
||||
private final @NonNull List<Uri> garbage = new LinkedList<>();
|
||||
|
@ -116,6 +118,7 @@ public class AttachmentManager {
|
|||
this.audioView = ViewUtil.findById(root, R.id.attachment_audio);
|
||||
this.documentView = ViewUtil.findById(root, R.id.attachment_document);
|
||||
this.webxdcView = ViewUtil.findById(root, R.id.attachment_webxdc);
|
||||
this.vcardView = ViewUtil.findById(root, R.id.attachment_vcard);
|
||||
//this.mapView = ViewUtil.findById(root, R.id.attachment_location);
|
||||
this.removableMediaView = ViewUtil.findById(root, R.id.removable_media_view);
|
||||
|
||||
|
@ -126,6 +129,7 @@ public class AttachmentManager {
|
|||
audioView.getBackground().setColorFilter(incomingBubbleColor, PorterDuff.Mode.MULTIPLY);
|
||||
documentView.getBackground().setColorFilter(incomingBubbleColor, PorterDuff.Mode.MULTIPLY);
|
||||
webxdcView.getBackground().setColorFilter(incomingBubbleColor, PorterDuff.Mode.MULTIPLY);
|
||||
vcardView.getBackground().setColorFilter(incomingBubbleColor, PorterDuff.Mode.MULTIPLY);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -308,6 +312,9 @@ public class AttachmentManager {
|
|||
audioView.setAudio((AudioSlide) slide, 0);
|
||||
removableMediaView.display(audioView, false);
|
||||
result.set(true);
|
||||
} else if (slide.isVcard()) {
|
||||
vcardView.setVcard(glideRequests, (VcardSlide)slide, DcHelper.getRpc(context));
|
||||
removableMediaView.display(vcardView, false);
|
||||
} else if (slide.hasDocument()) {
|
||||
if (slide.isWebxdcDocument()) {
|
||||
DcMsg instance = msg != null ? msg : DcHelper.getContext(context).getMsg(slide.dcMsgId);
|
||||
|
@ -685,13 +692,25 @@ public class AttachmentManager {
|
|||
DcContext dcContext = DcHelper.getContext(context);
|
||||
DcMsg msg = new DcMsg(dcContext, DcMsg.DC_MSG_WEBXDC);
|
||||
Attachment attachment = new UriAttachment(uri, null, MediaUtil.WEBXDC, AttachmentDatabase.TRANSFER_PROGRESS_STARTED, 0, 0, 0, fileName, null, false);
|
||||
String path = ConversationActivity.getRealPathFromAttachment(context, attachment);
|
||||
String path = attachment.getRealPath(context);
|
||||
msg.setFile(path, MediaUtil.WEBXDC);
|
||||
dcContext.setDraft(chatId, msg);
|
||||
return new DocumentSlide(context, msg);
|
||||
} else {
|
||||
return new DocumentSlide(context, uri, mimeType, dataSize, fileName);
|
||||
}
|
||||
|
||||
if (mimeType.equals(MediaUtil.VCARD) || (fileName != null && (fileName.endsWith(".vcf") || fileName.endsWith(".vcard")))) {
|
||||
VcardSlide slide = new VcardSlide(context, uri, dataSize, fileName);
|
||||
String path = slide.asAttachment().getRealPath(context);
|
||||
try {
|
||||
if (DcHelper.getRpc(context).parseVcard(path).size() == 1) {
|
||||
return slide;
|
||||
}
|
||||
} catch (RpcException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return new DocumentSlide(context, uri, mimeType, dataSize, fileName);
|
||||
default: throw new AssertionError("unrecognized enum");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -98,6 +98,10 @@ public abstract class Slide {
|
|||
return false;
|
||||
}
|
||||
|
||||
public boolean isVcard() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean hasLocation() {
|
||||
return false;
|
||||
}
|
||||
|
|
26
src/org/thoughtcrime/securesms/mms/VcardSlide.java
Normal file
26
src/org/thoughtcrime/securesms/mms/VcardSlide.java
Normal file
|
@ -0,0 +1,26 @@
|
|||
package org.thoughtcrime.securesms.mms;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.b44t.messenger.DcMsg;
|
||||
|
||||
public class VcardSlide extends DocumentSlide {
|
||||
|
||||
public VcardSlide(Context context, DcMsg dcMsg) {
|
||||
super(context, dcMsg);
|
||||
}
|
||||
|
||||
public VcardSlide(@NonNull Context context, @NonNull Uri uri, long size, @Nullable String fileName) {
|
||||
super(context, uri, "text/vcard", size, fileName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVcard() {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -21,13 +21,15 @@ import android.content.Context;
|
|||
import android.graphics.Color;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.b44t.messenger.DcChat;
|
||||
import com.b44t.messenger.DcContact;
|
||||
import com.b44t.messenger.DcContext;
|
||||
import com.b44t.messenger.rpc.VcardContact;
|
||||
|
||||
import org.thoughtcrime.securesms.connect.DcHelper;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto;
|
||||
|
@ -37,15 +39,14 @@ import org.thoughtcrime.securesms.contacts.avatars.GroupRecordContactPhoto;
|
|||
import org.thoughtcrime.securesms.contacts.avatars.LocalFileContactPhoto;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.ProfileContactPhoto;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.SystemContactPhoto;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.VcardContactPhoto;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.util.Hash;
|
||||
import org.thoughtcrime.securesms.util.Prefs;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
|
@ -65,6 +66,7 @@ public class Recipient {
|
|||
|
||||
private final @Nullable DcChat dcChat;
|
||||
private @Nullable DcContact dcContact;
|
||||
private final @Nullable VcardContact vContact;
|
||||
|
||||
public static @NonNull Recipient fromChat(@NonNull Context context, int dcMsgId) {
|
||||
DcContext dcContext = DcHelper.getContext(context);
|
||||
|
@ -90,21 +92,26 @@ public class Recipient {
|
|||
}
|
||||
|
||||
public Recipient(@NonNull Context context, @NonNull DcChat dcChat) {
|
||||
this(context, dcChat, null, null);
|
||||
this(context, dcChat, null, null, null);
|
||||
}
|
||||
|
||||
public Recipient(@NonNull Context context, @NonNull VcardContact vContact) {
|
||||
this(context, null, null, null, vContact);
|
||||
}
|
||||
|
||||
public Recipient(@NonNull Context context, @NonNull DcContact dcContact) {
|
||||
this(context, null, dcContact, null);
|
||||
this(context, null, dcContact, null, null);
|
||||
}
|
||||
|
||||
public Recipient(@NonNull Context context, @NonNull DcContact dcContact, @NonNull String profileName) {
|
||||
this(context, null, dcContact, profileName);
|
||||
this(context, null, dcContact, profileName, null);
|
||||
}
|
||||
|
||||
private Recipient(@NonNull Context context, @Nullable DcChat dcChat, @Nullable DcContact dcContact, @Nullable String profileName) {
|
||||
private Recipient(@NonNull Context context, @Nullable DcChat dcChat, @Nullable DcContact dcContact, @Nullable String profileName, @Nullable VcardContact vContact) {
|
||||
this.dcChat = dcChat;
|
||||
this.dcContact = dcContact;
|
||||
this.profileName = profileName;
|
||||
this.vContact = vContact;
|
||||
this.contactUri = null;
|
||||
this.systemContactPhoto = null;
|
||||
this.customLabel = null;
|
||||
|
@ -141,6 +148,9 @@ public class Recipient {
|
|||
else if(dcContact!=null) {
|
||||
return dcContact.getDisplayName();
|
||||
}
|
||||
else if(vContact!=null) {
|
||||
return vContact.getDisplayName();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -168,18 +178,6 @@ public class Recipient {
|
|||
return dcChat!=null && dcChat.isMultiUser();
|
||||
}
|
||||
|
||||
public @NonNull List<Recipient> loadParticipants(Context context) {
|
||||
List<Recipient> participants = new ArrayList<>();
|
||||
if (dcChat!=null) {
|
||||
DcContext dcContext = DcHelper.getAccounts(context).getAccount(dcChat.getAccountId());
|
||||
int[] contactIds = dcContext.getChatContacts(dcChat.getId());
|
||||
for (int contactId : contactIds) {
|
||||
participants.add(new Recipient(context, dcContext.getContact(contactId)));
|
||||
}
|
||||
}
|
||||
return participants;
|
||||
}
|
||||
|
||||
public synchronized void addListener(RecipientModifiedListener listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
@ -200,15 +198,21 @@ public class Recipient {
|
|||
else if(dcContact!=null) {
|
||||
rgb = dcContact.getColor();
|
||||
}
|
||||
int argb = Color.argb(0xFF, Color.red(rgb), Color.green(rgb), Color.blue(rgb));
|
||||
return argb;
|
||||
else if(vContact!=null) {
|
||||
rgb = Color.parseColor(vContact.getColor());
|
||||
}
|
||||
return Color.argb(0xFF, Color.red(rgb), Color.green(rgb), Color.blue(rgb));
|
||||
}
|
||||
|
||||
public synchronized @NonNull Drawable getFallbackAvatarDrawable(Context context) {
|
||||
return getFallbackContactPhoto().asDrawable(context, getFallbackAvatarColor());
|
||||
return getFallbackAvatarDrawable(context, true);
|
||||
}
|
||||
|
||||
public synchronized @NonNull FallbackContactPhoto getFallbackContactPhoto() {
|
||||
public synchronized @NonNull Drawable getFallbackAvatarDrawable(Context context, boolean roundShape) {
|
||||
return getFallbackContactPhoto().asDrawable(context, getFallbackAvatarColor(), roundShape);
|
||||
}
|
||||
|
||||
public synchronized @NonNull GeneratedContactPhoto getFallbackContactPhoto() {
|
||||
String name = getName();
|
||||
if (!TextUtils.isEmpty(profileName)) return new GeneratedContactPhoto(profileName);
|
||||
else if (!TextUtils.isEmpty(name)) return new GeneratedContactPhoto(name);
|
||||
|
@ -231,6 +235,10 @@ public class Recipient {
|
|||
}
|
||||
}
|
||||
|
||||
if (vContact!=null && vContact.hasProfileImage()) {
|
||||
return new VcardContactPhoto(vContact);
|
||||
}
|
||||
|
||||
if (systemContactPhoto != null) {
|
||||
return new SystemContactPhoto(address, systemContactPhoto, 0);
|
||||
}
|
||||
|
@ -262,7 +270,7 @@ public class Recipient {
|
|||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || !(o instanceof Recipient)) return false;
|
||||
if (!(o instanceof Recipient)) return false;
|
||||
|
||||
Recipient that = (Recipient) o;
|
||||
|
||||
|
@ -290,6 +298,7 @@ public class Recipient {
|
|||
return dcChat!=null? dcChat : new DcChat(0, 0);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Recipient{" +
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.thoughtcrime.securesms.mms.ImageSlide;
|
|||
import org.thoughtcrime.securesms.mms.PartAuthority;
|
||||
import org.thoughtcrime.securesms.mms.Slide;
|
||||
import org.thoughtcrime.securesms.mms.StickerSlide;
|
||||
import org.thoughtcrime.securesms.mms.VcardSlide;
|
||||
import org.thoughtcrime.securesms.mms.VideoSlide;
|
||||
import org.thoughtcrime.securesms.providers.PersistentBlobProvider;
|
||||
|
||||
|
@ -48,6 +49,7 @@ public class MediaUtil {
|
|||
public static final String VIDEO_UNSPECIFIED = "video/*";
|
||||
public static final String OCTET = "application/octet-stream";
|
||||
public static final String WEBXDC = "application/webxdc+zip";
|
||||
public static final String VCARD = "text/vcard";
|
||||
|
||||
|
||||
public static Slide getSlideForMsg(Context context, DcMsg dcMsg) {
|
||||
|
@ -63,6 +65,8 @@ public class MediaUtil {
|
|||
} else if (dcMsg.getType() == DcMsg.DC_MSG_AUDIO
|
||||
|| dcMsg.getType() == DcMsg.DC_MSG_VOICE) {
|
||||
slide = new AudioSlide(context, dcMsg);
|
||||
} else if (dcMsg.getType() == DcMsg.DC_MSG_VCARD) {
|
||||
slide = new VcardSlide(context, dcMsg);
|
||||
} else if (dcMsg.getType() == DcMsg.DC_MSG_FILE
|
||||
|| dcMsg.getType() == DcMsg.DC_MSG_WEBXDC) {
|
||||
slide = new DocumentSlide(context, dcMsg);
|
||||
|
@ -301,6 +305,8 @@ public class MediaUtil {
|
|||
return "webp";
|
||||
case WEBXDC:
|
||||
return "xdc";
|
||||
case VCARD:
|
||||
return "vcf";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue