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