Home | History | Annotate | Download | only in map
      1 /*
      2 * Copyright (C) 2015 Samsung System LSI
      3 * Licensed under the Apache License, Version 2.0 (the "License");
      4 * you may not use this file except in compliance with the License.
      5 * You may obtain a copy of the License at
      6 *
      7 *      http://www.apache.org/licenses/LICENSE-2.0
      8 *
      9 * Unless required by applicable law or agreed to in writing, software
     10 * distributed under the License is distributed on an "AS IS" BASIS,
     11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 * See the License for the specific language governing permissions and
     13 * limitations under the License.
     14 */
     15 
     16 package com.android.bluetooth.map;
     17 
     18 import java.util.Arrays;
     19 import java.util.HashMap;
     20 import java.util.regex.Pattern;
     21 
     22 import android.annotation.TargetApi;
     23 import android.content.ContentResolver;
     24 import android.database.Cursor;
     25 import android.net.Uri;
     26 import android.provider.ContactsContract;
     27 import android.provider.ContactsContract.Contacts;
     28 import android.provider.ContactsContract.PhoneLookup;
     29 import android.provider.Telephony.CanonicalAddressesColumns;
     30 import android.provider.Telephony.MmsSms;
     31 import android.util.Log;
     32 
     33 /**
     34  * Use these functions when extracting data for listings. It caches frequently used data to
     35  * speed up building large listings - e.g. before applying filtering.
     36  */
     37 @TargetApi(19)
     38 public class SmsMmsContacts {
     39 
     40     private static final String TAG = "SmsMmsContacts";
     41 
     42     private HashMap<Long,String> mPhoneNumbers = null;
     43     private final HashMap<String,MapContact> mNames = new HashMap<String, MapContact>(10);
     44 
     45     private static final Uri ADDRESS_URI =
     46             MmsSms.CONTENT_URI.buildUpon().appendPath("canonical-addresses").build();
     47 
     48     private static final String[] ADDRESS_PROJECTION = { CanonicalAddressesColumns._ID,
     49                     CanonicalAddressesColumns.ADDRESS };
     50     private static final int COL_ADDR_ID =
     51             Arrays.asList(ADDRESS_PROJECTION).indexOf(CanonicalAddressesColumns._ID);
     52     private static final int COL_ADDR_ADDR =
     53             Arrays.asList(ADDRESS_PROJECTION).indexOf(CanonicalAddressesColumns.ADDRESS);
     54 
     55     private static final String[] CONTACT_PROJECTION = {Contacts._ID, Contacts.DISPLAY_NAME};
     56     private static final String CONTACT_SEL_VISIBLE = Contacts.IN_VISIBLE_GROUP + "=1";
     57     private static final int COL_CONTACT_ID =
     58             Arrays.asList(CONTACT_PROJECTION).indexOf(Contacts._ID);
     59     private static final int COL_CONTACT_NAME =
     60             Arrays.asList(CONTACT_PROJECTION).indexOf(Contacts.DISPLAY_NAME);
     61 
     62     /**
     63      * Get a contacts phone number based on the canonical addresses id of the contact.
     64      * (The ID listed in the Threads table.)
     65      * @param resolver the ContantResolver to be used.
     66      * @param id the id of the contact, as listed in the Threads table
     67      * @return the phone number of the contact - or null if id does not exist.
     68      */
     69     public String getPhoneNumber(ContentResolver resolver, long id) {
     70         String number;
     71         if(mPhoneNumbers != null && (number = mPhoneNumbers.get(id)) != null) {
     72             return number;
     73         }
     74         fillPhoneCache(resolver);
     75         return mPhoneNumbers.get(id);
     76     }
     77 
     78     public static String getPhoneNumberUncached(ContentResolver resolver, long id) {
     79         String where = CanonicalAddressesColumns._ID + " = " + id;
     80         Cursor c = resolver.query(ADDRESS_URI, ADDRESS_PROJECTION, where, null, null);
     81         try {
     82             if (c != null) {
     83                 if(c.moveToPosition(0)) {
     84                     return c.getString(COL_ADDR_ADDR);
     85                 }
     86             }
     87             Log.e(TAG, "query failed");
     88         } finally {
     89             if(c != null) c.close();
     90         }
     91         return null;
     92     }
     93 
     94     /**
     95      * Clears the local cache. Call after a listing is complete, to avoid using invalid data.
     96      */
     97     public void clearCache() {
     98         if(mPhoneNumbers != null) mPhoneNumbers.clear();
     99         if(mNames != null) mNames.clear();
    100     }
    101 
    102     /**
    103      * Refreshes the cache, by clearing all cached values and fill the cache with the result of
    104      * a new query.
    105      * @param resolver the ContantResolver to be used.
    106      */
    107     private void fillPhoneCache(ContentResolver resolver){
    108         Cursor c = resolver.query(ADDRESS_URI, ADDRESS_PROJECTION, null, null, null);
    109         if(mPhoneNumbers == null) {
    110             int size = 0;
    111             if(c != null)
    112             {
    113                 size = c.getCount();
    114             }
    115             mPhoneNumbers = new HashMap<Long, String>(size);
    116         } else {
    117             mPhoneNumbers.clear();
    118         }
    119         try {
    120             if (c != null) {
    121                 long id;
    122                 String addr;
    123                 c.moveToPosition(-1);
    124                 while (c.moveToNext()) {
    125                     id = c.getLong(COL_ADDR_ID);
    126                     addr = c.getString(COL_ADDR_ADDR);
    127                     mPhoneNumbers.put(id, addr);
    128                 }
    129             } else {
    130                 Log.e(TAG, "query failed");
    131             }
    132         } finally {
    133             if(c != null) c.close();
    134         }
    135     }
    136 
    137     public MapContact getContactNameFromPhone(String phone, ContentResolver resolver) {
    138         return getContactNameFromPhone(phone, resolver, null);
    139     }
    140     /**
    141      * Lookup a contacts name in the Android Contacts database.
    142      * @param phone the phone number of the contact
    143      * @param resolver the ContentResolver to use.
    144      * @return the name of the contact or null, if no contact was found.
    145      */
    146     public MapContact getContactNameFromPhone(String phone, ContentResolver resolver,
    147             String contactNameFilter) {
    148         MapContact contact = mNames.get(phone);
    149 
    150         if(contact != null){
    151             if(contact.getId() < 0) {
    152                 return null;
    153             }
    154             if(contactNameFilter == null) {
    155                 return contact;
    156             }
    157             // Validate filter
    158             String searchString = contactNameFilter.replace("*", ".*");
    159             searchString = ".*" + searchString + ".*";
    160             Pattern p = Pattern.compile(Pattern.quote(searchString), Pattern.CASE_INSENSITIVE);
    161             if(p.matcher(contact.getName()).find()) {
    162                 return contact;
    163             }
    164             return null;
    165         }
    166 
    167         // TODO: Should we change to extract both formatted name, and display name?
    168 
    169         Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,Uri.encode(phone));
    170         String selection = CONTACT_SEL_VISIBLE;
    171         String[] selectionArgs = null;
    172         if(contactNameFilter != null) {
    173             selection += "AND " + ContactsContract.Contacts.DISPLAY_NAME + " like ?";
    174             selectionArgs = new String[]{"%" + contactNameFilter.replace("*", "%") + "%"};
    175         }
    176 
    177         Cursor c = resolver.query(uri, CONTACT_PROJECTION, selection, selectionArgs, null);
    178         try {
    179             if (c != null && c.getCount() >= 1) {
    180                 c.moveToFirst();
    181                 long id = c.getLong(COL_CONTACT_ID);
    182                 String name = c.getString(COL_CONTACT_NAME);
    183                 contact = MapContact.create(id, name);
    184                 mNames.put(phone, contact);
    185             } else {
    186                 contact = MapContact.create(-1, null);
    187                 mNames.put(phone, contact);
    188                 contact = null;
    189             }
    190         } finally {
    191             if (c != null) c.close();
    192         }
    193         return contact;
    194     }
    195 }
    196