Android VCard通讯录导入导出详解

详细介绍Android通讯录的导出和导入

通讯录导出到SD卡时,会在SD卡中生成一个vcf文件,用于保存联系人姓名,手机号码。

vCard 规范容许公开交换个人数据交换 (Personal Data Interchange PDI)信息,在传统纸质商业名片可找到这些信息。规范定义电子名片(或叫vCard)的格式。

而在Android上使用vcard就要借助第三方包:我已经上传,看后面的链接地址

下载后里面会有2个Example {ReadExample.java / WriteExample.java} 。
但是凭借这两个Example,不足以让你更好的完成其他信息的备份和恢复,于是你要看下源码。

其中比较的2个类的源码如下.

 

/*
 * 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 a_vcard.android.syncml.pim.vcard;

//import android.content.AbstractSyncableContentProvider;
//import android.content.ContentResolver;
//import android.content.ContentUris;
//import android.content.ContentValues;
//import android.net.Uri;
import a_vcard.android.provider.Contacts;
import a_vcard.android.provider.Contacts.ContactMethods;
//import android.provider.Contacts.Extensions;
//import android.provider.Contacts.GroupMembership;
//import android.provider.Contacts.Organizations;
//import android.provider.Contacts.People;
import a_vcard.android.provider.Contacts.Phones;
//import android.provider.Contacts.Photos;
import a_vcard.android.syncml.pim.PropertyNode;
import a_vcard.android.syncml.pim.VNode;
import a_vcard.android.telephony.PhoneNumberUtils;
import a_vcard.android.text.TextUtils;
import a_vcard.android.util.Log;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;

/**
 * The parameter class of VCardComposer.
 * This class standy by the person-contact in
 * Android system, we must use this class instance as parameter to transmit to
 * VCardComposer so that create vCard string.
 */
// TODO: rename the class name, next step
public class ContactStruct {
    private static final String LOG_TAG = "ContactStruct";
    
    // Note: phonetic name probably should be "LAST FIRST MIDDLE" for European languages, and
    //       space should be added between each element while it should not be in Japanese.
    //       But unfortunately, we currently do not have the data and are not sure whether we should
    //       support European version of name ordering.
    //
    // TODO: Implement the logic described above if we really need European version of
    //        phonetic name handling. Also, adding the appropriate test case of vCard would be
    //        highly appreciated.
    public static final int NAME_ORDER_TYPE_ENGLISH = 0;
    public static final int NAME_ORDER_TYPE_JAPANESE = 1;

    /** MUST exist */
    public String name;
    public String phoneticName;
    /** maybe folding */
    public List notes = new ArrayList();
    /** maybe folding */
    public String title;
    /** binary bytes of pic. */
    public byte[] photoBytes;
    /** The type of Photo (e.g. JPEG, BMP, etc.) */
    public String photoType;
    /** Only for GET. Use addPhoneList() to PUT. */
    public List phoneList;
    /** Only for GET. Use addContactmethodList() to PUT. */
    public List contactmethodList;
    /** Only for GET. Use addOrgList() to PUT. */
    public List organizationList;
    /** Only for GET. Use addExtension() to PUT */
    public Map> extensionMap;

    // Use organizationList instead when handling ORG.
    @Deprecated
    public String company;
    
    public static class PhoneData {
        public int type;
        /** maybe folding */
        public String data;
        public String label;
        public boolean isPrimary; 
    }

    public static class ContactMethod {
        // Contacts.KIND_EMAIL, Contacts.KIND_POSTAL
        public int kind;
        // e.g. Contacts.ContactMethods.TYPE_HOME, Contacts.PhoneColumns.TYPE_HOME
        // If type == Contacts.PhoneColumns.TYPE_CUSTOM, label is used.
        public int type;
        public String data;
        // Used only when TYPE is TYPE_CUSTOM.
        public String label;
        public boolean isPrimary;
    }
    
    public static class OrganizationData {
        public int type;
        public String companyName;
        public String positionName;
        public boolean isPrimary;
    }

    /**
     * Add a phone info to phoneList.
     * @param data phone number
     * @param type type col of content://contacts/phones
     * @param label lable col of content://contacts/phones
     */
    public void addPhone(int type, String data, String label, boolean isPrimary){
        if (phoneList == null) {
            phoneList = new ArrayList();
        }
        PhoneData phoneData = new PhoneData();
        phoneData.type = type;
        
        StringBuilder builder = new StringBuilder();
        String trimed = data.trim();
        int length = trimed.length();
        for (int i = 0; i < length; i++) {
            char ch = trimed.charAt(i);
            if (('0' <= ch && ch <= '9') || (i == 0 && ch == '+')) {
                builder.append(ch);
            }
        }
        phoneData.data = PhoneNumberUtils.formatNumber(builder.toString());
        phoneData.label = label;
        phoneData.isPrimary = isPrimary;
        phoneList.add(phoneData);
    }

    /**
     * Add a contactmethod info to contactmethodList.
     * @param kind integer value defined in Contacts.java
     * (e.g. Contacts.KIND_EMAIL)
     * @param type type col of content://contacts/contact_methods
     * @param data contact data
     * @param label extra string used only when kind is Contacts.KIND_CUSTOM.
     */
    public void addContactmethod(int kind, int type, String data,
            String label, boolean isPrimary){
        if (contactmethodList == null) {
            contactmethodList = new ArrayList();
        }
        ContactMethod contactMethod = new ContactMethod();
        contactMethod.kind = kind;
        contactMethod.type = type;
        contactMethod.data = data;
        contactMethod.label = label;
        contactMethod.isPrimary = isPrimary;
        contactmethodList.add(contactMethod);
    }
    
    /**
     * Add a Organization info to organizationList.
     */
    public void addOrganization(int type, String companyName, String positionName,
            boolean isPrimary) {
        if (organizationList == null) {
            organizationList = new ArrayList();
        }
        OrganizationData organizationData = new OrganizationData();
        organizationData.type = type;
        organizationData.companyName = companyName;
        organizationData.positionName = positionName;
        organizationData.isPrimary = isPrimary;
        organizationList.add(organizationData);
    }

    /**
     * Set "position" value to the appropriate data. If there's more than one
     * OrganizationData objects, the value is set to the last one. If there's no
     * OrganizationData object, a new OrganizationData is created, whose company name is
     * empty.  
     * 
     * TODO: incomplete logic. fix this:
     * 
     * e.g. This assumes ORG comes earlier, but TITLE may come earlier like this, though we do not
     * know how to handle it in general cases...
     * ----
     * TITLE:Software Engineer
     * ORG:Google
     * ----
     */
    public void setPosition(String positionValue) {
        if (organizationList == null) {
            organizationList = new ArrayList();
        }
        int size = organizationList.size();
        if (size == 0) {
            addOrganization(Contacts.OrganizationColumns.TYPE_OTHER, "", null, false);
            size = 1;
        }
        OrganizationData lastData = organizationList.get(size - 1);
        lastData.positionName = positionValue;
    }
    
    public void addExtension(PropertyNode propertyNode) {
        if (propertyNode.propValue.length() == 0) {
            return;
        }
        // Now store the string into extensionMap.
        List list;
        String name = propertyNode.propName;
        if (extensionMap == null) {
            extensionMap = new HashMap>();
        }
        if (!extensionMap.containsKey(name)){
            list = new ArrayList();
            extensionMap.put(name, list);
        } else {
            list = extensionMap.get(name);
        }        
        
        list.add(propertyNode.encode());
    }
    
    private static String getNameFromNProperty(List elems, int nameOrderType) {
        // Family, Given, Middle, Prefix, Suffix. (1 - 5)
        int size = elems.size();
        if (size > 1) {
            StringBuilder builder = new StringBuilder();
            boolean builderIsEmpty = true;
            // Prefix
            if (size > 3 && elems.get(3).length() > 0) {
                builder.append(elems.get(3));
                builderIsEmpty = false;
            }
            String first, second;
            if (nameOrderType == NAME_ORDER_TYPE_JAPANESE) {
                first = elems.get(0);
                second = elems.get(1);
            } else {
                first = elems.get(1);
                second = elems.get(0);
            }
            if (first.length() > 0) {
                if (!builderIsEmpty) {
                    builder.append(' ');
                }
                builder.append(first);
                builderIsEmpty = false;
            }
            // Middle name
            if (size > 2 && elems.get(2).length() > 0) {
                if (!builderIsEmpty) {
                    builder.append(' ');
                }
                builder.append(elems.get(2));
                builderIsEmpty = false;
            }
            if (second.length() > 0) {
                if (!builderIsEmpty) {
                    builder.append(' ');
                }
                builder.append(second);
                builderIsEmpty = false;
            }
            // Suffix
            if (size > 4 && elems.get(4).length() > 0) {
                if (!builderIsEmpty) {
                    builder.append(' ');
                }
                builder.append(elems.get(4));
                builderIsEmpty = false;
            }
            return builder.toString();
        } else if (size == 1) {
            return elems.get(0);
        } else {
            return "";
        }
    }
    
    public static ContactStruct constructContactFromVNode(VNode node,
            int nameOrderType) {
        if (!node.VName.equals("VCARD")) {
            // Impossible in current implementation. Just for safety.
            Log.e(LOG_TAG, "Non VCARD data is inserted.");
            return null;
        }

        // For name, there are three fields in vCard: FN, N, NAME.
        // We prefer FN, which is a required field in vCard 3.0 , but not in vCard 2.1.
        // Next, we prefer NAME, which is defined only in vCard 3.0.
        // Finally, we use N, which is a little difficult to parse.
        String fullName = null;
        String nameFromNProperty = null;

        // Some vCard has "X-PHONETIC-FIRST-NAME", "X-PHONETIC-MIDDLE-NAME", and
        // "X-PHONETIC-LAST-NAME"
        String xPhoneticFirstName = null;
        String xPhoneticMiddleName = null;
        String xPhoneticLastName = null;
        
        ContactStruct contact = new ContactStruct();

        // Each Column of four properties has ISPRIMARY field
        // (See android.provider.Contacts)
        // If false even after the following loop, we choose the first
        // entry as a "primary" entry.
        boolean prefIsSetAddress = false;
        boolean prefIsSetPhone = false;
        boolean prefIsSetEmail = false;
        boolean prefIsSetOrganization = false;
        
        for (PropertyNode propertyNode: node.propList) {
            String name = propertyNode.propName;

            if (TextUtils.isEmpty(propertyNode.propValue)) {
                continue;
            }
            
            if (name.equals("VERSION")) {
                // vCard version. Ignore this.
            } else if (name.equals("FN")) {
                fullName = propertyNode.propValue;
            } else if (name.equals("NAME") && fullName == null) {
                // Only in vCard 3.0. Use this if FN does not exist.
                // Though, note that vCard 3.0 requires FN.
                fullName = propertyNode.propValue;
            } else if (name.equals("N")) {
                nameFromNProperty = getNameFromNProperty(propertyNode.propValue_vector,
                        nameOrderType);
            } else if (name.equals("SORT-STRING")) {
                contact.phoneticName = propertyNode.propValue;
            } else if (name.equals("SOUND")) {
                if (propertyNode.paramMap_TYPE.contains("X-IRMC-N") &&
                        contact.phoneticName == null) {
                    // Some Japanese mobile phones use this field for phonetic name,
                    // since vCard 2.1 does not have "SORT-STRING" type.
                    // Also, in some cases, the field has some ';' in it.
                    // We remove them.
                    StringBuilder builder = new StringBuilder();
                    String value = propertyNode.propValue;
                    int length = value.length();
                    for (int i = 0; i < length; i++) {
                        char ch = value.charAt(i);
                        if (ch != ';') {
                            builder.append(ch);
                        }
                    }
                    contact.phoneticName = builder.toString();
                } else {
                    contact.addExtension(propertyNode);
                }
            } else if (name.equals("ADR")) {
                List values = propertyNode.propValue_vector;
                boolean valuesAreAllEmpty = true;
                for (String value : values) {
                    if (value.length() > 0) {
                        valuesAreAllEmpty = false;
                        break;
                    }
                }
                if (valuesAreAllEmpty) {
                    continue;
                }

                int kind = Contacts.KIND_POSTAL;
                int type = -1;
                String label = "";
                boolean isPrimary = false;
                for (String typeString : propertyNode.paramMap_TYPE) {
                    if (typeString.equals("PREF") && !prefIsSetAddress) {
                        // Only first "PREF" is considered.
                        prefIsSetAddress = true;
                        isPrimary = true;
                    } else if (typeString.equalsIgnoreCase("HOME")) {
                        type = Contacts.ContactMethodsColumns.TYPE_HOME;
                        label = "";
                    } else if (typeString.equalsIgnoreCase("WORK") || 
                            typeString.equalsIgnoreCase("COMPANY")) {
                        // "COMPANY" seems emitted by Windows Mobile, which is not
                        // specifically supported by vCard 2.1. We assume this is same
                        // as "WORK".
                        type = Contacts.ContactMethodsColumns.TYPE_WORK;
                        label = "";
                    } else if (typeString.equalsIgnoreCase("POSTAL")) {
                        kind = Contacts.KIND_POSTAL;
                    } else if (typeString.equalsIgnoreCase("PARCEL") || 
                            typeString.equalsIgnoreCase("DOM") ||
                            typeString.equalsIgnoreCase("INTL")) {
                        // We do not have a kind or type matching these.
                        // TODO: fix this. We may need to split entries into two.
                        // (e.g. entries for KIND_POSTAL and KIND_PERCEL)
                    } else if (typeString.toUpperCase().startsWith("X-") &&
                            type < 0) {
                        type = Contacts.ContactMethodsColumns.TYPE_CUSTOM;
                        label = typeString.substring(2);
                    } else if (type < 0) {
                        // vCard 3.0 allows iana-token. Also some vCard 2.1 exporters
                        // emit non-standard types. We do not handle their values now.
                        type = Contacts.ContactMethodsColumns.TYPE_CUSTOM;
                        label = typeString;
                    }
                }
                // We use "HOME" as default
                if (type < 0) {
                    type = Contacts.ContactMethodsColumns.TYPE_HOME;
                }
                                
                // adr-value    = 0*6(text-value ";") text-value
                //              ; PO Box, Extended Address, Street, Locality, Region, Postal
                //              ; Code, Country Name
                String address;
                List list = propertyNode.propValue_vector;
                int size = list.size();
                if (size > 1) {
                    StringBuilder builder = new StringBuilder();
                    boolean builderIsEmpty = true;
                    if (Locale.getDefault().getCountry().equals(Locale.JAPAN.getCountry())) {
                        // In Japan, the order is reversed.
                        for (int i = size - 1; i >= 0; i--) {
                            String addressPart = list.get(i);
                            if (addressPart.length() > 0) {
                                if (!builderIsEmpty) {
                                    builder.append(' ');
                                }
                                builder.append(addressPart);
                                builderIsEmpty = false;
                            }
                        }
                    } else {
                        for (int i = 0; i < size; i++) {
                            String addressPart = list.get(i);
                            if (addressPart.length() > 0) {
                                if (!builderIsEmpty) {
                                    builder.append(' ');
                                }
                                builder.append(addressPart);
                                builderIsEmpty = false;
                            }
                        }
                    }
                    address = builder.toString().trim();
                } else {
                    address = propertyNode.propValue; 
                }
                contact.addContactmethod(kind, type, address, label, isPrimary);
            } else if (name.equals("ORG")) {
                // vCard specification does not specify other types.
                int type = Contacts.OrganizationColumns.TYPE_WORK;
                boolean isPrimary = false;
                
                for (String typeString : propertyNode.paramMap_TYPE) {
                    if (typeString.equals("PREF") && !prefIsSetOrganization) {
                        // vCard specification officially does not have PREF in ORG.
                        // This is just for safety.
                        prefIsSetOrganization = true;
                        isPrimary = true;
                    }
                    // XXX: Should we cope with X- words?
                }

                List list = propertyNode.propValue_vector; 
                int size = list.size();
                StringBuilder builder = new StringBuilder();
                for (Iterator iter = list.iterator(); iter.hasNext();) {
                    builder.append(iter.next());
                    if (iter.hasNext()) {
                        builder.append(' ');
                    }
                }

                contact.addOrganization(type, builder.toString(), "", isPrimary);
            } else if (name.equals("TITLE")) {
                contact.setPosition(propertyNode.propValue);
            } else if (name.equals("ROLE")) {
                contact.setPosition(propertyNode.propValue);
            } else if (name.equals("PHOTO")) {
                // We prefer PHOTO to LOGO.
                String valueType = propertyNode.paramMap.getAsString("VALUE");
                if (valueType != null && valueType.equals("URL")) {
                    // TODO: do something.
                } else {
                    // Assume PHOTO is stored in BASE64. In that case,
                    // data is already stored in propValue_bytes in binary form.
                    // It should be automatically done by VBuilder (VDataBuilder/VCardDatabuilder) 
                    contact.photoBytes = propertyNode.propValue_bytes;
                    String type = propertyNode.paramMap.getAsString("TYPE");
                    if (type != null) {
                        contact.photoType = type;
                    }
                }
            } else if (name.equals("LOGO")) {
                // When PHOTO is not available this is not URL,
                // we use this instead of PHOTO.
                String valueType = propertyNode.paramMap.getAsString("VALUE");
                if (valueType != null && valueType.equals("URL")) {
                    // TODO: do something.
                } else if (contact.photoBytes == null) {
                    contact.photoBytes = propertyNode.propValue_bytes;
                    String type = propertyNode.paramMap.getAsString("TYPE");
                    if (type != null) {
                        contact.photoType = type;
                    }
                }
            } else if (name.equals("EMAIL")) {
                int type = -1;
                String label = null;
                boolean isPrimary = false;
                for (String typeString : propertyNode.paramMap_TYPE) {
                    if (typeString.equals("PREF") && !prefIsSetEmail) {
                        // Only first "PREF" is considered.
                        prefIsSetEmail = true;
                        isPrimary = true;
                    } else if (typeString.equalsIgnoreCase("HOME")) {
                        type = Contacts.ContactMethodsColumns.TYPE_HOME;
                    } else if (typeString.equalsIgnoreCase("WORK")) {
                        type = Contacts.ContactMethodsColumns.TYPE_WORK;
                    } else if (typeString.equalsIgnoreCase("CELL")) {
                        // We do not have Contacts.ContactMethodsColumns.TYPE_MOBILE yet.
                        type = Contacts.ContactMethodsColumns.TYPE_CUSTOM;
                        label = Contacts.ContactMethodsColumns.MOBILE_EMAIL_TYPE_NAME;
                    } else if (typeString.toUpperCase().startsWith("X-") &&
                            type < 0) {
                        type = Contacts.ContactMethodsColumns.TYPE_CUSTOM;
                        label = typeString.substring(2);
                    } else if (type < 0) {
                        // vCard 3.0 allows iana-token.
                        // We may have INTERNET (specified in vCard spec),
                        // SCHOOL, etc.
                        type = Contacts.ContactMethodsColumns.TYPE_CUSTOM;
                        label = typeString;
                    }
                }
                // We use "OTHER" as default.
                if (type < 0) {
                    type = Contacts.ContactMethodsColumns.TYPE_OTHER;
                }
                contact.addContactmethod(Contacts.KIND_EMAIL,
                        type, propertyNode.propValue,label, isPrimary);
            } else if (name.equals("TEL")) {
                int type = -1;
                String label = null;
                boolean isPrimary = false;
                boolean isFax = false;
                for (String typeString : propertyNode.paramMap_TYPE) {
                    if (typeString.equals("PREF") && !prefIsSetPhone) {
                        // Only first "PREF" is considered.
                        prefIsSetPhone = true;
                        isPrimary = true;
                    } else if (typeString.equalsIgnoreCase("HOME")) {
                        type = Contacts.PhonesColumns.TYPE_HOME;
                    } else if (typeString.equalsIgnoreCase("WORK")) {
                        type = Contacts.PhonesColumns.TYPE_WORK;
                    } else if (typeString.equalsIgnoreCase("CELL")) {
                        type = Contacts.PhonesColumns.TYPE_MOBILE;
                    } else if (typeString.equalsIgnoreCase("PAGER")) {
                        type = Contacts.PhonesColumns.TYPE_PAGER;
                    } else if (typeString.equalsIgnoreCase("FAX")) {
                        isFax = true;
                    } else if (typeString.equalsIgnoreCase("VOICE") ||
                            typeString.equalsIgnoreCase("MSG")) {
                        // Defined in vCard 3.0. Ignore these because they
                        // conflict with "HOME", "WORK", etc.
                        // XXX: do something?
                    } else if (typeString.toUpperCase().startsWith("X-") &&
                            type < 0) {
                        type = Contacts.PhonesColumns.TYPE_CUSTOM;
                        label = typeString.substring(2);
                    } else if (type < 0){
                        // We may have MODEM, CAR, ISDN, etc...
                        type = Contacts.PhonesColumns.TYPE_CUSTOM;
                        label = typeString;
                    }
                }
                // We use "HOME" as default
                if (type < 0) {
                    type = Contacts.PhonesColumns.TYPE_HOME;
                }
                if (isFax) {
                    if (type == Contacts.PhonesColumns.TYPE_HOME) {
                        type = Contacts.PhonesColumns.TYPE_FAX_HOME; 
                    } else if (type == Contacts.PhonesColumns.TYPE_WORK) {
                        type = Contacts.PhonesColumns.TYPE_FAX_WORK; 
                    }
                }

                contact.addPhone(type, propertyNode.propValue, label, isPrimary);
            } else if (name.equals("NOTE")) {
                contact.notes.add(propertyNode.propValue);
            } else if (name.equals("BDAY")) {
                contact.addExtension(propertyNode);
            } else if (name.equals("URL")) {
                contact.addExtension(propertyNode);
            } else if (name.equals("REV")) {                
                // Revision of this VCard entry. I think we can ignore this.
                contact.addExtension(propertyNode);
            } else if (name.equals("UID")) {
                contact.addExtension(propertyNode);
            } else if (name.equals("KEY")) {
                // Type is X509 or PGP? I don't know how to handle this...
                contact.addExtension(propertyNode);
            } else if (name.equals("MAILER")) {
                contact.addExtension(propertyNode);
            } else if (name.equals("TZ")) {
                contact.addExtension(propertyNode);
            } else if (name.equals("GEO")) {
                contact.addExtension(propertyNode);
            } else if (name.equals("NICKNAME")) {
                // vCard 3.0 only.
                contact.addExtension(propertyNode);
            } else if (name.equals("CLASS")) {
                // vCard 3.0 only.
                // e.g. CLASS:CONFIDENTIAL
                contact.addExtension(propertyNode);
            } else if (name.equals("PROFILE")) {
                // VCard 3.0 only. Must be "VCARD". I think we can ignore this.
                contact.addExtension(propertyNode);
            } else if (name.equals("CATEGORIES")) {
                // VCard 3.0 only.
                // e.g. CATEGORIES:INTERNET,IETF,INDUSTRY,INFORMATION TECHNOLOGY
                contact.addExtension(propertyNode);
            } else if (name.equals("SOURCE")) {
                // VCard 3.0 only.
                contact.addExtension(propertyNode);
            } else if (name.equals("PRODID")) {
                // VCard 3.0 only.
                // To specify the identifier for the product that created
                // the vCard object.
                contact.addExtension(propertyNode);
            } else if (name.equals("X-PHONETIC-FIRST-NAME")) {
                xPhoneticFirstName = propertyNode.propValue;
            } else if (name.equals("X-PHONETIC-MIDDLE-NAME")) {
                xPhoneticMiddleName = propertyNode.propValue;
            } else if (name.equals("X-PHONETIC-LAST-NAME")) {
                xPhoneticLastName = propertyNode.propValue;
            } else {
                // Unknown X- words and IANA token.
                contact.addExtension(propertyNode);
            }
        }

        if (fullName != null) {
            contact.name = fullName;
        } else if(nameFromNProperty != null) {
            contact.name = nameFromNProperty;
        } else {
            contact.name = "";
        }

        if (contact.phoneticName == null &&
                (xPhoneticFirstName != null || xPhoneticMiddleName != null ||
                        xPhoneticLastName != null)) {
            // Note: In Europe, this order should be "LAST FIRST MIDDLE". See the comment around
            //       NAME_ORDER_TYPE_* for more detail.
            String first;
            String second;
            if (nameOrderType == NAME_ORDER_TYPE_JAPANESE) {
                first = xPhoneticLastName;
                second = xPhoneticFirstName;
            } else {
                first = xPhoneticFirstName;
                second = xPhoneticLastName;
            }
            StringBuilder builder = new StringBuilder();
            if (first != null) {
                builder.append(first);
            }
            if (xPhoneticMiddleName != null) {
                builder.append(xPhoneticMiddleName);
            }
            if (second != null) {
                builder.append(second);
            }
            contact.phoneticName = builder.toString();
        }
        
        // Remove unnecessary white spaces.
        // It is found that some mobile phone emits  phonetic name with just one white space
        // when a user does not specify one.
        // This logic is effective toward such kind of weird data.
        if (contact.phoneticName != null) {
            contact.phoneticName = contact.phoneticName.trim();
        }

        // If there is no "PREF", we choose the first entries as primary.
        if (!prefIsSetPhone &&
                contact.phoneList != null && 
                contact.phoneList.size() > 0) {
            contact.phoneList.get(0).isPrimary = true;
        }

        if (!prefIsSetAddress && contact.contactmethodList != null) {
            for (ContactMethod contactMethod : contact.contactmethodList) {
                if (contactMethod.kind == Contacts.KIND_POSTAL) {
                    contactMethod.isPrimary = true;
                    break;
                }
            }
        }
        if (!prefIsSetEmail && contact.contactmethodList != null) {
            for (ContactMethod contactMethod : contact.contactmethodList) {
                if (contactMethod.kind == Contacts.KIND_EMAIL) {
                    contactMethod.isPrimary = true;
                    break;
                }
            }
        }
        if (!prefIsSetOrganization &&
                contact.organizationList != null &&
                contact.organizationList.size() > 0) {
            contact.organizationList.get(0).isPrimary = true;
        }
        
        return contact;
    }
    
    public String displayString() {
        if (name.length() > 0) {
            return name;
        }
        if (contactmethodList != null && contactmethodList.size() > 0) {
            for (ContactMethod contactMethod : contactmethodList) {
                if (contactMethod.kind == Contacts.KIND_EMAIL && contactMethod.isPrimary) {
                    return contactMethod.data;
                }
            }
        }
        if (phoneList != null && phoneList.size() > 0) {
            for (PhoneData phoneData : phoneList) {
                if (phoneData.isPrimary) {
                    return phoneData.data;
                }
            }
        }
        return "";
    }
    
//    private void pushIntoContentProviderOrResolver(Object contentSomething,
//            long myContactsGroupId) {
//        ContentResolver resolver = null;
//        AbstractSyncableContentProvider provider = null;
//        if (contentSomething instanceof ContentResolver) {
//            resolver = (ContentResolver)contentSomething;
//        } else if (contentSomething instanceof AbstractSyncableContentProvider) {
//            provider = (AbstractSyncableContentProvider)contentSomething;
//        } else {
//            Log.e(LOG_TAG, "Unsupported object came.");
//            return;
//        }
//
//        ContentValues contentValues = new ContentValues();
//        contentValues.put(People.NAME, name);
//        contentValues.put(People.PHONETIC_NAME, phoneticName);
//
//        if (notes.size() > 1) {
//            StringBuilder builder = new StringBuilder();
//            for (String note : notes) {
//                builder.append(note);
//                builder.append("\n");
//            }
//            contentValues.put(People.NOTES, builder.toString());
//        } else if (notes.size() == 1){
//            contentValues.put(People.NOTES, notes.get(0));
//        }
//
//        Uri personUri;
//        long personId = 0;
//        if (resolver != null) {
//            personUri = Contacts.People.createPersonInMyContactsGroup(
//                    resolver, contentValues);
//            if (personUri != null) {
//                personId = ContentUris.parseId(personUri);
//            }
//        } else {
//            personUri = provider.nonTransactionalInsert(People.CONTENT_URI, contentValues);
//            if (personUri != null) {
//                personId = ContentUris.parseId(personUri);
//                ContentValues values = new ContentValues();
//                values.put(GroupMembership.PERSON_ID, personId);
//                values.put(GroupMembership.GROUP_ID, myContactsGroupId);
//                Uri resultUri = provider.nonTransactionalInsert(
//                        GroupMembership.CONTENT_URI, values);
//                if (resultUri == null) {
//                    Log.e(LOG_TAG, "Faild to insert the person to MyContact.");
//                    provider.nonTransactionalDelete(personUri, null, null);
//                    personUri = null;
//                }
//            }
//        }
//
//        if (personUri == null) {
//            Log.e(LOG_TAG, "Failed to create the contact.");
//            return;
//        }
//
//        if (photoBytes != null) {
//            if (resolver != null) {
//                People.setPhotoData(resolver, personUri, photoBytes);
//            } else {
//                Uri photoUri = Uri.withAppendedPath(personUri, Contacts.Photos.CONTENT_DIRECTORY);
//                ContentValues values = new ContentValues();
//                values.put(Photos.DATA, photoBytes);
//                provider.update(photoUri, values, null, null);
//            }
//        }
//
//        long primaryPhoneId = -1;
//        if (phoneList != null && phoneList.size() > 0) {
//            for (PhoneData phoneData : phoneList) {
//                ContentValues values = new ContentValues();
//                values.put(Contacts.PhonesColumns.TYPE, phoneData.type);
//                if (phoneData.type == Contacts.PhonesColumns.TYPE_CUSTOM) {
//                    values.put(Contacts.PhonesColumns.LABEL, phoneData.label);
//                }
//                // Already formatted.
//                values.put(Contacts.PhonesColumns.NUMBER, phoneData.data);
//
//                // Not sure about Contacts.PhonesColumns.NUMBER_KEY ...
//                values.put(Contacts.PhonesColumns.ISPRIMARY, 1);
//                values.put(Contacts.Phones.PERSON_ID, personId);
//                Uri phoneUri;
//                if (resolver != null) {
//                    phoneUri = resolver.insert(Phones.CONTENT_URI, values);
//                } else {
//                    phoneUri = provider.nonTransactionalInsert(Phones.CONTENT_URI, values);
//                }
//                if (phoneData.isPrimary) {
//                    primaryPhoneId = Long.parseLong(phoneUri.getLastPathSegment());
//                }
//            }
//        }
//
//        long primaryOrganizationId = -1;
//        if (organizationList != null && organizationList.size() > 0) {
//            for (OrganizationData organizationData : organizationList) {
//                ContentValues values = new ContentValues();
//                // Currently, we do not use TYPE_CUSTOM.
//                values.put(Contacts.OrganizationColumns.TYPE,
//                        organizationData.type);
//                values.put(Contacts.OrganizationColumns.COMPANY,
//                        organizationData.companyName);
//                values.put(Contacts.OrganizationColumns.TITLE,
//                        organizationData.positionName);
//                values.put(Contacts.OrganizationColumns.ISPRIMARY, 1);
//                values.put(Contacts.OrganizationColumns.PERSON_ID, personId);
//
//                Uri organizationUri;
//                if (resolver != null) {
//                    organizationUri = resolver.insert(Organizations.CONTENT_URI, values);
//                } else {
//                    organizationUri = provider.nonTransactionalInsert(
//                            Organizations.CONTENT_URI, values);
//                }
//                if (organizationData.isPrimary) {
//                    primaryOrganizationId = Long.parseLong(organizationUri.getLastPathSegment());
//                }
//            }
//        }
//
//        long primaryEmailId = -1;
//        if (contactmethodList != null && contactmethodList.size() > 0) {
//            for (ContactMethod contactMethod : contactmethodList) {
//                ContentValues values = new ContentValues();
//                values.put(Contacts.ContactMethodsColumns.KIND, contactMethod.kind);
//                values.put(Contacts.ContactMethodsColumns.TYPE, contactMethod.type);
//                if (contactMethod.type == Contacts.ContactMethodsColumns.TYPE_CUSTOM) {
//                    values.put(Contacts.ContactMethodsColumns.LABEL, contactMethod.label);
//                }
//                values.put(Contacts.ContactMethodsColumns.DATA, contactMethod.data);
//                values.put(Contacts.ContactMethodsColumns.ISPRIMARY, 1);
//                values.put(Contacts.ContactMethods.PERSON_ID, personId);
//
//                if (contactMethod.kind == Contacts.KIND_EMAIL) {
//                    Uri emailUri;
//                    if (resolver != null) {
//                        emailUri = resolver.insert(ContactMethods.CONTENT_URI, values);
//                    } else {
//                        emailUri = provider.nonTransactionalInsert(
//                                ContactMethods.CONTENT_URI, values);
//                    }
//                    if (contactMethod.isPrimary) {
//                        primaryEmailId = Long.parseLong(emailUri.getLastPathSegment());
//                    }
//                } else {  // probably KIND_POSTAL
//                    if (resolver != null) {
//                        resolver.insert(ContactMethods.CONTENT_URI, values);
//                    } else {
//                        provider.nonTransactionalInsert(
//                                ContactMethods.CONTENT_URI, values);
//                    }
//                }
//            }
//        }
//
//        if (extensionMap != null && extensionMap.size() > 0) {
//            ArrayList contentValuesArray;
//            if (resolver != null) {
//                contentValuesArray = new ArrayList();
//            } else {
//                contentValuesArray = null;
//            }
//            for (Entry> entry : extensionMap.entrySet()) {
//                String key = entry.getKey();
//                List list = entry.getValue();
//                for (String value : list) {
//                    ContentValues values = new ContentValues();
//                    values.put(Extensions.NAME, key);
//                    values.put(Extensions.VALUE, value);
//                    values.put(Extensions.PERSON_ID, personId);
//                    if (resolver != null) {
//                        contentValuesArray.add(values);
//                    } else {
//                        provider.nonTransactionalInsert(Extensions.CONTENT_URI, values);
//                    }
//                }
//            }
//            if (resolver != null) {
//                resolver.bulkInsert(Extensions.CONTENT_URI,
//                        contentValuesArray.toArray(new ContentValues[0]));
//            }
//        }
//
//        if (primaryPhoneId >= 0 || primaryOrganizationId >= 0 || primaryEmailId >= 0) {
//            ContentValues values = new ContentValues();
//            if (primaryPhoneId >= 0) {
//                values.put(People.PRIMARY_PHONE_ID, primaryPhoneId);
//            }
//            if (primaryOrganizationId >= 0) {
//                values.put(People.PRIMARY_ORGANIZATION_ID, primaryOrganizationId);
//            }
//            if (primaryEmailId >= 0) {
//                values.put(People.PRIMARY_EMAIL_ID, primaryEmailId);
//            }
//            if (resolver != null) {
//                resolver.update(personUri, values, null, null);
//            } else {
//                provider.nonTransactionalUpdate(personUri, values, null, null);
//            }
//        }
//    }
//
//    /**
//     * Push this object into database in the resolver.
//     */
//    public void pushIntoContentResolver(ContentResolver resolver) {
//        pushIntoContentProviderOrResolver(resolver, 0);
//    }
//
//    /**
//     * Push this object into AbstractSyncableContentProvider object.
//     */
//    public void pushIntoAbstractSyncableContentProvider(
//            AbstractSyncableContentProvider provider, long myContactsGroupId) {
//        boolean successful = false;
//        provider.beginTransaction();
//        try {
//            pushIntoContentProviderOrResolver(provider, myContactsGroupId);
//            successful = true;
//        } finally {
//            provider.endTransaction(successful);
//        }
//    }
    
    public boolean isIgnorable() {
        return TextUtils.isEmpty(name) &&
                TextUtils.isEmpty(phoneticName) &&
                (phoneList == null || phoneList.size() == 0) &&
                (contactmethodList == null || contactmethodList.size() == 0);
    }
}


/*
 * Copyright (C) 2006 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 a_vcard.android.provider;

//import com.android.internal.R;

//import android.content.ContentResolver;
//import android.content.ContentUris;
//import android.content.ContentValues;
//import android.content.Context;
//import android.content.Intent;
//import android.database.Cursor;
//import android.graphics.Bitmap;
//import android.graphics.BitmapFactory;
//import android.net.Uri;
//import android.text.TextUtils;
//import android.util.Log;
//import android.widget.ImageView;

//import java.io.ByteArrayInputStream;
//import java.io.InputStream;

/**
 * The Contacts provider stores all information about contacts.
 */
public class Contacts {
    private static final String TAG = "Contacts";
    
    public static final String AUTHORITY = "contacts";

//    /**
//     * The content:// style URL for this provider
//     */
//    public static final Uri CONTENT_URI =
//        Uri.parse("content://" + AUTHORITY);

    /** Signifies an email address row that is stored in the ContactMethods table */
    public static final int KIND_EMAIL = 1;
    /** Signifies a postal address row that is stored in the ContactMethods table */
    public static final int KIND_POSTAL = 2;
    /** Signifies an IM address row that is stored in the ContactMethods table */
    public static final int KIND_IM = 3;
    /** Signifies an Organization row that is stored in the Organizations table */
    public static final int KIND_ORGANIZATION = 4;
    /** Signifies an Phone row that is stored in the Phones table */
    public static final int KIND_PHONE = 5;

    /**
     * no public constructor since this is a utility class
     */
    private Contacts() {}

//    /**
//     * Columns from the Settings table that other columns join into themselves.
//     */
//    public interface SettingsColumns {
//        /**
//         * The _SYNC_ACCOUNT to which this setting corresponds. This may be null.
//         * 

Type: TEXT

// */ // public static final String _SYNC_ACCOUNT = "_sync_account"; // // /** // * The key of this setting. // *

Type: TEXT

// */ // public static final String KEY = "key"; // // /** // * The value of this setting. // *

Type: TEXT

// */ // public static final String VALUE = "value"; // } // // /** // * The settings over all of the people // */ // public static final class Settings implements BaseColumns, SettingsColumns { // /** // * no public constructor since this is a utility class // */ // private Settings() {} // // /** // * The content:// style URL for this table // */ // public static final Uri CONTENT_URI = // Uri.parse("content://contacts/settings"); // // /** // * The directory twig for this sub-table // */ // public static final String CONTENT_DIRECTORY = "settings"; // // /** // * The default sort order for this table // */ // public static final String DEFAULT_SORT_ORDER = "key ASC"; // // /** // * A setting that is used to indicate if we should sync down all groups for the // * specified account. For this setting the _SYNC_ACCOUNT column must be set. // * If this isn't set then we will only sync the groups whose SHOULD_SYNC column // * is set to true. // *

// * This is a boolean setting. It is true if it is set and it is anything other than the // * emptry string or "0". // */ // public static final String SYNC_EVERYTHING = "syncEverything"; // // public static String getSetting(ContentResolver cr, String account, String key) { // // For now we only support a single account and the UI doesn't know what // // the account name is, so we're using a global setting for SYNC_EVERYTHING. // // Some day when we add multiple accounts to the UI this should honor the account // // that was asked for. // String selectString; // String[] selectArgs; // if (false) { // selectString = (account == null) // ? "_sync_account is null AND key=?" // : "_sync_account=? AND key=?"; // selectArgs = (account == null) // ? new String[]{key} // : new String[]{account, key}; // } else { // selectString = "key=?"; // selectArgs = new String[] {key}; // } // Cursor cursor = cr.query(Settings.CONTENT_URI, new String[]{VALUE}, // selectString, selectArgs, null); // try { // if (!cursor.moveToNext()) return null; // return cursor.getString(0); // } finally { // cursor.close(); // } // } // // public static void setSetting(ContentResolver cr, String account, String key, // String value) { // ContentValues values = new ContentValues(); // // For now we only support a single account and the UI doesn't know what // // the account name is, so we're using a global setting for SYNC_EVERYTHING. // // Some day when we add multiple accounts to the UI this should honor the account // // that was asked for. // //values.put(_SYNC_ACCOUNT, account); // values.put(KEY, key); // values.put(VALUE, value); // cr.update(Settings.CONTENT_URI, values, null, null); // } // } // /** * Columns from the People table that other tables join into themselves. */ public interface PeopleColumns { /** * The person's name. *

Type: TEXT

*/ public static final String NAME = "name"; /** * Phonetic equivalent of the person's name, in a locale-dependent * character set (e.g. hiragana for Japanese). * Used for pronunciation and/or collation in some languages. *

Type: TEXT

*/ public static final String PHONETIC_NAME = "phonetic_name"; /** * The display name. If name is not null name, else if number is not null number, * else if email is not null email. *

Type: TEXT

*/ public static final String DISPLAY_NAME = "display_name"; /** * The field for sorting list phonetically. The content of this field * may not be human readable but phonetically sortable. *

Type: TEXT

* @hide Used only in Contacts application for now. */ public static final String SORT_STRING = "sort_string"; /** * Notes about the person. *

Type: TEXT

*/ public static final String NOTES = "notes"; /** * The number of times a person has been contacted *

Type: INTEGER

*/ public static final String TIMES_CONTACTED = "times_contacted"; /** * The last time a person was contacted. *

Type: INTEGER

*/ public static final String LAST_TIME_CONTACTED = "last_time_contacted"; /** * A custom ringtone associated with a person. Not always present. *

Type: TEXT (URI to the ringtone)

*/ public static final String CUSTOM_RINGTONE = "custom_ringtone"; /** * Whether the person should always be sent to voicemail. Not always * present. *

Type: INTEGER (0 for false, 1 for true)

*/ public static final String SEND_TO_VOICEMAIL = "send_to_voicemail"; /** * Is the contact starred? *

Type: INTEGER (boolean)

*/ public static final String STARRED = "starred"; /** * The server version of the photo *

Type: TEXT (the version number portion of the photo URI)

*/ public static final String PHOTO_VERSION = "photo_version"; } // // /** // * This table contains people. // */ // public static final class People implements BaseColumns, SyncConstValue, PeopleColumns, // PhonesColumns, PresenceColumns { // /** // * no public constructor since this is a utility class // */ // private People() {} // // /** // * The content:// style URL for this table // */ // public static final Uri CONTENT_URI = // Uri.parse("content://contacts/people"); // // /** // * The content:// style URL for filtering people by name. The filter // * argument should be passed as an additional path segment after this URI. // */ // public static final Uri CONTENT_FILTER_URI = // Uri.parse("content://contacts/people/filter"); // // /** // * The content:// style URL for the table that holds the deleted // * contacts. // */ // public static final Uri DELETED_CONTENT_URI = // Uri.parse("content://contacts/deleted_people"); // // /** // * The content:// style URL for filtering people that have a specific // * E-mail or IM address. The filter argument should be passed as an // * additional path segment after this URI. This matches any people with // * at least one E-mail or IM {@link ContactMethods} that match the // * filter. // * // * Not exposed because we expect significant changes in the contacts // * schema and do not want to have to support this. // * @hide // */ // public static final Uri WITH_EMAIL_OR_IM_FILTER_URI = // Uri.parse("content://contacts/people/with_email_or_im_filter"); // // /** // * The MIME type of {@link #CONTENT_URI} providing a directory of // * people. // */ // public static final String CONTENT_TYPE = "vnd.android.cursor.dir/person"; // // /** // * The MIME type of a {@link #CONTENT_URI} subdirectory of a single // * person. // */ // public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/person"; // // /** // * The default sort order for this table // */ // public static final String DEFAULT_SORT_ORDER = People.NAME + " ASC"; // // /** // * The ID of the persons preferred phone number. // *

Type: INTEGER (foreign key to phones table on the _ID field)

// */ // public static final String PRIMARY_PHONE_ID = "primary_phone"; // // /** // * The ID of the persons preferred email. // *

Type: INTEGER (foreign key to contact_methods table on the // * _ID field)

// */ // public static final String PRIMARY_EMAIL_ID = "primary_email"; // // /** // * The ID of the persons preferred organization. // *

Type: INTEGER (foreign key to organizations table on the // * _ID field)

// */ // public static final String PRIMARY_ORGANIZATION_ID = "primary_organization"; // // /** // * Mark a person as having been contacted. // * // * @param resolver the ContentResolver to use // * @param personId the person who was contacted // */ // public static void markAsContacted(ContentResolver resolver, long personId) { // Uri uri = ContentUris.withAppendedId(CONTENT_URI, personId); // uri = Uri.withAppendedPath(uri, "update_contact_time"); // ContentValues values = new ContentValues(); // // There is a trigger in place that will update TIMES_CONTACTED when // // LAST_TIME_CONTACTED is modified. // values.put(LAST_TIME_CONTACTED, System.currentTimeMillis()); // resolver.update(uri, values, null, null); // } // // /** // * @hide Used in vCard parser code. // */ // public static long tryGetMyContactsGroupId(ContentResolver resolver) { // Cursor groupsCursor = resolver.query(Groups.CONTENT_URI, GROUPS_PROJECTION, // Groups.SYSTEM_ID + "='" + Groups.GROUP_MY_CONTACTS + "'", null, null); // if (groupsCursor != null) { // try { // if (groupsCursor.moveToFirst()) { // return groupsCursor.getLong(0); // } // } finally { // groupsCursor.close(); // } // } // return 0; // } // // /** // * Adds a person to the My Contacts group. // * // * @param resolver the resolver to use // * @param personId the person to add to the group // * @return the URI of the group membership row // * @throws IllegalStateException if the My Contacts group can't be found // */ // public static Uri addToMyContactsGroup(ContentResolver resolver, long personId) { // long groupId = tryGetMyContactsGroupId(resolver); // if (groupId == 0) { // throw new IllegalStateException("Failed to find the My Contacts group"); // } // // return addToGroup(resolver, personId, groupId); // } // // /** // * Adds a person to a group referred to by name. // * // * @param resolver the resolver to use // * @param personId the person to add to the group // * @param groupName the name of the group to add the contact to // * @return the URI of the group membership row // * @throws IllegalStateException if the group can't be found // */ // public static Uri addToGroup(ContentResolver resolver, long personId, String groupName) { // long groupId = 0; // Cursor groupsCursor = resolver.query(Groups.CONTENT_URI, GROUPS_PROJECTION, // Groups.NAME + "=?", new String[] { groupName }, null); // if (groupsCursor != null) { // try { // if (groupsCursor.moveToFirst()) { // groupId = groupsCursor.getLong(0); // } // } finally { // groupsCursor.close(); // } // } // // if (groupId == 0) { // throw new IllegalStateException("Failed to find the My Contacts group"); // } // // return addToGroup(resolver, personId, groupId); // } // // /** // * Adds a person to a group. // * // * @param resolver the resolver to use // * @param personId the person to add to the group // * @param groupId the group to add the person to // * @return the URI of the group membership row // */ // public static Uri addToGroup(ContentResolver resolver, long personId, long groupId) { // ContentValues values = new ContentValues(); // values.put(GroupMembership.PERSON_ID, personId); // values.put(GroupMembership.GROUP_ID, groupId); // return resolver.insert(GroupMembership.CONTENT_URI, values); // } // // private static final String[] GROUPS_PROJECTION = new String[] { // Groups._ID, // }; // // /** // * Creates a new contacts and adds it to the "My Contacts" group. // * // * @param resolver the ContentResolver to use // * @param values the values to use when creating the contact // * @return the URI of the contact, or null if the operation fails // */ // public static Uri createPersonInMyContactsGroup(ContentResolver resolver, // ContentValues values) { // // Uri contactUri = resolver.insert(People.CONTENT_URI, values); // if (contactUri == null) { // Log.e(TAG, "Failed to create the contact"); // return null; // } // // if (addToMyContactsGroup(resolver, ContentUris.parseId(contactUri)) == null) { // resolver.delete(contactUri, null, null); // return null; // } // return contactUri; // } // // public static Cursor queryGroups(ContentResolver resolver, long person) { // return resolver.query(GroupMembership.CONTENT_URI, null, "person=?", // new String[]{String.valueOf(person)}, Groups.DEFAULT_SORT_ORDER); // } // // /** // * Set the photo for this person. data may be null // * @param cr the ContentResolver to use // * @param person the Uri of the person whose photo is to be updated // * @param data the byte[] that represents the photo // */ // public static void setPhotoData(ContentResolver cr, Uri person, byte[] data) { // Uri photoUri = Uri.withAppendedPath(person, Contacts.Photos.CONTENT_DIRECTORY); // ContentValues values = new ContentValues(); // values.put(Photos.DATA, data); // cr.update(photoUri, values, null, null); // } // // /** // * Opens an InputStream for the person's photo and returns the photo as a Bitmap. // * If the person's photo isn't present returns the placeholderImageResource instead. // * @param person the person whose photo should be used // */ // public static InputStream openContactPhotoInputStream(ContentResolver cr, Uri person) { // Uri photoUri = Uri.withAppendedPath(person, Contacts.Photos.CONTENT_DIRECTORY); // Cursor cursor = cr.query(photoUri, new String[]{Photos.DATA}, null, null, null); // try { // if (!cursor.moveToNext()) { // return null; // } // byte[] data = cursor.getBlob(0); // if (data == null) { // return null; // } // return new ByteArrayInputStream(data); // } finally { // cursor.close(); // } // } // // /** // * Opens an InputStream for the person's photo and returns the photo as a Bitmap. // * If the person's photo isn't present returns the placeholderImageResource instead. // * @param context the Context // * @param person the person whose photo should be used // * @param placeholderImageResource the image resource to use if the person doesn't // * have a photo // * @param options the decoding options, can be set to null // */ // public static Bitmap loadContactPhoto(Context context, Uri person, // int placeholderImageResource, BitmapFactory.Options options) { // if (person == null) { // return loadPlaceholderPhoto(placeholderImageResource, context, options); // } // // InputStream stream = openContactPhotoInputStream(context.getContentResolver(), person); // Bitmap bm = stream != null ? BitmapFactory.decodeStream(stream, null, options) : null; // if (bm == null) { // bm = loadPlaceholderPhoto(placeholderImageResource, context, options); // } // return bm; // } // // private static Bitmap loadPlaceholderPhoto(int placeholderImageResource, Context context, // BitmapFactory.Options options) { // if (placeholderImageResource == 0) { // return null; // } // return BitmapFactory.decodeResource(context.getResources(), // placeholderImageResource, options); // } /** * A sub directory of a single person that contains all of their Phones. */ public static final class Phones implements BaseColumns, PhonesColumns, PeopleColumns { /** * no public constructor since this is a utility class */ private Phones() {} /** * The directory twig for this sub-table */ public static final String CONTENT_DIRECTORY = "phones"; /** * The default sort order for this table */ public static final String DEFAULT_SORT_ORDER = "number ASC"; } /** * A subdirectory of a single person that contains all of their * ContactMethods. */ public static final class ContactMethods implements BaseColumns, ContactMethodsColumns, PeopleColumns { /** * no public constructor since this is a utility class */ private ContactMethods() {} /** * The directory twig for this sub-table */ public static final String CONTENT_DIRECTORY = "contact_methods"; /** * The default sort order for this table */ public static final String DEFAULT_SORT_ORDER = "data ASC"; } // /** // * The extensions for a person // */ // public static class Extensions implements BaseColumns, ExtensionsColumns { // /** // * no public constructor since this is a utility class // */ // private Extensions() {} // // /** // * The directory twig for this sub-table // */ // public static final String CONTENT_DIRECTORY = "extensions"; // // /** // * The default sort order for this table // */ // public static final String DEFAULT_SORT_ORDER = "name ASC"; // // /** // * The ID of the person this phone number is assigned to. // *

Type: INTEGER (long)

// */ // public static final String PERSON_ID = "person"; // } // } // // /** // * Columns from the groups table. // */ // public interface GroupsColumns { // /** // * The group name. // *

Type: TEXT

// */ // public static final String NAME = "name"; // // /** // * Notes about the group. // *

Type: TEXT

// */ // public static final String NOTES = "notes"; // // /** // * Whether this group should be synced if the SYNC_EVERYTHING settings is false // * for this group's account. // *

Type: INTEGER (boolean)

// */ // public static final String SHOULD_SYNC = "should_sync"; // // /** // * The ID of this group if it is a System Group, null otherwise. // *

Type: TEXT

// */ // public static final String SYSTEM_ID = "system_id"; // } // // /** // * This table contains the groups for an account. // */ // public static final class Groups // implements BaseColumns, SyncConstValue, GroupsColumns { // /** // * no public constructor since this is a utility class // */ // private Groups() {} // // /** // * The content:// style URL for this table // */ // public static final Uri CONTENT_URI = // Uri.parse("content://contacts/groups"); // // /** // * The content:// style URL for the table that holds the deleted // * groups. // */ // public static final Uri DELETED_CONTENT_URI = // Uri.parse("content://contacts/deleted_groups"); // // /** // * The MIME type of {@link #CONTENT_URI} providing a directory of // * groups. // */ // public static final String CONTENT_TYPE = "vnd.android.cursor.dir/contactsgroup"; // // /** // * The MIME type of a {@link #CONTENT_URI} subdirectory of a single // * group. // */ // public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contactsgroup"; // // /** // * The default sort order for this table // */ // public static final String DEFAULT_SORT_ORDER = NAME + " ASC"; // // /** // * // */ // public static final String GROUP_ANDROID_STARRED = "Starred in Android"; // // /** // * The "My Contacts" system group. // */ // public static final String GROUP_MY_CONTACTS = "Contacts"; // } // /** * Columns from the Phones table that other columns join into themselves. */ public interface PhonesColumns { /** * The type of the the phone number. *

Type: INTEGER (one of the constants below)

*/ public static final String TYPE = "type"; public static final int TYPE_CUSTOM = 0; public static final int TYPE_HOME = 1; public static final int TYPE_MOBILE = 2; public static final int TYPE_WORK = 3; public static final int TYPE_FAX_WORK = 4; public static final int TYPE_FAX_HOME = 5; public static final int TYPE_PAGER = 6; public static final int TYPE_OTHER = 7; /** * The user provided label for the phone number, only used if TYPE is TYPE_CUSTOM. *

Type: TEXT

*/ public static final String LABEL = "label"; /** * The phone number as the user entered it. *

Type: TEXT

*/ public static final String NUMBER = "number"; /** * The normalized phone number *

Type: TEXT

*/ public static final String NUMBER_KEY = "number_key"; /** * Whether this is the primary phone number *

Type: INTEGER (if set, non-0 means true)

*/ public static final String ISPRIMARY = "isprimary"; } // // /** // * This table stores phone numbers and a reference to the person that the // * contact method belongs to. Phone numbers are stored separately from // * other contact methods to make caller ID lookup more efficient. // */ // public static final class Phones // implements BaseColumns, PhonesColumns, PeopleColumns { // /** // * no public constructor since this is a utility class // */ // private Phones() {} // // public static final CharSequence getDisplayLabel(Context context, int type, // CharSequence label, CharSequence[] labelArray) { // CharSequence display = ""; // // if (type != People.Phones.TYPE_CUSTOM) { // CharSequence[] labels = labelArray != null? labelArray // : context.getResources().getTextArray( // com.android.internal.R.array.phoneTypes); // try { // display = labels[type - 1]; // } catch (ArrayIndexOutOfBoundsException e) { // display = labels[People.Phones.TYPE_HOME - 1]; // } // } else { // if (!TextUtils.isEmpty(label)) { // display = label; // } // } // return display; // } // // public static final CharSequence getDisplayLabel(Context context, int type, // CharSequence label) { // return getDisplayLabel(context, type, label, null); // } // // /** // * The content:// style URL for this table // */ // public static final Uri CONTENT_URI = // Uri.parse("content://contacts/phones"); // // /** // * The content:// style URL for filtering phone numbers // */ // public static final Uri CONTENT_FILTER_URL = // Uri.parse("content://contacts/phones/filter"); // // /** // * The MIME type of {@link #CONTENT_URI} providing a directory of // * phones. // */ // public static final String CONTENT_TYPE = "vnd.android.cursor.dir/phone"; // // /** // * The MIME type of a {@link #CONTENT_URI} subdirectory of a single // * phone. // */ // public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/phone"; // // /** // * The default sort order for this table // */ // public static final String DEFAULT_SORT_ORDER = "name ASC"; // // /** // * The ID of the person this phone number is assigned to. // *

Type: INTEGER (long)

// */ // public static final String PERSON_ID = "person"; // } // // public static final class GroupMembership implements BaseColumns, GroupsColumns { // /** // * no public constructor since this is a utility class // */ // private GroupMembership() {} // // /** // * The content:// style URL for this table // */ // public static final Uri CONTENT_URI = // Uri.parse("content://contacts/groupmembership"); // // /** // * The content:// style URL for this table // */ // public static final Uri RAW_CONTENT_URI = // Uri.parse("content://contacts/groupmembershipraw"); // // /** // * The directory twig for this sub-table // */ // public static final String CONTENT_DIRECTORY = "groupmembership"; // /** // * The MIME type of {@link #CONTENT_URI} providing a directory of all // * person groups. // */ // public static final String CONTENT_TYPE = "vnd.android.cursor.dir/contactsgroupmembership"; // // /** // * The MIME type of a {@link #CONTENT_URI} subdirectory of a single // * person group. // */ // public static final String CONTENT_ITEM_TYPE = // "vnd.android.cursor.item/contactsgroupmembership"; // // /** // * The default sort order for this table // */ // public static final String DEFAULT_SORT_ORDER = "group_id ASC"; // // /** // * The row id of the accounts group. // *

Type: TEXT

// */ // public static final String GROUP_ID = "group_id"; // // /** // * The sync id of the group. // *

Type: TEXT

// */ // public static final String GROUP_SYNC_ID = "group_sync_id"; // // /** // * The account of the group. // *

Type: TEXT

// */ // public static final String GROUP_SYNC_ACCOUNT = "group_sync_account"; // // /** // * The row id of the person. // *

Type: TEXT

// */ // public static final String PERSON_ID = "person"; // } // /** * Columns from the ContactMethods table that other tables join into * themseleves. */ public interface ContactMethodsColumns { /** * The kind of the the contact method. For example, email address, * postal address, etc. *

Type: INTEGER (one of the values below)

*/ public static final String KIND = "kind"; /** * The type of the contact method, must be one of the types below. *

Type: INTEGER (one of the values below)

*/ public static final String TYPE = "type"; public static final int TYPE_CUSTOM = 0; public static final int TYPE_HOME = 1; public static final int TYPE_WORK = 2; public static final int TYPE_OTHER = 3; /** * @hide This is temporal. TYPE_MOBILE should be added to TYPE in the future. */ public static final int MOBILE_EMAIL_TYPE_INDEX = 2; /** * @hide This is temporal. TYPE_MOBILE should be added to TYPE in the future. * This is not "mobile" but "CELL" since vCard uses it for identifying mobile phone. */ public static final String MOBILE_EMAIL_TYPE_NAME = "_AUTO_CELL"; /** * The user defined label for the the contact method. *

Type: TEXT

*/ public static final String LABEL = "label"; /** * The data for the contact method. *

Type: TEXT

*/ public static final String DATA = "data"; /** * Auxiliary data for the contact method. *

Type: TEXT

*/ public static final String AUX_DATA = "aux_data"; /** * Whether this is the primary organization *

Type: INTEGER (if set, non-0 means true)

*/ public static final String ISPRIMARY = "isprimary"; } // // /** // * This table stores all non-phone contact methods and a reference to the // * person that the contact method belongs to. // */ // public static final class ContactMethods // implements BaseColumns, ContactMethodsColumns, PeopleColumns { // /** // * The column with latitude data for postal locations // *

Type: REAL

// */ // public static final String POSTAL_LOCATION_LATITUDE = DATA; // // /** // * The column with longitude data for postal locations // *

Type: REAL

// */ // public static final String POSTAL_LOCATION_LONGITUDE = AUX_DATA; // // /** // * The predefined IM protocol types. The protocol can either be non-present, one // * of these types, or a free-form string. These cases are encoded in the AUX_DATA // * column as: // * - null // * - pre: // * - custom: // */ // public static final int PROTOCOL_AIM = 0; // public static final int PROTOCOL_MSN = 1; // public static final int PROTOCOL_YAHOO = 2; // public static final int PROTOCOL_SKYPE = 3; // public static final int PROTOCOL_QQ = 4; // public static final int PROTOCOL_GOOGLE_TALK = 5; // public static final int PROTOCOL_ICQ = 6; // public static final int PROTOCOL_JABBER = 7; // // public static String encodePredefinedImProtocol(int protocol) { // return "pre:" + protocol; // } // // public static String encodeCustomImProtocol(String protocolString) { // return "custom:" + protocolString; // } // // public static Object decodeImProtocol(String encodedString) { // if (encodedString == null) { // return null; // } // // if (encodedString.startsWith("pre:")) { // return Integer.parseInt(encodedString.substring(4)); // } // // if (encodedString.startsWith("custom:")) { // return encodedString.substring(7); // } // // throw new IllegalArgumentException( // "the value is not a valid encoded protocol, " + encodedString); // } // // /** // * This looks up the provider name defined in // * {@link android.provider.Im.ProviderNames} from the predefined IM protocol id. // * This is used for interacting with the IM application. // * // * @param protocol the protocol ID // * @return the provider name the IM app uses for the given protocol, or null if no // * provider is defined for the given protocol // * @hide // */ // public static String lookupProviderNameFromId(int protocol) { // switch (protocol) { // case PROTOCOL_GOOGLE_TALK: // return Im.ProviderNames.GTALK; // case PROTOCOL_AIM: // return Im.ProviderNames.AIM; // case PROTOCOL_MSN: // return Im.ProviderNames.MSN; // case PROTOCOL_YAHOO: // return Im.ProviderNames.YAHOO; // case PROTOCOL_ICQ: // return Im.ProviderNames.ICQ; // case PROTOCOL_JABBER: // return Im.ProviderNames.JABBER; // case PROTOCOL_SKYPE: // return Im.ProviderNames.SKYPE; // case PROTOCOL_QQ: // return Im.ProviderNames.QQ; // } // return null; // } // // /** // * no public constructor since this is a utility class // */ // private ContactMethods() {} // // public static final CharSequence getDisplayLabel(Context context, int kind, // int type, CharSequence label) { // CharSequence display = ""; // switch (kind) { // case KIND_EMAIL: { // if (type != People.ContactMethods.TYPE_CUSTOM) { // CharSequence[] labels = context.getResources().getTextArray( // com.android.internal.R.array.emailAddressTypes); // try { // display = labels[type - 1]; // } catch (ArrayIndexOutOfBoundsException e) { // display = labels[ContactMethods.TYPE_HOME - 1]; // } // } else { // if (!TextUtils.isEmpty(label)) { // if (label.toString().equals(MOBILE_EMAIL_TYPE_NAME)) { // display = // context.getString( // com.android.internal.R.string.mobileEmailTypeName); // } else { // display = label; // } // } // } // break; // } // // case KIND_POSTAL: { // if (type != People.ContactMethods.TYPE_CUSTOM) { // CharSequence[] labels = context.getResources().getTextArray( // com.android.internal.R.array.postalAddressTypes); // try { // display = labels[type - 1]; // } catch (ArrayIndexOutOfBoundsException e) { // display = labels[ContactMethods.TYPE_HOME - 1]; // } // } else { // if (!TextUtils.isEmpty(label)) { // display = label; // } // } // break; // } // // default: // display = context.getString(R.string.untitled); // } // return display; // } // // /** // * Add a longitude and latitude location to a postal address. // * // * @param context the context to use when updating the database // * @param postalId the address to update // * @param latitude the latitude for the address // * @param longitude the longitude for the address // */ // public void addPostalLocation(Context context, long postalId, // double latitude, double longitude) { // final ContentResolver resolver = context.getContentResolver(); // // Insert the location // ContentValues values = new ContentValues(2); // values.put(POSTAL_LOCATION_LATITUDE, latitude); // values.put(POSTAL_LOCATION_LONGITUDE, longitude); // Uri loc = resolver.insert(CONTENT_URI, values); // long locId = ContentUris.parseId(loc); // // // Update the postal address // values.clear(); // values.put(AUX_DATA, locId); // resolver.update(ContentUris.withAppendedId(CONTENT_URI, postalId), values, null, null); // } // // /** // * The content:// style URL for this table // */ // public static final Uri CONTENT_URI = // Uri.parse("content://contacts/contact_methods"); // // /** // * The content:// style URL for sub-directory of e-mail addresses. // */ // public static final Uri CONTENT_EMAIL_URI = // Uri.parse("content://contacts/contact_methods/email"); // // /** // * The MIME type of {@link #CONTENT_URI} providing a directory of // * phones. // */ // public static final String CONTENT_TYPE = "vnd.android.cursor.dir/contact-methods"; // // /** // * The MIME type of a {@link #CONTENT_EMAIL_URI} sub-directory of\ // * multiple {@link Contacts#KIND_EMAIL} entries. // */ // public static final String CONTENT_EMAIL_TYPE = "vnd.android.cursor.dir/email"; // // /** // * The MIME type of a {@link #CONTENT_EMAIL_URI} sub-directory of\ // * multiple {@link Contacts#KIND_POSTAL} entries. // */ // public static final String CONTENT_POSTAL_TYPE = "vnd.android.cursor.dir/postal-address"; // // /** // * The MIME type of a {@link #CONTENT_URI} sub-directory of a single // * {@link Contacts#KIND_EMAIL} entry. // */ // public static final String CONTENT_EMAIL_ITEM_TYPE = "vnd.android.cursor.item/email"; // // /** // * The MIME type of a {@link #CONTENT_URI} sub-directory of a single // * {@link Contacts#KIND_POSTAL} entry. // */ // public static final String CONTENT_POSTAL_ITEM_TYPE // = "vnd.android.cursor.item/postal-address"; // // /** // * The MIME type of a {@link #CONTENT_URI} sub-directory of a single // * {@link Contacts#KIND_IM} entry. // */ // public static final String CONTENT_IM_ITEM_TYPE = "vnd.android.cursor.item/jabber-im"; // // /** // * The default sort order for this table // */ // public static final String DEFAULT_SORT_ORDER = "name ASC"; // // /** // * The ID of the person this contact method is assigned to. // *

Type: INTEGER (long)

// */ // public static final String PERSON_ID = "person"; // } // // /** // * The IM presence columns with some contacts specific columns mixed in. // */ // public interface PresenceColumns extends Im.CommonPresenceColumns { // /** // * The IM service the presence is coming from. Formatted using either // * {@link Contacts.ContactMethods#encodePredefinedImProtocol} or // * {@link Contacts.ContactMethods#encodeCustomImProtocol}. // *

Type: STRING

// */ // public static final String IM_PROTOCOL = "im_protocol"; // // /** // * The IM handle the presence item is for. The handle is scoped to // * the {@link #IM_PROTOCOL}. // *

Type: STRING

// */ // public static final String IM_HANDLE = "im_handle"; // // /** // * The IM account for the local user that the presence data came from. // *

Type: STRING

// */ // public static final String IM_ACCOUNT = "im_account"; // } // // /** // * Contains presence information about contacts. // * @hide // */ // public static final class Presence // implements BaseColumns, PresenceColumns, PeopleColumns { // /** // * The content:// style URL for this table // */ // public static final Uri CONTENT_URI = // Uri.parse("content://contacts/presence"); // // /** // * The ID of the person this presence item is assigned to. // *

Type: INTEGER (long)

// */ // public static final String PERSON_ID = "person"; // // /** // * Gets the resource ID for the proper presence icon. // * // * @param status the status to get the icon for // * @return the resource ID for the proper presence icon // */ // public static final int getPresenceIconResourceId(int status) { // switch (status) { // case Contacts.People.AVAILABLE: // return com.android.internal.R.drawable.presence_online; // // case Contacts.People.IDLE: // case Contacts.People.AWAY: // return com.android.internal.R.drawable.presence_away; // // case Contacts.People.DO_NOT_DISTURB: // return com.android.internal.R.drawable.presence_busy; // // case Contacts.People.INVISIBLE: // return com.android.internal.R.drawable.presence_invisible; // // case Contacts.People.OFFLINE: // default: // return com.android.internal.R.drawable.presence_offline; // } // } // // /** // * Sets a presence icon to the proper graphic // * // * @param icon the icon to to set // * @param serverStatus that status // */ // public static final void setPresenceIcon(ImageView icon, int serverStatus) { // icon.setImageResource(getPresenceIconResourceId(serverStatus)); // } // } // /** * Columns from the Organizations table that other columns join into themselves. */ public interface OrganizationColumns { /** * The type of the organizations. *

Type: INTEGER (one of the constants below)

*/ public static final String TYPE = "type"; public static final int TYPE_CUSTOM = 0; public static final int TYPE_WORK = 1; public static final int TYPE_OTHER = 2; /** * The user provided label, only used if TYPE is TYPE_CUSTOM. *

Type: TEXT

*/ public static final String LABEL = "label"; /** * The name of the company for this organization. *

Type: TEXT

*/ public static final String COMPANY = "company"; /** * The title within this organization. *

Type: TEXT

*/ public static final String TITLE = "title"; /** * The person this organization is tied to. *

Type: TEXT

*/ public static final String PERSON_ID = "person"; /** * Whether this is the primary organization *

Type: INTEGER (if set, non-0 means true)

*/ public static final String ISPRIMARY = "isprimary"; } // // /** // * A sub directory of a single person that contains all of their Phones. // */ // public static final class Organizations implements BaseColumns, OrganizationColumns { // /** // * no public constructor since this is a utility class // */ // private Organizations() {} // // public static final CharSequence getDisplayLabel(Context context, int type, // CharSequence label) { // CharSequence display = ""; // // if (type != TYPE_CUSTOM) { // CharSequence[] labels = context.getResources().getTextArray( // com.android.internal.R.array.organizationTypes); // try { // display = labels[type - 1]; // } catch (ArrayIndexOutOfBoundsException e) { // display = labels[Organizations.TYPE_WORK - 1]; // } // } else { // if (!TextUtils.isEmpty(label)) { // display = label; // } // } // return display; // } // // /** // * The content:// style URL for this table // */ // public static final Uri CONTENT_URI = // Uri.parse("content://contacts/organizations"); // // /** // * The directory twig for this sub-table // */ // public static final String CONTENT_DIRECTORY = "organizations"; // // /** // * The default sort order for this table // */ // public static final String DEFAULT_SORT_ORDER = "company, title, isprimary ASC"; // } // // /** // * Columns from the Photos table that other columns join into themselves. // */ // public interface PhotosColumns { // /** // * The _SYNC_VERSION of the photo that was last downloaded // *

Type: TEXT

// */ // public static final String LOCAL_VERSION = "local_version"; // // /** // * The person this photo is associated with. // *

Type: TEXT

// */ // public static final String PERSON_ID = "person"; // // /** // * non-zero if a download is required and the photo isn't marked as a bad resource. // * You must specify this in the columns in order to use it in the where clause. // *

Type: INTEGER(boolean)

// */ // public static final String DOWNLOAD_REQUIRED = "download_required"; // // /** // * non-zero if this photo is known to exist on the server // *

Type: INTEGER(boolean)

// */ // public static final String EXISTS_ON_SERVER = "exists_on_server"; // // /** // * Contains the description of the upload or download error from // * the previous attempt. If null then the previous attempt succeeded. // *

Type: TEXT

// */ // public static final String SYNC_ERROR = "sync_error"; // // /** // * The image data, or null if there is no image. // *

Type: BLOB

// */ // public static final String DATA = "data"; // // } // // /** // * The photos over all of the people // */ // public static final class Photos implements BaseColumns, PhotosColumns, SyncConstValue { // /** // * no public constructor since this is a utility class // */ // private Photos() {} // // /** // * The content:// style URL for this table // */ // public static final Uri CONTENT_URI = // Uri.parse("content://contacts/photos"); // // /** // * The directory twig for this sub-table // */ // public static final String CONTENT_DIRECTORY = "photo"; // // /** // * The default sort order for this table // */ // public static final String DEFAULT_SORT_ORDER = "person ASC"; // } // // public interface ExtensionsColumns { // /** // * The name of this extension. May not be null. There may be at most one row for each name. // *

Type: TEXT

// */ // public static final String NAME = "name"; // // /** // * The value of this extension. May not be null. // *

Type: TEXT

// */ // public static final String VALUE = "value"; // } // // /** // * The extensions for a person // */ // public static final class Extensions implements BaseColumns, ExtensionsColumns { // /** // * no public constructor since this is a utility class // */ // private Extensions() {} // // /** // * The content:// style URL for this table // */ // public static final Uri CONTENT_URI = // Uri.parse("content://contacts/extensions"); // // /** // * The MIME type of {@link #CONTENT_URI} providing a directory of // * phones. // */ // public static final String CONTENT_TYPE = "vnd.android.cursor.dir/contact_extensions"; // // /** // * The MIME type of a {@link #CONTENT_URI} subdirectory of a single // * phone. // */ // public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contact_extensions"; // /** // * The default sort order for this table // */ // public static final String DEFAULT_SORT_ORDER = "person, name ASC"; // // /** // * The ID of the person this phone number is assigned to. // *

Type: INTEGER (long)

// */ // public static final String PERSON_ID = "person"; // } // // /** // * Contains helper classes used to create or manage {@link android.content.Intent Intents} // * that involve contacts. // */ // public static final class Intents { // /** // * This is the intent that is fired when a search suggestion is clicked on. // */ // public static final String SEARCH_SUGGESTION_CLICKED = // "android.provider.Contacts.SEARCH_SUGGESTION_CLICKED"; // // /** // * This is the intent that is fired when a search suggestion for dialing a number // * is clicked on. // */ // public static final String SEARCH_SUGGESTION_DIAL_NUMBER_CLICKED = // "android.provider.Contacts.SEARCH_SUGGESTION_DIAL_NUMBER_CLICKED"; // // /** // * This is the intent that is fired when a search suggestion for creating a contact // * is clicked on. // */ // public static final String SEARCH_SUGGESTION_CREATE_CONTACT_CLICKED = // "android.provider.Contacts.SEARCH_SUGGESTION_CREATE_CONTACT_CLICKED"; // // /** // * Starts an Activity that lets the user pick a contact to attach an image to. // * After picking the contact it launches the image cropper in face detection mode. // */ // public static final String ATTACH_IMAGE = // "com.android.contacts.action.ATTACH_IMAGE"; // // /** // * Takes as input a data URI with a mailto: or tel: scheme. If a single // * contact exists with the given data it will be shown. If no contact // * exists, a dialog will ask the user if they want to create a new // * contact with the provided details filled in. If multiple contacts // * share the data the user will be prompted to pick which contact they // * want to view. // *

// * For mailto: URIs, the scheme specific portion must be a // * raw email address, such as one built using // * {@link Uri#fromParts(String, String, String)}. // *

// * For tel: URIs, the scheme specific portion is compared // * to existing numbers using the standard caller ID lookup algorithm. // * The number must be properly encoded, for example using // * {@link Uri#fromParts(String, String, String)}. // *

// * Any extras from the {@link Insert} class will be passed along to the // * create activity if there are no contacts to show. // *

// * Passing true for the {@link #EXTRA_FORCE_CREATE} extra will skip // * prompting the user when the contact doesn't exist. // */ // public static final String SHOW_OR_CREATE_CONTACT = // "com.android.contacts.action.SHOW_OR_CREATE_CONTACT"; // // /** // * Used with {@link #SHOW_OR_CREATE_CONTACT} to force creating a new // * contact if no matching contact found. Otherwise, default behavior is // * to prompt user with dialog before creating. // *

// * Type: BOOLEAN // */ // public static final String EXTRA_FORCE_CREATE = // "com.android.contacts.action.FORCE_CREATE"; // // /** // * Used with {@link #SHOW_OR_CREATE_CONTACT} to specify an exact // * description to be shown when prompting user about creating a new // * contact. // *

// * Type: STRING // */ // public static final String EXTRA_CREATE_DESCRIPTION = // "com.android.contacts.action.CREATE_DESCRIPTION"; // // /** // * Intents related to the Contacts app UI. // */ // public static final class UI { // /** // * The action for the default contacts list tab. // */ // public static final String LIST_DEFAULT = // "com.android.contacts.action.LIST_DEFAULT"; // // /** // * The action for the contacts list tab. // */ // public static final String LIST_GROUP_ACTION = // "com.android.contacts.action.LIST_GROUP"; // // /** // * When in LIST_GROUP_ACTION mode, this is the group to display. // */ // public static final String GROUP_NAME_EXTRA_KEY = "com.android.contacts.extra.GROUP"; // // /** // * The action for the all contacts list tab. // */ // public static final String LIST_ALL_CONTACTS_ACTION = // "com.android.contacts.action.LIST_ALL_CONTACTS"; // // /** // * The action for the contacts with phone numbers list tab. // */ // public static final String LIST_CONTACTS_WITH_PHONES_ACTION = // "com.android.contacts.action.LIST_CONTACTS_WITH_PHONES"; // // /** // * The action for the starred contacts list tab. // */ // public static final String LIST_STARRED_ACTION = // "com.android.contacts.action.LIST_STARRED"; // // /** // * The action for the frequent contacts list tab. // */ // public static final String LIST_FREQUENT_ACTION = // "com.android.contacts.action.LIST_FREQUENT"; // // /** // * The action for the "strequent" contacts list tab. It first lists the starred // * contacts in alphabetical order and then the frequent contacts in descending // * order of the number of times they have been contacted. // */ // public static final String LIST_STREQUENT_ACTION = // "com.android.contacts.action.LIST_STREQUENT"; // // /** // * A key for to be used as an intent extra to set the activity // * title to a custom String value. // */ // public static final String TITLE_EXTRA_KEY = // "com.android.contacts.extra.TITLE_EXTRA"; // // /** // * Activity Action: Display a filtered list of contacts // *

// * Input: Extra field {@link #FILTER_TEXT_EXTRA_KEY} is the text to use for // * filtering // *

// * Output: Nothing. // */ // public static final String FILTER_CONTACTS_ACTION = // "com.android.contacts.action.FILTER_CONTACTS"; // // /** // * Used as an int extra field in {@link #FILTER_CONTACTS_ACTION} // * intents to supply the text on which to filter. // */ // public static final String FILTER_TEXT_EXTRA_KEY = // "com.android.contacts.extra.FILTER_TEXT"; // } // // /** // * Convenience class that contains string constants used // * to create contact {@link android.content.Intent Intents}. // */ // public static final class Insert { // /** The action code to use when adding a contact */ // public static final String ACTION = Intent.ACTION_INSERT; // // /** // * If present, forces a bypass of quick insert mode. // */ // public static final String FULL_MODE = "full_mode"; // // /** // * The extra field for the contact name. // *

Type: String

// */ // public static final String NAME = "name"; // // /** // * The extra field for the contact phonetic name. // *

Type: String

// */ // public static final String PHONETIC_NAME = "phonetic_name"; // // /** // * The extra field for the contact company. // *

Type: String

// */ // public static final String COMPANY = "company"; // // /** // * The extra field for the contact job title. // *

Type: String

// */ // public static final String JOB_TITLE = "job_title"; // // /** // * The extra field for the contact notes. // *

Type: String

// */ // public static final String NOTES = "notes"; // // /** // * The extra field for the contact phone number. // *

Type: String

// */ // public static final String PHONE = "phone"; // // /** // * The extra field for the contact phone number type. // *

Type: Either an integer value from {@link android.provider.Contacts.PhonesColumns PhonesColumns}, // * or a string specifying a custom label.

// */ // public static final String PHONE_TYPE = "phone_type"; // // /** // * The extra field for the phone isprimary flag. // *

Type: boolean

// */ // public static final String PHONE_ISPRIMARY = "phone_isprimary"; // // /** // * The extra field for an optional second contact phone number. // *

Type: String

// */ // public static final String SECONDARY_PHONE = "secondary_phone"; // // /** // * The extra field for an optional second contact phone number type. // *

Type: Either an integer value from {@link android.provider.Contacts.PhonesColumns PhonesColumns}, // * or a string specifying a custom label.

// */ // public static final String SECONDARY_PHONE_TYPE = "secondary_phone_type"; // // /** // * The extra field for an optional third contact phone number. // *

Type: String

// */ // public static final String TERTIARY_PHONE = "tertiary_phone"; // // /** // * The extra field for an optional third contact phone number type. // *

Type: Either an integer value from {@link android.provider.Contacts.PhonesColumns PhonesColumns}, // * or a string specifying a custom label.

// */ // public static final String TERTIARY_PHONE_TYPE = "tertiary_phone_type"; // // /** // * The extra field for the contact email address. // *

Type: String

// */ // public static final String EMAIL = "email"; // // /** // * The extra field for the contact email type. // *

Type: Either an integer value from {@link android.provider.Contacts.ContactMethodsColumns ContactMethodsColumns} // * or a string specifying a custom label.

// */ // public static final String EMAIL_TYPE = "email_type"; // // /** // * The extra field for the email isprimary flag. // *

Type: boolean

// */ // public static final String EMAIL_ISPRIMARY = "email_isprimary"; // // /** // * The extra field for an optional second contact email address. // *

Type: String

// */ // public static final String SECONDARY_EMAIL = "secondary_email"; // // /** // * The extra field for an optional second contact email type. // *

Type: Either an integer value from {@link android.provider.Contacts.ContactMethodsColumns ContactMethodsColumns} // * or a string specifying a custom label.

// */ // public static final String SECONDARY_EMAIL_TYPE = "secondary_email_type"; // // /** // * The extra field for an optional third contact email address. // *

Type: String

// */ // public static final String TERTIARY_EMAIL = "tertiary_email"; // // /** // * The extra field for an optional third contact email type. // *

Type: Either an integer value from {@link android.provider.Contacts.ContactMethodsColumns ContactMethodsColumns} // * or a string specifying a custom label.

// */ // public static final String TERTIARY_EMAIL_TYPE = "tertiary_email_type"; // // /** // * The extra field for the contact postal address. // *

Type: String

// */ // public static final String POSTAL = "postal"; // // /** // * The extra field for the contact postal address type. // *

Type: Either an integer value from {@link android.provider.Contacts.ContactMethodsColumns ContactMethodsColumns} // * or a string specifying a custom label.

// */ // public static final String POSTAL_TYPE = "postal_type"; // // /** // * The extra field for the postal isprimary flag. // *

Type: boolean

// */ // public static final String POSTAL_ISPRIMARY = "postal_isprimary"; // // /** // * The extra field for an IM handle. // *

Type: String

// */ // public static final String IM_HANDLE = "im_handle"; // // /** // * The extra field for the IM protocol // *

Type: the result of {@link Contacts.ContactMethods#encodePredefinedImProtocol} // * or {@link Contacts.ContactMethods#encodeCustomImProtocol}.

// */ // public static final String IM_PROTOCOL = "im_protocol"; // // /** // * The extra field for the IM isprimary flag. // *

Type: boolean

// */ // public static final String IM_ISPRIMARY = "im_isprimary"; // } // } }



接着我将给你导入导出通讯录的类


 

package com.hh.assistant.app.vo;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;

import a_vcard.android.provider.Contacts;
import a_vcard.android.syncml.pim.VDataBuilder;
import a_vcard.android.syncml.pim.VNode;
import a_vcard.android.syncml.pim.vcard.ContactStruct;
import a_vcard.android.syncml.pim.vcard.ContactStruct.ContactMethod;
import a_vcard.android.syncml.pim.vcard.ContactStruct.PhoneData;
import a_vcard.android.syncml.pim.vcard.VCardComposer;
import a_vcard.android.syncml.pim.vcard.VCardException;
import a_vcard.android.syncml.pim.vcard.VCardParser;
import android.app.Activity;
import android.content.ContentUris;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
import android.provider.ContactsContract.RawContacts;
import android.provider.ContactsContract.RawContacts.Data;
import android.widget.Toast;


/**
 * 联系人信息包装类
 * 
 * @author LW
 * 
 */
public class ContactInfo {

    /** MUST exist */
    private String name; // 姓名
    
    /** 联系人电话信息 */
    public static class PhoneInfo{
        /** 联系电话类型 */
        public int type;
        /** 联系电话 */
        public String number;
    }
    
    /** 联系人邮箱信息 */
    public static class EmailInfo{
        /** 邮箱类型 */
        public int type;
        /** 邮箱 */
        public String email;
    }
    
    private List phoneList = new ArrayList(); // 联系号码
    private List email = new ArrayList(); // Email

    /**
     * 构造联系人信息
     * @param name 联系人姓名 
     */
    public ContactInfo(String name) {
        this.name = name;
    }
    
    /** 姓名 */
    public String getName() {
        return name;
    }
    /** 姓名 */
    public ContactInfo setName(String name) {
        this.name = name;
        return this;
    }
    /** 联系电话信息 */
    public List getPhoneList() {
        return phoneList;
    }
    /** 联系电话信息 */
    public ContactInfo setPhoneList(List phoneList) {
        this.phoneList = phoneList;
        return this;
    }
    /** 邮箱信息 */
    public List getEmail() {
        return email;
    }
    /** 邮箱信息 */
    public ContactInfo setEmail(List email) {
        this.email = email;
        return this;
    }

    @Override
    public String toString() {
        return "{name: "+name+", number: "+phoneList+", email: "+email+"}";
    }
    
    /**
     * 联系人
     *         备份/还原操作
     * @author LW
     *
     */
    public static class ContactHandler {

        private static ContactHandler instance_ = new ContactHandler();
        
        /** 获取实例 */
        public static ContactHandler getInstance(){
            return instance_;
        }
        
        /**
         * 获取联系人指定信息
         * @param projection 指定要获取的列数组, 获取全部列则设置为null
         * @return
         * @throws Exception
         */
        public Cursor queryContact(Activity context, String[] projection){
            // 获取联系人的所需信息
            Cursor cur = context.getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, projection, null, null, null);
            return cur;
        }
        
        /**
         * 获取联系人信息
         * @param context
         * @return
         */
        public List getContactInfo(Activity context){
            List infoList = new ArrayList();
            
            Cursor cur = queryContact(context, null);
            
            if(cur.moveToFirst()){
                do{
                    
                    // 获取联系人id号
                    String id = cur.getString(cur.getColumnIndex(ContactsContract.Contacts._ID));
                    // 获取联系人姓名
                    String displayName = cur.getString(cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
                    ContactInfo info = new ContactInfo(displayName);// 初始化联系人信息
                    
                    // 查看联系人有多少电话号码, 如果没有返回0
                    int phoneCount = cur.getInt(cur.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER));
                    
                    if(phoneCount>0){
                        
                        Cursor phonesCursor = context.getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID + "=" + id , null, null);
                        
                        if(phonesCursor.moveToFirst()) {
                            List phoneNumberList = new ArrayList();
                            do{
                                // 遍历所有电话号码
                                String phoneNumber = phonesCursor.getString(phonesCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
                                // 对应的联系人类型
                                int type = phonesCursor.getInt(phonesCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE));
                                
                                // 初始化联系人电话信息
                                ContactInfo.PhoneInfo phoneInfo = new ContactInfo.PhoneInfo();
                                phoneInfo.type=type;
                                phoneInfo.number=phoneNumber;
                                
                                phoneNumberList.add(phoneInfo);
                            }while(phonesCursor.moveToNext());
                            // 设置联系人电话信息
                            info.setPhoneList(phoneNumberList);
                        }
                    }
                    
                    // 获得联系人的EMAIL
                    Cursor emailCur = context.getContentResolver().query(ContactsContract.CommonDataKinds.Email.CONTENT_URI, null, ContactsContract.CommonDataKinds.Email.CONTACT_ID+"="+id, null, null);
                    
                    if(emailCur.moveToFirst()){
                        List emailList = new ArrayList();
                        do{
                            // 遍历所有的email
                            String email = emailCur.getString(emailCur.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA1));
                            int type = emailCur.getInt(emailCur.getColumnIndex(ContactsContract.CommonDataKinds.Email.TYPE));
                            
                            // 初始化联系人邮箱信息
                            ContactInfo.EmailInfo emailInfo=new ContactInfo.EmailInfo();
                            emailInfo.type=type;    // 设置邮箱类型
                            emailInfo.email=email;    // 设置邮箱地址
                            
                            emailList.add(emailInfo);
                        }while(emailCur.moveToNext());
                        
                        info.setEmail(emailList);
                    }
                    
                    //Cursor postalCursor = getContentResolver().query(ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_URI, null, ContactsContract.CommonDataKinds.StructuredPostal.CONTACT_ID + "=" + id, null, null);
                    infoList.add(info);
                }while(cur.moveToNext());
            }
            return infoList;
        }
        
        /**
         * 备份联系人
         */
        public void backupContacts(Activity context, List infos){
            
            try {
                
                String path = Environment.getExternalStorageDirectory() + "/contacts.vcf";
                
                OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(path),"UTF-8");
                
                VCardComposer composer = new VCardComposer();
                
                for (ContactInfo info : infos)
                {
                    ContactStruct contact = new ContactStruct();
                    contact.name = info.getName();
                    // 获取联系人电话信息, 添加至 ContactStruct 
                    List numberList = info
                            .getPhoneList();
                    for (ContactInfo.PhoneInfo phoneInfo : numberList)
                    {
                        contact.addPhone(phoneInfo.type, phoneInfo.number,
                                null, true);
                    }
                    // 获取联系人Email信息, 添加至 ContactStruct 
                    List emailList = info.getEmail();
                    for (ContactInfo.EmailInfo emailInfo : emailList)
                    {
                        contact.addContactmethod(Contacts.KIND_EMAIL,
                                emailInfo.type, emailInfo.email, null, true);
                    }
                    String vcardString = composer.createVCard(contact,
                            VCardComposer.VERSION_VCARD30_INT);
                    writer.write(vcardString);
                    writer.write("\n");
                    
                    writer.flush();
                }
                writer.close();
            
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (VCardException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            
            Toast.makeText(context, "备份成功!", Toast.LENGTH_SHORT).show();
        }
        
        
        /**
         * 获取vCard文件中的联系人信息 
         * @return 
         */
        public List restoreContacts() throws Exception {
            List contactInfoList = new ArrayList();
            
            VCardParser parse = new VCardParser();
            VDataBuilder builder = new VDataBuilder();
            String file = Environment.getExternalStorageDirectory() + "/contacts.vcf";
            
            BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));
            
            String vcardString = "";
            String line;
            while((line = reader.readLine()) != null) {
                vcardString += line + "\n";
            }
            reader.close();
            
            boolean parsed = parse.parse(vcardString, "UTF-8", builder);
            
            if(!parsed){
                throw new VCardException("Could not parse vCard file: "+ file);
            }
            
            List pimContacts = builder.vNodeList;
            
            for (VNode contact : pimContacts) {
                
                ContactStruct contactStruct=ContactStruct.constructContactFromVNode(contact, 1);
                // 获取备份文件中的联系人电话信息
                List phoneDataList = contactStruct.phoneList;
                List phoneInfoList = new ArrayList();
                for(PhoneData phoneData : phoneDataList){
                    ContactInfo.PhoneInfo phoneInfo = new ContactInfo.PhoneInfo();
                    phoneInfo.number=phoneData.data;
                    phoneInfo.type=phoneData.type;
                    phoneInfoList.add(phoneInfo);
                }
                
                // 获取备份文件中的联系人邮箱信息
                List emailList = contactStruct.contactmethodList;
                List emailInfoList = new ArrayList();
                // 存在 Email 信息
                if (null!=emailList)
                {
                    for (ContactMethod contactMethod : emailList)
                    {
                        if (Contacts.KIND_EMAIL == contactMethod.kind)
                        {
                            ContactInfo.EmailInfo emailInfo = new ContactInfo.EmailInfo();
                            emailInfo.email = contactMethod.data;
                            emailInfo.type = contactMethod.type;
                            emailInfoList.add(emailInfo);
                        }
                    }
                }
                ContactInfo info = new ContactInfo(contactStruct.name).setPhoneList(phoneInfoList).setEmail(emailInfoList);
                contactInfoList.add(info);
            }
            
            return contactInfoList;
        }

        
        /**
         * 向手机中录入联系人信息
         * @param info 要录入的联系人信息
         */
        public void addContacts(Activity context, ContactInfo info){
            ContentValues values = new ContentValues();
            //首先向RawContacts.CONTENT_URI执行一个空值插入,目的是获取系统返回的rawContactId
            Uri rawContactUri = context.getContentResolver().insert(RawContacts.CONTENT_URI, values);
            long rawContactId = ContentUris.parseId(rawContactUri);
            
            //往data表入姓名数据
            values.clear();
            values.put(Data.RAW_CONTACT_ID, rawContactId);
            values.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
            values.put(StructuredName.GIVEN_NAME, info.getName());
            context.getContentResolver().insert(
                    android.provider.ContactsContract.Data.CONTENT_URI, values);
            
            // 获取联系人电话信息
            List phoneList = info.getPhoneList();
            /** 录入联系电话 */
            for (ContactInfo.PhoneInfo phoneInfo : phoneList) {
                values.clear();
                values.put(android.provider.ContactsContract.Contacts.Data.RAW_CONTACT_ID, rawContactId);
                values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
                // 设置录入联系人电话信息
                values.put(Phone.NUMBER, phoneInfo.number);
                values.put(Phone.TYPE, phoneInfo.type);
                // 往data表入电话数据
                context.getContentResolver().insert(
                        android.provider.ContactsContract.Data.CONTENT_URI, values);
            }
            
            // 获取联系人邮箱信息
            List emailList = info.getEmail();
            
            /** 录入联系人邮箱信息 */
            for (ContactInfo.EmailInfo email : emailList) {
                values.clear();
                values.put(android.provider.ContactsContract.Contacts.Data.RAW_CONTACT_ID, rawContactId);
                values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
                // 设置录入的邮箱信息
                values.put(Email.DATA, email.email);
                values.put(Email.TYPE, email.type);
                // 往data表入Email数据
                context.getContentResolver().insert(
                        android.provider.ContactsContract.Data.CONTENT_URI, values);
            }
            
        }
        
    }
}


 

// 获取联系人处理实例
        ContactInfo.ContactHandler handler=ContactInfo.ContactHandler.getInstance();
        
        switch (id) {
        case R.id.save_linkman:
            // 获取要备份的信息
            List _infoList = handler.getContactInfo(this);
            handler.backupContacts(this, _infoList);    // 备份联系人信息
            break;

        case R.id.restore_linkman:    // 恢复
            try {
                // 获取要恢复的联系人信息
                List infoList = handler.restoreContacts();
                for (ContactInfo contactInfo : infoList) {
                    // 恢复联系人
                    handler.addContacts(this, contactInfo);
                }
                
                Toast.makeText(this, "导入联系人信息成功!", Toast.LENGTH_LONG);
                
            } catch (Exception e) {
                Toast.makeText(this, "导入联系人信息失败!", Toast.LENGTH_SHORT).show();
                e.printStackTrace();
            }
            
            break;
        }


由于要对存储卡做读写操作,记得要加读写权限

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE">uses-permission> 

 http://download.csdn.net/detail/yongsdm/6407495

你可能感兴趣的:(Android)