Loading core/java/android/pim/vcard/VCardComposer.java +46 −23 Original line number Original line Diff line number Diff line Loading @@ -111,6 +111,10 @@ public class VCardComposer { public static final String FAILURE_REASON_NOT_INITIALIZED = public static final String FAILURE_REASON_NOT_INITIALIZED = "The vCard composer object is not correctly initialized"; "The vCard composer object is not correctly initialized"; /** Should be visible only from developers... (no need to translate, hopefully) */ public static final String FAILURE_REASON_UNSUPPORTED_URI = "The Uri vCard composer received is not supported by the composer."; public static final String NO_ERROR = "No error"; public static final String NO_ERROR = "No error"; public static final String VCARD_TYPE_STRING_DOCOMO = "docomo"; public static final String VCARD_TYPE_STRING_DOCOMO = "docomo"; Loading Loading @@ -138,6 +142,15 @@ public class VCardComposer { private static final String SHIFT_JIS = "SHIFT_JIS"; private static final String SHIFT_JIS = "SHIFT_JIS"; /** * Special URI for testing. */ public static final String VCARD_TEST_AUTHORITY = "com.android.unit_tests.vcard"; public static final Uri VCARD_TEST_AUTHORITY_URI = Uri.parse("content://" + VCARD_TEST_AUTHORITY); public static final Uri CONTACTS_TEST_CONTENT_URI = Uri.withAppendedPath(VCARD_TEST_AUTHORITY_URI, "contacts"); private static final Uri sDataRequestUri; private static final Uri sDataRequestUri; private static final Map<Integer, String> sImMap; private static final Map<Integer, String> sImMap; Loading Loading @@ -286,7 +299,7 @@ public class VCardComposer { private String mErrorReason = NO_ERROR; private String mErrorReason = NO_ERROR; private boolean mIsCallLogComposer = false; private boolean mIsCallLogComposer; private static final String[] sContactsProjection = new String[] { private static final String[] sContactsProjection = new String[] { Contacts._ID, Contacts._ID, Loading @@ -307,30 +320,24 @@ public class VCardComposer { private static final String FLAG_TIMEZONE_UTC = "Z"; private static final String FLAG_TIMEZONE_UTC = "Z"; public VCardComposer(Context context) { public VCardComposer(Context context) { this(context, VCardConfig.VCARD_TYPE_DEFAULT, true, false); this(context, VCardConfig.VCARD_TYPE_DEFAULT, true); } } public VCardComposer(Context context, String vcardTypeStr, public VCardComposer(Context context, int vcardType) { boolean careHandlerErrors) { this(context, vcardType, true); this(context, VCardConfig.getVCardTypeFromString(vcardTypeStr), careHandlerErrors, false); } } public VCardComposer(Context context, int vcardType, boolean careHandlerErrors) { public VCardComposer(Context context, String vcardTypeStr, boolean careHandlerErrors) { this(context, vcardType, careHandlerErrors, false); this(context, VCardConfig.getVCardTypeFromString(vcardTypeStr), careHandlerErrors); } } /** /** * Construct for supporting call log entry vCard composing. * Construct for supporting call log entry vCard composing. * * @param isCallLogComposer true if this composer is for creating Call Log vCard. */ */ public VCardComposer(Context context, int vcardType, boolean careHandlerErrors, public VCardComposer(Context context, int vcardType, boolean careHandlerErrors) { boolean isCallLogComposer) { mContext = context; mContext = context; mVCardType = vcardType; mVCardType = vcardType; mCareHandlerErrors = careHandlerErrors; mCareHandlerErrors = careHandlerErrors; mIsCallLogComposer = isCallLogComposer; mContentResolver = context.getContentResolver(); mContentResolver = context.getContentResolver(); mIsV30 = VCardConfig.isV30(vcardType); mIsV30 = VCardConfig.isV30(vcardType); Loading Loading @@ -370,15 +377,26 @@ public class VCardComposer { mHandlerList.add(handler); mHandlerList.add(handler); } } /** * @return Returns true when initialization is successful and all the other * methods are available. Returns false otherwise. */ public boolean init() { public boolean init() { return init(null, null); return init(null, null); } } public boolean init(final String selection, final String[] selectionArgs) { return init(Contacts.CONTENT_URI, selection, selectionArgs, null); } /** /** * @return Returns true when initialization is successful and all the other * Note that this is unstable interface, may be deleted in the future. * methods are available. Returns false otherwise. */ */ public boolean init(final String selection, final String[] selectionArgs) { public boolean init(final Uri contentUri, final String selection, final String[] selectionArgs, final String sortOrder) { if (contentUri == null) { return false; } if (mCareHandlerErrors) { if (mCareHandlerErrors) { List<OneEntryHandler> finishedList = new ArrayList<OneEntryHandler>( List<OneEntryHandler> finishedList = new ArrayList<OneEntryHandler>( mHandlerList.size()); mHandlerList.size()); Loading @@ -397,13 +415,19 @@ public class VCardComposer { } } } } if (mIsCallLogComposer) { final String[] projection; mCursor = mContentResolver.query(CallLog.Calls.CONTENT_URI, sCallLogProjection, if (CallLog.Calls.CONTENT_URI.equals(contentUri)) { selection, selectionArgs, null); projection = sCallLogProjection; mIsCallLogComposer = true; } else if (Contacts.CONTENT_URI.equals(contentUri) || CONTACTS_TEST_CONTENT_URI.equals(contentUri)) { projection = sContactsProjection; } else { } else { mCursor = mContentResolver.query(Contacts.CONTENT_URI, sContactsProjection, mErrorReason = FAILURE_REASON_UNSUPPORTED_URI; selection, selectionArgs, null); return false; } } mCursor = mContentResolver.query( contentUri, projection, selection, selectionArgs, sortOrder); if (mCursor == null) { if (mCursor == null) { mErrorReason = FAILURE_REASON_FAILED_TO_GET_DATABASE_INFO; mErrorReason = FAILURE_REASON_FAILED_TO_GET_DATABASE_INFO; Loading Loading @@ -496,8 +520,7 @@ public class VCardComposer { dataExists = entityIterator.hasNext(); dataExists = entityIterator.hasNext(); while (entityIterator.hasNext()) { while (entityIterator.hasNext()) { Entity entity = entityIterator.next(); Entity entity = entityIterator.next(); for (NamedContentValues namedContentValues : entity for (NamedContentValues namedContentValues : entity.getSubValues()) { .getSubValues()) { ContentValues contentValues = namedContentValues.values; ContentValues contentValues = namedContentValues.values; String key = contentValues.getAsString(Data.MIMETYPE); String key = contentValues.getAsString(Data.MIMETYPE); if (key != null) { if (key != null) { Loading core/java/android/pim/vcard/VCardParser_V21.java +12 −1 Original line number Original line Diff line number Diff line Loading @@ -525,10 +525,18 @@ public class VCardParser_V21 extends VCardParser { throw new VCardException("Unknown type \"" + paramName + "\""); throw new VCardException("Unknown type \"" + paramName + "\""); } } } else { } else { handleType(strArray[0]); handleParamWithoutName(strArray[0]); } } } } /** * vCard 3.0 parser may throw VCardException. */ @SuppressWarnings("unused") protected void handleParamWithoutName(final String paramValue) throws VCardException { handleType(paramValue); } /** /** * ptypeval = knowntype / "X-" word * ptypeval = knowntype / "X-" word */ */ Loading Loading @@ -832,6 +840,9 @@ public class VCardParser_V21 extends VCardParser { @Override @Override public boolean parse(InputStream is, String charset, VCardBuilder builder) public boolean parse(InputStream is, String charset, VCardBuilder builder) throws IOException, VCardException { throws IOException, VCardException { if (charset == null) { charset = VCardConfig.DEFAULT_CHARSET; } final InputStreamReader tmpReader = new InputStreamReader(is, charset); final InputStreamReader tmpReader = new InputStreamReader(is, charset); if (VCardConfig.showPerformanceLog()) { if (VCardConfig.showPerformanceLog()) { mReader = new CustomBufferedReader(tmpReader); mReader = new CustomBufferedReader(tmpReader); Loading core/java/android/pim/vcard/VCardParser_V30.java +34 −2 Original line number Original line Diff line number Diff line Loading @@ -49,6 +49,29 @@ public class VCardParser_V30 extends VCardParser_V21 { private boolean mEmittedAgentWarning = false; private boolean mEmittedAgentWarning = false; /** * True when the caller wants the parser to be strict about the input. * Currently this is only for testing. */ private final boolean mStrictParsing; public VCardParser_V30() { super(); mStrictParsing = false; } /** * @param strictParsing when true, this object throws VCardException when the vcard is not * valid from the view of vCard 3.0 specification (defined in RFC 2426). Note that this class * is not fully yet for being used with this flag and may not notice invalid line(s). * * @hide currently only for testing! */ public VCardParser_V30(boolean strictParsing) { super(); mStrictParsing = strictParsing; } @Override @Override protected int getVersion() { protected int getVersion() { return VCardConfig.FLAG_V30; return VCardConfig.FLAG_V30; Loading Loading @@ -205,6 +228,15 @@ public class VCardParser_V30 extends VCardParser_V21 { super.handleAnyParam(paramName, paramValue); super.handleAnyParam(paramName, paramValue); } } @Override protected void handleParamWithoutName(final String paramValue) throws VCardException { if (mStrictParsing) { throw new VCardException("Parameter without name is not acceptable in vCard 3.0"); } else { super.handleParamWithoutName(paramValue); } } /** /** * vCard 3.0 defines * vCard 3.0 defines * * Loading tests/AndroidTests/src/com/android/unit_tests/vcard/MockCursor.java 0 → 100644 +254 −0 Original line number Original line Diff line number Diff line /* * 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 License for the specific language governing permissions and * limitations under the License. */ package com.android.unit_tests.vcard; import android.content.ContentResolver; import android.database.CharArrayBuffer; import android.database.ContentObserver; import android.database.Cursor; import android.database.DataSetObserver; import android.net.Uri; import android.os.Bundle; import java.util.Map; public class MockCursor implements Cursor { public int getColumnCount() { throw new UnsupportedOperationException("unimplemented mock method"); } public int getColumnIndex(String columnName) { throw new UnsupportedOperationException("unimplemented mock method"); } public int getColumnIndexOrThrow(String columnName) { throw new UnsupportedOperationException("unimplemented mock method"); } public String getColumnName(int columnIndex) { throw new UnsupportedOperationException("unimplemented mock method"); } public String[] getColumnNames() { throw new UnsupportedOperationException("unimplemented mock method"); } public int getCount() { throw new UnsupportedOperationException("unimplemented mock method"); } public boolean isNull(int columnIndex) { throw new UnsupportedOperationException("unimplemented mock method"); } public int getInt(int columnIndex) { throw new UnsupportedOperationException("unimplemented mock method"); } public long getLong(int columnIndex) { throw new UnsupportedOperationException("unimplemented mock method"); } public short getShort(int columnIndex) { throw new UnsupportedOperationException("unimplemented mock method"); } public float getFloat(int columnIndex) { throw new UnsupportedOperationException("unimplemented mock method"); } public double getDouble(int columnIndex) { throw new UnsupportedOperationException("unimplemented mock method"); } public byte[] getBlob(int columnIndex) { throw new UnsupportedOperationException("unimplemented mock method"); } public String getString(int columnIndex) { throw new UnsupportedOperationException("unimplemented mock method"); } public Bundle getExtras() { throw new UnsupportedOperationException("unimplemented mock method"); } public int getPosition() { throw new UnsupportedOperationException("unimplemented mock method"); } public boolean isAfterLast() { throw new UnsupportedOperationException("unimplemented mock method"); } public boolean isBeforeFirst() { throw new UnsupportedOperationException("unimplemented mock method"); } public boolean isFirst() { throw new UnsupportedOperationException("unimplemented mock method"); } public boolean isLast() { throw new UnsupportedOperationException("unimplemented mock method"); } public boolean move(int offset) { throw new UnsupportedOperationException("unimplemented mock method"); } public boolean moveToFirst() { throw new UnsupportedOperationException("unimplemented mock method"); } public boolean moveToLast() { throw new UnsupportedOperationException("unimplemented mock method"); } public boolean moveToNext() { throw new UnsupportedOperationException("unimplemented mock method"); } public boolean moveToPrevious() { throw new UnsupportedOperationException("unimplemented mock method"); } public boolean moveToPosition(int position) { throw new UnsupportedOperationException("unimplemented mock method"); } public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) { throw new UnsupportedOperationException("unimplemented mock method"); } public void deactivate() { throw new UnsupportedOperationException("unimplemented mock method"); } public void close() { throw new UnsupportedOperationException("unimplemented mock method"); } public boolean isClosed() { throw new UnsupportedOperationException("unimplemented mock method"); } public boolean requery() { throw new UnsupportedOperationException("unimplemented mock method"); } public void registerContentObserver(ContentObserver observer) { throw new UnsupportedOperationException("unimplemented mock method"); } public void registerDataSetObserver(DataSetObserver observer) { throw new UnsupportedOperationException("unimplemented mock method"); } public Bundle respond(Bundle extras) { throw new UnsupportedOperationException("unimplemented mock method"); } public boolean getWantsAllOnMoveCalls() { throw new UnsupportedOperationException("unimplemented mock method"); } @SuppressWarnings("deprecation") public boolean commitUpdates() { throw new UnsupportedOperationException("unimplemented mock method"); } @SuppressWarnings("deprecation") public boolean commitUpdates(Map<? extends Long, ? extends Map<String, Object>> values) { throw new UnsupportedOperationException("unimplemented mock method"); } @SuppressWarnings("deprecation") public boolean hasUpdates() { throw new UnsupportedOperationException("unimplemented mock method"); } @SuppressWarnings("deprecation") public void setNotificationUri(ContentResolver cr, Uri uri) { throw new UnsupportedOperationException("unimplemented mock method"); } @SuppressWarnings("deprecation") public boolean supportsUpdates() { throw new UnsupportedOperationException("unimplemented mock method"); } @SuppressWarnings("deprecation") public boolean deleteRow() { throw new UnsupportedOperationException("unimplemented mock method"); } @SuppressWarnings("deprecation") public void unregisterContentObserver(ContentObserver observer) { throw new UnsupportedOperationException("unimplemented mock method"); } @SuppressWarnings("deprecation") public void unregisterDataSetObserver(DataSetObserver observer) { throw new UnsupportedOperationException("unimplemented mock method"); } @SuppressWarnings("deprecation") public boolean updateBlob(int columnIndex, byte[] value) { throw new UnsupportedOperationException("unimplemented mock method"); } @SuppressWarnings("deprecation") public boolean updateDouble(int columnIndex, double value) { throw new UnsupportedOperationException("unimplemented mock method"); } @SuppressWarnings("deprecation") public boolean updateFloat(int columnIndex, float value) { throw new UnsupportedOperationException("unimplemented mock method"); } @SuppressWarnings("deprecation") public boolean updateInt(int columnIndex, int value) { throw new UnsupportedOperationException("unimplemented mock method"); } @SuppressWarnings("deprecation") public boolean updateLong(int columnIndex, long value) { throw new UnsupportedOperationException("unimplemented mock method"); } @SuppressWarnings("deprecation") public boolean updateShort(int columnIndex, short value) { throw new UnsupportedOperationException("unimplemented mock method"); } @SuppressWarnings("deprecation") public boolean updateString(int columnIndex, String value) { throw new UnsupportedOperationException("unimplemented mock method"); } @SuppressWarnings("deprecation") public boolean updateToNull(int columnIndex) { throw new UnsupportedOperationException("unimplemented mock method"); } @SuppressWarnings("deprecation") public void abortUpdates() { throw new UnsupportedOperationException("unimplemented mock method"); } } No newline at end of file tests/AndroidTests/src/com/android/unit_tests/vcard/PropertyNode.java +9 −164 Original line number Original line Diff line number Diff line Loading @@ -18,15 +18,11 @@ package com.android.unit_tests.vcard; import android.content.ContentValues; import android.content.ContentValues; import android.pim.vcard.ContactStruct; import android.pim.vcard.ContactStruct; import org.apache.commons.codec.binary.Base64; import java.util.ArrayList; import java.util.ArrayList; import java.util.Arrays; import java.util.Arrays; import java.util.HashSet; import java.util.HashSet; import java.util.List; import java.util.List; import java.util.Set; import java.util.Set; import java.util.Map.Entry; import java.util.regex.Pattern; /** /** * Previously used in main vCard handling code but now exists only for testing. * Previously used in main vCard handling code but now exists only for testing. Loading Loading @@ -106,6 +102,15 @@ public class PropertyNode { } } } } @Override public int hashCode() { // vCard may contain more than one same line in one entry, while HashSet or any other // library which utilize hashCode() does not honor that, so intentionally throw an // Exception. throw new UnsupportedOperationException( "PropertyNode does not provide hashCode() implementation intentionally."); } @Override @Override public boolean equals(Object obj) { public boolean equals(Object obj) { if (!(obj instanceof PropertyNode)) { if (!(obj instanceof PropertyNode)) { Loading Loading @@ -165,164 +170,4 @@ public class PropertyNode { builder.append(propValue); builder.append(propValue); return builder.toString(); return builder.toString(); } } /** * Encode this object into a string which can be decoded. */ public String encode() { // PropertyNode#toString() is for reading, not for parsing in the future. // We construct appropriate String here. StringBuilder builder = new StringBuilder(); if (propName.length() > 0) { builder.append("propName:["); builder.append(propName); builder.append("],"); } int size = propGroupSet.size(); if (size > 0) { Set<String> set = propGroupSet; builder.append("propGroup:["); int i = 0; for (String group : set) { // We do not need to double quote groups. // group = 1*(ALPHA / DIGIT / "-") builder.append(group); if (i < size - 1) { builder.append(","); } i++; } builder.append("],"); } if (paramMap.size() > 0 || paramMap_TYPE.size() > 0) { ContentValues values = paramMap; builder.append("paramMap:["); size = paramMap.size(); int i = 0; for (Entry<String, Object> entry : values.valueSet()) { // Assuming param-key does not contain NON-ASCII nor symbols. // // According to vCard 3.0: // param-name = iana-token / x-name builder.append(entry.getKey()); // param-value may contain any value including NON-ASCIIs. // We use the following replacing rule. // \ -> \\ // , -> \, // In String#replaceAll(), "\\\\" means a single backslash. builder.append("="); builder.append(entry.getValue().toString() .replaceAll("\\\\", "\\\\\\\\") .replaceAll(",", "\\\\,")); if (i < size -1) { builder.append(","); } i++; } Set<String> set = paramMap_TYPE; size = paramMap_TYPE.size(); if (i > 0 && size > 0) { builder.append(","); } i = 0; for (String type : set) { builder.append("TYPE="); builder.append(type .replaceAll("\\\\", "\\\\\\\\") .replaceAll(",", "\\\\,")); if (i < size - 1) { builder.append(","); } i++; } builder.append("],"); } size = propValue_vector.size(); if (size > 0) { builder.append("propValue:["); List<String> list = propValue_vector; for (int i = 0; i < size; i++) { builder.append(list.get(i) .replaceAll("\\\\", "\\\\\\\\") .replaceAll(",", "\\\\,")); if (i < size -1) { builder.append(","); } } builder.append("],"); } return builder.toString(); } public static PropertyNode decode(String encodedString) { PropertyNode propertyNode = new PropertyNode(); String trimed = encodedString.trim(); if (trimed.length() == 0) { return propertyNode; } String[] elems = trimed.split("],"); for (String elem : elems) { int index = elem.indexOf('['); String name = elem.substring(0, index - 1); Pattern pattern = Pattern.compile("(?<!\\\\),"); String[] values = pattern.split(elem.substring(index + 1), -1); if (name.equals("propName")) { propertyNode.propName = values[0]; } else if (name.equals("propGroupSet")) { for (String value : values) { propertyNode.propGroupSet.add(value); } } else if (name.equals("paramMap")) { ContentValues paramMap = propertyNode.paramMap; Set<String> paramMap_TYPE = propertyNode.paramMap_TYPE; for (String value : values) { String[] tmp = value.split("=", 2); String mapKey = tmp[0]; // \, -> , // \\ -> \ // In String#replaceAll(), "\\\\" means a single backslash. String mapValue = tmp[1].replaceAll("\\\\,", ",").replaceAll("\\\\\\\\", "\\\\"); if (mapKey.equalsIgnoreCase("TYPE")) { paramMap_TYPE.add(mapValue); } else { paramMap.put(mapKey, mapValue); } } } else if (name.equals("propValue")) { StringBuilder builder = new StringBuilder(); List<String> list = propertyNode.propValue_vector; int length = values.length; for (int i = 0; i < length; i++) { String normValue = values[i] .replaceAll("\\\\,", ",") .replaceAll("\\\\\\\\", "\\\\"); list.add(normValue); builder.append(normValue); if (i < length - 1) { builder.append(";"); } } propertyNode.propValue = builder.toString(); } } // At this time, QUOTED-PRINTABLE is already decoded to Java String. // We just need to decode BASE64 String to binary. String encoding = propertyNode.paramMap.getAsString("ENCODING"); if (encoding != null && (encoding.equalsIgnoreCase("BASE64") || encoding.equalsIgnoreCase("B"))) { propertyNode.propValue_bytes = Base64.decodeBase64(propertyNode.propValue_vector.get(0).getBytes()); } return propertyNode; } } } Loading
core/java/android/pim/vcard/VCardComposer.java +46 −23 Original line number Original line Diff line number Diff line Loading @@ -111,6 +111,10 @@ public class VCardComposer { public static final String FAILURE_REASON_NOT_INITIALIZED = public static final String FAILURE_REASON_NOT_INITIALIZED = "The vCard composer object is not correctly initialized"; "The vCard composer object is not correctly initialized"; /** Should be visible only from developers... (no need to translate, hopefully) */ public static final String FAILURE_REASON_UNSUPPORTED_URI = "The Uri vCard composer received is not supported by the composer."; public static final String NO_ERROR = "No error"; public static final String NO_ERROR = "No error"; public static final String VCARD_TYPE_STRING_DOCOMO = "docomo"; public static final String VCARD_TYPE_STRING_DOCOMO = "docomo"; Loading Loading @@ -138,6 +142,15 @@ public class VCardComposer { private static final String SHIFT_JIS = "SHIFT_JIS"; private static final String SHIFT_JIS = "SHIFT_JIS"; /** * Special URI for testing. */ public static final String VCARD_TEST_AUTHORITY = "com.android.unit_tests.vcard"; public static final Uri VCARD_TEST_AUTHORITY_URI = Uri.parse("content://" + VCARD_TEST_AUTHORITY); public static final Uri CONTACTS_TEST_CONTENT_URI = Uri.withAppendedPath(VCARD_TEST_AUTHORITY_URI, "contacts"); private static final Uri sDataRequestUri; private static final Uri sDataRequestUri; private static final Map<Integer, String> sImMap; private static final Map<Integer, String> sImMap; Loading Loading @@ -286,7 +299,7 @@ public class VCardComposer { private String mErrorReason = NO_ERROR; private String mErrorReason = NO_ERROR; private boolean mIsCallLogComposer = false; private boolean mIsCallLogComposer; private static final String[] sContactsProjection = new String[] { private static final String[] sContactsProjection = new String[] { Contacts._ID, Contacts._ID, Loading @@ -307,30 +320,24 @@ public class VCardComposer { private static final String FLAG_TIMEZONE_UTC = "Z"; private static final String FLAG_TIMEZONE_UTC = "Z"; public VCardComposer(Context context) { public VCardComposer(Context context) { this(context, VCardConfig.VCARD_TYPE_DEFAULT, true, false); this(context, VCardConfig.VCARD_TYPE_DEFAULT, true); } } public VCardComposer(Context context, String vcardTypeStr, public VCardComposer(Context context, int vcardType) { boolean careHandlerErrors) { this(context, vcardType, true); this(context, VCardConfig.getVCardTypeFromString(vcardTypeStr), careHandlerErrors, false); } } public VCardComposer(Context context, int vcardType, boolean careHandlerErrors) { public VCardComposer(Context context, String vcardTypeStr, boolean careHandlerErrors) { this(context, vcardType, careHandlerErrors, false); this(context, VCardConfig.getVCardTypeFromString(vcardTypeStr), careHandlerErrors); } } /** /** * Construct for supporting call log entry vCard composing. * Construct for supporting call log entry vCard composing. * * @param isCallLogComposer true if this composer is for creating Call Log vCard. */ */ public VCardComposer(Context context, int vcardType, boolean careHandlerErrors, public VCardComposer(Context context, int vcardType, boolean careHandlerErrors) { boolean isCallLogComposer) { mContext = context; mContext = context; mVCardType = vcardType; mVCardType = vcardType; mCareHandlerErrors = careHandlerErrors; mCareHandlerErrors = careHandlerErrors; mIsCallLogComposer = isCallLogComposer; mContentResolver = context.getContentResolver(); mContentResolver = context.getContentResolver(); mIsV30 = VCardConfig.isV30(vcardType); mIsV30 = VCardConfig.isV30(vcardType); Loading Loading @@ -370,15 +377,26 @@ public class VCardComposer { mHandlerList.add(handler); mHandlerList.add(handler); } } /** * @return Returns true when initialization is successful and all the other * methods are available. Returns false otherwise. */ public boolean init() { public boolean init() { return init(null, null); return init(null, null); } } public boolean init(final String selection, final String[] selectionArgs) { return init(Contacts.CONTENT_URI, selection, selectionArgs, null); } /** /** * @return Returns true when initialization is successful and all the other * Note that this is unstable interface, may be deleted in the future. * methods are available. Returns false otherwise. */ */ public boolean init(final String selection, final String[] selectionArgs) { public boolean init(final Uri contentUri, final String selection, final String[] selectionArgs, final String sortOrder) { if (contentUri == null) { return false; } if (mCareHandlerErrors) { if (mCareHandlerErrors) { List<OneEntryHandler> finishedList = new ArrayList<OneEntryHandler>( List<OneEntryHandler> finishedList = new ArrayList<OneEntryHandler>( mHandlerList.size()); mHandlerList.size()); Loading @@ -397,13 +415,19 @@ public class VCardComposer { } } } } if (mIsCallLogComposer) { final String[] projection; mCursor = mContentResolver.query(CallLog.Calls.CONTENT_URI, sCallLogProjection, if (CallLog.Calls.CONTENT_URI.equals(contentUri)) { selection, selectionArgs, null); projection = sCallLogProjection; mIsCallLogComposer = true; } else if (Contacts.CONTENT_URI.equals(contentUri) || CONTACTS_TEST_CONTENT_URI.equals(contentUri)) { projection = sContactsProjection; } else { } else { mCursor = mContentResolver.query(Contacts.CONTENT_URI, sContactsProjection, mErrorReason = FAILURE_REASON_UNSUPPORTED_URI; selection, selectionArgs, null); return false; } } mCursor = mContentResolver.query( contentUri, projection, selection, selectionArgs, sortOrder); if (mCursor == null) { if (mCursor == null) { mErrorReason = FAILURE_REASON_FAILED_TO_GET_DATABASE_INFO; mErrorReason = FAILURE_REASON_FAILED_TO_GET_DATABASE_INFO; Loading Loading @@ -496,8 +520,7 @@ public class VCardComposer { dataExists = entityIterator.hasNext(); dataExists = entityIterator.hasNext(); while (entityIterator.hasNext()) { while (entityIterator.hasNext()) { Entity entity = entityIterator.next(); Entity entity = entityIterator.next(); for (NamedContentValues namedContentValues : entity for (NamedContentValues namedContentValues : entity.getSubValues()) { .getSubValues()) { ContentValues contentValues = namedContentValues.values; ContentValues contentValues = namedContentValues.values; String key = contentValues.getAsString(Data.MIMETYPE); String key = contentValues.getAsString(Data.MIMETYPE); if (key != null) { if (key != null) { Loading
core/java/android/pim/vcard/VCardParser_V21.java +12 −1 Original line number Original line Diff line number Diff line Loading @@ -525,10 +525,18 @@ public class VCardParser_V21 extends VCardParser { throw new VCardException("Unknown type \"" + paramName + "\""); throw new VCardException("Unknown type \"" + paramName + "\""); } } } else { } else { handleType(strArray[0]); handleParamWithoutName(strArray[0]); } } } } /** * vCard 3.0 parser may throw VCardException. */ @SuppressWarnings("unused") protected void handleParamWithoutName(final String paramValue) throws VCardException { handleType(paramValue); } /** /** * ptypeval = knowntype / "X-" word * ptypeval = knowntype / "X-" word */ */ Loading Loading @@ -832,6 +840,9 @@ public class VCardParser_V21 extends VCardParser { @Override @Override public boolean parse(InputStream is, String charset, VCardBuilder builder) public boolean parse(InputStream is, String charset, VCardBuilder builder) throws IOException, VCardException { throws IOException, VCardException { if (charset == null) { charset = VCardConfig.DEFAULT_CHARSET; } final InputStreamReader tmpReader = new InputStreamReader(is, charset); final InputStreamReader tmpReader = new InputStreamReader(is, charset); if (VCardConfig.showPerformanceLog()) { if (VCardConfig.showPerformanceLog()) { mReader = new CustomBufferedReader(tmpReader); mReader = new CustomBufferedReader(tmpReader); Loading
core/java/android/pim/vcard/VCardParser_V30.java +34 −2 Original line number Original line Diff line number Diff line Loading @@ -49,6 +49,29 @@ public class VCardParser_V30 extends VCardParser_V21 { private boolean mEmittedAgentWarning = false; private boolean mEmittedAgentWarning = false; /** * True when the caller wants the parser to be strict about the input. * Currently this is only for testing. */ private final boolean mStrictParsing; public VCardParser_V30() { super(); mStrictParsing = false; } /** * @param strictParsing when true, this object throws VCardException when the vcard is not * valid from the view of vCard 3.0 specification (defined in RFC 2426). Note that this class * is not fully yet for being used with this flag and may not notice invalid line(s). * * @hide currently only for testing! */ public VCardParser_V30(boolean strictParsing) { super(); mStrictParsing = strictParsing; } @Override @Override protected int getVersion() { protected int getVersion() { return VCardConfig.FLAG_V30; return VCardConfig.FLAG_V30; Loading Loading @@ -205,6 +228,15 @@ public class VCardParser_V30 extends VCardParser_V21 { super.handleAnyParam(paramName, paramValue); super.handleAnyParam(paramName, paramValue); } } @Override protected void handleParamWithoutName(final String paramValue) throws VCardException { if (mStrictParsing) { throw new VCardException("Parameter without name is not acceptable in vCard 3.0"); } else { super.handleParamWithoutName(paramValue); } } /** /** * vCard 3.0 defines * vCard 3.0 defines * * Loading
tests/AndroidTests/src/com/android/unit_tests/vcard/MockCursor.java 0 → 100644 +254 −0 Original line number Original line Diff line number Diff line /* * 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 License for the specific language governing permissions and * limitations under the License. */ package com.android.unit_tests.vcard; import android.content.ContentResolver; import android.database.CharArrayBuffer; import android.database.ContentObserver; import android.database.Cursor; import android.database.DataSetObserver; import android.net.Uri; import android.os.Bundle; import java.util.Map; public class MockCursor implements Cursor { public int getColumnCount() { throw new UnsupportedOperationException("unimplemented mock method"); } public int getColumnIndex(String columnName) { throw new UnsupportedOperationException("unimplemented mock method"); } public int getColumnIndexOrThrow(String columnName) { throw new UnsupportedOperationException("unimplemented mock method"); } public String getColumnName(int columnIndex) { throw new UnsupportedOperationException("unimplemented mock method"); } public String[] getColumnNames() { throw new UnsupportedOperationException("unimplemented mock method"); } public int getCount() { throw new UnsupportedOperationException("unimplemented mock method"); } public boolean isNull(int columnIndex) { throw new UnsupportedOperationException("unimplemented mock method"); } public int getInt(int columnIndex) { throw new UnsupportedOperationException("unimplemented mock method"); } public long getLong(int columnIndex) { throw new UnsupportedOperationException("unimplemented mock method"); } public short getShort(int columnIndex) { throw new UnsupportedOperationException("unimplemented mock method"); } public float getFloat(int columnIndex) { throw new UnsupportedOperationException("unimplemented mock method"); } public double getDouble(int columnIndex) { throw new UnsupportedOperationException("unimplemented mock method"); } public byte[] getBlob(int columnIndex) { throw new UnsupportedOperationException("unimplemented mock method"); } public String getString(int columnIndex) { throw new UnsupportedOperationException("unimplemented mock method"); } public Bundle getExtras() { throw new UnsupportedOperationException("unimplemented mock method"); } public int getPosition() { throw new UnsupportedOperationException("unimplemented mock method"); } public boolean isAfterLast() { throw new UnsupportedOperationException("unimplemented mock method"); } public boolean isBeforeFirst() { throw new UnsupportedOperationException("unimplemented mock method"); } public boolean isFirst() { throw new UnsupportedOperationException("unimplemented mock method"); } public boolean isLast() { throw new UnsupportedOperationException("unimplemented mock method"); } public boolean move(int offset) { throw new UnsupportedOperationException("unimplemented mock method"); } public boolean moveToFirst() { throw new UnsupportedOperationException("unimplemented mock method"); } public boolean moveToLast() { throw new UnsupportedOperationException("unimplemented mock method"); } public boolean moveToNext() { throw new UnsupportedOperationException("unimplemented mock method"); } public boolean moveToPrevious() { throw new UnsupportedOperationException("unimplemented mock method"); } public boolean moveToPosition(int position) { throw new UnsupportedOperationException("unimplemented mock method"); } public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) { throw new UnsupportedOperationException("unimplemented mock method"); } public void deactivate() { throw new UnsupportedOperationException("unimplemented mock method"); } public void close() { throw new UnsupportedOperationException("unimplemented mock method"); } public boolean isClosed() { throw new UnsupportedOperationException("unimplemented mock method"); } public boolean requery() { throw new UnsupportedOperationException("unimplemented mock method"); } public void registerContentObserver(ContentObserver observer) { throw new UnsupportedOperationException("unimplemented mock method"); } public void registerDataSetObserver(DataSetObserver observer) { throw new UnsupportedOperationException("unimplemented mock method"); } public Bundle respond(Bundle extras) { throw new UnsupportedOperationException("unimplemented mock method"); } public boolean getWantsAllOnMoveCalls() { throw new UnsupportedOperationException("unimplemented mock method"); } @SuppressWarnings("deprecation") public boolean commitUpdates() { throw new UnsupportedOperationException("unimplemented mock method"); } @SuppressWarnings("deprecation") public boolean commitUpdates(Map<? extends Long, ? extends Map<String, Object>> values) { throw new UnsupportedOperationException("unimplemented mock method"); } @SuppressWarnings("deprecation") public boolean hasUpdates() { throw new UnsupportedOperationException("unimplemented mock method"); } @SuppressWarnings("deprecation") public void setNotificationUri(ContentResolver cr, Uri uri) { throw new UnsupportedOperationException("unimplemented mock method"); } @SuppressWarnings("deprecation") public boolean supportsUpdates() { throw new UnsupportedOperationException("unimplemented mock method"); } @SuppressWarnings("deprecation") public boolean deleteRow() { throw new UnsupportedOperationException("unimplemented mock method"); } @SuppressWarnings("deprecation") public void unregisterContentObserver(ContentObserver observer) { throw new UnsupportedOperationException("unimplemented mock method"); } @SuppressWarnings("deprecation") public void unregisterDataSetObserver(DataSetObserver observer) { throw new UnsupportedOperationException("unimplemented mock method"); } @SuppressWarnings("deprecation") public boolean updateBlob(int columnIndex, byte[] value) { throw new UnsupportedOperationException("unimplemented mock method"); } @SuppressWarnings("deprecation") public boolean updateDouble(int columnIndex, double value) { throw new UnsupportedOperationException("unimplemented mock method"); } @SuppressWarnings("deprecation") public boolean updateFloat(int columnIndex, float value) { throw new UnsupportedOperationException("unimplemented mock method"); } @SuppressWarnings("deprecation") public boolean updateInt(int columnIndex, int value) { throw new UnsupportedOperationException("unimplemented mock method"); } @SuppressWarnings("deprecation") public boolean updateLong(int columnIndex, long value) { throw new UnsupportedOperationException("unimplemented mock method"); } @SuppressWarnings("deprecation") public boolean updateShort(int columnIndex, short value) { throw new UnsupportedOperationException("unimplemented mock method"); } @SuppressWarnings("deprecation") public boolean updateString(int columnIndex, String value) { throw new UnsupportedOperationException("unimplemented mock method"); } @SuppressWarnings("deprecation") public boolean updateToNull(int columnIndex) { throw new UnsupportedOperationException("unimplemented mock method"); } @SuppressWarnings("deprecation") public void abortUpdates() { throw new UnsupportedOperationException("unimplemented mock method"); } } No newline at end of file
tests/AndroidTests/src/com/android/unit_tests/vcard/PropertyNode.java +9 −164 Original line number Original line Diff line number Diff line Loading @@ -18,15 +18,11 @@ package com.android.unit_tests.vcard; import android.content.ContentValues; import android.content.ContentValues; import android.pim.vcard.ContactStruct; import android.pim.vcard.ContactStruct; import org.apache.commons.codec.binary.Base64; import java.util.ArrayList; import java.util.ArrayList; import java.util.Arrays; import java.util.Arrays; import java.util.HashSet; import java.util.HashSet; import java.util.List; import java.util.List; import java.util.Set; import java.util.Set; import java.util.Map.Entry; import java.util.regex.Pattern; /** /** * Previously used in main vCard handling code but now exists only for testing. * Previously used in main vCard handling code but now exists only for testing. Loading Loading @@ -106,6 +102,15 @@ public class PropertyNode { } } } } @Override public int hashCode() { // vCard may contain more than one same line in one entry, while HashSet or any other // library which utilize hashCode() does not honor that, so intentionally throw an // Exception. throw new UnsupportedOperationException( "PropertyNode does not provide hashCode() implementation intentionally."); } @Override @Override public boolean equals(Object obj) { public boolean equals(Object obj) { if (!(obj instanceof PropertyNode)) { if (!(obj instanceof PropertyNode)) { Loading Loading @@ -165,164 +170,4 @@ public class PropertyNode { builder.append(propValue); builder.append(propValue); return builder.toString(); return builder.toString(); } } /** * Encode this object into a string which can be decoded. */ public String encode() { // PropertyNode#toString() is for reading, not for parsing in the future. // We construct appropriate String here. StringBuilder builder = new StringBuilder(); if (propName.length() > 0) { builder.append("propName:["); builder.append(propName); builder.append("],"); } int size = propGroupSet.size(); if (size > 0) { Set<String> set = propGroupSet; builder.append("propGroup:["); int i = 0; for (String group : set) { // We do not need to double quote groups. // group = 1*(ALPHA / DIGIT / "-") builder.append(group); if (i < size - 1) { builder.append(","); } i++; } builder.append("],"); } if (paramMap.size() > 0 || paramMap_TYPE.size() > 0) { ContentValues values = paramMap; builder.append("paramMap:["); size = paramMap.size(); int i = 0; for (Entry<String, Object> entry : values.valueSet()) { // Assuming param-key does not contain NON-ASCII nor symbols. // // According to vCard 3.0: // param-name = iana-token / x-name builder.append(entry.getKey()); // param-value may contain any value including NON-ASCIIs. // We use the following replacing rule. // \ -> \\ // , -> \, // In String#replaceAll(), "\\\\" means a single backslash. builder.append("="); builder.append(entry.getValue().toString() .replaceAll("\\\\", "\\\\\\\\") .replaceAll(",", "\\\\,")); if (i < size -1) { builder.append(","); } i++; } Set<String> set = paramMap_TYPE; size = paramMap_TYPE.size(); if (i > 0 && size > 0) { builder.append(","); } i = 0; for (String type : set) { builder.append("TYPE="); builder.append(type .replaceAll("\\\\", "\\\\\\\\") .replaceAll(",", "\\\\,")); if (i < size - 1) { builder.append(","); } i++; } builder.append("],"); } size = propValue_vector.size(); if (size > 0) { builder.append("propValue:["); List<String> list = propValue_vector; for (int i = 0; i < size; i++) { builder.append(list.get(i) .replaceAll("\\\\", "\\\\\\\\") .replaceAll(",", "\\\\,")); if (i < size -1) { builder.append(","); } } builder.append("],"); } return builder.toString(); } public static PropertyNode decode(String encodedString) { PropertyNode propertyNode = new PropertyNode(); String trimed = encodedString.trim(); if (trimed.length() == 0) { return propertyNode; } String[] elems = trimed.split("],"); for (String elem : elems) { int index = elem.indexOf('['); String name = elem.substring(0, index - 1); Pattern pattern = Pattern.compile("(?<!\\\\),"); String[] values = pattern.split(elem.substring(index + 1), -1); if (name.equals("propName")) { propertyNode.propName = values[0]; } else if (name.equals("propGroupSet")) { for (String value : values) { propertyNode.propGroupSet.add(value); } } else if (name.equals("paramMap")) { ContentValues paramMap = propertyNode.paramMap; Set<String> paramMap_TYPE = propertyNode.paramMap_TYPE; for (String value : values) { String[] tmp = value.split("=", 2); String mapKey = tmp[0]; // \, -> , // \\ -> \ // In String#replaceAll(), "\\\\" means a single backslash. String mapValue = tmp[1].replaceAll("\\\\,", ",").replaceAll("\\\\\\\\", "\\\\"); if (mapKey.equalsIgnoreCase("TYPE")) { paramMap_TYPE.add(mapValue); } else { paramMap.put(mapKey, mapValue); } } } else if (name.equals("propValue")) { StringBuilder builder = new StringBuilder(); List<String> list = propertyNode.propValue_vector; int length = values.length; for (int i = 0; i < length; i++) { String normValue = values[i] .replaceAll("\\\\,", ",") .replaceAll("\\\\\\\\", "\\\\"); list.add(normValue); builder.append(normValue); if (i < length - 1) { builder.append(";"); } } propertyNode.propValue = builder.toString(); } } // At this time, QUOTED-PRINTABLE is already decoded to Java String. // We just need to decode BASE64 String to binary. String encoding = propertyNode.paramMap.getAsString("ENCODING"); if (encoding != null && (encoding.equalsIgnoreCase("BASE64") || encoding.equalsIgnoreCase("B"))) { propertyNode.propValue_bytes = Base64.decodeBase64(propertyNode.propValue_vector.get(0).getBytes()); } return propertyNode; } } }