M7350v1_en_gpl

This commit is contained in:
T
2024-09-09 08:52:07 +00:00
commit f9cc65cfda
65988 changed files with 26357421 additions and 0 deletions

View File

@ -0,0 +1,379 @@
/*
* 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 android.pim.vcard;
import java.util.HashMap;
import java.util.Map;
/**
* TextUtils especially for Japanese.
*/
/* package */ class JapaneseUtils {
static private final Map<Character, String> sHalfWidthMap =
new HashMap<Character, String>();
static {
sHalfWidthMap.put('\u3001', "\uFF64");
sHalfWidthMap.put('\u3002', "\uFF61");
sHalfWidthMap.put('\u300C', "\uFF62");
sHalfWidthMap.put('\u300D', "\uFF63");
sHalfWidthMap.put('\u301C', "~");
sHalfWidthMap.put('\u3041', "\uFF67");
sHalfWidthMap.put('\u3042', "\uFF71");
sHalfWidthMap.put('\u3043', "\uFF68");
sHalfWidthMap.put('\u3044', "\uFF72");
sHalfWidthMap.put('\u3045', "\uFF69");
sHalfWidthMap.put('\u3046', "\uFF73");
sHalfWidthMap.put('\u3047', "\uFF6A");
sHalfWidthMap.put('\u3048', "\uFF74");
sHalfWidthMap.put('\u3049', "\uFF6B");
sHalfWidthMap.put('\u304A', "\uFF75");
sHalfWidthMap.put('\u304B', "\uFF76");
sHalfWidthMap.put('\u304C', "\uFF76\uFF9E");
sHalfWidthMap.put('\u304D', "\uFF77");
sHalfWidthMap.put('\u304E', "\uFF77\uFF9E");
sHalfWidthMap.put('\u304F', "\uFF78");
sHalfWidthMap.put('\u3050', "\uFF78\uFF9E");
sHalfWidthMap.put('\u3051', "\uFF79");
sHalfWidthMap.put('\u3052', "\uFF79\uFF9E");
sHalfWidthMap.put('\u3053', "\uFF7A");
sHalfWidthMap.put('\u3054', "\uFF7A\uFF9E");
sHalfWidthMap.put('\u3055', "\uFF7B");
sHalfWidthMap.put('\u3056', "\uFF7B\uFF9E");
sHalfWidthMap.put('\u3057', "\uFF7C");
sHalfWidthMap.put('\u3058', "\uFF7C\uFF9E");
sHalfWidthMap.put('\u3059', "\uFF7D");
sHalfWidthMap.put('\u305A', "\uFF7D\uFF9E");
sHalfWidthMap.put('\u305B', "\uFF7E");
sHalfWidthMap.put('\u305C', "\uFF7E\uFF9E");
sHalfWidthMap.put('\u305D', "\uFF7F");
sHalfWidthMap.put('\u305E', "\uFF7F\uFF9E");
sHalfWidthMap.put('\u305F', "\uFF80");
sHalfWidthMap.put('\u3060', "\uFF80\uFF9E");
sHalfWidthMap.put('\u3061', "\uFF81");
sHalfWidthMap.put('\u3062', "\uFF81\uFF9E");
sHalfWidthMap.put('\u3063', "\uFF6F");
sHalfWidthMap.put('\u3064', "\uFF82");
sHalfWidthMap.put('\u3065', "\uFF82\uFF9E");
sHalfWidthMap.put('\u3066', "\uFF83");
sHalfWidthMap.put('\u3067', "\uFF83\uFF9E");
sHalfWidthMap.put('\u3068', "\uFF84");
sHalfWidthMap.put('\u3069', "\uFF84\uFF9E");
sHalfWidthMap.put('\u306A', "\uFF85");
sHalfWidthMap.put('\u306B', "\uFF86");
sHalfWidthMap.put('\u306C', "\uFF87");
sHalfWidthMap.put('\u306D', "\uFF88");
sHalfWidthMap.put('\u306E', "\uFF89");
sHalfWidthMap.put('\u306F', "\uFF8A");
sHalfWidthMap.put('\u3070', "\uFF8A\uFF9E");
sHalfWidthMap.put('\u3071', "\uFF8A\uFF9F");
sHalfWidthMap.put('\u3072', "\uFF8B");
sHalfWidthMap.put('\u3073', "\uFF8B\uFF9E");
sHalfWidthMap.put('\u3074', "\uFF8B\uFF9F");
sHalfWidthMap.put('\u3075', "\uFF8C");
sHalfWidthMap.put('\u3076', "\uFF8C\uFF9E");
sHalfWidthMap.put('\u3077', "\uFF8C\uFF9F");
sHalfWidthMap.put('\u3078', "\uFF8D");
sHalfWidthMap.put('\u3079', "\uFF8D\uFF9E");
sHalfWidthMap.put('\u307A', "\uFF8D\uFF9F");
sHalfWidthMap.put('\u307B', "\uFF8E");
sHalfWidthMap.put('\u307C', "\uFF8E\uFF9E");
sHalfWidthMap.put('\u307D', "\uFF8E\uFF9F");
sHalfWidthMap.put('\u307E', "\uFF8F");
sHalfWidthMap.put('\u307F', "\uFF90");
sHalfWidthMap.put('\u3080', "\uFF91");
sHalfWidthMap.put('\u3081', "\uFF92");
sHalfWidthMap.put('\u3082', "\uFF93");
sHalfWidthMap.put('\u3083', "\uFF6C");
sHalfWidthMap.put('\u3084', "\uFF94");
sHalfWidthMap.put('\u3085', "\uFF6D");
sHalfWidthMap.put('\u3086', "\uFF95");
sHalfWidthMap.put('\u3087', "\uFF6E");
sHalfWidthMap.put('\u3088', "\uFF96");
sHalfWidthMap.put('\u3089', "\uFF97");
sHalfWidthMap.put('\u308A', "\uFF98");
sHalfWidthMap.put('\u308B', "\uFF99");
sHalfWidthMap.put('\u308C', "\uFF9A");
sHalfWidthMap.put('\u308D', "\uFF9B");
sHalfWidthMap.put('\u308E', "\uFF9C");
sHalfWidthMap.put('\u308F', "\uFF9C");
sHalfWidthMap.put('\u3090', "\uFF72");
sHalfWidthMap.put('\u3091', "\uFF74");
sHalfWidthMap.put('\u3092', "\uFF66");
sHalfWidthMap.put('\u3093', "\uFF9D");
sHalfWidthMap.put('\u309B', "\uFF9E");
sHalfWidthMap.put('\u309C', "\uFF9F");
sHalfWidthMap.put('\u30A1', "\uFF67");
sHalfWidthMap.put('\u30A2', "\uFF71");
sHalfWidthMap.put('\u30A3', "\uFF68");
sHalfWidthMap.put('\u30A4', "\uFF72");
sHalfWidthMap.put('\u30A5', "\uFF69");
sHalfWidthMap.put('\u30A6', "\uFF73");
sHalfWidthMap.put('\u30A7', "\uFF6A");
sHalfWidthMap.put('\u30A8', "\uFF74");
sHalfWidthMap.put('\u30A9', "\uFF6B");
sHalfWidthMap.put('\u30AA', "\uFF75");
sHalfWidthMap.put('\u30AB', "\uFF76");
sHalfWidthMap.put('\u30AC', "\uFF76\uFF9E");
sHalfWidthMap.put('\u30AD', "\uFF77");
sHalfWidthMap.put('\u30AE', "\uFF77\uFF9E");
sHalfWidthMap.put('\u30AF', "\uFF78");
sHalfWidthMap.put('\u30B0', "\uFF78\uFF9E");
sHalfWidthMap.put('\u30B1', "\uFF79");
sHalfWidthMap.put('\u30B2', "\uFF79\uFF9E");
sHalfWidthMap.put('\u30B3', "\uFF7A");
sHalfWidthMap.put('\u30B4', "\uFF7A\uFF9E");
sHalfWidthMap.put('\u30B5', "\uFF7B");
sHalfWidthMap.put('\u30B6', "\uFF7B\uFF9E");
sHalfWidthMap.put('\u30B7', "\uFF7C");
sHalfWidthMap.put('\u30B8', "\uFF7C\uFF9E");
sHalfWidthMap.put('\u30B9', "\uFF7D");
sHalfWidthMap.put('\u30BA', "\uFF7D\uFF9E");
sHalfWidthMap.put('\u30BB', "\uFF7E");
sHalfWidthMap.put('\u30BC', "\uFF7E\uFF9E");
sHalfWidthMap.put('\u30BD', "\uFF7F");
sHalfWidthMap.put('\u30BE', "\uFF7F\uFF9E");
sHalfWidthMap.put('\u30BF', "\uFF80");
sHalfWidthMap.put('\u30C0', "\uFF80\uFF9E");
sHalfWidthMap.put('\u30C1', "\uFF81");
sHalfWidthMap.put('\u30C2', "\uFF81\uFF9E");
sHalfWidthMap.put('\u30C3', "\uFF6F");
sHalfWidthMap.put('\u30C4', "\uFF82");
sHalfWidthMap.put('\u30C5', "\uFF82\uFF9E");
sHalfWidthMap.put('\u30C6', "\uFF83");
sHalfWidthMap.put('\u30C7', "\uFF83\uFF9E");
sHalfWidthMap.put('\u30C8', "\uFF84");
sHalfWidthMap.put('\u30C9', "\uFF84\uFF9E");
sHalfWidthMap.put('\u30CA', "\uFF85");
sHalfWidthMap.put('\u30CB', "\uFF86");
sHalfWidthMap.put('\u30CC', "\uFF87");
sHalfWidthMap.put('\u30CD', "\uFF88");
sHalfWidthMap.put('\u30CE', "\uFF89");
sHalfWidthMap.put('\u30CF', "\uFF8A");
sHalfWidthMap.put('\u30D0', "\uFF8A\uFF9E");
sHalfWidthMap.put('\u30D1', "\uFF8A\uFF9F");
sHalfWidthMap.put('\u30D2', "\uFF8B");
sHalfWidthMap.put('\u30D3', "\uFF8B\uFF9E");
sHalfWidthMap.put('\u30D4', "\uFF8B\uFF9F");
sHalfWidthMap.put('\u30D5', "\uFF8C");
sHalfWidthMap.put('\u30D6', "\uFF8C\uFF9E");
sHalfWidthMap.put('\u30D7', "\uFF8C\uFF9F");
sHalfWidthMap.put('\u30D8', "\uFF8D");
sHalfWidthMap.put('\u30D9', "\uFF8D\uFF9E");
sHalfWidthMap.put('\u30DA', "\uFF8D\uFF9F");
sHalfWidthMap.put('\u30DB', "\uFF8E");
sHalfWidthMap.put('\u30DC', "\uFF8E\uFF9E");
sHalfWidthMap.put('\u30DD', "\uFF8E\uFF9F");
sHalfWidthMap.put('\u30DE', "\uFF8F");
sHalfWidthMap.put('\u30DF', "\uFF90");
sHalfWidthMap.put('\u30E0', "\uFF91");
sHalfWidthMap.put('\u30E1', "\uFF92");
sHalfWidthMap.put('\u30E2', "\uFF93");
sHalfWidthMap.put('\u30E3', "\uFF6C");
sHalfWidthMap.put('\u30E4', "\uFF94");
sHalfWidthMap.put('\u30E5', "\uFF6D");
sHalfWidthMap.put('\u30E6', "\uFF95");
sHalfWidthMap.put('\u30E7', "\uFF6E");
sHalfWidthMap.put('\u30E8', "\uFF96");
sHalfWidthMap.put('\u30E9', "\uFF97");
sHalfWidthMap.put('\u30EA', "\uFF98");
sHalfWidthMap.put('\u30EB', "\uFF99");
sHalfWidthMap.put('\u30EC', "\uFF9A");
sHalfWidthMap.put('\u30ED', "\uFF9B");
sHalfWidthMap.put('\u30EE', "\uFF9C");
sHalfWidthMap.put('\u30EF', "\uFF9C");
sHalfWidthMap.put('\u30F0', "\uFF72");
sHalfWidthMap.put('\u30F1', "\uFF74");
sHalfWidthMap.put('\u30F2', "\uFF66");
sHalfWidthMap.put('\u30F3', "\uFF9D");
sHalfWidthMap.put('\u30F4', "\uFF73\uFF9E");
sHalfWidthMap.put('\u30F5', "\uFF76");
sHalfWidthMap.put('\u30F6', "\uFF79");
sHalfWidthMap.put('\u30FB', "\uFF65");
sHalfWidthMap.put('\u30FC', "\uFF70");
sHalfWidthMap.put('\uFF01', "!");
sHalfWidthMap.put('\uFF02', "\"");
sHalfWidthMap.put('\uFF03', "#");
sHalfWidthMap.put('\uFF04', "$");
sHalfWidthMap.put('\uFF05', "%");
sHalfWidthMap.put('\uFF06', "&");
sHalfWidthMap.put('\uFF07', "'");
sHalfWidthMap.put('\uFF08', "(");
sHalfWidthMap.put('\uFF09', ")");
sHalfWidthMap.put('\uFF0A', "*");
sHalfWidthMap.put('\uFF0B', "+");
sHalfWidthMap.put('\uFF0C', ",");
sHalfWidthMap.put('\uFF0D', "-");
sHalfWidthMap.put('\uFF0E', ".");
sHalfWidthMap.put('\uFF0F', "/");
sHalfWidthMap.put('\uFF10', "0");
sHalfWidthMap.put('\uFF11', "1");
sHalfWidthMap.put('\uFF12', "2");
sHalfWidthMap.put('\uFF13', "3");
sHalfWidthMap.put('\uFF14', "4");
sHalfWidthMap.put('\uFF15', "5");
sHalfWidthMap.put('\uFF16', "6");
sHalfWidthMap.put('\uFF17', "7");
sHalfWidthMap.put('\uFF18', "8");
sHalfWidthMap.put('\uFF19', "9");
sHalfWidthMap.put('\uFF1A', ":");
sHalfWidthMap.put('\uFF1B', ";");
sHalfWidthMap.put('\uFF1C', "<");
sHalfWidthMap.put('\uFF1D', "=");
sHalfWidthMap.put('\uFF1E', ">");
sHalfWidthMap.put('\uFF1F', "?");
sHalfWidthMap.put('\uFF20', "@");
sHalfWidthMap.put('\uFF21', "A");
sHalfWidthMap.put('\uFF22', "B");
sHalfWidthMap.put('\uFF23', "C");
sHalfWidthMap.put('\uFF24', "D");
sHalfWidthMap.put('\uFF25', "E");
sHalfWidthMap.put('\uFF26', "F");
sHalfWidthMap.put('\uFF27', "G");
sHalfWidthMap.put('\uFF28', "H");
sHalfWidthMap.put('\uFF29', "I");
sHalfWidthMap.put('\uFF2A', "J");
sHalfWidthMap.put('\uFF2B', "K");
sHalfWidthMap.put('\uFF2C', "L");
sHalfWidthMap.put('\uFF2D', "M");
sHalfWidthMap.put('\uFF2E', "N");
sHalfWidthMap.put('\uFF2F', "O");
sHalfWidthMap.put('\uFF30', "P");
sHalfWidthMap.put('\uFF31', "Q");
sHalfWidthMap.put('\uFF32', "R");
sHalfWidthMap.put('\uFF33', "S");
sHalfWidthMap.put('\uFF34', "T");
sHalfWidthMap.put('\uFF35', "U");
sHalfWidthMap.put('\uFF36', "V");
sHalfWidthMap.put('\uFF37', "W");
sHalfWidthMap.put('\uFF38', "X");
sHalfWidthMap.put('\uFF39', "Y");
sHalfWidthMap.put('\uFF3A', "Z");
sHalfWidthMap.put('\uFF3B', "[");
sHalfWidthMap.put('\uFF3C', "\\");
sHalfWidthMap.put('\uFF3D', "]");
sHalfWidthMap.put('\uFF3E', "^");
sHalfWidthMap.put('\uFF3F', "_");
sHalfWidthMap.put('\uFF41', "a");
sHalfWidthMap.put('\uFF42', "b");
sHalfWidthMap.put('\uFF43', "c");
sHalfWidthMap.put('\uFF44', "d");
sHalfWidthMap.put('\uFF45', "e");
sHalfWidthMap.put('\uFF46', "f");
sHalfWidthMap.put('\uFF47', "g");
sHalfWidthMap.put('\uFF48', "h");
sHalfWidthMap.put('\uFF49', "i");
sHalfWidthMap.put('\uFF4A', "j");
sHalfWidthMap.put('\uFF4B', "k");
sHalfWidthMap.put('\uFF4C', "l");
sHalfWidthMap.put('\uFF4D', "m");
sHalfWidthMap.put('\uFF4E', "n");
sHalfWidthMap.put('\uFF4F', "o");
sHalfWidthMap.put('\uFF50', "p");
sHalfWidthMap.put('\uFF51', "q");
sHalfWidthMap.put('\uFF52', "r");
sHalfWidthMap.put('\uFF53', "s");
sHalfWidthMap.put('\uFF54', "t");
sHalfWidthMap.put('\uFF55', "u");
sHalfWidthMap.put('\uFF56', "v");
sHalfWidthMap.put('\uFF57', "w");
sHalfWidthMap.put('\uFF58', "x");
sHalfWidthMap.put('\uFF59', "y");
sHalfWidthMap.put('\uFF5A', "z");
sHalfWidthMap.put('\uFF5B', "{");
sHalfWidthMap.put('\uFF5C', "|");
sHalfWidthMap.put('\uFF5D', "}");
sHalfWidthMap.put('\uFF5E', "~");
sHalfWidthMap.put('\uFF61', "\uFF61");
sHalfWidthMap.put('\uFF62', "\uFF62");
sHalfWidthMap.put('\uFF63', "\uFF63");
sHalfWidthMap.put('\uFF64', "\uFF64");
sHalfWidthMap.put('\uFF65', "\uFF65");
sHalfWidthMap.put('\uFF66', "\uFF66");
sHalfWidthMap.put('\uFF67', "\uFF67");
sHalfWidthMap.put('\uFF68', "\uFF68");
sHalfWidthMap.put('\uFF69', "\uFF69");
sHalfWidthMap.put('\uFF6A', "\uFF6A");
sHalfWidthMap.put('\uFF6B', "\uFF6B");
sHalfWidthMap.put('\uFF6C', "\uFF6C");
sHalfWidthMap.put('\uFF6D', "\uFF6D");
sHalfWidthMap.put('\uFF6E', "\uFF6E");
sHalfWidthMap.put('\uFF6F', "\uFF6F");
sHalfWidthMap.put('\uFF70', "\uFF70");
sHalfWidthMap.put('\uFF71', "\uFF71");
sHalfWidthMap.put('\uFF72', "\uFF72");
sHalfWidthMap.put('\uFF73', "\uFF73");
sHalfWidthMap.put('\uFF74', "\uFF74");
sHalfWidthMap.put('\uFF75', "\uFF75");
sHalfWidthMap.put('\uFF76', "\uFF76");
sHalfWidthMap.put('\uFF77', "\uFF77");
sHalfWidthMap.put('\uFF78', "\uFF78");
sHalfWidthMap.put('\uFF79', "\uFF79");
sHalfWidthMap.put('\uFF7A', "\uFF7A");
sHalfWidthMap.put('\uFF7B', "\uFF7B");
sHalfWidthMap.put('\uFF7C', "\uFF7C");
sHalfWidthMap.put('\uFF7D', "\uFF7D");
sHalfWidthMap.put('\uFF7E', "\uFF7E");
sHalfWidthMap.put('\uFF7F', "\uFF7F");
sHalfWidthMap.put('\uFF80', "\uFF80");
sHalfWidthMap.put('\uFF81', "\uFF81");
sHalfWidthMap.put('\uFF82', "\uFF82");
sHalfWidthMap.put('\uFF83', "\uFF83");
sHalfWidthMap.put('\uFF84', "\uFF84");
sHalfWidthMap.put('\uFF85', "\uFF85");
sHalfWidthMap.put('\uFF86', "\uFF86");
sHalfWidthMap.put('\uFF87', "\uFF87");
sHalfWidthMap.put('\uFF88', "\uFF88");
sHalfWidthMap.put('\uFF89', "\uFF89");
sHalfWidthMap.put('\uFF8A', "\uFF8A");
sHalfWidthMap.put('\uFF8B', "\uFF8B");
sHalfWidthMap.put('\uFF8C', "\uFF8C");
sHalfWidthMap.put('\uFF8D', "\uFF8D");
sHalfWidthMap.put('\uFF8E', "\uFF8E");
sHalfWidthMap.put('\uFF8F', "\uFF8F");
sHalfWidthMap.put('\uFF90', "\uFF90");
sHalfWidthMap.put('\uFF91', "\uFF91");
sHalfWidthMap.put('\uFF92', "\uFF92");
sHalfWidthMap.put('\uFF93', "\uFF93");
sHalfWidthMap.put('\uFF94', "\uFF94");
sHalfWidthMap.put('\uFF95', "\uFF95");
sHalfWidthMap.put('\uFF96', "\uFF96");
sHalfWidthMap.put('\uFF97', "\uFF97");
sHalfWidthMap.put('\uFF98', "\uFF98");
sHalfWidthMap.put('\uFF99', "\uFF99");
sHalfWidthMap.put('\uFF9A', "\uFF9A");
sHalfWidthMap.put('\uFF9B', "\uFF9B");
sHalfWidthMap.put('\uFF9C', "\uFF9C");
sHalfWidthMap.put('\uFF9D', "\uFF9D");
sHalfWidthMap.put('\uFF9E', "\uFF9E");
sHalfWidthMap.put('\uFF9F', "\uFF9F");
sHalfWidthMap.put('\uFFE5', "\u005C\u005C");
}
/**
* Returns half-width version of that character if possible. Returns null if not possible
* @param ch input character
* @return CharSequence object if the mapping for ch exists. Return null otherwise.
*/
public static String tryGetHalfWidthText(final char ch) {
if (sHalfWidthMap.containsKey(ch)) {
return sHalfWidthMap.get(ch);
} else {
return null;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,678 @@
/*
* 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 android.pim.vcard;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Entity;
import android.content.Entity.NamedContentValues;
import android.content.EntityIterator;
import android.database.Cursor;
import android.database.sqlite.SQLiteException;
import android.net.Uri;
import android.pim.vcard.exception.VCardException;
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.Event;
import android.provider.ContactsContract.CommonDataKinds.Im;
import android.provider.ContactsContract.CommonDataKinds.Nickname;
import android.provider.ContactsContract.CommonDataKinds.Note;
import android.provider.ContactsContract.CommonDataKinds.Organization;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.Photo;
import android.provider.ContactsContract.CommonDataKinds.Relation;
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
import android.provider.ContactsContract.CommonDataKinds.Website;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.RawContacts;
import android.provider.ContactsContract.RawContactsEntity;
import android.text.TextUtils;
import android.util.CharsetUtils;
import android.util.Log;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.UnsupportedCharsetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* <p>
* The class for composing vCard from Contacts information.
* </p>
* <p>
* Usually, this class should be used like this.
* </p>
* <pre class="prettyprint">VCardComposer composer = null;
* try {
* composer = new VCardComposer(context);
* composer.addHandler(
* composer.new HandlerForOutputStream(outputStream));
* if (!composer.init()) {
* // Do something handling the situation.
* return;
* }
* while (!composer.isAfterLast()) {
* if (mCanceled) {
* // Assume a user may cancel this operation during the export.
* return;
* }
* if (!composer.createOneEntry()) {
* // Do something handling the error situation.
* return;
* }
* }
* } finally {
* if (composer != null) {
* composer.terminate();
* }
* }</pre>
* <p>
* Users have to manually take care of memory efficiency. Even one vCard may contain
* image of non-trivial size for mobile devices.
* </p>
* <p>
* {@link VCardBuilder} is used to build each vCard.
* </p>
*/
public class VCardComposer {
private static final String LOG_TAG = "VCardComposer";
public static final String FAILURE_REASON_FAILED_TO_GET_DATABASE_INFO =
"Failed to get database information";
public static final String FAILURE_REASON_NO_ENTRY =
"There's no exportable in the database";
public static final String FAILURE_REASON_NOT_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 VCARD_TYPE_STRING_DOCOMO = "docomo";
// Strictly speaking, "Shift_JIS" is the most appropriate, but we use upper version here,
// since usual vCard devices for Japanese devices already use it.
private static final String SHIFT_JIS = "SHIFT_JIS";
private static final String UTF_8 = "UTF-8";
/**
* 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 Map<Integer, String> sImMap;
static {
sImMap = new HashMap<Integer, String>();
sImMap.put(Im.PROTOCOL_AIM, VCardConstants.PROPERTY_X_AIM);
sImMap.put(Im.PROTOCOL_MSN, VCardConstants.PROPERTY_X_MSN);
sImMap.put(Im.PROTOCOL_YAHOO, VCardConstants.PROPERTY_X_YAHOO);
sImMap.put(Im.PROTOCOL_ICQ, VCardConstants.PROPERTY_X_ICQ);
sImMap.put(Im.PROTOCOL_JABBER, VCardConstants.PROPERTY_X_JABBER);
sImMap.put(Im.PROTOCOL_SKYPE, VCardConstants.PROPERTY_X_SKYPE_USERNAME);
// We don't add Google talk here since it has to be handled separately.
}
public static interface OneEntryHandler {
public boolean onInit(Context context);
public boolean onEntryCreated(String vcard);
public void onTerminate();
}
/**
* <p>
* An useful handler for emitting vCard String to an OutputStream object one by one.
* </p>
* <p>
* The input OutputStream object is closed() on {@link #onTerminate()}.
* Must not close the stream outside this class.
* </p>
*/
public final class HandlerForOutputStream implements OneEntryHandler {
@SuppressWarnings("hiding")
private static final String LOG_TAG = "VCardComposer.HandlerForOutputStream";
private boolean mOnTerminateIsCalled = false;
private final OutputStream mOutputStream; // mWriter will close this.
private Writer mWriter;
/**
* Input stream will be closed on the detruction of this object.
*/
public HandlerForOutputStream(final OutputStream outputStream) {
mOutputStream = outputStream;
}
public boolean onInit(final Context context) {
try {
mWriter = new BufferedWriter(new OutputStreamWriter(
mOutputStream, mCharset));
} catch (UnsupportedEncodingException e1) {
Log.e(LOG_TAG, "Unsupported charset: " + mCharset);
mErrorReason = "Encoding is not supported (usually this does not happen!): "
+ mCharset;
return false;
}
if (mIsDoCoMo) {
try {
// Create one empty entry.
mWriter.write(createOneEntryInternal("-1", null));
} catch (VCardException e) {
Log.e(LOG_TAG, "VCardException has been thrown during on Init(): " +
e.getMessage());
return false;
} catch (IOException e) {
Log.e(LOG_TAG,
"IOException occurred during exportOneContactData: "
+ e.getMessage());
mErrorReason = "IOException occurred: " + e.getMessage();
return false;
}
}
return true;
}
public boolean onEntryCreated(String vcard) {
try {
mWriter.write(vcard);
} catch (IOException e) {
Log.e(LOG_TAG,
"IOException occurred during exportOneContactData: "
+ e.getMessage());
mErrorReason = "IOException occurred: " + e.getMessage();
return false;
}
return true;
}
public void onTerminate() {
mOnTerminateIsCalled = true;
if (mWriter != null) {
try {
// Flush and sync the data so that a user is able to pull
// the SDCard just after
// the export.
mWriter.flush();
if (mOutputStream != null
&& mOutputStream instanceof FileOutputStream) {
((FileOutputStream) mOutputStream).getFD().sync();
}
} catch (IOException e) {
Log.d(LOG_TAG,
"IOException during closing the output stream: "
+ e.getMessage());
} finally {
closeOutputStream();
}
}
}
public void closeOutputStream() {
try {
mWriter.close();
} catch (IOException e) {
Log.w(LOG_TAG, "IOException is thrown during close(). Ignoring.");
}
}
@Override
public void finalize() {
if (!mOnTerminateIsCalled) {
onTerminate();
}
}
}
private final Context mContext;
private final int mVCardType;
private final boolean mCareHandlerErrors;
private final ContentResolver mContentResolver;
private final boolean mIsDoCoMo;
private Cursor mCursor;
private int mIdColumn;
private final String mCharset;
private boolean mTerminateIsCalled;
private final List<OneEntryHandler> mHandlerList;
private String mErrorReason = NO_ERROR;
private static final String[] sContactsProjection = new String[] {
Contacts._ID,
};
public VCardComposer(Context context) {
this(context, VCardConfig.VCARD_TYPE_DEFAULT, null, true);
}
/**
* The variant which sets charset to null and sets careHandlerErrors to true.
*/
public VCardComposer(Context context, int vcardType) {
this(context, vcardType, null, true);
}
public VCardComposer(Context context, int vcardType, String charset) {
this(context, vcardType, charset, true);
}
/**
* The variant which sets charset to null.
*/
public VCardComposer(final Context context, final int vcardType,
final boolean careHandlerErrors) {
this(context, vcardType, null, careHandlerErrors);
}
/**
* Construct for supporting call log entry vCard composing.
*
* @param context Context to be used during the composition.
* @param vcardType The type of vCard, typically available via {@link VCardConfig}.
* @param charset The charset to be used. Use null when you don't need the charset.
* @param careHandlerErrors If true, This object returns false everytime
* a Handler object given via {{@link #addHandler(OneEntryHandler)} returns false.
* If false, this ignores those errors.
*/
public VCardComposer(final Context context, final int vcardType, String charset,
final boolean careHandlerErrors) {
mContext = context;
mVCardType = vcardType;
mCareHandlerErrors = careHandlerErrors;
mContentResolver = context.getContentResolver();
mIsDoCoMo = VCardConfig.isDoCoMo(vcardType);
mHandlerList = new ArrayList<OneEntryHandler>();
charset = (TextUtils.isEmpty(charset) ? VCardConfig.DEFAULT_EXPORT_CHARSET : charset);
final boolean shouldAppendCharsetParam = !(
VCardConfig.isVersion30(vcardType) && UTF_8.equalsIgnoreCase(charset));
if (mIsDoCoMo || shouldAppendCharsetParam) {
if (SHIFT_JIS.equalsIgnoreCase(charset)) {
if (mIsDoCoMo) {
try {
charset = CharsetUtils.charsetForVendor(SHIFT_JIS, "docomo").name();
} catch (UnsupportedCharsetException e) {
Log.e(LOG_TAG,
"DoCoMo-specific SHIFT_JIS was not found. "
+ "Use SHIFT_JIS as is.");
charset = SHIFT_JIS;
}
} else {
try {
charset = CharsetUtils.charsetForVendor(SHIFT_JIS).name();
} catch (UnsupportedCharsetException e) {
Log.e(LOG_TAG,
"Career-specific SHIFT_JIS was not found. "
+ "Use SHIFT_JIS as is.");
charset = SHIFT_JIS;
}
}
mCharset = charset;
} else {
Log.w(LOG_TAG,
"The charset \"" + charset + "\" is used while "
+ SHIFT_JIS + " is needed to be used.");
if (TextUtils.isEmpty(charset)) {
mCharset = SHIFT_JIS;
} else {
try {
charset = CharsetUtils.charsetForVendor(charset).name();
} catch (UnsupportedCharsetException e) {
Log.i(LOG_TAG,
"Career-specific \"" + charset + "\" was not found (as usual). "
+ "Use it as is.");
}
mCharset = charset;
}
}
} else {
if (TextUtils.isEmpty(charset)) {
mCharset = UTF_8;
} else {
try {
charset = CharsetUtils.charsetForVendor(charset).name();
} catch (UnsupportedCharsetException e) {
Log.i(LOG_TAG,
"Career-specific \"" + charset + "\" was not found (as usual). "
+ "Use it as is.");
}
mCharset = charset;
}
}
Log.d(LOG_TAG, "Use the charset \"" + mCharset + "\"");
}
/**
* Must be called before {@link #init()}.
*/
public void addHandler(OneEntryHandler handler) {
if (handler != null) {
mHandlerList.add(handler);
}
}
/**
* @return Returns true when initialization is successful and all the other
* methods are available. Returns false otherwise.
*/
public boolean init() {
return init(null, null);
}
public boolean init(final String selection, final String[] selectionArgs) {
return init(Contacts.CONTENT_URI, selection, selectionArgs, null);
}
/**
* Note that this is unstable interface, may be deleted in the future.
*/
public boolean init(final Uri contentUri, final String selection,
final String[] selectionArgs, final String sortOrder) {
if (contentUri == null) {
return false;
}
if (mCareHandlerErrors) {
final List<OneEntryHandler> finishedList = new ArrayList<OneEntryHandler>(
mHandlerList.size());
for (OneEntryHandler handler : mHandlerList) {
if (!handler.onInit(mContext)) {
for (OneEntryHandler finished : finishedList) {
finished.onTerminate();
}
return false;
}
}
} else {
// Just ignore the false returned from onInit().
for (OneEntryHandler handler : mHandlerList) {
handler.onInit(mContext);
}
}
final String[] projection;
if (Contacts.CONTENT_URI.equals(contentUri) ||
CONTACTS_TEST_CONTENT_URI.equals(contentUri)) {
projection = sContactsProjection;
} else {
mErrorReason = FAILURE_REASON_UNSUPPORTED_URI;
return false;
}
mCursor = mContentResolver.query(
contentUri, projection, selection, selectionArgs, sortOrder);
if (mCursor == null) {
mErrorReason = FAILURE_REASON_FAILED_TO_GET_DATABASE_INFO;
return false;
}
if (getCount() == 0 || !mCursor.moveToFirst()) {
try {
mCursor.close();
} catch (SQLiteException e) {
Log.e(LOG_TAG, "SQLiteException on Cursor#close(): " + e.getMessage());
} finally {
mCursor = null;
mErrorReason = FAILURE_REASON_NO_ENTRY;
}
return false;
}
mIdColumn = mCursor.getColumnIndex(Contacts._ID);
return true;
}
public boolean createOneEntry() {
return createOneEntry(null);
}
/**
* @param getEntityIteratorMethod For Dependency Injection.
* @hide just for testing.
*/
public boolean createOneEntry(Method getEntityIteratorMethod) {
if (mCursor == null || mCursor.isAfterLast()) {
mErrorReason = FAILURE_REASON_NOT_INITIALIZED;
return false;
}
final String vcard;
try {
if (mIdColumn >= 0) {
vcard = createOneEntryInternal(mCursor.getString(mIdColumn),
getEntityIteratorMethod);
} else {
Log.e(LOG_TAG, "Incorrect mIdColumn: " + mIdColumn);
return true;
}
} catch (VCardException e) {
Log.e(LOG_TAG, "VCardException has been thrown: " + e.getMessage());
return false;
} catch (OutOfMemoryError error) {
// Maybe some data (e.g. photo) is too big to have in memory. But it
// should be rare.
Log.e(LOG_TAG, "OutOfMemoryError occured. Ignore the entry.");
System.gc();
// TODO: should tell users what happened?
return true;
} finally {
mCursor.moveToNext();
}
// This function does not care the OutOfMemoryError on the handler side :-P
if (mCareHandlerErrors) {
List<OneEntryHandler> finishedList = new ArrayList<OneEntryHandler>(
mHandlerList.size());
for (OneEntryHandler handler : mHandlerList) {
if (!handler.onEntryCreated(vcard)) {
return false;
}
}
} else {
for (OneEntryHandler handler : mHandlerList) {
handler.onEntryCreated(vcard);
}
}
return true;
}
private String createOneEntryInternal(final String contactId,
final Method getEntityIteratorMethod) throws VCardException {
final Map<String, List<ContentValues>> contentValuesListMap =
new HashMap<String, List<ContentValues>>();
// The resolver may return the entity iterator with no data. It is possible.
// e.g. If all the data in the contact of the given contact id are not exportable ones,
// they are hidden from the view of this method, though contact id itself exists.
EntityIterator entityIterator = null;
try {
final Uri uri = RawContactsEntity.CONTENT_URI.buildUpon()
// .appendQueryParameter("for_export_only", "1")
.appendQueryParameter(Data.FOR_EXPORT_ONLY, "1")
.build();
final String selection = Data.CONTACT_ID + "=?";
final String[] selectionArgs = new String[] {contactId};
if (getEntityIteratorMethod != null) {
// Please note that this branch is executed by unit tests only
try {
entityIterator = (EntityIterator)getEntityIteratorMethod.invoke(null,
mContentResolver, uri, selection, selectionArgs, null);
} catch (IllegalArgumentException e) {
Log.e(LOG_TAG, "IllegalArgumentException has been thrown: " +
e.getMessage());
} catch (IllegalAccessException e) {
Log.e(LOG_TAG, "IllegalAccessException has been thrown: " +
e.getMessage());
} catch (InvocationTargetException e) {
Log.e(LOG_TAG, "InvocationTargetException has been thrown: ");
StackTraceElement[] stackTraceElements = e.getCause().getStackTrace();
for (StackTraceElement element : stackTraceElements) {
Log.e(LOG_TAG, " at " + element.toString());
}
throw new VCardException("InvocationTargetException has been thrown: " +
e.getCause().getMessage());
}
} else {
entityIterator = RawContacts.newEntityIterator(mContentResolver.query(
uri, null, selection, selectionArgs, null));
}
if (entityIterator == null) {
Log.e(LOG_TAG, "EntityIterator is null");
return "";
}
if (!entityIterator.hasNext()) {
Log.w(LOG_TAG, "Data does not exist. contactId: " + contactId);
return "";
}
while (entityIterator.hasNext()) {
Entity entity = entityIterator.next();
for (NamedContentValues namedContentValues : entity.getSubValues()) {
ContentValues contentValues = namedContentValues.values;
String key = contentValues.getAsString(Data.MIMETYPE);
if (key != null) {
List<ContentValues> contentValuesList =
contentValuesListMap.get(key);
if (contentValuesList == null) {
contentValuesList = new ArrayList<ContentValues>();
contentValuesListMap.put(key, contentValuesList);
}
contentValuesList.add(contentValues);
}
}
}
} finally {
if (entityIterator != null) {
entityIterator.close();
}
}
return buildVCard(contentValuesListMap);
}
/**
* Builds and returns vCard using given map, whose key is CONTENT_ITEM_TYPE defined in
* {ContactsContract}. Developers can override this method to customize the output.
*/
public String buildVCard(final Map<String, List<ContentValues>> contentValuesListMap) {
if (contentValuesListMap == null) {
Log.e(LOG_TAG, "The given map is null. Ignore and return empty String");
return "";
} else {
final VCardBuilder builder = new VCardBuilder(mVCardType, mCharset);
builder.appendNameProperties(contentValuesListMap.get(StructuredName.CONTENT_ITEM_TYPE))
.appendNickNames(contentValuesListMap.get(Nickname.CONTENT_ITEM_TYPE))
.appendPhones(contentValuesListMap.get(Phone.CONTENT_ITEM_TYPE))
.appendEmails(contentValuesListMap.get(Email.CONTENT_ITEM_TYPE))
.appendPostals(contentValuesListMap.get(StructuredPostal.CONTENT_ITEM_TYPE))
.appendOrganizations(contentValuesListMap.get(Organization.CONTENT_ITEM_TYPE))
.appendWebsites(contentValuesListMap.get(Website.CONTENT_ITEM_TYPE));
if ((mVCardType & VCardConfig.FLAG_REFRAIN_IMAGE_EXPORT) == 0) {
builder.appendPhotos(contentValuesListMap.get(Photo.CONTENT_ITEM_TYPE));
}
builder.appendNotes(contentValuesListMap.get(Note.CONTENT_ITEM_TYPE))
.appendEvents(contentValuesListMap.get(Event.CONTENT_ITEM_TYPE))
.appendIms(contentValuesListMap.get(Im.CONTENT_ITEM_TYPE))
.appendRelation(contentValuesListMap.get(Relation.CONTENT_ITEM_TYPE));
return builder.toString();
}
}
public void terminate() {
for (OneEntryHandler handler : mHandlerList) {
handler.onTerminate();
}
if (mCursor != null) {
try {
mCursor.close();
} catch (SQLiteException e) {
Log.e(LOG_TAG, "SQLiteException on Cursor#close(): " + e.getMessage());
}
mCursor = null;
}
mTerminateIsCalled = true;
}
@Override
public void finalize() {
if (!mTerminateIsCalled) {
Log.w(LOG_TAG, "terminate() is not called yet. We call it in finalize() step.");
terminate();
}
}
/**
* @return returns the number of available entities. The return value is undefined
* when this object is not ready yet (typically when {{@link #init()} is not called
* or when {@link #terminate()} is already called).
*/
public int getCount() {
if (mCursor == null) {
Log.w(LOG_TAG, "This object is not ready yet.");
return 0;
}
return mCursor.getCount();
}
/**
* @return true when there's no entity to be built. The return value is undefined
* when this object is not ready yet.
*/
public boolean isAfterLast() {
if (mCursor == null) {
Log.w(LOG_TAG, "This object is not ready yet.");
return false;
}
return mCursor.isAfterLast();
}
/**
* @return Returns the error reason.
*/
public String getErrorReason() {
return mErrorReason;
}
}

View File

@ -0,0 +1,505 @@
/*
* 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 android.pim.vcard;
import android.telephony.PhoneNumberUtils;
import android.util.Log;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* The class representing VCard related configurations. Useful static methods are not in this class
* but in VCardUtils.
*/
public class VCardConfig {
private static final String LOG_TAG = "VCardConfig";
/* package */ static final int LOG_LEVEL_NONE = 0;
/* package */ static final int LOG_LEVEL_PERFORMANCE_MEASUREMENT = 0x1;
/* package */ static final int LOG_LEVEL_SHOW_WARNING = 0x2;
/* package */ static final int LOG_LEVEL_VERBOSE =
LOG_LEVEL_PERFORMANCE_MEASUREMENT | LOG_LEVEL_SHOW_WARNING;
/* package */ static final int LOG_LEVEL = LOG_LEVEL_NONE;
/**
* <p>
* The charset used during import.
* </p>
* <p>
* We cannot determine which charset should be used to interpret lines in vCard,
* while Java requires us to specify it when InputStream is used.
* We need to rely on the mechanism due to some performance reason.
* </p>
* <p>
* In order to avoid "misinterpretation" of charset and lose any data in vCard,
* "ISO-8859-1" is first used for reading the stream.
* When a charset is specified in a property (with "CHARSET=..." parameter),
* the string is decoded to raw bytes and encoded into the specific charset,
* </p>
* <p>
* Unicode specification there's a one to one mapping between each byte in ISO-8859-1
* and a codepoint, and Java specification requires runtime must have the charset.
* Thus, ISO-8859-1 is one effective mapping for intermediate mapping.
* </p>
*/
public static final String DEFAULT_INTERMEDIATE_CHARSET = "ISO-8859-1";
/**
* The charset used when there's no information affbout what charset should be used to
* encode the binary given from vCard.
*/
public static final String DEFAULT_IMPORT_CHARSET = "UTF-8";
public static final String DEFAULT_EXPORT_CHARSET = "UTF-8";
/**
* Do not use statically like "version == VERSION_V21"
*/
public static final int VERSION_21 = 0;
public static final int VERSION_30 = 1;
public static final int VERSION_40 = 2;
public static final int VERSION_MASK = 3;
public static final int NAME_ORDER_DEFAULT = 0;
public static final int NAME_ORDER_EUROPE = 0x4;
public static final int NAME_ORDER_JAPANESE = 0x8;
private static final int NAME_ORDER_MASK = 0xC;
// 0x10 is reserved for safety
/**
* <p>
* The flag indicating the vCard composer will add some "X-" properties used only in Android
* when the formal vCard specification does not have appropriate fields for that data.
* </p>
* <p>
* For example, Android accepts nickname information while vCard 2.1 does not.
* When this flag is on, vCard composer emits alternative "X-" property (like "X-NICKNAME")
* instead of just dropping it.
* </p>
* <p>
* vCard parser code automatically parses the field emitted even when this flag is off.
* </p>
*/
private static final int FLAG_USE_ANDROID_PROPERTY = 0x80000000;
/**
* <p>
* The flag indicating the vCard composer will add some "X-" properties seen in the
* vCard data emitted by the other softwares/devices when the formal vCard specification
* does not have appropriate field(s) for that data.
* </p>
* <p>
* One example is X-PHONETIC-FIRST-NAME/X-PHONETIC-MIDDLE-NAME/X-PHONETIC-LAST-NAME, which are
* for phonetic name (how the name is pronounced), seen in the vCard emitted by some other
* non-Android devices/softwares. We chose to enable the vCard composer to use those
* defact properties since they are also useful for Android devices.
* </p>
* <p>
* Note for developers: only "X-" properties should be added with this flag. vCard 2.1/3.0
* allows any kind of "X-" properties but does not allow non-"X-" properties (except IANA tokens
* in vCard 3.0). Some external parsers may get confused with non-valid, non-"X-" properties.
* </p>
*/
private static final int FLAG_USE_DEFACT_PROPERTY = 0x40000000;
/**
* <p>
* The flag indicating some specific dialect seen in vCard of DoCoMo (one of Japanese
* mobile careers) should be used. This flag does not include any other information like
* that "the vCard is for Japanese". So it is "possible" that "the vCard should have DoCoMo's
* dialect but the name order should be European", but it is not recommended.
* </p>
*/
private static final int FLAG_DOCOMO = 0x20000000;
/**
* <p>
* The flag indicating the vCard composer does "NOT" use Quoted-Printable toward "primary"
* properties even though it is required by vCard 2.1 (QP is prohibited in vCard 3.0).
* </p>
* <p>
* We actually cannot define what is the "primary" property. Note that this is NOT defined
* in vCard specification either. Also be aware that it is NOT related to "primary" notion
* used in {@link android.provider.ContactsContract}.
* This notion is just for vCard composition in Android.
* </p>
* <p>
* We added this Android-specific notion since some (incomplete) vCard exporters for vCard 2.1
* do NOT use Quoted-Printable encoding toward some properties related names like "N", "FN", etc.
* even when their values contain non-ascii or/and CR/LF, while they use the encoding in the
* other properties like "ADR", "ORG", etc.
* <p>
* We are afraid of the case where some vCard importer also forget handling QP presuming QP is
* not used in such fields.
* </p>
* <p>
* This flag is useful when some target importer you are going to focus on does not accept
* such properties with Quoted-Printable encoding.
* </p>
* <p>
* Again, we should not use this flag at all for complying vCard 2.1 spec.
* </p>
* <p>
* In vCard 3.0, Quoted-Printable is explicitly "prohibitted", so we don't need to care this
* kind of problem (hopefully).
* </p>
* @hide
*/
public static final int FLAG_REFRAIN_QP_TO_NAME_PROPERTIES = 0x10000000;
/**
* <p>
* The flag indicating that phonetic name related fields must be converted to
* appropriate form. Note that "appropriate" is not defined in any vCard specification.
* This is Android-specific.
* </p>
* <p>
* One typical (and currently sole) example where we need this flag is the time when
* we need to emit Japanese phonetic names into vCard entries. The property values
* should be encoded into half-width katakana when the target importer is Japanese mobile
* phones', which are probably not able to parse full-width hiragana/katakana for
* historical reasons, while the vCard importers embedded to softwares for PC should be
* able to parse them as we expect.
* </p>
*/
public static final int FLAG_CONVERT_PHONETIC_NAME_STRINGS = 0x08000000;
/**
* <p>
* The flag indicating the vCard composer "for 2.1" emits "TYPE=" string toward TYPE params
* every time possible. The default behavior does not emit it and is valid in the spec.
* In vCrad 3.0, this flag is unnecessary, since "TYPE=" is MUST in vCard 3.0 specification.
* </p>
* <p>
* Detail:
* How more than one TYPE fields are expressed is different between vCard 2.1 and vCard 3.0.
* </p>
* <p>
* e.g.
* </p>
* <ol>
* <li>Probably valid in both vCard 2.1 and vCard 3.0: "ADR;TYPE=DOM;TYPE=HOME:..."</li>
* <li>Valid in vCard 2.1 but not in vCard 3.0: "ADR;DOM;HOME:..."</li>
* <li>Valid in vCard 3.0 but not in vCard 2.1: "ADR;TYPE=DOM,HOME:..."</li>
* </ol>
* <p>
* If you are targeting to the importer which cannot accept TYPE params without "TYPE="
* strings (which should be rare though), please use this flag.
* </p>
* <p>
* Example usage:
* <pre class="prettyprint">int type = (VCARD_TYPE_V21_GENERIC | FLAG_APPEND_TYPE_PARAM);</pre>
* </p>
*/
public static final int FLAG_APPEND_TYPE_PARAM = 0x04000000;
/**
* <p>
* The flag indicating the vCard composer does touch nothing toward phone number Strings
* but leave it as is.
* </p>
* <p>
* The vCard specifications mention nothing toward phone numbers, while some devices
* do (wrongly, but with innevitable reasons).
* For example, there's a possibility Japanese mobile phones are expected to have
* just numbers, hypens, plus, etc. but not usual alphabets, while US mobile phones
* should get such characters. To make exported vCard simple for external parsers,
* we have used {@link PhoneNumberUtils#formatNumber(String)} during export, and
* removed unnecessary characters inside the number (e.g. "111-222-3333 (Miami)"
* becomes "111-222-3333").
* Unfortunate side effect of that use was some control characters used in the other
* areas may be badly affected by the formatting.
* </p>
* <p>
* This flag disables that formatting, affecting both importer and exporter.
* If the user is aware of some side effects due to the implicit formatting, use this flag.
* </p>
*/
public static final int FLAG_REFRAIN_PHONE_NUMBER_FORMATTING = 0x02000000;
/**
* <p>
* For importer only. Ignored in exporter.
* </p>
* <p>
* The flag indicating the parser should handle a nested vCard, in which vCard clause starts
* in another vCard clause. Here's a typical example.
* </p>
* <pre class="prettyprint">BEGIN:VCARD
* BEGIN:VCARD
* VERSION:2.1
* ...
* END:VCARD
* END:VCARD</pre>
* <p>
* The vCard 2.1 specification allows the nest, but also let parsers ignore nested entries,
* while some mobile devices emit nested ones as primary data to be imported.
* </p>
* <p>
* This flag forces a vCard parser to torelate such a nest and understand its content.
* </p>
*/
public static final int FLAG_TORELATE_NEST = 0x01000000;
//// The followings are VCard types available from importer/exporter. ////
public static final int FLAG_REFRAIN_IMAGE_EXPORT = 0x00800000;
/**
* <p>
* The type indicating nothing. Used by {@link VCardSourceDetector} when it
* was not able to guess the exact vCard type.
* </p>
*/
public static final int VCARD_TYPE_UNKNOWN = 0;
/**
* <p>
* Generic vCard format with the vCard 2.1. When composing a vCard entry,
* the US convension will be used toward formatting some values.
* </p>
* <p>
* e.g. The order of the display name would be "Prefix Given Middle Family Suffix",
* while it should be "Prefix Family Middle Given Suffix" in Japan for example.
* </p>
* <p>
* Uses UTF-8 for the charset as a charset for exporting. Note that old vCard importer
* outside Android cannot accept it since vCard 2.1 specifically does not allow
* that charset, while we need to use it to support various languages around the world.
* </p>
* <p>
* If you want to use alternative charset, you should notify the charset to the other
* compontent to be used.
* </p>
*/
public static final int VCARD_TYPE_V21_GENERIC =
(VERSION_21 | NAME_ORDER_DEFAULT | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
/* package */ static String VCARD_TYPE_V21_GENERIC_STR = "v21_generic";
/**
* <p>
* General vCard format with the version 3.0. Uses UTF-8 for the charset.
* </p>
* <p>
* Not fully ready yet. Use with caution when you use this.
* </p>
*/
public static final int VCARD_TYPE_V30_GENERIC =
(VERSION_30 | NAME_ORDER_DEFAULT | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
/* package */ static final String VCARD_TYPE_V30_GENERIC_STR = "v30_generic";
/**
* General vCard format with the version 4.0.
* @hide vCard 4.0 is not published yet.
*/
public static final int VCARD_TYPE_V40_GENERIC =
(VERSION_40 | NAME_ORDER_DEFAULT | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
/* package */ static final String VCARD_TYPE_V40_GENERIC_STR = "v40_generic";
/**
* <p>
* General vCard format for the vCard 2.1 with some Europe convension. Uses Utf-8.
* Currently, only name order is considered ("Prefix Middle Given Family Suffix")
* </p>
*/
public static final int VCARD_TYPE_V21_EUROPE =
(VERSION_21 | NAME_ORDER_EUROPE | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
/* package */ static final String VCARD_TYPE_V21_EUROPE_STR = "v21_europe";
/**
* <p>
* General vCard format with the version 3.0 with some Europe convension. Uses UTF-8.
* </p>
* <p>
* Not ready yet. Use with caution when you use this.
* </p>
*/
public static final int VCARD_TYPE_V30_EUROPE =
(VERSION_30 | NAME_ORDER_EUROPE | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
/* package */ static final String VCARD_TYPE_V30_EUROPE_STR = "v30_europe";
/**
* <p>
* The vCard 2.1 format for miscellaneous Japanese devices, using UTF-8 as default charset.
* </p>
* <p>
* Not ready yet. Use with caution when you use this.
* </p>
*/
public static final int VCARD_TYPE_V21_JAPANESE =
(VERSION_21 | NAME_ORDER_JAPANESE | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
/* package */ static final String VCARD_TYPE_V21_JAPANESE_STR = "v21_japanese_utf8";
/**
* <p>
* The vCard 3.0 format for miscellaneous Japanese devices, using UTF-8 as default charset.
* </p>
* <p>
* Not ready yet. Use with caution when you use this.
* </p>
*/
public static final int VCARD_TYPE_V30_JAPANESE =
(VERSION_30 | NAME_ORDER_JAPANESE | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
/* package */ static final String VCARD_TYPE_V30_JAPANESE_STR = "v30_japanese_utf8";
/**
* <p>
* The vCard 2.1 based format which (partially) considers the convention in Japanese
* mobile phones, where phonetic names are translated to half-width katakana if
* possible, etc. It would be better to use Shift_JIS as a charset for maximum
* compatibility.
* </p>
* @hide Should not be available world wide.
*/
public static final int VCARD_TYPE_V21_JAPANESE_MOBILE =
(VERSION_21 | NAME_ORDER_JAPANESE |
FLAG_CONVERT_PHONETIC_NAME_STRINGS | FLAG_REFRAIN_QP_TO_NAME_PROPERTIES);
/* package */ static final String VCARD_TYPE_V21_JAPANESE_MOBILE_STR = "v21_japanese_mobile";
/**
* <p>
* The vCard format used in DoCoMo, which is one of Japanese mobile phone careers.
* </p>
* <p>
* Base version is vCard 2.1, but the data has several DoCoMo-specific convensions.
* No Android-specific property nor defact property is included. The "Primary" properties
* are NOT encoded to Quoted-Printable.
* </p>
* @hide Should not be available world wide.
*/
public static final int VCARD_TYPE_DOCOMO =
(VCARD_TYPE_V21_JAPANESE_MOBILE | FLAG_DOCOMO);
/* package */ static final String VCARD_TYPE_DOCOMO_STR = "docomo";
public static int VCARD_TYPE_DEFAULT = VCARD_TYPE_V21_GENERIC;
private static final Map<String, Integer> sVCardTypeMap;
private static final Set<Integer> sJapaneseMobileTypeSet;
static {
sVCardTypeMap = new HashMap<String, Integer>();
sVCardTypeMap.put(VCARD_TYPE_V21_GENERIC_STR, VCARD_TYPE_V21_GENERIC);
sVCardTypeMap.put(VCARD_TYPE_V30_GENERIC_STR, VCARD_TYPE_V30_GENERIC);
sVCardTypeMap.put(VCARD_TYPE_V21_EUROPE_STR, VCARD_TYPE_V21_EUROPE);
sVCardTypeMap.put(VCARD_TYPE_V30_EUROPE_STR, VCARD_TYPE_V30_EUROPE);
sVCardTypeMap.put(VCARD_TYPE_V21_JAPANESE_STR, VCARD_TYPE_V21_JAPANESE);
sVCardTypeMap.put(VCARD_TYPE_V30_JAPANESE_STR, VCARD_TYPE_V30_JAPANESE);
sVCardTypeMap.put(VCARD_TYPE_V21_JAPANESE_MOBILE_STR, VCARD_TYPE_V21_JAPANESE_MOBILE);
sVCardTypeMap.put(VCARD_TYPE_DOCOMO_STR, VCARD_TYPE_DOCOMO);
sJapaneseMobileTypeSet = new HashSet<Integer>();
sJapaneseMobileTypeSet.add(VCARD_TYPE_V21_JAPANESE);
sJapaneseMobileTypeSet.add(VCARD_TYPE_V30_JAPANESE);
sJapaneseMobileTypeSet.add(VCARD_TYPE_V21_JAPANESE_MOBILE);
sJapaneseMobileTypeSet.add(VCARD_TYPE_DOCOMO);
}
public static int getVCardTypeFromString(final String vcardTypeString) {
final String loweredKey = vcardTypeString.toLowerCase();
if (sVCardTypeMap.containsKey(loweredKey)) {
return sVCardTypeMap.get(loweredKey);
} else if ("default".equalsIgnoreCase(vcardTypeString)) {
return VCARD_TYPE_DEFAULT;
} else {
Log.e(LOG_TAG, "Unknown vCard type String: \"" + vcardTypeString + "\"");
return VCARD_TYPE_DEFAULT;
}
}
public static boolean isVersion21(final int vcardType) {
return (vcardType & VERSION_MASK) == VERSION_21;
}
public static boolean isVersion30(final int vcardType) {
return (vcardType & VERSION_MASK) == VERSION_30;
}
public static boolean isVersion40(final int vcardType) {
return (vcardType & VERSION_MASK) == VERSION_40;
}
public static boolean shouldUseQuotedPrintable(final int vcardType) {
return !isVersion30(vcardType);
}
public static int getNameOrderType(final int vcardType) {
return vcardType & NAME_ORDER_MASK;
}
public static boolean usesAndroidSpecificProperty(final int vcardType) {
return ((vcardType & FLAG_USE_ANDROID_PROPERTY) != 0);
}
public static boolean usesDefactProperty(final int vcardType) {
return ((vcardType & FLAG_USE_DEFACT_PROPERTY) != 0);
}
public static boolean showPerformanceLog() {
return (VCardConfig.LOG_LEVEL & VCardConfig.LOG_LEVEL_PERFORMANCE_MEASUREMENT) != 0;
}
public static boolean shouldRefrainQPToNameProperties(final int vcardType) {
return (!shouldUseQuotedPrintable(vcardType) ||
((vcardType & FLAG_REFRAIN_QP_TO_NAME_PROPERTIES) != 0));
}
public static boolean appendTypeParamName(final int vcardType) {
return (isVersion30(vcardType) || ((vcardType & FLAG_APPEND_TYPE_PARAM) != 0));
}
/**
* @return true if the device is Japanese and some Japanese convension is
* applied to creating "formatted" something like FORMATTED_ADDRESS.
*/
public static boolean isJapaneseDevice(final int vcardType) {
// TODO: Some mask will be required so that this method wrongly interpret
// Japanese"-like" vCard type.
// e.g. VCARD_TYPE_V21_JAPANESE_SJIS | FLAG_APPEND_TYPE_PARAMS
return sJapaneseMobileTypeSet.contains(vcardType);
}
/* package */ static boolean refrainPhoneNumberFormatting(final int vcardType) {
return ((vcardType & FLAG_REFRAIN_PHONE_NUMBER_FORMATTING) != 0);
}
public static boolean needsToConvertPhoneticString(final int vcardType) {
return ((vcardType & FLAG_CONVERT_PHONETIC_NAME_STRINGS) != 0);
}
public static boolean onlyOneNoteFieldIsAvailable(final int vcardType) {
return vcardType == VCARD_TYPE_DOCOMO;
}
public static boolean isDoCoMo(final int vcardType) {
return ((vcardType & FLAG_DOCOMO) != 0);
}
private VCardConfig() {
}
}

View File

@ -0,0 +1,175 @@
/*
* 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 android.pim.vcard;
/**
* Constants used in both exporter and importer code.
*/
public class VCardConstants {
public static final String VERSION_V21 = "2.1";
public static final String VERSION_V30 = "3.0";
public static final String VERSION_V40 = "4.0";
// The property names valid both in vCard 2.1 and 3.0.
public static final String PROPERTY_BEGIN = "BEGIN";
public static final String PROPERTY_VERSION = "VERSION";
public static final String PROPERTY_N = "N";
public static final String PROPERTY_FN = "FN";
public static final String PROPERTY_ADR = "ADR";
public static final String PROPERTY_EMAIL = "EMAIL";
public static final String PROPERTY_NOTE = "NOTE";
public static final String PROPERTY_ORG = "ORG";
public static final String PROPERTY_SOUND = "SOUND"; // Not fully supported.
public static final String PROPERTY_TEL = "TEL";
public static final String PROPERTY_TITLE = "TITLE";
public static final String PROPERTY_ROLE = "ROLE";
public static final String PROPERTY_PHOTO = "PHOTO";
public static final String PROPERTY_LOGO = "LOGO";
public static final String PROPERTY_URL = "URL";
public static final String PROPERTY_BDAY = "BDAY"; // Birthday (3.0, 4.0)
public static final String PROPERTY_BIRTH = "BIRTH"; // Place of birth (4.0)
public static final String PROPERTY_ANNIVERSARY = "ANNIVERSARY"; // Date of marriage (4.0)
public static final String PROPERTY_NAME = "NAME"; // (3.0, 4,0)
public static final String PROPERTY_NICKNAME = "NICKNAME"; // (3.0, 4.0)
public static final String PROPERTY_SORT_STRING = "SORT-STRING"; // (3.0, 4.0)
public static final String PROPERTY_END = "END";
// Valid property names not supported (not appropriately handled) by our importer.
// TODO: Should be removed from the view of memory efficiency?
public static final String PROPERTY_REV = "REV";
public static final String PROPERTY_AGENT = "AGENT"; // (3.0)
public static final String PROPERTY_DDAY = "DDAY"; // Date of death (4.0)
public static final String PROPERTY_DEATH = "DEATH"; // Place of death (4.0)
// Available in vCard 3.0. Shoud not use when composing vCard 2.1 file.
// De-fact property values expressing phonetic names.
public static final String PROPERTY_X_PHONETIC_FIRST_NAME = "X-PHONETIC-FIRST-NAME";
public static final String PROPERTY_X_PHONETIC_MIDDLE_NAME = "X-PHONETIC-MIDDLE-NAME";
public static final String PROPERTY_X_PHONETIC_LAST_NAME = "X-PHONETIC-LAST-NAME";
// Properties both ContactsStruct and de-fact vCard extensions
// Shown in http://en.wikipedia.org/wiki/VCard support are defined here.
public static final String PROPERTY_X_AIM = "X-AIM";
public static final String PROPERTY_X_MSN = "X-MSN";
public static final String PROPERTY_X_YAHOO = "X-YAHOO";
public static final String PROPERTY_X_ICQ = "X-ICQ";
public static final String PROPERTY_X_JABBER = "X-JABBER";
public static final String PROPERTY_X_GOOGLE_TALK = "X-GOOGLE-TALK";
public static final String PROPERTY_X_SKYPE_USERNAME = "X-SKYPE-USERNAME";
// Properties only ContactsStruct has. We alse use this.
public static final String PROPERTY_X_QQ = "X-QQ";
public static final String PROPERTY_X_NETMEETING = "X-NETMEETING";
// Phone number for Skype, available as usual phone.
public static final String PROPERTY_X_SKYPE_PSTNNUMBER = "X-SKYPE-PSTNNUMBER";
// Property for Android-specific fields.
public static final String PROPERTY_X_ANDROID_CUSTOM = "X-ANDROID-CUSTOM";
// Properties for DoCoMo vCard.
public static final String PROPERTY_X_CLASS = "X-CLASS";
public static final String PROPERTY_X_REDUCTION = "X-REDUCTION";
public static final String PROPERTY_X_NO = "X-NO";
public static final String PROPERTY_X_DCM_HMN_MODE = "X-DCM-HMN-MODE";
public static final String PARAM_TYPE = "TYPE";
public static final String PARAM_TYPE_HOME = "HOME";
public static final String PARAM_TYPE_WORK = "WORK";
public static final String PARAM_TYPE_FAX = "FAX";
public static final String PARAM_TYPE_CELL = "CELL";
public static final String PARAM_TYPE_VOICE = "VOICE";
public static final String PARAM_TYPE_INTERNET = "INTERNET";
public static final String PARAM_CHARSET = "CHARSET";
public static final String PARAM_ENCODING = "ENCODING";
// Abbreviation of "prefered" according to vCard 2.1 specification.
// We interpret this value as "primary" property during import/export.
//
// Note: Both vCard specs does not mention anything about the requirement for this parameter,
// but there may be some vCard importer which will get confused with more than
// one "PREF"s in one property name, while Android accepts them.
public static final String PARAM_TYPE_PREF = "PREF";
// Phone type parameters valid in vCard and known to ContactsContract, but not so common.
public static final String PARAM_TYPE_CAR = "CAR";
public static final String PARAM_TYPE_ISDN = "ISDN";
public static final String PARAM_TYPE_PAGER = "PAGER";
public static final String PARAM_TYPE_TLX = "TLX"; // Telex
// Phone types existing in vCard 2.1 but not known to ContactsContract.
public static final String PARAM_TYPE_MODEM = "MODEM";
public static final String PARAM_TYPE_MSG = "MSG";
public static final String PARAM_TYPE_BBS = "BBS";
public static final String PARAM_TYPE_VIDEO = "VIDEO";
public static final String PARAM_ENCODING_7BIT = "7BIT";
public static final String PARAM_ENCODING_8BIT = "8BIT";
public static final String PARAM_ENCODING_QP = "QUOTED-PRINTABLE";
public static final String PARAM_ENCODING_BASE64 = "BASE64"; // Available in vCard 2.1
public static final String PARAM_ENCODING_B = "B"; // Available in vCard 3.0
// TYPE parameters for Phones, which are not formally valid in vCard (at least 2.1).
// These types are basically encoded to "X-" parameters when composing vCard.
// Parser passes these when "X-" is added to the parameter or not.
public static final String PARAM_PHONE_EXTRA_TYPE_CALLBACK = "CALLBACK";
public static final String PARAM_PHONE_EXTRA_TYPE_RADIO = "RADIO";
public static final String PARAM_PHONE_EXTRA_TYPE_TTY_TDD = "TTY-TDD";
public static final String PARAM_PHONE_EXTRA_TYPE_ASSISTANT = "ASSISTANT";
// vCard composer translates this type to "WORK" + "PREF". Just for parsing.
public static final String PARAM_PHONE_EXTRA_TYPE_COMPANY_MAIN = "COMPANY-MAIN";
// vCard composer translates this type to "VOICE" Just for parsing.
public static final String PARAM_PHONE_EXTRA_TYPE_OTHER = "OTHER";
// TYPE parameters for postal addresses.
public static final String PARAM_ADR_TYPE_PARCEL = "PARCEL";
public static final String PARAM_ADR_TYPE_DOM = "DOM";
public static final String PARAM_ADR_TYPE_INTL = "INTL";
public static final String PARAM_LANGUAGE = "LANGUAGE";
// SORT-AS parameter introduced in vCard 4.0 (as of rev.13)
public static final String PARAM_SORT_AS = "SORT-AS";
// TYPE parameters not officially valid but used in some vCard exporter.
// Do not use in composer side.
public static final String PARAM_EXTRA_TYPE_COMPANY = "COMPANY";
public interface ImportOnly {
public static final String PROPERTY_X_NICKNAME = "X-NICKNAME";
// Some device emits this "X-" parameter for expressing Google Talk,
// which is specifically invalid but should be always properly accepted, and emitted
// in some special case (for that device/application).
public static final String PROPERTY_X_GOOGLE_TALK_WITH_SPACE = "X-GOOGLE TALK";
}
//// Mainly for package constants.
// DoCoMo specific type parameter. Used with "SOUND" property, which is alternate of
// SORT-STRING invCard 3.0.
/* package */ static final String PARAM_TYPE_X_IRMC_N = "X-IRMC-N";
// Used in unit test.
public static final int MAX_DATA_COLUMN = 15;
/* package */ static final int MAX_CHARACTER_NUMS_QP = 76;
static final int MAX_CHARACTER_NUMS_BASE64_V30 = 75;
private VCardConstants() {
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,68 @@
/*
* 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 android.pim.vcard;
import android.content.ContentResolver;
import android.net.Uri;
import android.util.Log;
import java.util.ArrayList;
/**
* <P>
* {@link VCardEntryHandler} implementation which commits the entry to ContentResolver.
* </P>
* <P>
* Note:<BR />
* Each vCard may contain big photo images encoded by BASE64,
* If we store all vCard entries in memory, OutOfMemoryError may be thrown.
* Thus, this class push each VCard entry into ContentResolver immediately.
* </P>
*/
public class VCardEntryCommitter implements VCardEntryHandler {
public static String LOG_TAG = "VCardEntryComitter";
private final ContentResolver mContentResolver;
private long mTimeToCommit;
private ArrayList<Uri> mCreatedUris = new ArrayList<Uri>();
public VCardEntryCommitter(ContentResolver resolver) {
mContentResolver = resolver;
}
public void onStart() {
}
public void onEnd() {
if (VCardConfig.showPerformanceLog()) {
Log.d(LOG_TAG, String.format("time to commit entries: %d ms", mTimeToCommit));
}
}
public void onEntryCreated(final VCardEntry vcardEntry) {
long start = System.currentTimeMillis();
mCreatedUris.add(vcardEntry.pushIntoContentResolver(mContentResolver));
mTimeToCommit += System.currentTimeMillis() - start;
}
/**
* Returns the list of created Uris. This list should not be modified by the caller as it is
* not a clone.
*/
public ArrayList<Uri> getCreatedUris() {
return mCreatedUris;
}
}

View File

@ -0,0 +1,234 @@
/*
* 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 android.pim.vcard;
import android.accounts.Account;
import android.text.TextUtils;
import android.util.Base64;
import android.util.CharsetUtils;
import android.util.Log;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* <p>
* The {@link VCardInterpreter} implementation which enables {@link VCardEntryHandler} objects
* to easily handle each vCard entry.
* </p>
* <p>
* This class understand details inside vCard and translates it to {@link VCardEntry}.
* Then the class throw it to {@link VCardEntryHandler} registered via
* {@link #addEntryHandler(VCardEntryHandler)}, so that all those registered objects
* are able to handle the {@link VCardEntry} object.
* </p>
* <p>
* If you want to know the detail inside vCard, it would be better to implement
* {@link VCardInterpreter} directly, instead of relying on this class and
* {@link VCardEntry} created by the object.
* </p>
*/
public class VCardEntryConstructor implements VCardInterpreter {
private static String LOG_TAG = "VCardEntryConstructor";
private VCardEntry.Property mCurrentProperty = new VCardEntry.Property();
private VCardEntry mCurrentVCardEntry;
private String mParamType;
// The charset using which {@link VCardInterpreter} parses the text.
// Each String is first decoded into binary stream with this charset, and encoded back
// to "target charset", which may be explicitly specified by the vCard with "CHARSET"
// property or implicitly mentioned by its version (e.g. vCard 3.0 recommends UTF-8).
private final String mSourceCharset;
private final boolean mStrictLineBreaking;
private final int mVCardType;
private final Account mAccount;
// For measuring performance.
private long mTimePushIntoContentResolver;
private final List<VCardEntryHandler> mEntryHandlers = new ArrayList<VCardEntryHandler>();
public VCardEntryConstructor() {
this(VCardConfig.VCARD_TYPE_V21_GENERIC, null);
}
public VCardEntryConstructor(final int vcardType) {
this(vcardType, null, null, false);
}
public VCardEntryConstructor(final int vcardType, final Account account) {
this(vcardType, account, null, false);
}
public VCardEntryConstructor(final int vcardType, final Account account,
final String inputCharset) {
this(vcardType, account, inputCharset, false);
}
/**
* @hide Just for testing.
*/
public VCardEntryConstructor(final int vcardType, final Account account,
final String inputCharset, final boolean strictLineBreakParsing) {
if (inputCharset != null) {
mSourceCharset = inputCharset;
} else {
mSourceCharset = VCardConfig.DEFAULT_INTERMEDIATE_CHARSET;
}
mStrictLineBreaking = strictLineBreakParsing;
mVCardType = vcardType;
mAccount = account;
}
public void addEntryHandler(VCardEntryHandler entryHandler) {
mEntryHandlers.add(entryHandler);
}
@Override
public void start() {
for (VCardEntryHandler entryHandler : mEntryHandlers) {
entryHandler.onStart();
}
}
@Override
public void end() {
for (VCardEntryHandler entryHandler : mEntryHandlers) {
entryHandler.onEnd();
}
}
public void clear() {
mCurrentVCardEntry = null;
mCurrentProperty = new VCardEntry.Property();
}
@Override
public void startEntry() {
if (mCurrentVCardEntry != null) {
Log.e(LOG_TAG, "Nested VCard code is not supported now.");
}
mCurrentVCardEntry = new VCardEntry(mVCardType, mAccount);
}
@Override
public void endEntry() {
mCurrentVCardEntry.consolidateFields();
for (VCardEntryHandler entryHandler : mEntryHandlers) {
entryHandler.onEntryCreated(mCurrentVCardEntry);
}
mCurrentVCardEntry = null;
}
@Override
public void startProperty() {
mCurrentProperty.clear();
}
@Override
public void endProperty() {
mCurrentVCardEntry.addProperty(mCurrentProperty);
}
@Override
public void propertyName(String name) {
mCurrentProperty.setPropertyName(name);
}
@Override
public void propertyGroup(String group) {
}
@Override
public void propertyParamType(String type) {
if (mParamType != null) {
Log.e(LOG_TAG, "propertyParamType() is called more than once " +
"before propertyParamValue() is called");
}
mParamType = type;
}
@Override
public void propertyParamValue(String value) {
if (mParamType == null) {
// From vCard 2.1 specification. vCard 3.0 formally does not allow this case.
mParamType = "TYPE";
}
if (!VCardUtils.containsOnlyAlphaDigitHyphen(value)) {
value = VCardUtils.convertStringCharset(
value, mSourceCharset, VCardConfig.DEFAULT_IMPORT_CHARSET);
}
mCurrentProperty.addParameter(mParamType, value);
mParamType = null;
}
private String handleOneValue(String value,
String sourceCharset, String targetCharset, String encoding) {
// It is possible when some of multiple values are empty.
// e.g. N:;a;;; -> values are "", "a", "", "", and "".
if (TextUtils.isEmpty(value)) {
return "";
}
if (encoding != null) {
if (encoding.equals("BASE64") || encoding.equals("B")) {
mCurrentProperty.setPropertyBytes(Base64.decode(value.getBytes(), Base64.DEFAULT));
return value;
} else if (encoding.equals("QUOTED-PRINTABLE")) {
return VCardUtils.parseQuotedPrintable(
value, mStrictLineBreaking, sourceCharset, targetCharset);
}
Log.w(LOG_TAG, "Unknown encoding. Fall back to default.");
}
// Just translate the charset of a given String from inputCharset to a system one.
return VCardUtils.convertStringCharset(value, sourceCharset, targetCharset);
}
public void propertyValues(List<String> values) {
if (values == null || values.isEmpty()) {
return;
}
final Collection<String> charsetCollection =
mCurrentProperty.getParameters(VCardConstants.PARAM_CHARSET);
final Collection<String> encodingCollection =
mCurrentProperty.getParameters(VCardConstants.PARAM_ENCODING);
final String encoding =
((encodingCollection != null) ? encodingCollection.iterator().next() : null);
String targetCharset = CharsetUtils.nameForDefaultVendor(
((charsetCollection != null) ? charsetCollection.iterator().next() : null));
if (TextUtils.isEmpty(targetCharset)) {
targetCharset = VCardConfig.DEFAULT_IMPORT_CHARSET;
}
for (final String value : values) {
mCurrentProperty.addToPropertyValueList(
handleOneValue(value, mSourceCharset, targetCharset, encoding));
}
}
/**
* @hide
*/
public void showPerformanceInfo() {
Log.d(LOG_TAG, "time for insert ContactStruct to database: " +
mTimePushIntoContentResolver + " ms");
}
}

View File

@ -0,0 +1,63 @@
/*
* 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 android.pim.vcard;
import java.util.List;
/**
* The class which just counts the number of vCard entries in the specified input.
*/
public class VCardEntryCounter implements VCardInterpreter {
private int mCount;
public int getCount() {
return mCount;
}
public void start() {
}
public void end() {
}
public void startEntry() {
}
public void endEntry() {
mCount++;
}
public void startProperty() {
}
public void endProperty() {
}
public void propertyGroup(String group) {
}
public void propertyName(String name) {
}
public void propertyParamType(String type) {
}
public void propertyParamValue(String value) {
}
public void propertyValues(List<String> values) {
}
}

View File

@ -0,0 +1,43 @@
/*
* 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 android.pim.vcard;
/**
* <p>
* The interface called by {@link VCardEntryConstructor}.
* </p>
* <p>
* This class is useful when you don't want to know vCard data in detail. If you want to know
* it, it would be better to consider using {@link VCardInterpreter}.
* </p>
*/
public interface VCardEntryHandler {
/**
* Called when the parsing started.
*/
public void onStart();
/**
* The method called when one VCard entry is successfully created
*/
public void onEntryCreated(final VCardEntry entry);
/**
* Called when the parsing ended.
* Able to be use this method for showing performance log, etc.
*/
public void onEnd();
}

View File

@ -0,0 +1,102 @@
/*
* 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 android.pim.vcard;
import java.util.List;
/**
* <P>
* The interface which should be implemented by the classes which have to analyze each
* vCard entry minutely.
* </P>
* <P>
* Here, there are several terms specific to vCard (and this library).
* </P>
* <P>
* The term "entry" is one vCard representation in the input, which should start with "BEGIN:VCARD"
* and end with "END:VCARD".
* </P>
* <P>
* The term "property" is one line in vCard entry, which consists of "group", "property name",
* "parameter(param) names and values", and "property values".
* </P>
* <P>
* e.g. group1.propName;paramName1=paramValue1;paramName2=paramValue2;propertyValue1;propertyValue2...
* </P>
*/
public interface VCardInterpreter {
/**
* Called when vCard interpretation started.
*/
void start();
/**
* Called when vCard interpretation finished.
*/
void end();
/**
* Called when parsing one vCard entry started.
* More specifically, this method is called when "BEGIN:VCARD" is read.
*/
void startEntry();
/**
* Called when parsing one vCard entry ended.
* More specifically, this method is called when "END:VCARD" is read.
* Note that {@link #startEntry()} may be called since
* vCard (especially 2.1) allows nested vCard.
*/
void endEntry();
/**
* Called when reading one property started.
*/
void startProperty();
/**
* Called when reading one property ended.
*/
void endProperty();
/**
* @param group A group name. This method may be called more than once or may not be
* called at all, depending on how many gruoups are appended to the property.
*/
void propertyGroup(String group);
/**
* @param name A property name like "N", "FN", "ADR", etc.
*/
void propertyName(String name);
/**
* @param type A parameter name like "ENCODING", "CHARSET", etc.
*/
void propertyParamType(String type);
/**
* @param value A parameter value. This method may be called without
* {@link #propertyParamType(String)} being called (when the vCard is vCard 2.1).
*/
void propertyParamValue(String value);
/**
* @param values List of property values. The size of values would be 1 unless
* coressponding property name is "N", "ADR", or "ORG".
*/
void propertyValues(List<String> values);
}

View File

@ -0,0 +1,102 @@
/*
* 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 android.pim.vcard;
import java.util.Collection;
import java.util.List;
/**
* The {@link VCardInterpreter} implementation which aggregates more than one
* {@link VCardInterpreter} objects and make a user object treat them as one
* {@link VCardInterpreter} object.
*/
public final class VCardInterpreterCollection implements VCardInterpreter {
private final Collection<VCardInterpreter> mInterpreterCollection;
public VCardInterpreterCollection(Collection<VCardInterpreter> interpreterCollection) {
mInterpreterCollection = interpreterCollection;
}
public Collection<VCardInterpreter> getCollection() {
return mInterpreterCollection;
}
public void start() {
for (VCardInterpreter builder : mInterpreterCollection) {
builder.start();
}
}
public void end() {
for (VCardInterpreter builder : mInterpreterCollection) {
builder.end();
}
}
public void startEntry() {
for (VCardInterpreter builder : mInterpreterCollection) {
builder.startEntry();
}
}
public void endEntry() {
for (VCardInterpreter builder : mInterpreterCollection) {
builder.endEntry();
}
}
public void startProperty() {
for (VCardInterpreter builder : mInterpreterCollection) {
builder.startProperty();
}
}
public void endProperty() {
for (VCardInterpreter builder : mInterpreterCollection) {
builder.endProperty();
}
}
public void propertyGroup(String group) {
for (VCardInterpreter builder : mInterpreterCollection) {
builder.propertyGroup(group);
}
}
public void propertyName(String name) {
for (VCardInterpreter builder : mInterpreterCollection) {
builder.propertyName(name);
}
}
public void propertyParamType(String type) {
for (VCardInterpreter builder : mInterpreterCollection) {
builder.propertyParamType(type);
}
}
public void propertyParamValue(String value) {
for (VCardInterpreter builder : mInterpreterCollection) {
builder.propertyParamValue(value);
}
}
public void propertyValues(List<String> values) {
for (VCardInterpreter builder : mInterpreterCollection) {
builder.propertyValues(values);
}
}
}

View File

@ -0,0 +1,55 @@
/*
* 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 android.pim.vcard;
import android.pim.vcard.exception.VCardException;
import java.io.IOException;
import java.io.InputStream;
public interface VCardParser {
/**
* <p>
* Parses the given stream and send the vCard data into VCardBuilderBase object.
* </p>.
* <p>
* Note that vCard 2.1 specification allows "CHARSET" parameter, and some career sets
* local encoding to it. For example, Japanese phone career uses Shift_JIS, which is
* formally allowed in vCard 2.1, but not allowed in vCard 3.0. In vCard 2.1,
* In some exreme case, it is allowed for vCard to have different charsets in one vCard.
* </p>
* <p>
* We recommend you use {@link VCardSourceDetector} and detect which kind of source the
* vCard comes from and explicitly specify a charset using the result.
* </p>
*
* @param is The source to parse.
* @param interepreter A {@link VCardInterpreter} object which used to construct data.
* @throws IOException, VCardException
*/
public void parse(InputStream is, VCardInterpreter interepreter)
throws IOException, VCardException;
/**
* <p>
* Cancel parsing vCard. Useful when you want to stop the parse in the other threads.
* </p>
* <p>
* Actual cancel is done after parsing the current vcard.
* </p>
*/
public abstract void cancel();
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,388 @@
/*
* Copyright (C) 2010 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 android.pim.vcard;
import android.pim.vcard.exception.VCardException;
import android.util.Log;
import java.io.IOException;
import java.util.Set;
/**
* <p>
* Basic implementation achieving vCard 3.0 parsing.
* </p>
* <p>
* This class inherits vCard 2.1 implementation since technically they are similar,
* while specifically there's logical no relevance between them.
* So that developers are not confused with the inheritance,
* {@link VCardParser_V30} does not inherit {@link VCardParser_V21}, while
* {@link VCardParserImpl_V30} inherits {@link VCardParserImpl_V21}.
* </p>
* @hide
*/
/* package */ class VCardParserImpl_V30 extends VCardParserImpl_V21 {
private static final String LOG_TAG = "VCardParserImpl_V30";
private String mPreviousLine;
private boolean mEmittedAgentWarning = false;
public VCardParserImpl_V30() {
super();
}
public VCardParserImpl_V30(int vcardType) {
super(vcardType);
}
@Override
protected int getVersion() {
return VCardConfig.VERSION_30;
}
@Override
protected String getVersionString() {
return VCardConstants.VERSION_V30;
}
@Override
protected String getLine() throws IOException {
if (mPreviousLine != null) {
String ret = mPreviousLine;
mPreviousLine = null;
return ret;
} else {
return mReader.readLine();
}
}
/**
* vCard 3.0 requires that the line with space at the beginning of the line
* must be combined with previous line.
*/
@Override
protected String getNonEmptyLine() throws IOException, VCardException {
String line;
StringBuilder builder = null;
while (true) {
line = mReader.readLine();
if (line == null) {
if (builder != null) {
return builder.toString();
} else if (mPreviousLine != null) {
String ret = mPreviousLine;
mPreviousLine = null;
return ret;
}
throw new VCardException("Reached end of buffer.");
} else if (line.length() == 0) {
if (builder != null) {
return builder.toString();
} else if (mPreviousLine != null) {
String ret = mPreviousLine;
mPreviousLine = null;
return ret;
}
} else if (line.charAt(0) == ' ' || line.charAt(0) == '\t') {
if (builder != null) {
// See Section 5.8.1 of RFC 2425 (MIME-DIR document).
// Following is the excerpts from it.
//
// DESCRIPTION:This is a long description that exists on a long line.
//
// Can be represented as:
//
// DESCRIPTION:This is a long description
// that exists on a long line.
//
// It could also be represented as:
//
// DESCRIPTION:This is a long descrip
// tion that exists o
// n a long line.
builder.append(line.substring(1));
} else if (mPreviousLine != null) {
builder = new StringBuilder();
builder.append(mPreviousLine);
mPreviousLine = null;
builder.append(line.substring(1));
} else {
throw new VCardException("Space exists at the beginning of the line");
}
} else {
if (mPreviousLine == null) {
mPreviousLine = line;
if (builder != null) {
return builder.toString();
}
} else {
String ret = mPreviousLine;
mPreviousLine = line;
return ret;
}
}
}
}
/*
* vcard = [group "."] "BEGIN" ":" "VCARD" 1 * CRLF
* 1 * (contentline)
* ;A vCard object MUST include the VERSION, FN and N types.
* [group "."] "END" ":" "VCARD" 1 * CRLF
*/
@Override
protected boolean readBeginVCard(boolean allowGarbage) throws IOException, VCardException {
// TODO: vCard 3.0 supports group.
return super.readBeginVCard(allowGarbage);
}
@Override
protected void readEndVCard(boolean useCache, boolean allowGarbage)
throws IOException, VCardException {
// TODO: vCard 3.0 supports group.
super.readEndVCard(useCache, allowGarbage);
}
/**
* vCard 3.0 allows iana-token as paramType, while vCard 2.1 does not.
*/
@Override
protected void handleParams(final String params) throws VCardException {
try {
super.handleParams(params);
} catch (VCardException e) {
// maybe IANA type
String[] strArray = params.split("=", 2);
if (strArray.length == 2) {
handleAnyParam(strArray[0], strArray[1]);
} else {
// Must not come here in the current implementation.
throw new VCardException(
"Unknown params value: " + params);
}
}
}
@Override
protected void handleAnyParam(final String paramName, final String paramValue) {
mInterpreter.propertyParamType(paramName);
splitAndPutParamValue(paramValue);
}
@Override
protected void handleParamWithoutName(final String paramValue) {
handleType(paramValue);
}
/*
* vCard 3.0 defines
*
* param = param-name "=" param-value *("," param-value)
* param-name = iana-token / x-name
* param-value = ptext / quoted-string
* quoted-string = DQUOTE QSAFE-CHAR DQUOTE
* QSAFE-CHAR = WSP / %x21 / %x23-7E / NON-ASCII
* ; Any character except CTLs, DQUOTE
*
* QSAFE-CHAR must not contain DQUOTE, including escaped one (\").
*/
@Override
protected void handleType(final String paramValue) {
mInterpreter.propertyParamType("TYPE");
splitAndPutParamValue(paramValue);
}
/**
* Splits parameter values into pieces in accordance with vCard 3.0 specification and
* puts pieces into mInterpreter.
*/
/*
* param-value = ptext / quoted-string
* quoted-string = DQUOTE QSAFE-CHAR DQUOTE
* QSAFE-CHAR = WSP / %x21 / %x23-7E / NON-ASCII
* ; Any character except CTLs, DQUOTE
*
* QSAFE-CHAR must not contain DQUOTE, including escaped one (\")
*/
private void splitAndPutParamValue(String paramValue) {
// "comma,separated:inside.dquote",pref
// -->
// - comma,separated:inside.dquote
// - pref
//
// Note: Though there's a code, we don't need to take much care of
// wrongly-added quotes like the example above, as they induce
// parse errors at the top level (when splitting a line into parts).
StringBuilder builder = null; // Delay initialization.
boolean insideDquote = false;
final int length = paramValue.length();
for (int i = 0; i < length; i++) {
final char ch = paramValue.charAt(i);
if (ch == '"') {
if (insideDquote) {
// End of Dquote.
mInterpreter.propertyParamValue(builder.toString());
builder = null;
insideDquote = false;
} else {
if (builder != null) {
if (builder.length() > 0) {
// e.g.
// pref"quoted"
Log.w(LOG_TAG, "Unexpected Dquote inside property.");
} else {
// e.g.
// pref,"quoted"
// "quoted",pref
mInterpreter.propertyParamValue(builder.toString());
}
}
insideDquote = true;
}
} else if (ch == ',' && !insideDquote) {
if (builder == null) {
Log.w(LOG_TAG, "Comma is used before actual string comes. (" +
paramValue + ")");
} else {
mInterpreter.propertyParamValue(builder.toString());
builder = null;
}
} else {
// To stop creating empty StringBuffer at the end of parameter,
// we delay creating this object until this point.
if (builder == null) {
builder = new StringBuilder();
}
builder.append(ch);
}
}
if (insideDquote) {
// e.g.
// "non-quote-at-end
Log.d(LOG_TAG, "Dangling Dquote.");
}
if (builder != null) {
if (builder.length() == 0) {
Log.w(LOG_TAG, "Unintended behavior. We must not see empty StringBuilder " +
"at the end of parameter value parsing.");
} else {
mInterpreter.propertyParamValue(builder.toString());
}
}
}
@Override
protected void handleAgent(final String propertyValue) {
// The way how vCard 3.0 supports "AGENT" is completely different from vCard 2.1.
//
// e.g.
// AGENT:BEGIN:VCARD\nFN:Joe Friday\nTEL:+1-919-555-7878\n
// TITLE:Area Administrator\, Assistant\n EMAIL\;TYPE=INTERN\n
// ET:jfriday@host.com\nEND:VCARD\n
//
// TODO: fix this.
//
// issue:
// vCard 3.0 also allows this as an example.
//
// AGENT;VALUE=uri:
// CID:JQPUBLIC.part3.960129T083020.xyzMail@host3.com
//
// This is not vCard. Should we support this?
//
// Just ignore the line for now, since we cannot know how to handle it...
if (!mEmittedAgentWarning) {
Log.w(LOG_TAG, "AGENT in vCard 3.0 is not supported yet. Ignore it");
mEmittedAgentWarning = true;
}
}
/**
* vCard 3.0 does not require two CRLF at the last of BASE64 data.
* It only requires that data should be MIME-encoded.
*/
@Override
protected String getBase64(final String firstString)
throws IOException, VCardException {
final StringBuilder builder = new StringBuilder();
builder.append(firstString);
while (true) {
final String line = getLine();
if (line == null) {
throw new VCardException("File ended during parsing BASE64 binary");
}
if (line.length() == 0) {
break;
} else if (!line.startsWith(" ") && !line.startsWith("\t")) {
mPreviousLine = line;
break;
}
builder.append(line);
}
return builder.toString();
}
/**
* ESCAPED-CHAR = "\\" / "\;" / "\," / "\n" / "\N")
* ; \\ encodes \, \n or \N encodes newline
* ; \; encodes ;, \, encodes ,
*
* Note: Apple escapes ':' into '\:' while does not escape '\'
*/
@Override
protected String maybeUnescapeText(final String text) {
return unescapeText(text);
}
public static String unescapeText(final String text) {
StringBuilder builder = new StringBuilder();
final int length = text.length();
for (int i = 0; i < length; i++) {
char ch = text.charAt(i);
if (ch == '\\' && i < length - 1) {
final char next_ch = text.charAt(++i);
if (next_ch == 'n' || next_ch == 'N') {
builder.append("\n");
} else {
builder.append(next_ch);
}
} else {
builder.append(ch);
}
}
return builder.toString();
}
@Override
protected String maybeUnescapeCharacter(final char ch) {
return unescapeCharacter(ch);
}
public static String unescapeCharacter(final char ch) {
if (ch == 'n' || ch == 'N') {
return "\n";
} else {
return String.valueOf(ch);
}
}
@Override
protected Set<String> getKnownPropertyNameSet() {
return VCardParser_V30.sKnownPropertyNameSet;
}
}

View File

@ -0,0 +1,92 @@
/*
* Copyright (C) 2010 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 android.pim.vcard;
import java.util.Set;
/**
* <p>
* Basic implementation parsing vCard 4.0.
* </p>
* <p>
* vCard 4.0 is not published yet. Also this implementation is premature.
* </p>
* @hide
*/
/* package */ class VCardParserImpl_V40 extends VCardParserImpl_V30 {
// private static final String LOG_TAG = "VCardParserImpl_V40";
public VCardParserImpl_V40() {
super();
}
public VCardParserImpl_V40(final int vcardType) {
super(vcardType);
}
@Override
protected int getVersion() {
return VCardConfig.VERSION_40;
}
@Override
protected String getVersionString() {
return VCardConstants.VERSION_V40;
}
/**
* We escape "\N" into new line for safety.
*/
@Override
protected String maybeUnescapeText(final String text) {
return unescapeText(text);
}
public static String unescapeText(final String text) {
// TODO: more strictly, vCard 4.0 requires different type of unescaping rule
// toward each property.
final StringBuilder builder = new StringBuilder();
final int length = text.length();
for (int i = 0; i < length; i++) {
char ch = text.charAt(i);
if (ch == '\\' && i < length - 1) {
final char next_ch = text.charAt(++i);
if (next_ch == 'n' || next_ch == 'N') {
builder.append("\n");
} else {
builder.append(next_ch);
}
} else {
builder.append(ch);
}
}
return builder.toString();
}
public static String unescapeCharacter(final char ch) {
if (ch == 'n' || ch == 'N') {
return "\n";
} else {
return String.valueOf(ch);
}
}
@Override
protected Set<String> getKnownPropertyNameSet() {
return VCardParser_V40.sKnownPropertyNameSet;
}
}

View File

@ -0,0 +1,109 @@
/*
* 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 android.pim.vcard;
import android.pim.vcard.exception.VCardException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
* </p>
* vCard parser for vCard 2.1. See the specification for more detail about the spec itself.
* </p>
* <p>
* The spec is written in 1996, and currently various types of "vCard 2.1" exist.
* To handle real the world vCard formats appropriately and effectively, this class does not
* obey with strict vCard 2.1.
* In stead, not only vCard spec but also real world vCard is considered.
* </p>
* e.g. A lot of devices and softwares let vCard importer/exporter to use
* the PNG format to determine the type of image, while it is not allowed in
* the original specification. As of 2010, we can see even the FLV format
* (possible in Japanese mobile phones).
* </p>
*/
public final class VCardParser_V21 implements VCardParser {
/**
* A unmodifiable Set storing the property names available in the vCard 2.1 specification.
*/
/* package */ static final Set<String> sKnownPropertyNameSet =
Collections.unmodifiableSet(new HashSet<String>(
Arrays.asList("BEGIN", "LOGO", "PHOTO", "LABEL", "FN", "TITLE", "SOUND",
"VERSION", "TEL", "EMAIL", "TZ", "GEO", "NOTE", "URL",
"BDAY", "ROLE", "REV", "UID", "KEY", "MAILER")));
/**
* A unmodifiable Set storing the types known in vCard 2.1.
*/
/* package */ static final Set<String> sKnownTypeSet =
Collections.unmodifiableSet(new HashSet<String>(
Arrays.asList("DOM", "INTL", "POSTAL", "PARCEL", "HOME", "WORK",
"PREF", "VOICE", "FAX", "MSG", "CELL", "PAGER", "BBS",
"MODEM", "CAR", "ISDN", "VIDEO", "AOL", "APPLELINK",
"ATTMAIL", "CIS", "EWORLD", "INTERNET", "IBMMAIL",
"MCIMAIL", "POWERSHARE", "PRODIGY", "TLX", "X400", "GIF",
"CGM", "WMF", "BMP", "MET", "PMB", "DIB", "PICT", "TIFF",
"PDF", "PS", "JPEG", "QTIME", "MPEG", "MPEG2", "AVI",
"WAVE", "AIFF", "PCM", "X509", "PGP")));
/**
* A unmodifiable Set storing the values for the type "VALUE", available in the vCard 2.1.
*/
/* package */ static final Set<String> sKnownValueSet =
Collections.unmodifiableSet(new HashSet<String>(
Arrays.asList("INLINE", "URL", "CONTENT-ID", "CID")));
/**
* <p>
* A unmodifiable Set storing the values for the type "ENCODING", available in the vCard 2.1.
* </p>
* <p>
* Though vCard 2.1 specification does not allow "B" encoding, some data may have it.
* We allow it for safety.
* </p>
*/
/* package */ static final Set<String> sAvailableEncoding =
Collections.unmodifiableSet(new HashSet<String>(
Arrays.asList(VCardConstants.PARAM_ENCODING_7BIT,
VCardConstants.PARAM_ENCODING_8BIT,
VCardConstants.PARAM_ENCODING_QP,
VCardConstants.PARAM_ENCODING_BASE64,
VCardConstants.PARAM_ENCODING_B)));
private final VCardParserImpl_V21 mVCardParserImpl;
public VCardParser_V21() {
mVCardParserImpl = new VCardParserImpl_V21();
}
public VCardParser_V21(int vcardType) {
mVCardParserImpl = new VCardParserImpl_V21(vcardType);
}
public void parse(InputStream is, VCardInterpreter interepreter)
throws IOException, VCardException {
mVCardParserImpl.parse(is, interepreter);
}
public void cancel() {
mVCardParserImpl.cancel();
}
}

View File

@ -0,0 +1,87 @@
/*
* 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 android.pim.vcard;
import android.pim.vcard.exception.VCardException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
* <p>
* vCard parser for vCard 3.0. See RFC 2426 for more detail.
* </p>
* <p>
* This parser allows vCard format which is not allowed in the RFC, since
* we have seen several vCard 3.0 files which don't comply with it.
* </p>
* <p>
* e.g. vCard 3.0 does not allow "CHARSET" attribute, but some actual files
* have it and they uses non UTF-8 charsets. UTF-8 is recommended in RFC 2426,
* but it is not a must. We silently allow "CHARSET".
* </p>
*/
public class VCardParser_V30 implements VCardParser {
/* package */ static final Set<String> sKnownPropertyNameSet =
Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(
"BEGIN", "LOGO", "PHOTO", "LABEL", "FN", "TITLE", "SOUND",
"VERSION", "TEL", "EMAIL", "TZ", "GEO", "NOTE", "URL",
"BDAY", "ROLE", "REV", "UID", "KEY", "MAILER", // 2.1
"NAME", "PROFILE", "SOURCE", "NICKNAME", "CLASS",
"SORT-STRING", "CATEGORIES", "PRODID"))); // 3.0
/**
* <p>
* A unmodifiable Set storing the values for the type "ENCODING", available in the vCard 3.0.
* </p>
* <p>
* Though vCard 2.1 specification does not allow "7BIT" or "BASE64", we allow them for safety.
* </p>
* <p>
* "QUOTED-PRINTABLE" is not allowed in vCard 3.0 and not in this parser either,
* because the encoding ambiguates how the vCard file to be parsed.
* </p>
*/
/* package */ static final Set<String> sAcceptableEncoding =
Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(
VCardConstants.PARAM_ENCODING_7BIT,
VCardConstants.PARAM_ENCODING_8BIT,
VCardConstants.PARAM_ENCODING_BASE64,
VCardConstants.PARAM_ENCODING_B)));
private final VCardParserImpl_V30 mVCardParserImpl;
public VCardParser_V30() {
mVCardParserImpl = new VCardParserImpl_V30();
}
public VCardParser_V30(int vcardType) {
mVCardParserImpl = new VCardParserImpl_V30(vcardType);
}
public void parse(InputStream is, VCardInterpreter interepreter)
throws IOException, VCardException {
mVCardParserImpl.parse(is, interepreter);
}
public void cancel() {
mVCardParserImpl.cancel();
}
}

View File

@ -0,0 +1,78 @@
/*
* Copyright (C) 2010 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 android.pim.vcard;
import android.pim.vcard.exception.VCardException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
* <p>
* vCard parser for vCard 4.0.
* </p>
* <p>
* Currently this parser is based on vCard 4.0 specification rev 11.
* </p>
*/
public class VCardParser_V40 implements VCardParser {
/* package */ static final Set<String> sKnownPropertyNameSet =
Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(
"BEGIN", "END", "SOURCE", "NAME", "KIND", "XML",
"FN", "N", "NICKNAME", "PHOTO", "BDAY", "DDAY",
"BIRTH", "DEATH", "ANNIVERSARY", "SEX", "ADR",
"LABEL", "TEL", "EMAIL", "IMPP", "LANG", "TZ",
"GEO", "TITLE", "ROLE", "LOGO", "ORG", "MEMBER",
"RELATED", "CATEGORIES", "NOTE", "PRODID",
"REV", "SOUND", "UID", "CLIENTPIDMAP",
"URL", "VERSION", "CLASS", "KEY", "FBURL", "CALENDRURI",
"CALURI")));
/**
* <p>
* A unmodifiable Set storing the values for the type "ENCODING", available in vCard 4.0.
* </p>
*/
/* package */ static final Set<String> sAcceptableEncoding =
Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(
VCardConstants.PARAM_ENCODING_8BIT,
VCardConstants.PARAM_ENCODING_B)));
private final VCardParserImpl_V30 mVCardParserImpl;
public VCardParser_V40() {
mVCardParserImpl = new VCardParserImpl_V40();
}
public VCardParser_V40(int vcardType) {
mVCardParserImpl = new VCardParserImpl_V40(vcardType);
}
@Override
public void parse(InputStream is, VCardInterpreter interepreter)
throws IOException, VCardException {
mVCardParserImpl.parse(is, interepreter);
}
@Override
public void cancel() {
mVCardParserImpl.cancel();
}
}

View File

@ -0,0 +1,205 @@
/*
* 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 android.pim.vcard;
import android.text.TextUtils;
import android.util.Log;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* <p>
* The class which tries to detects the source of a vCard file from its contents.
* </p>
* <p>
* The specification of vCard (including both 2.1 and 3.0) is not so strict as to
* guess its format just by reading beginning few lines (usually we can, but in
* some most pessimistic case, we cannot until at almost the end of the file).
* Also we cannot store all vCard entries in memory, while there's no specification
* how big the vCard entry would become after the parse.
* </p>
* <p>
* This class is usually used for the "first scan", in which we can understand which vCard
* version is used (and how many entries exist in a file).
* </p>
*/
public class VCardSourceDetector implements VCardInterpreter {
private static final String LOG_TAG = "VCardSourceDetector";
private static Set<String> APPLE_SIGNS = new HashSet<String>(Arrays.asList(
"X-PHONETIC-FIRST-NAME", "X-PHONETIC-MIDDLE-NAME", "X-PHONETIC-LAST-NAME",
"X-ABADR", "X-ABUID"));
private static Set<String> JAPANESE_MOBILE_PHONE_SIGNS = new HashSet<String>(Arrays.asList(
"X-GNO", "X-GN", "X-REDUCTION"));
private static Set<String> WINDOWS_MOBILE_PHONE_SIGNS = new HashSet<String>(Arrays.asList(
"X-MICROSOFT-ASST_TEL", "X-MICROSOFT-ASSISTANT", "X-MICROSOFT-OFFICELOC"));
// Note: these signes appears before the signs of the other type (e.g. "X-GN").
// In other words, Japanese FOMA mobile phones are detected as FOMA, not JAPANESE_MOBILE_PHONES.
private static Set<String> FOMA_SIGNS = new HashSet<String>(Arrays.asList(
"X-SD-VERN", "X-SD-FORMAT_VER", "X-SD-CATEGORIES", "X-SD-CLASS", "X-SD-DCREATED",
"X-SD-DESCRIPTION"));
private static String TYPE_FOMA_CHARSET_SIGN = "X-SD-CHAR_CODE";
/**
* Represents that no estimation is available. Users of this class is able to this
* constant when you don't want to let a vCard parser rely on estimation for parse type.
*/
public static final int PARSE_TYPE_UNKNOWN = 0;
// For Apple's software, which does not mean this type is effective for all its products.
// We confirmed they usually use UTF-8, but not sure about vCard type.
private static final int PARSE_TYPE_APPLE = 1;
// For Japanese mobile phones, which are usually using Shift_JIS as a charset.
private static final int PARSE_TYPE_MOBILE_PHONE_JP = 2;
// For some of mobile phones released from DoCoMo, which use nested vCard.
private static final int PARSE_TYPE_DOCOMO_TORELATE_NEST = 3;
// For Japanese Windows Mobel phones. It's version is supposed to be 6.5.
private static final int PARSE_TYPE_WINDOWS_MOBILE_V65_JP = 4;
private int mParseType = 0; // Not sure.
private boolean mNeedToParseVersion = false;
private int mVersion = -1; // -1 == unknown
// Some mobile phones (like FOMA) tells us the charset of the data.
private boolean mNeedToParseCharset;
private String mSpecifiedCharset;
public void start() {
}
public void end() {
}
public void startEntry() {
}
public void startProperty() {
mNeedToParseCharset = false;
mNeedToParseVersion = false;
}
public void endProperty() {
}
public void endEntry() {
}
public void propertyGroup(String group) {
}
public void propertyName(String name) {
if (name.equalsIgnoreCase(VCardConstants.PROPERTY_VERSION)) {
mNeedToParseVersion = true;
return;
} else if (name.equalsIgnoreCase(TYPE_FOMA_CHARSET_SIGN)) {
mParseType = PARSE_TYPE_DOCOMO_TORELATE_NEST;
// Probably Shift_JIS is used, but we should double confirm.
mNeedToParseCharset = true;
return;
}
if (mParseType != PARSE_TYPE_UNKNOWN) {
return;
}
if (WINDOWS_MOBILE_PHONE_SIGNS.contains(name)) {
mParseType = PARSE_TYPE_WINDOWS_MOBILE_V65_JP;
} else if (FOMA_SIGNS.contains(name)) {
mParseType = PARSE_TYPE_DOCOMO_TORELATE_NEST;
} else if (JAPANESE_MOBILE_PHONE_SIGNS.contains(name)) {
mParseType = PARSE_TYPE_MOBILE_PHONE_JP;
} else if (APPLE_SIGNS.contains(name)) {
mParseType = PARSE_TYPE_APPLE;
}
}
public void propertyParamType(String type) {
}
public void propertyParamValue(String value) {
}
public void propertyValues(List<String> values) {
if (mNeedToParseVersion && values.size() > 0) {
final String versionString = values.get(0);
if (versionString.equals(VCardConstants.VERSION_V21)) {
mVersion = VCardConfig.VERSION_21;
} else if (versionString.equals(VCardConstants.VERSION_V30)) {
mVersion = VCardConfig.VERSION_30;
} else if (versionString.equals(VCardConstants.VERSION_V40)) {
mVersion = VCardConfig.VERSION_40;
} else {
Log.w(LOG_TAG, "Invalid version string: " + versionString);
}
} else if (mNeedToParseCharset && values.size() > 0) {
mSpecifiedCharset = values.get(0);
}
}
/**
* @return The available type can be used with vCard parser. You probably need to
* use {{@link #getEstimatedCharset()} to understand the charset to be used.
*/
public int getEstimatedType() {
switch (mParseType) {
case PARSE_TYPE_DOCOMO_TORELATE_NEST:
return VCardConfig.VCARD_TYPE_DOCOMO | VCardConfig.FLAG_TORELATE_NEST;
case PARSE_TYPE_MOBILE_PHONE_JP:
return VCardConfig.VCARD_TYPE_V21_JAPANESE_MOBILE;
case PARSE_TYPE_APPLE:
case PARSE_TYPE_WINDOWS_MOBILE_V65_JP:
default: {
if (mVersion == VCardConfig.VERSION_21) {
return VCardConfig.VCARD_TYPE_V21_GENERIC;
} else if (mVersion == VCardConfig.VERSION_30) {
return VCardConfig.VCARD_TYPE_V30_GENERIC;
} else if (mVersion == VCardConfig.VERSION_40) {
return VCardConfig.VCARD_TYPE_V40_GENERIC;
} else {
return VCardConfig.VCARD_TYPE_UNKNOWN;
}
}
}
}
/**
* <p>
* Returns charset String guessed from the source's properties.
* This method must be called after parsing target file(s).
* </p>
* @return Charset String. Null is returned if guessing the source fails.
*/
public String getEstimatedCharset() {
if (TextUtils.isEmpty(mSpecifiedCharset)) {
return mSpecifiedCharset;
}
switch (mParseType) {
case PARSE_TYPE_WINDOWS_MOBILE_V65_JP:
case PARSE_TYPE_DOCOMO_TORELATE_NEST:
case PARSE_TYPE_MOBILE_PHONE_JP:
return "SHIFT_JIS";
case PARSE_TYPE_APPLE:
return "UTF-8";
default:
return null;
}
}
}

View File

@ -0,0 +1,796 @@
/*
* 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 android.pim.vcard;
import android.content.ContentProviderOperation;
import android.pim.vcard.exception.VCardException;
import android.provider.ContactsContract.CommonDataKinds.Im;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
import android.provider.ContactsContract.Data;
import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
import android.util.Log;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.net.QuotedPrintableCodec;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Utilities for VCard handling codes.
*/
public class VCardUtils {
private static final String LOG_TAG = "VCardUtils";
// Note that not all types are included in this map/set, since, for example, TYPE_HOME_FAX is
// converted to two parameter Strings. These only contain some minor fields valid in both
// vCard and current (as of 2009-08-07) Contacts structure.
private static final Map<Integer, String> sKnownPhoneTypesMap_ItoS;
private static final Set<String> sPhoneTypesUnknownToContactsSet;
private static final Map<String, Integer> sKnownPhoneTypeMap_StoI;
private static final Map<Integer, String> sKnownImPropNameMap_ItoS;
private static final Set<String> sMobilePhoneLabelSet;
static {
sKnownPhoneTypesMap_ItoS = new HashMap<Integer, String>();
sKnownPhoneTypeMap_StoI = new HashMap<String, Integer>();
sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_CAR, VCardConstants.PARAM_TYPE_CAR);
sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_CAR, Phone.TYPE_CAR);
sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_PAGER, VCardConstants.PARAM_TYPE_PAGER);
sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_PAGER, Phone.TYPE_PAGER);
sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_ISDN, VCardConstants.PARAM_TYPE_ISDN);
sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_ISDN, Phone.TYPE_ISDN);
sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_HOME, Phone.TYPE_HOME);
sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_WORK, Phone.TYPE_WORK);
sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_CELL, Phone.TYPE_MOBILE);
sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_PHONE_EXTRA_TYPE_OTHER, Phone.TYPE_OTHER);
sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_PHONE_EXTRA_TYPE_CALLBACK,
Phone.TYPE_CALLBACK);
sKnownPhoneTypeMap_StoI.put(
VCardConstants.PARAM_PHONE_EXTRA_TYPE_COMPANY_MAIN, Phone.TYPE_COMPANY_MAIN);
sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_PHONE_EXTRA_TYPE_RADIO, Phone.TYPE_RADIO);
sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_PHONE_EXTRA_TYPE_TTY_TDD,
Phone.TYPE_TTY_TDD);
sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_PHONE_EXTRA_TYPE_ASSISTANT,
Phone.TYPE_ASSISTANT);
sPhoneTypesUnknownToContactsSet = new HashSet<String>();
sPhoneTypesUnknownToContactsSet.add(VCardConstants.PARAM_TYPE_MODEM);
sPhoneTypesUnknownToContactsSet.add(VCardConstants.PARAM_TYPE_MSG);
sPhoneTypesUnknownToContactsSet.add(VCardConstants.PARAM_TYPE_BBS);
sPhoneTypesUnknownToContactsSet.add(VCardConstants.PARAM_TYPE_VIDEO);
sKnownImPropNameMap_ItoS = new HashMap<Integer, String>();
sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_AIM, VCardConstants.PROPERTY_X_AIM);
sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_MSN, VCardConstants.PROPERTY_X_MSN);
sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_YAHOO, VCardConstants.PROPERTY_X_YAHOO);
sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_SKYPE, VCardConstants.PROPERTY_X_SKYPE_USERNAME);
sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_GOOGLE_TALK,
VCardConstants.PROPERTY_X_GOOGLE_TALK);
sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_ICQ, VCardConstants.PROPERTY_X_ICQ);
sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_JABBER, VCardConstants.PROPERTY_X_JABBER);
sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_QQ, VCardConstants.PROPERTY_X_QQ);
sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_NETMEETING, VCardConstants.PROPERTY_X_NETMEETING);
// \u643A\u5E2F\u96FB\u8A71 = Full-width Hiragana "Keitai-Denwa" (mobile phone)
// \u643A\u5E2F = Full-width Hiragana "Keitai" (mobile phone)
// \u30B1\u30A4\u30BF\u30A4 = Full-width Katakana "Keitai" (mobile phone)
// \uFF79\uFF72\uFF80\uFF72 = Half-width Katakana "Keitai" (mobile phone)
sMobilePhoneLabelSet = new HashSet<String>(Arrays.asList(
"MOBILE", "\u643A\u5E2F\u96FB\u8A71", "\u643A\u5E2F", "\u30B1\u30A4\u30BF\u30A4",
"\uFF79\uFF72\uFF80\uFF72"));
}
public static String getPhoneTypeString(Integer type) {
return sKnownPhoneTypesMap_ItoS.get(type);
}
/**
* Returns Interger when the given types can be parsed as known type. Returns String object
* when not, which should be set to label.
*/
public static Object getPhoneTypeFromStrings(Collection<String> types,
String number) {
if (number == null) {
number = "";
}
int type = -1;
String label = null;
boolean isFax = false;
boolean hasPref = false;
if (types != null) {
for (String typeString : types) {
if (typeString == null) {
continue;
}
typeString = typeString.toUpperCase();
if (typeString.equals(VCardConstants.PARAM_TYPE_PREF)) {
hasPref = true;
} else if (typeString.equals(VCardConstants.PARAM_TYPE_FAX)) {
isFax = true;
} else {
if (typeString.startsWith("X-") && type < 0) {
typeString = typeString.substring(2);
}
if (typeString.length() == 0) {
continue;
}
final Integer tmp = sKnownPhoneTypeMap_StoI.get(typeString);
if (tmp != null) {
final int typeCandidate = tmp;
// TYPE_PAGER is prefered when the number contains @ surronded by
// a pager number and a domain name.
// e.g.
// o 1111@domain.com
// x @domain.com
// x 1111@
final int indexOfAt = number.indexOf("@");
if ((typeCandidate == Phone.TYPE_PAGER
&& 0 < indexOfAt && indexOfAt < number.length() - 1)
|| type < 0
|| type == Phone.TYPE_CUSTOM) {
type = tmp;
}
} else if (type < 0) {
type = Phone.TYPE_CUSTOM;
label = typeString;
}
}
}
}
if (type < 0) {
if (hasPref) {
type = Phone.TYPE_MAIN;
} else {
// default to TYPE_HOME
type = Phone.TYPE_HOME;
}
}
if (isFax) {
if (type == Phone.TYPE_HOME) {
type = Phone.TYPE_FAX_HOME;
} else if (type == Phone.TYPE_WORK) {
type = Phone.TYPE_FAX_WORK;
} else if (type == Phone.TYPE_OTHER) {
type = Phone.TYPE_OTHER_FAX;
}
}
if (type == Phone.TYPE_CUSTOM) {
return label;
} else {
return type;
}
}
@SuppressWarnings("deprecation")
public static boolean isMobilePhoneLabel(final String label) {
// For backward compatibility.
// Detail: Until Donut, there isn't TYPE_MOBILE for email while there is now.
// To support mobile type at that time, this custom label had been used.
return ("_AUTO_CELL".equals(label) || sMobilePhoneLabelSet.contains(label));
}
public static boolean isValidInV21ButUnknownToContactsPhoteType(final String label) {
return sPhoneTypesUnknownToContactsSet.contains(label);
}
public static String getPropertyNameForIm(final int protocol) {
return sKnownImPropNameMap_ItoS.get(protocol);
}
public static String[] sortNameElements(final int vcardType,
final String familyName, final String middleName, final String givenName) {
final String[] list = new String[3];
final int nameOrderType = VCardConfig.getNameOrderType(vcardType);
switch (nameOrderType) {
case VCardConfig.NAME_ORDER_JAPANESE: {
if (containsOnlyPrintableAscii(familyName) &&
containsOnlyPrintableAscii(givenName)) {
list[0] = givenName;
list[1] = middleName;
list[2] = familyName;
} else {
list[0] = familyName;
list[1] = middleName;
list[2] = givenName;
}
break;
}
case VCardConfig.NAME_ORDER_EUROPE: {
list[0] = middleName;
list[1] = givenName;
list[2] = familyName;
break;
}
default: {
list[0] = givenName;
list[1] = middleName;
list[2] = familyName;
break;
}
}
return list;
}
public static int getPhoneNumberFormat(final int vcardType) {
if (VCardConfig.isJapaneseDevice(vcardType)) {
return PhoneNumberUtils.FORMAT_JAPAN;
} else {
return PhoneNumberUtils.FORMAT_NANP;
}
}
/**
* <p>
* Inserts postal data into the builder object.
* </p>
* <p>
* Note that the data structure of ContactsContract is different from that defined in vCard.
* So some conversion may be performed in this method.
* </p>
*/
public static void insertStructuredPostalDataUsingContactsStruct(int vcardType,
final ContentProviderOperation.Builder builder,
final VCardEntry.PostalData postalData) {
builder.withValueBackReference(StructuredPostal.RAW_CONTACT_ID, 0);
builder.withValue(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE);
builder.withValue(StructuredPostal.TYPE, postalData.type);
if (postalData.type == StructuredPostal.TYPE_CUSTOM) {
builder.withValue(StructuredPostal.LABEL, postalData.label);
}
final String streetString;
if (TextUtils.isEmpty(postalData.street)) {
if (TextUtils.isEmpty(postalData.extendedAddress)) {
streetString = null;
} else {
streetString = postalData.extendedAddress;
}
} else {
if (TextUtils.isEmpty(postalData.extendedAddress)) {
streetString = postalData.street;
} else {
streetString = postalData.street + " " + postalData.extendedAddress;
}
}
builder.withValue(StructuredPostal.POBOX, postalData.pobox);
builder.withValue(StructuredPostal.STREET, streetString);
builder.withValue(StructuredPostal.CITY, postalData.localty);
builder.withValue(StructuredPostal.REGION, postalData.region);
builder.withValue(StructuredPostal.POSTCODE, postalData.postalCode);
builder.withValue(StructuredPostal.COUNTRY, postalData.country);
builder.withValue(StructuredPostal.FORMATTED_ADDRESS,
postalData.getFormattedAddress(vcardType));
if (postalData.isPrimary) {
builder.withValue(Data.IS_PRIMARY, 1);
}
}
public static String constructNameFromElements(final int vcardType,
final String familyName, final String middleName, final String givenName) {
return constructNameFromElements(vcardType, familyName, middleName, givenName,
null, null);
}
public static String constructNameFromElements(final int vcardType,
final String familyName, final String middleName, final String givenName,
final String prefix, final String suffix) {
final StringBuilder builder = new StringBuilder();
final String[] nameList = sortNameElements(vcardType, familyName, middleName, givenName);
boolean first = true;
if (!TextUtils.isEmpty(prefix)) {
first = false;
builder.append(prefix);
}
for (final String namePart : nameList) {
if (!TextUtils.isEmpty(namePart)) {
if (first) {
first = false;
} else {
builder.append(' ');
}
builder.append(namePart);
}
}
if (!TextUtils.isEmpty(suffix)) {
if (!first) {
builder.append(' ');
}
builder.append(suffix);
}
return builder.toString();
}
/**
* Splits the given value into pieces using the delimiter ';' inside it.
*
* Escaped characters in those values are automatically unescaped into original form.
*/
public static List<String> constructListFromValue(final String value,
final int vcardType) {
final List<String> list = new ArrayList<String>();
StringBuilder builder = new StringBuilder();
final int length = value.length();
for (int i = 0; i < length; i++) {
char ch = value.charAt(i);
if (ch == '\\' && i < length - 1) {
char nextCh = value.charAt(i + 1);
final String unescapedString;
if (VCardConfig.isVersion40(vcardType)) {
unescapedString = VCardParserImpl_V40.unescapeCharacter(nextCh);
} else if (VCardConfig.isVersion30(vcardType)) {
unescapedString = VCardParserImpl_V30.unescapeCharacter(nextCh);
} else {
if (!VCardConfig.isVersion21(vcardType)) {
// Unknown vCard type
Log.w(LOG_TAG, "Unknown vCard type");
}
unescapedString = VCardParserImpl_V21.unescapeCharacter(nextCh);
}
if (unescapedString != null) {
builder.append(unescapedString);
i++;
} else {
builder.append(ch);
}
} else if (ch == ';') {
list.add(builder.toString());
builder = new StringBuilder();
} else {
builder.append(ch);
}
}
list.add(builder.toString());
return list;
}
public static boolean containsOnlyPrintableAscii(final String...values) {
if (values == null) {
return true;
}
return containsOnlyPrintableAscii(Arrays.asList(values));
}
public static boolean containsOnlyPrintableAscii(final Collection<String> values) {
if (values == null) {
return true;
}
for (final String value : values) {
if (TextUtils.isEmpty(value)) {
continue;
}
if (!TextUtils.isPrintableAsciiOnly(value)) {
return false;
}
}
return true;
}
/**
* <p>
* This is useful when checking the string should be encoded into quoted-printable
* or not, which is required by vCard 2.1.
* </p>
* <p>
* See the definition of "7bit" in vCard 2.1 spec for more information.
* </p>
*/
public static boolean containsOnlyNonCrLfPrintableAscii(final String...values) {
if (values == null) {
return true;
}
return containsOnlyNonCrLfPrintableAscii(Arrays.asList(values));
}
public static boolean containsOnlyNonCrLfPrintableAscii(final Collection<String> values) {
if (values == null) {
return true;
}
final int asciiFirst = 0x20;
final int asciiLast = 0x7E; // included
for (final String value : values) {
if (TextUtils.isEmpty(value)) {
continue;
}
final int length = value.length();
for (int i = 0; i < length; i = value.offsetByCodePoints(i, 1)) {
final int c = value.codePointAt(i);
if (!(asciiFirst <= c && c <= asciiLast)) {
return false;
}
}
}
return true;
}
private static final Set<Character> sUnAcceptableAsciiInV21WordSet =
new HashSet<Character>(Arrays.asList('[', ']', '=', ':', '.', ',', ' '));
/**
* <p>
* This is useful since vCard 3.0 often requires the ("X-") properties and groups
* should contain only alphabets, digits, and hyphen.
* </p>
* <p>
* Note: It is already known some devices (wrongly) outputs properties with characters
* which should not be in the field. One example is "X-GOOGLE TALK". We accept
* such kind of input but must never output it unless the target is very specific
* to the device which is able to parse the malformed input.
* </p>
*/
public static boolean containsOnlyAlphaDigitHyphen(final String...values) {
if (values == null) {
return true;
}
return containsOnlyAlphaDigitHyphen(Arrays.asList(values));
}
public static boolean containsOnlyAlphaDigitHyphen(final Collection<String> values) {
if (values == null) {
return true;
}
final int upperAlphabetFirst = 0x41; // A
final int upperAlphabetAfterLast = 0x5b; // [
final int lowerAlphabetFirst = 0x61; // a
final int lowerAlphabetAfterLast = 0x7b; // {
final int digitFirst = 0x30; // 0
final int digitAfterLast = 0x3A; // :
final int hyphen = '-';
for (final String str : values) {
if (TextUtils.isEmpty(str)) {
continue;
}
final int length = str.length();
for (int i = 0; i < length; i = str.offsetByCodePoints(i, 1)) {
int codepoint = str.codePointAt(i);
if (!((lowerAlphabetFirst <= codepoint && codepoint < lowerAlphabetAfterLast) ||
(upperAlphabetFirst <= codepoint && codepoint < upperAlphabetAfterLast) ||
(digitFirst <= codepoint && codepoint < digitAfterLast) ||
(codepoint == hyphen))) {
return false;
}
}
}
return true;
}
public static boolean containsOnlyWhiteSpaces(final String...values) {
if (values == null) {
return true;
}
return containsOnlyWhiteSpaces(Arrays.asList(values));
}
public static boolean containsOnlyWhiteSpaces(final Collection<String> values) {
if (values == null) {
return true;
}
for (final String str : values) {
if (TextUtils.isEmpty(str)) {
continue;
}
final int length = str.length();
for (int i = 0; i < length; i = str.offsetByCodePoints(i, 1)) {
if (!Character.isWhitespace(str.codePointAt(i))) {
return false;
}
}
}
return true;
}
/**
* <p>
* Returns true when the given String is categorized as "word" specified in vCard spec 2.1.
* </p>
* <p>
* vCard 2.1 specifies:<br />
* word = &lt;any printable 7bit us-ascii except []=:., &gt;
* </p>
*/
public static boolean isV21Word(final String value) {
if (TextUtils.isEmpty(value)) {
return true;
}
final int asciiFirst = 0x20;
final int asciiLast = 0x7E; // included
final int length = value.length();
for (int i = 0; i < length; i = value.offsetByCodePoints(i, 1)) {
final int c = value.codePointAt(i);
if (!(asciiFirst <= c && c <= asciiLast) ||
sUnAcceptableAsciiInV21WordSet.contains((char)c)) {
return false;
}
}
return true;
}
private static final int[] sEscapeIndicatorsV30 = new int[]{
':', ';', ',', ' '
};
private static final int[] sEscapeIndicatorsV40 = new int[]{
';', ':'
};
/**
* <P>
* Returns String available as parameter value in vCard 3.0.
* </P>
* <P>
* RFC 2426 requires vCard composer to quote parameter values when it contains
* semi-colon, for example (See RFC 2426 for more information).
* This method checks whether the given String can be used without quotes.
* </P>
* <P>
* Note: We remove DQUOTE inside the given value silently for now.
* </P>
*/
public static String toStringAsV30ParamValue(String value) {
return toStringAsParamValue(value, sEscapeIndicatorsV30);
}
public static String toStringAsV40ParamValue(String value) {
return toStringAsParamValue(value, sEscapeIndicatorsV40);
}
private static String toStringAsParamValue(String value, final int[] escapeIndicators) {
if (TextUtils.isEmpty(value)) {
value = "";
}
final int asciiFirst = 0x20;
final int asciiLast = 0x7E; // included
final StringBuilder builder = new StringBuilder();
final int length = value.length();
boolean needQuote = false;
for (int i = 0; i < length; i = value.offsetByCodePoints(i, 1)) {
final int codePoint = value.codePointAt(i);
if (codePoint < asciiFirst || codePoint == '"') {
// CTL characters and DQUOTE are never accepted. Remove them.
continue;
}
builder.appendCodePoint(codePoint);
for (int indicator : escapeIndicators) {
if (codePoint == indicator) {
needQuote = true;
break;
}
}
}
final String result = builder.toString();
return ((result.isEmpty() || VCardUtils.containsOnlyWhiteSpaces(result))
? ""
: (needQuote ? ('"' + result + '"')
: result));
}
public static String toHalfWidthString(final String orgString) {
if (TextUtils.isEmpty(orgString)) {
return null;
}
final StringBuilder builder = new StringBuilder();
final int length = orgString.length();
for (int i = 0; i < length; i = orgString.offsetByCodePoints(i, 1)) {
// All Japanese character is able to be expressed by char.
// Do not need to use String#codepPointAt().
final char ch = orgString.charAt(i);
final String halfWidthText = JapaneseUtils.tryGetHalfWidthText(ch);
if (halfWidthText != null) {
builder.append(halfWidthText);
} else {
builder.append(ch);
}
}
return builder.toString();
}
/**
* Guesses the format of input image. Currently just the first few bytes are used.
* The type "GIF", "PNG", or "JPEG" is returned when possible. Returns null when
* the guess failed.
* @param input Image as byte array.
* @return The image type or null when the type cannot be determined.
*/
public static String guessImageType(final byte[] input) {
if (input == null) {
return null;
}
if (input.length >= 3 && input[0] == 'G' && input[1] == 'I' && input[2] == 'F') {
return "GIF";
} else if (input.length >= 4 && input[0] == (byte) 0x89
&& input[1] == 'P' && input[2] == 'N' && input[3] == 'G') {
// Note: vCard 2.1 officially does not support PNG, but we may have it and
// using X- word like "X-PNG" may not let importers know it is PNG.
// So we use the String "PNG" as is...
return "PNG";
} else if (input.length >= 2 && input[0] == (byte) 0xff
&& input[1] == (byte) 0xd8) {
return "JPEG";
} else {
return null;
}
}
/**
* @return True when all the given values are null or empty Strings.
*/
public static boolean areAllEmpty(final String...values) {
if (values == null) {
return true;
}
for (final String value : values) {
if (!TextUtils.isEmpty(value)) {
return false;
}
}
return true;
}
//// The methods bellow may be used by unit test.
/**
* Unquotes given Quoted-Printable value. value must not be null.
*/
public static String parseQuotedPrintable(
final String value, boolean strictLineBreaking,
String sourceCharset, String targetCharset) {
// "= " -> " ", "=\t" -> "\t".
// Previous code had done this replacement. Keep on the safe side.
final String quotedPrintable;
{
final StringBuilder builder = new StringBuilder();
final int length = value.length();
for (int i = 0; i < length; i++) {
char ch = value.charAt(i);
if (ch == '=' && i < length - 1) {
char nextCh = value.charAt(i + 1);
if (nextCh == ' ' || nextCh == '\t') {
builder.append(nextCh);
i++;
continue;
}
}
builder.append(ch);
}
quotedPrintable = builder.toString();
}
String[] lines;
if (strictLineBreaking) {
lines = quotedPrintable.split("\r\n");
} else {
StringBuilder builder = new StringBuilder();
final int length = quotedPrintable.length();
ArrayList<String> list = new ArrayList<String>();
for (int i = 0; i < length; i++) {
char ch = quotedPrintable.charAt(i);
if (ch == '\n') {
list.add(builder.toString());
builder = new StringBuilder();
} else if (ch == '\r') {
list.add(builder.toString());
builder = new StringBuilder();
if (i < length - 1) {
char nextCh = quotedPrintable.charAt(i + 1);
if (nextCh == '\n') {
i++;
}
}
} else {
builder.append(ch);
}
}
final String lastLine = builder.toString();
if (lastLine.length() > 0) {
list.add(lastLine);
}
lines = list.toArray(new String[0]);
}
final StringBuilder builder = new StringBuilder();
for (String line : lines) {
if (line.endsWith("=")) {
line = line.substring(0, line.length() - 1);
}
builder.append(line);
}
final String rawString = builder.toString();
if (TextUtils.isEmpty(rawString)) {
Log.w(LOG_TAG, "Given raw string is empty.");
}
byte[] rawBytes = null;
try {
rawBytes = rawString.getBytes(sourceCharset);
} catch (UnsupportedEncodingException e) {
Log.w(LOG_TAG, "Failed to decode: " + sourceCharset);
rawBytes = rawString.getBytes();
}
byte[] decodedBytes = null;
try {
decodedBytes = QuotedPrintableCodec.decodeQuotedPrintable(rawBytes);
} catch (DecoderException e) {
Log.e(LOG_TAG, "DecoderException is thrown.");
decodedBytes = rawBytes;
}
try {
return new String(decodedBytes, targetCharset);
} catch (UnsupportedEncodingException e) {
Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset);
return new String(decodedBytes);
}
}
public static final VCardParser getAppropriateParser(int vcardType)
throws VCardException {
if (VCardConfig.isVersion21(vcardType)) {
return new VCardParser_V21();
} else if (VCardConfig.isVersion30(vcardType)) {
return new VCardParser_V30();
} else if (VCardConfig.isVersion40(vcardType)) {
return new VCardParser_V40();
} else {
throw new VCardException("Version is not specified");
}
}
public static final String convertStringCharset(
String originalString, String sourceCharset, String targetCharset) {
if (sourceCharset.equalsIgnoreCase(targetCharset)) {
return originalString;
}
final Charset charset = Charset.forName(sourceCharset);
final ByteBuffer byteBuffer = charset.encode(originalString);
// byteBuffer.array() "may" return byte array which is larger than
// byteBuffer.remaining(). Here, we keep on the safe side.
final byte[] bytes = new byte[byteBuffer.remaining()];
byteBuffer.get(bytes);
try {
return new String(bytes, targetCharset);
} catch (UnsupportedEncodingException e) {
Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset);
return null;
}
}
// TODO: utilities for vCard 4.0: datetime, timestamp, integer, float, and boolean
private VCardUtils() {
}
}

View File

@ -0,0 +1,27 @@
/*
* 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 android.pim.vcard.exception;
public class VCardAgentNotSupportedException extends VCardNotSupportedException {
public VCardAgentNotSupportedException() {
super();
}
public VCardAgentNotSupportedException(String message) {
super(message);
}
}

View File

@ -0,0 +1,35 @@
/*
* 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 android.pim.vcard.exception;
public class VCardException extends java.lang.Exception {
/**
* Constructs a VCardException object
*/
public VCardException() {
super();
}
/**
* Constructs a VCardException object
*
* @param message the error message
*/
public VCardException(String message) {
super(message);
}
}

View File

@ -0,0 +1,32 @@
/*
* 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 android.pim.vcard.exception;
/**
* Thrown when the vCard has some line starting with '#'. In the specification,
* both vCard 2.1 and vCard 3.0 does not allow such line, but some actual exporter emit
* such lines.
*/
public class VCardInvalidCommentLineException extends VCardInvalidLineException {
public VCardInvalidCommentLineException() {
super();
}
public VCardInvalidCommentLineException(final String message) {
super(message);
}
}

View File

@ -0,0 +1,31 @@
/*
* 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 android.pim.vcard.exception;
/**
* Thrown when the vCard has some line starting with '#'. In the specification,
* both vCard 2.1 and vCard 3.0 does not allow such line, but some actual exporter emit
* such lines.
*/
public class VCardInvalidLineException extends VCardException {
public VCardInvalidLineException() {
super();
}
public VCardInvalidLineException(final String message) {
super(message);
}
}

View File

@ -0,0 +1,29 @@
/*
* 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 android.pim.vcard.exception;
/**
* VCardException thrown when VCard is nested without VCardParser's being notified.
*/
public class VCardNestedException extends VCardNotSupportedException {
public VCardNestedException() {
super();
}
public VCardNestedException(String message) {
super(message);
}
}

View File

@ -0,0 +1,33 @@
/*
* 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 android.pim.vcard.exception;
/**
* The exception which tells that the input VCard is probably valid from the view of
* specification but not supported in the current framework for now.
*
* This is a kind of a good news from the view of development.
* It may be good to ask users to send a report with the VCard example
* for the future development.
*/
public class VCardNotSupportedException extends VCardException {
public VCardNotSupportedException() {
super();
}
public VCardNotSupportedException(String message) {
super(message);
}
}

View File

@ -0,0 +1,28 @@
/*
* 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 android.pim.vcard.exception;
/**
* VCardException used only when the version of the vCard is different.
*/
public class VCardVersionException extends VCardException {
public VCardVersionException() {
super();
}
public VCardVersionException(String message) {
super(message);
}
}

View File

@ -0,0 +1,5 @@
<HTML>
<BODY>
{@hide}
</BODY>
</HTML>

View File

@ -0,0 +1,5 @@
<HTML>
<BODY>
{@hide}
</BODY>
</HTML>