15a60e47497f21f64e6d79420dc4c56c1907df22akschulz/* 25a60e47497f21f64e6d79420dc4c56c1907df22akschulz* Copyright (C) 2015 Samsung System LSI 35a60e47497f21f64e6d79420dc4c56c1907df22akschulz* Licensed under the Apache License, Version 2.0 (the "License"); 45a60e47497f21f64e6d79420dc4c56c1907df22akschulz* you may not use this file except in compliance with the License. 55a60e47497f21f64e6d79420dc4c56c1907df22akschulz* You may obtain a copy of the License at 65a60e47497f21f64e6d79420dc4c56c1907df22akschulz* 75a60e47497f21f64e6d79420dc4c56c1907df22akschulz* http://www.apache.org/licenses/LICENSE-2.0 85a60e47497f21f64e6d79420dc4c56c1907df22akschulz* 95a60e47497f21f64e6d79420dc4c56c1907df22akschulz* Unless required by applicable law or agreed to in writing, software 105a60e47497f21f64e6d79420dc4c56c1907df22akschulz* distributed under the License is distributed on an "AS IS" BASIS, 115a60e47497f21f64e6d79420dc4c56c1907df22akschulz* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 125a60e47497f21f64e6d79420dc4c56c1907df22akschulz* See the License for the specific language governing permissions and 135a60e47497f21f64e6d79420dc4c56c1907df22akschulz* limitations under the License. 145a60e47497f21f64e6d79420dc4c56c1907df22akschulz*/ 155a60e47497f21f64e6d79420dc4c56c1907df22akschulz 165a60e47497f21f64e6d79420dc4c56c1907df22akschulzpackage com.android.bluetooth.map; 175a60e47497f21f64e6d79420dc4c56c1907df22akschulz 185a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport java.util.Arrays; 195a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport java.util.HashMap; 205a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport java.util.regex.Pattern; 215a60e47497f21f64e6d79420dc4c56c1907df22akschulz 225a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport android.annotation.TargetApi; 235a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport android.content.ContentResolver; 245a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport android.database.Cursor; 255a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport android.net.Uri; 265a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport android.provider.ContactsContract; 275a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport android.provider.ContactsContract.Contacts; 285a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport android.provider.ContactsContract.PhoneLookup; 295a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport android.provider.Telephony.CanonicalAddressesColumns; 305a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport android.provider.Telephony.MmsSms; 315a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport android.util.Log; 325a60e47497f21f64e6d79420dc4c56c1907df22akschulz 335a60e47497f21f64e6d79420dc4c56c1907df22akschulz/** 345a60e47497f21f64e6d79420dc4c56c1907df22akschulz * Use these functions when extracting data for listings. It caches frequently used data to 355a60e47497f21f64e6d79420dc4c56c1907df22akschulz * speed up building large listings - e.g. before applying filtering. 365a60e47497f21f64e6d79420dc4c56c1907df22akschulz */ 375a60e47497f21f64e6d79420dc4c56c1907df22akschulz@TargetApi(19) 385a60e47497f21f64e6d79420dc4c56c1907df22akschulzpublic class SmsMmsContacts { 395a60e47497f21f64e6d79420dc4c56c1907df22akschulz 405a60e47497f21f64e6d79420dc4c56c1907df22akschulz private static final String TAG = "SmsMmsContacts"; 415a60e47497f21f64e6d79420dc4c56c1907df22akschulz 425a60e47497f21f64e6d79420dc4c56c1907df22akschulz private HashMap<Long,String> mPhoneNumbers = null; 435a60e47497f21f64e6d79420dc4c56c1907df22akschulz private final HashMap<String,MapContact> mNames = new HashMap<String, MapContact>(10); 445a60e47497f21f64e6d79420dc4c56c1907df22akschulz 455a60e47497f21f64e6d79420dc4c56c1907df22akschulz private static final Uri ADDRESS_URI = 465a60e47497f21f64e6d79420dc4c56c1907df22akschulz MmsSms.CONTENT_URI.buildUpon().appendPath("canonical-addresses").build(); 475a60e47497f21f64e6d79420dc4c56c1907df22akschulz 485a60e47497f21f64e6d79420dc4c56c1907df22akschulz private static final String[] ADDRESS_PROJECTION = { CanonicalAddressesColumns._ID, 495a60e47497f21f64e6d79420dc4c56c1907df22akschulz CanonicalAddressesColumns.ADDRESS }; 505a60e47497f21f64e6d79420dc4c56c1907df22akschulz private static final int COL_ADDR_ID = 515a60e47497f21f64e6d79420dc4c56c1907df22akschulz Arrays.asList(ADDRESS_PROJECTION).indexOf(CanonicalAddressesColumns._ID); 525a60e47497f21f64e6d79420dc4c56c1907df22akschulz private static final int COL_ADDR_ADDR = 535a60e47497f21f64e6d79420dc4c56c1907df22akschulz Arrays.asList(ADDRESS_PROJECTION).indexOf(CanonicalAddressesColumns.ADDRESS); 545a60e47497f21f64e6d79420dc4c56c1907df22akschulz 555a60e47497f21f64e6d79420dc4c56c1907df22akschulz private static final String[] CONTACT_PROJECTION = {Contacts._ID, Contacts.DISPLAY_NAME}; 565a60e47497f21f64e6d79420dc4c56c1907df22akschulz private static final String CONTACT_SEL_VISIBLE = Contacts.IN_VISIBLE_GROUP + "=1"; 575a60e47497f21f64e6d79420dc4c56c1907df22akschulz private static final int COL_CONTACT_ID = 585a60e47497f21f64e6d79420dc4c56c1907df22akschulz Arrays.asList(CONTACT_PROJECTION).indexOf(Contacts._ID); 595a60e47497f21f64e6d79420dc4c56c1907df22akschulz private static final int COL_CONTACT_NAME = 605a60e47497f21f64e6d79420dc4c56c1907df22akschulz Arrays.asList(CONTACT_PROJECTION).indexOf(Contacts.DISPLAY_NAME); 615a60e47497f21f64e6d79420dc4c56c1907df22akschulz 625a60e47497f21f64e6d79420dc4c56c1907df22akschulz /** 635a60e47497f21f64e6d79420dc4c56c1907df22akschulz * Get a contacts phone number based on the canonical addresses id of the contact. 645a60e47497f21f64e6d79420dc4c56c1907df22akschulz * (The ID listed in the Threads table.) 655a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param resolver the ContantResolver to be used. 665a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param id the id of the contact, as listed in the Threads table 675a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @return the phone number of the contact - or null if id does not exist. 685a60e47497f21f64e6d79420dc4c56c1907df22akschulz */ 695a60e47497f21f64e6d79420dc4c56c1907df22akschulz public String getPhoneNumber(ContentResolver resolver, long id) { 705a60e47497f21f64e6d79420dc4c56c1907df22akschulz String number; 715a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(mPhoneNumbers != null && (number = mPhoneNumbers.get(id)) != null) { 725a60e47497f21f64e6d79420dc4c56c1907df22akschulz return number; 735a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 745a60e47497f21f64e6d79420dc4c56c1907df22akschulz fillPhoneCache(resolver); 755a60e47497f21f64e6d79420dc4c56c1907df22akschulz return mPhoneNumbers.get(id); 765a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 775a60e47497f21f64e6d79420dc4c56c1907df22akschulz 785a60e47497f21f64e6d79420dc4c56c1907df22akschulz public static String getPhoneNumberUncached(ContentResolver resolver, long id) { 795a60e47497f21f64e6d79420dc4c56c1907df22akschulz String where = CanonicalAddressesColumns._ID + " = " + id; 805a60e47497f21f64e6d79420dc4c56c1907df22akschulz Cursor c = resolver.query(ADDRESS_URI, ADDRESS_PROJECTION, where, null, null); 815a60e47497f21f64e6d79420dc4c56c1907df22akschulz try { 825a60e47497f21f64e6d79420dc4c56c1907df22akschulz if (c != null) { 835a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(c.moveToPosition(0)) { 845a60e47497f21f64e6d79420dc4c56c1907df22akschulz return c.getString(COL_ADDR_ADDR); 855a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 865a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 875a60e47497f21f64e6d79420dc4c56c1907df22akschulz Log.e(TAG, "query failed"); 885a60e47497f21f64e6d79420dc4c56c1907df22akschulz } finally { 895a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(c != null) c.close(); 905a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 915a60e47497f21f64e6d79420dc4c56c1907df22akschulz return null; 925a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 935a60e47497f21f64e6d79420dc4c56c1907df22akschulz 945a60e47497f21f64e6d79420dc4c56c1907df22akschulz /** 955a60e47497f21f64e6d79420dc4c56c1907df22akschulz * Clears the local cache. Call after a listing is complete, to avoid using invalid data. 965a60e47497f21f64e6d79420dc4c56c1907df22akschulz */ 975a60e47497f21f64e6d79420dc4c56c1907df22akschulz public void clearCache() { 985a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(mPhoneNumbers != null) mPhoneNumbers.clear(); 995a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(mNames != null) mNames.clear(); 1005a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1015a60e47497f21f64e6d79420dc4c56c1907df22akschulz 1025a60e47497f21f64e6d79420dc4c56c1907df22akschulz /** 1035a60e47497f21f64e6d79420dc4c56c1907df22akschulz * Refreshes the cache, by clearing all cached values and fill the cache with the result of 1045a60e47497f21f64e6d79420dc4c56c1907df22akschulz * a new query. 1055a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param resolver the ContantResolver to be used. 1065a60e47497f21f64e6d79420dc4c56c1907df22akschulz */ 1075a60e47497f21f64e6d79420dc4c56c1907df22akschulz private void fillPhoneCache(ContentResolver resolver){ 1085a60e47497f21f64e6d79420dc4c56c1907df22akschulz Cursor c = resolver.query(ADDRESS_URI, ADDRESS_PROJECTION, null, null, null); 1095a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(mPhoneNumbers == null) { 1105a60e47497f21f64e6d79420dc4c56c1907df22akschulz int size = 0; 1115a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(c != null) 1125a60e47497f21f64e6d79420dc4c56c1907df22akschulz { 1135a60e47497f21f64e6d79420dc4c56c1907df22akschulz size = c.getCount(); 1145a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1155a60e47497f21f64e6d79420dc4c56c1907df22akschulz mPhoneNumbers = new HashMap<Long, String>(size); 1165a60e47497f21f64e6d79420dc4c56c1907df22akschulz } else { 1175a60e47497f21f64e6d79420dc4c56c1907df22akschulz mPhoneNumbers.clear(); 1185a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1195a60e47497f21f64e6d79420dc4c56c1907df22akschulz try { 1205a60e47497f21f64e6d79420dc4c56c1907df22akschulz if (c != null) { 1215a60e47497f21f64e6d79420dc4c56c1907df22akschulz long id; 1225a60e47497f21f64e6d79420dc4c56c1907df22akschulz String addr; 1235a60e47497f21f64e6d79420dc4c56c1907df22akschulz c.moveToPosition(-1); 1245a60e47497f21f64e6d79420dc4c56c1907df22akschulz while (c.moveToNext()) { 1255a60e47497f21f64e6d79420dc4c56c1907df22akschulz id = c.getLong(COL_ADDR_ID); 1265a60e47497f21f64e6d79420dc4c56c1907df22akschulz addr = c.getString(COL_ADDR_ADDR); 1275a60e47497f21f64e6d79420dc4c56c1907df22akschulz mPhoneNumbers.put(id, addr); 1285a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1295a60e47497f21f64e6d79420dc4c56c1907df22akschulz } else { 1305a60e47497f21f64e6d79420dc4c56c1907df22akschulz Log.e(TAG, "query failed"); 1315a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1325a60e47497f21f64e6d79420dc4c56c1907df22akschulz } finally { 1335a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(c != null) c.close(); 1345a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1355a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1365a60e47497f21f64e6d79420dc4c56c1907df22akschulz 1375a60e47497f21f64e6d79420dc4c56c1907df22akschulz public MapContact getContactNameFromPhone(String phone, ContentResolver resolver) { 1385a60e47497f21f64e6d79420dc4c56c1907df22akschulz return getContactNameFromPhone(phone, resolver, null); 1395a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1405a60e47497f21f64e6d79420dc4c56c1907df22akschulz /** 1415a60e47497f21f64e6d79420dc4c56c1907df22akschulz * Lookup a contacts name in the Android Contacts database. 1425a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param phone the phone number of the contact 1435a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param resolver the ContentResolver to use. 1445a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @return the name of the contact or null, if no contact was found. 1455a60e47497f21f64e6d79420dc4c56c1907df22akschulz */ 1465a60e47497f21f64e6d79420dc4c56c1907df22akschulz public MapContact getContactNameFromPhone(String phone, ContentResolver resolver, 1475a60e47497f21f64e6d79420dc4c56c1907df22akschulz String contactNameFilter) { 1485a60e47497f21f64e6d79420dc4c56c1907df22akschulz MapContact contact = mNames.get(phone); 1495a60e47497f21f64e6d79420dc4c56c1907df22akschulz 1505a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(contact != null){ 1515a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(contact.getId() < 0) { 1525a60e47497f21f64e6d79420dc4c56c1907df22akschulz return null; 1535a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1545a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(contactNameFilter == null) { 1555a60e47497f21f64e6d79420dc4c56c1907df22akschulz return contact; 1565a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1575a60e47497f21f64e6d79420dc4c56c1907df22akschulz // Validate filter 1585a60e47497f21f64e6d79420dc4c56c1907df22akschulz String searchString = contactNameFilter.replace("*", ".*"); 1595a60e47497f21f64e6d79420dc4c56c1907df22akschulz searchString = ".*" + searchString + ".*"; 1605a60e47497f21f64e6d79420dc4c56c1907df22akschulz Pattern p = Pattern.compile(Pattern.quote(searchString), Pattern.CASE_INSENSITIVE); 1615a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(p.matcher(contact.getName()).find()) { 1625a60e47497f21f64e6d79420dc4c56c1907df22akschulz return contact; 1635a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1645a60e47497f21f64e6d79420dc4c56c1907df22akschulz return null; 1655a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1665a60e47497f21f64e6d79420dc4c56c1907df22akschulz 1675a60e47497f21f64e6d79420dc4c56c1907df22akschulz // TODO: Should we change to extract both formatted name, and display name? 1685a60e47497f21f64e6d79420dc4c56c1907df22akschulz 1695a60e47497f21f64e6d79420dc4c56c1907df22akschulz Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,Uri.encode(phone)); 1705a60e47497f21f64e6d79420dc4c56c1907df22akschulz String selection = CONTACT_SEL_VISIBLE; 1715a60e47497f21f64e6d79420dc4c56c1907df22akschulz String[] selectionArgs = null; 1725a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(contactNameFilter != null) { 1735a60e47497f21f64e6d79420dc4c56c1907df22akschulz selection += "AND " + ContactsContract.Contacts.DISPLAY_NAME + " like ?"; 1745a60e47497f21f64e6d79420dc4c56c1907df22akschulz selectionArgs = new String[]{"%" + contactNameFilter.replace("*", "%") + "%"}; 1755a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1765a60e47497f21f64e6d79420dc4c56c1907df22akschulz 1775a60e47497f21f64e6d79420dc4c56c1907df22akschulz Cursor c = resolver.query(uri, CONTACT_PROJECTION, selection, selectionArgs, null); 1785a60e47497f21f64e6d79420dc4c56c1907df22akschulz try { 1795a60e47497f21f64e6d79420dc4c56c1907df22akschulz if (c != null && c.getCount() >= 1) { 1805a60e47497f21f64e6d79420dc4c56c1907df22akschulz c.moveToFirst(); 1815a60e47497f21f64e6d79420dc4c56c1907df22akschulz long id = c.getLong(COL_CONTACT_ID); 1825a60e47497f21f64e6d79420dc4c56c1907df22akschulz String name = c.getString(COL_CONTACT_NAME); 1835a60e47497f21f64e6d79420dc4c56c1907df22akschulz contact = MapContact.create(id, name); 1845a60e47497f21f64e6d79420dc4c56c1907df22akschulz mNames.put(phone, contact); 1855a60e47497f21f64e6d79420dc4c56c1907df22akschulz } else { 1865a60e47497f21f64e6d79420dc4c56c1907df22akschulz contact = MapContact.create(-1, null); 1875a60e47497f21f64e6d79420dc4c56c1907df22akschulz mNames.put(phone, contact); 1885a60e47497f21f64e6d79420dc4c56c1907df22akschulz contact = null; 1895a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1905a60e47497f21f64e6d79420dc4c56c1907df22akschulz } finally { 1915a60e47497f21f64e6d79420dc4c56c1907df22akschulz if (c != null) c.close(); 1925a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1935a60e47497f21f64e6d79420dc4c56c1907df22akschulz return contact; 1945a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1955a60e47497f21f64e6d79420dc4c56c1907df22akschulz} 196