36

I would like to get user contacts and then append some kind of regular expression and append them to a list view. I am currently able to get all the contacts via

getContentResolver().query(People.CONTENT_URI, null, null, null, null);

and then pass them to a custom class that extends SimpleCursorAdapter.

So I would like to know how to get only the contacts that match a regular expression and not all of users contacts.

bstpierre
  • 30,042
  • 15
  • 70
  • 103
maxsap
  • 2,971
  • 9
  • 44
  • 70

4 Answers4

74

Instead of

getContentResolver().query(People.CONTENT_URI, null, null, null, null); 

you should use something like

final ContentResolver resolver = getContentResolver();
final String[] projection = { People._ID, People.NAME, People.NUMBER };
final String sa1 = "%A%"; // contains an "A"
cursor = resolver.query(People.CONTENT_URI, projection, People.NAME + " LIKE ?",
   new String[] { sa1 }, null);

this uses a parameterized request (using ?) and provides the actual values as a different argument, this avoids concatenation and prevents SQL injection mainly if you are requesting the filter from the user. For example if you are using

cursor = resolver.query(People.CONTENT_URI, projection,
   People.NAME + " = '" + name + "'",
   new String[] { sa1 }, null);

imagine if

name =  "Donald Duck' OR name = 'Mickey Mouse") // notice the " and '

and you are concatenating the strings.

Diego Torres Milano
  • 65,697
  • 9
  • 111
  • 134
8

You can query the content provider with sql type input, the Query method is just a wrapper for an sql command.

Here is an example where I query for a Contacts name given a particular number

String [] requestedColumns = {
             Contacts.Phones.NAME,
             Contacts.Phones.TYPE
     };

Cursor contacts = context.getContentResolver().query(
             Contacts.Phones.CONTENT_URI,
             requestedColumns,
             Contacts.Phones.NUMBER + "='" + phoneNumber + "'",
             null, null);

Note that instead of null I have parameters that build up the sql statement.

The requestColumns are the data I want to get back and Contacts.Phones.NUMBER + "='" + phoneNumber + "'" is the Where clause, so I retrieve the Name and Type where the Phone Number matches

Donal Rafferty
  • 19,707
  • 39
  • 114
  • 191
  • 1
    What about if Contacts.Phones.NUMBER has spaces insides it but phoneNumber not? – Davide Nov 03 '18 at 07:46
  • I have spaces in Contacts.Phones.NUMBER but not in phoneNumber as @Davide mentioned. I could not use the SelectionClause for this. I have to do the filtering in the do while loop, which is a huge performance loss. – amitava Dec 28 '18 at 06:15
  • @AMITAVA I resolved in this manner: "replace(" + ContactsContract.CommonDataKinds.Phone.NUMBER + ", ' ', '')" + " = '" + phoneNumber + "'" instead Contacts.Phones.NUMBER + "='" + phoneNumber + "'", just use the replace function sqlite provides – Davide Dec 28 '18 at 09:16
  • Hi @Davide many thanks, one question, does it modify the original source ContactsContract data or just the table generated from it (or does not modify anything, just in the selection clause). I am not able to check it out of fear as I do not want to change the source data. – amitava Dec 29 '18 at 15:41
  • It makes a simple select. From that table, it will replace all spaces with the empty character and then it compares with the phone number. Original persisted data are NOT affected. In your case adapt the ContactsContract.CommonDataKinds.Phone.NUMBER column with yours (probably Contacts.Phones.NUMBER) – Davide Dec 29 '18 at 15:45
  • @Davide Your code is working but how can I remove whiteSpace and '+' sign together. – Dharmesh Patel Jan 09 '20 at 09:05
  • @DharmeshPatel I could suggest you to call the replace function two times. First time with the space and the second time with the plus character (or viceversa). If you look at performance, maybe it's not the best idea. You could look if the replace function in Android accept a regexp string and you're done – Davide Jan 09 '20 at 11:35
3

You should be able to put a legal SQLite WHERE clause as the third argument to the query() method, including a LIKE, but there's no native REGEXP function in SQLite and Android doesn't seem to let you define your own. So depending how complex your needs are, a set of other SQLite conditions and LIKE expressions might do the trick.

See the documentation on the query method under ContentResolver and SQLite expressions.

Yoni Samlan
  • 37,905
  • 5
  • 60
  • 62
2

Actually REGEXP with Calllog Content Provider works (means that regexp() function is defined for that content provider's Database https://sqlite.org/lang_expr.html#regexp)! But it is very slow: ~15 sec across ~1750 records.

String regexp = "([\\s\\S]{0,}" + 
TextUtils.join("||[\\s\\S]{0,}", numbers) + 
")";

cursor = context.getContentResolver().query(
CallLog.Calls.CONTENT_URI, 
null, 
CallLog.Calls.NUMBER + " REGEXP ?", 
new String[]{regexp}, 
CallLog.Calls.DATE + " DESC"
);