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

Commit e31dac84 authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

Trim edit fields that don't contain printable values.

When persisting edit changes, trim data rows that have no
printable characters.  Also wrote unit tests for new method
and building of IM intents to check Uri escaping.

Fixes http://b/2173811
parent b7da2d0f
Loading
Loading
Loading
Loading
+8 −0
Original line number Original line Diff line number Diff line
@@ -416,4 +416,12 @@ public class ContactsUtils {
                Uri.fromParts("sms", phoneNumber.toString(), null));
                Uri.fromParts("sms", phoneNumber.toString(), null));
        context.startActivity(intent);
        context.startActivity(intent);
    }
    }

    /**
     * Test if the given {@link CharSequence} contains any graphic characters,
     * first checking {@link TextUtils#isEmpty(CharSequence)} to handle null.
     */
    public static boolean isGraphic(CharSequence str) {
        return !TextUtils.isEmpty(str) && TextUtils.isGraphic(str);
    }
}
}
+4 −3
Original line number Original line Diff line number Diff line
@@ -16,6 +16,7 @@


package com.android.contacts.model;
package com.android.contacts.model;


import com.android.contacts.ContactsUtils;
import com.android.contacts.model.ContactsSource.DataKind;
import com.android.contacts.model.ContactsSource.DataKind;
import com.android.contacts.model.ContactsSource.EditField;
import com.android.contacts.model.ContactsSource.EditField;
import com.android.contacts.model.ContactsSource.EditType;
import com.android.contacts.model.ContactsSource.EditType;
@@ -412,7 +413,7 @@ public class EntityModifier {
        for (EditField field : kind.fieldList) {
        for (EditField field : kind.fieldList) {
            // If any field has values, we're not empty
            // If any field has values, we're not empty
            final String value = values.getAsString(field.column);
            final String value = values.getAsString(field.column);
            if (!TextUtils.isEmpty(value)) {
            if (ContactsUtils.isGraphic(value)) {
                hasValues = true;
                hasValues = true;
            }
            }
        }
        }
@@ -437,12 +438,12 @@ public class EntityModifier {
            final ValuesDelta child = state.getPrimaryEntry(StructuredName.CONTENT_ITEM_TYPE);
            final ValuesDelta child = state.getPrimaryEntry(StructuredName.CONTENT_ITEM_TYPE);


            final String name = extras.getString(Insert.NAME);
            final String name = extras.getString(Insert.NAME);
            if (!TextUtils.isEmpty(name) && TextUtils.isGraphic(name)) {
            if (ContactsUtils.isGraphic(name)) {
                child.put(StructuredName.GIVEN_NAME, name);
                child.put(StructuredName.GIVEN_NAME, name);
            }
            }


            final String phoneticName = extras.getString(Insert.PHONETIC_NAME);
            final String phoneticName = extras.getString(Insert.PHONETIC_NAME);
            if (!TextUtils.isEmpty(phoneticName) && TextUtils.isGraphic(phoneticName)) {
            if (ContactsUtils.isGraphic(phoneticName)) {
                child.put(StructuredName.PHONETIC_GIVEN_NAME, phoneticName);
                child.put(StructuredName.PHONETIC_GIVEN_NAME, phoneticName);
            }
            }
        }
        }
+3 −2
Original line number Original line Diff line number Diff line
@@ -16,6 +16,7 @@


package com.android.contacts.ui.widget;
package com.android.contacts.ui.widget;


import com.android.contacts.ContactsUtils;
import com.android.contacts.R;
import com.android.contacts.R;
import com.android.contacts.model.Editor;
import com.android.contacts.model.Editor;
import com.android.contacts.model.EntityDelta;
import com.android.contacts.model.EntityDelta;
@@ -222,7 +223,7 @@ public class GenericEditorView extends RelativeLayout implements Editor, View.On
            });
            });


            // Hide field when empty and optional value
            // Hide field when empty and optional value
            final boolean couldHide = (TextUtils.isEmpty(value) && field.optional);
            final boolean couldHide = (!ContactsUtils.isGraphic(value) && field.optional);
            final boolean willHide = (mHideOptional && couldHide);
            final boolean willHide = (mHideOptional && couldHide);
            fieldView.setVisibility(willHide ? View.GONE : View.VISIBLE);
            fieldView.setVisibility(willHide ? View.GONE : View.VISIBLE);
            fieldView.setEnabled(enabled);
            fieldView.setEnabled(enabled);
@@ -255,7 +256,7 @@ public class GenericEditorView extends RelativeLayout implements Editor, View.On
        builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
        builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
            public void onClick(DialogInterface dialog, int which) {
                final String customText = customType.getText().toString().trim();
                final String customText = customType.getText().toString().trim();
                if (!TextUtils.isEmpty(customText)) {
                if (ContactsUtils.isGraphic(customText)) {
                    // Now we're sure it's ok to actually change the type value.
                    // Now we're sure it's ok to actually change the type value.
                    mType = mPendingType;
                    mType = mPendingType;
                    mPendingType = null;
                    mPendingType = null;
+101 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.contacts;

import android.content.ContentValues;
import android.content.Intent;
import android.net.Uri;
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.Im;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.LargeTest;

/**
 * Tests for {@link ContactsUtils}.
 */
@LargeTest
public class ContactsUtilsTests extends AndroidTestCase {
    private static final String TEST_ADDRESS = "user@example.org";
    private static final String TEST_PROTOCOL = "prot%col";

    public void testImIntent() throws Exception {
        // Normal IM is appended as path
        final ContentValues values = new ContentValues();
        values.put(Im.MIMETYPE, Im.CONTENT_ITEM_TYPE);
        values.put(Im.TYPE, Im.TYPE_HOME);
        values.put(Im.PROTOCOL, Im.PROTOCOL_GOOGLE_TALK);
        values.put(Im.DATA, TEST_ADDRESS);

        final Intent intent = ContactsUtils.buildImIntent(values);
        assertEquals(Intent.ACTION_SENDTO, intent.getAction());

        final Uri data = intent.getData();
        assertEquals("imto", data.getScheme());
        assertEquals("gtalk", data.getAuthority());
        assertEquals(TEST_ADDRESS, data.getPathSegments().get(0));
    }

    public void testImIntentCustom() throws Exception {
        // Custom IM types have encoded authority
        final ContentValues values = new ContentValues();
        values.put(Im.MIMETYPE, Im.CONTENT_ITEM_TYPE);
        values.put(Im.TYPE, Im.TYPE_HOME);
        values.put(Im.PROTOCOL, Im.PROTOCOL_CUSTOM);
        values.put(Im.CUSTOM_PROTOCOL, TEST_PROTOCOL);
        values.put(Im.DATA, TEST_ADDRESS);

        final Intent intent = ContactsUtils.buildImIntent(values);
        assertEquals(Intent.ACTION_SENDTO, intent.getAction());

        final Uri data = intent.getData();
        assertEquals("imto", data.getScheme());
        assertEquals(TEST_PROTOCOL, data.getAuthority());
        assertEquals(TEST_ADDRESS, data.getPathSegments().get(0));
    }

    public void testImEmailIntent() throws Exception {
        // Email addresses are treated as Google Talk entries
        final ContentValues values = new ContentValues();
        values.put(Email.MIMETYPE, Email.CONTENT_ITEM_TYPE);
        values.put(Email.TYPE, Email.TYPE_HOME);
        values.put(Email.DATA, TEST_ADDRESS);

        final Intent intent = ContactsUtils.buildImIntent(values);
        assertEquals(Intent.ACTION_SENDTO, intent.getAction());

        final Uri data = intent.getData();
        assertEquals("imto", data.getScheme());
        assertEquals("gtalk", data.getAuthority());
        assertEquals(TEST_ADDRESS, data.getPathSegments().get(0));
    }

    public void testIsGraphicNull() throws Exception {
        assertFalse(ContactsUtils.isGraphic(null));
    }

    public void testIsGraphicEmpty() throws Exception {
        assertFalse(ContactsUtils.isGraphic(""));
    }

    public void testIsGraphicSpaces() throws Exception {
        assertFalse(ContactsUtils.isGraphic("  "));
    }

    public void testIsGraphicPunctuation() throws Exception {
        assertTrue(ContactsUtils.isGraphic("."));
    }
}
+55 −0
Original line number Original line Diff line number Diff line
@@ -52,6 +52,8 @@ import java.util.List;
public class EntityModifierTests extends AndroidTestCase {
public class EntityModifierTests extends AndroidTestCase {
    public static final String TAG = "EntityModifierTests";
    public static final String TAG = "EntityModifierTests";


    public static final long VER_FIRST = 100;

    private static final long TEST_ID = 4;
    private static final long TEST_ID = 4;
    private static final String TEST_PHONE = "218-555-1212";
    private static final String TEST_PHONE = "218-555-1212";


@@ -346,6 +348,59 @@ public class EntityModifierTests extends AndroidTestCase {
        }
        }
    }
    }


    public void testTrimEmptySpaces() {
        final ContactsSource source = getSource();
        final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
        final EditType typeHome = EntityModifier.getType(kindPhone, Phone.TYPE_HOME);

        // Test row that has type values, but values are spaces
        final EntityDelta state = EntitySetTests.buildBeforeEntity(TEST_ID, VER_FIRST);
        final ValuesDelta values = EntityModifier.insertChild(state, kindPhone, typeHome);
        values.put(Phone.NUMBER, "   ");

        // Build diff, expecting insert for data row and update enforcement
        EntitySetTests.assertDiffPattern(state,
                EntitySetTests.buildAssertVersion(VER_FIRST),
                EntitySetTests.buildUpdateAggregationSuspended(),
                EntitySetTests.buildOper(Data.CONTENT_URI, TYPE_INSERT,
                        EntitySetTests.buildDataInsert(values, TEST_ID)),
                EntitySetTests.buildUpdateAggregationDefault());

        // Trim empty rows and try again, expecting delete of overall contact
        EntityModifier.trimEmpty(state, source);
        EntitySetTests.assertDiffPattern(state,
                EntitySetTests.buildAssertVersion(VER_FIRST),
                EntitySetTests.buildDelete(RawContacts.CONTENT_URI));
    }

    public void testTrimLeaveValid() {
        final ContactsSource source = getSource();
        final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
        final EditType typeHome = EntityModifier.getType(kindPhone, Phone.TYPE_HOME);

        // Test row that has type values with valid number
        final EntityDelta state = EntitySetTests.buildBeforeEntity(TEST_ID, VER_FIRST);
        final ValuesDelta values = EntityModifier.insertChild(state, kindPhone, typeHome);
        values.put(Phone.NUMBER, TEST_PHONE);

        // Build diff, expecting insert for data row and update enforcement
        EntitySetTests.assertDiffPattern(state,
                EntitySetTests.buildAssertVersion(VER_FIRST),
                EntitySetTests.buildUpdateAggregationSuspended(),
                EntitySetTests.buildOper(Data.CONTENT_URI, TYPE_INSERT,
                        EntitySetTests.buildDataInsert(values, TEST_ID)),
                EntitySetTests.buildUpdateAggregationDefault());

        // Trim empty rows and try again, expecting no differences
        EntityModifier.trimEmpty(state, source);
        EntitySetTests.assertDiffPattern(state,
                EntitySetTests.buildAssertVersion(VER_FIRST),
                EntitySetTests.buildUpdateAggregationSuspended(),
                EntitySetTests.buildOper(Data.CONTENT_URI, TYPE_INSERT,
                        EntitySetTests.buildDataInsert(values, TEST_ID)),
                EntitySetTests.buildUpdateAggregationDefault());
    }

    public void testTrimEmptyUntouched() {
    public void testTrimEmptyUntouched() {
        final ContactsSource source = getSource();
        final ContactsSource source = getSource();
        final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
        final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
Loading