| /* |
| * Copyright (C) 2007 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.syncml.pim.vcard; |
| |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.commons.codec.binary.Base64; |
| |
| import android.provider.Contacts; |
| import android.syncml.pim.vcard.ContactStruct.PhoneData; |
| |
| /** |
| * Compose VCard string |
| */ |
| public class VCardComposer { |
| final public static int VERSION_VCARD21_INT = 1; |
| |
| final public static int VERSION_VCARD30_INT = 2; |
| |
| /** |
| * A new line |
| */ |
| private String mNewline; |
| |
| /** |
| * The composed string |
| */ |
| private StringBuilder mResult; |
| |
| /** |
| * The email's type |
| */ |
| static final private HashSet<String> emailTypes = new HashSet<String>( |
| Arrays.asList("CELL", "AOL", "APPLELINK", "ATTMAIL", "CIS", |
| "EWORLD", "INTERNET", "IBMMAIL", "MCIMAIL", "POWERSHARE", |
| "PRODIGY", "TLX", "X400")); |
| |
| static final private HashSet<String> phoneTypes = new HashSet<String>( |
| Arrays.asList("PREF", "WORK", "HOME", "VOICE", "FAX", "MSG", |
| "CELL", "PAGER", "BBS", "MODEM", "CAR", "ISDN", "VIDEO")); |
| |
| static final private String TAG = "VCardComposer"; |
| |
| public VCardComposer() { |
| } |
| |
| private static final HashMap<Integer, String> phoneTypeMap = new HashMap<Integer, String>(); |
| |
| private static final HashMap<Integer, String> emailTypeMap = new HashMap<Integer, String>(); |
| |
| static { |
| phoneTypeMap.put(Contacts.Phones.TYPE_HOME, "HOME"); |
| phoneTypeMap.put(Contacts.Phones.TYPE_MOBILE, "CELL"); |
| phoneTypeMap.put(Contacts.Phones.TYPE_WORK, "WORK"); |
| // FAX_WORK not exist in vcard spec. The approximate is the combine of |
| // WORK and FAX, here only map to FAX |
| phoneTypeMap.put(Contacts.Phones.TYPE_FAX_WORK, "WORK;FAX"); |
| phoneTypeMap.put(Contacts.Phones.TYPE_FAX_HOME, "HOME;FAX"); |
| phoneTypeMap.put(Contacts.Phones.TYPE_PAGER, "PAGER"); |
| phoneTypeMap.put(Contacts.Phones.TYPE_OTHER, "X-OTHER"); |
| emailTypeMap.put(Contacts.ContactMethods.TYPE_HOME, "HOME"); |
| emailTypeMap.put(Contacts.ContactMethods.TYPE_WORK, "WORK"); |
| } |
| |
| /** |
| * Create a vCard String. |
| * |
| * @param struct |
| * see more from ContactStruct class |
| * @param vcardversion |
| * MUST be VERSION_VCARD21 /VERSION_VCARD30 |
| * @return vCard string |
| * @throws VCardException |
| * struct.name is null /vcardversion not match |
| */ |
| public String createVCard(ContactStruct struct, int vcardversion) |
| throws VCardException { |
| |
| mResult = new StringBuilder(); |
| // check exception: |
| if (struct.name == null || struct.name.trim().equals("")) { |
| throw new VCardException(" struct.name MUST have value."); |
| } |
| if (vcardversion == VERSION_VCARD21_INT) { |
| mNewline = "\r\n"; |
| } else if (vcardversion == VERSION_VCARD30_INT) { |
| mNewline = "\n"; |
| } else { |
| throw new VCardException( |
| " version not match VERSION_VCARD21 or VERSION_VCARD30."); |
| } |
| // build vcard: |
| mResult.append("BEGIN:VCARD").append(mNewline); |
| |
| if (vcardversion == VERSION_VCARD21_INT) { |
| mResult.append("VERSION:2.1").append(mNewline); |
| } else { |
| mResult.append("VERSION:3.0").append(mNewline); |
| } |
| |
| if (!isNull(struct.name)) { |
| appendNameStr(struct.name); |
| } |
| |
| if (!isNull(struct.company)) { |
| mResult.append("ORG:").append(struct.company).append(mNewline); |
| } |
| |
| if (!isNull(struct.notes)) { |
| mResult.append("NOTE:").append( |
| foldingString(struct.notes, vcardversion)).append(mNewline); |
| } |
| |
| if (!isNull(struct.title)) { |
| mResult.append("TITLE:").append( |
| foldingString(struct.title, vcardversion)).append(mNewline); |
| } |
| |
| if (struct.photoBytes != null) { |
| appendPhotoStr(struct.photoBytes, struct.photoType, vcardversion); |
| } |
| |
| if (struct.phoneList != null) { |
| appendPhoneStr(struct.phoneList, vcardversion); |
| } |
| |
| if (struct.contactmethodList != null) { |
| appendContactMethodStr(struct.contactmethodList, vcardversion); |
| } |
| |
| mResult.append("END:VCARD").append(mNewline); |
| return mResult.toString(); |
| } |
| |
| /** |
| * Alter str to folding supported format. |
| * |
| * @param str |
| * the string to be folded |
| * @param version |
| * the vcard version |
| * @return the folded string |
| */ |
| private String foldingString(String str, int version) { |
| if (str.endsWith("\r\n")) { |
| str = str.substring(0, str.length() - 2); |
| } else if (str.endsWith("\n")) { |
| str = str.substring(0, str.length() - 1); |
| } else { |
| return null; |
| } |
| |
| str = str.replaceAll("\r\n", "\n"); |
| if (version == VERSION_VCARD21_INT) { |
| return str.replaceAll("\n", "\r\n "); |
| } else if (version == VERSION_VCARD30_INT) { |
| return str.replaceAll("\n", "\n "); |
| } else { |
| return null; |
| } |
| } |
| |
| /** |
| * Build LOGO property. format LOGO's param and encode value as base64. |
| * |
| * @param bytes |
| * the binary string to be converted |
| * @param type |
| * the type of the content |
| * @param version |
| * the version of vcard |
| */ |
| private void appendPhotoStr(byte[] bytes, String type, int version) |
| throws VCardException { |
| String value, apptype, encodingStr; |
| try { |
| value = foldingString(new String(Base64.encodeBase64(bytes, true)), |
| version); |
| } catch (Exception e) { |
| throw new VCardException(e.getMessage()); |
| } |
| |
| if (isNull(type)) { |
| type = "image/jpeg"; |
| } |
| if (type.indexOf("jpeg") > 0) { |
| apptype = "JPEG"; |
| } else if (type.indexOf("gif") > 0) { |
| apptype = "GIF"; |
| } else if (type.indexOf("bmp") > 0) { |
| apptype = "BMP"; |
| } else { |
| apptype = type.substring(type.indexOf("/")).toUpperCase(); |
| } |
| |
| mResult.append("LOGO;TYPE=").append(apptype); |
| if (version == VERSION_VCARD21_INT) { |
| encodingStr = ";ENCODING=BASE64:"; |
| value = value + mNewline; |
| } else if (version == VERSION_VCARD30_INT) { |
| encodingStr = ";ENCODING=b:"; |
| } else { |
| return; |
| } |
| mResult.append(encodingStr).append(value).append(mNewline); |
| } |
| |
| private boolean isNull(String str) { |
| if (str == null || str.trim().equals("")) { |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Build FN and N property. format N's value. |
| * |
| * @param name |
| * the name of the contact |
| */ |
| private void appendNameStr(String name) { |
| mResult.append("FN:").append(name).append(mNewline); |
| mResult.append("N:").append(name).append(mNewline); |
| /* |
| * if(name.indexOf(";") > 0) |
| * mResult.append("N:").append(name).append(mNewline); else |
| * if(name.indexOf(" ") > 0) mResult.append("N:").append(name.replace(' ', |
| * ';')). append(mNewline); else |
| * mResult.append("N:").append(name).append("; ").append(mNewline); |
| */ |
| } |
| |
| /** Loop append TEL property. */ |
| private void appendPhoneStr(List<ContactStruct.PhoneData> phoneList, |
| int version) { |
| HashMap<String, String> numMap = new HashMap<String, String>(); |
| String joinMark = version == VERSION_VCARD21_INT ? ";" : ","; |
| |
| for (ContactStruct.PhoneData phone : phoneList) { |
| String type; |
| if (!isNull(phone.data)) { |
| type = getPhoneTypeStr(phone); |
| if (version == VERSION_VCARD30_INT && type.indexOf(";") != -1) { |
| type = type.replace(";", ","); |
| } |
| if (numMap.containsKey(phone.data)) { |
| type = numMap.get(phone.data) + joinMark + type; |
| } |
| numMap.put(phone.data, type); |
| } |
| } |
| |
| for (Map.Entry<String, String> num : numMap.entrySet()) { |
| if (version == VERSION_VCARD21_INT) { |
| mResult.append("TEL;"); |
| } else { // vcard3.0 |
| mResult.append("TEL;TYPE="); |
| } |
| mResult.append(num.getValue()).append(":").append(num.getKey()) |
| .append(mNewline); |
| } |
| } |
| |
| private String getPhoneTypeStr(PhoneData phone) { |
| |
| int phoneType = Integer.parseInt(phone.type); |
| String typeStr, label; |
| |
| if (phoneTypeMap.containsKey(phoneType)) { |
| typeStr = phoneTypeMap.get(phoneType); |
| } else if (phoneType == Contacts.Phones.TYPE_CUSTOM) { |
| label = phone.label.toUpperCase(); |
| if (phoneTypes.contains(label) || label.startsWith("X-")) { |
| typeStr = label; |
| } else { |
| typeStr = "X-CUSTOM-" + label; |
| } |
| } else { |
| // TODO: need be updated with the provider's future changes |
| typeStr = "VOICE"; // the default type is VOICE in spec. |
| } |
| return typeStr; |
| } |
| |
| /** Loop append ADR / EMAIL property. */ |
| private void appendContactMethodStr( |
| List<ContactStruct.ContactMethod> contactMList, int version) { |
| |
| HashMap<String, String> emailMap = new HashMap<String, String>(); |
| String joinMark = version == VERSION_VCARD21_INT ? ";" : ","; |
| for (ContactStruct.ContactMethod contactMethod : contactMList) { |
| // same with v2.1 and v3.0 |
| switch (Integer.parseInt(contactMethod.kind)) { |
| case Contacts.KIND_EMAIL: |
| String mailType = "INTERNET"; |
| if (!isNull(contactMethod.data)) { |
| int methodType = new Integer(contactMethod.type).intValue(); |
| if (emailTypeMap.containsKey(methodType)) { |
| mailType = emailTypeMap.get(methodType); |
| } else if (emailTypes.contains(contactMethod.label |
| .toUpperCase())) { |
| mailType = contactMethod.label.toUpperCase(); |
| } |
| if (emailMap.containsKey(contactMethod.data)) { |
| mailType = emailMap.get(contactMethod.data) + joinMark |
| + mailType; |
| } |
| emailMap.put(contactMethod.data, mailType); |
| } |
| break; |
| case Contacts.KIND_POSTAL: |
| if (!isNull(contactMethod.data)) { |
| mResult.append("ADR;TYPE=POSTAL:").append( |
| foldingString(contactMethod.data, version)).append( |
| mNewline); |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| for (Map.Entry<String, String> email : emailMap.entrySet()) { |
| if (version == VERSION_VCARD21_INT) { |
| mResult.append("EMAIL;"); |
| } else { |
| mResult.append("EMAIL;TYPE="); |
| } |
| mResult.append(email.getValue()).append(":").append(email.getKey()) |
| .append(mNewline); |
| } |
| } |
| } |