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

Commit fc709241 authored by Walter Jang's avatar Walter Jang Committed by Android (Google) Code Review
Browse files

Merge "Include email and phone matches in display name search experiment" into nyc-dev

parents fd3e8918 bad5e4ba
Loading
Loading
Loading
Loading
+63 −16
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@ import android.database.Cursor;
import android.net.Uri;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.Directory;
import android.provider.ContactsContract.SearchSnippets;
import android.text.TextUtils;
@@ -27,9 +29,12 @@ import android.view.ViewGroup;
import android.widget.ListView;

import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
import com.android.contacts.common.Experiments;
import com.android.contacts.common.R;
import com.android.contacts.common.compat.ContactsCompat;
import com.android.contacts.common.preference.ContactsPreferences;
import com.android.contacts.common.util.ContactDisplayUtils;
import com.android.contacts.commonbind.experiments.Flags;

/**
 * A cursor adapter for the {@link ContactsContract.Contacts#CONTENT_TYPE} content type.
@@ -107,8 +112,8 @@ public abstract class ContactListAdapter extends ContactEntryListAdapter {
        public static final int CONTACT_SNIPPET          = 11;
    }

    // NOTE: These projections must match those in ContactQuery above expect we omit the
    // SearchSnippers.SNIPPET column since that is only supported with Contacts.CONTENT_FILTER_URI.
    // The projection columns should match those in ContactQuery above expect we must omit the
    // columns which are not supported
    protected static class ExperimentQuery {

        private static final String[] FILTER_PROJECTION_PRIMARY = new String[] {
@@ -123,7 +128,7 @@ public abstract class ContactListAdapter extends ContactEntryListAdapter {
                Contacts.PHONETIC_NAME,                 // 8
                Contacts.TIMES_CONTACTED,               // 9
                Contacts.STARRED,                       // 10
                // SearchSnippets.SNIPPET not supported
                // SearchSnippets.SNIPPET not supported for Contacts.CONTENT_URI
        };

        private static final String[] FILTER_PROJECTION_ALTERNATIVE = new String[] {
@@ -138,21 +143,48 @@ public abstract class ContactListAdapter extends ContactEntryListAdapter {
                Contacts.PHONETIC_NAME,                 // 8
                Contacts.TIMES_CONTACTED,               // 9
                Contacts.STARRED,                       // 10
                // SearchSnippets.SNIPPET not supported
                // SearchSnippets.SNIPPET not supported for Contacts.CONTENT_URI
        };

        public static final int CONTACT_ID               = 0;
        public static final int CONTACT_DISPLAY_NAME     = 1;
        public static final int CONTACT_PRESENCE_STATUS  = 2;
        public static final int CONTACT_CONTACT_STATUS   = 3;
        public static final int CONTACT_PHOTO_ID         = 4;
        public static final int CONTACT_PHOTO_URI        = 5;
        public static final int CONTACT_LOOKUP_KEY       = 6;
        public static final int CONTACT_IS_USER_PROFILE  = 7;
        public static final int CONTACT_PHONETIC_NAME    = 8;
        public static final int CONTACT_TIMES_CONTACTED  = 9;
        public static final int CONTACT_STARRED          = 10;
        // SearchSnippets.SNIPPET not supported
        public static final String[] FILTER_PROJECTION_PRIMARY_EMAIL = new String[] {
                Contacts._ID,                           // 0
                Contacts.DISPLAY_NAME_PRIMARY,          // 1
                Contacts.CONTACT_PRESENCE,              // 2
                Contacts.CONTACT_STATUS,                // 3
                Contacts.PHOTO_ID,                      // 4
                Contacts.PHOTO_THUMBNAIL_URI,           // 5
                Contacts.LOOKUP_KEY,                    // 6
                // Contacts.IS_USER_PROFILE not supported for Data.CONTENT_URI
                Contacts.IN_VISIBLE_GROUP,              // 7
                Contacts.PHONETIC_NAME,                 // 8
                Contacts.TIMES_CONTACTED,               // 9
                Contacts.STARRED,                       // 10
                // SearchSnippets.SNIPPET not supported for Data.CONTENT_URI
                Email.ADDRESS,                          // 11
        };

        public static final int EMAIL_ADDRESS = 11;

        public static final String[] FILTER_PROJECTION_PRIMARY_PHONE = new String[] {
                Contacts._ID,                           // 0
                Contacts.DISPLAY_NAME_PRIMARY,          // 1
                Contacts.CONTACT_PRESENCE,              // 2
                Contacts.CONTACT_STATUS,                // 3
                Contacts.PHOTO_ID,                      // 4
                Contacts.PHOTO_THUMBNAIL_URI,           // 5
                Contacts.LOOKUP_KEY,                    // 6
                // Contacts.IS_USER_PROFILE not supported for Data.CONTENT_URI
                Contacts.IN_VISIBLE_GROUP,              // 7
                Contacts.PHONETIC_NAME,                 // 8
                Contacts.TIMES_CONTACTED,               // 9
                Contacts.STARRED,                       // 10
                // SearchSnippets.SNIPPET not supported for Data.CONTENT_URI
                Phone.NUMBER,                           // 11
                Phone.NORMALIZED_NUMBER,                // 12
        };

        public static final int NUMBER = 11;
        public static final int NORMAILIZED_NUMBER = 12;
    }

    private CharSequence mUnknownNameText;
@@ -328,6 +360,21 @@ public abstract class ContactListAdapter extends ContactEntryListAdapter {
    }

    protected void bindSearchSnippet(final ContactListItemView view, Cursor cursor) {
        if (Flags.getInstance(mContext).getBoolean(
                Experiments.FLAG_SEARCH_DISPLAY_NAME_QUERY, false)) {
            final String queryString = getQueryString();
            if (ContactDisplayUtils.isPossiblePhoneNumber(queryString)
                    && cursor.getColumnCount() > ExperimentQuery.NUMBER
                    && Phone.NUMBER.equals(cursor.getColumnName(ExperimentQuery.NUMBER))) {
                view.showSnippet(cursor, queryString, ExperimentQuery.NUMBER);
                return;
            }
            if (cursor.getColumnCount() > ExperimentQuery.EMAIL_ADDRESS
                    && Email.ADDRESS.equals(cursor.getColumnName(ExperimentQuery.EMAIL_ADDRESS))) {
                view.showSnippet(cursor, queryString, ExperimentQuery.EMAIL_ADDRESS);
                return;
            }
        }
        view.showSnippet(cursor, ContactQuery.CONTACT_SNIPPET);
    }

+26 −1
Original line number Diff line number Diff line
@@ -38,7 +38,6 @@ import android.text.SpannableString;
import android.text.TextUtils;
import android.text.TextUtils.TruncateAt;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.MotionEvent;
@@ -56,6 +55,7 @@ import com.android.contacts.common.R;
import com.android.contacts.common.compat.CompatUtils;
import com.android.contacts.common.compat.PhoneNumberUtilsCompat;
import com.android.contacts.common.format.TextHighlighter;
import com.android.contacts.common.list.ContactListAdapter.ExperimentQuery;
import com.android.contacts.common.util.ContactDisplayUtils;
import com.android.contacts.common.util.SearchUtil;
import com.android.contacts.common.util.ViewUtil;
@@ -1478,6 +1478,31 @@ public class ContactListItemView extends ViewGroup
        setStatus(statusMessage);
    }

    /**
     * Shows search snippet for email and phone number matches.
     */
    public void showSnippet(Cursor cursor, String query, int snippetColumn) {
        // TODO: this does not properly handle phone numbers with control characters
        // For example if the phone number is 444-5555, the search query 4445 will match the
        // number since we normalize it before querying CP2 but the snippet will fail since
        // the portion to be highlighted is 444-5 not 4445.
        final String snippet = cursor.getString(snippetColumn);
        if (snippet == null) {
            setSnippet(null);
            return;
        }
        final String displayName = cursor.getColumnIndex(Contacts.DISPLAY_NAME) >= 0
                ? cursor.getString(cursor.getColumnIndex(Contacts.DISPLAY_NAME)) : null;
        if (snippet.equals(displayName)) {
            // If the snippet exactly matches the display name (i.e. the phone number or email
            // address is being used as the display name) then no snippet is necessary
            setSnippet(null);
            return;
        }
        // Show the snippet with the part of the query that matched it
        setSnippet(updateSnippet(snippet, query, displayName));
    }

    /**
     * Shows search snippet.
     */
+36 −7
Original line number Diff line number Diff line
@@ -25,6 +25,9 @@ import android.net.Uri.Builder;
import android.preference.PreferenceManager;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.Directory;
import android.provider.ContactsContract.SearchSnippets;
import android.support.annotation.VisibleForTesting;
@@ -33,7 +36,9 @@ import android.view.View;

import com.android.contacts.common.Experiments;
import com.android.contacts.common.compat.ContactsCompat;
import com.android.contacts.common.compat.PhoneNumberUtilsCompat;
import com.android.contacts.common.preference.ContactsPreferences;
import com.android.contacts.common.util.ContactDisplayUtils;
import com.android.contacts.commonbind.experiments.Flags;

import java.util.ArrayList;
@@ -71,10 +76,10 @@ public class DefaultContactListAdapter extends ContactListAdapter {
                loader.setSelection("0");
            } else if (flags.getBoolean(Experiments.FLAG_SEARCH_DISPLAY_NAME_QUERY, false)
                && directoryId == Directory.DEFAULT) {
                // Configure the loader to match display and phonetic names
                final String displayNameColumn =
                        getContactNameDisplayOrder() == ContactsPreferences.DISPLAY_ORDER_PRIMARY
                                ? Contacts.DISPLAY_NAME_PRIMARY : Contacts.DISPLAY_NAME_ALTERNATIVE;

                final Builder builder = Contacts.CONTENT_URI.buildUpon();
                builder.appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
                        String.valueOf(directoryId));
@@ -82,6 +87,30 @@ public class DefaultContactListAdapter extends ContactListAdapter {
                loader.setProjection(getExperimentProjection());
                loader.setSelection(getDisplayNameSelection(query, displayNameColumn));
                loader.setSelectionArgs(getDisplayNameSelectionArgs(query));

                // Configure an extra query to show email and phone number matches and merge
                // them in after the display name loader query result.
                final ProfileAndContactsLoader profileAndContactsLoader =
                        (ProfileAndContactsLoader) loader;
                final Builder extraBuilder = Data.CONTENT_URI.buildUpon();
                if (ContactDisplayUtils.isPossiblePhoneNumber(query)) {
                    final String normalizedQuery = PhoneNumberUtilsCompat.normalizeNumber(query);
                    profileAndContactsLoader.setLoadExtraContactsLast(
                            extraBuilder.build(),
                            ExperimentQuery.FILTER_PROJECTION_PRIMARY_PHONE,
                            Data.MIMETYPE + "=? AND " + Phone.NORMALIZED_NUMBER + " LIKE ? AND " +
                                    Contacts.IN_VISIBLE_GROUP + "=?",
                            new String[]{Phone.CONTENT_ITEM_TYPE, "%" + normalizedQuery + "%",
                                    "1"});
                } else {
                    final Builder emailBuilder = Data.CONTENT_URI.buildUpon();
                    profileAndContactsLoader.setLoadExtraContactsLast(
                            emailBuilder.build(),
                            ExperimentQuery.FILTER_PROJECTION_PRIMARY_EMAIL,
                            Data.MIMETYPE + "=? AND " + Email.ADDRESS + " LIKE ? AND " +
                                    Contacts.IN_VISIBLE_GROUP + "=?",
                            new String[]{Email.CONTENT_ITEM_TYPE, "%" + query + "%", "1"});
                }
                if (flags.getBoolean(Experiments.FLAG_SEARCH_STREQUENTS_FIRST, false)) {
                    sortOrder = String.format("%s DESC, %s DESC",
                            Contacts.TIMES_CONTACTED, Contacts.STARRED);
@@ -97,14 +126,14 @@ public class DefaultContactListAdapter extends ContactListAdapter {
                    loader.setSelection(Contacts.TIMES_CONTACTED + "=0 AND "
                            + Contacts.STARRED + "=0");

                    // Strequent contacts will be merged back in after the profile (ME) and before
                    // the main loader query results.
                    // Configure an extra query to load strequent contacts and merge them in
                    // before the main loader query results.
                    final ProfileAndContactsLoader profileAndContactsLoader =
                            (ProfileAndContactsLoader) loader;
                    final Builder strequentBuilder =
                            Contacts.CONTENT_STREQUENT_FILTER_URI.buildUpon();
                    appendSearchParameters(strequentBuilder, query, directoryId);
                    profileAndContactsLoader.setLoadStrequents(
                    profileAndContactsLoader.setLoadExtraContactsFirst(
                            strequentBuilder.build(), getExperimentProjection());
                }
            }
@@ -139,7 +168,7 @@ public class DefaultContactListAdapter extends ContactListAdapter {
     */
    @VisibleForTesting
    static String getDisplayNameSelection(String query, String displayNameColumn) {
        final String[] tokens = getDisplayNameSearchSelectionTokens(query);
        final String[] tokens = getDisplayNameSelectionTokens(query);
        if (tokens == null) return null;
        final StringBuilder builder = new StringBuilder();
        for (int i = 0; i < tokens.length; i++) {
@@ -157,7 +186,7 @@ public class DefaultContactListAdapter extends ContactListAdapter {
     */
    @VisibleForTesting
    static String[] getDisplayNameSelectionArgs(String query) {
        final String[] tokens = getDisplayNameSearchSelectionTokens(query);
        final String[] tokens = getDisplayNameSelectionTokens(query);
        if (tokens == null) return null;
        for (int i = 0; i < tokens.length; i++) {
            tokens[i] = "%" + tokens[i] + "%";
@@ -165,7 +194,7 @@ public class DefaultContactListAdapter extends ContactListAdapter {
        return tokens;
    }

    private static String[] getDisplayNameSearchSelectionTokens(String query) {
    private static String[] getDisplayNameSelectionTokens(String query) {
        if (query == null) return null;
        query = query.trim();
        if (query.length() == 0) return null;
+32 −12
Original line number Diff line number Diff line
@@ -38,13 +38,17 @@ public class ProfileAndContactsLoader extends CursorLoader {

    private String[] mProjection;

    private Uri mStrequentUri;
    private String[] mStrequentProjection;
    private Uri mExtraUri;
    private String[] mExtraProjection;
    private String mExtraSelection;
    private String[] mExtraSelectionArgs;
    private boolean mMergeExtraContactsAfterPrimary;

    public ProfileAndContactsLoader(Context context) {
        super(context);
    }

    /** Whether to load the profile and merge results in before any other results. */
    public void setLoadProfile(boolean flag) {
        mLoadProfile = flag;
    }
@@ -54,9 +58,25 @@ public class ProfileAndContactsLoader extends CursorLoader {
        mProjection = projection;
    }

    public void setLoadStrequents(Uri uri, String[] projection) {
        mStrequentUri = uri;
        mStrequentProjection = projection;
    /** Configure an extra query and merge results in before the primary results. */
    public void setLoadExtraContactsFirst(Uri uri, String[] projection) {
        mExtraUri = uri;
        mExtraProjection = projection;
        mMergeExtraContactsAfterPrimary = false;
    }

    /** Configure an extra query and merge results in after the primary results. */
    public void setLoadExtraContactsLast(Uri uri, String[] projection, String selection,
            String[] selectionArgs) {
        mExtraUri = uri;
        mExtraProjection = projection;
        mExtraSelection = selection;
        mExtraSelectionArgs = selectionArgs;
        mMergeExtraContactsAfterPrimary = true;
    }

    private boolean canLoadExtraContacts() {
        return mExtraUri != null && mExtraProjection != null;
    }

    @Override
@@ -66,8 +86,8 @@ public class ProfileAndContactsLoader extends CursorLoader {
        if (mLoadProfile) {
            cursors.add(loadProfile());
        }
        if (mStrequentUri != null && mStrequentProjection != null) {
            cursors.add(loadStrequent());
        if (canLoadExtraContacts() && !mMergeExtraContactsAfterPrimary) {
            cursors.add(loadExtraContacts());
        }
        // ContactsCursor.loadInBackground() can return null; MergeCursor
        // correctly handles null cursors.
@@ -79,6 +99,9 @@ public class ProfileAndContactsLoader extends CursorLoader {
        }
        final Cursor contactsCursor = cursor;
        cursors.add(contactsCursor);
        if (canLoadExtraContacts() && mMergeExtraContactsAfterPrimary) {
            cursors.add(loadExtraContacts());
        }
        return new MergeCursor(cursors.toArray(new Cursor[cursors.size()])) {
            @Override
            public Bundle getExtras() {
@@ -115,11 +138,8 @@ public class ProfileAndContactsLoader extends CursorLoader {
        }
    }

    /**
     * Loads starred and frequently contacted contacts
     */
    private Cursor loadStrequent() {
    private Cursor loadExtraContacts() {
        return getContext().getContentResolver().query(
                mStrequentUri, mStrequentProjection, null, null, null);
                mExtraUri, mExtraProjection, mExtraSelection, mExtraSelectionArgs, null);
    }
}