Android: Get unique contacts called from the call log - android

Lets say that i have 100 calls in my call log. I want to find the unique contacts (not numbers) that they have been called.
The problem is that if a contact has two phone numbers (e.g. for Contact A i have a number for home and another for mobile) i will count that contact twice!
I tried the following.
I am reading the call log. Then for each number of the call log i call the following custom function:
private String getContactID (String number)
{
String contactID = "";
ContentResolver context = getContentResolver();
/// number is the phone number
Uri lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,Uri.encode(number));
String[] mPhoneNumberProjection = { PhoneLookup._ID };
Cursor cur = context.query(lookupUri,mPhoneNumberProjection, null, null, null);
try
{
if (cur.moveToFirst())
{
contactID = cur.getString(0);
return contactID;
}
}
finally
{
if (cur != null)
cur.close();
}
return contactID;
}
So then i have a calllog with contact ids and timestamp of the call and using a Set i get the unique...
The above code works fine BUT the performance if very poor! I tried it in a new Google Nexus 4 and it takes about 1600 msec! I don't want to think about older smart phones...
Any suggestions?

Use a background thread to lazy load the information in a ListView.
Initially fetch only about 10 results and display them in the list. That should happen fast. After that, in the background thread, keep on fetching information, 10 at a time, and keep on adding them to your list.

Related

How do i check if a phone number is already saved on my android phone or not?

I am building an app that enables users to save and manage a list of people, with important dates such as birthdays, anniversaries, Etc. for each person.
The goal, is to send them a personalized PDF image (with their name and details), on the important day.
The problem I am having, is what to do if the user saves a person on my app, that isn't in their contact list.
In such a case, I can't send him a whatsapp message.
So, I want to add the phone number to the phone's contacts list, only if the phone number isn't already saved in the user's regular contacts list.
How do I check if phone number XXX-XXX-XXXX exists in user's contact list or not ?
String res = null;
try {
ContentResolver resolver = ctx.getContentResolver();
Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneNumber));
Cursor c = resolver.query(uri, new String[]{PhoneLookup.DISPLAY_NAME}, null, null, null);
if (c != null) { // cursor not null means number is found contactsTable
if (c.moveToFirst()) { // so now find the contact Name
res = c.getString(c.getColumnIndex(CommonDataKinds.Phone.DISPLAY_NAME));
}
c.close();
}
} catch (Exception ex) {
/* Ignore */
}
return res;

ContentResolver returns outdated data

The following code should return the last phone number in the call log.
It mostly works, but sometimes it returns not the last phone number, but the second last. If I check the list of calls, I can clearly see that the number returned is not the last in the history. Running the program again will return the last ("correct") number.
Can anyone tell what's wrong?
private String getPhoneNumber() {
String[] projection = {CallLog.Calls.NUMBER};
Cursor callLog = getContentResolver().query(
CallLog.Calls.CONTENT_URI,
projection,
null,
null,
CallLog.Calls.DEFAULT_SORT_ORDER
);
// Set number
callLog.moveToFirst();
return callLog.getString(callLog.getColumnIndex(CallLog.Calls.NUMBER));
}

How to detect a duplicate contact when Inserting a new one?

For a Contacts backup app, I save all the information to a CSV file, and then I need to restore it back. It works great, however if I press restore twice, it duplicates all the contacts.
I tried the following code to remove duplicates, it does work but fails in certain cases.
Basically it fails when there is no explicit DISPLAY_NAME, for e.g. if a contact seems to only have a phone number and the DISPLAY_NAME is the phone number, or same for an email address. I cannot understand why it wont always work since it does seem that the DISPLAY_NAME field contains phonenumber/email address.
Here is the code that I used:
private boolean contactExists(String displayname, String emailstring, String phonestring){
Cursor crsr = BA.applicationContext.getContentResolver().query(
ContactsContract.Contacts.CONTENT_URI,
new String[] { "display_name", "_id"},
"display_name = ? ",
new String[] {displayname},
null);
while (crsr.moveToNext()){
HashMap m = new HashMap();
for (int col = 0; col < crsr.getColumnCount(); col++) {
m.put(crsr.getColumnName(col), Integer.valueOf(col));
}
int id = crsr.getInt(((Integer)m.get("_id")).intValue());
String emails = GetEmails(id);
String phones = GetPhones(id);
if (emails.contentEquals(emailstring) && phones.contentEquals(phonestring))
{
crsr.close();
return true;
}
}
crsr.close();
return false;
}
UPDATE:
I tried with DISPLAY_NAME_PRIMARY with the same results.
However what I noticed is that, if I create the contacts on the same device/emulator, the duplicate is detected, when I re-restore the same contacts.
On going across devices, it seems that one reason it does not work is that at some point the special characthers are removed.
For e.g. the display name "John.Doe" is read from the CSV, but when it gets inserted, it becomes "John Doe". I cannot see where in the code the "." is ever stripped out.
What happens depends on the version of Android the device is running. If the version is Honeycomb (3.0) or later, the contact will always have a name. The name field is DISPLAY_NAME_PRIMARY, and if there's no name in any of the raw contacts, this field is set to a phone number or email address.
It's hard to know exactly what's going on with your code, because I can't tell how you're calling contactExists in all cases. But my guess is that you're looking at DISPLAY_NAME, when you may want to look at DISPLAY_NAME_PRIMARY.
As a side comment, what you're trying to do here is fraught with peril. The contacts provider is a complex system, and backing it up to a CSV may cause a lot of problems down the road. A much better strategy is to run a sync between the contacts provider and the cloud-based Google Contacts app.
Here is the code which finds duplicate contact. You need to pass the "NAME" as string and it will look for duplicate. It works in ICS but didn't check in GB, so basically you need to try your luck.
/**
* #param name
* #param context
* #return
*/
public boolean isContactExist(String name) {
boolean result = false;
try {
ContentResolver contentResolver = getContentResolver();
Uri uri = Data.CONTENT_URI;
String[] projection = new String[] { PhoneLookup._ID,
PhoneLookup.LOOKUP_KEY };
String selection = StructuredName.DISPLAY_NAME + " = ?";
String[] selectionArguments = { name };
Cursor cursor = contentResolver.query(uri, projection, selection,
selectionArguments, null);
if (cursor != null) {
while (cursor.moveToNext()) {
/*
* Log.i(TAG, "KEY = " + cursor.getString(cursor
* .getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY)));
*/
result = true;
}
}
cursor.close();
} catch (Exception e) {
result = false;
e.printStackTrace();
}
return result;
}

Android Froyo: my life with call_log

I'm stuck with the call_log functionality in Froyo. As many of you know, Froyo logs in call log not only calls but also each outgoing and incomming SMS message. You can chose in options to show all that crap, or only specific types (outgoing calls, incoming calls, sent messages, received messages etc), but since this is radio button, you cannot specify for example only ongoing and incoming calls. Very known and annoing Froyo functionality.
So I started to write some simple tool to read the call log by myself. Here is the code snippet:
try {
mCur = getApplicationContext().getContentResolver()
.query(CallLog.Calls.CONTENT_URI, columns, null, null, null );
mCur.moveToFirst();
io = mCur.getColumnIndex(CallLog.Calls._ID);
bo = mCur.getColumnIndex(CallLog.Calls.NUMBER);
no = mCur.getColumnIndex(CallLog.Calls.CACHED_NAME);
to = mCur.getColumnIndex(CallLog.Calls.TYPE);
while (mCur.isAfterLast() == false) {
i = mCur.getString(io);
b = mCur.getString(bo);
n = mCur.getString(no);
t = mCur.getString(to);
Log.i(TAG, "CallLog: ID="+i+" number="+b+" name="+n+" type="+t);
mCur.moveToNext();
}
} catch (Exception e) {
Log.e(TAG, "updateCallLog", e);
} finally {
if (mCur != null) {
mCur.close();
mCur = null;
}
}
Surprise, surprise, the call_log provider skips the sms records from the call log. So with the code above I see only call records (incoming or outgoing), all other records are skipped. The little more digging into it revealed that the CallLog provider adds internally filtering to the call log database:
02-03 09:26:42.348 E/CLCService(28244): android.database.sqlite.SQLiteException:
near ")": syntax error: , while compiling:
SELECT _id, name, number, type FROM logs WHERE (logtype=100 OR logtype=500) AND (_ID=)
Do not look for the syntax error, it was created on purpose to force provider to dump the SQL query by calling query(CallLog.Calls.CONTENT_URI, columns, "_ID=", null, null )). The (_ID=) is what is provided in the query, the rest of (logtype=100 OR logtype=500) is apparently added by the call log provider itself.
So I have two questions:
Where I can find in the Android code how the provider is adding the logtype filter? I was looking into CallLog.java and CallLogProvider.java and cannot find it.
How can I read all records from the call log in Froyo? I cannot bypass the call log provider and use my own SQL helper for this until I will not root the phone, which is not an option. Is there any other way to do it?
I'm not certain just what is going wrong but reading the call log to get just incoming or outgoing calls is simple enough. The sample below adds restrictions to the query so that it only returns data for outgoing calls made after a certain date. The where string uses question marks to indicate where the values from the wargs array should be substituted in to form the sql query.
About where the extra WHERE clause occurs. Almost certainly in the calllog provider implementation. The providers commonly have a switch statement that uses the uri that you use to open the provider and then adds restrictions based on the uri. The calllog one seems to be in packages/providers/ContactsProvider.
public static int getMinutesUsedSince(Context context, Date date) {
Uri uri = CallLog.Calls.CONTENT_URI;
String columns[] = new String[] { CallLog.Calls.DURATION };
String where = CallLog.Calls.TYPE + "=? AND " + CallLog.Calls.DATE + ">?";
String wargs[] = new String[] {
String.valueOf(CallLog.Calls.OUTGOING_TYPE),
String.valueOf(date.getTime())
};
String sortOrder = "date DESC";
Cursor c = context.getContentResolver().query(uri, columns, where, wargs, sortOrder);
long sum = 0;
int durationIndex = c.getColumnIndex(CallLog.Calls.DURATION);
if (c.moveToFirst()) {
do {
/* for each individual call, round up to the nearest minute */
long duration = c.getLong(durationIndex);
long minutes = (long)Math.ceil(duration / 60.0);
sum += minutes;
} while (c.moveToNext());
}
c.close();
return (int)sum;
}

Android : Check phone number present in Contact List ? (Phone number retrieve from phone call)

I make a BroadcastReceiver to receive Phone number of the person who call me
<intent-filter>
<action
android:name="android.intent.action.PHONE_STATE" />
</intent-filter>
How to check if the phone number receive is on my contact list ?
Do you have a tip to know if this phone number exist on contact list with out loading contact list ?
I don't want more information, just if this phone number exist.
If it's not possible, and I must load contact list, how to do it on BroadcastReceiver ?
When I try to do getContentResolver, it's not working because I'm on BroadcastReceiver and not inside Activity...
Thanks for your help
public boolean contactExists(Context context, String number) {
// number is the phone number
Uri lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number));
String[] mPhoneNumberProjection = { PhoneLookup._ID, PhoneLookup.NUMBER, PhoneLookup.DISPLAY_NAME };
Cursor cur = context.getContentResolver().query(lookupUri, mPhoneNumberProjection, null, null, null);
try {
if (cur.moveToFirst()) {
cur.close();
return true;
}
} finally {
if (cur != null)
cur.close();
}
}
return false;
}
I think it's important to say that you need to add the following in your manifest file.
<uses-permission android:name="android.permission.READ_CONTACTS" />
for 1 you should have a look at the recommended ContactsContract.PhoneLookup provider
A table that represents the result of looking up a phone number, for example for caller ID. To perform a lookup you must append the number you want to find to CONTENT_FILTER_URI. This query is highly optimized.
Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneNumber));
Cursor mycursor=resolver.query(uri, new String[]{PhoneLookup.DISPLAY_NAME,...
if (mycursor!=null && mycursor.moveToFirst()) {
// record exists
}
for 2 you can use the context from the onReceive method to call methods that belong to Context
ContentResolver cr=context.getContentResolver();
I suggest you to use Phone.CONTENT_FILTER_URI instead of PhoneLookup.CONTENT_FILTER_URI
because PhoneLookup can be empty and you will get no result from time to time (tested on LG-P500, froyo)
The problem on my device happens for example when:
switch to airplane mode
use the default message application to send a sms (will be queued).
use PhoneLookup.CONTENT_FILTER_URI to query for a contact
Not all devices seems to be affected
Using PhoneLookup.CONTENT_FILTER_URI the returned cursor is always empty.
Using Phone.CONTENT_FILTER_URI everything is ok (you find the contact if any).
Therefore I suggest you to always use Phone.* Uris except when you really need to use PhoneLookup.*... Which usually is just address book synchronization related stuff (and most of the times is not what you are interested in).

Categories

Resources