Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 56374c43 authored by Daisuke Miyakawa's avatar Daisuke Miyakawa
Browse files

vCard refactoring.

Change-Id: Ie6c5e0edae4a13279d5118f0380d863e4ff05727
parent b633123f
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -27,7 +27,6 @@ import java.util.Map;
        new HashMap<Character, String>();

    static {
        // There's no logical mapping rule in Unicode. Sigh.
        sHalfWidthMap.put('\u3001', "\uFF64");
        sHalfWidthMap.put('\u3002', "\uFF61");
        sHalfWidthMap.put('\u300C', "\uFF62");
+15 −18
Original line number Diff line number Diff line
@@ -91,13 +91,13 @@ import java.util.Map;
 *         composer.terminate();
 *     }
 * }</pre>
 * <P>
 * <p>
 * Users have to manually take care of memory efficiency. Even one vCard may contain
 * image of non-trivial size for mobile devices.
 * </P>
 * <P>
 * In default, Default {@link VCardBuilder} class is used to build each vCard.
 * </P>
 * </p>
 * <p>
 * {@link VCardBuilder} is used to build each vCard.
 * </p>
 */
public class VCardComposer {
    private static final String LOG_TAG = "VCardComposer";
@@ -161,14 +161,14 @@ public class VCardComposer {
     * Must not close the stream outside this class.
     * </p>
     */
    public class HandlerForOutputStream implements OneEntryHandler {
    public final class HandlerForOutputStream implements OneEntryHandler {
        @SuppressWarnings("hiding")
        private static final String LOG_TAG = "vcard.VCardComposer.HandlerForOutputStream";
        private static final String LOG_TAG = "VCardComposer.HandlerForOutputStream";

        private boolean mOnTerminateIsCalled = false;

        final private OutputStream mOutputStream; // mWriter will close this.
        protected Writer mWriter;
        private final OutputStream mOutputStream; // mWriter will close this.
        private Writer mWriter;

        /**
         * Input stream will be closed on the detruction of this object.
@@ -177,7 +177,7 @@ public class VCardComposer {
            mOutputStream = outputStream;
        }

        public final boolean onInit(final Context context) {
        public boolean onInit(final Context context) {
            try {
                mWriter = new BufferedWriter(new OutputStreamWriter(
                        mOutputStream, mCharset));
@@ -207,7 +207,7 @@ public class VCardComposer {
            return true;
        }

        public final boolean onEntryCreated(String vcard) {
        public boolean onEntryCreated(String vcard) {
            try {
                mWriter.write(vcard);
            } catch (IOException e) {
@@ -220,7 +220,7 @@ public class VCardComposer {
            return true;
        }

        public final void onTerminate() {
        public void onTerminate() {
            mOnTerminateIsCalled = true;
            if (mWriter != null) {
                try {
@@ -242,8 +242,6 @@ public class VCardComposer {
            }
        }

        // Users can override this if they want to (e.g. if they don't want to close the stream).
        // TODO: Should expose bare OutputStream instead?
        public void closeOutputStream() {
            try {
                mWriter.close();
@@ -312,8 +310,7 @@ public class VCardComposer {
     * a Handler object given via {{@link #addHandler(OneEntryHandler)} returns false.
     * If false, this ignores those errors.
     */
    public VCardComposer(final Context context, final int vcardType,
            String charset,
    public VCardComposer(final Context context, final int vcardType, String charset,
            final boolean careHandlerErrors) {
        mContext = context;
        mVCardType = vcardType;
@@ -380,6 +377,7 @@ public class VCardComposer {
                mCharset = charset;
            }
        }

        Log.d(LOG_TAG, "Use the charset \"" + mCharset + "\"");
    }

@@ -602,7 +600,6 @@ public class VCardComposer {
            return "";
        } else {
            final VCardBuilder builder = new VCardBuilder(mVCardType, mCharset);
            // TODO: Android-specific X attributes?
            builder.appendNameProperties(contentValuesListMap.get(StructuredName.CONTENT_ITEM_TYPE))
                    .appendNickNames(contentValuesListMap.get(Nickname.CONTENT_ITEM_TYPE))
                    .appendPhones(contentValuesListMap.get(Phone.CONTENT_ITEM_TYPE))
+10 −11
Original line number Diff line number Diff line
@@ -153,6 +153,7 @@ public class VCardConfig {
     * In vCard 3.0, Quoted-Printable is explicitly "prohibitted", so we don't need to care this
     * kind of problem (hopefully).
     * </p>
     * @hide
     */
    public static final int FLAG_REFRAIN_QP_TO_NAME_PROPERTIES = 0x10000000;

@@ -171,7 +172,7 @@ public class VCardConfig {
     * able to parse them as we expect.
     * </p>
     */
    public static final int FLAG_CONVERT_PHONETIC_NAME_STRINGS = 0x0800000;
    public static final int FLAG_CONVERT_PHONETIC_NAME_STRINGS = 0x08000000;

    /**
     * <p>
@@ -184,21 +185,20 @@ public class VCardConfig {
     * How more than one TYPE fields are expressed is different between vCard 2.1 and vCard 3.0.
     * </p>
     * <p>
     * e.g.<BR />
     * 1) Probably valid in both vCard 2.1 and vCard 3.0: "ADR;TYPE=DOM;TYPE=HOME:..."<BR />
     * 2) Valid in vCard 2.1 but not in vCard 3.0: "ADR;DOM;HOME:..."<BR />
     * 3) Valid in vCard 3.0 but not in vCard 2.1: "ADR;TYPE=DOM,HOME:..."<BR />
     * </p>
     * <p>
     * 2) had been the default of VCard exporter/importer in Android, but it is found that
     * some external exporter is not able to parse the type format like 2) but only 3).
     * e.g.
     * </p>
     * <ol>
     * <li>Probably valid in both vCard 2.1 and vCard 3.0: "ADR;TYPE=DOM;TYPE=HOME:..."</li>
     * <li>Valid in vCard 2.1 but not in vCard 3.0: "ADR;DOM;HOME:..."</li>
     * <li>Valid in vCard 3.0 but not in vCard 2.1: "ADR;TYPE=DOM,HOME:..."</li>
     * </ol>
     * <p>
     * If you are targeting to the importer which cannot accept TYPE params without "TYPE="
     * strings (which should be rare though), please use this flag.
     * </p>
     * <p>
     * Example usage: int vcardType = (VCARD_TYPE_V21_GENERIC | FLAG_APPEND_TYPE_PARAM);
     * Example usage:
     * <pre class="prettyprint">int type = (VCARD_TYPE_V21_GENERIC | FLAG_APPEND_TYPE_PARAM);</pre>
     * </p>
     */
    public static final int FLAG_APPEND_TYPE_PARAM = 0x04000000;
@@ -261,7 +261,6 @@ public class VCardConfig {
     */
    public static final int VCARD_TYPE_UNKNOWN = 0;


    /**
     * <p>
     * Generic vCard format with the vCard 2.1. When composing a vCard entry,
+19 −21
Original line number Diff line number Diff line
@@ -78,12 +78,12 @@ public class VCardEntry {
                Im.PROTOCOL_GOOGLE_TALK);
    }

    static public class PhoneData {
    public static class PhoneData {
        public final int type;
        public final String data;
        public final String label;
        // isPrimary is changable only when there's no appropriate one existing in
        // the original VCard.
        // isPrimary is (not final but) changable, only when there's no appropriate one existing
        // in the original VCard.
        public boolean isPrimary;
        public PhoneData(int type, String data, String label, boolean isPrimary) {
            this.type = type;
@@ -109,13 +109,11 @@ public class VCardEntry {
        }
    }

    static public class EmailData {
    public static class EmailData {
        public final int type;
        public final String data;
        // Used only when TYPE is TYPE_CUSTOM.
        public final String label;
        // isPrimary is changable only when there's no appropriate one existing in
        // the original VCard.
        public boolean isPrimary;
        public EmailData(int type, String data, String label, boolean isPrimary) {
            this.type = type;
@@ -141,9 +139,9 @@ public class VCardEntry {
        }
    }

    static public class PostalData {
        // Determined by vCard spec.
        // PO Box, Extended Addr, Street, Locality, Region, Postal Code, Country Name
    public static class PostalData {
        // Determined by vCard specification.
        // - PO Box, Extended Addr, Street, Locality, Region, Postal Code, Country Name
        public static final int ADDR_MAX_DATA_SIZE = 7;
        private final String[] dataArray;
        public final String pobox;
@@ -248,10 +246,11 @@ public class VCardEntry {
        }
    }

    static public class OrganizationData {
    public static class OrganizationData {
        public final int type;
        // non-final is Intentional: we may change the values since this info is separated into
        // two parts in vCard: "ORG" + "TITLE".
        // two parts in vCard: "ORG" + "TITLE", and we have to cope with each field in
        // different timing.
        public String companyName;
        public String departmentName;
        public String titleName;
@@ -313,7 +312,7 @@ public class VCardEntry {
        }
    }

    static public class ImData {
    public static class ImData {
        public final int protocol;
        public final String customProtocol;
        public final int type;
@@ -441,7 +440,7 @@ public class VCardEntry {
    private String mSuffix;

    // Used only when no family nor given name is found.
    private String mFullName;
    private String mFormattedName;

    private String mPhoneticFamilyName;
    private String mPhoneticGivenName;
@@ -499,7 +498,6 @@ public class VCardEntry {
                }
            }

            // Use NANP in default when there's no information about locale.
            final int formattingType = VCardUtils.getPhoneNumberFormat(mVCardType);
            formattedNumber = PhoneNumberUtils.formatNumber(builder.toString(), formattingType);
        }
@@ -754,11 +752,11 @@ public class VCardEntry {
        if (propName.equals(VCardConstants.PROPERTY_VERSION)) {
            // vCard version. Ignore this.
        } else if (propName.equals(VCardConstants.PROPERTY_FN)) {
            mFullName = propValue;
        } else if (propName.equals(VCardConstants.PROPERTY_NAME) && mFullName == null) {
            mFormattedName = propValue;
        } else if (propName.equals(VCardConstants.PROPERTY_NAME) && mFormattedName == null) {
            // Only in vCard 3.0. Use this if FN, which must exist in vCard 3.0 but may not
            // actually exist in the real vCard data, does not exist.
            mFullName = propValue;
            mFormattedName = propValue;
        } else if (propName.equals(VCardConstants.PROPERTY_N)) {
            handleNProperty(propValueList);
        } else if (propName.equals(VCardConstants.PROPERTY_SORT_STRING)) {
@@ -1016,8 +1014,8 @@ public class VCardEntry {
     */
    private void constructDisplayName() {
        // FullName (created via "FN" or "NAME" field) is prefered.
        if (!TextUtils.isEmpty(mFullName)) {
            mDisplayName = mFullName;
        if (!TextUtils.isEmpty(mFormattedName)) {
            mDisplayName = mFormattedName;
        } else if (!(TextUtils.isEmpty(mFamilyName) && TextUtils.isEmpty(mGivenName))) {
            mDisplayName = VCardUtils.constructNameFromElements(mVCardType,
                    mFamilyName, mMiddleName, mGivenName, mPrefix, mSuffix);
@@ -1320,7 +1318,7 @@ public class VCardEntry {
                && TextUtils.isEmpty(mGivenName)
                && TextUtils.isEmpty(mPrefix)
                && TextUtils.isEmpty(mSuffix)
                && TextUtils.isEmpty(mFullName)
                && TextUtils.isEmpty(mFormattedName)
                && TextUtils.isEmpty(mPhoneticFamilyName)
                && TextUtils.isEmpty(mPhoneticMiddleName)
                && TextUtils.isEmpty(mPhoneticGivenName)
@@ -1379,7 +1377,7 @@ public class VCardEntry {
    }

    public String getFullName() {
        return mFullName;
        return mFormattedName;
    }

    public String getPhoneticFamilyName() {
+36 −18
Original line number Diff line number Diff line
@@ -20,9 +20,7 @@ import android.text.TextUtils;
import android.util.CharsetUtils;
import android.util.Log;

import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.net.QuotedPrintableCodec;

import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
@@ -31,6 +29,23 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * <p>
 * The {@link VCardInterpreter} implementation which enables {@link VCardEntryHandler} objects
 * to easily handle each vCard entry.
 * </p>
 * <p>
 * This class understand details inside vCard and translates it to {@link VCardEntry}.
 * Then the class throw it to {@link VCardEntryHandler} registered via
 * {@link #addEntryHandler(VCardEntryHandler)}, so that all those registered objects
 * are able to handle the {@link VCardEntry} object.
 * </p>
 * <p>
 * If you want to know the detail inside vCard, it would be better to implement
 * {@link VCardInterpreter} directly, instead of relying on this class and
 * {@link VCardEntry} created by the object.
 * </p>
 */
public class VCardEntryConstructor implements VCardInterpreter {
    private static String LOG_TAG = "VCardEntryConstructor";

@@ -48,27 +63,33 @@ public class VCardEntryConstructor implements VCardInterpreter {
    private final int mVCardType;
    private final Account mAccount;
    
    /** For measuring performance. */
    // For measuring performance.
    private long mTimePushIntoContentResolver;

    final private List<VCardEntryHandler> mEntryHandlers = new ArrayList<VCardEntryHandler>();
    private final List<VCardEntryHandler> mEntryHandlers = new ArrayList<VCardEntryHandler>();

    public VCardEntryConstructor() {
        this(null, false, VCardConfig.VCARD_TYPE_V21_GENERIC, null);
        this(VCardConfig.VCARD_TYPE_V21_GENERIC, null, null, false);
    }

    public VCardEntryConstructor(final int vcardType) {
        this(null, false, vcardType, null);
        this(vcardType, null, null, false);
    }

    public VCardEntryConstructor(final int vcardType, final Account account) {
        this(vcardType, account, null, false);
    }

    public VCardEntryConstructor(final String inputCharset,
            final int vcardType, final Account account) {
        this(inputCharset, false, vcardType, account);
    public VCardEntryConstructor(final int vcardType, final Account account,
            final String inputCharset) {
        this(vcardType, account, inputCharset, false);
    }

    public VCardEntryConstructor(final String inputCharset,
            final boolean strictLineBreakParsing, final int vcardType,
            final Account account) {
    /**
     * @hide
     */
    public VCardEntryConstructor(final int vcardType, final Account account,
            final String inputCharset, final boolean strictLineBreakParsing) {
        if (inputCharset != null) {
            mSourceCharset = inputCharset;
        } else {
@@ -95,17 +116,11 @@ public class VCardEntryConstructor implements VCardInterpreter {
        }
    }

    /**
     * Called when the parse failed between {@link #startEntry()} and {@link #endEntry()}.
     */
    public void clear() {
        mCurrentVCardEntry = null;
        mCurrentProperty = new VCardEntry.Property();
    }

    /**
     * Assume that VCard is not nested. In other words, this code does not accept 
     */
    public void startEntry() {
        if (mCurrentVCardEntry != null) {
            Log.e(LOG_TAG, "Nested VCard code is not supported now.");
@@ -211,6 +226,9 @@ public class VCardEntryConstructor implements VCardInterpreter {
        }
    }

    /**
     * @hide
     */
    public void showPerformanceInfo() {
        Log.d(LOG_TAG, "time for insert ContactStruct to database: " + 
                mTimePushIntoContentResolver + " ms");
Loading