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

Commit e47ebf09 authored by Zhu Youhua's avatar Zhu Youhua Committed by Linux Build Service Account
Browse files

Add support of multi language smart search in dialpad

Use the new API to realize multi-language smart search
for contact name. Change the match rules for number,
use fuzzy match rules.

CRs-Fixed: 988088

Change-Id: I5d2a57edddec7a0721017fe8582aa1a7af96dd3b
parent bebe9106
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -73,6 +73,7 @@
        <meta-data android:name="com.google.android.backup.api_key"
            android:value="AEdPqrEAAAAIBXgtCEKQ6W0PXVnW-ZVia2KmlV2AxsTw3GjAeQ" />

        <uses-library android:name="com.qualcomm.qti.smartsearch" android:required="false" />
        <!-- The entrance point for Phone UI.
             stateAlwaysHidden is set to suppress keyboard show up on
             dialpad screen. -->
+121 −20
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.Directory;
import android.provider.ContactsContract.RawContacts;
import android.text.TextUtils;
import android.util.Log;

@@ -49,6 +50,7 @@ import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
@@ -70,6 +72,11 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {
    private static final AtomicBoolean sInUpdate = new AtomicBoolean(false);
    private final Context mContext;

    private Class mMultiMatchClass;
    private Object mMultiMatchObject;
    private Method mMultiMatchMethod;
    private Method mMultiGetNameNumberMethod;

    /**
     * SmartDial DB version ranges:
     * <pre>
@@ -86,7 +93,7 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {
    private static final String LAST_UPDATED_MILLIS = "last_updated_millis";
    private static final String DATABASE_VERSION_PROPERTY = "database_version";

    private static final int MAX_ENTRIES = 20;
    private static final int MAX_ENTRIES = 40;

    public interface Tables {
        /** Saves a list of numbers to be blocked.*/
@@ -120,6 +127,8 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {
        static final String IS_PRIMARY = "is_primary";
        static final String CARRIER_PRESENCE = "carrier_presence";
        static final String LAST_SMARTDIAL_UPDATE_TIME = "last_smartdial_update_time";
        static final String ACCOUNT_TYPE = "account_type";
        static final String ACCOUNT_NAME = "account_name";
    }

    public static interface PrefixColumns extends BaseColumns {
@@ -156,6 +165,8 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {
            Contacts.IN_VISIBLE_GROUP,          // 12
            Data.IS_PRIMARY,                    // 13
            Data.CARRIER_PRESENCE,              // 14
            RawContacts.ACCOUNT_TYPE,           // 15
            RawContacts.ACCOUNT_NAME,           // 16
        };

        static final int PHONE_ID = 0;
@@ -173,6 +184,8 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {
        static final int PHONE_IN_VISIBLE_GROUP = 12;
        static final int PHONE_IS_PRIMARY = 13;
        static final int PHONE_CARRIER_PRESENCE = 14;
        static final int PHONE_ACCOUNT_TYPE = 15;
        static final int PHONE_ACCOUNT_NAME = 16;

        /** Selects only rows that have been updated after a certain time stamp.*/
        static final String SELECT_UPDATED_CLAUSE =
@@ -275,6 +288,8 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {
        public final String lookupKey;
        public final long photoId;
        public final int carrierPresence;
        public final String accountType;
        public final String accountName;

        public ContactNumber(long id, long dataID, String displayName, String phoneNumber,
                String lookupKey, long photoId, int carrierPresence) {
@@ -285,6 +300,22 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {
            this.lookupKey = lookupKey;
            this.photoId = photoId;
            this.carrierPresence = carrierPresence;
            this.accountType = null;
            this.accountName = null;
        }

        public ContactNumber(long id, long dataID, String displayName, String phoneNumber,
                String lookupKey, long photoId, int carrierPresence,
                        String accountType, String accountName) {
            this.dataId = dataID;
            this.id = id;
            this.displayName = displayName;
            this.phoneNumber = phoneNumber;
            this.lookupKey = lookupKey;
            this.photoId = photoId;
            this.carrierPresence = carrierPresence;
            this.accountType = accountType;
            this.accountName = accountName;
        }

        @Override
@@ -306,7 +337,10 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {
                        && Objects.equal(this.phoneNumber, that.phoneNumber)
                        && Objects.equal(this.lookupKey, that.lookupKey)
                        && Objects.equal(this.photoId, that.photoId)
                        && Objects.equal(this.carrierPresence, that.carrierPresence);
                        && Objects.equal(this.carrierPresence, that.carrierPresence)
                        && Objects.equal(this.accountType, that.accountType)
                        && Objects.equal(this.accountName, that.accountName);

            }
            return false;
        }
@@ -382,6 +416,39 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {
        mContext = Preconditions.checkNotNull(context, "Context must not be null");
    }

    private void initMultiLanguageSearch() {
        try {
            if (mMultiMatchClass == null) {
                mMultiMatchClass = Class.forName("com.qualcomm.qti.smartsearch.SmartMatch");
                Log.d(TAG, "create multi match success");
            }
            if (mMultiMatchClass != null) {
                if (mMultiMatchObject == null) {
                    mMultiMatchObject = mMultiMatchClass.newInstance();
                }
                if (mMultiMatchMethod == null) {
                    mMultiMatchMethod = mMultiMatchClass.getDeclaredMethod(
                            "getMatchStringIndex", String.class, String.class,
                            int.class);
                }
                if (mMultiGetNameNumberMethod == null) {
                    mMultiGetNameNumberMethod = mMultiMatchClass.getDeclaredMethod(
                            "getNameNumber", String.class, int.class);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Object getMultiMatchObject() {
        return mMultiMatchObject;
    }

    public Method getMultiMatchMethod() {
        return mMultiMatchMethod;
    }

    /**
     * Creates tables in the database when database is created for the first time.
     *
@@ -409,7 +476,9 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {
                + SmartDialDbColumns.IS_SUPER_PRIMARY + " INTEGER, "
                + SmartDialDbColumns.IN_VISIBLE_GROUP + " INTEGER, "
                + SmartDialDbColumns.IS_PRIMARY + " INTEGER, "
                + SmartDialDbColumns.CARRIER_PRESENCE + " INTEGER NOT NULL DEFAULT 0"
                + SmartDialDbColumns.CARRIER_PRESENCE + " INTEGER NOT NULL DEFAULT 0,"
                + SmartDialDbColumns.ACCOUNT_TYPE + " TEXT, "
                + SmartDialDbColumns.ACCOUNT_NAME + " TEXT "
                + ");");

        db.execSQL("CREATE TABLE " + Tables.PREFIX_TABLE + " ("
@@ -770,8 +839,10 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {
                    SmartDialDbColumns.IN_VISIBLE_GROUP+ ", " +
                    SmartDialDbColumns.IS_PRIMARY + ", " +
                    SmartDialDbColumns.CARRIER_PRESENCE + ", " +
                    SmartDialDbColumns.LAST_SMARTDIAL_UPDATE_TIME + ") " +
                    " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
                    SmartDialDbColumns.LAST_SMARTDIAL_UPDATE_TIME + ", " +
                    SmartDialDbColumns.ACCOUNT_TYPE + ", " +
                    SmartDialDbColumns.ACCOUNT_NAME + ") " +
                    " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
            final SQLiteStatement insert = db.compileStatement(sqlInsert);

            final String numberSqlInsert = "INSERT INTO " + Tables.PREFIX_TABLE + " (" +
@@ -820,6 +891,10 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {
                insert.bindLong(12, updatedContactCursor.getInt(PhoneQuery.PHONE_IS_PRIMARY));
                insert.bindLong(13, updatedContactCursor.getInt(PhoneQuery.PHONE_CARRIER_PRESENCE));
                insert.bindLong(14, currentMillis);
                insert.bindString(15, updatedContactCursor
                        .getString(PhoneQuery.PHONE_ACCOUNT_TYPE));
                insert.bindString(16, updatedContactCursor
                        .getString(PhoneQuery.PHONE_ACCOUNT_NAME));
                insert.executeInsert();
                final String contactPhoneNumber =
                        updatedContactCursor.getString(PhoneQuery.PHONE_NUMBER);
@@ -863,9 +938,21 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {

            while (nameCursor.moveToNext()) {
                /** Computes a list of prefixes of a given contact name. */
                final ArrayList<String> namePrefixes =
                        SmartDialPrefix.generateNamePrefixes(nameCursor.getString(columnIndexName));

                if (mMultiGetNameNumberMethod != null) {
                    try {
                        String nameNumber = (String) mMultiGetNameNumberMethod.invoke(
                                mMultiMatchObject,nameCursor.getString(columnIndexName), 0);
                        nameNumber = nameNumber.replaceAll("[\\[\\.\\]]", "");
                        insert.bindLong(1,nameCursor.getLong(columnIndexContactId));
                        insert.bindString(2, nameNumber);
                        insert.executeInsert();
                        insert.clearBindings();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                } else {
                    final ArrayList<String> namePrefixes = SmartDialPrefix
                            .generateNamePrefixes(nameCursor.getString(columnIndexName));
                    for (String namePrefix : namePrefixes) {
                        insert.bindLong(1, nameCursor.getLong(columnIndexContactId));
                        insert.bindString(2, namePrefix);
@@ -873,6 +960,7 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {
                        insert.clearBindings();
                    }
                }
            }

            db.setTransactionSuccessful();
        } finally {
@@ -888,6 +976,8 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {
     * update.
     */
    public void updateSmartDialDatabase() {
        initMultiLanguageSearch();

        final SQLiteDatabase db = getWritableDatabase();

        synchronized(mLock) {
@@ -1064,14 +1154,19 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {
    public ArrayList<ContactNumber>  getLooseMatches(String query,
            SmartDialNameMatcher nameMatcher) {
        final boolean inUpdate = sInUpdate.get();
        if (inUpdate) {
        if (inUpdate || query.length() == 0) {
            return Lists.newArrayList();
        }

        final SQLiteDatabase db = getReadableDatabase();

        /** Uses SQL query wildcard '%' to represent prefix matching.*/
        final String looseQuery = query + "%";
        StringBuilder looseQuery = new StringBuilder(query);
        for (int i = 0; i < looseQuery.toString().length();) {
            looseQuery.insert(i, "%");
            i = i + 2;
        }
        looseQuery.append("%");

        final ArrayList<ContactNumber> result = Lists.newArrayList();

@@ -1087,9 +1182,11 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {
                SmartDialDbColumns.NUMBER + ", " +
                SmartDialDbColumns.CONTACT_ID + ", " +
                SmartDialDbColumns.LOOKUP_KEY + ", " +
                SmartDialDbColumns.CARRIER_PRESENCE +
                " FROM " + Tables.SMARTDIAL_TABLE + " WHERE " +
                SmartDialDbColumns.CONTACT_ID + " IN " +
                SmartDialDbColumns.CARRIER_PRESENCE + ", " +
                SmartDialDbColumns.ACCOUNT_TYPE + ", " +
                SmartDialDbColumns.ACCOUNT_NAME +
                " FROM " + Tables.SMARTDIAL_TABLE +
                " WHERE " + SmartDialDbColumns.CONTACT_ID + " IN " +
                " (SELECT " + PrefixColumns.CONTACT_ID +
                    " FROM " + Tables.PREFIX_TABLE +
                    " WHERE " + Tables.PREFIX_TABLE + "." + PrefixColumns.PREFIX +
@@ -1112,6 +1209,8 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {
            final int columnId = 4;
            final int columnLookupKey = 5;
            final int columnCarrierPresence = 6;
            final int columnAccountType = 7;
            final int columnAccountName = 8;
            if (DEBUG) {
                stopWatch.lap("Found column IDs");
            }
@@ -1130,6 +1229,8 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {
                final long photoId = cursor.getLong(columnPhotoId);
                final String lookupKey = cursor.getString(columnLookupKey);
                final int carrierPresence = cursor.getInt(columnCarrierPresence);
                final String accountType = cursor.getString(columnAccountType);
                final String accountName = cursor.getString(columnAccountName);

                /** If a contact already exists and another phone number of the contact is being
                 * processed, skip the second instance.
@@ -1150,7 +1251,7 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {
                    /** If a contact has not been added, add it to the result and the hash set.*/
                    duplicates.add(contactMatch);
                    result.add(new ContactNumber(id, dataID, displayName, phoneNumber, lookupKey,
                            photoId, carrierPresence));
                            photoId, carrierPresence, accountType, accountName));
                    counter++;
                    if (DEBUG) {
                        stopWatch.lap("Added one result: Name: " + displayName);
+3 −1
Original line number Diff line number Diff line
@@ -65,7 +65,7 @@ public class SmartDialCursorLoader extends AsyncTaskLoader<Cursor> {
        mQuery = SmartDialNameMatcher.normalizeNumber(query, SmartDialPrefix.getMap());

        /** Constructs a name matcher object for matching names. */
        mNameMatcher = new SmartDialNameMatcher(mQuery, SmartDialPrefix.getMap());
        mNameMatcher = new SmartDialNameMatcher(mQuery, SmartDialPrefix.getMap(), mContext);
    }

    /**
@@ -103,6 +103,8 @@ public class SmartDialCursorLoader extends AsyncTaskLoader<Cursor> {
            row[PhoneQuery.PHOTO_ID] = contact.photoId;
            row[PhoneQuery.DISPLAY_NAME] = contact.displayName;
            row[PhoneQuery.CARRIER_PRESENCE] = contact.carrierPresence;
            row[PhoneQuery.PHONE_ACCOUNT_TYPE] = contact.accountType;
            row[PhoneQuery.PHONE_ACCOUNT_NAME] = contact.accountName;
            cursor.addRow(row);
        }
        return cursor;
+101 −47
Original line number Diff line number Diff line
@@ -17,13 +17,17 @@
package com.android.dialer.dialpad;

import android.support.annotation.Nullable;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;

import com.android.dialer.database.DialerDatabaseHelper;
import com.android.dialer.dialpad.SmartDialPrefix.PhoneNumberTokens;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Lists;

import java.lang.reflect.Method;
import java.util.ArrayList;

/**
@@ -35,6 +39,8 @@ import java.util.ArrayList;
 */
public class SmartDialNameMatcher {

    private final static String TAG = "SmartDialNameMatcher";

    private String mQuery;

    // Whether or not we allow matches like 57 - (J)ohn (S)mith
@@ -53,14 +59,24 @@ public class SmartDialNameMatcher {
    private String mNameMatchMask = "";
    private String mPhoneNumberMatchMask = "";

    private Context mContext;
    private String mSchar = "+*#-.(,)/ ";
    private Object mMultiMatchObject;
    private Method mMultiMatchMethod;

    @VisibleForTesting
    public SmartDialNameMatcher(String query) {
        this(query, LATIN_SMART_DIAL_MAP);
    public SmartDialNameMatcher(String query, Context context) {
        this(query, LATIN_SMART_DIAL_MAP, context);
    }

    public SmartDialNameMatcher(String query, SmartDialMap map) {
    public SmartDialNameMatcher(String query, SmartDialMap map, Context context) {
        mQuery = query;
        mMap = map;
        mContext = context;
        mMultiMatchObject = DialerDatabaseHelper.getInstance(mContext)
                .getMultiMatchObject();
        mMultiMatchMethod = DialerDatabaseHelper.getInstance(mContext)
                .getMultiMatchMethod();
    }

    /**
@@ -135,22 +151,6 @@ public class SmartDialNameMatcher {

        // Try matching the number as is
        SmartDialMatchPosition matchPos = matchesNumberWithOffset(phoneNumber, query, 0);
        if (matchPos == null) {
            final PhoneNumberTokens phoneNumberTokens =
                    SmartDialPrefix.parsePhoneNumber(phoneNumber);

            if (phoneNumberTokens == null) {
                return matchPos;
            }
            if (phoneNumberTokens.countryCodeOffset != 0) {
                matchPos = matchesNumberWithOffset(phoneNumber, query,
                        phoneNumberTokens.countryCodeOffset);
            }
            if (matchPos == null && phoneNumberTokens.nanpCodeOffset != 0 && useNanp) {
                matchPos = matchesNumberWithOffset(phoneNumber, query,
                        phoneNumberTokens.nanpCodeOffset);
            }
        }
        if (matchPos != null) {
            replaceBitInMask(builder, matchPos);
            mPhoneNumberMatchMask = builder.toString();
@@ -195,40 +195,47 @@ public class SmartDialNameMatcher {
     */
    private SmartDialMatchPosition matchesNumberWithOffset(String phoneNumber, String query,
            int offset) {
        if (TextUtils.isEmpty(phoneNumber) || TextUtils.isEmpty(query)) {
        if (TextUtils.isEmpty(phoneNumber) || TextUtils.isEmpty(query)
                || query.length() > phoneNumber.length()) {
            return null;
        }
        int queryAt = 0;
        int numberAt = offset;
        for (int i = offset; i < phoneNumber.length(); i++) {
            if (queryAt == query.length()) {

        String phoneNum = phoneNumber.replaceAll("[\\+\\*\\#\\-\\.\\(\\,\\)\\/ ]", "");
        if (!TextUtils.isEmpty(phoneNum) && phoneNum.contains(query)) {
            // firstly, find the start position in original phone number.
            int start = phoneNum.indexOf(query);
            int length = phoneNumber.length();
            for (int i = start; i < length; i++) {
                char ch = phoneNumber.charAt(i);
                if (ch != phoneNum.charAt(start)) {
                    continue;
                }
                if (phoneNumber.substring(i).replaceAll("[\\+\\*\\#\\-\\.\\(\\,\\)\\/ ]", "")
                        .indexOf(query) == 0) {
                    start = i;
                    break;
                }
            char ch = phoneNumber.charAt(i);
            if (mMap.isValidDialpadNumericChar(ch)) {
                if (ch != query.charAt(queryAt)) {
                    return null;
            }
                queryAt++;
            } else {
                if (queryAt == 0) {
                    // Found a separator before any part of the query was matched, so advance the
                    // offset to avoid prematurely highlighting separators before the rest of the
                    // query.
                    // E.g. don't highlight the first '-' if we're matching 1-510-111-1111 with
                    // '510'.
                    // However, if the current offset is 0, just include the beginning separators
                    // anyway, otherwise the highlighting ends up looking weird.
                    // E.g. if we're matching (510)-111-1111 with '510', we should include the
                    // first '('.
                    if (offset != 0) {
                        offset++;
            // secondly, find the end position in original phone number.
            int specialCount = 0;
            int queryLength = query.length();
            int end = start + queryLength;
            for (int i = start; i < length; i++) {
                char ch = phoneNumber.charAt(i);
                if (mSchar.indexOf(ch) != -1) {
                    specialCount++;
                    continue;
                }

                if (i - start + 1 - specialCount == queryLength) {
                    end = i + 1;
                    break;
                }
            }
            numberAt++;
            return new SmartDialMatchPosition(start, end);
        } else {
            return null;
        }
        return new SmartDialMatchPosition(0 + offset, numberAt);
    }

    /**
@@ -412,8 +419,12 @@ public class SmartDialNameMatcher {

    public boolean matches(String displayName) {
        mMatchPositions.clear();
        if (mMultiMatchObject != null && mMultiMatchMethod != null) {
            return matchesMultiLanguage(displayName, mQuery, mMatchPositions);
        } else {
            return matchesCombination(displayName, mQuery, mMatchPositions);
        }
    }

    public ArrayList<SmartDialMatchPosition> getMatchPositions() {
        // Return a clone of mMatchPositions so that the caller can use it without
@@ -436,4 +447,47 @@ public class SmartDialNameMatcher {
    public String getQuery() {
        return mQuery;
    }

    boolean matchesMultiLanguage(String displayName, String query,
            ArrayList<SmartDialMatchPosition> matchList) {
        StringBuilder builder = new StringBuilder();
        constructEmptyMask(builder, displayName.length());
        mNameMatchMask = builder.toString();
        final int nameLength = displayName.length();
        final int queryLength = query.length();

        if (queryLength == 0) {
            return false;
        }
        // contains the start, not the end poing
        int[] indexs = null;
        try {
            indexs = (int[]) mMultiMatchMethod.invoke(mMultiMatchObject,
                    query, displayName, 0);
            // mMultimatch.getMatchStringIndex(query, displayName, 0);
            if (indexs == null) {
                return false;
            }
        } catch (Exception e) {
            Log.d(TAG, "Exception:" + e);
            return false;
        }

        for (int i = 0; i < indexs.length; i = i + 2) {
            int start = indexs[i];
            int end = indexs[i + 1];
            if (start >= 0 && end >= 0) {
                matchList.add(new SmartDialMatchPosition(start, end + 1));
            } else {
                Log.d(TAG, "Invalid index, start is:" + start + " end is:"
                        + end + " for name:" + displayName);
            }
        }

        for (SmartDialMatchPosition match : matchList) {
            replaceBitInMask(builder, match);
        }
        mNameMatchMask = builder.toString();
        return true;
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -45,7 +45,7 @@ public class SmartDialNumberListAdapter extends DialerPhoneNumberListAdapter {

    public SmartDialNumberListAdapter(Context context) {
        super(context);
        mNameMatcher = new SmartDialNameMatcher("", SmartDialPrefix.getMap());
        mNameMatcher = new SmartDialNameMatcher("", SmartDialPrefix.getMap(), context);
        setShortcutEnabled(SmartDialNumberListAdapter.SHORTCUT_DIRECT_CALL, false);

        if (DEBUG) {
Loading