Python - Get Contact from Phone Number - android

Using SL4A and Python, is there an easy way to get a Contact ID from a phone number?
The phone number is from the 'address' field of an SMS.
I am trying to avoid searching through all contacts.
I use m = droid.smsGetMessageById(id, None).result to get the SMS message. The result looks like:
{u'read': u'1', u'body': u"Hello! Your mobile bill's now ready to view at virginmobile.co.uk/youraccount. We'll collect your Direct Debit of 12.12 on or just after 19th Nov.", u'_id': u'1323', u'date': u'1415372649502', u'address': u'1234567890'}
The value in the address entry is the phone number that sent the SMS.
I want to get a contact ID using this number, but, if possible, I want to avoid searching all my contacts.

I figured it out:
def contactFromPhone(phone):
uri='content://com.android.contacts/data'
filter='data4 LIKE ?'
args=['%'+phone+'%']
columns=['contact_id']
contacts=droid.queryContent(uri, columns, filter, args).result
cs=[]
if len(contacts)>0:
for c in contacts:
cs.append(c['contact_id'])
return cs
'phone' is a normalized phone number
returns a list of contact_id

Related

How to obtain Skype ID from Contact DB on Android (my solution is almost OK)

EDIT
FYI, The trick which consists in extracting RawContactID from lookupKey (similar to "1450iSkype_288") does not work anymore.
Summary
I am able to obtain 'Skype ID' / 'Skype user name' , but my solution does not always work because it seems the contact DB of the Skype app does not always contain (expose?) 'all' the data.
Let me explain the situation before asking my question:
1 - Gathering info from Skype apk
Skype App (apk) contains /res/xml/contacts.xml which contains
<ContactsDataKind
android:icon="#ref/0x7f0e0004"
android:mimeType="vnd.android.cursor.item/com.skype4life.audio"
android:summaryColumn="data3"
android:detailColumn="data4" />
<ContactsDataKind
android:icon="#ref/0x7f0e0004"
android:mimeType="vnd.android.cursor.item/com.skype4life.video"
android:summaryColumn="data3"
android:detailColumn="data4" />
<ContactsDataKind
android:icon="#ref/0x7f0e0004"
android:mimeType="vnd.android.cursor.item/com.skype4life.message"
android:summaryColumn="data3"
android:detailColumn="data4" />
<ContactsDataKind
android:icon="#ref/0x7f0e0004"
android:mimeType="vnd.android.cursor.item/com.skype4life.phone"
android:summaryColumn="data3"
android:detailColumn="data4" />
The important data here is these 4 Skype mimeTypes:
"vnd.android.cursor.item/com.skype4life.phone"
"vnd.android.cursor.item/com.skype4life.message"
"vnd.android.cursor.item/com.skype4life.audio"
"vnd.android.cursor.item/com.skype4life.video"
Note:
It looks like these have been added around Augustus 2018 (see here http://www.skaip.org/skype-8-27-0-88-for-android )
One more usefull info is that the ContactsContract.RawContacts.ACCOUNT_TYPE of Skype is "com.skype.raider"
2 - Querying Content provider
Using info obtained in point 1, I could query the Skype ContentProvider (using selection ContactsContract.Data.MIMETYPE+ "= ? " and selectionArgs "com.skype.raider" ) and obtain data which must be 'interpreted' according to the mimeType (one of the 4 Skype mimeTypes obtained in point 1).
2.1 - For mimeType vnd.android.cursor.item/com.skype4life.message, I obtain something like this:
mimetype = vnd.android.cursor.item/com.skype4life.message
_id = 2773
lookup = 1450iSkype_288
contact_id = 1601
display_name = <Skype User DISPLAY Name HERE>
raw_contact_id = 515
mimetype = vnd.android.cursor.item/com.skype4life.message
data1 = 8:<skype.user.name.value.here>
data2 = null
data3 = Skype
is_primary = 0
data5 = null
Where
<Skype User DISPLAY Name HERE> is an actual Display Name
<skype.user.name.value.here> is an actual Skype user name / Skype ID
<skype.user.name.value.here> is the data I want, so this is good!
Important note:
EDIT: this trick does not work anymore ->
The lookup = 1450iSkype_288 can be parsed to obtain 288 which is the RawcontactId of some other contact!
If this rawContact has a contactId, then this allows me to find the original Contact (vs RawContact)
It does even sometimes refer some contact which is NOT a Google contact, but, for example, a WhatsApp contact!
Enjoy this important info ;-)
<- this trick does not work anymore
2.2 - Similar data for theses 2 mimeTypes:
"vnd.android.cursor.item/com.skype4life.audio"
"vnd.android.cursor.item/com.skype4life.video"
So I am also able to obtain <skype.user.name.value.here>. Again, this is good!
2.3 - the Phone mimeType
"vnd.android.cursor.item/com.skype4life.phone"
does not contain Skype ID (in field data1), but instead the phone number, which makes sense.
3 - The Problem
So, all this works fine an I decided to test my code on some other devices, where I installed Skype and used it with same Skype account + same Google account and, BOOM, here is the problem:
The Skype Content Provider only has data for mimeType
"vnd.android.cursor.item/com.skype4life.phone" // <- contains Skype user's phone number, NOT Skype user name / Skype ID
and has NO data for these 3 mimeTypes
"vnd.android.cursor.item/com.skype4life.message" // <- contains Skype username / ID
"vnd.android.cursor.item/com.skype4life.audio" // <- contains Skype username / ID
"vnd.android.cursor.item/com.skype4life.video" // <- contains Skype username / ID
Skype Phone Numbers are only usefull to pass phone call using Skype, but that's not what I want to do: I want to pass 'Internet Skype audio / video' calls, not 'regular cell phone' calls.
4 - Question
Why is the Skype app sometimes storing all 4 'phone/message/audio/video' mimeTypes data and sometimes, only 'phone' mimeType?
In the latter case, I am not able to obtain Skype Ids / Skype user names, i.e. the <skype.user.name.value.here> I mentioned above.
I understand my solution is the result of some kind of 'reverse-engineering-like method' and is not 'official' at all, but maybe someone else did the same kind of investigation I did and could find a solution?
Maybe there is some 'Skype sync settings' which should be activated within the Skype app or Within Android?
I already force contacts re-sync for Google and Skype accounts, but this did not help.
Thanks for your help.
What a great research! I got here because I stumbled upon the same problem. I also want to get skype username, but only mimetype "vnd.android.cursor.item/com.skype4life.phone" is present in the address book.
If I look at your link, where you mention these com.skype4life.* are added, I also see the addition of:
sync_adapter_account_type:
= com.skype4life
sync_adapter_authority:
= com.android.contacts
So they added this together. Maybe we should sync it in one way or another?
The mere problem is, that you don't have com.skype.raider.permission.ACCOUNT.
So you can only query them through ContentProvider or build an index to look them up.
For example:
How to get Skype info from the Android contact list?
Get Skype name from Contacts list
Intent strings alike com.skype.android.skypecall.action might not exist anymore, because com.skype.raider/com.skype4life obviously is not the same application.

Compare phone number from database in different condition

I'm developing sms APP and want to receive sms from the specific numbers. But number can be changed sometime with country code as +923201234567 or sometime without country code 03201234567 how I can compare number from database? because don't know in which format number is saved in database(with country code or without country code)
public boolean isMember(String phone, long id){
String query = "SELECT * from members where phone = ? AND active = 1 AND gid = ?";
Cursor c = dbActions.rawQuery(query, new String[]{String.valueOf(phone), String.valueOf(id)});
return c.moveToFirst();
}
Suppose if the number is saved in database without country code 03201234567 then my requirement is to get true if I compare it with country code. +923201234567. Country code could be changed.
PhoneNumberUtils.compare(); is not useful because it not compare with database.
If you can't acquire the correct information always; then you need to look into heuristics.
Meaning: you could write your own comparisons; and when you encounter two numbers like:
03201234567
+923201234567
you can figure: their "tail" is equal; the only difference is that the first one starts with 0 (so no country code) and the second one with +92. So it might be reasonable to declare those two numbers to be equal.
So a "solution" would do things like "normalize" your input (remove all non-digit content; except for leading + signs); and to then make such "tail-bound" comparisons.
If that is "too" fuzzy; I guess then you should step back and describe the requirement that you actually try to resolve here. Why are you comparing numbers; and what do you intend to do with the output of that comparison?!
Normalize all of the phone numbers into the same format before you put them into the database. That way you can just do a normal db search.
The other thing I've done for phone numbers is to convert all letters into the appropriate number, then remove all non digits, then just compare the last 7 digits.

Get unread conversations gmail

I want to get an int with the number of unread emails in the accounts of the device.
I have seen that there is a new way to do this using the "Gmail Labels Public API"
http://android-developers.blogspot.in/2012/04/gmail-public-labels-api.html
I have read the documentation and downloaded the sample application and it really works.
But I have two problems: (
My intention is to get an int with the number of unread conversations, i try this:
String unread = GmailContract.Labels.NUM_UNREAD_CONVERSATIONS.toString();
but not works, always returns "numUnreadConversations"
How I can get an int to this value?
My second problem is this: The code works correctly with the default count of my device, but I want to get the value with all unread conversations of all device gmail accounts
eally appreciate any help
thanks and regards
With GmailContract.Labels.NUM_UNREAD_CONVERSATIONS.toString(); you're just converting the enum to string.
You need a Cursor object to iterate through labels and then acquire the number with cursor.getInt(GmailContract.Labels.NUM_UNREAD_CONVERSATIONS)
Check this code for iterating through accounts and labels.

Samsung device returns text messages when querying for call-log

Some of my users report that on their Samsung devices (GT-N7000 & SGH-I777) a query I make in my app for the CallLog.Calls displays also text messages.
I've created a dump of their CallLog ContentProvider, and it seems to have extra fields not mentioned in the Android API, and not returned on any of our test devices.
Specifically, looking through the dump, there's a field called logtype, which seems to equal 100 for calls, and 300 for text messages.
Having searching online for this field, I didn't find any official documentation for this field's values, but I came across lots of other possible values for this field mainly via crash stack traces, which reveal underlining queries by the ContentProvider:
logtype=300 OR logtype=200
logtype=100 OR logtype=500 OR logtype=800 OR logtype=900 OR
logtype=1000
So I assume that 300/200 are used for text messages, and 100/500/800/900/1000 are used for calls, but I'm not sure, since I haven't seen 500/800/900/1000 being used on the reporting users' devices.
Can someone shed some light for the possible values of logtype, and their meaning?
Hello If you will check callLog.Calls columns you will find messageid field which says that this is message for samsung phones.
So if you just want to get the list of calls without messages simply do :
int messageIdIndex=cursor.getColumnIndex("messageid");
while (cursor.moveToNext())
{
if(messageIdIndex>=0)
messageID=cursor.getLong(messageIdIndex);
if(messageID<=0)
{
//do whatever you need with calls log data
}
}
cursor.close();
I've managed to tentatively solve it by querying CallLog.Calls for the column logtype, if an exception is thrown, I query normally, otherwise, I query with selection of (logtype=100 OR logtype=500)
This seems to be working for my reporting users, but I'm still not sure if it covers all bases, since there are many possible values for logtype for which I don't know the meaning.
If anyone has a better answer, please add it.
When i was debugging a sgs2 device i have found this. may be useful for someone.
SELECT number, name, type, date, duration FROM logs WHERE (
logs.logtype=100 OR
logs.logtype=110 OR
logs.logtype=900 OR
logs.logtype=500 OR
logs.logtype=800 OR
logs.logtype=120 OR
logs.logtype=510 OR
logs.logtype=1000 OR
(logs.logtype=200 AND number NOT IN (SELECT number FROM logs WHERE number LIKE '%#%')) OR logs.logtype=300)
AND ((type != 4)) AND (logtype=100 OR logtype=500)))
ORDER BY date DESC
So far, we have found Samsung devices using the value 1150, 100 or 1000 for the 'logtype' field in the Calls content provider.
I cannot confirm if any of the other values mentioned here are used.

Matching incoming and outgoing phone numbers

I'm currently making an SMS viewing application and using the ContentResolver to obtain all SMS messages on the phone (Yes, I understand the risks). Like other applications, I want to group all messages from the same person to one thread, display the latest message from them, and order the contacts by date of the last message.
When it comes to the address values of the incoming messages, they all contain the country code (e.g. +44123456789). But when the user saves his contacts, he could ignore the country code and simply type in the local format. So all outgoing messages are stored as 0123456789.
So, the database will contain the same address in both formats, +44123456789 and 0123456789. How do you match this 2 and remove the duplicate address?
Note:
1) Messages from the same person may not have the same "thread id"
2) There may not be a "contact id"/"display name" value for the address
Actually, messages to and from the same contact are in the same thread, therefore they have the same thread_id. (Apart from multiple recipient messages, which are in their own thread).
By looking in content://sms and storing a list of obtained thread_ids you can make sure there's no duplicates. With the address value you can use the following code to obtain the Display name.
Now, I'm trying to optimise this:
private String quickCallerId(String phoneNumber){
Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneNumber));
ContentResolver resolver=getContentResolver();
Cursor cur = resolver.query(uri, new String[]{PhoneLookup.DISPLAY_NAME}, null, null, null);
if(cur!=null&&cur.moveToFirst()){
String value=cur.getString(cur.getColumnIndex(PhoneLookup.DISPLAY_NAME));
if(value!=null){
cur.close();
return value;
}
}
cur.close();
return "";
}
I don't have the code on me, but it's pretty easy to parse a string from right to left. You could do this and simply set an arbitrary limit on how accurate it must be to stop.
For instance (pseudo-code), given 2 strings (string1 and string2):
if first-char = '+'
len = 9
else
len = length(string1)
end
len = min(len, length(string2))
match = true
for i = len to 1
if substr( string2, i, 1) != substr( string2, i, 1)
match = false
quit
end
i--
end
You could get fancier by checking the characters immediately following the '+' sign to determine the country code, which would let you know how long that country's phone numbers are likely to be.
You would also need to check for people entering numbers as e.g. '(123) 456-7890 x1234' if that's a possibility. So it might be simpler to use some regexp variant...
Rory

Categories

Resources