diff options
-rw-r--r-- | core/java/android/pim/vcard/VCardComposer.java | 240 |
1 files changed, 202 insertions, 38 deletions
diff --git a/core/java/android/pim/vcard/VCardComposer.java b/core/java/android/pim/vcard/VCardComposer.java index 3c01a9e..45f7ef3 100644 --- a/core/java/android/pim/vcard/VCardComposer.java +++ b/core/java/android/pim/vcard/VCardComposer.java @@ -1,12 +1,12 @@ /* * Copyright (C) 2009 The Android Open Source Project - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the @@ -38,6 +38,10 @@ import android.provider.ContactsContract.CommonDataKinds.Photo; import android.provider.ContactsContract.CommonDataKinds.StructuredName; import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; import android.provider.ContactsContract.CommonDataKinds.Website; +import android.provider.CallLog.Calls; +import android.provider.CallLog; +import android.text.format.DateUtils; +import android.text.format.Time; import android.text.TextUtils; import android.util.CharsetUtils; import android.util.Log; @@ -61,11 +65,11 @@ import java.util.Map; * completely differnt implementation from * android.syncml.pim.vcard.VCardComposer, which is not maintained anymore. * </p> - * + * * <p> * Usually, this class should be used like this. * </p> - * + * * <pre class="prettyprint"> VCardComposer composer = null; try { composer = new * VCardComposer(context); composer.addHandler(composer.new * HandlerForOutputStream(outputStream)); if (!composer.init()) { // Do @@ -213,6 +217,9 @@ public class VCardComposer { private static final String VCARD_PROPERTY_X_NICKNAME = "X-NICKNAME"; // TODO: add properties like X-LATITUDE + // Property for call log entry + private static final String VCARD_PROPERTY_X_TIMESTAMP = "X-IRMC-CALL-DATETIME"; + // Properties for DoCoMo vCard. private static final String VCARD_PROPERTY_X_CLASS = "X-CLASS"; private static final String VCARD_PROPERTY_X_REDUCTION = "X-REDUCTION"; @@ -227,6 +234,7 @@ public class VCardComposer { private static final String VCARD_DATA_SEPARATOR = ":"; private static final String VCARD_ITEM_SEPARATOR = ";"; private static final String VCARD_WS = " "; + private static final String VCARD_ATTR_EQUAL = "="; // Type strings are now in VCardConstants.java. @@ -238,20 +246,20 @@ public class VCardComposer { private static final String SHIFT_JIS = "SHIFT_JIS"; private final Context mContext; - private final int mVCardType; - private final boolean mCareHandlerErrors; - private final ContentResolver mContentResolver; + private int mVCardType; + private boolean mCareHandlerErrors; + private ContentResolver mContentResolver; // Convenient member variables about the restriction of the vCard format. // Used for not calling the same methods returning same results. - private final boolean mIsV30; - private final boolean mIsJapaneseMobilePhone; - private final boolean mOnlyOneNoteFieldIsAvailable; - private final boolean mIsDoCoMo; - private final boolean mUsesQuotedPrintable; - private final boolean mUsesAndroidProperty; - private final boolean mUsesDefactProperty; - private final boolean mUsesShiftJis; + private boolean mIsV30; + private boolean mIsJapaneseMobilePhone; + private boolean mOnlyOneNoteFieldIsAvailable; + private boolean mIsDoCoMo; + private boolean mUsesQuotedPrintable; + private boolean mUsesAndroidProperty; + private boolean mUsesDefactProperty; + private boolean mUsesShiftJis; private Cursor mCursor; private int mIdColumn; @@ -264,7 +272,7 @@ public class VCardComposer { private String mErrorReason = "No error"; private static final Map<Integer, String> sImMap; - + static { sImMap = new HashMap<Integer, String>(); sImMap.put(Im.PROTOCOL_AIM, Constants.PROPERTY_X_AIM); @@ -275,8 +283,27 @@ public class VCardComposer { sImMap.put(Im.PROTOCOL_SKYPE, Constants.PROPERTY_X_SKYPE_USERNAME); // Google talk is a special case. } - - + + private boolean mIsCallLogComposer = false; + + private static final String[] CONTACTS_PROJECTION = new String[] { + Contacts._ID, + }; + + /** The projection to use when querying the call log table */ + private static final String[] CALL_LOG_PROJECTION = new String[] { + Calls.NUMBER, Calls.DATE, Calls.TYPE, Calls.CACHED_NAME, Calls.CACHED_NUMBER_TYPE, + Calls.CACHED_NUMBER_LABEL + }; + private static final int NUMBER_COLUMN_INDEX = 0; + private static final int DATE_COLUMN_INDEX = 1; + private static final int CALL_TYPE_COLUMN_INDEX = 2; + private static final int CALLER_NAME_COLUMN_INDEX = 3; + private static final int CALLER_NUMBERTYPE_COLUMN_INDEX = 4; + private static final int CALLER_NUMBERLABEL_COLUMN_INDEX = 5; + + private static final String FLAG_TIMEZONE_UTC = "Z"; + public VCardComposer(Context context) { this(context, VCardConfig.VCARD_TYPE_DEFAULT, true); } @@ -287,10 +314,15 @@ public class VCardComposer { careHandlerErrors); } - public VCardComposer(Context context, int vcardType, boolean careHandlerErrors) { + /** + * Construct for supporting call log entry vCard composing + */ + public VCardComposer(Context context, int vcardType, boolean careHandlerErrors, + boolean isCallLogComposer) { mContext = context; mVCardType = vcardType; mCareHandlerErrors = careHandlerErrors; + mIsCallLogComposer = isCallLogComposer; mContentResolver = context.getContentResolver(); mIsV30 = VCardConfig.isV30(vcardType); @@ -320,6 +352,38 @@ public class VCardComposer { } } + public VCardComposer(Context context, int vcardType, boolean careHandlerErrors) { + this(context, vcardType, careHandlerErrors, false); + } + + /** + * This static function is to compose vCard for phone own number + */ + public String composeVCardForPhoneOwnNumber(int phonetype, String phoneName, + String phoneNumber, boolean vcardVer21) { + final StringBuilder builder = new StringBuilder(); + appendVCardLine(builder, VCARD_PROPERTY_BEGIN, VCARD_DATA_VCARD); + if (!vcardVer21) { + appendVCardLine(builder, VCARD_PROPERTY_VERSION, Constants.VERSION_V30); + } else { + appendVCardLine(builder, VCARD_PROPERTY_VERSION, Constants.VERSION_V21); + } + + boolean needCharset = false; + if (!(VCardUtils.containsOnlyAscii(phoneName))) { + needCharset = true; + } + appendVCardLine(builder, VCARD_PROPERTY_FULL_NAME, phoneName, needCharset, false); + appendVCardLine(builder, VCARD_PROPERTY_NAME, phoneName, needCharset, false); + + String label = Integer.toString(phonetype); + appendVCardTelephoneLine(builder, phonetype, label, phoneNumber); + + appendVCardLine(builder, VCARD_PROPERTY_END, VCARD_DATA_VCARD); + + return builder.toString(); + } + /** * Must call before {{@link #init()}. */ @@ -357,11 +421,15 @@ public class VCardComposer { } } - final String[] projection = new String[] {Contacts._ID,}; + if (mIsCallLogComposer) { + mCursor = mContentResolver.query(CallLog.Calls.CONTENT_URI, CALL_LOG_PROJECTION, + selection, selectionArgs, null); + } else { + // TODO: thorow an appropriate exception! + mCursor = mContentResolver.query(RawContacts.CONTENT_URI, CONTACTS_PROJECTION, + selection, selectionArgs, null); + } - // TODO: thorow an appropriate exception! - mCursor = mContentResolver.query(RawContacts.CONTENT_URI, projection, - selection, selectionArgs, null); if (mCursor == null || !mCursor.moveToFirst()) { if (mCursor != null) { try { @@ -376,7 +444,11 @@ public class VCardComposer { return false; } - mIdColumn = mCursor.getColumnIndex(Contacts._ID); + if (mIsCallLogComposer) { + mIdColumn = -1; + } else { + mIdColumn = mCursor.getColumnIndex(Contacts._ID); + } return true; } @@ -390,7 +462,16 @@ public class VCardComposer { String name = null; String vcard; try { - vcard = createOneEntryInternal(mCursor.getString(mIdColumn)); + if (mIsCallLogComposer) { + vcard = createOneCallLogEntryInternal(); + } else { + if (mIdColumn >= 0) { + vcard = createOneEntryInternal(mCursor.getString(mIdColumn)); + } else { + Log.e(LOG_TAG, "Incorrect mIdColumn: " + mIdColumn); + return true; + } + } } catch (OutOfMemoryError error) { // Maybe some data (e.g. photo) is too big to have in memory. But it // should be rare. @@ -422,6 +503,89 @@ public class VCardComposer { return true; } + /** + * Format according to RFC 2445 DATETIME type. + * The format is: ("%Y%m%dT%H%M%S"). + */ + private final String formatDate(final long millSecs) { + Time startDate = new Time(); + startDate.set(millSecs); + String date = startDate.format2445(); + return date + FLAG_TIMEZONE_UTC; + } + + /** + * Create call history time stamp field. + * + * @param type call type + */ + private String createCallHistoryTimeStampField(int type) { + // Extension for call history as defined in + // in the Specification for Ic Mobile Communcation - ver 1.1, + // Oct 2000. This is used to send the details of the call + // history - missed, incoming, outgoing along with date and time + // to the requesting device (For example, transferring phone book + // when connected over bluetooth) + // X-IRMC-CALL-DATETIME;MISSED:20050320T100000 + final StringBuilder builder = new StringBuilder(); + builder.append(VCARD_PROPERTY_X_TIMESTAMP); + builder.append(VCARD_ATTR_SEPARATOR); + + if (mIsV30) { + builder.append(Constants.ATTR_TYPE).append(VCARD_ATTR_EQUAL); + } + + if (type == Calls.INCOMING_TYPE) { + builder.append("INCOMING"); + } else if (type == Calls.OUTGOING_TYPE) { + builder.append("OUTGOING"); + } else if (type == Calls.MISSED_TYPE) { + builder.append("MISSED"); + } else { + Log.w(LOG_TAG, "Call log type not correct."); + return null; + } + + return builder.toString(); + } + + private String createOneCallLogEntryInternal() { + final StringBuilder builder = new StringBuilder(); + appendVCardLine(builder, VCARD_PROPERTY_BEGIN, VCARD_DATA_VCARD); + if (mIsV30) { + appendVCardLine(builder, VCARD_PROPERTY_VERSION, Constants.VERSION_V30); + } else { + appendVCardLine(builder, VCARD_PROPERTY_VERSION, Constants.VERSION_V21); + } + String name = mCursor.getString(CALLER_NAME_COLUMN_INDEX); + if (TextUtils.isEmpty(name)) { + name = mCursor.getString(NUMBER_COLUMN_INDEX); + } + boolean needCharset = !(VCardUtils.containsOnlyAscii(name)); + appendVCardLine(builder, VCARD_PROPERTY_FULL_NAME, name, needCharset, false); + appendVCardLine(builder, VCARD_PROPERTY_NAME, name, needCharset, false); + + String number = mCursor.getString(NUMBER_COLUMN_INDEX); + int type = mCursor.getInt(CALLER_NUMBERTYPE_COLUMN_INDEX); + String label = mCursor.getString(CALLER_NUMBERLABEL_COLUMN_INDEX); + if (TextUtils.isEmpty(label)) { + label = Integer.toString(type); + } + appendVCardTelephoneLine(builder, type, label, number); + + long date = mCursor.getLong(DATE_COLUMN_INDEX); + String dateClause = formatDate(date); + int callLogType = mCursor.getInt(CALL_TYPE_COLUMN_INDEX); + String timestampFeldString = createCallHistoryTimeStampField(callLogType); + if (timestampFeldString != null) { + appendVCardLine(builder, timestampFeldString, dateClause); + } + + appendVCardLine(builder, VCARD_PROPERTY_END, VCARD_DATA_VCARD); + + return builder.toString(); + } + private String createOneEntryInternal(final String contactId) { final StringBuilder builder = new StringBuilder(); appendVCardLine(builder, VCARD_PROPERTY_BEGIN, VCARD_DATA_VCARD); @@ -576,7 +740,7 @@ public class VCardComposer { final String encodedPrefix = escapeCharacters(prefix); final String encodedSuffix = escapeCharacters(suffix); - // N property. This order is specified by vCard spec and does not depend on countries. + // N property. This order is specified by vCard spec and does not depend on countries. builder.append(VCARD_PROPERTY_NAME); if (!(VCardUtils.containsOnlyAscii(familyName) && VCardUtils.containsOnlyAscii(givenName) && @@ -584,7 +748,7 @@ public class VCardComposer { VCardUtils.containsOnlyAscii(prefix) && VCardUtils.containsOnlyAscii(suffix))) { builder.append(VCARD_ATTR_SEPARATOR); - builder.append(mVCardAttributeCharset); + builder.append(mVCardAttributeCharset); } builder.append(VCARD_DATA_SEPARATOR); @@ -658,7 +822,7 @@ public class VCardComposer { // but we'll add this info since parser side may be able to // use the charset via // this attribute field. - // + // // e.g. Japanese mobile phones use Shift_Jis while RFC 2426 // recommends // UTF-8. By adding this field, parsers may be able to know @@ -684,12 +848,12 @@ public class VCardComposer { builder.append(VCARD_ATTR_SEPARATOR); builder.append(Constants.ATTR_TYPE_X_IRMC_N); builder.append(VCARD_ATTR_SEPARATOR); - + if (!(VCardUtils.containsOnlyAscii(phoneticFamilyName) && VCardUtils.containsOnlyAscii(phoneticMiddleName) && VCardUtils.containsOnlyAscii(phoneticGivenName))) { builder.append(mVCardAttributeCharset); - builder.append(VCARD_DATA_SEPARATOR); + builder.append(VCARD_DATA_SEPARATOR); } builder.append(escapeCharacters(phoneticFamilyName)); @@ -841,7 +1005,7 @@ public class VCardComposer { builder.append(VCARD_COL_SEPARATOR); } } - + /** * Try to append just one line. If there's no appropriate address * information, append an empty line. @@ -907,10 +1071,10 @@ public class VCardComposer { } // TODO: add "X-GOOGLE TALK" case... } - } + } } } - + private void appendWebsites(final StringBuilder builder, final Map<String, List<ContentValues>> contentValuesListMap) { List<ContentValues> contentValuesList = contentValuesListMap @@ -922,7 +1086,7 @@ public class VCardComposer { } } } - + private void appendBirthday(final StringBuilder builder, final Map<String, List<ContentValues>> contentValuesListMap) { List<ContentValues> contentValuesList = contentValuesListMap @@ -1029,7 +1193,7 @@ public class VCardComposer { /** * Append '\' to the characters which should be escaped. The character set is different * not only between vCard 2.1 and vCard 3.0 but also among each device. - * + * * Note that Quoted-Printable string must not be input here. */ @SuppressWarnings("fallthrough") @@ -1037,7 +1201,7 @@ public class VCardComposer { if (TextUtils.isEmpty(unescaped)) { return ""; } - + StringBuilder builder = new StringBuilder(); final int length = unescaped.length(); for (int i = 0; i < length; i++) { @@ -1358,7 +1522,7 @@ public class VCardComposer { encodedData = encodeQuotedPrintable(rawData); } else { // TODO: one line may be too huge, which may be invalid in vCard spec, though - // several (even well-known) applications do not care this. + // several (even well-known) applications do not care this. encodedData = escapeCharacters(rawData); } |