VCardComposer.java revision da2f6ef422b360827f2c5231552d8c9fad0ed8b1
14199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa/*
24199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Copyright (C) 2009 The Android Open Source Project
34199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa *
44199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Licensed under the Apache License, Version 2.0 (the "License"); you may not
54199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * use this file except in compliance with the License. You may obtain a copy of
64199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * the License at
74199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa *
84199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * http://www.apache.org/licenses/LICENSE-2.0
94199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa *
104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Unless required by applicable law or agreed to in writing, software
114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * License for the specific language governing permissions and limitations under
144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * the License.
154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */
164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawapackage com.android.vcard;
174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
18422643669a44d08ca8b22a73286fae988a288b0eDaisuke Miyakawaimport com.android.vcard.exception.VCardException;
19422643669a44d08ca8b22a73286fae988a288b0eDaisuke Miyakawa
204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.content.ContentResolver;
214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.content.ContentValues;
224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.content.Context;
234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.content.Entity;
244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.content.Entity.NamedContentValues;
25422643669a44d08ca8b22a73286fae988a288b0eDaisuke Miyakawaimport android.content.EntityIterator;
264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.database.Cursor;
274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.database.sqlite.SQLiteException;
284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.net.Uri;
294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.Email;
304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.Event;
314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.Im;
324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.Nickname;
334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.Note;
344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.Organization;
354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.Phone;
364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.Photo;
374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.Relation;
38422643669a44d08ca8b22a73286fae988a288b0eDaisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.SipAddress;
394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.StructuredName;
404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.Website;
42422643669a44d08ca8b22a73286fae988a288b0eDaisuke Miyakawaimport android.provider.ContactsContract.Contacts;
43422643669a44d08ca8b22a73286fae988a288b0eDaisuke Miyakawaimport android.provider.ContactsContract.Data;
44422643669a44d08ca8b22a73286fae988a288b0eDaisuke Miyakawaimport android.provider.ContactsContract.RawContacts;
45422643669a44d08ca8b22a73286fae988a288b0eDaisuke Miyakawaimport android.provider.ContactsContract.RawContactsEntity;
464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.text.TextUtils;
474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.util.CharsetUtils;
484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.util.Log;
494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.io.BufferedWriter;
514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.io.FileOutputStream;
524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.io.IOException;
534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.io.OutputStream;
544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.io.OutputStreamWriter;
554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.io.UnsupportedEncodingException;
564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.io.Writer;
574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.lang.reflect.InvocationTargetException;
584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.lang.reflect.Method;
594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.nio.charset.UnsupportedCharsetException;
604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.ArrayList;
614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.HashMap;
624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.List;
634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.Map;
644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa/**
664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * <p>
674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * The class for composing vCard from Contacts information.
684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * </p>
694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * <p>
704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Usually, this class should be used like this.
714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * </p>
724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * <pre class="prettyprint">VCardComposer composer = null;
734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * try {
744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa *     composer = new VCardComposer(context);
754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa *     composer.addHandler(
764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa *             composer.new HandlerForOutputStream(outputStream));
774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa *     if (!composer.init()) {
784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa *         // Do something handling the situation.
794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa *         return;
804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa *     }
814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa *     while (!composer.isAfterLast()) {
824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa *         if (mCanceled) {
834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa *             // Assume a user may cancel this operation during the export.
844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa *             return;
854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa *         }
864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa *         if (!composer.createOneEntry()) {
874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa *             // Do something handling the error situation.
884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa *             return;
894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa *         }
904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa *     }
914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * } finally {
924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa *     if (composer != null) {
934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa *         composer.terminate();
944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa *     }
954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * }</pre>
964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * <p>
974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Users have to manually take care of memory efficiency. Even one vCard may contain
984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * image of non-trivial size for mobile devices.
994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * </p>
1004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * <p>
1014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * {@link VCardBuilder} is used to build each vCard.
1024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * </p>
1034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */
1044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawapublic class VCardComposer {
1054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private static final String LOG_TAG = "VCardComposer";
1064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static final String FAILURE_REASON_FAILED_TO_GET_DATABASE_INFO =
1084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        "Failed to get database information";
1094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static final String FAILURE_REASON_NO_ENTRY =
1114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        "There's no exportable in the database";
1124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static final String FAILURE_REASON_NOT_INITIALIZED =
1144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        "The vCard composer object is not correctly initialized";
1154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /** Should be visible only from developers... (no need to translate, hopefully) */
1174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static final String FAILURE_REASON_UNSUPPORTED_URI =
1184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        "The Uri vCard composer received is not supported by the composer.";
1194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static final String NO_ERROR = "No error";
1214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static final String VCARD_TYPE_STRING_DOCOMO = "docomo";
1234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    // Strictly speaking, "Shift_JIS" is the most appropriate, but we use upper version here,
1254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    // since usual vCard devices for Japanese devices already use it.
1264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private static final String SHIFT_JIS = "SHIFT_JIS";
1274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private static final String UTF_8 = "UTF-8";
1284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
1304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Special URI for testing.
1314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
1324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static final String VCARD_TEST_AUTHORITY = "com.android.unit_tests.vcard";
1334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static final Uri VCARD_TEST_AUTHORITY_URI =
1344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        Uri.parse("content://" + VCARD_TEST_AUTHORITY);
1354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static final Uri CONTACTS_TEST_CONTENT_URI =
1364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        Uri.withAppendedPath(VCARD_TEST_AUTHORITY_URI, "contacts");
1374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private static final Map<Integer, String> sImMap;
1394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    static {
1414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sImMap = new HashMap<Integer, String>();
1424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sImMap.put(Im.PROTOCOL_AIM, VCardConstants.PROPERTY_X_AIM);
1434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sImMap.put(Im.PROTOCOL_MSN, VCardConstants.PROPERTY_X_MSN);
1444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sImMap.put(Im.PROTOCOL_YAHOO, VCardConstants.PROPERTY_X_YAHOO);
1454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sImMap.put(Im.PROTOCOL_ICQ, VCardConstants.PROPERTY_X_ICQ);
1464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sImMap.put(Im.PROTOCOL_JABBER, VCardConstants.PROPERTY_X_JABBER);
1474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sImMap.put(Im.PROTOCOL_SKYPE, VCardConstants.PROPERTY_X_SKYPE_USERNAME);
1484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // We don't add Google talk here since it has to be handled separately.
1494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
1504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static interface OneEntryHandler {
1524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        public boolean onInit(Context context);
1534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        public boolean onEntryCreated(String vcard);
1544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        public void onTerminate();
1554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
1564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
1584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
1594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * An useful handler for emitting vCard String to an OutputStream object one by one.
1604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
1614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
1624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * The input OutputStream object is closed() on {@link #onTerminate()}.
1634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Must not close the stream outside this class.
1644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
1654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
1664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public final class HandlerForOutputStream implements OneEntryHandler {
1674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        @SuppressWarnings("hiding")
1684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        private static final String LOG_TAG = "VCardComposer.HandlerForOutputStream";
1694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        private boolean mOnTerminateIsCalled = false;
1714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        private final OutputStream mOutputStream; // mWriter will close this.
1734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        private Writer mWriter;
1744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        /**
1764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa         * Input stream will be closed on the detruction of this object.
1774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa         */
1784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        public HandlerForOutputStream(final OutputStream outputStream) {
1794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            mOutputStream = outputStream;
1804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
1814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
182da2f6ef422b360827f2c5231552d8c9fad0ed8b1Daisuke Miyakawa        @Override
1834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        public boolean onInit(final Context context) {
1844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            try {
1854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                mWriter = new BufferedWriter(new OutputStreamWriter(
1864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        mOutputStream, mCharset));
1874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } catch (UnsupportedEncodingException e1) {
1884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                Log.e(LOG_TAG, "Unsupported charset: " + mCharset);
1894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                mErrorReason = "Encoding is not supported (usually this does not happen!): "
1904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        + mCharset;
1914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                return false;
1924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
1934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (mIsDoCoMo) {
1954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                try {
1964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    // Create one empty entry.
1974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    mWriter.write(createOneEntryInternal("-1", null));
1984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                } catch (VCardException e) {
1994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    Log.e(LOG_TAG, "VCardException has been thrown during on Init(): " +
2004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                            e.getMessage());
2014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    return false;
2024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                } catch (IOException e) {
2034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    Log.e(LOG_TAG,
2044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                            "IOException occurred during exportOneContactData: "
2054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                                    + e.getMessage());
2064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    mErrorReason = "IOException occurred: " + e.getMessage();
2074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    return false;
2084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
2094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
2104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return true;
2114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
2124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
213da2f6ef422b360827f2c5231552d8c9fad0ed8b1Daisuke Miyakawa        @Override
2144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        public boolean onEntryCreated(String vcard) {
2154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            try {
2164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                mWriter.write(vcard);
2174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } catch (IOException e) {
2184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                Log.e(LOG_TAG,
2194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        "IOException occurred during exportOneContactData: "
2204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                                + e.getMessage());
2214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                mErrorReason = "IOException occurred: " + e.getMessage();
2224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                return false;
2234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
2244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return true;
2254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
2264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
227da2f6ef422b360827f2c5231552d8c9fad0ed8b1Daisuke Miyakawa        @Override
2284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        public void onTerminate() {
2294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            mOnTerminateIsCalled = true;
2304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (mWriter != null) {
2314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                try {
2324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    // Flush and sync the data so that a user is able to pull
2334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    // the SDCard just after
2344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    // the export.
2354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    mWriter.flush();
2364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    if (mOutputStream != null
2374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                            && mOutputStream instanceof FileOutputStream) {
2384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                            ((FileOutputStream) mOutputStream).getFD().sync();
2394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    }
2404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                } catch (IOException e) {
2414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    Log.d(LOG_TAG,
2424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                            "IOException during closing the output stream: "
2434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                                    + e.getMessage());
2444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                } finally {
2454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    closeOutputStream();
2464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
2474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
2484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
2494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        public void closeOutputStream() {
2514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            try {
2524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                mWriter.close();
2534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } catch (IOException e) {
2544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                Log.w(LOG_TAG, "IOException is thrown during close(). Ignoring.");
2554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
2564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
2574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        @Override
2594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        public void finalize() {
2604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (!mOnTerminateIsCalled) {
2614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                onTerminate();
2624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
2634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
2644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
2654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private final Context mContext;
2674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private final int mVCardType;
2684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private final boolean mCareHandlerErrors;
2694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private final ContentResolver mContentResolver;
2704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private final boolean mIsDoCoMo;
2724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private Cursor mCursor;
2734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private int mIdColumn;
2744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private final String mCharset;
2764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private boolean mTerminateIsCalled;
2774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private final List<OneEntryHandler> mHandlerList;
2784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private String mErrorReason = NO_ERROR;
2804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private static final String[] sContactsProjection = new String[] {
2824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        Contacts._ID,
2834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    };
2844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public VCardComposer(Context context) {
2864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        this(context, VCardConfig.VCARD_TYPE_DEFAULT, null, true);
2874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
2884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
2904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * The variant which sets charset to null and sets careHandlerErrors to true.
2914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
2924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public VCardComposer(Context context, int vcardType) {
2934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        this(context, vcardType, null, true);
2944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
2954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public VCardComposer(Context context, int vcardType, String charset) {
2974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        this(context, vcardType, charset, true);
2984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
2994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
3014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * The variant which sets charset to null.
3024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
3034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public VCardComposer(final Context context, final int vcardType,
3044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final boolean careHandlerErrors) {
3054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        this(context, vcardType, null, careHandlerErrors);
3064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
3094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Construct for supporting call log entry vCard composing.
3104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *
3114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @param context Context to be used during the composition.
3124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @param vcardType The type of vCard, typically available via {@link VCardConfig}.
3134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @param charset The charset to be used. Use null when you don't need the charset.
3144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @param careHandlerErrors If true, This object returns false everytime
3154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * a Handler object given via {{@link #addHandler(OneEntryHandler)} returns false.
3164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * If false, this ignores those errors.
3174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
3184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public VCardComposer(final Context context, final int vcardType, String charset,
3194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final boolean careHandlerErrors) {
3204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        mContext = context;
3214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        mVCardType = vcardType;
3224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        mCareHandlerErrors = careHandlerErrors;
3234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        mContentResolver = context.getContentResolver();
3244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        mIsDoCoMo = VCardConfig.isDoCoMo(vcardType);
3264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        mHandlerList = new ArrayList<OneEntryHandler>();
3274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        charset = (TextUtils.isEmpty(charset) ? VCardConfig.DEFAULT_EXPORT_CHARSET : charset);
3294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final boolean shouldAppendCharsetParam = !(
330be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa                VCardConfig.isVersion30(vcardType) && UTF_8.equalsIgnoreCase(charset));
3314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (mIsDoCoMo || shouldAppendCharsetParam) {
3334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (SHIFT_JIS.equalsIgnoreCase(charset)) {
3344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                if (mIsDoCoMo) {
3354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    try {
3364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        charset = CharsetUtils.charsetForVendor(SHIFT_JIS, "docomo").name();
3374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    } catch (UnsupportedCharsetException e) {
3384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        Log.e(LOG_TAG,
3394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                                "DoCoMo-specific SHIFT_JIS was not found. "
3404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                                + "Use SHIFT_JIS as is.");
3414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        charset = SHIFT_JIS;
3424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    }
3434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                } else {
3444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    try {
3454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        charset = CharsetUtils.charsetForVendor(SHIFT_JIS).name();
3464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    } catch (UnsupportedCharsetException e) {
347422643669a44d08ca8b22a73286fae988a288b0eDaisuke Miyakawa                        /* Log.e(LOG_TAG,
3484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                                "Career-specific SHIFT_JIS was not found. "
349422643669a44d08ca8b22a73286fae988a288b0eDaisuke Miyakawa                                + "Use SHIFT_JIS as is."); */
3504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        charset = SHIFT_JIS;
3514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    }
3524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
3534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                mCharset = charset;
3544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else {
355422643669a44d08ca8b22a73286fae988a288b0eDaisuke Miyakawa                /* Log.w(LOG_TAG,
3564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        "The charset \"" + charset + "\" is used while "
357422643669a44d08ca8b22a73286fae988a288b0eDaisuke Miyakawa                        + SHIFT_JIS + " is needed to be used."); */
3584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                if (TextUtils.isEmpty(charset)) {
3594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    mCharset = SHIFT_JIS;
3604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                } else {
3614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    try {
3624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        charset = CharsetUtils.charsetForVendor(charset).name();
3634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    } catch (UnsupportedCharsetException e) {
3644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        Log.i(LOG_TAG,
3654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                                "Career-specific \"" + charset + "\" was not found (as usual). "
3664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                                + "Use it as is.");
3674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    }
3684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    mCharset = charset;
3694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
3704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
3714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else {
3724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (TextUtils.isEmpty(charset)) {
3734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                mCharset = UTF_8;
3744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else {
3754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                try {
3764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    charset = CharsetUtils.charsetForVendor(charset).name();
3774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                } catch (UnsupportedCharsetException e) {
3784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    Log.i(LOG_TAG,
3794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                            "Career-specific \"" + charset + "\" was not found (as usual). "
3804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                            + "Use it as is.");
3814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
3824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                mCharset = charset;
3834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
3844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
3854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        Log.d(LOG_TAG, "Use the charset \"" + mCharset + "\"");
3874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
3904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Must be called before {@link #init()}.
3914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
3924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public void addHandler(OneEntryHandler handler) {
3934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (handler != null) {
3944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            mHandlerList.add(handler);
3954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
3964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
3994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @return Returns true when initialization is successful and all the other
4004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *          methods are available. Returns false otherwise.
4014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
4024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public boolean init() {
4034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return init(null, null);
4044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
4054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
4064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public boolean init(final String selection, final String[] selectionArgs) {
4074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return init(Contacts.CONTENT_URI, selection, selectionArgs, null);
4084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
4094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
4104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
4114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Note that this is unstable interface, may be deleted in the future.
4124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
4134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public boolean init(final Uri contentUri, final String selection,
4144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final String[] selectionArgs, final String sortOrder) {
4154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (contentUri == null) {
4164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return false;
4174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
4184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
4194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (mCareHandlerErrors) {
4204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final List<OneEntryHandler> finishedList = new ArrayList<OneEntryHandler>(
4214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    mHandlerList.size());
4224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            for (OneEntryHandler handler : mHandlerList) {
4234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                if (!handler.onInit(mContext)) {
4244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    for (OneEntryHandler finished : finishedList) {
4254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        finished.onTerminate();
4264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    }
4274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    return false;
4284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
4294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
4304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else {
4314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            // Just ignore the false returned from onInit().
4324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            for (OneEntryHandler handler : mHandlerList) {
4334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                handler.onInit(mContext);
4344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
4354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
4364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
4374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final String[] projection;
4384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (Contacts.CONTENT_URI.equals(contentUri) ||
4394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                CONTACTS_TEST_CONTENT_URI.equals(contentUri)) {
4404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            projection = sContactsProjection;
4414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else {
4424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            mErrorReason = FAILURE_REASON_UNSUPPORTED_URI;
4434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return false;
4444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
4454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        mCursor = mContentResolver.query(
4464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                contentUri, projection, selection, selectionArgs, sortOrder);
4474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
4484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (mCursor == null) {
4494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            mErrorReason = FAILURE_REASON_FAILED_TO_GET_DATABASE_INFO;
4504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return false;
4514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
4524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
4534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (getCount() == 0 || !mCursor.moveToFirst()) {
4544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            try {
4554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                mCursor.close();
4564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } catch (SQLiteException e) {
4574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                Log.e(LOG_TAG, "SQLiteException on Cursor#close(): " + e.getMessage());
4584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } finally {
4594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                mCursor = null;
4604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                mErrorReason = FAILURE_REASON_NO_ENTRY;
4614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
4624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return false;
4634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
4644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
4654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        mIdColumn = mCursor.getColumnIndex(Contacts._ID);
4664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
4674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return true;
4684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
4694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
4704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public boolean createOneEntry() {
4714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return createOneEntry(null);
4724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
4734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
4744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
4754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @param getEntityIteratorMethod For Dependency Injection.
4764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @hide just for testing.
4774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
4784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public boolean createOneEntry(Method getEntityIteratorMethod) {
4794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (mCursor == null || mCursor.isAfterLast()) {
4804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            mErrorReason = FAILURE_REASON_NOT_INITIALIZED;
4814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return false;
4824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
4834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final String vcard;
4844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        try {
4854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (mIdColumn >= 0) {
4864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                vcard = createOneEntryInternal(mCursor.getString(mIdColumn),
4874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        getEntityIteratorMethod);
4884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else {
4894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                Log.e(LOG_TAG, "Incorrect mIdColumn: " + mIdColumn);
4904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                return true;
4914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
4924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } catch (VCardException e) {
4934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            Log.e(LOG_TAG, "VCardException has been thrown: " + e.getMessage());
4944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return false;
4954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } catch (OutOfMemoryError error) {
4964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            // Maybe some data (e.g. photo) is too big to have in memory. But it
4974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            // should be rare.
4984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            Log.e(LOG_TAG, "OutOfMemoryError occured. Ignore the entry.");
4994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            System.gc();
5004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            // TODO: should tell users what happened?
5014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return true;
5024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } finally {
5034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            mCursor.moveToNext();
5044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
5054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
5064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // This function does not care the OutOfMemoryError on the handler side :-P
5074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (mCareHandlerErrors) {
5084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            List<OneEntryHandler> finishedList = new ArrayList<OneEntryHandler>(
5094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    mHandlerList.size());
5104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            for (OneEntryHandler handler : mHandlerList) {
5114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                if (!handler.onEntryCreated(vcard)) {
5124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    return false;
5134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
5144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
5154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else {
5164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            for (OneEntryHandler handler : mHandlerList) {
5174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                handler.onEntryCreated(vcard);
5184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
5194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
5204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
5214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return true;
5224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
5234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
5244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private String createOneEntryInternal(final String contactId,
5254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final Method getEntityIteratorMethod) throws VCardException {
5264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final Map<String, List<ContentValues>> contentValuesListMap =
5274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                new HashMap<String, List<ContentValues>>();
5284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // The resolver may return the entity iterator with no data. It is possible.
5294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // e.g. If all the data in the contact of the given contact id are not exportable ones,
5304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //      they are hidden from the view of this method, though contact id itself exists.
5314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        EntityIterator entityIterator = null;
5324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        try {
5334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final Uri uri = RawContactsEntity.CONTENT_URI.buildUpon()
5344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    // .appendQueryParameter("for_export_only", "1")
5354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    .appendQueryParameter(Data.FOR_EXPORT_ONLY, "1")
5364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    .build();
5374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final String selection = Data.CONTACT_ID + "=?";
5384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final String[] selectionArgs = new String[] {contactId};
5394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (getEntityIteratorMethod != null) {
5404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                // Please note that this branch is executed by unit tests only
5414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                try {
5424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    entityIterator = (EntityIterator)getEntityIteratorMethod.invoke(null,
5434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                            mContentResolver, uri, selection, selectionArgs, null);
5444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                } catch (IllegalArgumentException e) {
5454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    Log.e(LOG_TAG, "IllegalArgumentException has been thrown: " +
5464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                            e.getMessage());
5474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                } catch (IllegalAccessException e) {
5484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    Log.e(LOG_TAG, "IllegalAccessException has been thrown: " +
5494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                            e.getMessage());
5504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                } catch (InvocationTargetException e) {
5514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    Log.e(LOG_TAG, "InvocationTargetException has been thrown: ");
5524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    StackTraceElement[] stackTraceElements = e.getCause().getStackTrace();
5534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    for (StackTraceElement element : stackTraceElements) {
5544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        Log.e(LOG_TAG, "    at " + element.toString());
5554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    }
5564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    throw new VCardException("InvocationTargetException has been thrown: " +
5574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                            e.getCause().getMessage());
5584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
5594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else {
5604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                entityIterator = RawContacts.newEntityIterator(mContentResolver.query(
5614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        uri, null, selection, selectionArgs, null));
5624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
5634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
5644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (entityIterator == null) {
5654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                Log.e(LOG_TAG, "EntityIterator is null");
5664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                return "";
5674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
5684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
5694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (!entityIterator.hasNext()) {
5704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                Log.w(LOG_TAG, "Data does not exist. contactId: " + contactId);
5714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                return "";
5724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
5734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
5744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            while (entityIterator.hasNext()) {
5754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                Entity entity = entityIterator.next();
5764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                for (NamedContentValues namedContentValues : entity.getSubValues()) {
5774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    ContentValues contentValues = namedContentValues.values;
5784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    String key = contentValues.getAsString(Data.MIMETYPE);
5794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    if (key != null) {
5804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        List<ContentValues> contentValuesList =
5814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                                contentValuesListMap.get(key);
5824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        if (contentValuesList == null) {
5834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                            contentValuesList = new ArrayList<ContentValues>();
5844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                            contentValuesListMap.put(key, contentValuesList);
5854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        }
5864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        contentValuesList.add(contentValues);
5874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    }
5884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
5894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
5904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } finally {
5914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (entityIterator != null) {
5924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                entityIterator.close();
5934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
5944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
5954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
5964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return buildVCard(contentValuesListMap);
5974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
5984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
5994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
6004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Builds and returns vCard using given map, whose key is CONTENT_ITEM_TYPE defined in
6014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * {ContactsContract}. Developers can override this method to customize the output.
6024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
6034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public String buildVCard(final Map<String, List<ContentValues>> contentValuesListMap) {
6044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (contentValuesListMap == null) {
6054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            Log.e(LOG_TAG, "The given map is null. Ignore and return empty String");
6064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return "";
6074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else {
6084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final VCardBuilder builder = new VCardBuilder(mVCardType, mCharset);
6094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            builder.appendNameProperties(contentValuesListMap.get(StructuredName.CONTENT_ITEM_TYPE))
6104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    .appendNickNames(contentValuesListMap.get(Nickname.CONTENT_ITEM_TYPE))
6114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    .appendPhones(contentValuesListMap.get(Phone.CONTENT_ITEM_TYPE))
6124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    .appendEmails(contentValuesListMap.get(Email.CONTENT_ITEM_TYPE))
6134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    .appendPostals(contentValuesListMap.get(StructuredPostal.CONTENT_ITEM_TYPE))
6144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    .appendOrganizations(contentValuesListMap.get(Organization.CONTENT_ITEM_TYPE))
6158c1cdbbccd5169122c183f6fbfd4436faacf2a1dDaisuke Miyakawa                    .appendWebsites(contentValuesListMap.get(Website.CONTENT_ITEM_TYPE));
6168c1cdbbccd5169122c183f6fbfd4436faacf2a1dDaisuke Miyakawa            if ((mVCardType & VCardConfig.FLAG_REFRAIN_IMAGE_EXPORT) == 0) {
6178c1cdbbccd5169122c183f6fbfd4436faacf2a1dDaisuke Miyakawa                builder.appendPhotos(contentValuesListMap.get(Photo.CONTENT_ITEM_TYPE));
6188c1cdbbccd5169122c183f6fbfd4436faacf2a1dDaisuke Miyakawa            }
6198c1cdbbccd5169122c183f6fbfd4436faacf2a1dDaisuke Miyakawa            builder.appendNotes(contentValuesListMap.get(Note.CONTENT_ITEM_TYPE))
6204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    .appendEvents(contentValuesListMap.get(Event.CONTENT_ITEM_TYPE))
6214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    .appendIms(contentValuesListMap.get(Im.CONTENT_ITEM_TYPE))
622422643669a44d08ca8b22a73286fae988a288b0eDaisuke Miyakawa                    .appendSipAddresses(contentValuesListMap.get(SipAddress.CONTENT_ITEM_TYPE))
6234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    .appendRelation(contentValuesListMap.get(Relation.CONTENT_ITEM_TYPE));
6244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return builder.toString();
6254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
6264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
6274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
6284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public void terminate() {
6294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        for (OneEntryHandler handler : mHandlerList) {
6304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            handler.onTerminate();
6314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
6324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
6334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (mCursor != null) {
6344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            try {
6354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                mCursor.close();
6364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } catch (SQLiteException e) {
6374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                Log.e(LOG_TAG, "SQLiteException on Cursor#close(): " + e.getMessage());
6384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
6394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            mCursor = null;
6404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
6414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
6424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        mTerminateIsCalled = true;
6434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
6444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
6454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
6464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public void finalize() {
6474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (!mTerminateIsCalled) {
6484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            Log.w(LOG_TAG, "terminate() is not called yet. We call it in finalize() step.");
6494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            terminate();
6504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
6514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
6524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
6534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
6544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @return returns the number of available entities. The return value is undefined
6554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * when this object is not ready yet (typically when {{@link #init()} is not called
6564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * or when {@link #terminate()} is already called).
6574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
6584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public int getCount() {
6594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (mCursor == null) {
6604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            Log.w(LOG_TAG, "This object is not ready yet.");
6614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return 0;
6624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
6634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return mCursor.getCount();
6644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
6654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
6664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
6674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @return true when there's no entity to be built. The return value is undefined
6684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * when this object is not ready yet.
6694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
6704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public boolean isAfterLast() {
6714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (mCursor == null) {
6724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            Log.w(LOG_TAG, "This object is not ready yet.");
6734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return false;
6744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
6754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return mCursor.isAfterLast();
6764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
6774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
6784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
6794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @return Returns the error reason.
6804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
6814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public String getErrorReason() {
6824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return mErrorReason;
6834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
6844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa}
685