Android contacts tables update but do not get saved to SIM - android

I am trying to update the phone number associated with a contact on the SIM. I am able to update the Data table's row ok, but the change does not get propagated to the SIM. I dump out the Data row after it has been updated; the _ID is still the same, the phone (data1) col has the new phone number. When I powercycle the phone, the entry has the old value. The raw contact number of the contact has been incremented by 1. Any ideas on how to make the change persistent in the SIM? (Yes, I know some doc says access to SIM is not supported. But the Contacts app can do it - there must be a way for an app to update the SIM.) There may be typos - I had to (re)type the code by hand here.
Here is the code I use to update the row.
String rawId = "87"; // provided by caller; hardcoded here for brevity
String num = "212-222-3333"; // new phone #
ContentResolver cr = ...get actvity's resolver...
Uri uri = ContactsContract.Data.CONTENT_URI;
ContentValues cv = new ContentValues();
cv.put(ContactsContract.CommonDataKinds.Phone.Number, num);
String select = ContactsContract.Data.RAW_CONTACT_ID " + " = " + rawId + " and " +
ContactsContract.Data.MIMETYPE + " = '" +
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_ITEM + "'";
String[] selectArgs = {};
cr.update(uri, cv, select, selectArgs);
Catch exception and other details omitted.
Thanks for any clues!

Related

Show contacts with multiple phone numbers using CursorLoader

I want to retrieve all the contacts in an Android phone along with all their phone numbers. Let's say following is the list of contacts on my phone along with their phone numbers:
A - 1111 (Mobile), 2222(Home), 3333(Work)
B - 4444 (Mobile), 5555(Home)
C - 6666 (Mobile), 7777(Home), 8888(Work)
I want to display the contacts in the following fashion but using CursorLoader.
Things I have tried :
Get all the contacts and their phone numbers at a time and save them in an array list of custom contact objects and use them to display the list in a RecyclerView.
Issue with this is that if there are a lot of phone numbers then re-visiting this screen causes a blank screen possibly either due to resource not being free or memory leak issues.
Stack Overflow won't indent the code properly so here it is: https://pastebin.com/y9i5R5iN
final Uri uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, String.valueOf(contactListCursor.getLong(ContactsQuery.ID)));
Uri thisContactUri = ContactsContract.Contacts.lookupContact(context.getContentResolver(), uri);
final UserContact contact = new UserContact();
contact.setName(contactListCursor.getString(contactListCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)));
final ArrayList<PhoneType> multipleContacts = new ArrayList<>();
final Uri phoneNumUri = Uri.withAppendedPath(thisContactUri, ContactsContract.Contacts.Data.CONTENT_DIRECTORY);
final Cursor contactPhoneNumCursor = contentResolver.query(phoneNumUri, ContactPhoneQuery.PROJECTION, ContactPhoneQuery.SELECTION, null, null);
Get all the contacts using a CursorLoader and a CursorAdapter by querying the ContactsContract.Contacts.CONTENT_URI and getting the ID and DISPLAY_NAME_PRIMARY. In the getView() method I thought I could initialize a second CursorLoader to call into the details of the contact that the first cursor has given me. Something like this :
IF the user has multiple contacts then show a list of radio buttons, else show a textview with the only number they have.
This seems to load the phone numbers in advance and not update the view later on. Either because there's a delay or something I am not sure.
Found a way to do it.
Used the CursorLoader to pass to the CustomAdapter for initial query of contacts and then used the _ID field to lookup the contact and created a URI for the contact and looked up the Phone specific details inside the CustomAdapter's getView method itself.
I was trying to use CursorLoaders in both or Bulk loads in both. The hybrid approach worked out best.
Hope this helps someone. :)
You can use a single query to get all phone numbers in the Database along with their contact-ids and contact-names.
I would run this single query once, store all data in a map (contact-id => name, phone, ...)
And then use a custom adapter to display the map as a list of contacts.
Here's the code for querying all phones along with contact-ids and names (make sure you import everything from ContactsContract):
Map<Long, List<String>> contacts = new HashMap<Long, List<String>>();
String[] projection = { Data.CONTACT_ID, Data.DISPLAY_NAME, Data.MIMETYPE, Data.DATA1, Data.DATA2, Data.DATA3 };
String selection = Data.MIMETYPE + " IN ('" + Phone.CONTENT_ITEM_TYPE + "')";
Cursor cur = cr.query(Data.CONTENT_URI, projection, selection, null, null);
while (cur != null && cur.moveToNext()) {
long id = cur.getLong(0);
String name = cur.getString(1);
String mime = cur.getString(2); // type of data (e.g. "phone")
String data = cur.getString(3); // the actual info, e.g. +1-212-555-1234
int type = cur.getInt(4); // a numeric value representing type: e.g. home / office / personal
String label = cur.getString(5); // a custom label in case type is "TYPE_CUSTOM"
String labelStr = Phone.getTypeLabel(getResources(), type, label);
Log.d(TAG, "got " + id + ", " + name + ", " + kind + " - " + data + " (" + labelStr + ")");
// add info to existing list if this contact-id was already found, or create a new list in case it's new
List<String> infos;
if (contacts.containsKey(id)) {
infos = contacts.get(id);
} else {
infos = new ArrayList<String>();
infos.add("name = " + name);
contacts.put(id, infos);
}
infos.add(kind + " = " + data + " (" + labelStr + ")");
}
Note: The map's value is a list of strings, just for code-clarity. You should change it to be a custom Contact object that holds the name and list of phones in a proper Java Object

Search contacts provider for phone numbers in multiple formats?

I've been working on a block of code to let the user search (character by character using an AutoCompleteTextView) contacts by name, email or phone number. I've worked out the below code:
// General contact data, so we have to get the DATA1 attribute and use MIMETYPE
// to figure out what it is. Usually we'd query, say, ContactsContract.CommonDataKinds.Email.CONTENT_URI
Uri uri = ContactsContract.Data.CONTENT_URI;
// Limit the query results to only the columns we need for faster operations.
// Using a projection also seems to make the query DISTINCT
String[] projection = new String[] {ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.Data.DATA1,
ContactsContract.Data.MIMETYPE};
// Find contact records with an email address or phone number
// Search the name and data1 field (which may contain an email or phone number)
// for user-entered search phrase
String filter = "(" + ContactsContract.Data.MIMETYPE + "=? OR " + ContactsContract.Data.MIMETYPE + "=?)"
+ " AND (" + ContactsContract.Data.DATA1 + " LIKE ? OR " + ContactsContract.Data.DISPLAY_NAME + " LIKE ?)";
String wildcardedConstraint = "%" + constraintString + "%";
String[] filterParams = new String[]{ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE, wildcardedConstraint, wildcardedConstraint};
// Sort contacts with the most recently contacted ones first. That's often 0 (unset)
// so do a sub-sort by last updated date, most recent contacts first
String orderBy = ContactsContract.Contacts.LAST_TIME_CONTACTED + " DESC, " + ContactsContract.Contacts.CONTACT_LAST_UPDATED_TIMESTAMP + " DESC";
Cursor cursor = getContext().getContentResolver().query(uri, projection, filter, filterParams, orderBy);
if (cursor != null) {
while (cursor.moveToNext()) {
String name = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.DISPLAY_NAME));
String data1 = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.DATA1));
String mimetype = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.MIMETYPE));
String number = null;
String email = null;
if (mimetype.equals(ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)) {
email = data1;
} else if (mimetype.equals(ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)) {
number = data1;
}
items.add(new Person(name, number, email));
Log.e("temp", name + " " + data1 + " " + mimetype);
}
cursor.close();
}
There is a problem with the phone number search, however. In contacts, phone numbers are in many different formats:
+101234567890
(123) 456-7890
1234567890
123-456-7890
And so on.
How can I adapt my Contacts query filter so the user's input will find phone numbers in any format--preferably without making the entire query extremely slow?
Some solutions I've found rely on editing table data to standardize the phone numbers, which isn't an option with contacts. Maybe that normalized number field would work... if I could find a way to easily build it into this query on the Contacts Data table. I know I could do extra phone number searches for each record, or use Java to make the checks, but I think that would make it very slow. Perhaps a regexp SQL operator in the query--but I don't know how I could make it work for the user's character-by-character search where they may have only entered part of the phone number.
Any ideas?
You can do this with Android's built-in SQLite function PHONE_NUMBERS_EQUAL, which compares two numbers and will return 1 if they're identical enough for caller ID purposes.
You simply need to change your filter as follows:
String filter = "(" + ContactsContract.Data.MIMETYPE + "=? OR "
+ ContactsContract.Data.MIMETYPE + "=?) AND "
+ "(PHONE_NUMBERS_EQUAL(" + ContactsContract.Data.DATA1 + ", ?, 0) OR "
+ ContactsContract.Data.DATA1 + " LIKE ? OR "
+ ContactsContract.Data.DISPLAY_NAME + " LIKE ?)";
And add another wildcardedConstraint to your filterParams:
String[] filterParams = new String[] { ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE,
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE,
wildcardedConstraint,
wildcardedConstraint,
wildcardedConstraint };
The final INTEGER parameter in the PHONE_NUMBERS_EQUAL function indicates whether to use strict number comparation; 1 meaning do use strict, 0 meaning non-strict. Apparently this is a system-wide setting that can be retrieved from the system Resources, but I am uncertain as to what factors dictate how this is determined for a particular environment. The example above just uses non-strict comparation. However, if it is a concern, the actual resource value can be obtained like so:
private static final String STRICT_COMPARE = "config_use_strict_phone_number_comparation";
...
int strictResId = Resources.getSystem().getIdentifier(STRICT_COMPARE, "bool", "android");
boolean useStrict = Resources.getSystem().getBoolean(strictResId);

Android Contact Picking Uri

I have a string content//com.android.contacts/contacts/contacts/2 ie aft the user has selected a particular number.
From this string, how do i get the phone number of this particular contact?
I am new to android environment, so my question might seem a bit primitive.
Cursor c = (Cursor)mAdapter.getItem(a.keyAt(i));
Long id = c.getLong(c.getColumnIndex(ContactsContract.Contacts._ID));
contacts.add(ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, id));
chosenContacts = sb.toString();
chosenContacts is my string and that contains content//com.android.contacts/contacts/contacts/2
You need a second request. You can use the id of your snippet for it:
Cursor phoneNoCursor = contentResolver.query(
Data.CONTENT_URI,
new String[] {Phone.NUMBER, Phone.TYPE, Phone.LABEL},
Data.MIMETYPE + " = ? AND " + Data.CONTACT_ID + " = ? ",
new String[] {String.valueOf(Phone.CONTENT_ITEM_TYPE, id)},
null);
For the list of possible types see the description of the Phone class. But maybe the number itself and its label are enough anyway.

Android: Having trouble updating a row in a database after determining what row I need to update

I have a table that contains a list of devices with catagories like type, manufacturer and model and a device ID. Another table called system uses the device id to point to the table with the devices. I have a edit system function that I am trying to get working where I can edit the device id field in a record on the system table to change which device in the devices table I am pointing too. I run the update command and it looks like it should work but the row does not get updated. Does anyone know what I am doing wrong?
Here is my update code routine.
SQLiteDatabase db = dbHelper.getWritableDatabase();
String UpdateDeviceName = txtEditDeviceName.getText().toString();
String UpdateDeviceIP = txtEditDeviceIP.getText().toString();
//get the new device id based off of the three spinner values
String dbQuery = "SELECT * FROM " + dbHelper.Devices_Table + ";";
Cursor c = db.rawQuery(dbQuery, null);
if (c.getCount() > 0)
{
while (c.moveToNext())
{
int iColumnID = c.getColumnIndex(dbHelper.Attribute_Device_ID);
int iColumnDeviceType = c.getColumnIndex(dbHelper.Attribute_Device_Type);
int iColumnDeviceManufacturer = c.getColumnIndex(dbHelper.Attribute_Device_Manufacturer);
int iColumnDeviceModel = c.getColumnIndex(dbHelper.Attribute_Device_Model);
String CheckDeviceType = c.getString(iColumnDeviceType);
if (CheckDeviceType.equals(DeviceType))
{
String CheckDeviceManufacturer = c.getString(iColumnDeviceManufacturer);
if (CheckDeviceManufacturer.equals(DeviceManufacturer))
{
String CheckDeviceModel = c.getString(iColumnDeviceModel);
if (CheckDeviceModel.equals(DeviceModel))
{
DeviceID = c.getString(iColumnID);
}
}
}
}
}
db.close();
c.close();
db = dbHelper.getWritableDatabase();
//with the device ID update the system
dbQuery = "UPDATE " + dbHelper.System_Table + " SET " + dbHelper.Attribute_Device_ID + " = " + DeviceID + ", " +
dbHelper.Attribute_Device_Name + " = '" + UpdateDeviceName + "', " +
dbHelper.Attribute_Device_IP + " = '" +
UpdateDeviceIP + "' WHERE " + dbHelper.Attribute_Device_ID + " = " + OrginalDeviceID + ";";
c = db.rawQuery(dbQuery, null);
Log.d("Database Dump", "Edit Device Query: " + dbQuery);
c.close();
db.close();
You closed the DB after the search:
db.close();
leave out this line, since the DB helper code prefers to keep the DB open until it is no longer used (use dbHelper.close() to close the DB when you are done with it).
The same applies after the update.
First of all any rawQuery statement must not contain the trailing semicolon! See the documentation of SQLiteDatabase.
Also you always must close cursors before closing the database. And you have to close the db when you get a new one for each query. Otherwise the system at some point later on will throw exceptions. While those only will be logged and do not cause a Force Close, you should still care about proper resource management.
Finally you should use the updateWithOnConflict method instead with using CONFLICT_REPLACE as the conflictAlgorithm. This simply ignores any UNIQUE constraints and overwrites conflicting rows. So be careful. If you do not want to overwrite existing rows you have to make sure that a constarint violation isn't causing your problem.
I found the answer. FIrst off I want to thank Henry and Wolfram Rittmeyer. You guys where very helpful. :)
For whatever reason the android SQLite does not like to use the update statement in raw sql query form. In researching what Wolfram Rittmeyer said about updateWithConflict I found the update method in the SQLiteOpenHelper object which my dbHelper extends. When I switched to that method I was able to update the database. My where conditions have been expanded on to include the device name as well in the event that the system has more then one device make and model of the same type and only one has to change.
For reference the working code to update the database is the following. I'm only posting the changed portion of the code. Everything in the if (c.getCount() > 0) block and above is unchanged.
c.close();
dbHelper.close();
db = dbHelper.getWritableDatabase();
//with the device ID update the system
ContentValues values = new ContentValues();
values.put(dbHelper.Attribute_Device_ID, DeviceID);
values.put(dbHelper.Attribute_Device_Name, UpdateDeviceName);
values.put(dbHelper.Attribute_Device_IP, UpdateDeviceIP);
String Where = dbHelper.Attribute_Device_ID + " = " + OrginalDeviceID + " AND " + dbHelper.Attribute_Device_Name + " = '" + OrginalDeviceName + "'";
db.update(dbHelper.System_Table, values, Where, null);
c.close();
dbHelper.close();
finish(); //this exits the activity and goes back to the calling activity

Android change number to primary number

I'm trying to make someones number a primary number in the specific contact numbers. This is the code:
Cursor the_phone = _context.getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null, ContactsContract.CommonDataKinds.Phone.NUMBER +" = "+ numberToCall, null, null);
ContentValues values = new ContentValues();
if (the_phone.moveToFirst()){
values.put(ContactsContract.CommonDataKinds.Phone.LABEL,
the_phone.getString(the_phone.getColumnIndex(ContactsContract.CommonDataKinds.Phone.LABEL)));
values.put(ContactsContract.CommonDataKinds.Phone.IS_PRIMARY,1);
values.put(ContactsContract.CommonDataKinds.Phone.NUMBER,numberToCall);
int phones = _context.getContentResolver().update(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,values, ContactsContract.CommonDataKinds.Phone.NUMBER +" = "+ numberToCall, null);
}
Then I keep getting the following:
03-27 08:18:27.009: E/AndroidRuntime(640): FATAL EXCEPTION: main
03-27 08:18:27.009: E/AndroidRuntime(640): java.lang.UnsupportedOperationException: URI: content://com.android.contacts/data/phones, calling user: com...
I checked the first query and it's working I'm able to find the number in the resolver but unable to update it.
ok actually solved after looking into the resolver class.
private Boolean editPrimary( Cursor phones , String contactId, String contactNumber, int primaryTo){
ArrayList ops = new ArrayList();
String where = ContactsContract.Data.CONTACT_ID + " = ? AND " +
ContactsContract.CommonDataKinds.Phone.MIMETYPE + " = ? AND " +
String.valueOf(ContactsContract.CommonDataKinds.Phone.TYPE) + " = ? AND " +
ContactsContract.CommonDataKinds.Phone.NUMBER + " = ?";
String[] params = new String[] {contactId,
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE,
String.valueOf(phones.getInt(phones.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.TYPE))),
contactNumber};
ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
.withSelection(where, params)
.withValue(ContactsContract.CommonDataKinds.Phone.IS_SUPER_PRIMARY,primaryTo)
.build());
try {
_context.getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
return true;
} catch (RemoteException e) {
e.printStackTrace();
return false;
} catch (OperationApplicationException e) {
e.printStackTrace();
return false;
}
}
from the developer guide we can see that :
Overview
ContactsContract defines an extensible database of contact-related information. Contact information is stored in a three-tier data model:
A row in the ContactsContract.Data table can store any kind of personal data, such as a phone number or email addresses. The set of data kinds that can be stored in this table is open-ended. There is a predefined set of common kinds, but any application can add its own data kinds.
A row in the ContactsContract.RawContacts table represents a set of data describing a person and associated with a single account (for example, one of the user's Gmail accounts).
A row in the ContactsContract.Contacts table represents an aggregate of one or more RawContacts presumably describing the same person. When data in or associated with the RawContacts table is changed, the affected aggregate contacts are updated as necessary.
so my first approach was wrong i didn't realize that the datakind class could represent the same data as the data class only directed to the context we are looking at

Categories

Resources