Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5c3e6879 authored by Daisuke Miyakawa's avatar Daisuke Miyakawa
Browse files

Develop ContentResolver-based unit tests for vCard importer and fix vCard code

along with the tests

Make test code not only check the validity of VCardParser but also check
the validity of the data insertion part of ContactStruct class, using
MockContentResolver/MockContentProvider. With these tests, we are now really sure
vCard side appropriately sends vCard data into the resolver.

Fix ContactStruct so that it properly handles ORG property and TITLE property,
though it still does not see Group information. There's no vCard found which
uses Group and ORG and TITLE in convolted orders...

e.g. Current implementation misinterprets the following case, but we're not sure
whether any exporter emits data in this kind of complicated form...
group2.ORG:ComparyA
group1.ORG:CompanyB
group1.TITLE:TitleForA
group2.TITLE:TitleForB

Expected: CompanyA + TitleForA, CompanyB + TitleForB
Actual: CompanyA + TitleForB, CompanyB + TitleForA

Also change the parser part a little, so that some component can be reused via
the other part of vCard code.

Added several additional files for the tests, which ensures that
- ORG/TITLE properties are handled as we expect.
- PREF is appropriately handled and passed to the resolver as "IS_PRIMARY" flag.
-- We discarded the code which ensures that "IS_PRIMARY" is added to only one
   field in each type, after the local discussion (the duplication or no primary
   state should be handled by the resolver).

Internal Issue number: 2160039
parent 50b11ec6
Loading
Loading
Loading
Loading
+331 −298

File changed.

Preview size limit exceeded, changes collapsed.

+12 −4
Original line number Diff line number Diff line
@@ -41,8 +41,8 @@ public class VCardConfig {
    // TODO: make the other codes use this flag
    public static final boolean IGNORE_CASE_EXCEPT_VALUE = true;
    
    private static final int FLAG_V21 = 0;
    private static final int FLAG_V30 = 1;
    public static final int FLAG_V21 = 0;
    public static final int FLAG_V30 = 1;

    // 0x2 is reserved for the future use ...

@@ -106,7 +106,15 @@ public class VCardConfig {
     */
    public static final int FLAG_USE_QP_TO_PRIMARY_PROPERTIES = 0x10000000;

    // VCard types
    // Note: if you really want to add additional flag(s) incompatible with the main source tree,
    // please use flags from 0x0001000 to 0x00080000.
    //
    // If we notice we cannot manage flags in just one integer, we'll change this interface and
    // create/use a complete class, not just an integer, with similar API (but imcompatible).
    //
    // Again, please be aware that this API is intentionally hidden ~= unstable!

    //// The followings are VCard types available from importer/exporter. ////

    /**
     * General vCard format with the version 2.1. Uses UTF-8 for the charset.
+20 −32
Original line number Diff line number Diff line
@@ -144,7 +144,11 @@ public class VCardParser_V21 extends VCardParser {
        }
    }

    protected String getVersion() {
    protected int getVersion() {
        return VCardConfig.FLAG_V21;
    }

    protected String getVersionString() {
        return "2.1";
    }

@@ -392,9 +396,10 @@ public class VCardParser_V21 extends VCardParser {
                } else {
                    throw new VCardException("Unknown BEGIN type: " + propertyValue);
                }
            } else if (propertyName.equals("VERSION") && !propertyValue.equals(getVersion())) {
            } else if (propertyName.equals("VERSION") &&
                    !propertyValue.equals(getVersionString())) {
                throw new VCardVersionException("Incompatible version: " + 
                        propertyValue + " != " + getVersion());
                        propertyValue + " != " + getVersionString());
            }
            start = System.currentTimeMillis();
            handlePropertyValue(propertyName, propertyValue);
@@ -761,29 +766,8 @@ public class VCardParser_V21 extends VCardParser {
        }

        if (mBuilder != null) {
            StringBuilder builder = new StringBuilder();
            ArrayList<String> list = new ArrayList<String>();
            int length = propertyValue.length();
            for (int i = 0; i < length; i++) {
                char ch = propertyValue.charAt(i);
                if (ch == '\\' && i < length - 1) {
                    char nextCh = propertyValue.charAt(i + 1);
                    String unescapedString = maybeUnescapeCharacter(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());
            mBuilder.propertyValues(list);
            mBuilder.propertyValues(VCardUtils.constructListFromValue(
                    propertyValue, (getVersion() == VCardConfig.FLAG_V30)));
        }
    }

@@ -825,6 +809,10 @@ public class VCardParser_V21 extends VCardParser {
     * e.g. In vCard 2.1, "\;" should be unescaped into ";" while "\x" should not be.
     */
    protected String maybeUnescapeCharacter(char ch) {
        return unescapeCharacter(ch);
    }

    public static String unescapeCharacter(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.
+18 −5
Original line number Diff line number Diff line
@@ -50,7 +50,12 @@ public class VCardParser_V30 extends VCardParser_V21 {
    private boolean mEmittedAgentWarning = false;

    @Override
    protected String getVersion() {
    protected int getVersion() {
        return VCardConfig.FLAG_V30;
    }

    @Override
    protected String getVersionString() {
        return Constants.VERSION_V30;
    }

@@ -284,6 +289,10 @@ public class VCardParser_V30 extends VCardParser_V21 {
     */ 
    @Override
    protected String maybeUnescapeText(String text) {
        return unescapeText(text);
    }

    public static String unescapeText(String text) {
        StringBuilder builder = new StringBuilder();
        int length = text.length();
        for (int i = 0; i < length; i++) {
@@ -304,6 +313,10 @@ public class VCardParser_V30 extends VCardParser_V21 {
    
    @Override
    protected String maybeUnescapeCharacter(char ch) {
        return unescapeCharacter(ch);
    }

    public static String unescapeCharacter(char ch) {
        if (ch == 'n' || ch == 'N') {
            return "\n";
        } else {
+36 −2
Original line number Diff line number Diff line
@@ -22,9 +22,11 @@ import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
import android.text.TextUtils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

@@ -187,7 +189,10 @@ public class VCardUtils {
        }

        builder.withValue(StructuredPostal.POBOX, postalData.pobox);
        // Extended address is dropped since there's no relevant entry in ContactsContract.
        // TODO: Japanese phone seems to use this field for expressing all the address including
        // region, city, etc. Not sure we're ok to store them into NEIGHBORHOOD, while it would be
        // better than dropping them all.
        builder.withValue(StructuredPostal.NEIGHBORHOOD, postalData.extendedAddress);
        builder.withValue(StructuredPostal.STREET, postalData.street);
        builder.withValue(StructuredPostal.CITY, postalData.localty);
        builder.withValue(StructuredPostal.REGION, postalData.region);
@@ -283,6 +288,35 @@ public class VCardUtils {
        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 ? VCardParser_V30.unescapeCharacter(nextCh) :
                        VCardParser_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(String str) {
        if (TextUtils.isEmpty(str)) {
            return true;
Loading