blob: f43a27011c233cc1c9a167598c88ad851bab7957 [file] [log] [blame]
/*
* Copyright (C) 2015 Samsung System LSI
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.bluetooth.map;
import java.util.Arrays;
import java.util.HashMap;
import java.util.regex.Pattern;
import android.annotation.TargetApi;
import android.content.ContentResolver;
import android.database.Cursor;
import android.net.Uri;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.PhoneLookup;
import android.provider.Telephony.CanonicalAddressesColumns;
import android.provider.Telephony.MmsSms;
import android.util.Log;
/**
* Use these functions when extracting data for listings. It caches frequently used data to
* speed up building large listings - e.g. before applying filtering.
*/
@TargetApi(19)
public class SmsMmsContacts {
private static final String TAG = "SmsMmsContacts";
private HashMap<Long,String> mPhoneNumbers = null;
private final HashMap<String,MapContact> mNames = new HashMap<String, MapContact>(10);
private static final Uri ADDRESS_URI =
MmsSms.CONTENT_URI.buildUpon().appendPath("canonical-addresses").build();
private static final String[] ADDRESS_PROJECTION = { CanonicalAddressesColumns._ID,
CanonicalAddressesColumns.ADDRESS };
private static final int COL_ADDR_ID =
Arrays.asList(ADDRESS_PROJECTION).indexOf(CanonicalAddressesColumns._ID);
private static final int COL_ADDR_ADDR =
Arrays.asList(ADDRESS_PROJECTION).indexOf(CanonicalAddressesColumns.ADDRESS);
private static final String[] CONTACT_PROJECTION = {Contacts._ID, Contacts.DISPLAY_NAME};
private static final String CONTACT_SEL_VISIBLE = Contacts.IN_VISIBLE_GROUP + "=1";
private static final int COL_CONTACT_ID =
Arrays.asList(CONTACT_PROJECTION).indexOf(Contacts._ID);
private static final int COL_CONTACT_NAME =
Arrays.asList(CONTACT_PROJECTION).indexOf(Contacts.DISPLAY_NAME);
/**
* Get a contacts phone number based on the canonical addresses id of the contact.
* (The ID listed in the Threads table.)
* @param resolver the ContantResolver to be used.
* @param id the id of the contact, as listed in the Threads table
* @return the phone number of the contact - or null if id does not exist.
*/
public String getPhoneNumber(ContentResolver resolver, long id) {
String number;
if(mPhoneNumbers != null && (number = mPhoneNumbers.get(id)) != null) {
return number;
}
fillPhoneCache(resolver);
return mPhoneNumbers.get(id);
}
public static String getPhoneNumberUncached(ContentResolver resolver, long id) {
String where = CanonicalAddressesColumns._ID + " = " + id;
Cursor c = resolver.query(ADDRESS_URI, ADDRESS_PROJECTION, where, null, null);
try {
if (c != null) {
if(c.moveToPosition(0)) {
return c.getString(COL_ADDR_ADDR);
}
}
Log.e(TAG, "query failed");
} finally {
if(c != null) c.close();
}
return null;
}
/**
* Clears the local cache. Call after a listing is complete, to avoid using invalid data.
*/
public void clearCache() {
if(mPhoneNumbers != null) mPhoneNumbers.clear();
if(mNames != null) mNames.clear();
}
/**
* Refreshes the cache, by clearing all cached values and fill the cache with the result of
* a new query.
* @param resolver the ContantResolver to be used.
*/
private void fillPhoneCache(ContentResolver resolver){
Cursor c = resolver.query(ADDRESS_URI, ADDRESS_PROJECTION, null, null, null);
if(mPhoneNumbers == null) {
int size = 0;
if(c != null)
{
size = c.getCount();
}
mPhoneNumbers = new HashMap<Long, String>(size);
} else {
mPhoneNumbers.clear();
}
try {
if (c != null) {
long id;
String addr;
c.moveToPosition(-1);
while (c.moveToNext()) {
id = c.getLong(COL_ADDR_ID);
addr = c.getString(COL_ADDR_ADDR);
mPhoneNumbers.put(id, addr);
}
} else {
Log.e(TAG, "query failed");
}
} finally {
if(c != null) c.close();
}
}
public MapContact getContactNameFromPhone(String phone, ContentResolver resolver) {
return getContactNameFromPhone(phone, resolver, null);
}
/**
* Lookup a contacts name in the Android Contacts database.
* @param phone the phone number of the contact
* @param resolver the ContentResolver to use.
* @return the name of the contact or null, if no contact was found.
*/
public MapContact getContactNameFromPhone(String phone, ContentResolver resolver,
String contactNameFilter) {
MapContact contact = mNames.get(phone);
if(contact != null){
if(contact.getId() < 0) {
return null;
}
if(contactNameFilter == null) {
return contact;
}
// Validate filter
String searchString = contactNameFilter.replace("*", ".*");
searchString = ".*" + searchString + ".*";
Pattern p = Pattern.compile(Pattern.quote(searchString), Pattern.CASE_INSENSITIVE);
if(p.matcher(contact.getName()).find()) {
return contact;
}
return null;
}
// TODO: Should we change to extract both formatted name, and display name?
Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,Uri.encode(phone));
String selection = CONTACT_SEL_VISIBLE;
String[] selectionArgs = null;
if(contactNameFilter != null) {
selection += "AND " + ContactsContract.Contacts.DISPLAY_NAME + " like ?";
selectionArgs = new String[]{"%" + contactNameFilter.replace("*", "%") + "%"};
}
Cursor c = resolver.query(uri, CONTACT_PROJECTION, selection, selectionArgs, null);
try {
if (c != null && c.getCount() >= 1) {
c.moveToFirst();
long id = c.getLong(COL_CONTACT_ID);
String name = c.getString(COL_CONTACT_NAME);
contact = MapContact.create(id, name);
mNames.put(phone, contact);
} else {
contact = MapContact.create(-1, null);
mNames.put(phone, contact);
contact = null;
}
} finally {
if (c != null) c.close();
}
return contact;
}
}