diff options
| author | cretin45 <cretin45@gmail.com> | 2014-07-23 16:32:45 -0700 |
|---|---|---|
| committer | Steve Kondik <steve@cyngn.com> | 2015-11-16 19:53:26 -0800 |
| commit | e0bbf0a974f9ff5bc8870f53e8dfd001c3cbacae (patch) | |
| tree | 1bc9202166ffffe411e44f3f35a9b1f5a48c8908 | |
| parent | 3bc8f2bb1de7dd790bcb198a790d752f1a73b6fd (diff) | |
| download | packages_apps_Dialer-e0bbf0a974f9ff5bc8870f53e8dfd001c3cbacae.tar.gz packages_apps_Dialer-e0bbf0a974f9ff5bc8870f53e8dfd001c3cbacae.tar.bz2 packages_apps_Dialer-e0bbf0a974f9ff5bc8870f53e8dfd001c3cbacae.zip | |
Dialer: Refactor SmartDial for additional languages
Adds Chinese support and improved Korean support.
p4: Refactor for transliterating the displayName.
Change-Id: Id3175c6f4eda4962be9d25bd43c24a834c3c5ff1
Read option for t9 search input locale and refresh smart dial db when locale changes
Change-Id: I3cd942741310f16678ed9eb675bbba2cbc72d5e8
Add setting for t9 search input locale
Change-Id: I410b4e20afa85302a9ef3174c2f219e33b1b7987
Return match positions to enable highlighting for CN smart dial
Change-Id: I9255c582708c2fbfe22e7a2bf3ad971da692e2a9
Dialer: Fix T9 for Hebrew
Change-Id: I1c8be92acc1fc29b557eb7cda0d910cf036f3aea
Move smart dialer initialization to onStart().
The SmartDialCursorLoader expects the SmartDialPrefix map to be
initialized when it's created. As
- the loader is initialized in SmartDialSearchFragment's onStart and
- the fragment's onStart is called asynchronously after the activity's
onStart and
- the map initialization happens in
SmartDialPrefix.initializeNanpSettings() and
- the above method is currently called in the activity's onResume()
we have a race condition between the activity's onResume() and the
fragment's onStart() (at least when processing a dial intent, which is
what causes the dialpad fragment to be immediately shown).
Fix that race condition by moving the initialization into the activity's
onStart() and running it before calling the super method to ensure it
happens before fragments are started.
JIRA:NIGHTLIES-760
Change-Id: I9767cbba3b177fdd5b1de86914cb1e40d20835ab
Ensure the smart dial name matcher is initialized in onStart().
Otherwise SmartDialPrefix.getMap() might not be initialized yet.
JIRA: NIGHTLIES-1279
Change-Id: If3aca2809faf0c1f379518096a76f17d357fb8e2
18 files changed, 919 insertions, 21 deletions
diff --git a/Android.mk b/Android.mk index d6679479a..1f8e35dc5 100644 --- a/Android.mk +++ b/Android.mk @@ -18,6 +18,9 @@ res_dirs := res \ $(phone_common_dir)/res LOCAL_SRC_FILES := $(call all-java-files-under, $(src_dirs)) +LOCAL_SRC_FILES += ../../providers/ContactsProvider/src/com/android/providers/contacts/NameSplitter.java \ + ../../providers/ContactsProvider/src/com/android/providers/contacts/HanziToPinyin.java \ + ../../providers/ContactsProvider/src/com/android/providers/contacts/util/NeededForTesting.java LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs)) \ frameworks/support/v7/cardview/res frameworks/support/v7/recyclerview/res LOCAL_ASSET_DIR += $(LOCAL_PATH)/assets diff --git a/res/values/cm_strings.xml b/res/values/cm_strings.xml index 597e79051..5b0391933 100644 --- a/res/values/cm_strings.xml +++ b/res/values/cm_strings.xml @@ -19,6 +19,11 @@ <string name="nearby_places">Nearby places</string> <string name="people">People</string> + <!-- dialpad t9 search --> + <string name="preference_category_t9_dialpad_search">Dialpad T9 search</string> + <string name="t9_search_input_locale">T9 search input</string> + <string name="t9_search_input_locale_default">Default</string> + <!-- Number lookup --> <string name="lookup_settings_label">Phone number lookup</string> <string name="lookup_settings_description">Lookup of unknown phone numbers</string> diff --git a/res/xml/display_options_settings.xml b/res/xml/display_options_settings.xml index 07aadf758..28bdfe0a5 100644 --- a/res/xml/display_options_settings.xml +++ b/res/xml/display_options_settings.xml @@ -28,4 +28,11 @@ android:title="@string/display_options_view_names_as" android:dialogTitle="@string/display_options_view_names_as" /> + <ListPreference + android:key="button_t9_search_input" + android:title="@string/t9_search_input_locale" + android:summary="%s" + android:defaultValue="" + android:persistent="false" /> + </PreferenceScreen> diff --git a/src/com/android/dialer/DialtactsActivity.java b/src/com/android/dialer/DialtactsActivity.java index 84dedfa8b..a62b5f0aa 100644 --- a/src/com/android/dialer/DialtactsActivity.java +++ b/src/com/android/dialer/DialtactsActivity.java @@ -22,6 +22,7 @@ import android.app.FragmentTransaction; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Configuration; @@ -30,6 +31,10 @@ import android.net.Uri; import android.os.Bundle; import android.os.Trace; import android.provider.CallLog.Calls; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.preference.PreferenceManager; +import android.provider.CallLog.Calls; import android.speech.RecognizerIntent; import android.support.v4.view.ViewPager; import android.telecom.PhoneAccount; @@ -90,6 +95,7 @@ import com.android.dialer.widget.SearchEditTextLayout; import com.android.dialer.widget.SearchEditTextLayout.Callback; import com.android.dialerbind.DatabaseHelperManager; import com.android.phone.common.animation.AnimUtils; +import com.android.phone.common.util.SettingsUtil; import com.android.ims.ImsManager; import com.android.phone.common.animation.AnimationListenerAdapter; @@ -97,6 +103,7 @@ import junit.framework.Assert; import java.util.ArrayList; import java.util.List; +import java.util.Locale; /** * The dialer tab's title is 'phone', a more common name (see strings.xml). @@ -119,6 +126,7 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O public static final boolean DEBUG = false; public static final String SHARED_PREFS_NAME = "com.android.dialer_preferences"; + private static final String PREF_LAST_T9_LOCALE = "smart_dial_prefix_last_t9_locale"; /** @see #getCallOrigin() */ private static final String CALL_ORIGIN_DIALTACTS = @@ -504,12 +512,35 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O Trace.beginSection(TAG + " initialize smart dialing"); mDialerDatabaseHelper = DatabaseHelperManager.getDatabaseHelper(this); - SmartDialPrefix.initializeNanpSettings(this); Trace.endSection(); Trace.endSection(); } @Override + protected void onStart() { + super.onStart(); + + // make this call on resume in case user changed t9 locale in settings + SmartDialPrefix.initializeNanpSettings(this); + + // if locale has changed since last time, refresh the smart dial db + Locale locale = SettingsUtil.getT9SearchInputLocale(this); + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); + String prevLocale = prefs.getString(PREF_LAST_T9_LOCALE, null); + + if (!TextUtils.equals(locale.toString(), prevLocale)) { + mDialerDatabaseHelper.recreateSmartDialDatabaseInBackground(); + if (mDialpadFragment != null) + mDialpadFragment.refreshKeypad(); + + prefs.edit().putString(PREF_LAST_T9_LOCALE, locale.toString()).apply(); + } + else { + mDialerDatabaseHelper.startSmartDialUpdateThread(); + } + } + + @Override protected void onResume() { Trace.beginSection(TAG + " onResume"); super.onResume(); @@ -551,8 +582,6 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O setConferenceDialButtonImage(false); setConferenceDialButtonVisibility(true); - SmartDialPrefix.initializeNanpSettings(this); - if (getIntent().hasExtra(EXTRA_SHOW_TAB)) { int index = getIntent().getIntExtra(EXTRA_SHOW_TAB, ListsFragment.TAB_INDEX_SPEED_DIAL); if (index < mListsFragment.getTabCount()) { diff --git a/src/com/android/dialer/database/DialerDatabaseHelper.java b/src/com/android/dialer/database/DialerDatabaseHelper.java index aad9472a6..7bb5782e8 100644 --- a/src/com/android/dialer/database/DialerDatabaseHelper.java +++ b/src/com/android/dialer/database/DialerDatabaseHelper.java @@ -552,6 +552,48 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper { super.onPostExecute(o); } } + + /** + * Deletes all smart dial data and recreates it from contacts + */ + public void recreateSmartDialDatabaseInBackground() { new SmartDialRecreateAsyncTask().execute(); } + + private class SmartDialRecreateAsyncTask extends AsyncTask { + @Override + protected Object doInBackground(Object[] objects) { + if (DEBUG) { + Log.v(TAG, "Recreating database"); + } + + // reset last updated so that we query for all contacts + resetSmartDialLastUpdatedTime(); + + // clear all contacts + final SQLiteDatabase db = getWritableDatabase(); + removeAllContacts(db); + + // repopulate + updateSmartDialDatabase(); + return null; + } + + @Override + protected void onCancelled() { + if (DEBUG) { + Log.v(TAG, "Recreate Cancelled"); + } + super.onCancelled(); + } + + @Override + protected void onPostExecute(Object o) { + if (DEBUG) { + Log.v(TAG, "Recreate Finished"); + } + super.onPostExecute(o); + } + } + /** * Removes rows in the smartdial database that matches the contacts that have been deleted * by other apps since last update. diff --git a/src/com/android/dialer/dialpad/ChineseSmartDialMap.java b/src/com/android/dialer/dialpad/ChineseSmartDialMap.java new file mode 100644 index 000000000..5e63f8857 --- /dev/null +++ b/src/com/android/dialer/dialpad/ChineseSmartDialMap.java @@ -0,0 +1,505 @@ +package com.android.dialer.dialpad; + +import com.android.providers.contacts.HanziToPinyin; + +import android.text.TextUtils; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; + +public class ChineseSmartDialMap implements SmartDialMap { + + private static final char[] LATIN_LETTERS_TO_DIGITS = { + '2', '2', '2', // A,B,C -> 2 + '3', '3', '3', // D,E,F -> 3 + '4', '4', '4', // G,H,I -> 4 + '5', '5', '5', // J,K,L -> 5 + '6', '6', '6', // M,N,O -> 6 + '7', '7', '7', '7', // P,Q,R,S -> 7 + '8', '8', '8', // T,U,V -> 8 + '9', '9', '9', '9' // W,X,Y,Z -> 9 + }; + + @Override + public boolean isValidDialpadAlphabeticChar(char ch) { + return (ch >= 'a' && ch <= 'z'); + } + + @Override + public boolean isValidDialpadNumericChar(char ch) { + return (ch >= '0' && ch <= '9'); + } + + @Override + public boolean isValidDialpadCharacter(char ch) { + return (isValidDialpadAlphabeticChar(ch) || isValidDialpadNumericChar(ch)); + } + + /* + * The switch statement in this function was generated using the python code: + * from unidecode import unidecode + * for i in range(192, 564): + * char = unichr(i) + * decoded = unidecode(char) + * # Unicode characters that decompose into multiple characters i.e. + * # into ss are not supported for now + * if (len(decoded) == 1 and decoded.isalpha()): + * print "case '" + char + "': return '" + unidecode(char) + "';" + * + * This gives us a way to map characters containing accents/diacritics to their + * alphabetic equivalents. The unidecode library can be found at: + * http://pypi.python.org/pypi/Unidecode/0.04.1 + * + * Also remaps all upper case latin characters to their lower case equivalents. + */ + @Override + public char normalizeCharacter(char ch) { + switch (ch) { + case 'À': return 'a'; + case 'Á': return 'a'; + case 'Â': return 'a'; + case 'Ã': return 'a'; + case 'Ä': return 'a'; + case 'Å': return 'a'; + case 'Ç': return 'c'; + case 'È': return 'e'; + case 'É': return 'e'; + case 'Ê': return 'e'; + case 'Ë': return 'e'; + case 'Ì': return 'i'; + case 'Í': return 'i'; + case 'Î': return 'i'; + case 'Ï': return 'i'; + case 'Ð': return 'd'; + case 'Ñ': return 'n'; + case 'Ò': return 'o'; + case 'Ó': return 'o'; + case 'Ô': return 'o'; + case 'Õ': return 'o'; + case 'Ö': return 'o'; + case '×': return 'x'; + case 'Ø': return 'o'; + case 'Ù': return 'u'; + case 'Ú': return 'u'; + case 'Û': return 'u'; + case 'Ü': return 'u'; + case 'Ý': return 'u'; + case 'à': return 'a'; + case 'á': return 'a'; + case 'â': return 'a'; + case 'ã': return 'a'; + case 'ä': return 'a'; + case 'å': return 'a'; + case 'ç': return 'c'; + case 'è': return 'e'; + case 'é': return 'e'; + case 'ê': return 'e'; + case 'ë': return 'e'; + case 'ì': return 'i'; + case 'í': return 'i'; + case 'î': return 'i'; + case 'ï': return 'i'; + case 'ð': return 'd'; + case 'ñ': return 'n'; + case 'ò': return 'o'; + case 'ó': return 'o'; + case 'ô': return 'o'; + case 'õ': return 'o'; + case 'ö': return 'o'; + case 'ø': return 'o'; + case 'ù': return 'u'; + case 'ú': return 'u'; + case 'û': return 'u'; + case 'ü': return 'u'; + case 'ý': return 'y'; + case 'ÿ': return 'y'; + case 'Ā': return 'a'; + case 'ā': return 'a'; + case 'Ă': return 'a'; + case 'ă': return 'a'; + case 'Ą': return 'a'; + case 'ą': return 'a'; + case 'Ć': return 'c'; + case 'ć': return 'c'; + case 'Ĉ': return 'c'; + case 'ĉ': return 'c'; + case 'Ċ': return 'c'; + case 'ċ': return 'c'; + case 'Č': return 'c'; + case 'č': return 'c'; + case 'Ď': return 'd'; + case 'ď': return 'd'; + case 'Đ': return 'd'; + case 'đ': return 'd'; + case 'Ē': return 'e'; + case 'ē': return 'e'; + case 'Ĕ': return 'e'; + case 'ĕ': return 'e'; + case 'Ė': return 'e'; + case 'ė': return 'e'; + case 'Ę': return 'e'; + case 'ę': return 'e'; + case 'Ě': return 'e'; + case 'ě': return 'e'; + case 'Ĝ': return 'g'; + case 'ĝ': return 'g'; + case 'Ğ': return 'g'; + case 'ğ': return 'g'; + case 'Ġ': return 'g'; + case 'ġ': return 'g'; + case 'Ģ': return 'g'; + case 'ģ': return 'g'; + case 'Ĥ': return 'h'; + case 'ĥ': return 'h'; + case 'Ħ': return 'h'; + case 'ħ': return 'h'; + case 'Ĩ': return 'i'; + case 'ĩ': return 'i'; + case 'Ī': return 'i'; + case 'ī': return 'i'; + case 'Ĭ': return 'i'; + case 'ĭ': return 'i'; + case 'Į': return 'i'; + case 'į': return 'i'; + case 'İ': return 'i'; + case 'ı': return 'i'; + case 'Ĵ': return 'j'; + case 'ĵ': return 'j'; + case 'Ķ': return 'k'; + case 'ķ': return 'k'; + case 'ĸ': return 'k'; + case 'Ĺ': return 'l'; + case 'ĺ': return 'l'; + case 'Ļ': return 'l'; + case 'ļ': return 'l'; + case 'Ľ': return 'l'; + case 'ľ': return 'l'; + case 'Ŀ': return 'l'; + case 'ŀ': return 'l'; + case 'Ł': return 'l'; + case 'ł': return 'l'; + case 'Ń': return 'n'; + case 'ń': return 'n'; + case 'Ņ': return 'n'; + case 'ņ': return 'n'; + case 'Ň': return 'n'; + case 'ň': return 'n'; + case 'Ō': return 'o'; + case 'ō': return 'o'; + case 'Ŏ': return 'o'; + case 'ŏ': return 'o'; + case 'Ő': return 'o'; + case 'ő': return 'o'; + case 'Ŕ': return 'r'; + case 'ŕ': return 'r'; + case 'Ŗ': return 'r'; + case 'ŗ': return 'r'; + case 'Ř': return 'r'; + case 'ř': return 'r'; + case 'Ś': return 's'; + case 'ś': return 's'; + case 'Ŝ': return 's'; + case 'ŝ': return 's'; + case 'Ş': return 's'; + case 'ş': return 's'; + case 'Š': return 's'; + case 'š': return 's'; + case 'Ţ': return 't'; + case 'ţ': return 't'; + case 'Ť': return 't'; + case 'ť': return 't'; + case 'Ŧ': return 't'; + case 'ŧ': return 't'; + case 'Ũ': return 'u'; + case 'ũ': return 'u'; + case 'Ū': return 'u'; + case 'ū': return 'u'; + case 'Ŭ': return 'u'; + case 'ŭ': return 'u'; + case 'Ů': return 'u'; + case 'ů': return 'u'; + case 'Ű': return 'u'; + case 'ű': return 'u'; + case 'Ų': return 'u'; + case 'ų': return 'u'; + case 'Ŵ': return 'w'; + case 'ŵ': return 'w'; + case 'Ŷ': return 'y'; + case 'ŷ': return 'y'; + case 'Ÿ': return 'y'; + case 'Ź': return 'z'; + case 'ź': return 'z'; + case 'Ż': return 'z'; + case 'ż': return 'z'; + case 'Ž': return 'z'; + case 'ž': return 'z'; + case 'ſ': return 's'; + case 'ƀ': return 'b'; + case 'Ɓ': return 'b'; + case 'Ƃ': return 'b'; + case 'ƃ': return 'b'; + case 'Ɔ': return 'o'; + case 'Ƈ': return 'c'; + case 'ƈ': return 'c'; + case 'Ɖ': return 'd'; + case 'Ɗ': return 'd'; + case 'Ƌ': return 'd'; + case 'ƌ': return 'd'; + case 'ƍ': return 'd'; + case 'Ɛ': return 'e'; + case 'Ƒ': return 'f'; + case 'ƒ': return 'f'; + case 'Ɠ': return 'g'; + case 'Ɣ': return 'g'; + case 'Ɩ': return 'i'; + case 'Ɨ': return 'i'; + case 'Ƙ': return 'k'; + case 'ƙ': return 'k'; + case 'ƚ': return 'l'; + case 'ƛ': return 'l'; + case 'Ɯ': return 'w'; + case 'Ɲ': return 'n'; + case 'ƞ': return 'n'; + case 'Ɵ': return 'o'; + case 'Ơ': return 'o'; + case 'ơ': return 'o'; + case 'Ƥ': return 'p'; + case 'ƥ': return 'p'; + case 'ƫ': return 't'; + case 'Ƭ': return 't'; + case 'ƭ': return 't'; + case 'Ʈ': return 't'; + case 'Ư': return 'u'; + case 'ư': return 'u'; + case 'Ʊ': return 'y'; + case 'Ʋ': return 'v'; + case 'Ƴ': return 'y'; + case 'ƴ': return 'y'; + case 'Ƶ': return 'z'; + case 'ƶ': return 'z'; + case 'ƿ': return 'w'; + case 'Ǎ': return 'a'; + case 'ǎ': return 'a'; + case 'Ǐ': return 'i'; + case 'ǐ': return 'i'; + case 'Ǒ': return 'o'; + case 'ǒ': return 'o'; + case 'Ǔ': return 'u'; + case 'ǔ': return 'u'; + case 'Ǖ': return 'u'; + case 'ǖ': return 'u'; + case 'Ǘ': return 'u'; + case 'ǘ': return 'u'; + case 'Ǚ': return 'u'; + case 'ǚ': return 'u'; + case 'Ǜ': return 'u'; + case 'ǜ': return 'u'; + case 'Ǟ': return 'a'; + case 'ǟ': return 'a'; + case 'Ǡ': return 'a'; + case 'ǡ': return 'a'; + case 'Ǥ': return 'g'; + case 'ǥ': return 'g'; + case 'Ǧ': return 'g'; + case 'ǧ': return 'g'; + case 'Ǩ': return 'k'; + case 'ǩ': return 'k'; + case 'Ǫ': return 'o'; + case 'ǫ': return 'o'; + case 'Ǭ': return 'o'; + case 'ǭ': return 'o'; + case 'ǰ': return 'j'; + case 'Dz': return 'd'; + case 'Ǵ': return 'g'; + case 'ǵ': return 'g'; + case 'Ƿ': return 'w'; + case 'Ǹ': return 'n'; + case 'ǹ': return 'n'; + case 'Ǻ': return 'a'; + case 'ǻ': return 'a'; + case 'Ǿ': return 'o'; + case 'ǿ': return 'o'; + case 'Ȁ': return 'a'; + case 'ȁ': return 'a'; + case 'Ȃ': return 'a'; + case 'ȃ': return 'a'; + case 'Ȅ': return 'e'; + case 'ȅ': return 'e'; + case 'Ȇ': return 'e'; + case 'ȇ': return 'e'; + case 'Ȉ': return 'i'; + case 'ȉ': return 'i'; + case 'Ȋ': return 'i'; + case 'ȋ': return 'i'; + case 'Ȍ': return 'o'; + case 'ȍ': return 'o'; + case 'Ȏ': return 'o'; + case 'ȏ': return 'o'; + case 'Ȑ': return 'r'; + case 'ȑ': return 'r'; + case 'Ȓ': return 'r'; + case 'ȓ': return 'r'; + case 'Ȕ': return 'u'; + case 'ȕ': return 'u'; + case 'Ȗ': return 'u'; + case 'ȗ': return 'u'; + case 'Ș': return 's'; + case 'ș': return 's'; + case 'Ț': return 't'; + case 'ț': return 't'; + case 'Ȝ': return 'y'; + case 'ȝ': return 'y'; + case 'Ȟ': return 'h'; + case 'ȟ': return 'h'; + case 'Ȥ': return 'z'; + case 'ȥ': return 'z'; + case 'Ȧ': return 'a'; + case 'ȧ': return 'a'; + case 'Ȩ': return 'e'; + case 'ȩ': return 'e'; + case 'Ȫ': return 'o'; + case 'ȫ': return 'o'; + case 'Ȭ': return 'o'; + case 'ȭ': return 'o'; + case 'Ȯ': return 'o'; + case 'ȯ': return 'o'; + case 'Ȱ': return 'o'; + case 'ȱ': return 'o'; + case 'Ȳ': return 'y'; + case 'ȳ': return 'y'; + case 'A': return 'a'; + case 'B': return 'b'; + case 'C': return 'c'; + case 'D': return 'd'; + case 'E': return 'e'; + case 'F': return 'f'; + case 'G': return 'g'; + case 'H': return 'h'; + case 'I': return 'i'; + case 'J': return 'j'; + case 'K': return 'k'; + case 'L': return 'l'; + case 'M': return 'm'; + case 'N': return 'n'; + case 'O': return 'o'; + case 'P': return 'p'; + case 'Q': return 'q'; + case 'R': return 'r'; + case 'S': return 's'; + case 'T': return 't'; + case 'U': return 'u'; + case 'V': return 'v'; + case 'W': return 'w'; + case 'X': return 'x'; + case 'Y': return 'y'; + case 'Z': return 'z'; + default: + return ch; + } + } + + @Override + public byte getDialpadIndex(char ch) { + if (ch >= '0' && ch <= '9') { + return (byte) (ch - '0'); + } else if (ch >= 'a' && ch <= 'z') { + return (byte) (LATIN_LETTERS_TO_DIGITS[ch - 'a'] - '0'); + } else { + return -1; + } + } + + @Override + public char getDialpadNumericCharacter(char ch) { + if (ch >= 'a' && ch <= 'z') { + return LATIN_LETTERS_TO_DIGITS[ch - 'a']; + } + return ch; + } + + /* + * Generates a space delimited string of pinyins + */ + private String tokenizeToPinyins(String displayName) { + HanziToPinyin hanziToPinyin = HanziToPinyin.getInstance(); + ArrayList<HanziToPinyin.Token> tokens = hanziToPinyin.getTokens(displayName); + ArrayList<String> pinyins = new ArrayList<String>(); + for (HanziToPinyin.Token token : tokens) { + if (token.type != HanziToPinyin.Token.PINYIN) { + return displayName; + } else { + pinyins.add(token.target); + } + } + return TextUtils.join(" ", pinyins); + } + + @Override + public String transliterateName(String index) { + return tokenizeToPinyins(index); + } + + /* + * Uses the default matching logic on the pinyin name and attempts to map the match positions + * back to the original display name + */ + @Override + public boolean matchesCombination(SmartDialNameMatcher smartDialNameMatcher, + String displayName, String query, ArrayList<SmartDialMatchPosition> matchList) { + + String pinyinName = tokenizeToPinyins(displayName); + + ArrayList<SmartDialMatchPosition> computedMatchList = new ArrayList<SmartDialMatchPosition>(); + boolean matches = smartDialNameMatcher.matchesCombination(pinyinName, query, computedMatchList); + if (!matches) + return false; + + // name was translated to pinyin before matching. attempt to map the match positions + // back to the original display string + if (!displayName.equals(pinyinName)) { + + // construct an array that maps each character of the pinyin name back to the index of + // the hanzi token from which it came + // For example, if: + // displayName = 红霞李 + // pinyinName = "hong xia li" + // then: + // pinyinMapping = 0,0,0,0,-1,1,1,1,-1,2,2 + int[] pinyinMapping = new int[pinyinName.length()]; + int curToken = 0; + for (int i=0; i < pinyinName.length(); ++i) { + char c = pinyinName.charAt(i); + if (c == ' ') { + ++curToken; + pinyinMapping[i] = -1; + } + else { + pinyinMapping[i] = curToken; + } + } + + // calculate unique hanzi characters that are matched + Set<Integer> positionsToHighlight = new HashSet<Integer>(); + for (SmartDialMatchPosition matchPosition : computedMatchList) { + for (int pos = matchPosition.start; pos < matchPosition.end; ++pos) { + int mappedPos = pinyinMapping[pos]; + if (mappedPos >= 0) + positionsToHighlight.add(mappedPos); + } + } + + // reset computed matches + computedMatchList = new ArrayList<SmartDialMatchPosition>(); + for (int matchPos : positionsToHighlight) { + // use one object per position for simplicity + computedMatchList.add(new SmartDialMatchPosition(matchPos, matchPos+1)); + } + } + + matchList.addAll(computedMatchList); + return true; + } + + +} diff --git a/src/com/android/dialer/dialpad/DialpadFragment.java b/src/com/android/dialer/dialpad/DialpadFragment.java index d406837cc..eb6ad0341 100644 --- a/src/com/android/dialer/dialpad/DialpadFragment.java +++ b/src/com/android/dialer/dialpad/DialpadFragment.java @@ -27,6 +27,7 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; +import android.content.res.Configuration; import android.content.res.Resources; import android.database.Cursor; import android.graphics.Bitmap; @@ -90,6 +91,7 @@ import com.google.common.annotations.VisibleForTesting; import java.util.HashSet; import java.util.List; +import java.util.Locale; /** * Fragment that displays a twelve-key phone dialpad. @@ -633,6 +635,12 @@ public class DialpadFragment extends Fragment zero.setOnLongClickListener(this); } + public void refreshKeypad() { + if (mDialpadView != null) { + mDialpadView.refreshKeypad(); + } + } + @Override public void onStart() { Trace.beginSection(TAG + " onStart"); diff --git a/src/com/android/dialer/dialpad/GreekSmartDialMap.java b/src/com/android/dialer/dialpad/GreekSmartDialMap.java index 77929b8f9..beb25f38e 100644 --- a/src/com/android/dialer/dialpad/GreekSmartDialMap.java +++ b/src/com/android/dialer/dialpad/GreekSmartDialMap.java @@ -16,6 +16,8 @@ package com.android.dialer.dialpad; +import java.util.ArrayList; + public class GreekSmartDialMap implements SmartDialMap { private static final char[] LATIN_LETTERS_TO_DIGITS = { @@ -480,4 +482,14 @@ public class GreekSmartDialMap implements SmartDialMap { return ch; } + @Override + public String transliterateName(String index) { + return index; + } + + @Override + public boolean matchesCombination(SmartDialNameMatcher smartDialNameMatcher, + String displayName, String query, ArrayList<SmartDialMatchPosition> matchList) { + return smartDialNameMatcher.matchesCombination(displayName, query, matchList); + } } diff --git a/src/com/android/dialer/dialpad/HebrewSmartDialMap.java b/src/com/android/dialer/dialpad/HebrewSmartDialMap.java index 73d10ed92..43011f6d6 100644 --- a/src/com/android/dialer/dialpad/HebrewSmartDialMap.java +++ b/src/com/android/dialer/dialpad/HebrewSmartDialMap.java @@ -1,5 +1,7 @@ package com.android.dialer.dialpad; +import java.util.ArrayList; + public class HebrewSmartDialMap implements SmartDialMap { private static final char[] LATIN_LETTERS_TO_DIGITS = { @@ -415,4 +417,14 @@ public class HebrewSmartDialMap implements SmartDialMap { return ch; } + @Override + public String transliterateName(String index) { + return index; + } + + @Override + public boolean matchesCombination(SmartDialNameMatcher smartDialNameMatcher, + String displayName, String query, ArrayList<SmartDialMatchPosition> matchList) { + return smartDialNameMatcher.matchesCombination(displayName, query, matchList); + } }
\ No newline at end of file diff --git a/src/com/android/dialer/dialpad/KoreanSmartDialMap.java b/src/com/android/dialer/dialpad/KoreanSmartDialMap.java index 527c0d386..61c50a155 100644 --- a/src/com/android/dialer/dialpad/KoreanSmartDialMap.java +++ b/src/com/android/dialer/dialpad/KoreanSmartDialMap.java @@ -16,6 +16,15 @@ package com.android.dialer.dialpad; +import com.android.providers.contacts.NameSplitter; + +import android.app.AppGlobals; +import android.content.Context; +import android.provider.ContactsContract; + +import java.util.ArrayList; +import java.util.Locale; + /** * Ported the logic from 10.1: * https://github.com/CyanogenMod/android_packages_apps_Contacts/blob/cm-10.1/src/com/android/contacts/dialpad/util/NameToNumberKorean.java @@ -500,4 +509,91 @@ public class KoreanSmartDialMap implements SmartDialMap { } } + /** + * Splits the displayName into lastname/first names + */ + private NameSplitter.Name splitName(String displayName) { + Context context = AppGlobals.getInitialApplication(); + NameSplitter nameSplitter = new NameSplitter( + context.getString(com.android.internal.R.string.common_name_prefixes), + context.getString(com.android.internal.R.string.common_last_name_prefixes), + context.getString(com.android.internal.R.string.common_name_suffixes), + context.getString(com.android.internal.R.string.common_name_conjunctions), + Locale.KOREA); + NameSplitter.Name name = new NameSplitter.Name(); + nameSplitter.split(name, displayName, ContactsContract.FullNameStyle.KOREAN); + return name; + } + + /** + * Korean names don't contain spaces between lastname/first names. + * Split the name and add a space so better prefixes can be generated. + * @return lastname + " " + firstname + */ + private String separateFirstNameLastName(String displayName) { + for (int i=0; i<displayName.length(); i++) { + char ch = (char)displayName.codePointAt(i); + if (ch <= UNICODE_HANGUL_START || ch >= UNICODE_HANGUL_END) { + return displayName; + } + } + NameSplitter.Name name = splitName(displayName); + if (name.familyName != null && name.givenNames != null) { + return name.familyName + " " + name.givenNames; + } else { + return displayName; + } + } + + @Override + public String transliterateName(String index) { + return separateFirstNameLastName(index); + } + + @Override + public boolean matchesCombination(SmartDialNameMatcher smartDialNameMatcher, + String displayName, String query, ArrayList<SmartDialMatchPosition> matchList) { + matchList.clear(); + final int nameLength = displayName.length(); + final int queryLength = query.length(); + + if (nameLength < queryLength) { + return false; + } + + if (queryLength == 0) { + return false; + } + for (int i=0; i<nameLength; i++) { + char ch = (char)displayName.codePointAt(i); + if (ch <= UNICODE_HANGUL_START || ch >= UNICODE_HANGUL_END) { + return smartDialNameMatcher.matchesCombination(displayName, query, matchList); + } + } + /* + * For the matcher to work, we need to separate first/last names. + * Adjust the positions of the matches to account for the added space. + */ + NameSplitter.Name name = splitName(displayName); + if (name.familyName != null && name.givenNames != null) { + int separatorIndex = name.familyName.length(); + boolean matches = smartDialNameMatcher.matchesCombination(name.familyName + " " + name.givenNames, + query, matchList); + if (matches) { + for (SmartDialMatchPosition smartDialMatchPosition : matchList) { + if (smartDialMatchPosition.start > separatorIndex) { + smartDialMatchPosition.start--; + } + if (smartDialMatchPosition.end > separatorIndex) { + smartDialMatchPosition.end--; + } + } + return true; + } else { + return false; + } + } else { + return smartDialNameMatcher.matchesCombination(displayName, query, matchList); + } + } } diff --git a/src/com/android/dialer/dialpad/LatinSmartDialMap.java b/src/com/android/dialer/dialpad/LatinSmartDialMap.java index ef1ec0adc..84763e684 100644 --- a/src/com/android/dialer/dialpad/LatinSmartDialMap.java +++ b/src/com/android/dialer/dialpad/LatinSmartDialMap.java @@ -1,5 +1,7 @@ package com.android.dialer.dialpad; +import java.util.ArrayList; + public class LatinSmartDialMap implements SmartDialMap { private static final char[] LATIN_LETTERS_TO_DIGITS = { @@ -410,4 +412,14 @@ public class LatinSmartDialMap implements SmartDialMap { return ch; } + @Override + public String transliterateName(String index) { + return index; + } + + @Override + public boolean matchesCombination(SmartDialNameMatcher smartDialNameMatcher, + String displayName, String query, ArrayList<SmartDialMatchPosition> matchList) { + return smartDialNameMatcher.matchesCombination(displayName, query, matchList); + } } diff --git a/src/com/android/dialer/dialpad/RussianSmartDialMap.java b/src/com/android/dialer/dialpad/RussianSmartDialMap.java index 639049ebe..7a9ace23f 100644 --- a/src/com/android/dialer/dialpad/RussianSmartDialMap.java +++ b/src/com/android/dialer/dialpad/RussianSmartDialMap.java @@ -16,6 +16,8 @@ package com.android.dialer.dialpad; +import java.util.ArrayList; + public class RussianSmartDialMap implements SmartDialMap { private static final char[] LATIN_LETTERS_TO_DIGITS = { @@ -476,4 +478,14 @@ public class RussianSmartDialMap implements SmartDialMap { return ch; } + @Override + public String transliterateName(String index) { + return index; + } + + @Override + public boolean matchesCombination(SmartDialNameMatcher smartDialNameMatcher, + String displayName, String query, ArrayList<SmartDialMatchPosition> matchList) { + return smartDialNameMatcher.matchesCombination(displayName, query, matchList); + } } diff --git a/src/com/android/dialer/dialpad/SmartDialMap.java b/src/com/android/dialer/dialpad/SmartDialMap.java index b51891a8c..8773a1532 100644 --- a/src/com/android/dialer/dialpad/SmartDialMap.java +++ b/src/com/android/dialer/dialpad/SmartDialMap.java @@ -1,5 +1,7 @@ package com.android.dialer.dialpad; +import java.util.ArrayList; + /** * Note: These methods currently take characters as arguments. For future planned language support, * they will need to be changed to use codepoints instead of characters. @@ -40,4 +42,15 @@ public interface SmartDialMap { * from accented characters. */ public char normalizeCharacter(char ch); + + /* + * Allow the SmartDialMaps to convert the characters if needed. + */ + public String transliterateName(String index); + + /* + * Allow the SmartDialMaps to provide their own character to dialpad matching if needed. + */ + public boolean matchesCombination(SmartDialNameMatcher smartDialNameMatcher, String displayName, String query, + ArrayList<SmartDialMatchPosition> matchList); } diff --git a/src/com/android/dialer/dialpad/SmartDialNameMatcher.java b/src/com/android/dialer/dialpad/SmartDialNameMatcher.java index 77f417144..4daf0c837 100644 --- a/src/com/android/dialer/dialpad/SmartDialNameMatcher.java +++ b/src/com/android/dialer/dialpad/SmartDialNameMatcher.java @@ -415,7 +415,7 @@ public class SmartDialNameMatcher { if (mMultiMatchObject != null && mMultiMatchMethod != null) { return matchesMultiLanguage(displayName, mQuery, mMatchPositions); } else { - return matchesCombination(displayName, mQuery, mMatchPositions); + return mMap.matchesCombination(this, displayName, mQuery, mMatchPositions); } } diff --git a/src/com/android/dialer/dialpad/SmartDialPrefix.java b/src/com/android/dialer/dialpad/SmartDialPrefix.java index 6f993e4c1..1c2c46b1c 100644 --- a/src/com/android/dialer/dialpad/SmartDialPrefix.java +++ b/src/com/android/dialer/dialpad/SmartDialPrefix.java @@ -23,12 +23,20 @@ import android.preference.PreferenceManager; import android.telephony.TelephonyManager; import android.text.TextUtils; +import com.android.dialer.database.DialerDatabaseHelper; +import com.android.dialerbind.DatabaseHelperManager; +import com.android.phone.common.util.SettingsUtil; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Lists; import java.util.ArrayList; import java.util.HashSet; import java.util.Set; +import java.util.Locale; +import java.util.Map; +import java.util.HashMap; + +import android.util.Log; /** * Smart Dial utility class to find prefixes of contacts. It contains both methods to find supported @@ -69,6 +77,27 @@ public class SmartDialPrefix { private static boolean sNanpInitialized = false; + private static final Map<String, SmartDialMap> languageToSmartDialMap = new HashMap<String, SmartDialMap>(); + static { + languageToSmartDialMap.put("ko", new KoreanSmartDialMap()); + languageToSmartDialMap.put("el", new GreekSmartDialMap()); + languageToSmartDialMap.put("ru", new RussianSmartDialMap()); + // Per Locale.java documentation: + // Note that Java uses several deprecated two-letter codes. The Hebrew ("he") language + // code is rewritten as "iw" + languageToSmartDialMap.put("iw", new HebrewSmartDialMap()); + languageToSmartDialMap.put("zh", new ChineseSmartDialMap()); + } + + private static final Map<String, SmartDialMap> countryToSmartDialMap = new HashMap<String, SmartDialMap>(); + static { + languageToSmartDialMap.put("KR", new KoreanSmartDialMap()); + languageToSmartDialMap.put("GR", new GreekSmartDialMap()); + languageToSmartDialMap.put("RU", new RussianSmartDialMap()); + languageToSmartDialMap.put("IL", new HebrewSmartDialMap()); + languageToSmartDialMap.put("CN", new ChineseSmartDialMap()); + } + /** Initializes the Nanp settings, and finds out whether user is in a NANP region.*/ public static void initializeNanpSettings(Context context){ final TelephonyManager manager = (TelephonyManager) context.getSystemService( @@ -90,23 +119,23 @@ public class SmartDialPrefix { /** Queries the NANP country list to find out whether user is in a NANP region.*/ sUserInNanpRegion = isCountryNanp(sUserSimCountryCode); - /** Sets a layout for SmartDial depending on current UI language.*/ - String locale = context.getResources().getConfiguration().locale.getCountry(); - if (locale.equals("RU")) { - mMap = new RussianSmartDialMap(); - } else if (locale.equals("GR")) { - mMap = new GreekSmartDialMap(); - } else if (locale.equals("IL")) { - mMap = new HebrewSmartDialMap(); - } else if (locale.equals("KR")) { - mMap = new KoreanSmartDialMap(); - } else { + /** Sets a layout for SmartDial based on locale. Lookup by language first and fallback to country */ + Locale locale = SettingsUtil.getT9SearchInputLocale(context); + mMap = languageToSmartDialMap.get(locale.getLanguage()); + if (mMap == null) + mMap = countryToSmartDialMap.get(locale.getCountry()); + if (mMap == null) mMap = new LatinSmartDialMap(); - } sNanpInitialized = true; } + // for testing only + @VisibleForTesting + static void setSmartDialMap(SmartDialMap map) { + mMap = map; + } + /** * Explicitly setting the user Nanp to the given boolean */ @@ -180,7 +209,7 @@ public class SmartDialPrefix { */ public static ArrayList<String> generateNamePrefixes(String index) { final ArrayList<String> result = Lists.newArrayList(); - + index = mMap.transliterateName(index); /** Parses the name into a list of tokens.*/ final ArrayList<String> indexTokens = parseToIndexTokens(index); diff --git a/src/com/android/dialer/list/SmartDialNumberListAdapter.java b/src/com/android/dialer/list/SmartDialNumberListAdapter.java index ed1f82599..5563b0f91 100644 --- a/src/com/android/dialer/list/SmartDialNumberListAdapter.java +++ b/src/com/android/dialer/list/SmartDialNumberListAdapter.java @@ -41,11 +41,13 @@ public class SmartDialNumberListAdapter extends DialerPhoneNumberListAdapter { private static final String TAG = SmartDialNumberListAdapter.class.getSimpleName(); private static final boolean DEBUG = false; + private final Context mContext; + private SmartDialNameMatcher mNameMatcher; public SmartDialNumberListAdapter(Context context) { super(context); - mNameMatcher = new SmartDialNameMatcher("", SmartDialPrefix.getMap(), context); + mContext = context; setShortcutEnabled(SmartDialNumberListAdapter.SHORTCUT_DIRECT_CALL, false); if (DEBUG) { @@ -61,6 +63,8 @@ public class SmartDialNumberListAdapter extends DialerPhoneNumberListAdapter { Log.v(TAG, "Configure Loader with query" + getQueryString()); } + mNameMatcher = new SmartDialNameMatcher("", SmartDialPrefix.getMap(), mContext); + if (getQueryString() == null) { loader.configureQuery(""); mNameMatcher.setQuery(""); diff --git a/src/com/android/dialer/settings/DisplayOptionsSettingsFragment.java b/src/com/android/dialer/settings/DisplayOptionsSettingsFragment.java index 4b2c8f6db..ad11ac0de 100644 --- a/src/com/android/dialer/settings/DisplayOptionsSettingsFragment.java +++ b/src/com/android/dialer/settings/DisplayOptionsSettingsFragment.java @@ -16,16 +16,94 @@ package com.android.dialer.settings; +import android.content.Context; import android.os.Bundle; +import android.preference.ListPreference; +import android.preference.Preference; import android.preference.PreferenceFragment; +import android.text.TextUtils; import com.android.dialer.R; -public class DisplayOptionsSettingsFragment extends PreferenceFragment { +import cyanogenmod.providers.CMSettings; + +import java.util.Locale; + +public class DisplayOptionsSettingsFragment extends PreferenceFragment + implements Preference.OnPreferenceChangeListener { + + private static final String BUTTON_T9_SEARCH_INPUT_LOCALE = "button_t9_search_input"; + + private ListPreference mT9SearchInputLocale; + private Context mContext; + + // t9 search input locales that we have a custom overlay for + private static final Locale[] T9_SEARCH_INPUT_LOCALES = new Locale[] { + new Locale("ko"), new Locale("el"), new Locale("ru"), + new Locale("he"), new Locale("zh") + }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + mContext = getActivity().getApplicationContext(); + addPreferencesFromResource(R.xml.display_options_settings); + + mT9SearchInputLocale = (ListPreference) findPreference(BUTTON_T9_SEARCH_INPUT_LOCALE); + if (mT9SearchInputLocale != null) { + initT9SearchInputPreferenceList(); + mT9SearchInputLocale.setOnPreferenceChangeListener(this); + } + } + + /** + * Supports onPreferenceChangeListener to look for preference changes. + * + * @param preference The preference to be changed + * @param objValue The value of the selection, NOT its localized display value. + */ + @Override + public boolean onPreferenceChange(Preference preference, Object objValue) { + if (preference == mT9SearchInputLocale) { + saveT9SearchInputLocale(preference, (String) objValue); + } + return true; + } + + private void saveT9SearchInputLocale(Preference preference, String newT9Locale) { + String lastT9Locale = CMSettings.System.getString(mContext.getContentResolver(), + CMSettings.System.T9_SEARCH_INPUT_LOCALE); + + if (!TextUtils.equals(lastT9Locale, newT9Locale)) { + CMSettings.System.putString(mContext.getContentResolver(), + CMSettings.System.T9_SEARCH_INPUT_LOCALE, newT9Locale); + } + } + + private void initT9SearchInputPreferenceList() { + int len = T9_SEARCH_INPUT_LOCALES.length + 1; + String[] entries = new String[len]; + String[] values = new String[len]; + + entries[0] = getString(R.string.t9_search_input_locale_default); + values[0] = ""; + + // add locales programatically so we can use locale.getDisplayName + for (int i = 0; i < T9_SEARCH_INPUT_LOCALES.length; i++) { + Locale locale = T9_SEARCH_INPUT_LOCALES[i]; + entries[i + 1] = locale.getDisplayName(); + values[i + 1] = locale.toString(); + } + + // Set current entry from global system setting + String settingsT9Locale = CMSettings.System.getString(mContext.getContentResolver(), + CMSettings.System.T9_SEARCH_INPUT_LOCALE); + if (settingsT9Locale != null) { + mT9SearchInputLocale.setValue(settingsT9Locale); + } + + mT9SearchInputLocale.setEntries(entries); + mT9SearchInputLocale.setEntryValues(values); } } diff --git a/tests/src/com/android/dialer/dialpad/SmartDialNameMatcherTest.java b/tests/src/com/android/dialer/dialpad/SmartDialNameMatcherTest.java index b0dc91d2c..0c910670c 100644 --- a/tests/src/com/android/dialer/dialpad/SmartDialNameMatcherTest.java +++ b/tests/src/com/android/dialer/dialpad/SmartDialNameMatcherTest.java @@ -33,6 +33,12 @@ import junit.framework.TestCase; public class SmartDialNameMatcherTest extends AndroidTestCase { private static final String TAG = "SmartDialNameMatcherTest"; + @Override + public void setUp() { + SmartDialPrefix.setSmartDialMap(new LatinSmartDialMap()); + SmartDialPrefix.setUserInNanpRegion(true); + } + public void testMatches() { // Test to ensure that all alphabetic characters are covered checkMatches("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", @@ -162,6 +168,30 @@ public class SmartDialNameMatcherTest extends AndroidTestCase { fail("Cyrillic letters aren't supported yet."); } + public void testMatches_chinese() { + SmartDialPrefix.setSmartDialMap(new ChineseSmartDialMap()); + + // basic cases + checkMatches("红霞李", "46", true, 0, 1); + checkMatches("红霞李", "46649", true, 0, 1, 1, 2); + checkMatches("红霞李", "5", true, 2, 3); + checkMatches("红霞李", "466494254", true, 0, 1, 1, 2, 2, 3); + + // test with spaces + checkMatches("红霞 李", "466494254", true, 0, 1, 1, 2, 2, 3); + checkMatches(" 红 霞 李", "466494254", true, 0, 1, 1, 2, 2, 3); + + // when character sets are mixed, match english only + checkMatches("Hongxia 李", "46", true, 0, 2); + checkMatches("Hongxia 李", "5", false); + + // make sure regular english cases still work + checkMatches("Meow Face", "63", true, 0, 2); + checkMatches("Meow Face", "32", true, 5, 7); + checkMatches("Meow Face", "632", true, 0, 1, 5, 7); + + SmartDialPrefix.setSmartDialMap(new LatinSmartDialMap()); + } public void testMatches_NumberBasic() { // Simple basic examples that start the match from the start of the number @@ -199,6 +229,7 @@ public class SmartDialNameMatcherTest extends AndroidTestCase { public void testMatches_NumberNANP() { SmartDialPrefix.setUserInNanpRegion(true); + // An 11 digit number prefixed with 1 should be matched by the 10 digit number, as well as // the 7 digit number (without area code) checkMatchesNumber("1-510-333-7596", "5103337596", true, true, 2, 14); @@ -253,8 +284,8 @@ public class SmartDialNameMatcherTest extends AndroidTestCase { final SmartDialNameMatcher matcher = new SmartDialNameMatcher(query, getContext()); final ArrayList<SmartDialMatchPosition> matchPositions = new ArrayList<SmartDialMatchPosition>(); - final boolean matches = matcher.matchesCombination( - displayName, query, matchPositions); + final boolean matches = + SmartDialPrefix.getMap().matchesCombination(matcher, displayName, query, matchPositions); Log.d(TAG, "query=" + query + " text=" + displayName + " nfd=" + Normalizer.normalize(displayName, Normalizer.Form.NFD) + " nfc=" + Normalizer.normalize(displayName, Normalizer.Form.NFC) |
