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

Commit 1519eb1e authored by The Android Automerger's avatar The Android Automerger
Browse files

Merge branch 'gingerbread' into gingerbread-release

parents d85d63e5 3ac85d31
Loading
Loading
Loading
Loading
+2 −3
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");
@@ -366,11 +365,11 @@ import java.util.Map;
    }

    /**
     * Return half-width version of that character if possible. Return null if not possible
     * Returns half-width version of that character if possible. Returns null if not possible
     * @param ch input character
     * @return CharSequence object if the mapping for ch exists. Return null otherwise.
     */
    public static String tryGetHalfWidthText(char ch) {
    public static String tryGetHalfWidthText(final char ch) {
        if (sHalfWidthMap.containsKey(ch)) {
            return sHalfWidthMap.get(ch);
        } else {
+308 −107

File changed.

Preview size limit exceeded, changes collapsed.

+166 −80
Original line number Diff line number Diff line
@@ -19,16 +19,12 @@ import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Entity;
import android.content.EntityIterator;
import android.content.Entity.NamedContentValues;
import android.content.EntityIterator;
import android.database.Cursor;
import android.database.sqlite.SQLiteException;
import android.net.Uri;
import android.pim.vcard.exception.VCardException;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.RawContacts;
import android.provider.ContactsContract.RawContactsEntity;
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.Event;
import android.provider.ContactsContract.CommonDataKinds.Im;
@@ -41,6 +37,11 @@ import android.provider.ContactsContract.CommonDataKinds.Relation;
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
import android.provider.ContactsContract.CommonDataKinds.Website;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.RawContacts;
import android.provider.ContactsContract.RawContactsEntity;
import android.text.TextUtils;
import android.util.CharsetUtils;
import android.util.Log;

@@ -61,15 +62,11 @@ import java.util.Map;

/**
 * <p>
 * The class for composing VCard from Contacts information. Note that this is
 * completely differnt implementation from
 * android.syncml.pim.vcard.VCardComposer, which is not maintained anymore.
 * The class for composing vCard from Contacts information.
 * </p>
 *
 * <p>
 * Usually, this class should be used like this.
 * </p>
 *
 * <pre class="prettyprint">VCardComposer composer = null;
 * try {
 *     composer = new VCardComposer(context);
@@ -94,14 +91,17 @@ import java.util.Map;
 *         composer.terminate();
 *     }
 * }</pre>
 * <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>
 * {@link VCardBuilder} is used to build each vCard.
 * </p>
 */
public class VCardComposer {
    private static final String LOG_TAG = "VCardComposer";

    public static final int DEFAULT_PHONE_TYPE = Phone.TYPE_HOME;
    public static final int DEFAULT_POSTAL_TYPE = StructuredPostal.TYPE_HOME;
    public static final int DEFAULT_EMAIL_TYPE = Email.TYPE_OTHER;

    public static final String FAILURE_REASON_FAILED_TO_GET_DATABASE_INFO =
        "Failed to get database information";

@@ -119,6 +119,8 @@ public class VCardComposer {

    public static final String VCARD_TYPE_STRING_DOCOMO = "docomo";

    // Strictly speaking, "Shift_JIS" is the most appropriate, but we use upper version here,
    // since usual vCard devices for Japanese devices already use it.
    private static final String SHIFT_JIS = "SHIFT_JIS";
    private static final String UTF_8 = "UTF-8";

@@ -141,7 +143,7 @@ public class VCardComposer {
        sImMap.put(Im.PROTOCOL_ICQ, VCardConstants.PROPERTY_X_ICQ);
        sImMap.put(Im.PROTOCOL_JABBER, VCardConstants.PROPERTY_X_JABBER);
        sImMap.put(Im.PROTOCOL_SKYPE, VCardConstants.PROPERTY_X_SKYPE_USERNAME);
        // Google talk is a special case.
        // We don't add Google talk here since it has to be handled separately.
    }

    public static interface OneEntryHandler {
@@ -152,37 +154,37 @@ public class VCardComposer {

    /**
     * <p>
     * An useful example handler, which emits VCard String to outputstream one by one.
     * An useful handler for emitting vCard String to an OutputStream object one by one.
     * </p>
     * <p>
     * The input OutputStream object is closed() on {@link #onTerminate()}.
     * Must not close the stream outside.
     * 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";

        final private OutputStream mOutputStream; // mWriter will close this.
        private Writer mWriter;
        private static final String LOG_TAG = "VCardComposer.HandlerForOutputStream";

        private boolean mOnTerminateIsCalled = false;

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

        /**
         * Input stream will be closed on the detruction of this object.
         */
        public HandlerForOutputStream(OutputStream outputStream) {
        public HandlerForOutputStream(final OutputStream outputStream) {
            mOutputStream = outputStream;
        }

        public boolean onInit(Context context) {
        public boolean onInit(final Context context) {
            try {
                mWriter = new BufferedWriter(new OutputStreamWriter(
                        mOutputStream, mCharsetString));
                        mOutputStream, mCharset));
            } catch (UnsupportedEncodingException e1) {
                Log.e(LOG_TAG, "Unsupported charset: " + mCharsetString);
                Log.e(LOG_TAG, "Unsupported charset: " + mCharset);
                mErrorReason = "Encoding is not supported (usually this does not happen!): "
                        + mCharsetString;
                        + mCharset;
                return false;
            }

@@ -235,11 +237,16 @@ public class VCardComposer {
                            "IOException during closing the output stream: "
                                    + e.getMessage());
                } finally {
                    closeOutputStream();
                }
            }
        }

        public void closeOutputStream() {
            try {
                mWriter.close();
            } catch (IOException e) {
                    }
                }
                Log.w(LOG_TAG, "IOException is thrown during close(). Ignoring.");
            }
        }

@@ -257,11 +264,10 @@ public class VCardComposer {
    private final ContentResolver mContentResolver;

    private final boolean mIsDoCoMo;
    private final boolean mUsesShiftJis;
    private Cursor mCursor;
    private int mIdColumn;

    private final String mCharsetString;
    private final String mCharset;
    private boolean mTerminateIsCalled;
    private final List<OneEntryHandler> mHandlerList;

@@ -272,52 +278,107 @@ public class VCardComposer {
    };

    public VCardComposer(Context context) {
        this(context, VCardConfig.VCARD_TYPE_DEFAULT, true);
        this(context, VCardConfig.VCARD_TYPE_DEFAULT, null, true);
    }

    /**
     * The variant which sets charset to null and sets careHandlerErrors to true.
     */
    public VCardComposer(Context context, int vcardType) {
        this(context, vcardType, true);
        this(context, vcardType, null, true);
    }

    public VCardComposer(Context context, String vcardTypeStr, boolean careHandlerErrors) {
        this(context, VCardConfig.getVCardTypeFromString(vcardTypeStr), careHandlerErrors);
    public VCardComposer(Context context, int vcardType, String charset) {
        this(context, vcardType, charset, true);
    }

    /**
     * Construct for supporting call log entry vCard composing.
     * The variant which sets charset to null.
     */
    public VCardComposer(final Context context, final int vcardType,
            final boolean careHandlerErrors) {
        this(context, vcardType, null, careHandlerErrors);
    }

    /**
     * Construct for supporting call log entry vCard composing.
     *
     * @param context Context to be used during the composition.
     * @param vcardType The type of vCard, typically available via {@link VCardConfig}.
     * @param charset The charset to be used. Use null when you don't need the charset.
     * @param careHandlerErrors If true, This object returns false everytime
     * 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,
            final boolean careHandlerErrors) {
        mContext = context;
        mVCardType = vcardType;
        mCareHandlerErrors = careHandlerErrors;
        mContentResolver = context.getContentResolver();

        mIsDoCoMo = VCardConfig.isDoCoMo(vcardType);
        mUsesShiftJis = VCardConfig.usesShiftJis(vcardType);
        mHandlerList = new ArrayList<OneEntryHandler>();

        charset = (TextUtils.isEmpty(charset) ? VCardConfig.DEFAULT_EXPORT_CHARSET : charset);
        final boolean shouldAppendCharsetParam = !(
                VCardConfig.isVersion30(vcardType) && UTF_8.equalsIgnoreCase(charset));

        if (mIsDoCoMo || shouldAppendCharsetParam) {
            if (SHIFT_JIS.equalsIgnoreCase(charset)) {
                if (mIsDoCoMo) {
            String charset;
                    try {
                        charset = CharsetUtils.charsetForVendor(SHIFT_JIS, "docomo").name();
                    } catch (UnsupportedCharsetException e) {
                Log.e(LOG_TAG, "DoCoMo-specific SHIFT_JIS was not found. Use SHIFT_JIS as is.");
                        Log.e(LOG_TAG,
                                "DoCoMo-specific SHIFT_JIS was not found. "
                                + "Use SHIFT_JIS as is.");
                        charset = SHIFT_JIS;
                    }
            mCharsetString = charset;
        } else if (mUsesShiftJis) {
            String charset;
                } else {
                    try {
                        charset = CharsetUtils.charsetForVendor(SHIFT_JIS).name();
                    } catch (UnsupportedCharsetException e) {
                Log.e(LOG_TAG, "Vendor-specific SHIFT_JIS was not found. Use SHIFT_JIS as is.");
                        Log.e(LOG_TAG,
                                "Career-specific SHIFT_JIS was not found. "
                                + "Use SHIFT_JIS as is.");
                        charset = SHIFT_JIS;
                    }
            mCharsetString = charset;
                }
                mCharset = charset;
            } else {
                Log.w(LOG_TAG,
                        "The charset \"" + charset + "\" is used while "
                        + SHIFT_JIS + " is needed to be used.");
                if (TextUtils.isEmpty(charset)) {
                    mCharset = SHIFT_JIS;
                } else {
            mCharsetString = UTF_8;
                    try {
                        charset = CharsetUtils.charsetForVendor(charset).name();
                    } catch (UnsupportedCharsetException e) {
                        Log.i(LOG_TAG,
                                "Career-specific \"" + charset + "\" was not found (as usual). "
                                + "Use it as is.");
                    }
                    mCharset = charset;
                }
            }
        } else {
            if (TextUtils.isEmpty(charset)) {
                mCharset = UTF_8;
            } else {
                try {
                    charset = CharsetUtils.charsetForVendor(charset).name();
                } catch (UnsupportedCharsetException e) {
                    Log.i(LOG_TAG,
                            "Career-specific \"" + charset + "\" was not found (as usual). "
                            + "Use it as is.");
                }
                mCharset = charset;
            }
        }

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

    /**
@@ -351,7 +412,7 @@ public class VCardComposer {
        }

        if (mCareHandlerErrors) {
            List<OneEntryHandler> finishedList = new ArrayList<OneEntryHandler>(
            final List<OneEntryHandler> finishedList = new ArrayList<OneEntryHandler>(
                    mHandlerList.size());
            for (OneEntryHandler handler : mHandlerList) {
                if (!handler.onInit(mContext)) {
@@ -414,7 +475,7 @@ public class VCardComposer {
            mErrorReason = FAILURE_REASON_NOT_INITIALIZED;
            return false;
        }
        String vcard;
        final String vcard;
        try {
            if (mIdColumn >= 0) {
                vcard = createOneEntryInternal(mCursor.getString(mIdColumn),
@@ -437,8 +498,7 @@ public class VCardComposer {
            mCursor.moveToNext();
        }

        // This function does not care the OutOfMemoryError on the handler side
        // :-P
        // This function does not care the OutOfMemoryError on the handler side :-P
        if (mCareHandlerErrors) {
            List<OneEntryHandler> finishedList = new ArrayList<OneEntryHandler>(
                    mHandlerList.size());
@@ -457,7 +517,7 @@ public class VCardComposer {
    }

    private String createOneEntryInternal(final String contactId,
            Method getEntityIteratorMethod) throws VCardException {
            final Method getEntityIteratorMethod) throws VCardException {
        final Map<String, List<ContentValues>> contentValuesListMap =
                new HashMap<String, List<ContentValues>>();
        // The resolver may return the entity iterator with no data. It is possible.
@@ -466,12 +526,13 @@ public class VCardComposer {
        EntityIterator entityIterator = null;
        try {
            final Uri uri = RawContactsEntity.CONTENT_URI.buildUpon()
                    // .appendQueryParameter("for_export_only", "1")
                    .appendQueryParameter(Data.FOR_EXPORT_ONLY, "1")
                    .build();
            final String selection = Data.CONTACT_ID + "=?";
            final String[] selectionArgs = new String[] {contactId};
            if (getEntityIteratorMethod != null) {
                // Please note that this branch is executed by some tests only
                // Please note that this branch is executed by unit tests only
                try {
                    entityIterator = (EntityIterator)getEntityIteratorMethod.invoke(null,
                            mContentResolver, uri, selection, selectionArgs, null);
@@ -527,7 +588,19 @@ public class VCardComposer {
            }
        }

        final VCardBuilder builder = new VCardBuilder(mVCardType);
        return buildVCard(contentValuesListMap);
    }

    /**
     * Builds and returns vCard using given map, whose key is CONTENT_ITEM_TYPE defined in
     * {ContactsContract}. Developers can override this method to customize the output.
     */
    public String buildVCard(final Map<String, List<ContentValues>> contentValuesListMap) {
        if (contentValuesListMap == null) {
            Log.e(LOG_TAG, "The given map is null. Ignore and return empty String");
            return "";
        } else {
            final VCardBuilder builder = new VCardBuilder(mVCardType, mCharset);
            builder.appendNameProperties(contentValuesListMap.get(StructuredName.CONTENT_ITEM_TYPE))
                    .appendNickNames(contentValuesListMap.get(Nickname.CONTENT_ITEM_TYPE))
                    .appendPhones(contentValuesListMap.get(Phone.CONTENT_ITEM_TYPE))
@@ -544,6 +617,7 @@ public class VCardComposer {
                    .appendRelation(contentValuesListMap.get(Relation.CONTENT_ITEM_TYPE));
            return builder.toString();
        }
    }

    public void terminate() {
        for (OneEntryHandler handler : mHandlerList) {
@@ -565,26 +639,38 @@ public class VCardComposer {
    @Override
    public void finalize() {
        if (!mTerminateIsCalled) {
            Log.w(LOG_TAG, "terminate() is not called yet. We call it in finalize() step.");
            terminate();
        }
    }

    /**
     * @return returns the number of available entities. The return value is undefined
     * when this object is not ready yet (typically when {{@link #init()} is not called
     * or when {@link #terminate()} is already called).
     */
    public int getCount() {
        if (mCursor == null) {
            Log.w(LOG_TAG, "This object is not ready yet.");
            return 0;
        }
        return mCursor.getCount();
    }

    /**
     * @return true when there's no entity to be built. The return value is undefined
     * when this object is not ready yet.
     */
    public boolean isAfterLast() {
        if (mCursor == null) {
            Log.w(LOG_TAG, "This object is not ready yet.");
            return false;
        }
        return mCursor.isAfterLast();
    }

    /**
     * @return Return the error reason if possible.
     * @return Returns the error reason.
     */
    public String getErrorReason() {
        return mErrorReason;
+220 −192

File changed.

Preview size limit exceeded, changes collapsed.

+36 −13
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ package android.pim.vcard;
public class VCardConstants {
    public static final String VERSION_V21 = "2.1";
    public static final String VERSION_V30 = "3.0";
    public static final String VERSION_V40 = "4.0";

    // The property names valid both in vCard 2.1 and 3.0.
    public static final String PROPERTY_BEGIN = "BEGIN";
@@ -38,25 +39,30 @@ public class VCardConstants {
    public static final String PROPERTY_PHOTO = "PHOTO";
    public static final String PROPERTY_LOGO = "LOGO";
    public static final String PROPERTY_URL = "URL";
    public static final String PROPERTY_BDAY = "BDAY";  // Birthday
    public static final String PROPERTY_BDAY = "BDAY";  // Birthday (3.0, 4.0)
    public static final String PROPERTY_BIRTH = "BIRTH";  // Place of birth (4.0)
    public static final String PROPERTY_ANNIVERSARY = "ANNIVERSARY";  // Date of marriage (4.0)
    public static final String PROPERTY_NAME = "NAME";  // (3.0, 4,0)
    public static final String PROPERTY_NICKNAME = "NICKNAME";  // (3.0, 4.0)
    public static final String PROPERTY_SORT_STRING = "SORT-STRING";  // (3.0, 4.0)
    public static final String PROPERTY_END = "END";

    // Valid property names not supported (not appropriately handled) by our vCard importer now.
    // Valid property names not supported (not appropriately handled) by our importer.
    // TODO: Should be removed from the view of memory efficiency?
    public static final String PROPERTY_REV = "REV";
    public static final String PROPERTY_AGENT = "AGENT";
    public static final String PROPERTY_AGENT = "AGENT";  // (3.0)
    public static final String PROPERTY_DDAY = "DDAY";  // Date of death (4.0)
    public static final String PROPERTY_DEATH = "DEATH";  // Place of death (4.0)

    // Available in vCard 3.0. Shoud not use when composing vCard 2.1 file.
    public static final String PROPERTY_NAME = "NAME";
    public static final String PROPERTY_NICKNAME = "NICKNAME";
    public static final String PROPERTY_SORT_STRING = "SORT-STRING";
    
    // De-fact property values expressing phonetic names.
    public static final String PROPERTY_X_PHONETIC_FIRST_NAME = "X-PHONETIC-FIRST-NAME";
    public static final String PROPERTY_X_PHONETIC_MIDDLE_NAME = "X-PHONETIC-MIDDLE-NAME";
    public static final String PROPERTY_X_PHONETIC_LAST_NAME = "X-PHONETIC-LAST-NAME";

    // Properties both ContactsStruct in Eclair and de-fact vCard extensions
    // shown in http://en.wikipedia.org/wiki/VCard support are defined here.
    // Properties both ContactsStruct and de-fact vCard extensions
    // Shown in http://en.wikipedia.org/wiki/VCard support are defined here.
    public static final String PROPERTY_X_AIM = "X-AIM";
    public static final String PROPERTY_X_MSN = "X-MSN";
    public static final String PROPERTY_X_YAHOO = "X-YAHOO";
@@ -89,6 +95,9 @@ public class VCardConstants {
    public static final String PARAM_TYPE_VOICE = "VOICE";
    public static final String PARAM_TYPE_INTERNET = "INTERNET";

    public static final String PARAM_CHARSET = "CHARSET";
    public static final String PARAM_ENCODING = "ENCODING";

    // Abbreviation of "prefered" according to vCard 2.1 specification.
    // We interpret this value as "primary" property during import/export.
    //
@@ -109,6 +118,12 @@ public class VCardConstants {
    public static final String PARAM_TYPE_BBS = "BBS";
    public static final String PARAM_TYPE_VIDEO = "VIDEO";

    public static final String PARAM_ENCODING_7BIT = "7BIT";
    public static final String PARAM_ENCODING_8BIT = "8BIT";
    public static final String PARAM_ENCODING_QP = "QUOTED-PRINTABLE";
    public static final String PARAM_ENCODING_BASE64 = "BASE64";  // Available in vCard 2.1
    public static final String PARAM_ENCODING_B = "B";  // Available in vCard 3.0

    // TYPE parameters for Phones, which are not formally valid in vCard (at least 2.1).
    // These types are basically encoded to "X-" parameters when composing vCard.
    // Parser passes these when "X-" is added to the parameter or not.
@@ -126,14 +141,15 @@ public class VCardConstants {
    public static final String PARAM_ADR_TYPE_DOM = "DOM";
    public static final String PARAM_ADR_TYPE_INTL = "INTL";

    public static final String PARAM_LANGUAGE = "LANGUAGE";

    // SORT-AS parameter introduced in vCard 4.0 (as of rev.13)
    public static final String PARAM_SORT_AS = "SORT-AS";

    // TYPE parameters not officially valid but used in some vCard exporter.
    // Do not use in composer side.
    public static final String PARAM_EXTRA_TYPE_COMPANY = "COMPANY";

    // DoCoMo specific type parameter. Used with "SOUND" property, which is alternate of SORT-STRING in
    // vCard 3.0.
    public static final String PARAM_TYPE_X_IRMC_N = "X-IRMC-N";

    public interface ImportOnly {
        public static final String PROPERTY_X_NICKNAME = "X-NICKNAME";
        // Some device emits this "X-" parameter for expressing Google Talk,
@@ -142,7 +158,14 @@ public class VCardConstants {
        public static final String PROPERTY_X_GOOGLE_TALK_WITH_SPACE = "X-GOOGLE TALK";
    }

    /* package */ static final int MAX_DATA_COLUMN = 15;
    //// Mainly for package constants.

    // DoCoMo specific type parameter. Used with "SOUND" property, which is alternate of
    // SORT-STRING invCard 3.0.
    /* package */ static final String PARAM_TYPE_X_IRMC_N = "X-IRMC-N";

    // Used in unit test.
    public static final int MAX_DATA_COLUMN = 15;

    /* package */ static final int MAX_CHARACTER_NUMS_QP = 76;
    static final int MAX_CHARACTER_NUMS_BASE64_V30 = 75;
Loading