Add vCard support for received MMS.
This commit is contained in:
parent
edaf17bdd4
commit
1116502bc0
7 changed files with 219 additions and 148 deletions
|
@ -93,66 +93,7 @@ public class SharedContactRepository {
|
||||||
|
|
||||||
try (InputStream stream = PartAuthority.getAttachmentStream(context, uri)) {
|
try (InputStream stream = PartAuthority.getAttachmentStream(context, uri)) {
|
||||||
VCard vcard = Ezvcard.parse(stream).first();
|
VCard vcard = Ezvcard.parse(stream).first();
|
||||||
|
contact = VCardUtil.getContactFromVcard(vcard);
|
||||||
ezvcard.property.StructuredName vName = vcard.getStructuredName();
|
|
||||||
List<ezvcard.property.Telephone> vPhones = vcard.getTelephoneNumbers();
|
|
||||||
List<ezvcard.property.Email> vEmails = vcard.getEmails();
|
|
||||||
List<ezvcard.property.Address> vPostalAddresses = vcard.getAddresses();
|
|
||||||
|
|
||||||
String organization = vcard.getOrganization() != null && !vcard.getOrganization().getValues().isEmpty() ? vcard.getOrganization().getValues().get(0) : null;
|
|
||||||
String displayName = vcard.getFormattedName() != null ? vcard.getFormattedName().getValue() : null;
|
|
||||||
|
|
||||||
if (displayName == null && vName != null) {
|
|
||||||
displayName = vName.getGiven();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (displayName == null && vcard.getOrganization() != null) {
|
|
||||||
displayName = organization;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (displayName == null) {
|
|
||||||
throw new IOException("No valid name.");
|
|
||||||
}
|
|
||||||
|
|
||||||
Name name = new Name(displayName,
|
|
||||||
vName != null ? vName.getGiven() : null,
|
|
||||||
vName != null ? vName.getFamily() : null,
|
|
||||||
vName != null && !vName.getPrefixes().isEmpty() ? vName.getPrefixes().get(0) : null,
|
|
||||||
vName != null && !vName.getSuffixes().isEmpty() ? vName.getSuffixes().get(0) : null,
|
|
||||||
null);
|
|
||||||
|
|
||||||
|
|
||||||
List<Phone> phoneNumbers = new ArrayList<>(vPhones.size());
|
|
||||||
for (ezvcard.property.Telephone vEmail : vPhones) {
|
|
||||||
String label = !vEmail.getTypes().isEmpty() ? getCleanedVcardType(vEmail.getTypes().get(0).getValue()) : null;
|
|
||||||
|
|
||||||
// Phone number is stored in the uri field in v4.0 only. In other versions, it is in the text field.
|
|
||||||
String phoneNumberFromText = vEmail.getText();
|
|
||||||
String extractedPhoneNumber = phoneNumberFromText == null ? vEmail.getUri().getNumber() : phoneNumberFromText;
|
|
||||||
phoneNumbers.add(new Phone(extractedPhoneNumber, phoneTypeFromVcardType(label), label));
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Email> emails = new ArrayList<>(vEmails.size());
|
|
||||||
for (ezvcard.property.Email vEmail : vEmails) {
|
|
||||||
String label = !vEmail.getTypes().isEmpty() ? getCleanedVcardType(vEmail.getTypes().get(0).getValue()) : null;
|
|
||||||
emails.add(new Email(vEmail.getValue(), emailTypeFromVcardType(label), label));
|
|
||||||
}
|
|
||||||
|
|
||||||
List<PostalAddress> postalAddresses = new ArrayList<>(vPostalAddresses.size());
|
|
||||||
for (ezvcard.property.Address vPostalAddress : vPostalAddresses) {
|
|
||||||
String label = !vPostalAddress.getTypes().isEmpty() ? getCleanedVcardType(vPostalAddress.getTypes().get(0).getValue()) : null;
|
|
||||||
postalAddresses.add(new PostalAddress(postalAddressTypeFromVcardType(label),
|
|
||||||
label,
|
|
||||||
vPostalAddress.getStreetAddress(),
|
|
||||||
vPostalAddress.getPoBox(),
|
|
||||||
null,
|
|
||||||
vPostalAddress.getLocality(),
|
|
||||||
vPostalAddress.getRegion(),
|
|
||||||
vPostalAddress.getPostalCode(),
|
|
||||||
vPostalAddress.getCountry()));
|
|
||||||
}
|
|
||||||
|
|
||||||
contact = new Contact(name, organization, phoneNumbers, emails, postalAddresses, null);
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.w(TAG, "Failed to parse the vcard.", e);
|
Log.w(TAG, "Failed to parse the vcard.", e);
|
||||||
}
|
}
|
||||||
|
@ -201,7 +142,7 @@ public class SharedContactRepository {
|
||||||
|
|
||||||
String number = ContactUtil.getNormalizedPhoneNumber(context, cursorNumber);
|
String number = ContactUtil.getNormalizedPhoneNumber(context, cursorNumber);
|
||||||
Phone existing = numberMap.get(number);
|
Phone existing = numberMap.get(number);
|
||||||
Phone candidate = new Phone(number, phoneTypeFromContactType(cursorType), cursorLabel);
|
Phone candidate = new Phone(number, VCardUtil.phoneTypeFromContactType(cursorType), cursorLabel);
|
||||||
|
|
||||||
if (existing == null || (existing.getType() == Phone.Type.CUSTOM && existing.getLabel() == null)) {
|
if (existing == null || (existing.getType() == Phone.Type.CUSTOM && existing.getLabel() == null)) {
|
||||||
numberMap.put(number, candidate);
|
numberMap.put(number, candidate);
|
||||||
|
@ -224,7 +165,7 @@ public class SharedContactRepository {
|
||||||
int cursorType = cursor.getInt(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Email.TYPE));
|
int cursorType = cursor.getInt(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Email.TYPE));
|
||||||
String cursorLabel = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Email.LABEL));
|
String cursorLabel = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Email.LABEL));
|
||||||
|
|
||||||
emails.add(new Email(cursorEmail, emailTypeFromContactType(cursorType), cursorLabel));
|
emails.add(new Email(cursorEmail, VCardUtil.emailTypeFromContactType(cursorType), cursorLabel));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,7 +188,7 @@ public class SharedContactRepository {
|
||||||
String cursorPostal = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE));
|
String cursorPostal = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE));
|
||||||
String cursorCountry = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY));
|
String cursorCountry = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY));
|
||||||
|
|
||||||
postalAddresses.add(new PostalAddress(postalAddressTypeFromContactType(cursorType),
|
postalAddresses.add(new PostalAddress(VCardUtil.postalAddressTypeFromContactType(cursorType),
|
||||||
cursorLabel,
|
cursorLabel,
|
||||||
cursorStreet,
|
cursorStreet,
|
||||||
cursorPoBox,
|
cursorPoBox,
|
||||||
|
@ -304,70 +245,6 @@ public class SharedContactRepository {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Phone.Type phoneTypeFromContactType(int type) {
|
|
||||||
switch (type) {
|
|
||||||
case ContactsContract.CommonDataKinds.Phone.TYPE_HOME:
|
|
||||||
return Phone.Type.HOME;
|
|
||||||
case ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE:
|
|
||||||
return Phone.Type.MOBILE;
|
|
||||||
case ContactsContract.CommonDataKinds.Phone.TYPE_WORK:
|
|
||||||
return Phone.Type.WORK;
|
|
||||||
}
|
|
||||||
return Phone.Type.CUSTOM;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Phone.Type phoneTypeFromVcardType(@Nullable String type) {
|
|
||||||
if ("home".equalsIgnoreCase(type)) return Phone.Type.HOME;
|
|
||||||
else if ("cell".equalsIgnoreCase(type)) return Phone.Type.MOBILE;
|
|
||||||
else if ("work".equalsIgnoreCase(type)) return Phone.Type.WORK;
|
|
||||||
else return Phone.Type.CUSTOM;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Email.Type emailTypeFromContactType(int type) {
|
|
||||||
switch (type) {
|
|
||||||
case ContactsContract.CommonDataKinds.Email.TYPE_HOME:
|
|
||||||
return Email.Type.HOME;
|
|
||||||
case ContactsContract.CommonDataKinds.Email.TYPE_MOBILE:
|
|
||||||
return Email.Type.MOBILE;
|
|
||||||
case ContactsContract.CommonDataKinds.Email.TYPE_WORK:
|
|
||||||
return Email.Type.WORK;
|
|
||||||
}
|
|
||||||
return Email.Type.CUSTOM;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Email.Type emailTypeFromVcardType(@Nullable String type) {
|
|
||||||
if ("home".equalsIgnoreCase(type)) return Email.Type.HOME;
|
|
||||||
else if ("cell".equalsIgnoreCase(type)) return Email.Type.MOBILE;
|
|
||||||
else if ("work".equalsIgnoreCase(type)) return Email.Type.WORK;
|
|
||||||
else return Email.Type.CUSTOM;
|
|
||||||
}
|
|
||||||
|
|
||||||
private PostalAddress.Type postalAddressTypeFromContactType(int type) {
|
|
||||||
switch (type) {
|
|
||||||
case ContactsContract.CommonDataKinds.StructuredPostal.TYPE_HOME:
|
|
||||||
return PostalAddress.Type.HOME;
|
|
||||||
case ContactsContract.CommonDataKinds.StructuredPostal.TYPE_WORK:
|
|
||||||
return PostalAddress.Type.WORK;
|
|
||||||
}
|
|
||||||
return PostalAddress.Type.CUSTOM;
|
|
||||||
}
|
|
||||||
|
|
||||||
private PostalAddress.Type postalAddressTypeFromVcardType(@Nullable String type) {
|
|
||||||
if ("home".equalsIgnoreCase(type)) return PostalAddress.Type.HOME;
|
|
||||||
else if ("work".equalsIgnoreCase(type)) return PostalAddress.Type.WORK;
|
|
||||||
else return PostalAddress.Type.CUSTOM;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getCleanedVcardType(@Nullable String type) {
|
|
||||||
if (TextUtils.isEmpty(type)) return "";
|
|
||||||
|
|
||||||
if (type.startsWith("x-") && type.length() > 2) {
|
|
||||||
return type.substring(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ValueCallback<T> {
|
interface ValueCallback<T> {
|
||||||
void onComplete(@NonNull T value);
|
void onComplete(@NonNull T value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,160 @@
|
||||||
|
package org.thoughtcrime.securesms.contactshare;
|
||||||
|
|
||||||
|
import android.provider.ContactsContract;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import ezvcard.Ezvcard;
|
||||||
|
import ezvcard.VCard;
|
||||||
|
|
||||||
|
public final class VCardUtil {
|
||||||
|
|
||||||
|
private VCardUtil(){}
|
||||||
|
|
||||||
|
private static final String TAG = VCardUtil.class.getSimpleName();
|
||||||
|
|
||||||
|
public static List<Contact> parseContacts(@NonNull String vCardData) {
|
||||||
|
List<VCard> vContacts = Ezvcard.parse(vCardData).all();
|
||||||
|
List<Contact> contacts = new LinkedList<>();
|
||||||
|
for (VCard vCard: vContacts){
|
||||||
|
contacts.add(getContactFromVcard(vCard));
|
||||||
|
}
|
||||||
|
return contacts;
|
||||||
|
}
|
||||||
|
|
||||||
|
static @Nullable Contact getContactFromVcard(@NonNull VCard vcard) {
|
||||||
|
ezvcard.property.StructuredName vName = vcard.getStructuredName();
|
||||||
|
List<ezvcard.property.Telephone> vPhones = vcard.getTelephoneNumbers();
|
||||||
|
List<ezvcard.property.Email> vEmails = vcard.getEmails();
|
||||||
|
List<ezvcard.property.Address> vPostalAddresses = vcard.getAddresses();
|
||||||
|
|
||||||
|
String organization = vcard.getOrganization() != null && !vcard.getOrganization().getValues().isEmpty() ? vcard.getOrganization().getValues().get(0) : null;
|
||||||
|
String displayName = vcard.getFormattedName() != null ? vcard.getFormattedName().getValue() : null;
|
||||||
|
|
||||||
|
if (displayName == null && vName != null) {
|
||||||
|
displayName = vName.getGiven();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (displayName == null && vcard.getOrganization() != null) {
|
||||||
|
displayName = organization;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (displayName == null) {
|
||||||
|
Log.w(TAG, "Failed to parse the vcard: No valid name.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Contact.Name name = new Contact.Name(displayName,
|
||||||
|
vName != null ? vName.getGiven() : null,
|
||||||
|
vName != null ? vName.getFamily() : null,
|
||||||
|
vName != null && !vName.getPrefixes().isEmpty() ? vName.getPrefixes().get(0) : null,
|
||||||
|
vName != null && !vName.getSuffixes().isEmpty() ? vName.getSuffixes().get(0) : null,
|
||||||
|
null);
|
||||||
|
|
||||||
|
|
||||||
|
List<Contact.Phone> phoneNumbers = new ArrayList<>(vPhones.size());
|
||||||
|
for (ezvcard.property.Telephone vEmail : vPhones) {
|
||||||
|
String label = !vEmail.getTypes().isEmpty() ? getCleanedVcardType(vEmail.getTypes().get(0).getValue()) : null;
|
||||||
|
|
||||||
|
// Phone number is stored in the uri field in v4.0 only. In other versions, it is in the text field.
|
||||||
|
String phoneNumberFromText = vEmail.getText();
|
||||||
|
String extractedPhoneNumber = phoneNumberFromText == null ? vEmail.getUri().getNumber() : phoneNumberFromText;
|
||||||
|
phoneNumbers.add(new Contact.Phone(extractedPhoneNumber, phoneTypeFromVcardType(label), label));
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Contact.Email> emails = new ArrayList<>(vEmails.size());
|
||||||
|
for (ezvcard.property.Email vEmail : vEmails) {
|
||||||
|
String label = !vEmail.getTypes().isEmpty() ? getCleanedVcardType(vEmail.getTypes().get(0).getValue()) : null;
|
||||||
|
emails.add(new Contact.Email(vEmail.getValue(), emailTypeFromVcardType(label), label));
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Contact.PostalAddress> postalAddresses = new ArrayList<>(vPostalAddresses.size());
|
||||||
|
for (ezvcard.property.Address vPostalAddress : vPostalAddresses) {
|
||||||
|
String label = !vPostalAddress.getTypes().isEmpty() ? getCleanedVcardType(vPostalAddress.getTypes().get(0).getValue()) : null;
|
||||||
|
postalAddresses.add(new Contact.PostalAddress(postalAddressTypeFromVcardType(label),
|
||||||
|
label,
|
||||||
|
vPostalAddress.getStreetAddress(),
|
||||||
|
vPostalAddress.getPoBox(),
|
||||||
|
null,
|
||||||
|
vPostalAddress.getLocality(),
|
||||||
|
vPostalAddress.getRegion(),
|
||||||
|
vPostalAddress.getPostalCode(),
|
||||||
|
vPostalAddress.getCountry()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Contact(name, organization, phoneNumbers, emails, postalAddresses, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Contact.Phone.Type phoneTypeFromContactType(int type) {
|
||||||
|
switch (type) {
|
||||||
|
case ContactsContract.CommonDataKinds.Phone.TYPE_HOME:
|
||||||
|
return Contact.Phone.Type.HOME;
|
||||||
|
case ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE:
|
||||||
|
return Contact.Phone.Type.MOBILE;
|
||||||
|
case ContactsContract.CommonDataKinds.Phone.TYPE_WORK:
|
||||||
|
return Contact.Phone.Type.WORK;
|
||||||
|
}
|
||||||
|
return Contact.Phone.Type.CUSTOM;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Contact.Phone.Type phoneTypeFromVcardType(@Nullable String type) {
|
||||||
|
if ("home".equalsIgnoreCase(type)) return Contact.Phone.Type.HOME;
|
||||||
|
else if ("cell".equalsIgnoreCase(type)) return Contact.Phone.Type.MOBILE;
|
||||||
|
else if ("work".equalsIgnoreCase(type)) return Contact.Phone.Type.WORK;
|
||||||
|
else return Contact.Phone.Type.CUSTOM;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Contact.Email.Type emailTypeFromContactType(int type) {
|
||||||
|
switch (type) {
|
||||||
|
case ContactsContract.CommonDataKinds.Email.TYPE_HOME:
|
||||||
|
return Contact.Email.Type.HOME;
|
||||||
|
case ContactsContract.CommonDataKinds.Email.TYPE_MOBILE:
|
||||||
|
return Contact.Email.Type.MOBILE;
|
||||||
|
case ContactsContract.CommonDataKinds.Email.TYPE_WORK:
|
||||||
|
return Contact.Email.Type.WORK;
|
||||||
|
}
|
||||||
|
return Contact.Email.Type.CUSTOM;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Contact.Email.Type emailTypeFromVcardType(@Nullable String type) {
|
||||||
|
if ("home".equalsIgnoreCase(type)) return Contact.Email.Type.HOME;
|
||||||
|
else if ("cell".equalsIgnoreCase(type)) return Contact.Email.Type.MOBILE;
|
||||||
|
else if ("work".equalsIgnoreCase(type)) return Contact.Email.Type.WORK;
|
||||||
|
else return Contact.Email.Type.CUSTOM;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Contact.PostalAddress.Type postalAddressTypeFromContactType(int type) {
|
||||||
|
switch (type) {
|
||||||
|
case ContactsContract.CommonDataKinds.StructuredPostal.TYPE_HOME:
|
||||||
|
return Contact.PostalAddress.Type.HOME;
|
||||||
|
case ContactsContract.CommonDataKinds.StructuredPostal.TYPE_WORK:
|
||||||
|
return Contact.PostalAddress.Type.WORK;
|
||||||
|
}
|
||||||
|
return Contact.PostalAddress.Type.CUSTOM;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Contact.PostalAddress.Type postalAddressTypeFromVcardType(@Nullable String type) {
|
||||||
|
if ("home".equalsIgnoreCase(type)) return Contact.PostalAddress.Type.HOME;
|
||||||
|
else if ("work".equalsIgnoreCase(type)) return Contact.PostalAddress.Type.WORK;
|
||||||
|
else return Contact.PostalAddress.Type.CUSTOM;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getCleanedVcardType(@Nullable String type) {
|
||||||
|
if (TextUtils.isEmpty(type)) return "";
|
||||||
|
|
||||||
|
if (type.startsWith("x-") && type.length() > 2) {
|
||||||
|
return type.substring(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -336,6 +336,16 @@ public class ConversationItem extends LinearLayout implements BindableConversati
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hasSharedContact(messageRecord)) {
|
||||||
|
int contactWidth = sharedContactStub.get().getMeasuredWidth();
|
||||||
|
int availableWidth = getAvailableMessageBubbleWidth(sharedContactStub.get());
|
||||||
|
|
||||||
|
if (contactWidth != availableWidth) {
|
||||||
|
sharedContactStub.get().getLayoutParams().width = availableWidth;
|
||||||
|
needsMeasure = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ConversationItemFooter activeFooter = getActiveFooter(messageRecord);
|
ConversationItemFooter activeFooter = getActiveFooter(messageRecord);
|
||||||
int availableWidth = getAvailableMessageBubbleWidth(footer);
|
int availableWidth = getAvailableMessageBubbleWidth(footer);
|
||||||
|
|
||||||
|
@ -892,6 +902,7 @@ public class ConversationItem extends LinearLayout implements BindableConversati
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setSharedContactCorners(@NonNull MessageRecord current, @NonNull Optional<MessageRecord> previous, @NonNull Optional<MessageRecord> next, boolean isGroupThread) {
|
private void setSharedContactCorners(@NonNull MessageRecord current, @NonNull Optional<MessageRecord> previous, @NonNull Optional<MessageRecord> next, boolean isGroupThread) {
|
||||||
|
if (TextUtils.isEmpty(messageRecord.getDisplayBody(getContext()))){
|
||||||
if (isSingularMessage(current, previous, next, isGroupThread) || isEndOfMessageCluster(current, next, isGroupThread)) {
|
if (isSingularMessage(current, previous, next, isGroupThread) || isEndOfMessageCluster(current, next, isGroupThread)) {
|
||||||
sharedContactStub.get().setSingularStyle();
|
sharedContactStub.get().setSingularStyle();
|
||||||
} else if (current.isOutgoing()) {
|
} else if (current.isOutgoing()) {
|
||||||
|
@ -900,6 +911,7 @@ public class ConversationItem extends LinearLayout implements BindableConversati
|
||||||
sharedContactStub.get().setClusteredIncomingStyle();
|
sharedContactStub.get().setClusteredIncomingStyle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void setLinkPreviewCorners(@NonNull MessageRecord current, @NonNull Optional<MessageRecord> previous, @NonNull Optional<MessageRecord> next, boolean isGroupThread, boolean bigImage) {
|
private void setLinkPreviewCorners(@NonNull MessageRecord current, @NonNull Optional<MessageRecord> previous, @NonNull Optional<MessageRecord> next, boolean isGroupThread, boolean bigImage) {
|
||||||
int defaultRadius = readDimen(R.dimen.message_corner_radius);
|
int defaultRadius = readDimen(R.dimen.message_corner_radius);
|
||||||
|
@ -1075,7 +1087,7 @@ public class ConversationItem extends LinearLayout implements BindableConversati
|
||||||
private ConversationItemFooter getActiveFooter(@NonNull MessageRecord messageRecord) {
|
private ConversationItemFooter getActiveFooter(@NonNull MessageRecord messageRecord) {
|
||||||
if (hasSticker(messageRecord) || isBorderless(messageRecord)) {
|
if (hasSticker(messageRecord) || isBorderless(messageRecord)) {
|
||||||
return stickerFooter;
|
return stickerFooter;
|
||||||
} else if (hasSharedContact(messageRecord)) {
|
} else if (hasSharedContact(messageRecord) && TextUtils.isEmpty(messageRecord.getDisplayBody(getContext()))) {
|
||||||
return sharedContactStub.get().getFooter();
|
return sharedContactStub.get().getFooter();
|
||||||
} else if (hasOnlyThumbnail(messageRecord) && TextUtils.isEmpty(messageRecord.getDisplayBody(getContext()))) {
|
} else if (hasOnlyThumbnail(messageRecord) && TextUtils.isEmpty(messageRecord.getDisplayBody(getContext()))) {
|
||||||
return mediaThumbnailStub.get().getFooter();
|
return mediaThumbnailStub.get().getFooter();
|
||||||
|
@ -1442,7 +1454,7 @@ public class ConversationItem extends LinearLayout implements BindableConversati
|
||||||
Log.i(TAG, "Public URI: " + publicUri);
|
Log.i(TAG, "Public URI: " + publicUri);
|
||||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
intent.setDataAndType(PartAuthority.getAttachmentPublicUri(slide.getUri()), slide.getContentType());
|
intent.setDataAndType(PartAuthority.getAttachmentPublicUri(slide.getUri()), Intent.normalizeMimeType(slide.getContentType()));
|
||||||
try {
|
try {
|
||||||
context.startActivity(intent);
|
context.startActivity(intent);
|
||||||
} catch (ActivityNotFoundException anfe) {
|
} catch (ActivityNotFoundException anfe) {
|
||||||
|
|
|
@ -13,6 +13,9 @@ import com.google.android.mms.pdu_alt.RetrieveConf;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||||
import org.thoughtcrime.securesms.attachments.UriAttachment;
|
import org.thoughtcrime.securesms.attachments.UriAttachment;
|
||||||
|
import org.thoughtcrime.securesms.contactshare.Contact;
|
||||||
|
import org.thoughtcrime.securesms.contactshare.ContactUtil;
|
||||||
|
import org.thoughtcrime.securesms.contactshare.VCardUtil;
|
||||||
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.MessageDatabase;
|
import org.thoughtcrime.securesms.database.MessageDatabase;
|
||||||
|
@ -33,6 +36,7 @@ import org.thoughtcrime.securesms.providers.BlobProvider;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
import org.thoughtcrime.securesms.service.KeyCachingService;
|
import org.thoughtcrime.securesms.service.KeyCachingService;
|
||||||
|
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
|
@ -189,6 +193,7 @@ public class MmsDownloadJob extends BaseJob {
|
||||||
Set<RecipientId> members = new HashSet<>();
|
Set<RecipientId> members = new HashSet<>();
|
||||||
String body = null;
|
String body = null;
|
||||||
List<Attachment> attachments = new LinkedList<>();
|
List<Attachment> attachments = new LinkedList<>();
|
||||||
|
List<Contact> sharedContacts = new LinkedList<>();
|
||||||
|
|
||||||
RecipientId from = null;
|
RecipientId from = null;
|
||||||
|
|
||||||
|
@ -223,6 +228,9 @@ public class MmsDownloadJob extends BaseJob {
|
||||||
PduPart part = media.getPart(i);
|
PduPart part = media.getPart(i);
|
||||||
|
|
||||||
if (part.getData() != null) {
|
if (part.getData() != null) {
|
||||||
|
if (Util.toIsoString(part.getContentType()).toLowerCase().equals(MediaUtil.VCARD)){
|
||||||
|
sharedContacts.addAll(VCardUtil.parseContacts(new String(part.getData())));
|
||||||
|
} else {
|
||||||
Uri uri = BlobProvider.getInstance().forData(part.getData()).createForSingleUseInMemory();
|
Uri uri = BlobProvider.getInstance().forData(part.getData()).createForSingleUseInMemory();
|
||||||
String name = null;
|
String name = null;
|
||||||
|
|
||||||
|
@ -234,13 +242,14 @@ public class MmsDownloadJob extends BaseJob {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (members.size() > 2) {
|
if (members.size() > 2) {
|
||||||
List<RecipientId> recipients = new ArrayList<>(members);
|
List<RecipientId> recipients = new ArrayList<>(members);
|
||||||
group = Optional.of(DatabaseFactory.getGroupDatabase(context).getOrCreateMmsGroupForMembers(recipients));
|
group = Optional.of(DatabaseFactory.getGroupDatabase(context).getOrCreateMmsGroupForMembers(recipients));
|
||||||
}
|
}
|
||||||
|
|
||||||
IncomingMediaMessage message = new IncomingMediaMessage(from, group, body, retrieved.getDate() * 1000L, -1, attachments, subscriptionId, 0, false, false, false);
|
IncomingMediaMessage message = new IncomingMediaMessage(from, group, body, retrieved.getDate() * 1000L, -1, attachments, subscriptionId, 0, false, false, false, Optional.of(sharedContacts));
|
||||||
Optional<InsertResult> insertResult = database.insertMessageInbox(message, contentLocation, threadId);
|
Optional<InsertResult> insertResult = database.insertMessageInbox(message, contentLocation, threadId);
|
||||||
|
|
||||||
if (insertResult.isPresent()) {
|
if (insertResult.isPresent()) {
|
||||||
|
|
|
@ -48,7 +48,8 @@ public class IncomingMediaMessage {
|
||||||
long expiresIn,
|
long expiresIn,
|
||||||
boolean expirationUpdate,
|
boolean expirationUpdate,
|
||||||
boolean viewOnce,
|
boolean viewOnce,
|
||||||
boolean unidentified)
|
boolean unidentified,
|
||||||
|
Optional<List<Contact>> sharedContacts)
|
||||||
{
|
{
|
||||||
this.from = from;
|
this.from = from;
|
||||||
this.groupId = groupId.orNull();
|
this.groupId = groupId.orNull();
|
||||||
|
@ -64,6 +65,8 @@ public class IncomingMediaMessage {
|
||||||
this.unidentified = unidentified;
|
this.unidentified = unidentified;
|
||||||
|
|
||||||
this.attachments.addAll(attachments);
|
this.attachments.addAll(attachments);
|
||||||
|
this.sharedContacts.addAll(sharedContacts.or(Collections.emptyList()));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IncomingMediaMessage(@NonNull RecipientId from,
|
public IncomingMediaMessage(@NonNull RecipientId from,
|
||||||
|
|
|
@ -9,17 +9,20 @@ import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
public class PartParser {
|
public class PartParser {
|
||||||
|
|
||||||
private static final String TAG = Log.tag(PartParser.class);
|
private static final String TAG = Log.tag(PartParser.class);
|
||||||
|
private static final List<String> DOCUMENT_TYPES = Arrays.asList("text/vcard", "text/x-vcard");
|
||||||
|
|
||||||
public static String getMessageText(PduBody body) {
|
public static String getMessageText(PduBody body) {
|
||||||
String bodyText = null;
|
String bodyText = null;
|
||||||
|
|
||||||
for (int i=0;i<body.getPartsNum();i++) {
|
for (int i=0;i<body.getPartsNum();i++) {
|
||||||
if (ContentType.isTextType(Util.toIsoString(body.getPart(i).getContentType()))) {
|
if (isText(body.getPart(i)) && !isDocument(body.getPart(i))) {
|
||||||
String partText;
|
String partText;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -49,7 +52,7 @@ public class PartParser {
|
||||||
PduBody stripped = new PduBody();
|
PduBody stripped = new PduBody();
|
||||||
|
|
||||||
for (int i=0;i<body.getPartsNum();i++) {
|
for (int i=0;i<body.getPartsNum();i++) {
|
||||||
if (isDisplayableMedia(body.getPart(i))) {
|
if (isDisplayableMedia(body.getPart(i)) || isDocument(body.getPart(i))) {
|
||||||
stripped.addPart(body.getPart(i));
|
stripped.addPart(body.getPart(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,7 +64,7 @@ public class PartParser {
|
||||||
int partCount = 0;
|
int partCount = 0;
|
||||||
|
|
||||||
for (int i=0;i<body.getPartsNum();i++) {
|
for (int i=0;i<body.getPartsNum();i++) {
|
||||||
if (isDisplayableMedia(body.getPart(i))) {
|
if (isDisplayableMedia(body.getPart(i)) || isDocument(body.getPart(i))) {
|
||||||
partCount++;
|
partCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,4 +91,8 @@ public class PartParser {
|
||||||
public static boolean isDisplayableMedia(PduPart part) {
|
public static boolean isDisplayableMedia(PduPart part) {
|
||||||
return isImage(part) || isAudio(part) || isVideo(part);
|
return isImage(part) || isAudio(part) || isVideo(part);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isDocument(PduPart part) {
|
||||||
|
return DOCUMENT_TYPES.contains(Util.toIsoString(part.getContentType()).toLowerCase());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -204,7 +204,10 @@ public abstract class Slide {
|
||||||
Optional<String> fileName = getFileName();
|
Optional<String> fileName = getFileName();
|
||||||
|
|
||||||
if (fileName.isPresent()) {
|
if (fileName.isPresent()) {
|
||||||
return Optional.of(getFileType(fileName));
|
String fileType = getFileType(fileName);
|
||||||
|
if (!fileType.isEmpty()) {
|
||||||
|
return Optional.of(fileType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Optional.fromNullable(MediaUtil.getExtension(context, getUri()));
|
return Optional.fromNullable(MediaUtil.getExtension(context, getUri()));
|
||||||
|
|
Loading…
Add table
Reference in a new issue