Signal-Android/app/src/main/java/org/thoughtcrime/securesms/contactshare/VCardUtil.java

161 lines
6.9 KiB
Java

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;
}
}