Add support for email/phone/address types, pronunciation, IM handle in contacts with vCard

git-svn-id: https://zxing.googlecode.com/svn/trunk@1931 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
srowen 2011-09-24 16:43:41 +00:00
parent db08da3bb1
commit faf72f02eb
11 changed files with 303 additions and 53 deletions

View file

@ -93,9 +93,22 @@ public final class Contents {
Contacts.Intents.Insert.TERTIARY_PHONE
};
public static final String[] PHONE_TYPE_KEYS = {
Contacts.Intents.Insert.PHONE_TYPE,
Contacts.Intents.Insert.SECONDARY_PHONE_TYPE,
Contacts.Intents.Insert.TERTIARY_PHONE_TYPE
};
public static final String[] EMAIL_KEYS = {
Contacts.Intents.Insert.EMAIL,
Contacts.Intents.Insert.SECONDARY_EMAIL,
Contacts.Intents.Insert.TERTIARY_EMAIL
};
public static final String[] EMAIL_TYPE_KEYS = {
Contacts.Intents.Insert.EMAIL_TYPE,
Contacts.Intents.Insert.SECONDARY_EMAIL_TYPE,
Contacts.Intents.Insert.TERTIARY_EMAIL_TYPE
};
}

View file

@ -116,13 +116,23 @@ public final class AddressBookResultHandler extends ResultHandler {
AddressBookParsedResult addressResult = (AddressBookParsedResult) getResult();
String[] addresses = addressResult.getAddresses();
String address1 = addresses == null || addresses.length < 1 ? null : addresses[0];
String[] addressTypes = addressResult.getAddressTypes();
String address1Type = addressTypes == null || addressTypes.length < 1 ? null : addressTypes[0];
int action = mapIndexToAction(index);
switch (action) {
case 0:
addContact(addressResult.getNames(), addressResult.getPhoneNumbers(),
addressResult.getEmails(), addressResult.getNote(),
address1, addressResult.getOrg(),
addressResult.getTitle());
addContact(addressResult.getNames(),
addressResult.getPronunciation(),
addressResult.getPhoneNumbers(),
addressResult.getPhoneTypes(),
addressResult.getEmails(),
addressResult.getEmailTypes(),
addressResult.getNote(),
addressResult.getInstantMessenger(),
address1,
address1Type,
addressResult.getOrg(),
addressResult.getTitle());
break;
case 1:
String[] names = addressResult.getNames();

View file

@ -60,7 +60,7 @@ public final class EmailAddressResultHandler extends ResultHandler {
case 1:
String[] addresses = new String[1];
addresses[0] = emailResult.getEmailAddress();
addContact(null, null, addresses, null, null, null, null);
addEmailOnlyContact(addresses, null);
break;
}
}

View file

@ -79,6 +79,26 @@ public abstract class ResultHandler {
private static final String MARKET_REFERRER_SUFFIX =
"&referrer=utm_source%3Dbarcodescanner%26utm_medium%3Dapps%26utm_campaign%3Dscan";
private static final String[] EMAIL_TYPE_STRINGS = {"home", "work"};
private static final String[] PHONE_TYPE_STRINGS = {"home", "work", "mobile", "fax", "pager"};
private static final String[] ADDRESS_TYPE_STRINGS = {"home", "work"};
private static final int[] EMAIL_TYPE_VALUES = {
Contacts.ContactMethodsColumns.TYPE_HOME,
Contacts.ContactMethodsColumns.TYPE_WORK,
};
private static final int[] PHONE_TYPE_VALUES = {
Contacts.PhonesColumns.TYPE_HOME,
Contacts.PhonesColumns.TYPE_WORK,
Contacts.PhonesColumns.TYPE_MOBILE,
Contacts.PhonesColumns.TYPE_FAX_WORK,
Contacts.PhonesColumns.TYPE_PAGER,
};
private static final int[] ADDRESS_TYPE_VALUES = {
Contacts.ContactMethodsColumns.TYPE_HOME,
Contacts.ContactMethodsColumns.TYPE_WORK,
};
private static final int NO_TYPE = -1;
public static final int MAX_BUTTON_COUNT = 4;
private final ParsedResult result;
@ -251,32 +271,96 @@ public abstract class ResultHandler {
}
}
final void addContact(String[] names, String[] phoneNumbers, String[] emails, String note,
String address, String org, String title) {
final void addPhoneOnlyContact(String[] phoneNumbers,String[] phoneTypes) {
addContact(null, null, phoneNumbers, phoneTypes, null, null, null, null, null, null, null, null);
}
final void addEmailOnlyContact(String[] emails, String[] emailTypes) {
addContact(null, null, null, null, emails, emailTypes, null, null, null, null, null, null);
}
final void addContact(String[] names,
String pronunciation,
String[] phoneNumbers,
String[] phoneTypes,
String[] emails,
String[] emailTypes,
String note,
String instantMessenger,
String address,
String addressType,
String org,
String title) {
// Only use the first name in the array, if present.
Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT, Contacts.CONTENT_URI);
intent.setType(Contacts.People.CONTENT_ITEM_TYPE);
putExtra(intent, Contacts.Intents.Insert.NAME, names != null ? names[0] : null);
putExtra(intent, Contacts.Intents.Insert.PHONETIC_NAME, pronunciation);
int phoneCount = Math.min(phoneNumbers != null ? phoneNumbers.length : 0,
Contents.PHONE_KEYS.length);
for (int x = 0; x < phoneCount; x++) {
putExtra(intent, Contents.PHONE_KEYS[x], phoneNumbers[x]);
if (phoneTypes != null && x < phoneTypes.length) {
int type = toPhoneContractType(phoneTypes[x]);
if (type >= 0) {
intent.putExtra(Contents.PHONE_TYPE_KEYS[x], type);
}
}
}
int emailCount = Math.min(emails != null ? emails.length : 0, Contents.EMAIL_KEYS.length);
for (int x = 0; x < emailCount; x++) {
putExtra(intent, Contents.EMAIL_KEYS[x], emails[x]);
if (emailTypes != null && x < emailTypes.length) {
int type = toEmailContractType(emailTypes[x]);
if (type >= 0) {
intent.putExtra(Contents.EMAIL_TYPE_KEYS[x], type);
}
}
}
putExtra(intent, Contacts.Intents.Insert.NOTES, note);
putExtra(intent, Contacts.Intents.Insert.IM_HANDLE, instantMessenger);
putExtra(intent, Contacts.Intents.Insert.POSTAL, address);
if (addressType != null) {
int type = toAddressContractType(addressType);
if (type >= 0) {
intent.putExtra(Contacts.Intents.Insert.POSTAL_TYPE, type);
}
}
putExtra(intent, Contacts.Intents.Insert.COMPANY, org);
putExtra(intent, Contacts.Intents.Insert.JOB_TITLE, title);
launchIntent(intent);
}
private static int toEmailContractType(String typeString) {
return doToContractType(typeString, EMAIL_TYPE_STRINGS, EMAIL_TYPE_VALUES);
}
private static int toPhoneContractType(String typeString) {
return doToContractType(typeString, PHONE_TYPE_STRINGS, PHONE_TYPE_VALUES);
}
private static int toAddressContractType(String typeString) {
return doToContractType(typeString, ADDRESS_TYPE_STRINGS, ADDRESS_TYPE_VALUES);
}
private static int doToContractType(String typeString, String[] types, int[] values) {
if (typeString == null) {
return NO_TYPE;
}
for (int i = 0; i < types.length; i++) {
String type = types[i];
if (typeString.startsWith(type) || typeString.startsWith(type.toUpperCase())) {
return values[i];
}
}
return NO_TYPE;
}
final void shareByEmail(String contents) {
sendEmailFromUri("mailto:", null, activity.getString(R.string.msg_share_subject_line),
contents);
@ -350,10 +434,10 @@ public abstract class ResultHandler {
* @param address The address to find
* @param title An optional title, e.g. the name of the business at this address
*/
final void searchMap(String address, String title) {
final void searchMap(String address, CharSequence title) {
String query = address;
if (title != null && title.length() > 0) {
query = query + " (" + title + ')';
query += " (" + title + ')';
}
launchIntent(new Intent(Intent.ACTION_VIEW, Uri.parse("geo:0,0?q=" + Uri.encode(query))));
}

View file

@ -58,7 +58,7 @@ public final class TelResultHandler extends ResultHandler {
case 1:
String[] numbers = new String[1];
numbers[0] = telResult.getNumber();
addContact(null, numbers, null, null, null, null, null);
addPhoneOnlyContact(numbers, null);
break;
}
}

View file

@ -47,8 +47,20 @@ final class AddressBookAUResultParser extends ResultParser {
String note = matchSinglePrefixedField("MEMORY:", rawText, '\r', false);
String address = matchSinglePrefixedField("ADD:", rawText, '\r', true);
String[] addresses = address == null ? null : new String[] {address};
return new AddressBookParsedResult(maybeWrap(name), pronunciation, phoneNumbers, emails, note,
addresses, null, null, null, null);
return new AddressBookParsedResult(maybeWrap(name),
pronunciation,
phoneNumbers,
null,
emails,
null,
null,
note,
addresses,
null,
null,
null,
null,
null);
}
private static String[] matchMultipleValuePrefix(String prefix, int max, String rawText,

View file

@ -64,9 +64,13 @@ final class AddressBookDoCoMoResultParser extends AbstractDoCoMoResultParser {
return new AddressBookParsedResult(maybeWrap(name),
pronunciation,
phoneNumbers,
null,
emails,
null,
null,
note,
addresses,
null,
org,
birthday,
null,

View file

@ -24,9 +24,13 @@ public final class AddressBookParsedResult extends ParsedResult {
private final String[] names;
private final String pronunciation;
private final String[] phoneNumbers;
private final String[] phoneTypes;
private final String[] emails;
private final String[] emailTypes;
private final String instantMessenger;
private final String note;
private final String[] addresses;
private final String[] addressTypes;
private final String org;
private final String birthday;
private final String title;
@ -35,9 +39,13 @@ public final class AddressBookParsedResult extends ParsedResult {
public AddressBookParsedResult(String[] names,
String pronunciation,
String[] phoneNumbers,
String[] phoneTypes,
String[] emails,
String[] emailTypes,
String instantMessenger,
String note,
String[] addresses,
String[] addressTypes,
String org,
String birthday,
String title,
@ -46,9 +54,13 @@ public final class AddressBookParsedResult extends ParsedResult {
this.names = names;
this.pronunciation = pronunciation;
this.phoneNumbers = phoneNumbers;
this.phoneTypes = phoneTypes;
this.emails = emails;
this.emailTypes = emailTypes;
this.instantMessenger = instantMessenger;
this.note = note;
this.addresses = addresses;
this.addressTypes = addressTypes;
this.org = org;
this.birthday = birthday;
this.title = title;
@ -73,10 +85,30 @@ public final class AddressBookParsedResult extends ParsedResult {
return phoneNumbers;
}
/**
* @return optional descriptions of the type of each phone number. It could be like "HOME", but,
* there is no guaranteed or standard format.
*/
public String[] getPhoneTypes() {
return phoneTypes;
}
public String[] getEmails() {
return emails;
}
/**
* @return optional descriptions of the type of each e-mail. It could be like "WORK", but,
* there is no guaranteed or standard format.
*/
public String[] getEmailTypes() {
return emailTypes;
}
public String getInstantMessenger() {
return instantMessenger;
}
public String getNote() {
return note;
}
@ -85,6 +117,14 @@ public final class AddressBookParsedResult extends ParsedResult {
return addresses;
}
/**
* @return optional descriptions of the type of each e-mail. It could be like "WORK", but,
* there is no guaranteed or standard format.
*/
public String[] getAddressTypes() {
return addressTypes;
}
public String getTitle() {
return title;
}
@ -113,6 +153,7 @@ public final class AddressBookParsedResult extends ParsedResult {
maybeAppend(addresses, result);
maybeAppend(phoneNumbers, result);
maybeAppend(emails, result);
maybeAppend(instantMessenger, result);
maybeAppend(url, result);
maybeAppend(birthday, result);
maybeAppend(note, result);

View file

@ -52,9 +52,13 @@ final class BizcardResultParser extends AbstractDoCoMoResultParser {
return new AddressBookParsedResult(maybeWrap(fullName),
null,
buildPhoneNumbers(phoneNumber1, phoneNumber2, phoneNumber3),
null,
maybeWrap(email),
null,
null,
null,
addresses,
null,
org,
null,
title,

View file

@ -41,33 +41,49 @@ final class VCardResultParser extends ResultParser {
if (rawText == null || !rawText.startsWith("BEGIN:VCARD")) {
return null;
}
String[] names = matchVCardPrefixedField("FN", rawText, true);
Vector names = matchVCardPrefixedField("FN", rawText, true);
if (names == null) {
// If no display names found, look for regular name fields and format them
names = matchVCardPrefixedField("N", rawText, true);
formatNames(names);
}
String[] phoneNumbers = matchVCardPrefixedField("TEL", rawText, true);
String[] emails = matchVCardPrefixedField("EMAIL", rawText, true);
String note = matchSingleVCardPrefixedField("NOTE", rawText, false);
String[] addresses = matchVCardPrefixedField("ADR", rawText, true);
Vector phoneNumbers = matchVCardPrefixedField("TEL", rawText, true);
Vector emails = matchVCardPrefixedField("EMAIL", rawText, true);
Vector note = matchSingleVCardPrefixedField("NOTE", rawText, false);
Vector addresses = matchVCardPrefixedField("ADR", rawText, true);
if (addresses != null) {
for (int i = 0; i < addresses.length; i++) {
addresses[i] = formatAddress(addresses[i]);
for (int i = 0; i < addresses.size(); i++) {
Vector list = (Vector) addresses.elementAt(i);
list.setElementAt(formatAddress((String) list.elementAt(0)), 0);
}
}
String org = matchSingleVCardPrefixedField("ORG", rawText, true);
String birthday = matchSingleVCardPrefixedField("BDAY", rawText, true);
if (!isLikeVCardDate(birthday)) {
Vector org = matchSingleVCardPrefixedField("ORG", rawText, true);
Vector birthday = matchSingleVCardPrefixedField("BDAY", rawText, true);
if (birthday != null && !isLikeVCardDate((String) birthday.elementAt(0))) {
birthday = null;
}
String title = matchSingleVCardPrefixedField("TITLE", rawText, true);
String url = matchSingleVCardPrefixedField("URL", rawText, true);
return new AddressBookParsedResult(names, null, phoneNumbers, emails, note, addresses, org,
birthday, title, url);
Vector title = matchSingleVCardPrefixedField("TITLE", rawText, true);
Vector url = matchSingleVCardPrefixedField("URL", rawText, true);
Vector instantMessenger = matchSingleVCardPrefixedField("IMPP", rawText, true);
return new AddressBookParsedResult(toPrimaryValues(names),
null,
toPrimaryValues(phoneNumbers),
toTypes(phoneNumbers),
toPrimaryValues(emails),
toTypes(emails),
toPrimaryValue(instantMessenger),
toPrimaryValue(note),
toPrimaryValues(addresses),
toTypes(addresses),
toPrimaryValue(org),
toPrimaryValue(birthday),
toPrimaryValue(title),
toPrimaryValue(url));
}
private static String[] matchVCardPrefixedField(String prefix, String rawText, boolean trim) {
private static Vector matchVCardPrefixedField(String prefix,
String rawText,
boolean trim) {
Vector matches = null;
int i = 0;
int max = rawText.length();
@ -94,18 +110,23 @@ final class VCardResultParser extends ResultParser {
i++;
}
Vector metadata = null;
boolean quotedPrintable = false;
String quotedPrintableCharset = null;
if (i > metadataStart) {
// There was something after the tag, before colon
int j = metadataStart+1;
while (j <= i) {
if (rawText.charAt(j) == ';' || rawText.charAt(j) == ':') {
String metadata = rawText.substring(metadataStart+1, j);
int equals = metadata.indexOf('=');
for (int j = metadataStart + 1; j <= i; j++) {
char c = rawText.charAt(j);
if (c == ';' || c == ':') {
String metadatum = rawText.substring(metadataStart+1, j);
if (metadata == null) {
metadata = new Vector(1);
}
metadata.addElement(metadatum);
int equals = metadatum.indexOf('=');
if (equals >= 0) {
String key = metadata.substring(0, equals);
String value = metadata.substring(equals+1);
String key = metadatum.substring(0, equals);
String value = metadatum.substring(equals+1);
if ("ENCODING".equalsIgnoreCase(key)) {
if ("QUOTED-PRINTABLE".equalsIgnoreCase(value)) {
quotedPrintable = true;
@ -116,7 +137,6 @@ final class VCardResultParser extends ResultParser {
}
metadataStart = j;
}
j++;
}
}
@ -158,7 +178,14 @@ final class VCardResultParser extends ResultParser {
} else {
element = stripContinuationCRLF(element);
}
matches.addElement(element);
if (metadata == null) {
Vector match = new Vector(1);
match.addElement(element);
matches.addElement(match);
} else {
metadata.insertElementAt(element, 0);
matches.addElement(metadata);
}
i++;
} else {
i++;
@ -169,7 +196,7 @@ final class VCardResultParser extends ResultParser {
if (matches == null || matches.isEmpty()) {
return null;
}
return toStringArray(matches);
return matches;
}
private static String stripContinuationCRLF(String value) {
@ -235,9 +262,11 @@ final class VCardResultParser extends ResultParser {
private static int toHexValue(char c) {
if (c >= '0' && c <= '9') {
return c - '0';
} else if (c >= 'A' && c <= 'F') {
}
if (c >= 'A' && c <= 'F') {
return c - 'A' + 10;
} else if (c >= 'a' && c <= 'f') {
}
if (c >= 'a' && c <= 'f') {
return c - 'a' + 10;
}
throw new IllegalArgumentException();
@ -264,9 +293,53 @@ final class VCardResultParser extends ResultParser {
}
}
static String matchSingleVCardPrefixedField(String prefix, String rawText, boolean trim) {
String[] values = matchVCardPrefixedField(prefix, rawText, trim);
return values == null ? null : values[0];
static Vector matchSingleVCardPrefixedField(String prefix,
String rawText,
boolean trim) {
Vector values = matchVCardPrefixedField(prefix, rawText, trim);
return values == null || values.isEmpty() ? null : (Vector) values.elementAt(0);
}
private static String toPrimaryValue(Vector list) {
return list == null || list.isEmpty() ? null : (String) list.elementAt(0);
}
private static String[] toPrimaryValues(Vector lists) {
if (lists == null || lists.isEmpty()) {
return null;
}
Vector result = new Vector(lists.size());
for (int i = 0; i < lists.size(); i++) {
Vector list = (Vector) lists.elementAt(i);
result.addElement(list.elementAt(0));
}
return toStringArray(result);
}
private static String[] toTypes(Vector lists) {
if (lists == null || lists.isEmpty()) {
return null;
}
Vector result = new Vector(lists.size());
for (int j = 0; j < lists.size(); j++) {
Vector list = (Vector) lists.elementAt(j);
String type = null;
for (int i = 1; i < list.size(); i++) {
String metadatum = (String) list.elementAt(i);
int equals = metadatum.indexOf('=');
if (equals < 0) {
// take the whole thing as a usable label
type = metadatum;
break;
}
if ("TYPE".equalsIgnoreCase(metadatum.substring(0, equals))) {
type = metadatum.substring(equals + 1);
break;
}
}
result.addElement(type);
}
return toStringArray(result);
}
private static boolean isLikeVCardDate(String value) {
@ -311,10 +384,11 @@ final class VCardResultParser extends ResultParser {
*
* @param names name values to format, in place
*/
private static void formatNames(String[] names) {
private static void formatNames(Vector names) {
if (names != null) {
for (int i = 0; i < names.length; i++) {
String name = names[i];
for (int i = 0; i < names.size(); i++) {
Vector list = (Vector) names.elementAt(i);
String name = (String) list.elementAt(0);
String[] components = new String[5];
int start = 0;
int end;
@ -331,7 +405,7 @@ final class VCardResultParser extends ResultParser {
maybeAppendComponent(components, 2, newName);
maybeAppendComponent(components, 0, newName);
maybeAppendComponent(components, 4, newName);
names[i] = newName.toString().trim();
list.setElementAt(newName.toString().trim(), 0);
}
}
}

View file

@ -18,6 +18,8 @@ package com.google.zxing.client.result;
import com.google.zxing.Result;
import java.util.Vector;
/**
* Partially implements the iCalendar format's "VEVENT" format for specifying a
* calendar event. See RFC 2445. This supports SUMMARY, LOCATION, GEO, DTSTART and DTEND fields.
@ -39,14 +41,13 @@ final class VEventResultParser extends ResultParser {
return null;
}
String summary = VCardResultParser.matchSingleVCardPrefixedField("SUMMARY", rawText, true);
String start = VCardResultParser.matchSingleVCardPrefixedField("DTSTART", rawText, true);
String end = VCardResultParser.matchSingleVCardPrefixedField("DTEND", rawText, true);
String location = VCardResultParser.matchSingleVCardPrefixedField("LOCATION", rawText, true);
String description = VCardResultParser.matchSingleVCardPrefixedField("DESCRIPTION", rawText, true);
String summary = matchSingleVCardPrefixedField("SUMMARY", rawText, true);
String start = matchSingleVCardPrefixedField("DTSTART", rawText, true);
String end = matchSingleVCardPrefixedField("DTEND", rawText, true);
String location = matchSingleVCardPrefixedField("LOCATION", rawText, true);
String description = matchSingleVCardPrefixedField("DESCRIPTION", rawText, true);
String geoString = VCardResultParser.matchSingleVCardPrefixedField("GEO", rawText, true);
double latitude;
String geoString = matchSingleVCardPrefixedField("GEO", rawText, true); double latitude;
double longitude;
if (geoString == null) {
latitude = Double.NaN;
@ -68,4 +69,11 @@ final class VEventResultParser extends ResultParser {
}
}
static String matchSingleVCardPrefixedField(String prefix,
String rawText,
boolean trim) {
Vector values = VCardResultParser.matchSingleVCardPrefixedField(prefix, rawText, trim);
return values == null || values.isEmpty() ? null : (String) values.elementAt(0);
}
}