Make vCard code a separated static library.
- Move the library to a separate directory in framewokr/base, and rename its package from android.pim.vcard to com.android.vcard. - Move all tests for the library under the directory. - Confirm all tests for vCard are successful. It would be better for us to have this directory somewhere else (like external/). But I'll submit this here now and move it to the right place as soon as possible. From the view of build mechanism, we can do that immediately. BUG: 2689523 Change-Id: I435e10571b7160bfcc029bed7c37aaac1c6fd69a
This commit is contained in:
parent
9b0be73f2a
commit
e6cbafd07d
65 changed files with 13491 additions and 0 deletions
28
vcard/Android.mk
Normal file
28
vcard/Android.mk
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# 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.
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE := com.android.vcard
|
||||
LOCAL_SRC_FILES := $(call all-java-files-under, java)
|
||||
|
||||
# Use google-common instead of android-common for using hidden code in telephony library.
|
||||
# Use ext for using Quoted-Printable codec.
|
||||
LOCAL_JAVA_LIBRARIES := google-common ext
|
||||
|
||||
include $(BUILD_STATIC_JAVA_LIBRARY)
|
||||
|
||||
# Build the test package.
|
||||
include $(call all-makefiles-under, $(LOCAL_PATH))
|
||||
379
vcard/java/com/android/vcard/JapaneseUtils.java
Normal file
379
vcard/java/com/android/vcard/JapaneseUtils.java
Normal 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 com.android.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
1996
vcard/java/com/android/vcard/VCardBuilder.java
Normal file
1996
vcard/java/com/android/vcard/VCardBuilder.java
Normal file
File diff suppressed because it is too large
Load diff
677
vcard/java/com/android/vcard/VCardComposer.java
Normal file
677
vcard/java/com/android/vcard/VCardComposer.java
Normal file
|
|
@ -0,0 +1,677 @@
|
|||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.android.vcard;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.Entity;
|
||||
import android.content.EntityIterator;
|
||||
import android.content.Entity.NamedContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteException;
|
||||
import android.net.Uri;
|
||||
import android.provider.ContactsContract.Contacts;
|
||||
import android.provider.ContactsContract.Data;
|
||||
import android.provider.ContactsContract.RawContacts;
|
||||
import android.provider.ContactsContract.RawContactsEntity;
|
||||
import android.provider.ContactsContract.CommonDataKinds.Email;
|
||||
import android.provider.ContactsContract.CommonDataKinds.Event;
|
||||
import android.provider.ContactsContract.CommonDataKinds.Im;
|
||||
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.text.TextUtils;
|
||||
import android.util.CharsetUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.vcard.exception.VCardException;
|
||||
|
||||
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.isV30(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))
|
||||
.appendPhotos(contentValuesListMap.get(Photo.CONTENT_ITEM_TYPE))
|
||||
.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;
|
||||
}
|
||||
}
|
||||
478
vcard/java/com/android/vcard/VCardConfig.java
Normal file
478
vcard/java/com/android/vcard/VCardConfig.java
Normal file
|
|
@ -0,0 +1,478 @@
|
|||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.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 a given vCard file
|
||||
* at first, while we have to decode sime encoded data (e.g. BASE64) to binary.
|
||||
* In order to avoid "misinterpretation" of charset as much as possible,
|
||||
* "ISO-8859-1" (a.k.a Latin-1) is first used for reading a stream.
|
||||
* When charset is specified in a property (with "CHARSET=..." parameter),
|
||||
* the string is decoded to raw bytes and encoded into the specific charset,
|
||||
* assuming "ISO-8859-1" is able to map "all" 8bit characters to some unicode,
|
||||
* and it has 1 to 1 mapping in all 8bit characters.
|
||||
* If the assumption is not correct, this setting will cause some bug.
|
||||
* </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";
|
||||
|
||||
public static final int FLAG_V21 = 0;
|
||||
public static final int FLAG_V30 = 1;
|
||||
|
||||
// 0x2 is reserved for the future use ...
|
||||
|
||||
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. ////
|
||||
|
||||
/**
|
||||
* <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 =
|
||||
(FLAG_V21 | 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 =
|
||||
(FLAG_V30 | NAME_ORDER_DEFAULT | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
|
||||
|
||||
/* package */ static final String VCARD_TYPE_V30_GENERIC_STR = "v30_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 =
|
||||
(FLAG_V21 | 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 =
|
||||
(FLAG_V30 | 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 =
|
||||
(FLAG_V21 | 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 =
|
||||
(FLAG_V30 | 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 =
|
||||
(FLAG_V21 | 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 isV30(final int vcardType) {
|
||||
return ((vcardType & FLAG_V30) != 0);
|
||||
}
|
||||
|
||||
public static boolean shouldUseQuotedPrintable(final int vcardType) {
|
||||
return !isV30(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 (isV30(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() {
|
||||
}
|
||||
}
|
||||
160
vcard/java/com/android/vcard/VCardConstants.java
Normal file
160
vcard/java/com/android/vcard/VCardConstants.java
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.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";
|
||||
|
||||
// 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
|
||||
public static final String PROPERTY_END = "END";
|
||||
|
||||
// Valid property names not supported (not appropriately handled) by our vCard importer now.
|
||||
public static final String PROPERTY_REV = "REV";
|
||||
public static final String PROPERTY_AGENT = "AGENT";
|
||||
|
||||
// Available in vCard 3.0. Shoud not use when composing vCard 2.1 file.
|
||||
public static final String PROPERTY_NAME = "NAME";
|
||||
public static final String PROPERTY_NICKNAME = "NICKNAME";
|
||||
public static final String PROPERTY_SORT_STRING = "SORT-STRING";
|
||||
|
||||
// De-fact property values expressing phonetic names.
|
||||
public static final String PROPERTY_X_PHONETIC_FIRST_NAME = "X-PHONETIC-FIRST-NAME";
|
||||
public static final String PROPERTY_X_PHONETIC_MIDDLE_NAME = "X-PHONETIC-MIDDLE-NAME";
|
||||
public static final String PROPERTY_X_PHONETIC_LAST_NAME = "X-PHONETIC-LAST-NAME";
|
||||
|
||||
// Properties both ContactsStruct in Eclair and de-fact vCard extensions
|
||||
// shown in http://en.wikipedia.org/wiki/VCard support are defined here.
|
||||
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";
|
||||
|
||||
// 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";
|
||||
|
||||
// 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";
|
||||
|
||||
/* package */ 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() {
|
||||
}
|
||||
}
|
||||
1423
vcard/java/com/android/vcard/VCardEntry.java
Normal file
1423
vcard/java/com/android/vcard/VCardEntry.java
Normal file
File diff suppressed because it is too large
Load diff
68
vcard/java/com/android/vcard/VCardEntryCommitter.java
Normal file
68
vcard/java/com/android/vcard/VCardEntryCommitter.java
Normal 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 com.android.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;
|
||||
}
|
||||
}
|
||||
240
vcard/java/com/android/vcard/VCardEntryConstructor.java
Normal file
240
vcard/java/com/android/vcard/VCardEntryConstructor.java
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.vcard;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Base64;
|
||||
import android.util.CharsetUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
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, null, false);
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
public void start() {
|
||||
for (VCardEntryHandler entryHandler : mEntryHandlers) {
|
||||
entryHandler.onStart();
|
||||
}
|
||||
}
|
||||
|
||||
public void end() {
|
||||
for (VCardEntryHandler entryHandler : mEntryHandlers) {
|
||||
entryHandler.onEnd();
|
||||
}
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
mCurrentVCardEntry = null;
|
||||
mCurrentProperty = new VCardEntry.Property();
|
||||
}
|
||||
|
||||
public void startEntry() {
|
||||
if (mCurrentVCardEntry != null) {
|
||||
Log.e(LOG_TAG, "Nested VCard code is not supported now.");
|
||||
}
|
||||
mCurrentVCardEntry = new VCardEntry(mVCardType, mAccount);
|
||||
}
|
||||
|
||||
public void endEntry() {
|
||||
mCurrentVCardEntry.consolidateFields();
|
||||
for (VCardEntryHandler entryHandler : mEntryHandlers) {
|
||||
entryHandler.onEntryCreated(mCurrentVCardEntry);
|
||||
}
|
||||
mCurrentVCardEntry = null;
|
||||
}
|
||||
|
||||
public void startProperty() {
|
||||
mCurrentProperty.clear();
|
||||
}
|
||||
|
||||
public void endProperty() {
|
||||
mCurrentVCardEntry.addProperty(mCurrentProperty);
|
||||
}
|
||||
|
||||
public void propertyName(String name) {
|
||||
mCurrentProperty.setPropertyName(name);
|
||||
}
|
||||
|
||||
public void propertyGroup(String group) {
|
||||
}
|
||||
|
||||
public void propertyParamType(String type) {
|
||||
if (mParamType != null) {
|
||||
Log.e(LOG_TAG, "propertyParamType() is called more than once " +
|
||||
"before propertyParamValue() is called");
|
||||
}
|
||||
mParamType = type;
|
||||
}
|
||||
|
||||
public void propertyParamValue(String value) {
|
||||
if (mParamType == null) {
|
||||
// From vCard 2.1 specification. vCard 3.0 formally does not allow this case.
|
||||
mParamType = "TYPE";
|
||||
}
|
||||
mCurrentProperty.addParameter(mParamType, value);
|
||||
mParamType = null;
|
||||
}
|
||||
|
||||
private static String encodeToSystemCharset(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 {
|
||||
String ret = new String(bytes, targetCharset);
|
||||
return ret;
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String handleOneValue(String value,
|
||||
String sourceCharset, String targetCharset, String encoding) {
|
||||
if (value == null) {
|
||||
Log.w(LOG_TAG, "Null is given.");
|
||||
value = "";
|
||||
}
|
||||
|
||||
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 encodeToSystemCharset(value, sourceCharset, targetCharset);
|
||||
}
|
||||
|
||||
public void propertyValues(List<String> values) {
|
||||
if (values == null || values.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Collection<String> charsetCollection = mCurrentProperty.getParameters("CHARSET");
|
||||
final Collection<String> encodingCollection = mCurrentProperty.getParameters("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");
|
||||
}
|
||||
}
|
||||
63
vcard/java/com/android/vcard/VCardEntryCounter.java
Normal file
63
vcard/java/com/android/vcard/VCardEntryCounter.java
Normal 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 com.android.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) {
|
||||
}
|
||||
}
|
||||
43
vcard/java/com/android/vcard/VCardEntryHandler.java
Normal file
43
vcard/java/com/android/vcard/VCardEntryHandler.java
Normal 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 com.android.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();
|
||||
}
|
||||
102
vcard/java/com/android/vcard/VCardInterpreter.java
Normal file
102
vcard/java/com/android/vcard/VCardInterpreter.java
Normal 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 com.android.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);
|
||||
}
|
||||
102
vcard/java/com/android/vcard/VCardInterpreterCollection.java
Normal file
102
vcard/java/com/android/vcard/VCardInterpreterCollection.java
Normal 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 com.android.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
55
vcard/java/com/android/vcard/VCardParser.java
Normal file
55
vcard/java/com/android/vcard/VCardParser.java
Normal 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 com.android.vcard;
|
||||
|
||||
import com.android.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();
|
||||
}
|
||||
968
vcard/java/com/android/vcard/VCardParserImpl_V21.java
Normal file
968
vcard/java/com/android/vcard/VCardParserImpl_V21.java
Normal file
|
|
@ -0,0 +1,968 @@
|
|||
/*
|
||||
* 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 com.android.vcard;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.vcard.exception.VCardAgentNotSupportedException;
|
||||
import com.android.vcard.exception.VCardException;
|
||||
import com.android.vcard.exception.VCardInvalidCommentLineException;
|
||||
import com.android.vcard.exception.VCardInvalidLineException;
|
||||
import com.android.vcard.exception.VCardNestedException;
|
||||
import com.android.vcard.exception.VCardVersionException;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Basic implementation achieving vCard parsing. Based on vCard 2.1,
|
||||
* </p>
|
||||
* @hide
|
||||
*/
|
||||
/* package */ class VCardParserImpl_V21 {
|
||||
private static final String LOG_TAG = "VCardParserImpl_V21";
|
||||
|
||||
private static final class CustomBufferedReader extends BufferedReader {
|
||||
private long mTime;
|
||||
|
||||
public CustomBufferedReader(Reader in) {
|
||||
super(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readLine() throws IOException {
|
||||
long start = System.currentTimeMillis();
|
||||
String ret = super.readLine();
|
||||
long end = System.currentTimeMillis();
|
||||
mTime += end - start;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public long getTotalmillisecond() {
|
||||
return mTime;
|
||||
}
|
||||
}
|
||||
|
||||
private static final String sDefaultEncoding = "8BIT";
|
||||
|
||||
protected boolean mCanceled;
|
||||
protected VCardInterpreter mInterpreter;
|
||||
|
||||
protected final String mImportCharset;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* The encoding type for deconding byte streams. This member variable is
|
||||
* reset to a default encoding every time when a new item comes.
|
||||
* </p>
|
||||
* <p>
|
||||
* "Encoding" in vCard is different from "Charset". It is mainly used for
|
||||
* addresses, notes, images. "7BIT", "8BIT", "BASE64", and
|
||||
* "QUOTED-PRINTABLE" are known examples.
|
||||
* </p>
|
||||
*/
|
||||
protected String mCurrentEncoding;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* The reader object to be used internally.
|
||||
* </p>
|
||||
* <p>
|
||||
* Developers should not directly read a line from this object. Use
|
||||
* getLine() unless there some reason.
|
||||
* </p>
|
||||
*/
|
||||
protected BufferedReader mReader;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Set for storing unkonwn TYPE attributes, which is not acceptable in vCard
|
||||
* specification, but happens to be seen in real world vCard.
|
||||
* </p>
|
||||
*/
|
||||
protected final Set<String> mUnknownTypeSet = new HashSet<String>();
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Set for storing unkonwn VALUE attributes, which is not acceptable in
|
||||
* vCard specification, but happens to be seen in real world vCard.
|
||||
* </p>
|
||||
*/
|
||||
protected final Set<String> mUnknownValueSet = new HashSet<String>();
|
||||
|
||||
|
||||
// In some cases, vCard is nested. Currently, we only consider the most
|
||||
// interior vCard data.
|
||||
// See v21_foma_1.vcf in test directory for more information.
|
||||
// TODO: Don't ignore by using count, but read all of information outside vCard.
|
||||
private int mNestCount;
|
||||
|
||||
// Used only for parsing END:VCARD.
|
||||
private String mPreviousLine;
|
||||
|
||||
// For measuring performance.
|
||||
private long mTimeTotal;
|
||||
private long mTimeReadStartRecord;
|
||||
private long mTimeReadEndRecord;
|
||||
private long mTimeStartProperty;
|
||||
private long mTimeEndProperty;
|
||||
private long mTimeParseItems;
|
||||
private long mTimeParseLineAndHandleGroup;
|
||||
private long mTimeParsePropertyValues;
|
||||
private long mTimeParseAdrOrgN;
|
||||
private long mTimeHandleMiscPropertyValue;
|
||||
private long mTimeHandleQuotedPrintable;
|
||||
private long mTimeHandleBase64;
|
||||
|
||||
public VCardParserImpl_V21() {
|
||||
this(VCardConfig.VCARD_TYPE_DEFAULT, null);
|
||||
}
|
||||
|
||||
public VCardParserImpl_V21(int vcardType) {
|
||||
this(vcardType, null);
|
||||
}
|
||||
|
||||
public VCardParserImpl_V21(int vcardType, String importCharset) {
|
||||
if ((vcardType & VCardConfig.FLAG_TORELATE_NEST) != 0) {
|
||||
mNestCount = 1;
|
||||
}
|
||||
|
||||
mImportCharset = (!TextUtils.isEmpty(importCharset) ? importCharset :
|
||||
VCardConfig.DEFAULT_INTERMEDIATE_CHARSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Parses the file at the given position.
|
||||
* </p>
|
||||
*/
|
||||
// <pre class="prettyprint">vcard_file = [wsls] vcard [wsls]</pre>
|
||||
protected void parseVCardFile() throws IOException, VCardException {
|
||||
boolean readingFirstFile = true;
|
||||
while (true) {
|
||||
if (mCanceled) {
|
||||
break;
|
||||
}
|
||||
if (!parseOneVCard(readingFirstFile)) {
|
||||
break;
|
||||
}
|
||||
readingFirstFile = false;
|
||||
}
|
||||
|
||||
if (mNestCount > 0) {
|
||||
boolean useCache = true;
|
||||
for (int i = 0; i < mNestCount; i++) {
|
||||
readEndVCard(useCache, true);
|
||||
useCache = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true when a given property name is a valid property name.
|
||||
*/
|
||||
protected boolean isValidPropertyName(final String propertyName) {
|
||||
if (!(getKnownPropertyNameSet().contains(propertyName.toUpperCase()) ||
|
||||
propertyName.startsWith("X-"))
|
||||
&& !mUnknownTypeSet.contains(propertyName)) {
|
||||
mUnknownTypeSet.add(propertyName);
|
||||
Log.w(LOG_TAG, "Property name unsupported by vCard 2.1: " + propertyName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return String. It may be null, or its length may be 0
|
||||
* @throws IOException
|
||||
*/
|
||||
protected String getLine() throws IOException {
|
||||
return mReader.readLine();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return String with it's length > 0
|
||||
* @throws IOException
|
||||
* @throws VCardException when the stream reached end of line
|
||||
*/
|
||||
protected String getNonEmptyLine() throws IOException, VCardException {
|
||||
String line;
|
||||
while (true) {
|
||||
line = getLine();
|
||||
if (line == null) {
|
||||
throw new VCardException("Reached end of buffer.");
|
||||
} else if (line.trim().length() > 0) {
|
||||
return line;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* vcard = "BEGIN" [ws] ":" [ws] "VCARD" [ws] 1*CRLF
|
||||
* items *CRLF
|
||||
* "END" [ws] ":" [ws] "VCARD"
|
||||
*/
|
||||
private boolean parseOneVCard(boolean firstRead) throws IOException, VCardException {
|
||||
boolean allowGarbage = false;
|
||||
if (firstRead) {
|
||||
if (mNestCount > 0) {
|
||||
for (int i = 0; i < mNestCount; i++) {
|
||||
if (!readBeginVCard(allowGarbage)) {
|
||||
return false;
|
||||
}
|
||||
allowGarbage = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!readBeginVCard(allowGarbage)) {
|
||||
return false;
|
||||
}
|
||||
long start;
|
||||
if (mInterpreter != null) {
|
||||
start = System.currentTimeMillis();
|
||||
mInterpreter.startEntry();
|
||||
mTimeReadStartRecord += System.currentTimeMillis() - start;
|
||||
}
|
||||
start = System.currentTimeMillis();
|
||||
parseItems();
|
||||
mTimeParseItems += System.currentTimeMillis() - start;
|
||||
readEndVCard(true, false);
|
||||
if (mInterpreter != null) {
|
||||
start = System.currentTimeMillis();
|
||||
mInterpreter.endEntry();
|
||||
mTimeReadEndRecord += System.currentTimeMillis() - start;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True when successful. False when reaching the end of line
|
||||
* @throws IOException
|
||||
* @throws VCardException
|
||||
*/
|
||||
protected boolean readBeginVCard(boolean allowGarbage) throws IOException, VCardException {
|
||||
String line;
|
||||
do {
|
||||
while (true) {
|
||||
line = getLine();
|
||||
if (line == null) {
|
||||
return false;
|
||||
} else if (line.trim().length() > 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
String[] strArray = line.split(":", 2);
|
||||
int length = strArray.length;
|
||||
|
||||
// Though vCard 2.1/3.0 specification does not allow lower cases,
|
||||
// vCard file emitted by some external vCard expoter have such
|
||||
// invalid Strings.
|
||||
// So we allow it.
|
||||
// e.g. BEGIN:vCard
|
||||
if (length == 2 && strArray[0].trim().equalsIgnoreCase("BEGIN")
|
||||
&& strArray[1].trim().equalsIgnoreCase("VCARD")) {
|
||||
return true;
|
||||
} else if (!allowGarbage) {
|
||||
if (mNestCount > 0) {
|
||||
mPreviousLine = line;
|
||||
return false;
|
||||
} else {
|
||||
throw new VCardException("Expected String \"BEGIN:VCARD\" did not come "
|
||||
+ "(Instead, \"" + line + "\" came)");
|
||||
}
|
||||
}
|
||||
} while (allowGarbage);
|
||||
|
||||
throw new VCardException("Reached where must not be reached.");
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* The arguments useCache and allowGarbase are usually true and false
|
||||
* accordingly when this function is called outside this function itself.
|
||||
* </p>
|
||||
*
|
||||
* @param useCache When true, line is obtained from mPreviousline.
|
||||
* Otherwise, getLine() is used.
|
||||
* @param allowGarbage When true, ignore non "END:VCARD" line.
|
||||
* @throws IOException
|
||||
* @throws VCardException
|
||||
*/
|
||||
protected void readEndVCard(boolean useCache, boolean allowGarbage) throws IOException,
|
||||
VCardException {
|
||||
String line;
|
||||
do {
|
||||
if (useCache) {
|
||||
// Though vCard specification does not allow lower cases,
|
||||
// some data may have them, so we allow it.
|
||||
line = mPreviousLine;
|
||||
} else {
|
||||
while (true) {
|
||||
line = getLine();
|
||||
if (line == null) {
|
||||
throw new VCardException("Expected END:VCARD was not found.");
|
||||
} else if (line.trim().length() > 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String[] strArray = line.split(":", 2);
|
||||
if (strArray.length == 2 && strArray[0].trim().equalsIgnoreCase("END")
|
||||
&& strArray[1].trim().equalsIgnoreCase("VCARD")) {
|
||||
return;
|
||||
} else if (!allowGarbage) {
|
||||
throw new VCardException("END:VCARD != \"" + mPreviousLine + "\"");
|
||||
}
|
||||
useCache = false;
|
||||
} while (allowGarbage);
|
||||
}
|
||||
|
||||
/*
|
||||
* items = *CRLF item / item
|
||||
*/
|
||||
protected void parseItems() throws IOException, VCardException {
|
||||
boolean ended = false;
|
||||
|
||||
if (mInterpreter != null) {
|
||||
long start = System.currentTimeMillis();
|
||||
mInterpreter.startProperty();
|
||||
mTimeStartProperty += System.currentTimeMillis() - start;
|
||||
}
|
||||
ended = parseItem();
|
||||
if (mInterpreter != null && !ended) {
|
||||
long start = System.currentTimeMillis();
|
||||
mInterpreter.endProperty();
|
||||
mTimeEndProperty += System.currentTimeMillis() - start;
|
||||
}
|
||||
|
||||
while (!ended) {
|
||||
// follow VCARD ,it wont reach endProperty
|
||||
if (mInterpreter != null) {
|
||||
long start = System.currentTimeMillis();
|
||||
mInterpreter.startProperty();
|
||||
mTimeStartProperty += System.currentTimeMillis() - start;
|
||||
}
|
||||
try {
|
||||
ended = parseItem();
|
||||
} catch (VCardInvalidCommentLineException e) {
|
||||
Log.e(LOG_TAG, "Invalid line which looks like some comment was found. Ignored.");
|
||||
ended = false;
|
||||
}
|
||||
if (mInterpreter != null && !ended) {
|
||||
long start = System.currentTimeMillis();
|
||||
mInterpreter.endProperty();
|
||||
mTimeEndProperty += System.currentTimeMillis() - start;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* item = [groups "."] name [params] ":" value CRLF / [groups "."] "ADR"
|
||||
* [params] ":" addressparts CRLF / [groups "."] "ORG" [params] ":" orgparts
|
||||
* CRLF / [groups "."] "N" [params] ":" nameparts CRLF / [groups "."]
|
||||
* "AGENT" [params] ":" vcard CRLF
|
||||
*/
|
||||
protected boolean parseItem() throws IOException, VCardException {
|
||||
mCurrentEncoding = sDefaultEncoding;
|
||||
|
||||
final String line = getNonEmptyLine();
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
String[] propertyNameAndValue = separateLineAndHandleGroup(line);
|
||||
if (propertyNameAndValue == null) {
|
||||
return true;
|
||||
}
|
||||
if (propertyNameAndValue.length != 2) {
|
||||
throw new VCardInvalidLineException("Invalid line \"" + line + "\"");
|
||||
}
|
||||
String propertyName = propertyNameAndValue[0].toUpperCase();
|
||||
String propertyValue = propertyNameAndValue[1];
|
||||
|
||||
mTimeParseLineAndHandleGroup += System.currentTimeMillis() - start;
|
||||
|
||||
if (propertyName.equals("ADR") || propertyName.equals("ORG") || propertyName.equals("N")) {
|
||||
start = System.currentTimeMillis();
|
||||
handleMultiplePropertyValue(propertyName, propertyValue);
|
||||
mTimeParseAdrOrgN += System.currentTimeMillis() - start;
|
||||
return false;
|
||||
} else if (propertyName.equals("AGENT")) {
|
||||
handleAgent(propertyValue);
|
||||
return false;
|
||||
} else if (isValidPropertyName(propertyName)) {
|
||||
if (propertyName.equals("BEGIN")) {
|
||||
if (propertyValue.equals("VCARD")) {
|
||||
throw new VCardNestedException("This vCard has nested vCard data in it.");
|
||||
} else {
|
||||
throw new VCardException("Unknown BEGIN type: " + propertyValue);
|
||||
}
|
||||
} else if (propertyName.equals("VERSION") && !propertyValue.equals(getVersionString())) {
|
||||
throw new VCardVersionException("Incompatible version: " + propertyValue + " != "
|
||||
+ getVersionString());
|
||||
}
|
||||
start = System.currentTimeMillis();
|
||||
handlePropertyValue(propertyName, propertyValue);
|
||||
mTimeParsePropertyValues += System.currentTimeMillis() - start;
|
||||
return false;
|
||||
}
|
||||
|
||||
throw new VCardException("Unknown property name: \"" + propertyName + "\"");
|
||||
}
|
||||
|
||||
// For performance reason, the states for group and property name are merged into one.
|
||||
static private final int STATE_GROUP_OR_PROPERTY_NAME = 0;
|
||||
static private final int STATE_PARAMS = 1;
|
||||
// vCard 3.0 specification allows double-quoted parameters, while vCard 2.1 does not.
|
||||
static private final int STATE_PARAMS_IN_DQUOTE = 2;
|
||||
|
||||
protected String[] separateLineAndHandleGroup(String line) throws VCardException {
|
||||
final String[] propertyNameAndValue = new String[2];
|
||||
final int length = line.length();
|
||||
if (length > 0 && line.charAt(0) == '#') {
|
||||
throw new VCardInvalidCommentLineException();
|
||||
}
|
||||
|
||||
int state = STATE_GROUP_OR_PROPERTY_NAME;
|
||||
int nameIndex = 0;
|
||||
|
||||
// This loop is developed so that we don't have to take care of bottle neck here.
|
||||
// Refactor carefully when you need to do so.
|
||||
for (int i = 0; i < length; i++) {
|
||||
final char ch = line.charAt(i);
|
||||
switch (state) {
|
||||
case STATE_GROUP_OR_PROPERTY_NAME: {
|
||||
if (ch == ':') { // End of a property name.
|
||||
final String propertyName = line.substring(nameIndex, i);
|
||||
if (propertyName.equalsIgnoreCase("END")) {
|
||||
mPreviousLine = line;
|
||||
return null;
|
||||
}
|
||||
if (mInterpreter != null) {
|
||||
mInterpreter.propertyName(propertyName);
|
||||
}
|
||||
propertyNameAndValue[0] = propertyName;
|
||||
if (i < length - 1) {
|
||||
propertyNameAndValue[1] = line.substring(i + 1);
|
||||
} else {
|
||||
propertyNameAndValue[1] = "";
|
||||
}
|
||||
return propertyNameAndValue;
|
||||
} else if (ch == '.') { // Each group is followed by the dot.
|
||||
final String groupName = line.substring(nameIndex, i);
|
||||
if (groupName.length() == 0) {
|
||||
Log.w(LOG_TAG, "Empty group found. Ignoring.");
|
||||
} else if (mInterpreter != null) {
|
||||
mInterpreter.propertyGroup(groupName);
|
||||
}
|
||||
nameIndex = i + 1; // Next should be another group or a property name.
|
||||
} else if (ch == ';') { // End of property name and beginneng of parameters.
|
||||
final String propertyName = line.substring(nameIndex, i);
|
||||
if (propertyName.equalsIgnoreCase("END")) {
|
||||
mPreviousLine = line;
|
||||
return null;
|
||||
}
|
||||
if (mInterpreter != null) {
|
||||
mInterpreter.propertyName(propertyName);
|
||||
}
|
||||
propertyNameAndValue[0] = propertyName;
|
||||
nameIndex = i + 1;
|
||||
state = STATE_PARAMS; // Start parameter parsing.
|
||||
}
|
||||
break;
|
||||
}
|
||||
case STATE_PARAMS: {
|
||||
if (ch == '"') {
|
||||
if (VCardConstants.VERSION_V21.equalsIgnoreCase(getVersionString())) {
|
||||
Log.w(LOG_TAG, "Double-quoted params found in vCard 2.1. " +
|
||||
"Silently allow it");
|
||||
}
|
||||
state = STATE_PARAMS_IN_DQUOTE;
|
||||
} else if (ch == ';') { // Starts another param.
|
||||
handleParams(line.substring(nameIndex, i));
|
||||
nameIndex = i + 1;
|
||||
} else if (ch == ':') { // End of param and beginenning of values.
|
||||
handleParams(line.substring(nameIndex, i));
|
||||
if (i < length - 1) {
|
||||
propertyNameAndValue[1] = line.substring(i + 1);
|
||||
} else {
|
||||
propertyNameAndValue[1] = "";
|
||||
}
|
||||
return propertyNameAndValue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case STATE_PARAMS_IN_DQUOTE: {
|
||||
if (ch == '"') {
|
||||
if (VCardConstants.VERSION_V21.equalsIgnoreCase(getVersionString())) {
|
||||
Log.w(LOG_TAG, "Double-quoted params found in vCard 2.1. " +
|
||||
"Silently allow it");
|
||||
}
|
||||
state = STATE_PARAMS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new VCardInvalidLineException("Invalid line: \"" + line + "\"");
|
||||
}
|
||||
|
||||
/*
|
||||
* params = ";" [ws] paramlist paramlist = paramlist [ws] ";" [ws] param /
|
||||
* param param = "TYPE" [ws] "=" [ws] ptypeval / "VALUE" [ws] "=" [ws]
|
||||
* pvalueval / "ENCODING" [ws] "=" [ws] pencodingval / "CHARSET" [ws] "="
|
||||
* [ws] charsetval / "LANGUAGE" [ws] "=" [ws] langval / "X-" word [ws] "="
|
||||
* [ws] word / knowntype
|
||||
*/
|
||||
protected void handleParams(String params) throws VCardException {
|
||||
final String[] strArray = params.split("=", 2);
|
||||
if (strArray.length == 2) {
|
||||
final String paramName = strArray[0].trim().toUpperCase();
|
||||
String paramValue = strArray[1].trim();
|
||||
if (paramName.equals("TYPE")) {
|
||||
handleType(paramValue);
|
||||
} else if (paramName.equals("VALUE")) {
|
||||
handleValue(paramValue);
|
||||
} else if (paramName.equals("ENCODING")) {
|
||||
handleEncoding(paramValue);
|
||||
} else if (paramName.equals("CHARSET")) {
|
||||
handleCharset(paramValue);
|
||||
} else if (paramName.equals("LANGUAGE")) {
|
||||
handleLanguage(paramValue);
|
||||
} else if (paramName.startsWith("X-")) {
|
||||
handleAnyParam(paramName, paramValue);
|
||||
} else {
|
||||
throw new VCardException("Unknown type \"" + paramName + "\"");
|
||||
}
|
||||
} else {
|
||||
handleParamWithoutName(strArray[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* vCard 3.0 parser implementation may throw VCardException.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
protected void handleParamWithoutName(final String paramValue) throws VCardException {
|
||||
handleType(paramValue);
|
||||
}
|
||||
|
||||
/*
|
||||
* ptypeval = knowntype / "X-" word
|
||||
*/
|
||||
protected void handleType(final String ptypeval) {
|
||||
if (!(getKnownTypeSet().contains(ptypeval.toUpperCase())
|
||||
|| ptypeval.startsWith("X-"))
|
||||
&& !mUnknownTypeSet.contains(ptypeval)) {
|
||||
mUnknownTypeSet.add(ptypeval);
|
||||
Log.w(LOG_TAG, String.format("TYPE unsupported by %s: ", getVersion(), ptypeval));
|
||||
}
|
||||
if (mInterpreter != null) {
|
||||
mInterpreter.propertyParamType("TYPE");
|
||||
mInterpreter.propertyParamValue(ptypeval);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* pvalueval = "INLINE" / "URL" / "CONTENT-ID" / "CID" / "X-" word
|
||||
*/
|
||||
protected void handleValue(final String pvalueval) {
|
||||
if (!(getKnownValueSet().contains(pvalueval.toUpperCase())
|
||||
|| pvalueval.startsWith("X-")
|
||||
|| mUnknownValueSet.contains(pvalueval))) {
|
||||
mUnknownValueSet.add(pvalueval);
|
||||
Log.w(LOG_TAG, String.format(
|
||||
"The value unsupported by TYPE of %s: ", getVersion(), pvalueval));
|
||||
}
|
||||
if (mInterpreter != null) {
|
||||
mInterpreter.propertyParamType("VALUE");
|
||||
mInterpreter.propertyParamValue(pvalueval);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* pencodingval = "7BIT" / "8BIT" / "QUOTED-PRINTABLE" / "BASE64" / "X-" word
|
||||
*/
|
||||
protected void handleEncoding(String pencodingval) throws VCardException {
|
||||
if (getAvailableEncodingSet().contains(pencodingval) ||
|
||||
pencodingval.startsWith("X-")) {
|
||||
if (mInterpreter != null) {
|
||||
mInterpreter.propertyParamType("ENCODING");
|
||||
mInterpreter.propertyParamValue(pencodingval);
|
||||
}
|
||||
mCurrentEncoding = pencodingval;
|
||||
} else {
|
||||
throw new VCardException("Unknown encoding \"" + pencodingval + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* vCard 2.1 specification only allows us-ascii and iso-8859-xxx (See RFC 1521),
|
||||
* but recent vCard files often contain other charset like UTF-8, SHIFT_JIS, etc.
|
||||
* We allow any charset.
|
||||
* </p>
|
||||
*/
|
||||
protected void handleCharset(String charsetval) {
|
||||
if (mInterpreter != null) {
|
||||
mInterpreter.propertyParamType("CHARSET");
|
||||
mInterpreter.propertyParamValue(charsetval);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See also Section 7.1 of RFC 1521
|
||||
*/
|
||||
protected void handleLanguage(String langval) throws VCardException {
|
||||
String[] strArray = langval.split("-");
|
||||
if (strArray.length != 2) {
|
||||
throw new VCardException("Invalid Language: \"" + langval + "\"");
|
||||
}
|
||||
String tmp = strArray[0];
|
||||
int length = tmp.length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (!isAsciiLetter(tmp.charAt(i))) {
|
||||
throw new VCardException("Invalid Language: \"" + langval + "\"");
|
||||
}
|
||||
}
|
||||
tmp = strArray[1];
|
||||
length = tmp.length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (!isAsciiLetter(tmp.charAt(i))) {
|
||||
throw new VCardException("Invalid Language: \"" + langval + "\"");
|
||||
}
|
||||
}
|
||||
if (mInterpreter != null) {
|
||||
mInterpreter.propertyParamType("LANGUAGE");
|
||||
mInterpreter.propertyParamValue(langval);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAsciiLetter(char ch) {
|
||||
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mainly for "X-" type. This accepts any kind of type without check.
|
||||
*/
|
||||
protected void handleAnyParam(String paramName, String paramValue) {
|
||||
if (mInterpreter != null) {
|
||||
mInterpreter.propertyParamType(paramName);
|
||||
mInterpreter.propertyParamValue(paramValue);
|
||||
}
|
||||
}
|
||||
|
||||
protected void handlePropertyValue(String propertyName, String propertyValue)
|
||||
throws IOException, VCardException {
|
||||
final String upperEncoding = mCurrentEncoding.toUpperCase();
|
||||
if (upperEncoding.equals(VCardConstants.PARAM_ENCODING_QP)) {
|
||||
final long start = System.currentTimeMillis();
|
||||
final String result = getQuotedPrintable(propertyValue);
|
||||
if (mInterpreter != null) {
|
||||
ArrayList<String> v = new ArrayList<String>();
|
||||
v.add(result);
|
||||
mInterpreter.propertyValues(v);
|
||||
}
|
||||
mTimeHandleQuotedPrintable += System.currentTimeMillis() - start;
|
||||
} else if (upperEncoding.equals(VCardConstants.PARAM_ENCODING_BASE64)
|
||||
|| upperEncoding.equals(VCardConstants.PARAM_ENCODING_B)) {
|
||||
final long start = System.currentTimeMillis();
|
||||
// It is very rare, but some BASE64 data may be so big that
|
||||
// OutOfMemoryError occurs. To ignore such cases, use try-catch.
|
||||
try {
|
||||
final String result = getBase64(propertyValue);
|
||||
if (mInterpreter != null) {
|
||||
ArrayList<String> arrayList = new ArrayList<String>();
|
||||
arrayList.add(result);
|
||||
mInterpreter.propertyValues(arrayList);
|
||||
}
|
||||
} catch (OutOfMemoryError error) {
|
||||
Log.e(LOG_TAG, "OutOfMemoryError happened during parsing BASE64 data!");
|
||||
if (mInterpreter != null) {
|
||||
mInterpreter.propertyValues(null);
|
||||
}
|
||||
}
|
||||
mTimeHandleBase64 += System.currentTimeMillis() - start;
|
||||
} else {
|
||||
if (!(upperEncoding.equals("7BIT") || upperEncoding.equals("8BIT") ||
|
||||
upperEncoding.startsWith("X-"))) {
|
||||
Log.w(LOG_TAG,
|
||||
String.format("The encoding \"%s\" is unsupported by vCard %s",
|
||||
mCurrentEncoding, getVersionString()));
|
||||
}
|
||||
|
||||
final long start = System.currentTimeMillis();
|
||||
if (mInterpreter != null) {
|
||||
ArrayList<String> v = new ArrayList<String>();
|
||||
v.add(maybeUnescapeText(propertyValue));
|
||||
mInterpreter.propertyValues(v);
|
||||
}
|
||||
mTimeHandleMiscPropertyValue += System.currentTimeMillis() - start;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Parses and returns Quoted-Printable.
|
||||
* </p>
|
||||
*
|
||||
* @param firstString The string following a parameter name and attributes.
|
||||
* Example: "string" in
|
||||
* "ADR:ENCODING=QUOTED-PRINTABLE:string\n\r".
|
||||
* @return whole Quoted-Printable string, including a given argument and
|
||||
* following lines. Excludes the last empty line following to Quoted
|
||||
* Printable lines.
|
||||
* @throws IOException
|
||||
* @throws VCardException
|
||||
*/
|
||||
private String getQuotedPrintable(String firstString) throws IOException, VCardException {
|
||||
// Specifically, there may be some padding between = and CRLF.
|
||||
// See the following:
|
||||
//
|
||||
// qp-line := *(qp-segment transport-padding CRLF)
|
||||
// qp-part transport-padding
|
||||
// qp-segment := qp-section *(SPACE / TAB) "="
|
||||
// ; Maximum length of 76 characters
|
||||
//
|
||||
// e.g. (from RFC 2045)
|
||||
// Now's the time =
|
||||
// for all folk to come=
|
||||
// to the aid of their country.
|
||||
if (firstString.trim().endsWith("=")) {
|
||||
// remove "transport-padding"
|
||||
int pos = firstString.length() - 1;
|
||||
while (firstString.charAt(pos) != '=') {
|
||||
}
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(firstString.substring(0, pos + 1));
|
||||
builder.append("\r\n");
|
||||
String line;
|
||||
while (true) {
|
||||
line = getLine();
|
||||
if (line == null) {
|
||||
throw new VCardException("File ended during parsing a Quoted-Printable String");
|
||||
}
|
||||
if (line.trim().endsWith("=")) {
|
||||
// remove "transport-padding"
|
||||
pos = line.length() - 1;
|
||||
while (line.charAt(pos) != '=') {
|
||||
}
|
||||
builder.append(line.substring(0, pos + 1));
|
||||
builder.append("\r\n");
|
||||
} else {
|
||||
builder.append(line);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
} else {
|
||||
return firstString;
|
||||
}
|
||||
}
|
||||
|
||||
protected String getBase64(String firstString) throws IOException, VCardException {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(firstString);
|
||||
|
||||
while (true) {
|
||||
String line = getLine();
|
||||
if (line == null) {
|
||||
throw new VCardException("File ended during parsing BASE64 binary");
|
||||
}
|
||||
if (line.length() == 0) {
|
||||
break;
|
||||
}
|
||||
builder.append(line);
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Mainly for "ADR", "ORG", and "N"
|
||||
* </p>
|
||||
*/
|
||||
/*
|
||||
* addressparts = 0*6(strnosemi ";") strnosemi ; PO Box, Extended Addr,
|
||||
* Street, Locality, Region, Postal Code, Country Name orgparts =
|
||||
* *(strnosemi ";") strnosemi ; First is Organization Name, remainder are
|
||||
* Organization Units. nameparts = 0*4(strnosemi ";") strnosemi ; Family,
|
||||
* Given, Middle, Prefix, Suffix. ; Example:Public;John;Q.;Reverend Dr.;III,
|
||||
* Esq. strnosemi = *(*nonsemi ("\;" / "\" CRLF)) *nonsemi ; To include a
|
||||
* semicolon in this string, it must be escaped ; with a "\" character. We
|
||||
* do not care the number of "strnosemi" here. We are not sure whether we
|
||||
* should add "\" CRLF to each value. We exclude them for now.
|
||||
*/
|
||||
protected void handleMultiplePropertyValue(String propertyName, String propertyValue)
|
||||
throws IOException, VCardException {
|
||||
// vCard 2.1 does not allow QUOTED-PRINTABLE here, but some
|
||||
// softwares/devices
|
||||
// emit such data.
|
||||
if (mCurrentEncoding.equalsIgnoreCase("QUOTED-PRINTABLE")) {
|
||||
propertyValue = getQuotedPrintable(propertyValue);
|
||||
}
|
||||
|
||||
if (mInterpreter != null) {
|
||||
mInterpreter.propertyValues(VCardUtils.constructListFromValue(propertyValue,
|
||||
(getVersion() == VCardConfig.FLAG_V30)));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* vCard 2.1 specifies AGENT allows one vcard entry. Currently we emit an
|
||||
* error toward the AGENT property.
|
||||
* // TODO: Support AGENT property.
|
||||
* item =
|
||||
* ... / [groups "."] "AGENT" [params] ":" vcard CRLF vcard = "BEGIN" [ws]
|
||||
* ":" [ws] "VCARD" [ws] 1*CRLF items *CRLF "END" [ws] ":" [ws] "VCARD"
|
||||
*/
|
||||
protected void handleAgent(final String propertyValue) throws VCardException {
|
||||
if (!propertyValue.toUpperCase().contains("BEGIN:VCARD")) {
|
||||
// Apparently invalid line seen in Windows Mobile 6.5. Ignore them.
|
||||
return;
|
||||
} else {
|
||||
throw new VCardAgentNotSupportedException("AGENT Property is not supported now.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For vCard 3.0.
|
||||
*/
|
||||
protected String maybeUnescapeText(final String text) {
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns unescaped String if the character should be unescaped. Return
|
||||
* null otherwise. e.g. In vCard 2.1, "\;" should be unescaped into ";"
|
||||
* while "\x" should not be.
|
||||
*/
|
||||
protected String maybeUnescapeCharacter(final char ch) {
|
||||
return unescapeCharacter(ch);
|
||||
}
|
||||
|
||||
/* package */ static String unescapeCharacter(final char ch) {
|
||||
// Original vCard 2.1 specification does not allow transformation
|
||||
// "\:" -> ":", "\," -> ",", and "\\" -> "\", but previous
|
||||
// implementation of
|
||||
// this class allowed them, so keep it as is.
|
||||
if (ch == '\\' || ch == ';' || ch == ':' || ch == ',') {
|
||||
return String.valueOf(ch);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void showPerformanceInfo() {
|
||||
Log.d(LOG_TAG, "Total parsing time: " + mTimeTotal + " ms");
|
||||
if (mReader instanceof CustomBufferedReader) {
|
||||
Log.d(LOG_TAG, "Total readLine time: "
|
||||
+ ((CustomBufferedReader) mReader).getTotalmillisecond() + " ms");
|
||||
}
|
||||
Log.d(LOG_TAG, "Time for handling the beggining of the record: " + mTimeReadStartRecord
|
||||
+ " ms");
|
||||
Log.d(LOG_TAG, "Time for handling the end of the record: " + mTimeReadEndRecord + " ms");
|
||||
Log.d(LOG_TAG, "Time for parsing line, and handling group: " + mTimeParseLineAndHandleGroup
|
||||
+ " ms");
|
||||
Log.d(LOG_TAG, "Time for parsing ADR, ORG, and N fields:" + mTimeParseAdrOrgN + " ms");
|
||||
Log.d(LOG_TAG, "Time for parsing property values: " + mTimeParsePropertyValues + " ms");
|
||||
Log.d(LOG_TAG, "Time for handling normal property values: " + mTimeHandleMiscPropertyValue
|
||||
+ " ms");
|
||||
Log.d(LOG_TAG, "Time for handling Quoted-Printable: " + mTimeHandleQuotedPrintable + " ms");
|
||||
Log.d(LOG_TAG, "Time for handling Base64: " + mTimeHandleBase64 + " ms");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link VCardConfig#FLAG_V21}
|
||||
*/
|
||||
protected int getVersion() {
|
||||
return VCardConfig.FLAG_V21;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link VCardConfig#FLAG_V30}
|
||||
*/
|
||||
protected String getVersionString() {
|
||||
return VCardConstants.VERSION_V21;
|
||||
}
|
||||
|
||||
protected Set<String> getKnownPropertyNameSet() {
|
||||
return VCardParser_V21.sKnownPropertyNameSet;
|
||||
}
|
||||
|
||||
protected Set<String> getKnownTypeSet() {
|
||||
return VCardParser_V21.sKnownTypeSet;
|
||||
}
|
||||
|
||||
protected Set<String> getKnownValueSet() {
|
||||
return VCardParser_V21.sKnownValueSet;
|
||||
}
|
||||
|
||||
protected Set<String> getAvailableEncodingSet() {
|
||||
return VCardParser_V21.sAvailableEncoding;
|
||||
}
|
||||
|
||||
protected String getDefaultEncoding() {
|
||||
return sDefaultEncoding;
|
||||
}
|
||||
|
||||
|
||||
public void parse(InputStream is, VCardInterpreter interpreter)
|
||||
throws IOException, VCardException {
|
||||
if (is == null) {
|
||||
throw new NullPointerException("InputStream must not be null.");
|
||||
}
|
||||
|
||||
final InputStreamReader tmpReader = new InputStreamReader(is, mImportCharset);
|
||||
if (VCardConfig.showPerformanceLog()) {
|
||||
mReader = new CustomBufferedReader(tmpReader);
|
||||
} else {
|
||||
mReader = new BufferedReader(tmpReader);
|
||||
}
|
||||
|
||||
mInterpreter = interpreter;
|
||||
|
||||
final long start = System.currentTimeMillis();
|
||||
if (mInterpreter != null) {
|
||||
mInterpreter.start();
|
||||
}
|
||||
parseVCardFile();
|
||||
if (mInterpreter != null) {
|
||||
mInterpreter.end();
|
||||
}
|
||||
mTimeTotal += System.currentTimeMillis() - start;
|
||||
|
||||
if (VCardConfig.showPerformanceLog()) {
|
||||
showPerformanceInfo();
|
||||
}
|
||||
}
|
||||
|
||||
public final void cancel() {
|
||||
mCanceled = true;
|
||||
}
|
||||
}
|
||||
317
vcard/java/com/android/vcard/VCardParserImpl_V30.java
Normal file
317
vcard/java/com/android/vcard/VCardParserImpl_V30.java
Normal file
|
|
@ -0,0 +1,317 @@
|
|||
/*
|
||||
* 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 com.android.vcard;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.vcard.exception.VCardException;
|
||||
|
||||
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, null);
|
||||
}
|
||||
|
||||
public VCardParserImpl_V30(int vcardType, String importCharset) {
|
||||
super(vcardType, importCharset);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getVersion() {
|
||||
return VCardConfig.FLAG_V30;
|
||||
}
|
||||
|
||||
@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) {
|
||||
super.handleAnyParam(paramName, paramValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleParamWithoutName(final String paramValue) throws VCardException {
|
||||
super.handleParamWithoutName(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
|
||||
*/
|
||||
@Override
|
||||
protected void handleType(final String ptypevalues) {
|
||||
String[] ptypeArray = ptypevalues.split(",");
|
||||
mInterpreter.propertyParamType("TYPE");
|
||||
for (String value : ptypeArray) {
|
||||
int length = value.length();
|
||||
if (length >= 2 && value.startsWith("\"") && value.endsWith("\"")) {
|
||||
mInterpreter.propertyParamValue(value.substring(1, value.length() - 1));
|
||||
} else {
|
||||
mInterpreter.propertyParamValue(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
113
vcard/java/com/android/vcard/VCardParser_V21.java
Normal file
113
vcard/java/com/android/vcard/VCardParser_V21.java
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.vcard;
|
||||
|
||||
import com.android.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 VCardParser_V21(int parseType, String inputCharset) {
|
||||
mVCardParserImpl = new VCardParserImpl_V21(parseType, null);
|
||||
}
|
||||
|
||||
public void parse(InputStream is, VCardInterpreter interepreter)
|
||||
throws IOException, VCardException {
|
||||
mVCardParserImpl.parse(is, interepreter);
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
mVCardParserImpl.cancel();
|
||||
}
|
||||
}
|
||||
91
vcard/java/com/android/vcard/VCardParser_V30.java
Normal file
91
vcard/java/com/android/vcard/VCardParser_V30.java
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.vcard;
|
||||
|
||||
import com.android.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 VCardParser_V30(int vcardType, String importCharset) {
|
||||
mVCardParserImpl = new VCardParserImpl_V30(vcardType, importCharset);
|
||||
}
|
||||
|
||||
public void parse(InputStream is, VCardInterpreter interepreter)
|
||||
throws IOException, VCardException {
|
||||
mVCardParserImpl.parse(is, interepreter);
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
mVCardParserImpl.cancel();
|
||||
}
|
||||
}
|
||||
172
vcard/java/com/android/vcard/VCardSourceDetector.java
Normal file
172
vcard/java/com/android/vcard/VCardSourceDetector.java
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.vcard;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
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 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";
|
||||
|
||||
|
||||
// TODO: Should replace this with types in VCardConfig
|
||||
private 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.
|
||||
|
||||
// Some mobile phones (like FOMA) tells us the charset of the data.
|
||||
private boolean mNeedParseSpecifiedCharset;
|
||||
private String mSpecifiedCharset;
|
||||
|
||||
public void start() {
|
||||
}
|
||||
|
||||
public void end() {
|
||||
}
|
||||
|
||||
public void startEntry() {
|
||||
}
|
||||
|
||||
public void startProperty() {
|
||||
mNeedParseSpecifiedCharset = false;
|
||||
}
|
||||
|
||||
public void endProperty() {
|
||||
}
|
||||
|
||||
public void endEntry() {
|
||||
}
|
||||
|
||||
public void propertyGroup(String group) {
|
||||
}
|
||||
|
||||
public void propertyName(String name) {
|
||||
if (name.equalsIgnoreCase(TYPE_FOMA_CHARSET_SIGN)) {
|
||||
mParseType = PARSE_TYPE_DOCOMO_TORELATE_NEST;
|
||||
// Probably Shift_JIS is used, but we should double confirm.
|
||||
mNeedParseSpecifiedCharset = 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 (mNeedParseSpecifiedCharset && 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:
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
658
vcard/java/com/android/vcard/VCardUtils.java
Normal file
658
vcard/java/com/android/vcard/VCardUtils.java
Normal file
|
|
@ -0,0 +1,658 @@
|
|||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.vcard;
|
||||
|
||||
import org.apache.commons.codec.DecoderException;
|
||||
import org.apache.commons.codec.net.QuotedPrintableCodec;
|
||||
|
||||
import android.content.ContentProviderOperation;
|
||||
import android.provider.ContactsContract.Data;
|
||||
import android.provider.ContactsContract.CommonDataKinds.Im;
|
||||
import android.provider.ContactsContract.CommonDataKinds.Phone;
|
||||
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
|
||||
import android.telephony.PhoneNumberUtils;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
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();
|
||||
}
|
||||
|
||||
public static List<String> constructListFromValue(final String value,
|
||||
final boolean isV30) {
|
||||
final List<String> list = new ArrayList<String>();
|
||||
StringBuilder builder = new StringBuilder();
|
||||
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 =
|
||||
(isV30 ? VCardParserImpl_V30.unescapeCharacter(nextCh) :
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* <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 = <any printable 7bit us-ascii except []=:., >
|
||||
* </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;
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public static String parseQuotedPrintable(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);
|
||||
}
|
||||
}
|
||||
|
||||
private VCardUtils() {
|
||||
}
|
||||
}
|
||||
|
|
@ -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 com.android.vcard.exception;
|
||||
|
||||
public class VCardAgentNotSupportedException extends VCardNotSupportedException {
|
||||
public VCardAgentNotSupportedException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public VCardAgentNotSupportedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
35
vcard/java/com/android/vcard/exception/VCardException.java
Normal file
35
vcard/java/com/android/vcard/exception/VCardException.java
Normal 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 com.android.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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 com.android.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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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 com.android.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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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 com.android.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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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 com.android.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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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 com.android.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);
|
||||
}
|
||||
}
|
||||
25
vcard/tests/Android.mk
Normal file
25
vcard/tests/Android.mk
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
# 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.
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_CERTIFICATE := platform
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
LOCAL_PACKAGE_NAME := AndroidVCardTests
|
||||
LOCAL_SRC_FILES := $(call all-java-files-under, src)
|
||||
LOCAL_JAVA_LIBRARIES := android.test.runner google-common
|
||||
LOCAL_STATIC_JAVA_LIBRARIES := com.android.vcard
|
||||
|
||||
include $(BUILD_PACKAGE)
|
||||
30
vcard/tests/AndroidManifest.xml
Normal file
30
vcard/tests/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.vcard.tests"
|
||||
android:sharedUserId="com.android.uid.test">
|
||||
|
||||
<application>
|
||||
<uses-library android:name="android.test.runner" />
|
||||
</application>
|
||||
|
||||
<!-- Run tests with "runtest common" -->
|
||||
<instrumentation android:name="android.test.InstrumentationTestRunner"
|
||||
android:targetPackage="com.android.vcard.tests"
|
||||
android:label="Android vCard Library Tests" />
|
||||
|
||||
</manifest>
|
||||
5
vcard/tests/res/raw/v21_backslash.vcf
Normal file
5
vcard/tests/res/raw/v21_backslash.vcf
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
BEGIN:VCARD
|
||||
VERSION:2.1
|
||||
N:;A\;B\\;C\\\;;D;\:E;\\\\;
|
||||
FN:A;B\C\;D:E\\
|
||||
END:VCARD
|
||||
106
vcard/tests/res/raw/v21_complicated.vcf
Normal file
106
vcard/tests/res/raw/v21_complicated.vcf
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
BEGIN:VCARD
|
||||
VERSION:2.1
|
||||
N:Gump;Forrest;Hoge;Pos;Tao
|
||||
FN:Joe Due
|
||||
ORG:Gump Shrimp Co.;Sales Dept.\;Manager;Fish keeper
|
||||
ROLE:Fish Cake Keeper!
|
||||
X-CLASS:PUBLIC
|
||||
TITLE:Shrimp Man
|
||||
TEL;WORK;VOICE:(111) 555-1212
|
||||
TEL;HOME;VOICE:(404) 555-1212
|
||||
TEL;CELL:0311111111
|
||||
TEL;VIDEO:0322222222
|
||||
TEL;VOICE:0333333333
|
||||
ADR;WORK:;;100 Waters Edge;Baytown;LA;30314;United States of America
|
||||
LABEL;WORK;ENCODING=QUOTED-PRINTABLE:100 Waters Edge=0D=0ABaytown, LA 30314=0D=0AUnited States of America
|
||||
ADR;HOME:;;42 Plantation St.;Baytown;LA;30314;United States of America
|
||||
LABEL;HOME;ENCODING=QUOTED-PRINTABLE:42 Plantation St.=0D=0A=
|
||||
Baytown, LA 30314=0D=0A=
|
||||
United States of America
|
||||
EMAIL;PREF;INTERNET:forrestgump@walladalla.com
|
||||
EMAIL;CELL:cell@example.com
|
||||
NOTE:The following note is the example from RFC 2045.
|
||||
NOTE;ENCODING=QUOTED-PRINTABLE:Now's the time =
|
||||
for all folk to come=
|
||||
to the aid of their country.
|
||||
|
||||
PHOTO;ENCODING=BASE64;TYPE=JPEG:
|
||||
/9j/4QoPRXhpZgAATU0AKgAAAAgADQEOAAIAAAAPAAAAqgEPAAIAAAAHAAAAugEQAAIAAAAG
|
||||
AAAAwgESAAMAAAABAAEAAAEaAAUAAAABAAAAyAEbAAUAAAABAAAA0AEoAAMAAAABAAIAAAEx
|
||||
AAIAAAAOAAAA2AEyAAIAAAAUAAAA5gITAAMAAAABAAEAAIKYAAIAAAAOAAAA+odpAAQAAAAB
|
||||
AAABhMSlAAcAAAB8AAABCAAABB4yMDA4MTAyOTEzNTUzMQAARG9Db01vAABEOTA1aQAAAABI
|
||||
AAAAAQAAAEgAAAABRDkwNWkgVmVyMS4wMAAyMDA4OjEwOjI5IDEzOjU1OjQ3ACAgICAgICAg
|
||||
ICAgICAAUHJpbnRJTQAwMzAwAAAABgABABQAFAACAQAAAAADAAAANAEABQAAAAEBAQAAAAEQ
|
||||
gAAAAAAAEQkAACcQAAAPCwAAJxAAAAWXAAAnEAAACLAAACcQAAAcAQAAJxAAAAJeAAAnEAAA
|
||||
AIsAACcQAAADywAAJxAAABvlAAAnEAAogpoABQAAAAEAAANqgp0ABQAAAAEAAANyiCIAAwAA
|
||||
AAEAAgAAkAAABwAAAAQwMjIwkAMAAgAAABQAAAN6kAQAAgAAABQAAAOOkQEABwAAAAQBAgMA
|
||||
kQIABQAAAAEAAAOikgEACgAAAAEAAAOqkgIABQAAAAEAAAOykgQACgAAAAEAAAO6kgUABQAA
|
||||
AAEAAAPCkgcAAwAAAAEAAgAAkggAAwAAAAEAAAAAkgkAAwAAAAEAAAAAkgoABQAAAAEAAAPK
|
||||
knwABwAAAAEAAAAAkoYABwAAABYAAAPSoAAABwAAAAQwMTAwoAEAAwAAAAEAAQAAoAIAAwAA
|
||||
AAEAYAAAoAMAAwAAAAEASAAAoAUABAAAAAEAAAQAog4ABQAAAAEAAAPoog8ABQAAAAEAAAPw
|
||||
ohAAAwAAAAEAAgAAohcAAwAAAAEAAgAAowAABwAAAAEDAAAAowEABwAAAAEBAAAApAEAAwAA
|
||||
AAEAAAAApAIAAwAAAAEAAAAApAMAAwAAAAEAAAAApAQABQAAAAEAAAP4pAUAAwAAAAEAHQAA
|
||||
pAYAAwAAAAEAAAAApAcAAwAAAAEAAAAApAgAAwAAAAEAAAAApAkAAwAAAAEAAAAApAoAAwAA
|
||||
AAEAAAAApAwAAwAAAAEAAgAAAAAAAAAAAFMAACcQAAABXgAAAGQyMDA4OjEwOjI5IDEzOjU1
|
||||
OjMxADIwMDg6MTA6MjkgMTM6NTU6NDcAAAApiAAAGwAAAAKyAAAAZAAAAV4AAABkAAAAAAAA
|
||||
AGQAAAAlAAAACgAADpIAAAPoAAAAAAAAAAAyMDA4MTAyOTEzNTUzMQAAICoAAAAKAAAq4gAA
|
||||
AAoAAAAAAAAAAQACAAEAAgAAAARSOTgAAAIABwAAAAQwMTAwAAAAAAAGAQMAAwAAAAEABgAA
|
||||
ARoABQAAAAEAAARsARsABQAAAAEAAAR0ASgAAwAAAAEAAgAAAgEABAAAAAEAAAR8AgIABAAA
|
||||
AAEAAAWLAAAAAAAAAEgAAAABAAAASAAAAAH/2P/bAIQAIBYYHBgUIBwaHCQiICYwUDQwLCww
|
||||
YkZKOlB0Znp4cmZwboCQuJyAiK6KbnCg2qKuvsTO0M58muLy4MjwuMrOxgEiJCQwKjBeNDRe
|
||||
xoRwhMbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbG
|
||||
/8AAEQgAeACgAwEhAAIRAQMRAf/EAaIAAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKCxAA
|
||||
AgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkK
|
||||
FhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWG
|
||||
h4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl
|
||||
5ufo6erx8vP09fb3+Pn6AQADAQEBAQEBAQEBAAAAAAAAAQIDBAUGBwgJCgsRAAIBAgQEAwQH
|
||||
BQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBka
|
||||
JicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKT
|
||||
lJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz
|
||||
9PX29/j5+v/aAAwDAQACEQMRAD8AFFSqKkZIoqRVpgSKKeBTEOApwFADsUYpgIRSEUANIppF
|
||||
ICNhUTCgCMio2FICJhULCgC0oqVaAJFFSqKBkgFOApiHCnCgB2KMUCENJQA0imEUDGMKiYUA
|
||||
RtUbUgIWqJhQBZSpFoAlWpVoGPFPFMQ7tSK2ODQA4yKO9HmKe9FxAzDHFIOlAAaYaAGNUTUD
|
||||
ImqNqQETVE1AE6VKKAJFNSqaAHg08GmANIFFQM5Y5qJMBuT60ZNQIcrkVYSQMKuLGKaaasQx
|
||||
qiagZE1RtSAjaomoAkQ1KpoAlU1IpoAkU07OBTArO+5qkV12Y71lfUBmaKkCRSuznrTFba2a
|
||||
oCwGyM0E1qIjY1GxoGRNUZNICNqiagByGplNAEimpFNMB4YDvSucpxSYEIU04KazsAu1qArU
|
||||
WELtPpTSposBNETt5pxNaoCNjUbGgCNjUZoGRtUTUgFU1KpoAkBqQHigCFnO7rUqOdlZp6gA
|
||||
c+tODn1pXAXzD60eYfWncQvmNSGQ07gOMhCVEJGz1ptgS5yKYxqwGE1GxoAiamGkMapqVTQB
|
||||
Kpp+eKAICfmqWM/Kaz6gANOBqQFzRmmAuaTNACsfkqMHmm9wJs8U0mtRDGNRsaAI2phpDI1N
|
||||
SqaAJFNSA8UCISfmqSM/Kaz6jAHmnA1ICg0uaAFzSZpgKx+SmDrTe4E2eKaTWoiMmmMaAIzT
|
||||
DSGRKakU0ASKaeDTERseakjPyms+oxAacDUgOBpc0gFzSZpgOY/KKYv3qrqIlpprQBjGoyaA
|
||||
GGmmkMgU1IppgPBqQGgQu0Gn4wvFKwEQpwNZDHZpc0ALmigRKBleaQKBWtgA001QDGqM0gGm
|
||||
mGkMrqakBoAepp4NMRIDTwaAE2A008GokgHxjd1pzKFpW0uAg5NSBBTirgOpDWgDTTTQAw0w
|
||||
0gGGmmgZWBp4pASKaeDTEOBp4NADwajbrUyBEkXWnSUdAGr1qeiAMSkNWAhphoAaaYaQDDTT
|
||||
SGVRTwaYDxTwaBDwaeDQA4GlK5oauIeo20pGaLaAKqgU6hKwBSGmAhphoAaaYaQxhpppDKgN
|
||||
PFMB4p4oEPFOBpgPBp4NAhwpwoAWloAKSgBDTTQMYaYaQDTTTSGA/9n/2wCEAAoHBwgHBgoI
|
||||
CAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4+JS5ESUM8SDc9
|
||||
PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7
|
||||
Ozs7Ozs7Ozs7Ozs7O//AABEIAEgAYAMBIQACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAA
|
||||
AQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNC
|
||||
scEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hp
|
||||
anN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS
|
||||
09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcI
|
||||
CQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVi
|
||||
ctEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4
|
||||
eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY
|
||||
2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/AJ7SLgcVr20I4rNFGvbQAAHFaEUX
|
||||
SrQi5HCMdKk8oY6VSJYx4hjpVaWMelAFC4iGDxWPdR8mkxmRdxjBrEvI+tZjN20xtHNbNqAc
|
||||
UIDXg6Cr0WKtCY8XKQOyzOB3FKNWsyceZ+lS6sY6NkjvPSdwImBHUmmy4q076oCjOODWPdgc
|
||||
0MpGPdAYNYl4o5rNjNKzkyorZtXxihAa1vIDip7m9Frb7/4jwKcnyxbEzN3ieJppZsyZ4U1H
|
||||
urzZau4mWVlNrGk0UuWPVa1YroXEIkHfrXZh5W90RWncAHmsi6bJNdQ0ZNw3BrGuiMGs2Mks
|
||||
puBzWzbzdOaEBeOpR2oUtkk9hTru7iuo4m8wgemKyqTi04sBsfkEf68j8KlUQZz9o/SuZRj3
|
||||
JYriAji4/Sp7W6htbV2aXcu70ramoxle4gN7HcIXjbis+4k5NdaaauhmVcv1rHuW61DGiG1m
|
||||
6c1s20/TmgAv5vmj57VKk3+ixnPc1xVV70h9CVJuOtSrL71hFgxzScUkkn+iY/2q1i9xDrGT
|
||||
9y31pJ5Otd1L+GhMy7mTrWXO2SapjRn28vTmta3nxjmgGOvJd2w1Kkv+ipz/ABGuOoveYdCe
|
||||
ObjrU6y5rlsA8ycUksn+ij/eNaw6iJLNsW59zTJn6816FP4EJmbO+Saz5m602UjIgk4HNadv
|
||||
LwKaBl+MpIMOMipp490SCJeF7CoqQvF2JuRqWQ4YEGrSiQJuKnHrXByMpki73GFBNXIoh9n2
|
||||
SrnnOK6MPTbd3sSwIVF2qMCqkzHmuy1lYRnTHrVGWpZaMKB+BWlbycYoQM0IZDxzV+GU8c1a
|
||||
IYy5Y+dnHatAsfsAHfArmS1mPoh1gT8x9qtk1rQX7tCe5DIapzGtGBQm71SlqGWjnIH6Vowt
|
||||
zmhAy/E3vV6F6tEMuxlWIyAfrVxCCAO1VZEEyYA4AApxNGwyJ+lVJRUsaKMw61SlFQzRAP/Z
|
||||
|
||||
X-ATTRIBUTE:Some String
|
||||
BDAY:19800101
|
||||
GEO:35.6563854,139.6994233
|
||||
URL:http://www.example.com/
|
||||
REV:20080424T195243Z
|
||||
END:VCARD
|
||||
10
vcard/tests/res/raw/v21_invalid_comment_line.vcf
Normal file
10
vcard/tests/res/raw/v21_invalid_comment_line.vcf
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
BEGIN:vCard
|
||||
VERSION:2.1
|
||||
UID:357
|
||||
N:;Conference Call
|
||||
FN:Conference Call
|
||||
# This line must be ignored.
|
||||
NOTE;ENCODING=QUOTED-PRINTABLE:This is an (sharp ->=
|
||||
#<- sharp) example. This message must NOT be ignored.
|
||||
# This line must be ignored too.
|
||||
END:vCard
|
||||
6
vcard/tests/res/raw/v21_japanese_1.vcf
Normal file
6
vcard/tests/res/raw/v21_japanese_1.vcf
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
BEGIN:VCARD
|
||||
VERSION:2.1
|
||||
N;CHARSET=SHIFT_JIS:安藤ロイド;;;;
|
||||
SOUND;X-IRMC-N;CHARSET=SHIFT_JIS:アンドウロイド;;;;
|
||||
TEL;PREF;VOICE:0300000000
|
||||
END:VCARD
|
||||
10
vcard/tests/res/raw/v21_japanese_2.vcf
Normal file
10
vcard/tests/res/raw/v21_japanese_2.vcf
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
BEGIN:VCARD
|
||||
VERSION:2.1
|
||||
FN;CHARSET=SHIFT_JIS:安藤 ロイド 1
|
||||
N;CHARSET=SHIFT_JIS:安藤;ロイド1;;;
|
||||
SOUND;X-IRMC-N;CHARSET=SHIFT_JIS:アンドウ;ロイド1;;;
|
||||
ADR;HOME;CHARSET=SHIFT_JIS;ENCODING=QUOTED-PRINTABLE:;=93=8C=8B=9E=93=73=
|
||||
=8F=61=92=4A=8B=E6=8D=F7=8B=75=92=AC26-1=83=5A=83=8B=83=8A=83=41=83=93=
|
||||
=83=5E=83=8F=81=5B6=8A=4B;;;;150-8512;
|
||||
NOTE;CHARSET=SHIFT_JIS;ENCODING=QUOTED-PRINTABLE:=83=81=83=82
|
||||
END:VCARD
|
||||
33
vcard/tests/res/raw/v21_multiple_entry.vcf
Normal file
33
vcard/tests/res/raw/v21_multiple_entry.vcf
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
BEGIN:VCARD
|
||||
VERSION:2.1
|
||||
N;CHARSET=SHIFT_JIS:安藤ロイド3;;;;
|
||||
SOUND;X-IRMC-N;CHARSET=SHIFT_JIS:アンドウロイド3;;;;
|
||||
TEL;X-NEC-SECRET:9
|
||||
TEL;X-NEC-HOTEL:10
|
||||
TEL;X-NEC-SCHOOL:11
|
||||
TEL;HOME;FAX:12
|
||||
END:VCARD
|
||||
|
||||
|
||||
BEGIN:VCARD
|
||||
VERSION:2.1
|
||||
N;CHARSET=SHIFT_JIS:安藤ロイド4;;;;
|
||||
SOUND;X-IRMC-N;CHARSET=SHIFT_JIS:アンドウロイド4;;;;
|
||||
TEL;MODEM:13
|
||||
TEL;PAGER:14
|
||||
TEL;X-NEC-FAMILY:15
|
||||
TEL;X-NEC-GIRL:16
|
||||
END:VCARD
|
||||
|
||||
|
||||
BEGIN:VCARD
|
||||
VERSION:2.1
|
||||
N;CHARSET=SHIFT_JIS:安藤ロイド5;;;;
|
||||
SOUND;X-IRMC-N;CHARSET=SHIFT_JIS:アンドウロイド5;;;;
|
||||
TEL;X-NEC-BOY:17
|
||||
TEL;X-NEC-FRIEND:18
|
||||
TEL;X-NEC-PHS:19
|
||||
TEL;X-NEC-RESTAURANT:20
|
||||
END:VCARD
|
||||
|
||||
|
||||
6
vcard/tests/res/raw/v21_org_before_title.vcf
Normal file
6
vcard/tests/res/raw/v21_org_before_title.vcf
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
BEGIN:VCARD
|
||||
VERSION:2.1
|
||||
FN:Normal Guy
|
||||
ORG:Company;Organization;Devision;Room;Sheet No.
|
||||
TITLE:Excellent Janitor
|
||||
END:VCARD
|
||||
15
vcard/tests/res/raw/v21_pref_handling.vcf
Normal file
15
vcard/tests/res/raw/v21_pref_handling.vcf
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
BEGIN:VCARD
|
||||
VERSION:2.1
|
||||
FN:Smith
|
||||
TEL;HOME:1
|
||||
TEL;WORK;PREF:2
|
||||
TEL;ISDN:3
|
||||
EMAIL;PREF;HOME:test@example.com
|
||||
EMAIL;CELL;PREF:test2@examination.com
|
||||
ORG:Company
|
||||
TITLE:Engineer
|
||||
ORG:Mystery
|
||||
TITLE:Blogger
|
||||
ORG:Poetry
|
||||
TITLE:Poet
|
||||
END:VCARD
|
||||
3
vcard/tests/res/raw/v21_simple_1.vcf
Normal file
3
vcard/tests/res/raw/v21_simple_1.vcf
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
BEGIN:VCARD
|
||||
N:Ando;Roid;
|
||||
END:VCARD
|
||||
3
vcard/tests/res/raw/v21_simple_2.vcf
Normal file
3
vcard/tests/res/raw/v21_simple_2.vcf
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
BEGIN:VCARD
|
||||
FN:Ando Roid
|
||||
END:VCARD
|
||||
4
vcard/tests/res/raw/v21_simple_3.vcf
Normal file
4
vcard/tests/res/raw/v21_simple_3.vcf
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
BEGIN:VCARD
|
||||
N:Ando;Roid;
|
||||
FN:Ando Roid
|
||||
END:VCARD
|
||||
6
vcard/tests/res/raw/v21_title_before_org.vcf
Normal file
6
vcard/tests/res/raw/v21_title_before_org.vcf
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
BEGIN:VCARD
|
||||
VERSION:2.1
|
||||
FN:Nice Guy
|
||||
TITLE:Cool Title
|
||||
ORG:Marverous;Perfect;Great;Good;Bad;Poor
|
||||
END:VCARD
|
||||
10
vcard/tests/res/raw/v21_winmo_65.vcf
Normal file
10
vcard/tests/res/raw/v21_winmo_65.vcf
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
BEGIN:VCARD
|
||||
VERSION:2.1
|
||||
N:Example;;;;
|
||||
FN:Example
|
||||
ANNIVERSARY;VALUE=DATE:20091010
|
||||
AGENT:Invalid line which must be handled correctly.
|
||||
X-CLASS:PUBLIC
|
||||
X-REDUCTION:
|
||||
X-NO:
|
||||
END:VCARD
|
||||
5
vcard/tests/res/raw/v30_comma_separated.vcf
Normal file
5
vcard/tests/res/raw/v30_comma_separated.vcf
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
BEGIN:VCARD
|
||||
VERSION:3.0
|
||||
N:F;G;M;;
|
||||
TEL;TYPE=PAGER,WORK,MSG:6101231234@pagersample.com
|
||||
END:VCARD
|
||||
13
vcard/tests/res/raw/v30_simple.vcf
Normal file
13
vcard/tests/res/raw/v30_simple.vcf
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
BEGIN:VCARD
|
||||
VERSION:3.0
|
||||
FN:And Roid
|
||||
N:And;Roid;;;
|
||||
ORG:Open;Handset; Alliance
|
||||
SORT-STRING:android
|
||||
TEL;TYPE=PREF;TYPE=VOICE:0300000000
|
||||
CLASS:PUBLIC
|
||||
X-GNO:0
|
||||
X-GN:group0
|
||||
X-REDUCTION:0
|
||||
REV:20081031T065854Z
|
||||
END:VCARD
|
||||
971
vcard/tests/src/com/android/vcard/tests/VCardExporterTests.java
Normal file
971
vcard/tests/src/com/android/vcard/tests/VCardExporterTests.java
Normal file
|
|
@ -0,0 +1,971 @@
|
|||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.vcard.tests;
|
||||
|
||||
import android.content.ContentValues;
|
||||
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 com.android.vcard.VCardConfig;
|
||||
import com.android.vcard.tests.test_utils.ContactEntry;
|
||||
import com.android.vcard.tests.test_utils.PropertyNodesVerifierElem;
|
||||
import com.android.vcard.tests.test_utils.PropertyNodesVerifierElem.TypeSet;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Tests for the code related to vCard exporter, inculding vCard composer.
|
||||
* This test class depends on vCard importer code, so if tests for vCard importer fail,
|
||||
* the result of this class will not be reliable.
|
||||
*/
|
||||
public class VCardExporterTests extends VCardTestsBase {
|
||||
private static final byte[] sPhotoByteArray =
|
||||
VCardImporterTests.sPhotoByteArrayForComplicatedCase;
|
||||
|
||||
public void testSimpleV21() {
|
||||
mVerifier.initForExportTest(V21);
|
||||
mVerifier.addInputEntry().addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.FAMILY_NAME, "Ando")
|
||||
.put(StructuredName.GIVEN_NAME, "Roid");
|
||||
mVerifier.addPropertyNodesVerifierElem()
|
||||
.addExpectedNode("FN", "Roid Ando")
|
||||
.addExpectedNode("N", "Ando;Roid;;;",
|
||||
Arrays.asList("Ando", "Roid", "", "", ""));
|
||||
}
|
||||
|
||||
private void testStructuredNameBasic(int vcardType) {
|
||||
final boolean isV30 = VCardConfig.isV30(vcardType);
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
mVerifier.addInputEntry().addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.FAMILY_NAME, "AppropriateFamilyName")
|
||||
.put(StructuredName.GIVEN_NAME, "AppropriateGivenName")
|
||||
.put(StructuredName.MIDDLE_NAME, "AppropriateMiddleName")
|
||||
.put(StructuredName.PREFIX, "AppropriatePrefix")
|
||||
.put(StructuredName.SUFFIX, "AppropriateSuffix")
|
||||
.put(StructuredName.PHONETIC_FAMILY_NAME, "AppropriatePhoneticFamily")
|
||||
.put(StructuredName.PHONETIC_GIVEN_NAME, "AppropriatePhoneticGiven")
|
||||
.put(StructuredName.PHONETIC_MIDDLE_NAME, "AppropriatePhoneticMiddle");
|
||||
|
||||
PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElem()
|
||||
.addExpectedNodeWithOrder("N",
|
||||
"AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;"
|
||||
+ "AppropriatePrefix;AppropriateSuffix",
|
||||
Arrays.asList("AppropriateFamilyName", "AppropriateGivenName",
|
||||
"AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix"))
|
||||
.addExpectedNodeWithOrder("FN",
|
||||
"AppropriatePrefix AppropriateGivenName "
|
||||
+ "AppropriateMiddleName AppropriateFamilyName AppropriateSuffix")
|
||||
.addExpectedNode("X-PHONETIC-FIRST-NAME", "AppropriatePhoneticGiven")
|
||||
.addExpectedNode("X-PHONETIC-MIDDLE-NAME", "AppropriatePhoneticMiddle")
|
||||
.addExpectedNode("X-PHONETIC-LAST-NAME", "AppropriatePhoneticFamily");
|
||||
|
||||
if (isV30) {
|
||||
elem.addExpectedNode("SORT-STRING",
|
||||
"AppropriatePhoneticGiven AppropriatePhoneticMiddle "
|
||||
+ "AppropriatePhoneticFamily");
|
||||
}
|
||||
}
|
||||
|
||||
public void testStructuredNameBasicV21() {
|
||||
testStructuredNameBasic(V21);
|
||||
}
|
||||
|
||||
public void testStructuredNameBasicV30() {
|
||||
testStructuredNameBasic(V30);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that only "primary" StructuredName is emitted, so that our vCard file
|
||||
* will not confuse the external importer, assuming there may be some importer
|
||||
* which presume that there's only one property toward each of "N", "FN", etc.
|
||||
* Note that more than one "N", "FN", etc. properties are acceptable in vCard spec.
|
||||
*/
|
||||
private void testStructuredNameUsePrimaryCommon(int vcardType) {
|
||||
final boolean isV30 = (vcardType == V30);
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName1")
|
||||
.put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName1")
|
||||
.put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName1")
|
||||
.put(StructuredName.PREFIX, "DoNotEmitPrefix1")
|
||||
.put(StructuredName.SUFFIX, "DoNotEmitSuffix1")
|
||||
.put(StructuredName.PHONETIC_FAMILY_NAME, "DoNotEmitPhoneticFamily1")
|
||||
.put(StructuredName.PHONETIC_GIVEN_NAME, "DoNotEmitPhoneticGiven1")
|
||||
.put(StructuredName.PHONETIC_MIDDLE_NAME, "DoNotEmitPhoneticMiddle1");
|
||||
|
||||
// With "IS_PRIMARY=1". This is what we should use.
|
||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.FAMILY_NAME, "AppropriateFamilyName")
|
||||
.put(StructuredName.GIVEN_NAME, "AppropriateGivenName")
|
||||
.put(StructuredName.MIDDLE_NAME, "AppropriateMiddleName")
|
||||
.put(StructuredName.PREFIX, "AppropriatePrefix")
|
||||
.put(StructuredName.SUFFIX, "AppropriateSuffix")
|
||||
.put(StructuredName.PHONETIC_FAMILY_NAME, "AppropriatePhoneticFamily")
|
||||
.put(StructuredName.PHONETIC_GIVEN_NAME, "AppropriatePhoneticGiven")
|
||||
.put(StructuredName.PHONETIC_MIDDLE_NAME, "AppropriatePhoneticMiddle")
|
||||
.put(StructuredName.IS_PRIMARY, 1);
|
||||
|
||||
// With "IS_PRIMARY=1", but we should ignore this time, since this is second, not first.
|
||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName2")
|
||||
.put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName2")
|
||||
.put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName2")
|
||||
.put(StructuredName.PREFIX, "DoNotEmitPrefix2")
|
||||
.put(StructuredName.SUFFIX, "DoNotEmitSuffix2")
|
||||
.put(StructuredName.PHONETIC_FAMILY_NAME, "DoNotEmitPhoneticFamily2")
|
||||
.put(StructuredName.PHONETIC_GIVEN_NAME, "DoNotEmitPhoneticGiven2")
|
||||
.put(StructuredName.PHONETIC_MIDDLE_NAME, "DoNotEmitPhoneticMiddle2")
|
||||
.put(StructuredName.IS_PRIMARY, 1);
|
||||
|
||||
PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElem()
|
||||
.addExpectedNodeWithOrder("N",
|
||||
"AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;"
|
||||
+ "AppropriatePrefix;AppropriateSuffix",
|
||||
Arrays.asList("AppropriateFamilyName", "AppropriateGivenName",
|
||||
"AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix"))
|
||||
.addExpectedNodeWithOrder("FN",
|
||||
"AppropriatePrefix AppropriateGivenName "
|
||||
+ "AppropriateMiddleName AppropriateFamilyName AppropriateSuffix")
|
||||
.addExpectedNode("X-PHONETIC-FIRST-NAME", "AppropriatePhoneticGiven")
|
||||
.addExpectedNode("X-PHONETIC-MIDDLE-NAME", "AppropriatePhoneticMiddle")
|
||||
.addExpectedNode("X-PHONETIC-LAST-NAME", "AppropriatePhoneticFamily");
|
||||
|
||||
if (isV30) {
|
||||
elem.addExpectedNode("SORT-STRING",
|
||||
"AppropriatePhoneticGiven AppropriatePhoneticMiddle "
|
||||
+ "AppropriatePhoneticFamily");
|
||||
}
|
||||
}
|
||||
|
||||
public void testStructuredNameUsePrimaryV21() {
|
||||
testStructuredNameUsePrimaryCommon(V21);
|
||||
}
|
||||
|
||||
public void testStructuredNameUsePrimaryV30() {
|
||||
testStructuredNameUsePrimaryCommon(V30);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that only "super primary" StructuredName is emitted.
|
||||
* See also the comment in {@link #testStructuredNameUsePrimaryCommon(int)}.
|
||||
*/
|
||||
private void testStructuredNameUseSuperPrimaryCommon(int vcardType) {
|
||||
final boolean isV30 = (vcardType == V30);
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName1")
|
||||
.put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName1")
|
||||
.put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName1")
|
||||
.put(StructuredName.PREFIX, "DoNotEmitPrefix1")
|
||||
.put(StructuredName.SUFFIX, "DoNotEmitSuffix1")
|
||||
.put(StructuredName.PHONETIC_FAMILY_NAME, "DoNotEmitPhoneticFamily1")
|
||||
.put(StructuredName.PHONETIC_GIVEN_NAME, "DoNotEmitPhoneticGiven1")
|
||||
.put(StructuredName.PHONETIC_MIDDLE_NAME, "DoNotEmitPhoneticMiddle1");
|
||||
|
||||
// With "IS_PRIMARY=1", but we should ignore this time.
|
||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName2")
|
||||
.put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName2")
|
||||
.put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName2")
|
||||
.put(StructuredName.PREFIX, "DoNotEmitPrefix2")
|
||||
.put(StructuredName.SUFFIX, "DoNotEmitSuffix2")
|
||||
.put(StructuredName.PHONETIC_FAMILY_NAME, "DoNotEmitPhoneticFamily2")
|
||||
.put(StructuredName.PHONETIC_GIVEN_NAME, "DoNotEmitPhoneticGiven2")
|
||||
.put(StructuredName.PHONETIC_MIDDLE_NAME, "DoNotEmitPhoneticMiddle2")
|
||||
.put(StructuredName.IS_PRIMARY, 1);
|
||||
|
||||
// With "IS_SUPER_PRIMARY=1". This is what we should use.
|
||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.FAMILY_NAME, "AppropriateFamilyName")
|
||||
.put(StructuredName.GIVEN_NAME, "AppropriateGivenName")
|
||||
.put(StructuredName.MIDDLE_NAME, "AppropriateMiddleName")
|
||||
.put(StructuredName.PREFIX, "AppropriatePrefix")
|
||||
.put(StructuredName.SUFFIX, "AppropriateSuffix")
|
||||
.put(StructuredName.PHONETIC_FAMILY_NAME, "AppropriatePhoneticFamily")
|
||||
.put(StructuredName.PHONETIC_GIVEN_NAME, "AppropriatePhoneticGiven")
|
||||
.put(StructuredName.PHONETIC_MIDDLE_NAME, "AppropriatePhoneticMiddle")
|
||||
.put(StructuredName.IS_SUPER_PRIMARY, 1);
|
||||
|
||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName3")
|
||||
.put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName3")
|
||||
.put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName3")
|
||||
.put(StructuredName.PREFIX, "DoNotEmitPrefix3")
|
||||
.put(StructuredName.SUFFIX, "DoNotEmitSuffix3")
|
||||
.put(StructuredName.PHONETIC_FAMILY_NAME, "DoNotEmitPhoneticFamily3")
|
||||
.put(StructuredName.PHONETIC_GIVEN_NAME, "DoNotEmitPhoneticGiven3")
|
||||
.put(StructuredName.PHONETIC_MIDDLE_NAME, "DoNotEmitPhoneticMiddle3")
|
||||
.put(StructuredName.IS_PRIMARY, 1);
|
||||
|
||||
PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElem()
|
||||
.addExpectedNodeWithOrder("N",
|
||||
"AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;"
|
||||
+ "AppropriatePrefix;AppropriateSuffix",
|
||||
Arrays.asList("AppropriateFamilyName", "AppropriateGivenName",
|
||||
"AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix"))
|
||||
.addExpectedNodeWithOrder("FN",
|
||||
"AppropriatePrefix AppropriateGivenName "
|
||||
+ "AppropriateMiddleName AppropriateFamilyName AppropriateSuffix")
|
||||
.addExpectedNode("X-PHONETIC-FIRST-NAME", "AppropriatePhoneticGiven")
|
||||
.addExpectedNode("X-PHONETIC-MIDDLE-NAME", "AppropriatePhoneticMiddle")
|
||||
.addExpectedNode("X-PHONETIC-LAST-NAME", "AppropriatePhoneticFamily");
|
||||
|
||||
if (isV30) {
|
||||
elem.addExpectedNode("SORT-STRING",
|
||||
"AppropriatePhoneticGiven AppropriatePhoneticMiddle"
|
||||
+ " AppropriatePhoneticFamily");
|
||||
}
|
||||
}
|
||||
|
||||
public void testStructuredNameUseSuperPrimaryV21() {
|
||||
testStructuredNameUseSuperPrimaryCommon(V21);
|
||||
}
|
||||
|
||||
public void testStructuredNameUseSuperPrimaryV30() {
|
||||
testStructuredNameUseSuperPrimaryCommon(V30);
|
||||
}
|
||||
|
||||
public void testNickNameV30() {
|
||||
mVerifier.initForExportTest(V30);
|
||||
mVerifier.addInputEntry().addContentValues(Nickname.CONTENT_ITEM_TYPE)
|
||||
.put(Nickname.NAME, "Nicky");
|
||||
|
||||
mVerifier.addPropertyNodesVerifierElemWithEmptyName()
|
||||
.addExpectedNodeWithOrder("NICKNAME", "Nicky");
|
||||
}
|
||||
|
||||
private void testPhoneBasicCommon(int vcardType) {
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
mVerifier.addInputEntry().addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||
.put(Phone.NUMBER, "1")
|
||||
.put(Phone.TYPE, Phone.TYPE_HOME);
|
||||
mVerifier.addPropertyNodesVerifierElemWithEmptyName()
|
||||
.addExpectedNode("TEL", "1", new TypeSet("HOME"));
|
||||
}
|
||||
|
||||
public void testPhoneBasicV21() {
|
||||
testPhoneBasicCommon(V21);
|
||||
}
|
||||
|
||||
public void testPhoneBasicV30() {
|
||||
testPhoneBasicCommon(V30);
|
||||
}
|
||||
|
||||
public void testPhoneRefrainFormatting() {
|
||||
mVerifier.initForExportTest(V21 | VCardConfig.FLAG_REFRAIN_PHONE_NUMBER_FORMATTING);
|
||||
mVerifier.addInputEntry().addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||
.put(Phone.NUMBER, "1234567890(abcdefghijklmnopqrstuvwxyz)")
|
||||
.put(Phone.TYPE, Phone.TYPE_HOME);
|
||||
mVerifier.addPropertyNodesVerifierElemWithEmptyName()
|
||||
.addExpectedNode("TEL", "1234567890(abcdefghijklmnopqrstuvwxyz)",
|
||||
new TypeSet("HOME"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that vCard composer emits corresponding type param which we expect.
|
||||
*/
|
||||
private void testPhoneVariousTypeSupport(int vcardType) {
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||
.put(Phone.NUMBER, "10")
|
||||
.put(Phone.TYPE, Phone.TYPE_HOME);
|
||||
entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||
.put(Phone.NUMBER, "20")
|
||||
.put(Phone.TYPE, Phone.TYPE_WORK);
|
||||
entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||
.put(Phone.NUMBER, "30")
|
||||
.put(Phone.TYPE, Phone.TYPE_FAX_HOME);
|
||||
entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||
.put(Phone.NUMBER, "40")
|
||||
.put(Phone.TYPE, Phone.TYPE_FAX_WORK);
|
||||
entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||
.put(Phone.NUMBER, "50")
|
||||
.put(Phone.TYPE, Phone.TYPE_MOBILE);
|
||||
entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||
.put(Phone.NUMBER, "60")
|
||||
.put(Phone.TYPE, Phone.TYPE_PAGER);
|
||||
entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||
.put(Phone.NUMBER, "70")
|
||||
.put(Phone.TYPE, Phone.TYPE_OTHER);
|
||||
entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||
.put(Phone.NUMBER, "80")
|
||||
.put(Phone.TYPE, Phone.TYPE_CAR);
|
||||
entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||
.put(Phone.NUMBER, "90")
|
||||
.put(Phone.TYPE, Phone.TYPE_COMPANY_MAIN);
|
||||
entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||
.put(Phone.NUMBER, "100")
|
||||
.put(Phone.TYPE, Phone.TYPE_ISDN);
|
||||
entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||
.put(Phone.NUMBER, "110")
|
||||
.put(Phone.TYPE, Phone.TYPE_MAIN);
|
||||
entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||
.put(Phone.NUMBER, "120")
|
||||
.put(Phone.TYPE, Phone.TYPE_OTHER_FAX);
|
||||
entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||
.put(Phone.NUMBER, "130")
|
||||
.put(Phone.TYPE, Phone.TYPE_TELEX);
|
||||
entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||
.put(Phone.NUMBER, "140")
|
||||
.put(Phone.TYPE, Phone.TYPE_WORK_MOBILE);
|
||||
entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||
.put(Phone.NUMBER, "150")
|
||||
.put(Phone.TYPE, Phone.TYPE_WORK_PAGER);
|
||||
entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||
.put(Phone.NUMBER, "160")
|
||||
.put(Phone.TYPE, Phone.TYPE_MMS);
|
||||
|
||||
mVerifier.addPropertyNodesVerifierElemWithEmptyName()
|
||||
.addExpectedNode("TEL", "10", new TypeSet("HOME"))
|
||||
.addExpectedNode("TEL", "20", new TypeSet("WORK"))
|
||||
.addExpectedNode("TEL", "30", new TypeSet("HOME", "FAX"))
|
||||
.addExpectedNode("TEL", "40", new TypeSet("WORK", "FAX"))
|
||||
.addExpectedNode("TEL", "50", new TypeSet("CELL"))
|
||||
.addExpectedNode("TEL", "60", new TypeSet("PAGER"))
|
||||
.addExpectedNode("TEL", "70", new TypeSet("VOICE"))
|
||||
.addExpectedNode("TEL", "80", new TypeSet("CAR"))
|
||||
.addExpectedNode("TEL", "90", new TypeSet("WORK", "PREF"))
|
||||
.addExpectedNode("TEL", "100", new TypeSet("ISDN"))
|
||||
.addExpectedNode("TEL", "110", new TypeSet("PREF"))
|
||||
.addExpectedNode("TEL", "120", new TypeSet("FAX"))
|
||||
.addExpectedNode("TEL", "130", new TypeSet("TLX"))
|
||||
.addExpectedNode("TEL", "140", new TypeSet("WORK", "CELL"))
|
||||
.addExpectedNode("TEL", "150", new TypeSet("WORK", "PAGER"))
|
||||
.addExpectedNode("TEL", "160", new TypeSet("MSG"));
|
||||
}
|
||||
|
||||
public void testPhoneVariousTypeSupportV21() {
|
||||
testPhoneVariousTypeSupport(V21);
|
||||
}
|
||||
|
||||
public void testPhoneVariousTypeSupportV30() {
|
||||
testPhoneVariousTypeSupport(V30);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that "PREF"s are emitted appropriately.
|
||||
*/
|
||||
private void testPhonePrefHandlingCommon(int vcardType) {
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||
.put(Phone.NUMBER, "1")
|
||||
.put(Phone.TYPE, Phone.TYPE_HOME);
|
||||
entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||
.put(Phone.NUMBER, "2")
|
||||
.put(Phone.TYPE, Phone.TYPE_WORK)
|
||||
.put(Phone.IS_PRIMARY, 1);
|
||||
entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||
.put(Phone.NUMBER, "3")
|
||||
.put(Phone.TYPE, Phone.TYPE_FAX_HOME)
|
||||
.put(Phone.IS_PRIMARY, 1);
|
||||
entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||
.put(Phone.NUMBER, "4")
|
||||
.put(Phone.TYPE, Phone.TYPE_FAX_WORK);
|
||||
|
||||
mVerifier.addPropertyNodesVerifierElemWithEmptyName()
|
||||
.addExpectedNode("TEL", "4", new TypeSet("WORK", "FAX"))
|
||||
.addExpectedNode("TEL", "3", new TypeSet("HOME", "FAX", "PREF"))
|
||||
.addExpectedNode("TEL", "2", new TypeSet("WORK", "PREF"))
|
||||
.addExpectedNode("TEL", "1", new TypeSet("HOME"));
|
||||
}
|
||||
|
||||
public void testPhonePrefHandlingV21() {
|
||||
testPhonePrefHandlingCommon(V21);
|
||||
}
|
||||
|
||||
public void testPhonePrefHandlingV30() {
|
||||
testPhonePrefHandlingCommon(V30);
|
||||
}
|
||||
|
||||
private void testMiscPhoneTypeHandling(int vcardType) {
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||
.put(Phone.NUMBER, "1")
|
||||
.put(Phone.TYPE, Phone.TYPE_CUSTOM)
|
||||
.put(Phone.LABEL, "Modem");
|
||||
entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||
.put(Phone.NUMBER, "2")
|
||||
.put(Phone.TYPE, Phone.TYPE_CUSTOM)
|
||||
.put(Phone.LABEL, "MSG");
|
||||
entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||
.put(Phone.NUMBER, "3")
|
||||
.put(Phone.TYPE, Phone.TYPE_CUSTOM)
|
||||
.put(Phone.LABEL, "BBS");
|
||||
entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||
.put(Phone.NUMBER, "4")
|
||||
.put(Phone.TYPE, Phone.TYPE_CUSTOM)
|
||||
.put(Phone.LABEL, "VIDEO");
|
||||
entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||
.put(Phone.NUMBER, "5")
|
||||
.put(Phone.TYPE, Phone.TYPE_CUSTOM);
|
||||
entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||
.put(Phone.NUMBER, "6")
|
||||
.put(Phone.TYPE, Phone.TYPE_CUSTOM)
|
||||
.put(Phone.LABEL, "_AUTO_CELL"); // The old indicator for the type mobile.
|
||||
entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||
.put(Phone.NUMBER, "7")
|
||||
.put(Phone.TYPE, Phone.TYPE_CUSTOM)
|
||||
.put(Phone.LABEL, "\u643A\u5E2F"); // Mobile phone in Japanese Kanji
|
||||
entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||
.put(Phone.NUMBER, "8")
|
||||
.put(Phone.TYPE, Phone.TYPE_CUSTOM)
|
||||
.put(Phone.LABEL, "invalid");
|
||||
PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElemWithEmptyName();
|
||||
elem.addExpectedNode("TEL", "1", new TypeSet("MODEM"))
|
||||
.addExpectedNode("TEL", "2", new TypeSet("MSG"))
|
||||
.addExpectedNode("TEL", "3", new TypeSet("BBS"))
|
||||
.addExpectedNode("TEL", "4", new TypeSet("VIDEO"))
|
||||
.addExpectedNode("TEL", "5", new TypeSet("VOICE"))
|
||||
.addExpectedNode("TEL", "6", new TypeSet("CELL"))
|
||||
.addExpectedNode("TEL", "7", new TypeSet("CELL"))
|
||||
.addExpectedNode("TEL", "8", new TypeSet("X-invalid"));
|
||||
}
|
||||
|
||||
public void testPhoneTypeHandlingV21() {
|
||||
testMiscPhoneTypeHandling(V21);
|
||||
}
|
||||
|
||||
public void testPhoneTypeHandlingV30() {
|
||||
testMiscPhoneTypeHandling(V30);
|
||||
}
|
||||
|
||||
private void testEmailBasicCommon(int vcardType) {
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
mVerifier.addInputEntry().addContentValues(Email.CONTENT_ITEM_TYPE)
|
||||
.put(Email.DATA, "sample@example.com");
|
||||
mVerifier.addPropertyNodesVerifierElemWithEmptyName()
|
||||
.addExpectedNode("EMAIL", "sample@example.com");
|
||||
}
|
||||
|
||||
public void testEmailBasicV21() {
|
||||
testEmailBasicCommon(V21);
|
||||
}
|
||||
|
||||
public void testEmailBasicV30() {
|
||||
testEmailBasicCommon(V30);
|
||||
}
|
||||
|
||||
private void testEmailVariousTypeSupportCommon(int vcardType) {
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(Email.CONTENT_ITEM_TYPE)
|
||||
.put(Email.DATA, "type_home@example.com")
|
||||
.put(Email.TYPE, Email.TYPE_HOME);
|
||||
entry.addContentValues(Email.CONTENT_ITEM_TYPE)
|
||||
.put(Email.DATA, "type_work@example.com")
|
||||
.put(Email.TYPE, Email.TYPE_WORK);
|
||||
entry.addContentValues(Email.CONTENT_ITEM_TYPE)
|
||||
.put(Email.DATA, "type_mobile@example.com")
|
||||
.put(Email.TYPE, Email.TYPE_MOBILE);
|
||||
entry.addContentValues(Email.CONTENT_ITEM_TYPE)
|
||||
.put(Email.DATA, "type_other@example.com")
|
||||
.put(Email.TYPE, Email.TYPE_OTHER);
|
||||
mVerifier.addPropertyNodesVerifierElemWithEmptyName()
|
||||
.addExpectedNode("EMAIL", "type_home@example.com", new TypeSet("HOME"))
|
||||
.addExpectedNode("EMAIL", "type_work@example.com", new TypeSet("WORK"))
|
||||
.addExpectedNode("EMAIL", "type_mobile@example.com", new TypeSet("CELL"))
|
||||
.addExpectedNode("EMAIL", "type_other@example.com");
|
||||
}
|
||||
|
||||
public void testEmailVariousTypeSupportV21() {
|
||||
testEmailVariousTypeSupportCommon(V21);
|
||||
}
|
||||
|
||||
public void testEmailVariousTypeSupportV30() {
|
||||
testEmailVariousTypeSupportCommon(V30);
|
||||
}
|
||||
|
||||
private void testEmailPrefHandlingCommon(int vcardType) {
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(Email.CONTENT_ITEM_TYPE)
|
||||
.put(Email.DATA, "type_home@example.com")
|
||||
.put(Email.TYPE, Email.TYPE_HOME)
|
||||
.put(Email.IS_PRIMARY, 1);
|
||||
entry.addContentValues(Email.CONTENT_ITEM_TYPE)
|
||||
.put(Email.DATA, "type_notype@example.com")
|
||||
.put(Email.IS_PRIMARY, 1);
|
||||
|
||||
mVerifier.addPropertyNodesVerifierElemWithEmptyName()
|
||||
.addExpectedNode("EMAIL", "type_notype@example.com", new TypeSet("PREF"))
|
||||
.addExpectedNode("EMAIL", "type_home@example.com", new TypeSet("HOME", "PREF"));
|
||||
}
|
||||
|
||||
public void testEmailPrefHandlingV21() {
|
||||
testEmailPrefHandlingCommon(V21);
|
||||
}
|
||||
|
||||
public void testEmailPrefHandlingV30() {
|
||||
testEmailPrefHandlingCommon(V30);
|
||||
}
|
||||
|
||||
private void testPostalAddressCommon(int vcardType) {
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
mVerifier.addInputEntry().addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredPostal.POBOX, "Pobox")
|
||||
.put(StructuredPostal.NEIGHBORHOOD, "Neighborhood")
|
||||
.put(StructuredPostal.STREET, "Street")
|
||||
.put(StructuredPostal.CITY, "City")
|
||||
.put(StructuredPostal.REGION, "Region")
|
||||
.put(StructuredPostal.POSTCODE, "100")
|
||||
.put(StructuredPostal.COUNTRY, "Country")
|
||||
.put(StructuredPostal.FORMATTED_ADDRESS, "Formatted Address")
|
||||
.put(StructuredPostal.TYPE, StructuredPostal.TYPE_WORK);
|
||||
// adr-value = 0*6(text-value ";") text-value
|
||||
// ; PO Box, Extended Address, Street, Locality, Region, Postal Code,
|
||||
// ; Country Name
|
||||
//
|
||||
// The NEIGHBORHOOD field is appended after the CITY field.
|
||||
mVerifier.addPropertyNodesVerifierElemWithEmptyName()
|
||||
.addExpectedNode("ADR",
|
||||
Arrays.asList("Pobox", "", "Street", "City Neighborhood",
|
||||
"Region", "100", "Country"), new TypeSet("WORK"));
|
||||
}
|
||||
|
||||
public void testPostalAddressV21() {
|
||||
testPostalAddressCommon(V21);
|
||||
}
|
||||
|
||||
public void testPostalAddressV30() {
|
||||
testPostalAddressCommon(V30);
|
||||
}
|
||||
|
||||
private void testPostalAddressNonNeighborhood(int vcardType) {
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
mVerifier.addInputEntry().addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredPostal.CITY, "City");
|
||||
mVerifier.addPropertyNodesVerifierElemWithEmptyName()
|
||||
.addExpectedNode("ADR",
|
||||
Arrays.asList("", "", "", "City", "", "", ""), new TypeSet("HOME"));
|
||||
}
|
||||
|
||||
public void testPostalAddressNonNeighborhoodV21() {
|
||||
testPostalAddressNonNeighborhood(V21);
|
||||
}
|
||||
|
||||
public void testPostalAddressNonNeighborhoodV30() {
|
||||
testPostalAddressNonNeighborhood(V30);
|
||||
}
|
||||
|
||||
private void testPostalAddressNonCity(int vcardType) {
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
mVerifier.addInputEntry().addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredPostal.NEIGHBORHOOD, "Neighborhood");
|
||||
mVerifier.addPropertyNodesVerifierElemWithEmptyName()
|
||||
.addExpectedNode("ADR",
|
||||
Arrays.asList("", "", "", "Neighborhood", "", "", ""), new TypeSet("HOME"));
|
||||
}
|
||||
|
||||
public void testPostalAddressNonCityV21() {
|
||||
testPostalAddressNonCity(V21);
|
||||
}
|
||||
|
||||
public void testPostalAddressNonCityV30() {
|
||||
testPostalAddressNonCity(V30);
|
||||
}
|
||||
|
||||
private void testPostalOnlyWithFormattedAddressCommon(int vcardType) {
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
mVerifier.addInputEntry().addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredPostal.REGION, "") // Must be ignored.
|
||||
.put(StructuredPostal.FORMATTED_ADDRESS,
|
||||
"Formatted address CA 123-334 United Statue");
|
||||
mVerifier.addPropertyNodesVerifierElemWithEmptyName()
|
||||
.addExpectedNodeWithOrder("ADR", ";Formatted address CA 123-334 United Statue;;;;;",
|
||||
Arrays.asList("", "Formatted address CA 123-334 United Statue",
|
||||
"", "", "", "", ""), new TypeSet("HOME"));
|
||||
}
|
||||
|
||||
public void testPostalOnlyWithFormattedAddressV21() {
|
||||
testPostalOnlyWithFormattedAddressCommon(V21);
|
||||
}
|
||||
|
||||
public void testPostalOnlyWithFormattedAddressV30() {
|
||||
testPostalOnlyWithFormattedAddressCommon(V30);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the vCard composer honors formatted data when it is available
|
||||
* even when it is partial.
|
||||
*/
|
||||
private void testPostalWithBothStructuredAndFormattedCommon(int vcardType) {
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
mVerifier.addInputEntry().addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredPostal.POBOX, "Pobox")
|
||||
.put(StructuredPostal.COUNTRY, "Country")
|
||||
.put(StructuredPostal.FORMATTED_ADDRESS,
|
||||
"Formatted address CA 123-334 United Statue"); // Should be ignored
|
||||
mVerifier.addPropertyNodesVerifierElemWithEmptyName()
|
||||
.addExpectedNode("ADR", "Pobox;;;;;;Country",
|
||||
Arrays.asList("Pobox", "", "", "", "", "", "Country"),
|
||||
new TypeSet("HOME"));
|
||||
}
|
||||
|
||||
public void testPostalWithBothStructuredAndFormattedV21() {
|
||||
testPostalWithBothStructuredAndFormattedCommon(V21);
|
||||
}
|
||||
|
||||
public void testPostalWithBothStructuredAndFormattedV30() {
|
||||
testPostalWithBothStructuredAndFormattedCommon(V30);
|
||||
}
|
||||
|
||||
private void testOrganizationCommon(int vcardType) {
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(Organization.CONTENT_ITEM_TYPE)
|
||||
.put(Organization.COMPANY, "CompanyX")
|
||||
.put(Organization.DEPARTMENT, "DepartmentY")
|
||||
.put(Organization.TITLE, "TitleZ")
|
||||
.put(Organization.JOB_DESCRIPTION, "Description Rambda") // Ignored.
|
||||
.put(Organization.OFFICE_LOCATION, "Mountain View") // Ignored.
|
||||
.put(Organization.PHONETIC_NAME, "PhoneticName!") // Ignored
|
||||
.put(Organization.SYMBOL, "(^o^)/~~"); // Ignore him (her).
|
||||
entry.addContentValues(Organization.CONTENT_ITEM_TYPE)
|
||||
.putNull(Organization.COMPANY)
|
||||
.put(Organization.DEPARTMENT, "DepartmentXX")
|
||||
.putNull(Organization.TITLE);
|
||||
entry.addContentValues(Organization.CONTENT_ITEM_TYPE)
|
||||
.put(Organization.COMPANY, "CompanyXYZ")
|
||||
.putNull(Organization.DEPARTMENT)
|
||||
.put(Organization.TITLE, "TitleXYZYX");
|
||||
// Currently we do not use group but depend on the order.
|
||||
mVerifier.addPropertyNodesVerifierElemWithEmptyName()
|
||||
.addExpectedNodeWithOrder("ORG", "CompanyX;DepartmentY",
|
||||
Arrays.asList("CompanyX", "DepartmentY"))
|
||||
.addExpectedNodeWithOrder("TITLE", "TitleZ")
|
||||
.addExpectedNodeWithOrder("ORG", "DepartmentXX")
|
||||
.addExpectedNodeWithOrder("ORG", "CompanyXYZ")
|
||||
.addExpectedNodeWithOrder("TITLE", "TitleXYZYX");
|
||||
}
|
||||
|
||||
public void testOrganizationV21() {
|
||||
testOrganizationCommon(V21);
|
||||
}
|
||||
|
||||
public void testOrganizationV30() {
|
||||
testOrganizationCommon(V30);
|
||||
}
|
||||
|
||||
private void testImVariousTypeSupportCommon(int vcardType) {
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(Im.CONTENT_ITEM_TYPE)
|
||||
.put(Im.PROTOCOL, Im.PROTOCOL_AIM)
|
||||
.put(Im.DATA, "aim");
|
||||
entry.addContentValues(Im.CONTENT_ITEM_TYPE)
|
||||
.put(Im.PROTOCOL, Im.PROTOCOL_MSN)
|
||||
.put(Im.DATA, "msn");
|
||||
entry.addContentValues(Im.CONTENT_ITEM_TYPE)
|
||||
.put(Im.PROTOCOL, Im.PROTOCOL_YAHOO)
|
||||
.put(Im.DATA, "yahoo");
|
||||
entry.addContentValues(Im.CONTENT_ITEM_TYPE)
|
||||
.put(Im.PROTOCOL, Im.PROTOCOL_SKYPE)
|
||||
.put(Im.DATA, "skype");
|
||||
entry.addContentValues(Im.CONTENT_ITEM_TYPE)
|
||||
.put(Im.PROTOCOL, Im.PROTOCOL_QQ)
|
||||
.put(Im.DATA, "qq");
|
||||
entry.addContentValues(Im.CONTENT_ITEM_TYPE)
|
||||
.put(Im.PROTOCOL, Im.PROTOCOL_GOOGLE_TALK)
|
||||
.put(Im.DATA, "google talk");
|
||||
entry.addContentValues(Im.CONTENT_ITEM_TYPE)
|
||||
.put(Im.PROTOCOL, Im.PROTOCOL_ICQ)
|
||||
.put(Im.DATA, "icq");
|
||||
entry.addContentValues(Im.CONTENT_ITEM_TYPE)
|
||||
.put(Im.PROTOCOL, Im.PROTOCOL_JABBER)
|
||||
.put(Im.DATA, "jabber");
|
||||
entry.addContentValues(Im.CONTENT_ITEM_TYPE)
|
||||
.put(Im.PROTOCOL, Im.PROTOCOL_NETMEETING)
|
||||
.put(Im.DATA, "netmeeting");
|
||||
|
||||
// No determined way to express unknown type...
|
||||
mVerifier.addPropertyNodesVerifierElemWithEmptyName()
|
||||
.addExpectedNode("X-JABBER", "jabber")
|
||||
.addExpectedNode("X-ICQ", "icq")
|
||||
.addExpectedNode("X-GOOGLE-TALK", "google talk")
|
||||
.addExpectedNode("X-QQ", "qq")
|
||||
.addExpectedNode("X-SKYPE-USERNAME", "skype")
|
||||
.addExpectedNode("X-YAHOO", "yahoo")
|
||||
.addExpectedNode("X-MSN", "msn")
|
||||
.addExpectedNode("X-NETMEETING", "netmeeting")
|
||||
.addExpectedNode("X-AIM", "aim");
|
||||
}
|
||||
|
||||
public void testImBasiV21() {
|
||||
testImVariousTypeSupportCommon(V21);
|
||||
}
|
||||
|
||||
public void testImBasicV30() {
|
||||
testImVariousTypeSupportCommon(V30);
|
||||
}
|
||||
|
||||
private void testImPrefHandlingCommon(int vcardType) {
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(Im.CONTENT_ITEM_TYPE)
|
||||
.put(Im.PROTOCOL, Im.PROTOCOL_AIM)
|
||||
.put(Im.DATA, "aim1");
|
||||
entry.addContentValues(Im.CONTENT_ITEM_TYPE)
|
||||
.put(Im.PROTOCOL, Im.PROTOCOL_AIM)
|
||||
.put(Im.DATA, "aim2")
|
||||
.put(Im.TYPE, Im.TYPE_HOME)
|
||||
.put(Im.IS_PRIMARY, 1);
|
||||
|
||||
mVerifier.addPropertyNodesVerifierElemWithEmptyName()
|
||||
.addExpectedNode("X-AIM", "aim1")
|
||||
.addExpectedNode("X-AIM", "aim2", new TypeSet("HOME", "PREF"));
|
||||
}
|
||||
|
||||
public void testImPrefHandlingV21() {
|
||||
testImPrefHandlingCommon(V21);
|
||||
}
|
||||
|
||||
public void testImPrefHandlingV30() {
|
||||
testImPrefHandlingCommon(V30);
|
||||
}
|
||||
|
||||
private void testWebsiteCommon(int vcardType) {
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(Website.CONTENT_ITEM_TYPE)
|
||||
.put(Website.URL, "http://website.example.android.com/index.html")
|
||||
.put(Website.TYPE, Website.TYPE_BLOG);
|
||||
entry.addContentValues(Website.CONTENT_ITEM_TYPE)
|
||||
.put(Website.URL, "ftp://ftp.example.android.com/index.html")
|
||||
.put(Website.TYPE, Website.TYPE_FTP);
|
||||
|
||||
// We drop TYPE information since vCard (especially 3.0) does not allow us to emit it.
|
||||
mVerifier.addPropertyNodesVerifierElemWithEmptyName()
|
||||
.addExpectedNode("URL", "ftp://ftp.example.android.com/index.html")
|
||||
.addExpectedNode("URL", "http://website.example.android.com/index.html");
|
||||
}
|
||||
|
||||
public void testWebsiteV21() {
|
||||
testWebsiteCommon(V21);
|
||||
}
|
||||
|
||||
public void testWebsiteV30() {
|
||||
testWebsiteCommon(V30);
|
||||
}
|
||||
|
||||
private String getAndroidPropValue(final String mimeType, String value, Integer type) {
|
||||
return getAndroidPropValue(mimeType, value, type, null);
|
||||
}
|
||||
|
||||
private String getAndroidPropValue(final String mimeType, String value,
|
||||
Integer type, String label) {
|
||||
return (mimeType + ";" + value + ";"
|
||||
+ (type != null ? type : "") + ";"
|
||||
+ (label != null ? label : "") + ";;;;;;;;;;;;");
|
||||
}
|
||||
|
||||
private void testEventCommon(int vcardType) {
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(Event.CONTENT_ITEM_TYPE)
|
||||
.put(Event.TYPE, Event.TYPE_ANNIVERSARY)
|
||||
.put(Event.START_DATE, "1982-06-16");
|
||||
entry.addContentValues(Event.CONTENT_ITEM_TYPE)
|
||||
.put(Event.TYPE, Event.TYPE_BIRTHDAY)
|
||||
.put(Event.START_DATE, "2008-10-22");
|
||||
entry.addContentValues(Event.CONTENT_ITEM_TYPE)
|
||||
.put(Event.TYPE, Event.TYPE_OTHER)
|
||||
.put(Event.START_DATE, "2018-03-12");
|
||||
entry.addContentValues(Event.CONTENT_ITEM_TYPE)
|
||||
.put(Event.TYPE, Event.TYPE_CUSTOM)
|
||||
.put(Event.LABEL, "The last day")
|
||||
.put(Event.START_DATE, "When the Tower of Hanoi with 64 rings is completed.");
|
||||
entry.addContentValues(Event.CONTENT_ITEM_TYPE)
|
||||
.put(Event.TYPE, Event.TYPE_BIRTHDAY)
|
||||
.put(Event.START_DATE, "2009-05-19"); // Should be ignored.
|
||||
mVerifier.addPropertyNodesVerifierElemWithEmptyName()
|
||||
.addExpectedNode("BDAY", "2008-10-22")
|
||||
.addExpectedNode("X-ANDROID-CUSTOM",
|
||||
getAndroidPropValue(
|
||||
Event.CONTENT_ITEM_TYPE, "1982-06-16", Event.TYPE_ANNIVERSARY))
|
||||
.addExpectedNode("X-ANDROID-CUSTOM",
|
||||
getAndroidPropValue(
|
||||
Event.CONTENT_ITEM_TYPE, "2018-03-12", Event.TYPE_OTHER))
|
||||
.addExpectedNode("X-ANDROID-CUSTOM",
|
||||
getAndroidPropValue(
|
||||
Event.CONTENT_ITEM_TYPE,
|
||||
"When the Tower of Hanoi with 64 rings is completed.",
|
||||
Event.TYPE_CUSTOM, "The last day"));
|
||||
}
|
||||
|
||||
public void testEventV21() {
|
||||
testEventCommon(V21);
|
||||
}
|
||||
|
||||
public void testEventV30() {
|
||||
testEventCommon(V30);
|
||||
}
|
||||
|
||||
private void testNoteCommon(int vcardType) {
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(Note.CONTENT_ITEM_TYPE)
|
||||
.put(Note.NOTE, "note1");
|
||||
entry.addContentValues(Note.CONTENT_ITEM_TYPE)
|
||||
.put(Note.NOTE, "note2")
|
||||
.put(Note.IS_PRIMARY, 1); // Just ignored.
|
||||
mVerifier.addPropertyNodesVerifierElemWithEmptyName()
|
||||
.addExpectedNodeWithOrder("NOTE", "note1")
|
||||
.addExpectedNodeWithOrder("NOTE", "note2");
|
||||
}
|
||||
|
||||
public void testNoteV21() {
|
||||
testNoteCommon(V21);
|
||||
}
|
||||
|
||||
public void testNoteV30() {
|
||||
testNoteCommon(V30);
|
||||
}
|
||||
|
||||
private void testPhotoCommon(int vcardType) {
|
||||
final boolean isV30 = vcardType == V30;
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.FAMILY_NAME, "PhotoTest");
|
||||
entry.addContentValues(Photo.CONTENT_ITEM_TYPE)
|
||||
.put(Photo.PHOTO, sPhotoByteArray);
|
||||
|
||||
ContentValues contentValuesForPhoto = new ContentValues();
|
||||
contentValuesForPhoto.put("ENCODING", (isV30 ? "b" : "BASE64"));
|
||||
mVerifier.addPropertyNodesVerifierElem()
|
||||
.addExpectedNode("FN", "PhotoTest")
|
||||
.addExpectedNode("N", "PhotoTest;;;;",
|
||||
Arrays.asList("PhotoTest", "", "", "", ""))
|
||||
.addExpectedNodeWithOrder("PHOTO", null, null, sPhotoByteArray,
|
||||
contentValuesForPhoto, new TypeSet("JPEG"), null);
|
||||
}
|
||||
|
||||
public void testPhotoV21() {
|
||||
testPhotoCommon(V21);
|
||||
}
|
||||
|
||||
public void testPhotoV30() {
|
||||
testPhotoCommon(V30);
|
||||
}
|
||||
|
||||
private void testRelationCommon(int vcardType) {
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
mVerifier.addInputEntry().addContentValues(Relation.CONTENT_ITEM_TYPE)
|
||||
.put(Relation.TYPE, Relation.TYPE_MOTHER)
|
||||
.put(Relation.NAME, "Ms. Mother");
|
||||
mVerifier.addContentValuesVerifierElem().addExpected(Relation.CONTENT_ITEM_TYPE)
|
||||
.put(Relation.TYPE, Relation.TYPE_MOTHER)
|
||||
.put(Relation.NAME, "Ms. Mother");
|
||||
}
|
||||
|
||||
public void testRelationV21() {
|
||||
testRelationCommon(V21);
|
||||
}
|
||||
|
||||
public void testRelationV30() {
|
||||
testRelationCommon(V30);
|
||||
}
|
||||
|
||||
public void testV30HandleEscape() {
|
||||
mVerifier.initForExportTest(V30);
|
||||
mVerifier.addInputEntry().addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.FAMILY_NAME, "\\")
|
||||
.put(StructuredName.GIVEN_NAME, ";")
|
||||
.put(StructuredName.MIDDLE_NAME, ",")
|
||||
.put(StructuredName.PREFIX, "\n")
|
||||
.put(StructuredName.DISPLAY_NAME, "[<{Unescaped:Asciis}>]");
|
||||
// Verifies the vCard String correctly escapes each character which must be escaped.
|
||||
mVerifier.addLineVerifierElem()
|
||||
.addExpected("N:\\\\;\\;;\\,;\\n;")
|
||||
.addExpected("FN:[<{Unescaped:Asciis}>]");
|
||||
mVerifier.addPropertyNodesVerifierElem()
|
||||
.addExpectedNode("FN", "[<{Unescaped:Asciis}>]")
|
||||
.addExpectedNode("N", Arrays.asList("\\", ";", ",", "\n", ""));
|
||||
}
|
||||
|
||||
/**
|
||||
* There's no "NICKNAME" property in vCard 2.1, while there is in vCard 3.0.
|
||||
* We use Android-specific "X-ANDROID-CUSTOM" property.
|
||||
* This test verifies the functionality.
|
||||
*/
|
||||
public void testNickNameV21() {
|
||||
mVerifier.initForExportTest(V21);
|
||||
mVerifier.addInputEntry().addContentValues(Nickname.CONTENT_ITEM_TYPE)
|
||||
.put(Nickname.NAME, "Nicky");
|
||||
mVerifier.addPropertyNodesVerifierElemWithEmptyName()
|
||||
.addExpectedNode("X-ANDROID-CUSTOM",
|
||||
Nickname.CONTENT_ITEM_TYPE + ";Nicky;;;;;;;;;;;;;;");
|
||||
mVerifier.addContentValuesVerifierElem().addExpected(Nickname.CONTENT_ITEM_TYPE)
|
||||
.put(Nickname.NAME, "Nicky");
|
||||
}
|
||||
|
||||
public void testTolerateBrokenPhoneNumberEntryV21() {
|
||||
mVerifier.initForExportTest(V21);
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||
.put(Phone.TYPE, Phone.TYPE_HOME)
|
||||
.put(Phone.NUMBER, "111-222-3333 (Miami)\n444-5555-666 (Tokyo);"
|
||||
+ "777-888-9999 (Chicago);111-222-3333 (Miami)");
|
||||
mVerifier.addPropertyNodesVerifierElemWithEmptyName()
|
||||
.addExpectedNode("TEL", "111-222-3333", new TypeSet("HOME"))
|
||||
.addExpectedNode("TEL", "444-555-5666", new TypeSet("HOME"))
|
||||
.addExpectedNode("TEL", "777-888-9999", new TypeSet("HOME"));
|
||||
}
|
||||
|
||||
private void testPickUpNonEmptyContentValuesCommon(int vcardType) {
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.IS_PRIMARY, 1); // Empty name. Should be ignored.
|
||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.FAMILY_NAME, "family1"); // Not primary. Should be ignored.
|
||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.IS_PRIMARY, 1)
|
||||
.put(StructuredName.FAMILY_NAME, "family2"); // This entry is what we want.
|
||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.IS_PRIMARY, 1)
|
||||
.put(StructuredName.FAMILY_NAME, "family3");
|
||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.FAMILY_NAME, "family4");
|
||||
mVerifier.addPropertyNodesVerifierElem()
|
||||
.addExpectedNode("N", Arrays.asList("family2", "", "", "", ""))
|
||||
.addExpectedNode("FN", "family2");
|
||||
}
|
||||
|
||||
public void testPickUpNonEmptyContentValuesV21() {
|
||||
testPickUpNonEmptyContentValuesCommon(V21);
|
||||
}
|
||||
|
||||
public void testPickUpNonEmptyContentValuesV30() {
|
||||
testPickUpNonEmptyContentValuesCommon(V30);
|
||||
}
|
||||
}
|
||||
1008
vcard/tests/src/com/android/vcard/tests/VCardImporterTests.java
Normal file
1008
vcard/tests/src/com/android/vcard/tests/VCardImporterTests.java
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,436 @@
|
|||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.vcard.tests;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.provider.ContactsContract.CommonDataKinds.Nickname;
|
||||
import android.provider.ContactsContract.CommonDataKinds.Note;
|
||||
import android.provider.ContactsContract.CommonDataKinds.Phone;
|
||||
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
|
||||
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
|
||||
|
||||
import com.android.vcard.VCardConfig;
|
||||
import com.android.vcard.tests.test_utils.ContactEntry;
|
||||
import com.android.vcard.tests.test_utils.ContentValuesBuilder;
|
||||
import com.android.vcard.tests.test_utils.PropertyNodesVerifierElem;
|
||||
import com.android.vcard.tests.test_utils.PropertyNodesVerifierElem.TypeSet;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class VCardJapanizationTests extends VCardTestsBase {
|
||||
private void testNameUtf8Common(int vcardType) {
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.FAMILY_NAME, "\u3075\u308B\u3069")
|
||||
.put(StructuredName.GIVEN_NAME, "\u3091\u308A\u304B")
|
||||
.put(StructuredName.MIDDLE_NAME, "B")
|
||||
.put(StructuredName.PREFIX, "Dr.")
|
||||
.put(StructuredName.SUFFIX, "Ph.D");
|
||||
ContentValues contentValues =
|
||||
(VCardConfig.isV30(vcardType) ? null : mContentValuesForQPAndUtf8);
|
||||
mVerifier.addPropertyNodesVerifierElem()
|
||||
.addExpectedNode("FN", "Dr. \u3075\u308B\u3069 B \u3091\u308A\u304B Ph.D",
|
||||
contentValues)
|
||||
.addExpectedNode("N", "\u3075\u308B\u3069;\u3091\u308A\u304B;B;Dr.;Ph.D",
|
||||
Arrays.asList(
|
||||
"\u3075\u308B\u3069", "\u3091\u308A\u304B", "B", "Dr.", "Ph.D"),
|
||||
null, contentValues, null, null);
|
||||
}
|
||||
|
||||
public void testNameUtf8V21() {
|
||||
testNameUtf8Common(VCardConfig.VCARD_TYPE_V21_JAPANESE);
|
||||
}
|
||||
|
||||
public void testNameUtf8V30() {
|
||||
testNameUtf8Common(VCardConfig.VCARD_TYPE_V30_JAPANESE);
|
||||
}
|
||||
|
||||
public void testNameShiftJis() {
|
||||
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V30_JAPANESE, "Shift_JIS");
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.FAMILY_NAME, "\u3075\u308B\u3069")
|
||||
.put(StructuredName.GIVEN_NAME, "\u3091\u308A\u304B")
|
||||
.put(StructuredName.MIDDLE_NAME, "B")
|
||||
.put(StructuredName.PREFIX, "Dr.")
|
||||
.put(StructuredName.SUFFIX, "Ph.D");
|
||||
|
||||
mVerifier.addPropertyNodesVerifierElem()
|
||||
.addExpectedNode("FN", "Dr. \u3075\u308B\u3069 B \u3091\u308A\u304B Ph.D",
|
||||
mContentValuesForSJis)
|
||||
.addExpectedNode("N", "\u3075\u308B\u3069;\u3091\u308A\u304B;B;Dr.;Ph.D",
|
||||
Arrays.asList(
|
||||
"\u3075\u308B\u3069", "\u3091\u308A\u304B", "B", "Dr.", "Ph.D"),
|
||||
null, mContentValuesForSJis, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* DoCoMo phones require all name elements should be in "family name" field.
|
||||
*/
|
||||
public void testNameDoCoMo() {
|
||||
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS");
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.FAMILY_NAME, "\u3075\u308B\u3069")
|
||||
.put(StructuredName.GIVEN_NAME, "\u3091\u308A\u304B")
|
||||
.put(StructuredName.MIDDLE_NAME, "B")
|
||||
.put(StructuredName.PREFIX, "Dr.")
|
||||
.put(StructuredName.SUFFIX, "Ph.D");
|
||||
|
||||
final String fullName = "Dr. \u3075\u308B\u3069 B \u3091\u308A\u304B Ph.D";
|
||||
mVerifier.addPropertyNodesVerifierElem()
|
||||
.addExpectedNode("N", fullName + ";;;;",
|
||||
Arrays.asList(fullName, "", "", "", ""),
|
||||
null, mContentValuesForSJis, null, null)
|
||||
.addExpectedNode("FN", fullName, mContentValuesForSJis)
|
||||
.addExpectedNode("SOUND", ";;;;", new TypeSet("X-IRMC-N"))
|
||||
.addExpectedNode("TEL", "", new TypeSet("HOME"))
|
||||
.addExpectedNode("EMAIL", "", new TypeSet("HOME"))
|
||||
.addExpectedNode("ADR", "", new TypeSet("HOME"))
|
||||
.addExpectedNode("X-CLASS", "PUBLIC")
|
||||
.addExpectedNode("X-REDUCTION", "")
|
||||
.addExpectedNode("X-NO", "")
|
||||
.addExpectedNode("X-DCM-HMN-MODE", "");
|
||||
}
|
||||
|
||||
private void testPhoneticNameCommon(int vcardType, String charset) {
|
||||
mVerifier.initForExportTest(vcardType, charset);
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.PHONETIC_FAMILY_NAME, "\u3084\u307E\u3060")
|
||||
.put(StructuredName.PHONETIC_MIDDLE_NAME, "\u30DF\u30C9\u30EB\u30CD\u30FC\u30E0")
|
||||
.put(StructuredName.PHONETIC_GIVEN_NAME, "\u305F\u308D\u3046");
|
||||
|
||||
final ContentValues contentValues =
|
||||
("SHIFT_JIS".equalsIgnoreCase(charset) ?
|
||||
(VCardConfig.isV30(vcardType) ? mContentValuesForSJis :
|
||||
mContentValuesForQPAndSJis) :
|
||||
(VCardConfig.isV30(vcardType) ? null : mContentValuesForQPAndUtf8));
|
||||
PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElemWithEmptyName();
|
||||
elem.addExpectedNode("X-PHONETIC-LAST-NAME", "\u3084\u307E\u3060",
|
||||
contentValues)
|
||||
.addExpectedNode("X-PHONETIC-MIDDLE-NAME",
|
||||
"\u30DF\u30C9\u30EB\u30CD\u30FC\u30E0",
|
||||
contentValues)
|
||||
.addExpectedNode("X-PHONETIC-FIRST-NAME", "\u305F\u308D\u3046",
|
||||
contentValues);
|
||||
if (VCardConfig.isV30(vcardType)) {
|
||||
elem.addExpectedNode("SORT-STRING",
|
||||
"\u3084\u307E\u3060 \u30DF\u30C9\u30EB\u30CD\u30FC\u30E0 \u305F\u308D\u3046",
|
||||
contentValues);
|
||||
}
|
||||
ContentValuesBuilder builder = mVerifier.addContentValuesVerifierElem()
|
||||
.addExpected(StructuredName.CONTENT_ITEM_TYPE);
|
||||
builder.put(StructuredName.PHONETIC_FAMILY_NAME, "\u3084\u307E\u3060")
|
||||
.put(StructuredName.PHONETIC_MIDDLE_NAME, "\u30DF\u30C9\u30EB\u30CD\u30FC\u30E0")
|
||||
.put(StructuredName.PHONETIC_GIVEN_NAME, "\u305F\u308D\u3046")
|
||||
.put(StructuredName.DISPLAY_NAME,
|
||||
"\u3084\u307E\u3060 \u30DF\u30C9\u30EB\u30CD\u30FC\u30E0 " +
|
||||
"\u305F\u308D\u3046");
|
||||
}
|
||||
|
||||
public void testPhoneticNameForJapaneseV21Utf8() {
|
||||
testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE, null);
|
||||
}
|
||||
|
||||
public void testPhoneticNameForJapaneseV21Sjis() {
|
||||
testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE, "Shift_JIS");
|
||||
}
|
||||
|
||||
public void testPhoneticNameForJapaneseV30Utf8() {
|
||||
testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE, null);
|
||||
}
|
||||
|
||||
public void testPhoneticNameForJapaneseV30SJis() {
|
||||
testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE, "Shift_JIS");
|
||||
}
|
||||
|
||||
public void testPhoneticNameForMobileV21_1() {
|
||||
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_MOBILE, "Shift_JIS");
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.PHONETIC_FAMILY_NAME, "\u3084\u307E\u3060")
|
||||
.put(StructuredName.PHONETIC_MIDDLE_NAME, "\u30DF\u30C9\u30EB\u30CD\u30FC\u30E0")
|
||||
.put(StructuredName.PHONETIC_GIVEN_NAME, "\u305F\u308D\u3046");
|
||||
|
||||
mVerifier.addPropertyNodesVerifierElem()
|
||||
.addExpectedNode("SOUND",
|
||||
"\uFF94\uFF8F\uFF80\uFF9E \uFF90\uFF84\uFF9E\uFF99\uFF88\uFF70\uFF91 " +
|
||||
"\uFF80\uFF9B\uFF73;;;;",
|
||||
mContentValuesForSJis, new TypeSet("X-IRMC-N"));
|
||||
ContentValuesBuilder builder = mVerifier.addContentValuesVerifierElem()
|
||||
.addExpected(StructuredName.CONTENT_ITEM_TYPE);
|
||||
builder.put(StructuredName.PHONETIC_FAMILY_NAME, "\uFF94\uFF8F\uFF80\uFF9E")
|
||||
.put(StructuredName.PHONETIC_MIDDLE_NAME,
|
||||
"\uFF90\uFF84\uFF9E\uFF99\uFF88\uFF70\uFF91")
|
||||
.put(StructuredName.PHONETIC_GIVEN_NAME, "\uFF80\uFF9B\uFF73")
|
||||
.put(StructuredName.DISPLAY_NAME,
|
||||
"\uFF94\uFF8F\uFF80\uFF9E \uFF90\uFF84\uFF9E\uFF99\uFF88\uFF70\uFF91 " +
|
||||
"\uFF80\uFF9B\uFF73");
|
||||
}
|
||||
|
||||
public void testPhoneticNameForMobileV21_2() {
|
||||
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_MOBILE, "Shift_JIS");
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.PHONETIC_FAMILY_NAME, "\u3084\u307E\u3060")
|
||||
.put(StructuredName.PHONETIC_GIVEN_NAME, "\u305F\u308D\u3046");
|
||||
|
||||
mVerifier.addPropertyNodesVerifierElem()
|
||||
.addExpectedNode("SOUND", "\uFF94\uFF8F\uFF80\uFF9E \uFF80\uFF9B\uFF73;;;;",
|
||||
mContentValuesForSJis, new TypeSet("X-IRMC-N"));
|
||||
ContentValuesBuilder builder = mVerifier.addContentValuesVerifierElem()
|
||||
.addExpected(StructuredName.CONTENT_ITEM_TYPE);
|
||||
builder.put(StructuredName.PHONETIC_FAMILY_NAME, "\uFF94\uFF8F\uFF80\uFF9E")
|
||||
.put(StructuredName.PHONETIC_GIVEN_NAME, "\uFF80\uFF9B\uFF73")
|
||||
.put(StructuredName.DISPLAY_NAME, "\uFF94\uFF8F\uFF80\uFF9E \uFF80\uFF9B\uFF73");
|
||||
}
|
||||
|
||||
private void testPostalAddressWithJapaneseCommon(int vcardType, String charset) {
|
||||
mVerifier.initForExportTest(vcardType, charset);
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredPostal.POBOX, "\u79C1\u66F8\u7BB107")
|
||||
.put(StructuredPostal.STREET, "\u96DB\u898B\u6CA2\u6751")
|
||||
.put(StructuredPostal.CITY, "\u9E7F\u9AA8\u5E02")
|
||||
.put(StructuredPostal.REGION, "\u00D7\u00D7\u770C")
|
||||
.put(StructuredPostal.POSTCODE, "494-1313")
|
||||
.put(StructuredPostal.COUNTRY, "\u65E5\u672C")
|
||||
.put(StructuredPostal.FORMATTED_ADDRESS,
|
||||
"\u3053\u3093\u306A\u3068\u3053\u308D\u3092\u898B"
|
||||
+ "\u308B\u306A\u3093\u3066\u6687\u4EBA\u3067\u3059\u304B\uFF1F")
|
||||
.put(StructuredPostal.TYPE, StructuredPostal.TYPE_CUSTOM)
|
||||
.put(StructuredPostal.LABEL, "\u304A\u3082\u3061\u304B\u3048\u308A");
|
||||
|
||||
ContentValues contentValues = ("UTF-8".equalsIgnoreCase(charset) ?
|
||||
(VCardConfig.isV30(vcardType) ? mContentValuesForSJis :
|
||||
mContentValuesForQPAndSJis) :
|
||||
(VCardConfig.isV30(vcardType) ? mContentValuesForUtf8 :
|
||||
mContentValuesForQPAndUtf8));
|
||||
|
||||
PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElemWithEmptyName();
|
||||
// LABEL must be ignored in vCard 2.1. As for vCard 3.0, the current behavior is
|
||||
// same as that in vCard 3.0, which can be changed in the future.
|
||||
elem.addExpectedNode("ADR", Arrays.asList("\u79C1\u66F8\u7BB107",
|
||||
"", "\u96DB\u898B\u6CA2\u6751", "\u9E7F\u9AA8\u5E02", "\u00D7\u00D7\u770C",
|
||||
"494-1313", "\u65E5\u672C"),
|
||||
contentValues);
|
||||
mVerifier.addContentValuesVerifierElem().addExpected(StructuredPostal.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredPostal.POBOX, "\u79C1\u66F8\u7BB107")
|
||||
.put(StructuredPostal.STREET, "\u96DB\u898B\u6CA2\u6751")
|
||||
.put(StructuredPostal.CITY, "\u9E7F\u9AA8\u5E02")
|
||||
.put(StructuredPostal.REGION, "\u00D7\u00D7\u770C")
|
||||
.put(StructuredPostal.POSTCODE, "494-1313")
|
||||
.put(StructuredPostal.COUNTRY, "\u65E5\u672C")
|
||||
.put(StructuredPostal.FORMATTED_ADDRESS,
|
||||
"\u65E5\u672C 494-1313 \u00D7\u00D7\u770C \u9E7F\u9AA8\u5E02 " +
|
||||
"\u96DB\u898B\u6CA2\u6751 " + "\u79C1\u66F8\u7BB107")
|
||||
.put(StructuredPostal.TYPE, StructuredPostal.TYPE_HOME);
|
||||
}
|
||||
public void testPostalAddresswithJapaneseV21() {
|
||||
testPostalAddressWithJapaneseCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE, "Shift_JIS");
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that only one address field is emitted toward DoCoMo phones.
|
||||
* Prefered type must (should?) be: HOME > WORK > OTHER > CUSTOM
|
||||
*/
|
||||
public void testPostalAdrressForDoCoMo_1() {
|
||||
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS");
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredPostal.TYPE, StructuredPostal.TYPE_WORK)
|
||||
.put(StructuredPostal.POBOX, "1");
|
||||
entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredPostal.TYPE, StructuredPostal.TYPE_OTHER)
|
||||
.put(StructuredPostal.POBOX, "2");
|
||||
entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredPostal.TYPE, StructuredPostal.TYPE_HOME)
|
||||
.put(StructuredPostal.POBOX, "3");
|
||||
entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredPostal.TYPE, StructuredPostal.TYPE_CUSTOM)
|
||||
.put(StructuredPostal.LABEL, "custom")
|
||||
.put(StructuredPostal.POBOX, "4");
|
||||
|
||||
mVerifier.addPropertyNodesVerifierElemWithEmptyName()
|
||||
.addExpectedNode("TEL", "", new TypeSet("HOME"))
|
||||
.addExpectedNode("EMAIL", "", new TypeSet("HOME"))
|
||||
.addExpectedNode("X-CLASS", "PUBLIC")
|
||||
.addExpectedNode("X-REDUCTION", "")
|
||||
.addExpectedNode("X-NO", "")
|
||||
.addExpectedNode("X-DCM-HMN-MODE", "")
|
||||
.addExpectedNode("ADR",
|
||||
Arrays.asList("3", "", "", "", "", "", ""), new TypeSet("HOME"));
|
||||
}
|
||||
|
||||
public void testPostalAdrressForDoCoMo_2() {
|
||||
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS");
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredPostal.TYPE, StructuredPostal.TYPE_OTHER)
|
||||
.put(StructuredPostal.POBOX, "1");
|
||||
entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredPostal.TYPE, StructuredPostal.TYPE_WORK)
|
||||
.put(StructuredPostal.POBOX, "2");
|
||||
entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredPostal.TYPE, StructuredPostal.TYPE_CUSTOM)
|
||||
.put(StructuredPostal.LABEL, "custom")
|
||||
.put(StructuredPostal.POBOX, "3");
|
||||
|
||||
mVerifier.addPropertyNodesVerifierElemWithEmptyName()
|
||||
.addExpectedNode("TEL", "", new TypeSet("HOME"))
|
||||
.addExpectedNode("EMAIL", "", new TypeSet("HOME"))
|
||||
.addExpectedNode("X-CLASS", "PUBLIC")
|
||||
.addExpectedNode("X-REDUCTION", "")
|
||||
.addExpectedNode("X-NO", "")
|
||||
.addExpectedNode("X-DCM-HMN-MODE", "")
|
||||
.addExpectedNode("ADR",
|
||||
Arrays.asList("2", "", "", "", "", "", ""), new TypeSet("WORK"));
|
||||
}
|
||||
|
||||
public void testPostalAdrressForDoCoMo_3() {
|
||||
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS");
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredPostal.TYPE, StructuredPostal.TYPE_CUSTOM)
|
||||
.put(StructuredPostal.LABEL, "custom1")
|
||||
.put(StructuredPostal.POBOX, "1");
|
||||
entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredPostal.TYPE, StructuredPostal.TYPE_OTHER)
|
||||
.put(StructuredPostal.POBOX, "2");
|
||||
entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredPostal.TYPE, StructuredPostal.TYPE_CUSTOM)
|
||||
.put(StructuredPostal.LABEL, "custom2")
|
||||
.put(StructuredPostal.POBOX, "3");
|
||||
|
||||
mVerifier.addPropertyNodesVerifierElemWithEmptyName()
|
||||
.addExpectedNode("TEL", "", new TypeSet("HOME"))
|
||||
.addExpectedNode("EMAIL", "", new TypeSet("HOME"))
|
||||
.addExpectedNode("X-CLASS", "PUBLIC")
|
||||
.addExpectedNode("X-REDUCTION", "")
|
||||
.addExpectedNode("X-NO", "")
|
||||
.addExpectedNode("X-DCM-HMN-MODE", "")
|
||||
.addExpectedNode("ADR", Arrays.asList("2", "", "", "", "", "", ""));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the vCard exporter tolerates null TYPE.
|
||||
*/
|
||||
public void testPostalAdrressForDoCoMo_4() {
|
||||
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS");
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredPostal.POBOX, "1");
|
||||
entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredPostal.TYPE, StructuredPostal.TYPE_OTHER)
|
||||
.put(StructuredPostal.POBOX, "2");
|
||||
entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredPostal.TYPE, StructuredPostal.TYPE_HOME)
|
||||
.put(StructuredPostal.POBOX, "3");
|
||||
entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredPostal.TYPE, StructuredPostal.TYPE_WORK)
|
||||
.put(StructuredPostal.POBOX, "4");
|
||||
entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredPostal.POBOX, "5");
|
||||
|
||||
mVerifier.addPropertyNodesVerifierElemWithEmptyName()
|
||||
.addExpectedNode("TEL", "", new TypeSet("HOME"))
|
||||
.addExpectedNode("EMAIL", "", new TypeSet("HOME"))
|
||||
.addExpectedNode("X-CLASS", "PUBLIC")
|
||||
.addExpectedNode("X-REDUCTION", "")
|
||||
.addExpectedNode("X-NO", "")
|
||||
.addExpectedNode("X-DCM-HMN-MODE", "")
|
||||
.addExpectedNode("ADR",
|
||||
Arrays.asList("3", "", "", "", "", "", ""), new TypeSet("HOME"));
|
||||
}
|
||||
|
||||
private void testJapanesePhoneNumberCommon(int vcardType) {
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||
.put(Phone.NUMBER, "0312341234")
|
||||
.put(Phone.TYPE, Phone.TYPE_HOME);
|
||||
entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||
.put(Phone.NUMBER, "09012341234")
|
||||
.put(Phone.TYPE, Phone.TYPE_MOBILE);
|
||||
mVerifier.addPropertyNodesVerifierElemWithEmptyName()
|
||||
.addExpectedNode("TEL", "03-1234-1234", new TypeSet("HOME"))
|
||||
.addExpectedNode("TEL", "090-1234-1234", new TypeSet("CELL"));
|
||||
}
|
||||
|
||||
public void testJapanesePhoneNumberV21_1() {
|
||||
testJapanesePhoneNumberCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE);
|
||||
}
|
||||
|
||||
public void testJapanesePhoneNumberV30() {
|
||||
testJapanesePhoneNumberCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE);
|
||||
}
|
||||
|
||||
public void testJapanesePhoneNumberDoCoMo() {
|
||||
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS");
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||
.put(Phone.NUMBER, "0312341234")
|
||||
.put(Phone.TYPE, Phone.TYPE_HOME);
|
||||
entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||
.put(Phone.NUMBER, "09012341234")
|
||||
.put(Phone.TYPE, Phone.TYPE_MOBILE);
|
||||
mVerifier.addPropertyNodesVerifierElemWithEmptyName()
|
||||
.addExpectedNode("EMAIL", "", new TypeSet("HOME"))
|
||||
.addExpectedNode("X-CLASS", "PUBLIC")
|
||||
.addExpectedNode("X-REDUCTION", "")
|
||||
.addExpectedNode("X-NO", "")
|
||||
.addExpectedNode("X-DCM-HMN-MODE", "")
|
||||
.addExpectedNode("ADR", "", new TypeSet("HOME"))
|
||||
.addExpectedNode("TEL", "03-1234-1234", new TypeSet("HOME"))
|
||||
.addExpectedNode("TEL", "090-1234-1234", new TypeSet("CELL"));
|
||||
}
|
||||
|
||||
public void testNoteDoCoMo() {
|
||||
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS");
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(Note.CONTENT_ITEM_TYPE)
|
||||
.put(Note.NOTE, "note1");
|
||||
entry.addContentValues(Note.CONTENT_ITEM_TYPE)
|
||||
.put(Note.NOTE, "note2");
|
||||
entry.addContentValues(Note.CONTENT_ITEM_TYPE)
|
||||
.put(Note.NOTE, "note3");
|
||||
|
||||
// More than one note fields must be aggregated into one note.
|
||||
mVerifier.addPropertyNodesVerifierElemWithEmptyName()
|
||||
.addExpectedNode("TEL", "", new TypeSet("HOME"))
|
||||
.addExpectedNode("EMAIL", "", new TypeSet("HOME"))
|
||||
.addExpectedNode("X-CLASS", "PUBLIC")
|
||||
.addExpectedNode("X-REDUCTION", "")
|
||||
.addExpectedNode("X-NO", "")
|
||||
.addExpectedNode("X-DCM-HMN-MODE", "")
|
||||
.addExpectedNode("ADR", "", new TypeSet("HOME"))
|
||||
.addExpectedNode("NOTE", "note1\nnote2\nnote3", mContentValuesForQP);
|
||||
}
|
||||
|
||||
public void testAndroidCustomV21() {
|
||||
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V21_GENERIC);
|
||||
mVerifier.addInputEntry().addContentValues(Nickname.CONTENT_ITEM_TYPE)
|
||||
.put(Nickname.NAME, "\u304D\u3083\u30FC\u30A8\u30C3\u30C1\u30FC");
|
||||
mVerifier.addPropertyNodesVerifierElemWithEmptyName()
|
||||
.addExpectedNode("X-ANDROID-CUSTOM",
|
||||
Arrays.asList(Nickname.CONTENT_ITEM_TYPE,
|
||||
"\u304D\u3083\u30FC\u30A8\u30C3\u30C1\u30FC",
|
||||
"", "", "", "", "", "", "", "", "", "", "", "", "", ""),
|
||||
mContentValuesForQPAndUtf8);
|
||||
}
|
||||
}
|
||||
85
vcard/tests/src/com/android/vcard/tests/VCardTestsBase.java
Normal file
85
vcard/tests/src/com/android/vcard/tests/VCardTestsBase.java
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.vcard.tests;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import com.android.vcard.VCardConfig;
|
||||
import com.android.vcard.tests.test_utils.VCardVerifier;
|
||||
|
||||
/**
|
||||
* BaseClass for vCard unit tests with utility classes.
|
||||
* Please do not add each unit test here.
|
||||
*/
|
||||
/* package */ class VCardTestsBase extends AndroidTestCase {
|
||||
public static final int V21 = VCardConfig.VCARD_TYPE_V21_GENERIC;
|
||||
public static final int V30 = VCardConfig.VCARD_TYPE_V30_GENERIC;
|
||||
|
||||
// Do not modify these during tests.
|
||||
protected final ContentValues mContentValuesForQP;
|
||||
protected final ContentValues mContentValuesForSJis;
|
||||
protected final ContentValues mContentValuesForUtf8;
|
||||
protected final ContentValues mContentValuesForQPAndSJis;
|
||||
protected final ContentValues mContentValuesForQPAndUtf8;
|
||||
protected final ContentValues mContentValuesForBase64V21;
|
||||
protected final ContentValues mContentValuesForBase64V30;
|
||||
|
||||
protected VCardVerifier mVerifier;
|
||||
private boolean mSkipVerification;
|
||||
|
||||
public VCardTestsBase() {
|
||||
super();
|
||||
// Not using constants in vCard code since it may be wrong.
|
||||
mContentValuesForQP = new ContentValues();
|
||||
mContentValuesForQP.put("ENCODING", "QUOTED-PRINTABLE");
|
||||
mContentValuesForSJis = new ContentValues();
|
||||
mContentValuesForSJis.put("CHARSET", "SHIFT_JIS");
|
||||
mContentValuesForUtf8 = new ContentValues();
|
||||
mContentValuesForUtf8.put("CHARSET", "UTF-8");
|
||||
mContentValuesForQPAndSJis = new ContentValues();
|
||||
mContentValuesForQPAndSJis.put("ENCODING", "QUOTED-PRINTABLE");
|
||||
mContentValuesForQPAndSJis.put("CHARSET", "SHIFT_JIS");
|
||||
mContentValuesForQPAndUtf8 = new ContentValues();
|
||||
mContentValuesForQPAndUtf8.put("ENCODING", "QUOTED-PRINTABLE");
|
||||
mContentValuesForQPAndUtf8.put("CHARSET", "UTF-8");
|
||||
mContentValuesForBase64V21 = new ContentValues();
|
||||
mContentValuesForBase64V21.put("ENCODING", "BASE64");
|
||||
mContentValuesForBase64V30 = new ContentValues();
|
||||
mContentValuesForBase64V30.put("ENCODING", "b");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testAndroidTestCaseSetupProperly() {
|
||||
super.testAndroidTestCaseSetupProperly();
|
||||
mSkipVerification = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception{
|
||||
super.setUp();
|
||||
mVerifier = new VCardVerifier(this);
|
||||
mSkipVerification = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tearDown() throws Exception {
|
||||
super.tearDown();
|
||||
if (!mSkipVerification) {
|
||||
mVerifier.verify();
|
||||
}
|
||||
}
|
||||
}
|
||||
84
vcard/tests/src/com/android/vcard/tests/VCardUtilsTests.java
Normal file
84
vcard/tests/src/com/android/vcard/tests/VCardUtilsTests.java
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.vcard.tests;
|
||||
|
||||
import com.android.vcard.VCardUtils;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class VCardUtilsTests extends TestCase {
|
||||
public void testContainsOnlyPrintableAscii() {
|
||||
assertTrue(VCardUtils.containsOnlyPrintableAscii((String)null));
|
||||
assertTrue(VCardUtils.containsOnlyPrintableAscii((String[])null));
|
||||
assertTrue(VCardUtils.containsOnlyPrintableAscii((List<String>)null));
|
||||
assertTrue(VCardUtils.containsOnlyPrintableAscii(""));
|
||||
assertTrue(VCardUtils.containsOnlyPrintableAscii("abcdefghijklmnopqrstuvwxyz"));
|
||||
assertTrue(VCardUtils.containsOnlyPrintableAscii("ABCDEFGHIJKLMNOPQRSTUVWXYZ"));
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (int i = 0x20; i < 0x7F; i++) {
|
||||
builder.append((char)i);
|
||||
}
|
||||
assertTrue(VCardUtils.containsOnlyPrintableAscii(builder.toString()));
|
||||
assertTrue(VCardUtils.containsOnlyPrintableAscii("\r\n"));
|
||||
assertFalse(VCardUtils.containsOnlyPrintableAscii("\u0019"));
|
||||
assertFalse(VCardUtils.containsOnlyPrintableAscii("\u007F"));
|
||||
}
|
||||
|
||||
public void testContainsOnlyNonCrLfPrintableAscii() {
|
||||
assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii((String)null));
|
||||
assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii((String[])null));
|
||||
assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii((List<String>)null));
|
||||
assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii(""));
|
||||
assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii("abcdefghijklmnopqrstuvwxyz"));
|
||||
assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii("ABCDEFGHIJKLMNOPQRSTUVWXYZ"));
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (int i = 0x20; i < 0x7F; i++) {
|
||||
builder.append((char)i);
|
||||
}
|
||||
assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii(builder.toString()));
|
||||
assertFalse(VCardUtils.containsOnlyNonCrLfPrintableAscii("\u0019"));
|
||||
assertFalse(VCardUtils.containsOnlyNonCrLfPrintableAscii("\u007F"));
|
||||
assertFalse(VCardUtils.containsOnlyNonCrLfPrintableAscii("\r"));
|
||||
assertFalse(VCardUtils.containsOnlyNonCrLfPrintableAscii("\n"));
|
||||
}
|
||||
|
||||
public void testContainsOnlyAlphaDigitHyphen() {
|
||||
assertTrue(VCardUtils.containsOnlyAlphaDigitHyphen((String)null));
|
||||
assertTrue(VCardUtils.containsOnlyAlphaDigitHyphen((String[])null));
|
||||
assertTrue(VCardUtils.containsOnlyAlphaDigitHyphen((List<String>)null));
|
||||
assertTrue(VCardUtils.containsOnlyAlphaDigitHyphen(""));
|
||||
assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii("abcdefghijklmnopqrstuvwxyz"));
|
||||
assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii("ABCDEFGHIJKLMNOPQRSTUVWXYZ"));
|
||||
assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii("0123456789-"));
|
||||
for (int i = 0; i < 0x30; i++) {
|
||||
if (i == 0x2D) { // -
|
||||
continue;
|
||||
}
|
||||
assertFalse(VCardUtils.containsOnlyAlphaDigitHyphen(String.valueOf((char)i)));
|
||||
}
|
||||
for (int i = 0x3A; i < 0x41; i++) {
|
||||
assertFalse(VCardUtils.containsOnlyAlphaDigitHyphen(String.valueOf((char)i)));
|
||||
}
|
||||
for (int i = 0x5B; i < 0x61; i++) {
|
||||
assertFalse(VCardUtils.containsOnlyAlphaDigitHyphen(String.valueOf((char)i)));
|
||||
}
|
||||
for (int i = 0x7B; i < 0x100; i++) {
|
||||
assertFalse(VCardUtils.containsOnlyAlphaDigitHyphen(String.valueOf((char)i)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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 com.android.vcard.tests.test_utils;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.provider.ContactsContract.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* The class representing one contact, which should contain multiple ContentValues like
|
||||
* StructuredName, Email, etc.
|
||||
* </p>
|
||||
*/
|
||||
public final class ContactEntry {
|
||||
private final List<ContentValues> mContentValuesList = new ArrayList<ContentValues>();
|
||||
|
||||
public ContentValuesBuilder addContentValues(String mimeType) {
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(Data.MIMETYPE, mimeType);
|
||||
mContentValuesList.add(contentValues);
|
||||
return new ContentValuesBuilder(contentValues);
|
||||
}
|
||||
|
||||
public List<ContentValues> getList() {
|
||||
return mContentValuesList;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.vcard.tests.test_utils;
|
||||
|
||||
import android.content.ContentValues;
|
||||
|
||||
/**
|
||||
* ContentValues-like class which enables users to chain put() methods and restricts
|
||||
* the other methods.
|
||||
*/
|
||||
public class ContentValuesBuilder {
|
||||
private final ContentValues mContentValues;
|
||||
|
||||
public ContentValuesBuilder(final ContentValues contentValues) {
|
||||
mContentValues = contentValues;
|
||||
}
|
||||
|
||||
public ContentValuesBuilder put(String key, String value) {
|
||||
mContentValues.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ContentValuesBuilder put(String key, Byte value) {
|
||||
mContentValues.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ContentValuesBuilder put(String key, Short value) {
|
||||
mContentValues.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ContentValuesBuilder put(String key, Integer value) {
|
||||
mContentValues.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ContentValuesBuilder put(String key, Long value) {
|
||||
mContentValues.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ContentValuesBuilder put(String key, Float value) {
|
||||
mContentValues.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ContentValuesBuilder put(String key, Double value) {
|
||||
mContentValues.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ContentValuesBuilder put(String key, Boolean value) {
|
||||
mContentValues.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ContentValuesBuilder put(String key, byte[] value) {
|
||||
mContentValues.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ContentValuesBuilder putNull(String key) {
|
||||
mContentValues.putNull(key);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
@ -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 com.android.vcard.tests.test_utils;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import com.android.vcard.VCardConfig;
|
||||
import com.android.vcard.VCardEntry;
|
||||
import com.android.vcard.VCardEntryConstructor;
|
||||
import com.android.vcard.VCardEntryHandler;
|
||||
import com.android.vcard.VCardParser;
|
||||
import com.android.vcard.VCardParser_V21;
|
||||
import com.android.vcard.VCardParser_V30;
|
||||
import com.android.vcard.exception.VCardException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ContentValuesVerifier implements VCardEntryHandler {
|
||||
private AndroidTestCase mTestCase;
|
||||
private List<ContentValuesVerifierElem> mContentValuesVerifierElemList =
|
||||
new ArrayList<ContentValuesVerifierElem>();
|
||||
private int mIndex;
|
||||
|
||||
public ContentValuesVerifierElem addElem(AndroidTestCase androidTestCase) {
|
||||
mTestCase = androidTestCase;
|
||||
ContentValuesVerifierElem importVerifier = new ContentValuesVerifierElem(androidTestCase);
|
||||
mContentValuesVerifierElemList.add(importVerifier);
|
||||
return importVerifier;
|
||||
}
|
||||
|
||||
public void verify(int resId, int vCardType) throws IOException, VCardException {
|
||||
verify(mTestCase.getContext().getResources().openRawResource(resId), vCardType);
|
||||
}
|
||||
|
||||
public void verify(int resId, int vCardType, final VCardParser vCardParser)
|
||||
throws IOException, VCardException {
|
||||
verify(mTestCase.getContext().getResources().openRawResource(resId),
|
||||
vCardType, vCardParser);
|
||||
}
|
||||
|
||||
public void verify(InputStream is, int vCardType) throws IOException, VCardException {
|
||||
final VCardParser vCardParser;
|
||||
if (VCardConfig.isV30(vCardType)) {
|
||||
vCardParser = new VCardParser_V30();
|
||||
} else {
|
||||
vCardParser = new VCardParser_V21();
|
||||
}
|
||||
verify(is, vCardType, vCardParser);
|
||||
}
|
||||
|
||||
public void verify(InputStream is, int vCardType, final VCardParser vCardParser)
|
||||
throws IOException, VCardException {
|
||||
VCardEntryConstructor builder =
|
||||
new VCardEntryConstructor(vCardType, null, null, false);
|
||||
builder.addEntryHandler(this);
|
||||
try {
|
||||
vCardParser.parse(is, builder);
|
||||
} finally {
|
||||
if (is != null) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onStart() {
|
||||
for (ContentValuesVerifierElem elem : mContentValuesVerifierElemList) {
|
||||
elem.onParsingStart();
|
||||
}
|
||||
}
|
||||
|
||||
public void onEntryCreated(VCardEntry entry) {
|
||||
mTestCase.assertTrue(mIndex < mContentValuesVerifierElemList.size());
|
||||
mContentValuesVerifierElemList.get(mIndex).onEntryCreated(entry);
|
||||
mIndex++;
|
||||
}
|
||||
|
||||
public void onEnd() {
|
||||
for (ContentValuesVerifierElem elem : mContentValuesVerifierElemList) {
|
||||
elem.onParsingEnd();
|
||||
elem.verifyResolver();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.vcard.tests.test_utils;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.provider.ContactsContract.Data;
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import com.android.vcard.VCardConfig;
|
||||
import com.android.vcard.VCardEntry;
|
||||
import com.android.vcard.VCardEntryCommitter;
|
||||
import com.android.vcard.VCardEntryConstructor;
|
||||
import com.android.vcard.VCardEntryHandler;
|
||||
import com.android.vcard.VCardParser;
|
||||
import com.android.vcard.VCardParser_V21;
|
||||
import com.android.vcard.VCardParser_V30;
|
||||
import com.android.vcard.exception.VCardException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class ContentValuesVerifierElem {
|
||||
private final AndroidTestCase mTestCase;
|
||||
private final ImportTestResolver mResolver;
|
||||
private final VCardEntryHandler mHandler;
|
||||
|
||||
public ContentValuesVerifierElem(AndroidTestCase androidTestCase) {
|
||||
mTestCase = androidTestCase;
|
||||
mResolver = new ImportTestResolver(androidTestCase);
|
||||
mHandler = new VCardEntryCommitter(mResolver);
|
||||
}
|
||||
|
||||
public ContentValuesBuilder addExpected(String mimeType) {
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(Data.MIMETYPE, mimeType);
|
||||
mResolver.addExpectedContentValues(contentValues);
|
||||
return new ContentValuesBuilder(contentValues);
|
||||
}
|
||||
|
||||
public void verify(int resId, int vCardType)
|
||||
throws IOException, VCardException {
|
||||
verify(mTestCase.getContext().getResources().openRawResource(resId), vCardType);
|
||||
}
|
||||
|
||||
public void verify(InputStream is, int vCardType) throws IOException, VCardException {
|
||||
final VCardParser vCardParser;
|
||||
if (VCardConfig.isV30(vCardType)) {
|
||||
vCardParser = new VCardParser_V30();
|
||||
} else {
|
||||
vCardParser = new VCardParser_V21();
|
||||
}
|
||||
VCardEntryConstructor builder =
|
||||
new VCardEntryConstructor(vCardType, null, null, false);
|
||||
builder.addEntryHandler(mHandler);
|
||||
try {
|
||||
vCardParser.parse(is, builder);
|
||||
} finally {
|
||||
if (is != null) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
verifyResolver();
|
||||
}
|
||||
|
||||
public void verifyResolver() {
|
||||
mResolver.verify();
|
||||
}
|
||||
|
||||
public void onParsingStart() {
|
||||
mHandler.onStart();
|
||||
}
|
||||
|
||||
public void onEntryCreated(VCardEntry entry) {
|
||||
mHandler.onEntryCreated(entry);
|
||||
}
|
||||
|
||||
public void onParsingEnd() {
|
||||
mHandler.onEnd();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
* 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 com.android.vcard.tests.test_utils;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Entity;
|
||||
import android.content.EntityIterator;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.provider.ContactsContract.Contacts;
|
||||
import android.provider.ContactsContract.Data;
|
||||
import android.provider.ContactsContract.RawContacts;
|
||||
import android.test.mock.MockContentProvider;
|
||||
import android.test.mock.MockCursor;
|
||||
|
||||
import com.android.vcard.VCardComposer;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/* package */ class ExportTestProvider extends MockContentProvider {
|
||||
final private TestCase mTestCase;
|
||||
final private ArrayList<ContactEntry> mContactEntryList = new ArrayList<ContactEntry>();
|
||||
|
||||
private static class MockEntityIterator implements EntityIterator {
|
||||
List<Entity> mEntityList;
|
||||
Iterator<Entity> mIterator;
|
||||
|
||||
public MockEntityIterator(List<ContentValues> contentValuesList) {
|
||||
mEntityList = new ArrayList<Entity>();
|
||||
Entity entity = new Entity(new ContentValues());
|
||||
for (ContentValues contentValues : contentValuesList) {
|
||||
entity.addSubValue(Data.CONTENT_URI, contentValues);
|
||||
}
|
||||
mEntityList.add(entity);
|
||||
mIterator = mEntityList.iterator();
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return mIterator.hasNext();
|
||||
}
|
||||
|
||||
public Entity next() {
|
||||
return mIterator.next();
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException("remove not supported");
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
mIterator = mEntityList.iterator();
|
||||
}
|
||||
|
||||
public void close() {
|
||||
}
|
||||
}
|
||||
|
||||
public ExportTestProvider(TestCase testCase) {
|
||||
mTestCase = testCase;
|
||||
}
|
||||
|
||||
public ContactEntry buildInputEntry() {
|
||||
ContactEntry contactEntry = new ContactEntry();
|
||||
mContactEntryList.add(contactEntry);
|
||||
return contactEntry;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* An old method which had existed but was removed from ContentResolver.
|
||||
* </p>
|
||||
* <p>
|
||||
* We still keep using this method since we don't have a propeer way to know
|
||||
* which value in the ContentValue corresponds to the entry in Contacts database.
|
||||
* </p>
|
||||
*/
|
||||
public EntityIterator queryEntities(Uri uri,
|
||||
String selection, String[] selectionArgs, String sortOrder) {
|
||||
mTestCase.assertTrue(uri != null);
|
||||
mTestCase.assertTrue(ContentResolver.SCHEME_CONTENT.equals(uri.getScheme()));
|
||||
final String authority = uri.getAuthority();
|
||||
mTestCase.assertTrue(RawContacts.CONTENT_URI.getAuthority().equals(authority));
|
||||
mTestCase.assertTrue((Data.CONTACT_ID + "=?").equals(selection));
|
||||
mTestCase.assertEquals(1, selectionArgs.length);
|
||||
final int id = Integer.parseInt(selectionArgs[0]);
|
||||
mTestCase.assertTrue(id >= 0 && id < mContactEntryList.size());
|
||||
|
||||
return new MockEntityIterator(mContactEntryList.get(id).getList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor query(Uri uri,String[] projection,
|
||||
String selection, String[] selectionArgs, String sortOrder) {
|
||||
mTestCase.assertTrue(VCardComposer.CONTACTS_TEST_CONTENT_URI.equals(uri));
|
||||
// In this test, following arguments are not supported.
|
||||
mTestCase.assertNull(selection);
|
||||
mTestCase.assertNull(selectionArgs);
|
||||
mTestCase.assertNull(sortOrder);
|
||||
|
||||
return new MockCursor() {
|
||||
int mCurrentPosition = -1;
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mContactEntryList.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean moveToFirst() {
|
||||
mCurrentPosition = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean moveToNext() {
|
||||
if (mCurrentPosition < mContactEntryList.size()) {
|
||||
mCurrentPosition++;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBeforeFirst() {
|
||||
return mCurrentPosition < 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAfterLast() {
|
||||
return mCurrentPosition >= mContactEntryList.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnIndex(String columnName) {
|
||||
mTestCase.assertEquals(Contacts._ID, columnName);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(int columnIndex) {
|
||||
mTestCase.assertEquals(0, columnIndex);
|
||||
mTestCase.assertTrue(mCurrentPosition >= 0
|
||||
&& mCurrentPosition < mContactEntryList.size());
|
||||
return mCurrentPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(int columnIndex) {
|
||||
return String.valueOf(getInt(columnIndex));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.android.vcard.tests.test_utils;
|
||||
|
||||
import android.provider.ContactsContract.RawContacts;
|
||||
import android.test.mock.MockContentResolver;
|
||||
|
||||
import com.android.vcard.VCardComposer;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/* package */ class ExportTestResolver extends MockContentResolver {
|
||||
private final ExportTestProvider mProvider;
|
||||
public ExportTestResolver(TestCase testCase) {
|
||||
mProvider = new ExportTestProvider(testCase);
|
||||
addProvider(VCardComposer.VCARD_TEST_AUTHORITY, mProvider);
|
||||
addProvider(RawContacts.CONTENT_URI.getAuthority(), mProvider);
|
||||
}
|
||||
|
||||
public ContactEntry addInputContactEntry() {
|
||||
return mProvider.buildInputEntry();
|
||||
}
|
||||
|
||||
public ExportTestProvider getProvider() {
|
||||
return mProvider;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,274 @@
|
|||
/*
|
||||
* 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 com.android.vcard.tests.test_utils;
|
||||
|
||||
import android.content.ContentProviderOperation;
|
||||
import android.content.ContentProviderResult;
|
||||
import android.content.ContentValues;
|
||||
import android.net.Uri;
|
||||
import android.provider.ContactsContract.Data;
|
||||
import android.provider.ContactsContract.RawContacts;
|
||||
import android.provider.ContactsContract.CommonDataKinds.Email;
|
||||
import android.provider.ContactsContract.CommonDataKinds.Event;
|
||||
import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
|
||||
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.test.mock.MockContentProvider;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/* package */ class ImportTestProvider extends MockContentProvider {
|
||||
private static final Set<String> sKnownMimeTypeSet =
|
||||
new HashSet<String>(Arrays.asList(StructuredName.CONTENT_ITEM_TYPE,
|
||||
Nickname.CONTENT_ITEM_TYPE, Phone.CONTENT_ITEM_TYPE,
|
||||
Email.CONTENT_ITEM_TYPE, StructuredPostal.CONTENT_ITEM_TYPE,
|
||||
Im.CONTENT_ITEM_TYPE, Organization.CONTENT_ITEM_TYPE,
|
||||
Event.CONTENT_ITEM_TYPE, Photo.CONTENT_ITEM_TYPE,
|
||||
Note.CONTENT_ITEM_TYPE, Website.CONTENT_ITEM_TYPE,
|
||||
Relation.CONTENT_ITEM_TYPE, Event.CONTENT_ITEM_TYPE,
|
||||
GroupMembership.CONTENT_ITEM_TYPE));
|
||||
|
||||
final Map<String, Collection<ContentValues>> mMimeTypeToExpectedContentValues;
|
||||
|
||||
private final TestCase mTestCase;
|
||||
|
||||
public ImportTestProvider(TestCase testCase) {
|
||||
mTestCase = testCase;
|
||||
mMimeTypeToExpectedContentValues =
|
||||
new HashMap<String, Collection<ContentValues>>();
|
||||
for (String acceptanbleMimeType : sKnownMimeTypeSet) {
|
||||
// Do not use HashSet since the current implementation changes the content of
|
||||
// ContentValues after the insertion, which make the result of hashCode()
|
||||
// changes...
|
||||
mMimeTypeToExpectedContentValues.put(
|
||||
acceptanbleMimeType, new ArrayList<ContentValues>());
|
||||
}
|
||||
}
|
||||
|
||||
public void addExpectedContentValues(ContentValues expectedContentValues) {
|
||||
final String mimeType = expectedContentValues.getAsString(Data.MIMETYPE);
|
||||
if (!sKnownMimeTypeSet.contains(mimeType)) {
|
||||
mTestCase.fail(String.format(
|
||||
"Unknow MimeType %s in the test code. Test code should be broken.",
|
||||
mimeType));
|
||||
}
|
||||
|
||||
final Collection<ContentValues> contentValuesCollection =
|
||||
mMimeTypeToExpectedContentValues.get(mimeType);
|
||||
contentValuesCollection.add(expectedContentValues);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContentProviderResult[] applyBatch(
|
||||
ArrayList<ContentProviderOperation> operations) {
|
||||
if (operations == null) {
|
||||
mTestCase.fail("There is no operation.");
|
||||
}
|
||||
|
||||
final int size = operations.size();
|
||||
ContentProviderResult[] fakeResultArray = new ContentProviderResult[size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
Uri uri = Uri.withAppendedPath(RawContacts.CONTENT_URI, String.valueOf(i));
|
||||
fakeResultArray[i] = new ContentProviderResult(uri);
|
||||
}
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
ContentProviderOperation operation = operations.get(i);
|
||||
ContentValues contentValues = operation.resolveValueBackReferences(
|
||||
fakeResultArray, i);
|
||||
}
|
||||
for (int i = 0; i < size; i++) {
|
||||
ContentProviderOperation operation = operations.get(i);
|
||||
ContentValues actualContentValues = operation.resolveValueBackReferences(
|
||||
fakeResultArray, i);
|
||||
final Uri uri = operation.getUri();
|
||||
if (uri.equals(RawContacts.CONTENT_URI)) {
|
||||
mTestCase.assertNull(actualContentValues.get(RawContacts.ACCOUNT_NAME));
|
||||
mTestCase.assertNull(actualContentValues.get(RawContacts.ACCOUNT_TYPE));
|
||||
} else if (uri.equals(Data.CONTENT_URI)) {
|
||||
final String mimeType = actualContentValues.getAsString(Data.MIMETYPE);
|
||||
if (!sKnownMimeTypeSet.contains(mimeType)) {
|
||||
mTestCase.fail(String.format(
|
||||
"Unknown MimeType %s. Probably added after developing this test",
|
||||
mimeType));
|
||||
}
|
||||
// Remove data meaningless in this unit tests.
|
||||
// Specifically, Data.DATA1 - DATA7 are set to null or empty String
|
||||
// regardless of the input, but it may change depending on how
|
||||
// resolver-related code handles it.
|
||||
// Here, we ignore these implementation-dependent specs and
|
||||
// just check whether vCard importer correctly inserts rellevent data.
|
||||
Set<String> keyToBeRemoved = new HashSet<String>();
|
||||
for (Entry<String, Object> entry : actualContentValues.valueSet()) {
|
||||
Object value = entry.getValue();
|
||||
if (value == null || TextUtils.isEmpty(value.toString())) {
|
||||
keyToBeRemoved.add(entry.getKey());
|
||||
}
|
||||
}
|
||||
for (String key: keyToBeRemoved) {
|
||||
actualContentValues.remove(key);
|
||||
}
|
||||
/* for testing
|
||||
Log.d("@@@",
|
||||
String.format("MimeType: %s, data: %s",
|
||||
mimeType, actualContentValues.toString())); */
|
||||
// Remove RAW_CONTACT_ID entry just for safety, since we do not care
|
||||
// how resolver-related code handles the entry in this unit test,
|
||||
if (actualContentValues.containsKey(Data.RAW_CONTACT_ID)) {
|
||||
actualContentValues.remove(Data.RAW_CONTACT_ID);
|
||||
}
|
||||
final Collection<ContentValues> contentValuesCollection =
|
||||
mMimeTypeToExpectedContentValues.get(mimeType);
|
||||
if (contentValuesCollection.isEmpty()) {
|
||||
mTestCase.fail("ContentValues for MimeType " + mimeType
|
||||
+ " is not expected at all (" + actualContentValues + ")");
|
||||
}
|
||||
boolean checked = false;
|
||||
for (ContentValues expectedContentValues : contentValuesCollection) {
|
||||
/*for testing
|
||||
Log.d("@@@", "expected: "
|
||||
+ convertToEasilyReadableString(expectedContentValues));
|
||||
Log.d("@@@", "actual : "
|
||||
+ convertToEasilyReadableString(actualContentValues));*/
|
||||
if (equalsForContentValues(expectedContentValues,
|
||||
actualContentValues)) {
|
||||
mTestCase.assertTrue(contentValuesCollection.remove(expectedContentValues));
|
||||
checked = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!checked) {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
builder.append("Unexpected: ");
|
||||
builder.append(convertToEasilyReadableString(actualContentValues));
|
||||
builder.append("\nExpected: ");
|
||||
for (ContentValues expectedContentValues : contentValuesCollection) {
|
||||
builder.append(convertToEasilyReadableString(expectedContentValues));
|
||||
}
|
||||
mTestCase.fail(builder.toString());
|
||||
}
|
||||
} else {
|
||||
mTestCase.fail("Unexpected Uri has come: " + uri);
|
||||
}
|
||||
} // for (int i = 0; i < size; i++) {
|
||||
return fakeResultArray;
|
||||
}
|
||||
|
||||
public void verify() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (Collection<ContentValues> contentValuesCollection :
|
||||
mMimeTypeToExpectedContentValues.values()) {
|
||||
for (ContentValues expectedContentValues: contentValuesCollection) {
|
||||
builder.append(convertToEasilyReadableString(expectedContentValues));
|
||||
builder.append("\n");
|
||||
}
|
||||
}
|
||||
if (builder.length() > 0) {
|
||||
final String failMsg =
|
||||
"There is(are) remaining expected ContentValues instance(s): \n"
|
||||
+ builder.toString();
|
||||
mTestCase.fail(failMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to print ContentValues whose content is printed with sorted keys.
|
||||
*/
|
||||
private String convertToEasilyReadableString(ContentValues contentValues) {
|
||||
if (contentValues == null) {
|
||||
return "null";
|
||||
}
|
||||
String mimeTypeValue = "";
|
||||
SortedMap<String, String> sortedMap = new TreeMap<String, String>();
|
||||
for (Entry<String, Object> entry : contentValues.valueSet()) {
|
||||
final String key = entry.getKey();
|
||||
final Object value = entry.getValue();
|
||||
final String valueString = (value != null ? value.toString() : null);
|
||||
if (Data.MIMETYPE.equals(key)) {
|
||||
mimeTypeValue = valueString;
|
||||
} else {
|
||||
mTestCase.assertNotNull(key);
|
||||
sortedMap.put(key, valueString);
|
||||
}
|
||||
}
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(Data.MIMETYPE);
|
||||
builder.append('=');
|
||||
builder.append(mimeTypeValue);
|
||||
for (Entry<String, String> entry : sortedMap.entrySet()) {
|
||||
final String key = entry.getKey();
|
||||
final String value = entry.getValue();
|
||||
builder.append(' ');
|
||||
builder.append(key);
|
||||
builder.append("=\"");
|
||||
builder.append(value);
|
||||
builder.append('"');
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private static boolean equalsForContentValues(
|
||||
ContentValues expected, ContentValues actual) {
|
||||
if (expected == actual) {
|
||||
return true;
|
||||
} else if (expected == null || actual == null || expected.size() != actual.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (Entry<String, Object> entry : expected.valueSet()) {
|
||||
final String key = entry.getKey();
|
||||
final Object value = entry.getValue();
|
||||
if (!actual.containsKey(key)) {
|
||||
return false;
|
||||
}
|
||||
if (value instanceof byte[]) {
|
||||
Object actualValue = actual.get(key);
|
||||
if (!Arrays.equals((byte[])value, (byte[])actualValue)) {
|
||||
byte[] e = (byte[])value;
|
||||
byte[] a = (byte[])actualValue;
|
||||
Log.d("@@@", "expected (len: " + e.length + "): " + Arrays.toString(e));
|
||||
Log.d("@@@", "actual (len: " + a.length + "): " + Arrays.toString(a));
|
||||
return false;
|
||||
}
|
||||
} else if (!value.equals(actual.get(key))) {
|
||||
Log.d("@@@", "different.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.vcard.tests.test_utils;
|
||||
|
||||
import android.content.ContentProviderOperation;
|
||||
import android.content.ContentProviderResult;
|
||||
import android.content.ContentValues;
|
||||
import android.provider.ContactsContract.RawContacts;
|
||||
import android.test.mock.MockContentResolver;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/* package */ class ImportTestResolver extends MockContentResolver {
|
||||
private final ImportTestProvider mProvider;
|
||||
|
||||
public ImportTestResolver(TestCase testCase) {
|
||||
mProvider = new ImportTestProvider(testCase);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContentProviderResult[] applyBatch(String authority,
|
||||
ArrayList<ContentProviderOperation> operations) {
|
||||
equalsString(authority, RawContacts.CONTENT_URI.toString());
|
||||
return mProvider.applyBatch(operations);
|
||||
}
|
||||
|
||||
public void addExpectedContentValues(ContentValues expectedContentValues) {
|
||||
mProvider.addExpectedContentValues(expectedContentValues);
|
||||
}
|
||||
|
||||
public void verify() {
|
||||
mProvider.verify();
|
||||
}
|
||||
|
||||
private static boolean equalsString(String a, String b) {
|
||||
if (a == null || a.length() == 0) {
|
||||
return b == null || b.length() == 0;
|
||||
} else {
|
||||
return a.equals(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.vcard.tests.test_utils;
|
||||
|
||||
import com.android.vcard.VCardComposer;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class LineVerifier implements VCardComposer.OneEntryHandler {
|
||||
private final TestCase mTestCase;
|
||||
private final ArrayList<LineVerifierElem> mLineVerifierElemList;
|
||||
private int mVCardType;
|
||||
private int index;
|
||||
|
||||
public LineVerifier(TestCase testCase, int vcardType) {
|
||||
mTestCase = testCase;
|
||||
mLineVerifierElemList = new ArrayList<LineVerifierElem>();
|
||||
mVCardType = vcardType;
|
||||
}
|
||||
|
||||
public LineVerifierElem addLineVerifierElem() {
|
||||
LineVerifierElem lineVerifier = new LineVerifierElem(mTestCase, mVCardType);
|
||||
mLineVerifierElemList.add(lineVerifier);
|
||||
return lineVerifier;
|
||||
}
|
||||
|
||||
public void verify(String vcard) {
|
||||
if (index >= mLineVerifierElemList.size()) {
|
||||
mTestCase.fail("Insufficient number of LineVerifier (" + index + ")");
|
||||
}
|
||||
|
||||
LineVerifierElem lineVerifier = mLineVerifierElemList.get(index);
|
||||
lineVerifier.verify(vcard);
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
public boolean onEntryCreated(String vcard) {
|
||||
verify(vcard);
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean onInit(Context context) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void onTerminate() {
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.android.vcard.tests.test_utils;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.android.vcard.VCardConfig;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class LineVerifierElem {
|
||||
private final TestCase mTestCase;
|
||||
private final List<String> mExpectedLineList = new ArrayList<String>();
|
||||
private final boolean mIsV30;
|
||||
|
||||
public LineVerifierElem(TestCase testCase, int vcardType) {
|
||||
mTestCase = testCase;
|
||||
mIsV30 = VCardConfig.isV30(vcardType);
|
||||
}
|
||||
|
||||
public LineVerifierElem addExpected(final String line) {
|
||||
if (!TextUtils.isEmpty(line)) {
|
||||
mExpectedLineList.add(line);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public void verify(final String vcard) {
|
||||
final String[] lineArray = vcard.split("\\r?\\n");
|
||||
final int length = lineArray.length;
|
||||
boolean beginExists = false;
|
||||
boolean endExists = false;
|
||||
boolean versionExists = false;
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
final String line = lineArray[i];
|
||||
if (TextUtils.isEmpty(line)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ("BEGIN:VCARD".equalsIgnoreCase(line)) {
|
||||
if (beginExists) {
|
||||
mTestCase.fail("Multiple \"BEGIN:VCARD\" line found");
|
||||
} else {
|
||||
beginExists = true;
|
||||
continue;
|
||||
}
|
||||
} else if ("END:VCARD".equalsIgnoreCase(line)) {
|
||||
if (endExists) {
|
||||
mTestCase.fail("Multiple \"END:VCARD\" line found");
|
||||
} else {
|
||||
endExists = true;
|
||||
continue;
|
||||
}
|
||||
} else if ((mIsV30 ? "VERSION:3.0" : "VERSION:2.1").equalsIgnoreCase(line)) {
|
||||
if (versionExists) {
|
||||
mTestCase.fail("Multiple VERSION line + found");
|
||||
} else {
|
||||
versionExists = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!beginExists) {
|
||||
mTestCase.fail("Property other than BEGIN came before BEGIN property: "
|
||||
+ line);
|
||||
} else if (endExists) {
|
||||
mTestCase.fail("Property other than END came after END property: "
|
||||
+ line);
|
||||
}
|
||||
|
||||
final int index = mExpectedLineList.indexOf(line);
|
||||
if (index >= 0) {
|
||||
mExpectedLineList.remove(index);
|
||||
} else {
|
||||
mTestCase.fail("Unexpected line: " + line);
|
||||
}
|
||||
}
|
||||
|
||||
if (!mExpectedLineList.isEmpty()) {
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
for (String expectedLine : mExpectedLineList) {
|
||||
buffer.append(expectedLine);
|
||||
buffer.append("\n");
|
||||
}
|
||||
|
||||
mTestCase.fail("Expected line(s) not found:" + buffer.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 com.android.vcard.tests.test_utils;
|
||||
|
||||
import android.content.ContentValues;
|
||||
|
||||
import com.android.vcard.VCardEntry;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* The class representing one property (e.g. "N;ENCODING=UTF-8:family:given:middle:prefix:suffix").
|
||||
* </p>
|
||||
* <p>
|
||||
* Previously used in main vCard handling code but now exists only for testing.
|
||||
* </p>
|
||||
* <p>
|
||||
* Especially useful for testing parser code (VCardParser), since all properties can be
|
||||
* checked via this class unlike {@link VCardEntry}, which only emits the result of
|
||||
* interpretation of the content of each vCard. We cannot know whether vCard parser or
|
||||
* {@link VCardEntry} is wrong without this class.
|
||||
* </p>
|
||||
*/
|
||||
public class PropertyNode {
|
||||
public String propName;
|
||||
public String propValue;
|
||||
public List<String> propValue_vector;
|
||||
|
||||
/** Store value as byte[],after decode.
|
||||
* Used when propValue is encoded by something like BASE64, QUOTED-PRINTABLE, etc.
|
||||
*/
|
||||
public byte[] propValue_bytes;
|
||||
|
||||
/**
|
||||
* param store: key=paramType, value=paramValue
|
||||
* Note that currently PropertyNode class does not support multiple param-values
|
||||
* defined in vCard 3.0 (See also RFC 2426). multiple-values are stored as
|
||||
* one String value like "A,B", not ["A", "B"]...
|
||||
* TODO: fix this.
|
||||
*/
|
||||
public ContentValues paramMap;
|
||||
|
||||
/** Only for TYPE=??? param store. */
|
||||
public Set<String> paramMap_TYPE;
|
||||
|
||||
/** Store group values. Used only in VCard. */
|
||||
public Set<String> propGroupSet;
|
||||
|
||||
public PropertyNode() {
|
||||
propName = "";
|
||||
propValue = "";
|
||||
propValue_vector = new ArrayList<String>();
|
||||
paramMap = new ContentValues();
|
||||
paramMap_TYPE = new HashSet<String>();
|
||||
propGroupSet = new HashSet<String>();
|
||||
}
|
||||
|
||||
public PropertyNode(
|
||||
String propName, String propValue, List<String> propValue_vector,
|
||||
byte[] propValue_bytes, ContentValues paramMap, Set<String> paramMap_TYPE,
|
||||
Set<String> propGroupSet) {
|
||||
if (propName != null) {
|
||||
this.propName = propName;
|
||||
} else {
|
||||
this.propName = "";
|
||||
}
|
||||
if (propValue != null) {
|
||||
this.propValue = propValue;
|
||||
} else {
|
||||
this.propValue = "";
|
||||
}
|
||||
if (propValue_vector != null) {
|
||||
this.propValue_vector = propValue_vector;
|
||||
} else {
|
||||
this.propValue_vector = new ArrayList<String>();
|
||||
}
|
||||
this.propValue_bytes = propValue_bytes;
|
||||
if (paramMap != null) {
|
||||
this.paramMap = paramMap;
|
||||
} else {
|
||||
this.paramMap = new ContentValues();
|
||||
}
|
||||
if (paramMap_TYPE != null) {
|
||||
this.paramMap_TYPE = paramMap_TYPE;
|
||||
} else {
|
||||
this.paramMap_TYPE = new HashSet<String>();
|
||||
}
|
||||
if (propGroupSet != null) {
|
||||
this.propGroupSet = propGroupSet;
|
||||
} else {
|
||||
this.propGroupSet = new HashSet<String>();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
// vCard may contain more than one same line in one entry, while HashSet or any other
|
||||
// library which utilize hashCode() does not honor that, so intentionally throw an
|
||||
// Exception.
|
||||
throw new UnsupportedOperationException(
|
||||
"PropertyNode does not provide hashCode() implementation intentionally.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof PropertyNode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PropertyNode node = (PropertyNode)obj;
|
||||
|
||||
if (propName == null || !propName.equals(node.propName)) {
|
||||
return false;
|
||||
} else if (!paramMap_TYPE.equals(node.paramMap_TYPE)) {
|
||||
return false;
|
||||
} else if (!paramMap_TYPE.equals(node.paramMap_TYPE)) {
|
||||
return false;
|
||||
} else if (!propGroupSet.equals(node.propGroupSet)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (propValue_bytes != null && Arrays.equals(propValue_bytes, node.propValue_bytes)) {
|
||||
return true;
|
||||
} else {
|
||||
if (!propValue.equals(node.propValue)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The value in propValue_vector is not decoded even if it should be
|
||||
// decoded by BASE64 or QUOTED-PRINTABLE. When the size of propValue_vector
|
||||
// is 1, the encoded value is stored in propValue, so we do not have to
|
||||
// check it.
|
||||
return (propValue_vector.equals(node.propValue_vector) ||
|
||||
propValue_vector.size() == 1 ||
|
||||
node.propValue_vector.size() == 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("propName: ");
|
||||
builder.append(propName);
|
||||
builder.append(", paramMap: ");
|
||||
builder.append(paramMap.toString());
|
||||
builder.append(", paramMap_TYPE: [");
|
||||
boolean first = true;
|
||||
for (String elem : paramMap_TYPE) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
builder.append(", ");
|
||||
}
|
||||
builder.append('"');
|
||||
builder.append(elem);
|
||||
builder.append('"');
|
||||
}
|
||||
builder.append("]");
|
||||
if (!propGroupSet.isEmpty()) {
|
||||
builder.append(", propGroupSet: [");
|
||||
first = true;
|
||||
for (String elem : propGroupSet) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
builder.append(", ");
|
||||
}
|
||||
builder.append('"');
|
||||
builder.append(elem);
|
||||
builder.append('"');
|
||||
}
|
||||
builder.append("]");
|
||||
}
|
||||
if (propValue_vector != null && propValue_vector.size() > 1) {
|
||||
builder.append(", propValue_vector size: ");
|
||||
builder.append(propValue_vector.size());
|
||||
}
|
||||
if (propValue_bytes != null) {
|
||||
builder.append(", propValue_bytes size: ");
|
||||
builder.append(propValue_bytes.length);
|
||||
}
|
||||
builder.append(", propValue: \"");
|
||||
builder.append(propValue);
|
||||
builder.append("\"");
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.vcard.tests.test_utils;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import com.android.vcard.VCardConfig;
|
||||
import com.android.vcard.VCardParser;
|
||||
import com.android.vcard.VCardParser_V21;
|
||||
import com.android.vcard.VCardParser_V30;
|
||||
import com.android.vcard.exception.VCardException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class PropertyNodesVerifier extends VNodeBuilder {
|
||||
private final List<PropertyNodesVerifierElem> mPropertyNodesVerifierElemList;
|
||||
private final AndroidTestCase mAndroidTestCase;
|
||||
private int mIndex;
|
||||
|
||||
public PropertyNodesVerifier(AndroidTestCase testCase) {
|
||||
super();
|
||||
mPropertyNodesVerifierElemList = new ArrayList<PropertyNodesVerifierElem>();
|
||||
mAndroidTestCase = testCase;
|
||||
}
|
||||
|
||||
public PropertyNodesVerifierElem addPropertyNodesVerifierElem() {
|
||||
PropertyNodesVerifierElem elem = new PropertyNodesVerifierElem(mAndroidTestCase);
|
||||
mPropertyNodesVerifierElemList.add(elem);
|
||||
return elem;
|
||||
}
|
||||
|
||||
public void verify(int resId, int vCardType)
|
||||
throws IOException, VCardException {
|
||||
verify(mAndroidTestCase.getContext().getResources().openRawResource(resId), vCardType);
|
||||
}
|
||||
|
||||
public void verify(int resId, int vCardType, final VCardParser vCardParser)
|
||||
throws IOException, VCardException {
|
||||
verify(mAndroidTestCase.getContext().getResources().openRawResource(resId),
|
||||
vCardType, vCardParser);
|
||||
}
|
||||
|
||||
public void verify(InputStream is, int vCardType) throws IOException, VCardException {
|
||||
final VCardParser vCardParser;
|
||||
if (VCardConfig.isV30(vCardType)) {
|
||||
vCardParser = new VCardParser_V30();
|
||||
} else {
|
||||
vCardParser = new VCardParser_V21();
|
||||
}
|
||||
verify(is, vCardType, vCardParser);
|
||||
}
|
||||
|
||||
public void verify(InputStream is, int vCardType, final VCardParser vCardParser)
|
||||
throws IOException, VCardException {
|
||||
try {
|
||||
vCardParser.parse(is, this);
|
||||
} finally {
|
||||
if (is != null) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endEntry() {
|
||||
super.endEntry();
|
||||
mAndroidTestCase.assertTrue(mIndex < mPropertyNodesVerifierElemList.size());
|
||||
mAndroidTestCase.assertTrue(mIndex < vNodeList.size());
|
||||
mPropertyNodesVerifierElemList.get(mIndex).verify(vNodeList.get(mIndex));
|
||||
mIndex++;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,317 @@
|
|||
/*
|
||||
* 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 com.android.vcard.tests.test_utils;
|
||||
|
||||
import android.content.ContentValues;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Utility class which verifies input VNode.
|
||||
*
|
||||
* This class first checks whether each propertyNode in the VNode is in the
|
||||
* "ordered expected property list".
|
||||
* If the node does not exist in the "ordered list", the class refers to
|
||||
* "unorderd expected property set" and checks the node is expected somewhere.
|
||||
*/
|
||||
public class PropertyNodesVerifierElem {
|
||||
public static class TypeSet extends HashSet<String> {
|
||||
public TypeSet(String ... array) {
|
||||
super(Arrays.asList(array));
|
||||
}
|
||||
}
|
||||
|
||||
public static class GroupSet extends HashSet<String> {
|
||||
public GroupSet(String ... array) {
|
||||
super(Arrays.asList(array));
|
||||
}
|
||||
}
|
||||
|
||||
private final HashMap<String, List<PropertyNode>> mOrderedNodeMap;
|
||||
// Intentionally use ArrayList instead of Set, assuming there may be more than one
|
||||
// exactly same objects.
|
||||
private final ArrayList<PropertyNode> mUnorderedNodeList;
|
||||
private final TestCase mTestCase;
|
||||
|
||||
public PropertyNodesVerifierElem(TestCase testCase) {
|
||||
mOrderedNodeMap = new HashMap<String, List<PropertyNode>>();
|
||||
mUnorderedNodeList = new ArrayList<PropertyNode>();
|
||||
mTestCase = testCase;
|
||||
}
|
||||
|
||||
// WithOrder
|
||||
|
||||
public PropertyNodesVerifierElem addExpectedNodeWithOrder(String propName, String propValue) {
|
||||
return addExpectedNodeWithOrder(propName, propValue, null, null, null, null, null);
|
||||
}
|
||||
|
||||
public PropertyNodesVerifierElem addExpectedNodeWithOrder(
|
||||
String propName, String propValue, ContentValues contentValues) {
|
||||
return addExpectedNodeWithOrder(propName, propValue, null,
|
||||
null, contentValues, null, null);
|
||||
}
|
||||
|
||||
public PropertyNodesVerifierElem addExpectedNodeWithOrder(
|
||||
String propName, List<String> propValueList, ContentValues contentValues) {
|
||||
return addExpectedNodeWithOrder(propName, null, propValueList,
|
||||
null, contentValues, null, null);
|
||||
}
|
||||
|
||||
public PropertyNodesVerifierElem addExpectedNodeWithOrder(
|
||||
String propName, String propValue, List<String> propValueList) {
|
||||
return addExpectedNodeWithOrder(propName, propValue, propValueList, null,
|
||||
null, null, null);
|
||||
}
|
||||
|
||||
public PropertyNodesVerifierElem addExpectedNodeWithOrder(
|
||||
String propName, List<String> propValueList) {
|
||||
final String propValue = concatinateListWithSemiColon(propValueList);
|
||||
return addExpectedNodeWithOrder(propName, propValue.toString(), propValueList,
|
||||
null, null, null, null);
|
||||
}
|
||||
|
||||
public PropertyNodesVerifierElem addExpectedNodeWithOrder(String propName, String propValue,
|
||||
TypeSet paramMap_TYPE) {
|
||||
return addExpectedNodeWithOrder(propName, propValue, null,
|
||||
null, null, paramMap_TYPE, null);
|
||||
}
|
||||
|
||||
public PropertyNodesVerifierElem addExpectedNodeWithOrder(String propName,
|
||||
List<String> propValueList, TypeSet paramMap_TYPE) {
|
||||
return addExpectedNodeWithOrder(propName, null, propValueList, null, null,
|
||||
paramMap_TYPE, null);
|
||||
}
|
||||
|
||||
public PropertyNodesVerifierElem addExpectedNodeWithOrder(String propName, String propValue,
|
||||
ContentValues paramMap, TypeSet paramMap_TYPE) {
|
||||
return addExpectedNodeWithOrder(propName, propValue, null, null,
|
||||
paramMap, paramMap_TYPE, null);
|
||||
}
|
||||
|
||||
public PropertyNodesVerifierElem addExpectedNodeWithOrder(String propName, String propValue,
|
||||
List<String> propValueList, TypeSet paramMap_TYPE) {
|
||||
return addExpectedNodeWithOrder(propName, propValue, propValueList, null, null,
|
||||
paramMap_TYPE, null);
|
||||
}
|
||||
|
||||
public PropertyNodesVerifierElem addExpectedNodeWithOrder(String propName, String propValue,
|
||||
List<String> propValueList, byte[] propValue_bytes,
|
||||
ContentValues paramMap, TypeSet paramMap_TYPE, GroupSet propGroupSet) {
|
||||
if (propValue == null && propValueList != null) {
|
||||
propValue = concatinateListWithSemiColon(propValueList);
|
||||
}
|
||||
PropertyNode propertyNode = new PropertyNode(propName,
|
||||
propValue, propValueList, propValue_bytes,
|
||||
paramMap, paramMap_TYPE, propGroupSet);
|
||||
List<PropertyNode> expectedNodeList = mOrderedNodeMap.get(propName);
|
||||
if (expectedNodeList == null) {
|
||||
expectedNodeList = new ArrayList<PropertyNode>();
|
||||
mOrderedNodeMap.put(propName, expectedNodeList);
|
||||
}
|
||||
expectedNodeList.add(propertyNode);
|
||||
return this;
|
||||
}
|
||||
|
||||
// WithoutOrder
|
||||
|
||||
public PropertyNodesVerifierElem addExpectedNode(String propName, String propValue) {
|
||||
return addExpectedNode(propName, propValue, null, null, null, null, null);
|
||||
}
|
||||
|
||||
public PropertyNodesVerifierElem addExpectedNode(String propName, String propValue,
|
||||
ContentValues contentValues) {
|
||||
return addExpectedNode(propName, propValue, null, null, contentValues, null, null);
|
||||
}
|
||||
|
||||
public PropertyNodesVerifierElem addExpectedNode(String propName,
|
||||
List<String> propValueList, ContentValues contentValues) {
|
||||
return addExpectedNode(propName, null,
|
||||
propValueList, null, contentValues, null, null);
|
||||
}
|
||||
|
||||
public PropertyNodesVerifierElem addExpectedNode(String propName, String propValue,
|
||||
List<String> propValueList) {
|
||||
return addExpectedNode(propName, propValue, propValueList, null, null, null, null);
|
||||
}
|
||||
|
||||
public PropertyNodesVerifierElem addExpectedNode(String propName,
|
||||
List<String> propValueList) {
|
||||
return addExpectedNode(propName, null, propValueList,
|
||||
null, null, null, null);
|
||||
}
|
||||
|
||||
public PropertyNodesVerifierElem addExpectedNode(String propName, String propValue,
|
||||
TypeSet paramMap_TYPE) {
|
||||
return addExpectedNode(propName, propValue, null, null, null, paramMap_TYPE, null);
|
||||
}
|
||||
|
||||
public PropertyNodesVerifierElem addExpectedNode(String propName,
|
||||
List<String> propValueList, TypeSet paramMap_TYPE) {
|
||||
final String propValue = concatinateListWithSemiColon(propValueList);
|
||||
return addExpectedNode(propName, propValue, propValueList, null, null,
|
||||
paramMap_TYPE, null);
|
||||
}
|
||||
|
||||
public PropertyNodesVerifierElem addExpectedNode(String propName, String propValue,
|
||||
List<String> propValueList, TypeSet paramMap_TYPE) {
|
||||
return addExpectedNode(propName, propValue, propValueList, null, null,
|
||||
paramMap_TYPE, null);
|
||||
}
|
||||
|
||||
public PropertyNodesVerifierElem addExpectedNode(String propName, String propValue,
|
||||
ContentValues paramMap, TypeSet paramMap_TYPE) {
|
||||
return addExpectedNode(propName, propValue, null, null,
|
||||
paramMap, paramMap_TYPE, null);
|
||||
}
|
||||
|
||||
public PropertyNodesVerifierElem addExpectedNode(String propName, String propValue,
|
||||
List<String> propValueList, byte[] propValue_bytes,
|
||||
ContentValues paramMap, TypeSet paramMap_TYPE, GroupSet propGroupSet) {
|
||||
if (propValue == null && propValueList != null) {
|
||||
propValue = concatinateListWithSemiColon(propValueList);
|
||||
}
|
||||
mUnorderedNodeList.add(new PropertyNode(propName, propValue,
|
||||
propValueList, propValue_bytes, paramMap, paramMap_TYPE, propGroupSet));
|
||||
return this;
|
||||
}
|
||||
|
||||
public void verify(VNode vnode) {
|
||||
for (PropertyNode actualNode : vnode.propList) {
|
||||
verifyNode(actualNode.propName, actualNode);
|
||||
}
|
||||
if (!mOrderedNodeMap.isEmpty() || !mUnorderedNodeList.isEmpty()) {
|
||||
List<String> expectedProps = new ArrayList<String>();
|
||||
for (List<PropertyNode> nodes : mOrderedNodeMap.values()) {
|
||||
for (PropertyNode node : nodes) {
|
||||
if (!expectedProps.contains(node.propName)) {
|
||||
expectedProps.add(node.propName);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (PropertyNode node : mUnorderedNodeList) {
|
||||
if (!expectedProps.contains(node.propName)) {
|
||||
expectedProps.add(node.propName);
|
||||
}
|
||||
}
|
||||
mTestCase.fail("Expected property " + Arrays.toString(expectedProps.toArray())
|
||||
+ " was not found.");
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyNode(final String propName, final PropertyNode actualNode) {
|
||||
List<PropertyNode> expectedNodeList = mOrderedNodeMap.get(propName);
|
||||
final int size = (expectedNodeList != null ? expectedNodeList.size() : 0);
|
||||
if (size > 0) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
PropertyNode expectedNode = expectedNodeList.get(i);
|
||||
List<PropertyNode> expectedButDifferentValueList = new ArrayList<PropertyNode>();
|
||||
if (expectedNode.propName.equals(propName)) {
|
||||
if (expectedNode.equals(actualNode)) {
|
||||
expectedNodeList.remove(i);
|
||||
if (expectedNodeList.size() == 0) {
|
||||
mOrderedNodeMap.remove(propName);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
expectedButDifferentValueList.add(expectedNode);
|
||||
}
|
||||
}
|
||||
|
||||
// "actualNode" is not in ordered expected list.
|
||||
// Try looking over unordered expected list.
|
||||
if (tryFoundExpectedNodeFromUnorderedList(actualNode,
|
||||
expectedButDifferentValueList)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!expectedButDifferentValueList.isEmpty()) {
|
||||
// Same propName exists but with different value(s).
|
||||
failWithExpectedNodeList(propName, actualNode,
|
||||
expectedButDifferentValueList);
|
||||
} else {
|
||||
// There's no expected node with same propName.
|
||||
mTestCase.fail("Unexpected property \"" + propName + "\" exists.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
List<PropertyNode> expectedButDifferentValueList =
|
||||
new ArrayList<PropertyNode>();
|
||||
if (tryFoundExpectedNodeFromUnorderedList(actualNode, expectedButDifferentValueList)) {
|
||||
return;
|
||||
} else {
|
||||
if (!expectedButDifferentValueList.isEmpty()) {
|
||||
// Same propName exists but with different value(s).
|
||||
failWithExpectedNodeList(propName, actualNode,
|
||||
expectedButDifferentValueList);
|
||||
} else {
|
||||
// There's no expected node with same propName.
|
||||
mTestCase.fail("Unexpected property \"" + propName + "\" exists.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String concatinateListWithSemiColon(List<String> array) {
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
boolean first = true;
|
||||
for (String propValueElem : array) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
buffer.append(';');
|
||||
}
|
||||
buffer.append(propValueElem);
|
||||
}
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
private boolean tryFoundExpectedNodeFromUnorderedList(PropertyNode actualNode,
|
||||
List<PropertyNode> expectedButDifferentValueList) {
|
||||
final String propName = actualNode.propName;
|
||||
int unorderedListSize = mUnorderedNodeList.size();
|
||||
for (int i = 0; i < unorderedListSize; i++) {
|
||||
PropertyNode unorderedExpectedNode = mUnorderedNodeList.get(i);
|
||||
if (unorderedExpectedNode.propName.equals(propName)) {
|
||||
if (unorderedExpectedNode.equals(actualNode)) {
|
||||
mUnorderedNodeList.remove(i);
|
||||
return true;
|
||||
}
|
||||
expectedButDifferentValueList.add(unorderedExpectedNode);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void failWithExpectedNodeList(String propName, PropertyNode actualNode,
|
||||
List<PropertyNode> expectedNodeList) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (PropertyNode expectedNode : expectedNodeList) {
|
||||
builder.append("expected: ");
|
||||
builder.append(expectedNode.toString());
|
||||
builder.append("\n");
|
||||
}
|
||||
mTestCase.fail("Property \"" + propName + "\" has wrong value.\n"
|
||||
+ builder.toString()
|
||||
+ " actual: " + actualNode.toString());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,339 @@
|
|||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.android.vcard.tests.test_utils;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.EntityIterator;
|
||||
import android.net.Uri;
|
||||
import android.test.AndroidTestCase;
|
||||
import android.test.mock.MockContext;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.vcard.VCardComposer;
|
||||
import com.android.vcard.VCardConfig;
|
||||
import com.android.vcard.VCardEntryConstructor;
|
||||
import com.android.vcard.VCardInterpreter;
|
||||
import com.android.vcard.VCardInterpreterCollection;
|
||||
import com.android.vcard.VCardParser;
|
||||
import com.android.vcard.VCardParser_V21;
|
||||
import com.android.vcard.VCardParser_V30;
|
||||
import com.android.vcard.exception.VCardException;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* The class lets users checks that given expected vCard data are same as given actual vCard data.
|
||||
* Able to verify both vCard importer/exporter.
|
||||
* </p>
|
||||
* <p>
|
||||
* First a user has to initialize the object by calling either
|
||||
* {@link #initForImportTest(int, int)} or {@link #initForExportTest(int)}.
|
||||
* "Round trip test" (import -> export -> import, or export -> import -> export) is not supported.
|
||||
* </p>
|
||||
*/
|
||||
public class VCardVerifier {
|
||||
private static final String LOG_TAG = "VCardVerifier";
|
||||
|
||||
private static class CustomMockContext extends MockContext {
|
||||
final ContentResolver mResolver;
|
||||
public CustomMockContext(ContentResolver resolver) {
|
||||
mResolver = resolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContentResolver getContentResolver() {
|
||||
return mResolver;
|
||||
}
|
||||
}
|
||||
|
||||
private class VCardVerifierInternal implements VCardComposer.OneEntryHandler {
|
||||
public boolean onInit(Context context) {
|
||||
return true;
|
||||
}
|
||||
public boolean onEntryCreated(String vcard) {
|
||||
verifyOneVCard(vcard);
|
||||
return true;
|
||||
}
|
||||
public void onTerminate() {
|
||||
}
|
||||
}
|
||||
|
||||
private final AndroidTestCase mTestCase;
|
||||
private final VCardVerifierInternal mVCardVerifierInternal;
|
||||
private int mVCardType;
|
||||
private boolean mIsV30;
|
||||
private boolean mIsDoCoMo;
|
||||
|
||||
// Only one of them must be non-empty.
|
||||
private ExportTestResolver mExportTestResolver;
|
||||
private InputStream mInputStream;
|
||||
|
||||
// To allow duplication, use list instead of set.
|
||||
// When null, we don't need to do the verification.
|
||||
private PropertyNodesVerifier mPropertyNodesVerifier;
|
||||
private LineVerifier mLineVerifier;
|
||||
private ContentValuesVerifier mContentValuesVerifier;
|
||||
private boolean mInitialized;
|
||||
private boolean mVerified = false;
|
||||
private String mCharset;
|
||||
|
||||
// Called by VCardTestsBase
|
||||
public VCardVerifier(AndroidTestCase testCase) {
|
||||
mTestCase = testCase;
|
||||
mVCardVerifierInternal = new VCardVerifierInternal();
|
||||
mExportTestResolver = null;
|
||||
mInputStream = null;
|
||||
mInitialized = false;
|
||||
mVerified = false;
|
||||
}
|
||||
|
||||
// Should be called at the beginning of each import test.
|
||||
public void initForImportTest(int vcardType, int resId) {
|
||||
if (mInitialized) {
|
||||
mTestCase.fail("Already initialized");
|
||||
}
|
||||
mVCardType = vcardType;
|
||||
mIsV30 = VCardConfig.isV30(vcardType);
|
||||
mIsDoCoMo = VCardConfig.isDoCoMo(vcardType);
|
||||
setInputResourceId(resId);
|
||||
mInitialized = true;
|
||||
}
|
||||
|
||||
// Should be called at the beginning of each export test.
|
||||
public void initForExportTest(int vcardType) {
|
||||
initForExportTest(vcardType, "UTF-8");
|
||||
}
|
||||
|
||||
public void initForExportTest(int vcardType, String charset) {
|
||||
if (mInitialized) {
|
||||
mTestCase.fail("Already initialized");
|
||||
}
|
||||
mExportTestResolver = new ExportTestResolver(mTestCase);
|
||||
mVCardType = vcardType;
|
||||
mIsV30 = VCardConfig.isV30(vcardType);
|
||||
mIsDoCoMo = VCardConfig.isDoCoMo(vcardType);
|
||||
mInitialized = true;
|
||||
if (TextUtils.isEmpty(charset)) {
|
||||
mCharset = "UTF-8";
|
||||
} else {
|
||||
mCharset = charset;
|
||||
}
|
||||
}
|
||||
|
||||
private void setInputResourceId(int resId) {
|
||||
InputStream inputStream = mTestCase.getContext().getResources().openRawResource(resId);
|
||||
if (inputStream == null) {
|
||||
mTestCase.fail("Wrong resId: " + resId);
|
||||
}
|
||||
setInputStream(inputStream);
|
||||
}
|
||||
|
||||
private void setInputStream(InputStream inputStream) {
|
||||
if (mExportTestResolver != null) {
|
||||
mTestCase.fail("addInputEntry() is called.");
|
||||
} else if (mInputStream != null) {
|
||||
mTestCase.fail("InputStream is already set");
|
||||
}
|
||||
mInputStream = inputStream;
|
||||
}
|
||||
|
||||
public ContactEntry addInputEntry() {
|
||||
if (!mInitialized) {
|
||||
mTestCase.fail("Not initialized");
|
||||
}
|
||||
if (mInputStream != null) {
|
||||
mTestCase.fail("setInputStream is called");
|
||||
}
|
||||
return mExportTestResolver.addInputContactEntry();
|
||||
}
|
||||
|
||||
public PropertyNodesVerifierElem addPropertyNodesVerifierElem() {
|
||||
if (!mInitialized) {
|
||||
mTestCase.fail("Not initialized");
|
||||
}
|
||||
if (mPropertyNodesVerifier == null) {
|
||||
mPropertyNodesVerifier = new PropertyNodesVerifier(mTestCase);
|
||||
}
|
||||
PropertyNodesVerifierElem elem =
|
||||
mPropertyNodesVerifier.addPropertyNodesVerifierElem();
|
||||
elem.addExpectedNodeWithOrder("VERSION", (mIsV30 ? "3.0" : "2.1"));
|
||||
|
||||
return elem;
|
||||
}
|
||||
|
||||
public PropertyNodesVerifierElem addPropertyNodesVerifierElemWithEmptyName() {
|
||||
if (!mInitialized) {
|
||||
mTestCase.fail("Not initialized");
|
||||
}
|
||||
PropertyNodesVerifierElem elem = addPropertyNodesVerifierElem();
|
||||
if (mIsV30) {
|
||||
elem.addExpectedNodeWithOrder("N", "").addExpectedNodeWithOrder("FN", "");
|
||||
} else if (mIsDoCoMo) {
|
||||
elem.addExpectedNodeWithOrder("N", "");
|
||||
}
|
||||
return elem;
|
||||
}
|
||||
|
||||
public LineVerifierElem addLineVerifierElem() {
|
||||
if (!mInitialized) {
|
||||
mTestCase.fail("Not initialized");
|
||||
}
|
||||
if (mLineVerifier == null) {
|
||||
mLineVerifier = new LineVerifier(mTestCase, mVCardType);
|
||||
}
|
||||
return mLineVerifier.addLineVerifierElem();
|
||||
}
|
||||
|
||||
public ContentValuesVerifierElem addContentValuesVerifierElem() {
|
||||
if (!mInitialized) {
|
||||
mTestCase.fail("Not initialized");
|
||||
}
|
||||
if (mContentValuesVerifier == null) {
|
||||
mContentValuesVerifier = new ContentValuesVerifier();
|
||||
}
|
||||
|
||||
return mContentValuesVerifier.addElem(mTestCase);
|
||||
}
|
||||
|
||||
private void verifyOneVCard(final String vcard) {
|
||||
Log.d(LOG_TAG, vcard);
|
||||
final VCardInterpreter builder;
|
||||
if (mContentValuesVerifier != null) {
|
||||
final VNodeBuilder vnodeBuilder = mPropertyNodesVerifier;
|
||||
final VCardEntryConstructor vcardDataBuilder =
|
||||
new VCardEntryConstructor(mVCardType);
|
||||
vcardDataBuilder.addEntryHandler(mContentValuesVerifier);
|
||||
if (mPropertyNodesVerifier != null) {
|
||||
builder = new VCardInterpreterCollection(Arrays.asList(
|
||||
mPropertyNodesVerifier, vcardDataBuilder));
|
||||
} else {
|
||||
builder = vnodeBuilder;
|
||||
}
|
||||
} else {
|
||||
if (mPropertyNodesVerifier != null) {
|
||||
builder = mPropertyNodesVerifier;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
InputStream is = null;
|
||||
try {
|
||||
// Note: we must not specify charset toward vCard parsers. This code checks whether
|
||||
// those parsers are able to encode given binary without any extra information for
|
||||
// charset.
|
||||
final VCardParser parser = (mIsV30 ?
|
||||
new VCardParser_V30(mVCardType) : new VCardParser_V21(mVCardType));
|
||||
is = new ByteArrayInputStream(vcard.getBytes(mCharset));
|
||||
parser.parse(is, builder);
|
||||
} catch (IOException e) {
|
||||
mTestCase.fail("Unexpected IOException: " + e.getMessage());
|
||||
} catch (VCardException e) {
|
||||
mTestCase.fail("Unexpected VCardException: " + e.getMessage());
|
||||
} finally {
|
||||
if (is != null) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
mTestCase.fail("Unexpected IOException: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void verify() {
|
||||
if (!mInitialized) {
|
||||
mTestCase.fail("Not initialized.");
|
||||
}
|
||||
if (mVerified) {
|
||||
mTestCase.fail("verify() was called twice.");
|
||||
}
|
||||
if (mInputStream != null) {
|
||||
try {
|
||||
verifyForImportTest();
|
||||
} catch (IOException e) {
|
||||
mTestCase.fail("IOException was thrown: " + e.getMessage());
|
||||
} catch (VCardException e) {
|
||||
mTestCase.fail("VCardException was thrown: " + e.getMessage());
|
||||
}
|
||||
} else if (mExportTestResolver != null){
|
||||
verifyForExportTest();
|
||||
} else {
|
||||
mTestCase.fail("No input is determined");
|
||||
}
|
||||
mVerified = true;
|
||||
}
|
||||
|
||||
private void verifyForImportTest() throws IOException, VCardException {
|
||||
if (mLineVerifier != null) {
|
||||
mTestCase.fail("Not supported now.");
|
||||
}
|
||||
if (mContentValuesVerifier != null) {
|
||||
mContentValuesVerifier.verify(mInputStream, mVCardType);
|
||||
}
|
||||
}
|
||||
|
||||
public static EntityIterator mockGetEntityIteratorMethod(
|
||||
final ContentResolver resolver,
|
||||
final Uri uri, final String selection,
|
||||
final String[] selectionArgs, final String sortOrder) {
|
||||
if (ExportTestResolver.class.equals(resolver.getClass())) {
|
||||
return ((ExportTestResolver)resolver).getProvider().queryEntities(
|
||||
uri, selection, selectionArgs, sortOrder);
|
||||
}
|
||||
|
||||
Log.e(LOG_TAG, "Unexpected provider given.");
|
||||
return null;
|
||||
}
|
||||
|
||||
private Method getMockGetEntityIteratorMethod()
|
||||
throws SecurityException, NoSuchMethodException {
|
||||
return this.getClass().getMethod("mockGetEntityIteratorMethod",
|
||||
ContentResolver.class, Uri.class, String.class, String[].class, String.class);
|
||||
}
|
||||
|
||||
private void verifyForExportTest() {
|
||||
final VCardComposer composer =
|
||||
new VCardComposer(new CustomMockContext(mExportTestResolver), mVCardType, mCharset);
|
||||
composer.addHandler(mLineVerifier);
|
||||
composer.addHandler(mVCardVerifierInternal);
|
||||
if (!composer.init(VCardComposer.CONTACTS_TEST_CONTENT_URI, null, null, null)) {
|
||||
mTestCase.fail("init() failed. Reason: " + composer.getErrorReason());
|
||||
}
|
||||
mTestCase.assertFalse(composer.isAfterLast());
|
||||
try {
|
||||
while (!composer.isAfterLast()) {
|
||||
try {
|
||||
final Method mockGetEntityIteratorMethod = getMockGetEntityIteratorMethod();
|
||||
mTestCase.assertNotNull(mockGetEntityIteratorMethod);
|
||||
mTestCase.assertTrue(composer.createOneEntry(mockGetEntityIteratorMethod));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
mTestCase.fail();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
composer.terminate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.vcard.tests.test_utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Previously used in main vCard handling code but now exists only for testing.
|
||||
*/
|
||||
public class VNode {
|
||||
public String VName;
|
||||
|
||||
public ArrayList<PropertyNode> propList = new ArrayList<PropertyNode>();
|
||||
|
||||
/** 0:parse over. 1:parsing. */
|
||||
public int parseStatus = 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,247 @@
|
|||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.vcard.tests.test_utils;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.util.Base64;
|
||||
import android.util.CharsetUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.vcard.VCardConfig;
|
||||
import com.android.vcard.VCardInterpreter;
|
||||
import com.android.vcard.VCardUtils;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* The class storing the parse result to custom datastruct:
|
||||
* {@link VNode}, and {@link PropertyNode}.
|
||||
* Maybe several vcard instance, so use vNodeList to store.
|
||||
* </p>
|
||||
* <p>
|
||||
* This is called VNode, not VCardNode, since it was used for expressing vCalendar (iCal).
|
||||
* </p>
|
||||
*/
|
||||
/* package */ class VNodeBuilder implements VCardInterpreter {
|
||||
static private String LOG_TAG = "VNodeBuilder";
|
||||
|
||||
public List<VNode> vNodeList = new ArrayList<VNode>();
|
||||
private int mNodeListPos = 0;
|
||||
private VNode mCurrentVNode;
|
||||
private PropertyNode mCurrentPropNode;
|
||||
private String mCurrentParamType;
|
||||
|
||||
/**
|
||||
* The charset using which VParser parses the text.
|
||||
*/
|
||||
private String mSourceCharset;
|
||||
|
||||
/**
|
||||
* The charset with which byte array is encoded to String.
|
||||
*/
|
||||
private String mTargetCharset;
|
||||
|
||||
private boolean mStrictLineBreakParsing;
|
||||
|
||||
public VNodeBuilder() {
|
||||
this(VCardConfig.DEFAULT_INTERMEDIATE_CHARSET, VCardConfig.DEFAULT_IMPORT_CHARSET, false);
|
||||
}
|
||||
|
||||
public VNodeBuilder(String targetCharset, boolean strictLineBreakParsing) {
|
||||
this(null, targetCharset, strictLineBreakParsing);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide sourceCharset is temporal.
|
||||
*/
|
||||
public VNodeBuilder(String sourceCharset, String targetCharset,
|
||||
boolean strictLineBreakParsing) {
|
||||
if (sourceCharset != null) {
|
||||
mSourceCharset = sourceCharset;
|
||||
} else {
|
||||
mSourceCharset = VCardConfig.DEFAULT_INTERMEDIATE_CHARSET;
|
||||
}
|
||||
if (targetCharset != null) {
|
||||
mTargetCharset = targetCharset;
|
||||
} else {
|
||||
mTargetCharset = VCardConfig.DEFAULT_IMPORT_CHARSET;
|
||||
}
|
||||
mStrictLineBreakParsing = strictLineBreakParsing;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
}
|
||||
|
||||
public void end() {
|
||||
}
|
||||
|
||||
// Note: I guess that this code assumes the Record may nest like this:
|
||||
// START:VPOS
|
||||
// ...
|
||||
// START:VPOS2
|
||||
// ...
|
||||
// END:VPOS2
|
||||
// ...
|
||||
// END:VPOS
|
||||
//
|
||||
// However the following code has a bug.
|
||||
// When error occurs after calling startRecord(), the entry which is probably
|
||||
// the cause of the error remains to be in vNodeList, while endRecord() is not called.
|
||||
//
|
||||
// I leave this code as is since I'm not familiar with vcalendar specification.
|
||||
// But I believe we should refactor this code in the future.
|
||||
// Until this, the last entry has to be removed when some error occurs.
|
||||
public void startEntry() {
|
||||
VNode vnode = new VNode();
|
||||
vnode.parseStatus = 1;
|
||||
vnode.VName = "VCARD";
|
||||
// I feel this should be done in endRecord(), but it cannot be done because of
|
||||
// the reason above.
|
||||
vNodeList.add(vnode);
|
||||
mNodeListPos = vNodeList.size() - 1;
|
||||
mCurrentVNode = vNodeList.get(mNodeListPos);
|
||||
}
|
||||
|
||||
public void endEntry() {
|
||||
VNode endNode = vNodeList.get(mNodeListPos);
|
||||
endNode.parseStatus = 0;
|
||||
while(mNodeListPos > 0){
|
||||
mNodeListPos--;
|
||||
if((vNodeList.get(mNodeListPos)).parseStatus == 1)
|
||||
break;
|
||||
}
|
||||
mCurrentVNode = vNodeList.get(mNodeListPos);
|
||||
}
|
||||
|
||||
public void startProperty() {
|
||||
mCurrentPropNode = new PropertyNode();
|
||||
}
|
||||
|
||||
public void endProperty() {
|
||||
mCurrentVNode.propList.add(mCurrentPropNode);
|
||||
}
|
||||
|
||||
public void propertyName(String name) {
|
||||
mCurrentPropNode.propName = name;
|
||||
}
|
||||
|
||||
public void propertyGroup(String group) {
|
||||
mCurrentPropNode.propGroupSet.add(group);
|
||||
}
|
||||
|
||||
public void propertyParamType(String type) {
|
||||
mCurrentParamType = type;
|
||||
}
|
||||
|
||||
public void propertyParamValue(String value) {
|
||||
if (mCurrentParamType == null ||
|
||||
mCurrentParamType.equalsIgnoreCase("TYPE")) {
|
||||
mCurrentPropNode.paramMap_TYPE.add(value);
|
||||
} else {
|
||||
mCurrentPropNode.paramMap.put(mCurrentParamType, value);
|
||||
}
|
||||
|
||||
mCurrentParamType = null;
|
||||
}
|
||||
|
||||
private String encodeString(String originalString, String targetCharset) {
|
||||
if (mSourceCharset.equalsIgnoreCase(targetCharset)) {
|
||||
return originalString;
|
||||
}
|
||||
Charset charset = Charset.forName(mSourceCharset);
|
||||
ByteBuffer byteBuffer = charset.encode(originalString);
|
||||
// byteBuffer.array() "may" return byte array which is larger than
|
||||
// byteBuffer.remaining(). Here, we keep on the safe side.
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
private String handleOneValue(String value, String targetCharset, String encoding) {
|
||||
if (encoding != null) {
|
||||
encoding = encoding.toUpperCase();
|
||||
if (encoding.equals("BASE64") || encoding.equals("B")) {
|
||||
// Assume BASE64 is used only when the number of values is 1.
|
||||
mCurrentPropNode.propValue_bytes = Base64.decode(value.getBytes(), Base64.NO_WRAP);
|
||||
return value;
|
||||
} else if (encoding.equals("QUOTED-PRINTABLE")) {
|
||||
return VCardUtils.parseQuotedPrintable(
|
||||
value, mStrictLineBreakParsing, mSourceCharset, targetCharset);
|
||||
}
|
||||
// Unknown encoding. Fall back to default.
|
||||
}
|
||||
return encodeString(value, targetCharset);
|
||||
}
|
||||
|
||||
public void propertyValues(List<String> values) {
|
||||
if (values == null || values.size() == 0) {
|
||||
mCurrentPropNode.propValue_bytes = null;
|
||||
mCurrentPropNode.propValue_vector.clear();
|
||||
mCurrentPropNode.propValue_vector.add("");
|
||||
mCurrentPropNode.propValue = "";
|
||||
return;
|
||||
}
|
||||
|
||||
ContentValues paramMap = mCurrentPropNode.paramMap;
|
||||
|
||||
String targetCharset = CharsetUtils.nameForDefaultVendor(paramMap.getAsString("CHARSET"));
|
||||
String encoding = paramMap.getAsString("ENCODING");
|
||||
|
||||
if (targetCharset == null || targetCharset.length() == 0) {
|
||||
targetCharset = mTargetCharset;
|
||||
}
|
||||
|
||||
for (String value : values) {
|
||||
mCurrentPropNode.propValue_vector.add(
|
||||
handleOneValue(value, targetCharset, encoding));
|
||||
}
|
||||
|
||||
mCurrentPropNode.propValue = listToString(mCurrentPropNode.propValue_vector);
|
||||
}
|
||||
|
||||
private String listToString(List<String> list){
|
||||
int size = list.size();
|
||||
if (size > 1) {
|
||||
StringBuilder typeListB = new StringBuilder();
|
||||
for (String type : list) {
|
||||
typeListB.append(type).append(";");
|
||||
}
|
||||
int len = typeListB.length();
|
||||
if (len > 0 && typeListB.charAt(len - 1) == ';') {
|
||||
return typeListB.substring(0, len - 1);
|
||||
}
|
||||
return typeListB.toString();
|
||||
} else if (size == 1) {
|
||||
return list.get(0);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public String getResult(){
|
||||
throw new RuntimeException("Not supported");
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue