/** * Copyright (C) 2010-2012 Regis Montoya (aka r3gis - www.r3gis.fr) * This file is part of CSipSimple. * * CSipSimple is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * If you own a pjsip commercial license you can also redistribute it * and/or modify it under the terms of the GNU Lesser General Public License * as an android library. * * CSipSimple is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with CSipSimple. If not, see <http://www.gnu.org/licenses/>. */ package com.csipsimple.utils.contacts; import android.annotation.TargetApi; import android.content.ContentProviderOperation; import android.content.ContentResolver; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.OperationApplicationException; import android.database.Cursor; import android.database.DatabaseUtils; import android.database.SQLException; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.drawable.BitmapDrawable; import android.net.Uri; import android.os.Build; import android.os.RemoteException; import android.provider.ContactsContract; import android.provider.ContactsContract.CommonDataKinds; import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.Data; import android.provider.ContactsContract.Groups; import android.provider.ContactsContract.Intents.Insert; import android.provider.ContactsContract.PhoneLookup; import android.provider.ContactsContract.RawContacts; import android.provider.ContactsContract.StatusUpdates; import android.telephony.PhoneNumberUtils; import android.text.TextUtils; import android.view.View; import android.widget.ImageView; import android.widget.TextView; import com.csipsimple.R; import com.csipsimple.api.SipManager; import com.csipsimple.api.SipUri; import com.csipsimple.models.CallerInfo; import com.csipsimple.utils.Compatibility; import com.csipsimple.utils.Log; import com.csipsimple.utils.PreferencesProviderWrapper; import java.io.InputStream; import java.util.ArrayList; import java.util.List; @TargetApi(5) public class ContactsUtils5 extends ContactsWrapper { public static final int CONTACT_ID_INDEX = 1; public static final int TYPE_INDEX = 2; public static final int NUMBER_INDEX = 3; public static final int LABEL_INDEX = 4; public static final int NAME_INDEX = 5; private static final String[] PROJECTION_PHONE = { CommonDataKinds.Phone._ID, // 0 CommonDataKinds.Phone.CONTACT_ID, // 1 CommonDataKinds.Phone.TYPE, // 2 CommonDataKinds.Phone.NUMBER, // 3 CommonDataKinds.Phone.LABEL, // 4 CommonDataKinds.Phone.DISPLAY_NAME, // 5 }; private static final String DISPLAY_NAME_ORDER = Contacts.DISPLAY_NAME + " COLLATE LOCALIZED"; private static final String SORT_ORDER = Contacts.TIMES_CONTACTED + " DESC," + DISPLAY_NAME_ORDER + "," + CommonDataKinds.Phone.TYPE; private static final String THIS_FILE = "ContactsUtils5"; public Bitmap getContactPhoto(Context ctxt, Uri uri, boolean hiRes, Integer defaultResource) { Bitmap img = null; InputStream s = ContactsContract.Contacts.openContactPhotoInputStream( ctxt.getContentResolver(), uri); img = BitmapFactory.decodeStream(s); if (img == null && defaultResource != null) { BitmapDrawable drawableBitmap = ((BitmapDrawable) ctxt.getResources().getDrawable( defaultResource)); if (drawableBitmap != null) { img = drawableBitmap.getBitmap(); } } return img; } public List<Phone> getPhoneNumbers(Context ctxt, long contactId, int flag) { String id = Long.toString(contactId); ArrayList<Phone> phones = new ArrayList<Phone>(); Cursor pCur; if ((flag & ContactsWrapper.URI_NBR) > 0) { pCur = ctxt.getContentResolver().query( CommonDataKinds.Phone.CONTENT_URI, null, CommonDataKinds.Phone.CONTACT_ID + " = ?", new String[] { id }, null); while (pCur.moveToNext()) { phones.add(new Phone( pCur.getString(pCur .getColumnIndex(CommonDataKinds.Phone.NUMBER)), pCur.getString(pCur.getColumnIndex(CommonDataKinds.Phone.TYPE)) )); } pCur.close(); } // Add any custom IM named 'sip' and set its type to 'sip' if ((flag & ContactsWrapper.URI_IM) > 0) { pCur = ctxt.getContentResolver().query( Data.CONTENT_URI, null, Data.CONTACT_ID + " = ? AND " + Data.MIMETYPE + " = ?", new String[] { id, CommonDataKinds.Im.CONTENT_ITEM_TYPE }, null); while (pCur.moveToNext()) { // Could also use some other IM type but may be confusing. Are there // phones with no 'custom' IM type? if (pCur.getInt(pCur.getColumnIndex(CommonDataKinds.Im.PROTOCOL)) == CommonDataKinds.Im.PROTOCOL_CUSTOM) { String proto = pCur.getString(pCur .getColumnIndex(CommonDataKinds.Im.CUSTOM_PROTOCOL)); if (SipManager.PROTOCOL_SIP.equalsIgnoreCase(proto) || SipManager.PROTOCOL_CSIP.equalsIgnoreCase(proto)) { phones.add(new Phone(pCur.getString(pCur .getColumnIndex(CommonDataKinds.Im.DATA)), SipManager.PROTOCOL_SIP)); } } } pCur.close(); } // Add any SIP uri if android 9 if (Compatibility.isCompatible(9) && ((flag & ContactsWrapper.URI_SIP) > 0)) { pCur = ctxt.getContentResolver().query( Data.CONTENT_URI, null, Data.CONTACT_ID + " = ? AND " + Data.MIMETYPE + " = ?", new String[] { id, CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE }, null); while (pCur.moveToNext()) { // Could also use some other IM type but may be confusing. Are // there phones with no 'custom' IM type? phones.add(new Phone(pCur.getString(pCur .getColumnIndex(ContactsContract.Data.DATA1)), SipManager.PROTOCOL_SIP)); } pCur.close(); } return (phones); } @Override public CallerInfo findCallerInfo(Context ctxt, String number) { Uri searchUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number)); CallerInfo callerInfo = new CallerInfo(); String[] projection; if (Compatibility.isCompatible(11)) { projection = new String[] { PhoneLookup._ID, PhoneLookup.DISPLAY_NAME, PhoneLookup.TYPE, PhoneLookup.LABEL, PhoneLookup.NUMBER, PhoneLookup.PHOTO_ID, PhoneLookup.LOOKUP_KEY, PhoneLookup.CUSTOM_RINGTONE, PhoneLookup.PHOTO_URI }; } else { projection = new String[] { PhoneLookup._ID, PhoneLookup.DISPLAY_NAME, PhoneLookup.TYPE, PhoneLookup.LABEL, PhoneLookup.NUMBER, PhoneLookup.CUSTOM_RINGTONE, PhoneLookup.LOOKUP_KEY }; } Cursor cursor = null; try { cursor = ctxt.getContentResolver().query(searchUri, projection, null, null, null); }catch(SQLException e) { Log.e(THIS_FILE, "Stock contact app is not able to resolve contacts", e); } if (cursor != null) { try { if (cursor.getCount() > 0) { cursor.moveToFirst(); ContentValues cv = new ContentValues(); DatabaseUtils.cursorRowToContentValues(cursor, cv); callerInfo.contactExists = true; if (cv.containsKey(PhoneLookup.DISPLAY_NAME)) { callerInfo.name = cv.getAsString(PhoneLookup.DISPLAY_NAME); } callerInfo.phoneNumber = cv.getAsString(PhoneLookup.NUMBER); if (cv.containsKey(PhoneLookup.TYPE) && cv.containsKey(PhoneLookup.LABEL)) { callerInfo.numberType = cv.getAsInteger(PhoneLookup.TYPE); callerInfo.numberLabel = cv.getAsString(PhoneLookup.LABEL); callerInfo.phoneLabel = (String) android.provider.ContactsContract.CommonDataKinds.Phone .getTypeLabel(ctxt.getResources(), callerInfo.numberType, callerInfo.numberLabel); } if (cv.containsKey(PhoneLookup._ID)) { callerInfo.personId = cv.getAsLong(PhoneLookup._ID); callerInfo.contactContentUri = ContentUris.withAppendedId( Contacts.CONTENT_URI, callerInfo.personId); } if (cv.containsKey(PhoneLookup.CUSTOM_RINGTONE)) { String cRt = cv.getAsString(PhoneLookup.CUSTOM_RINGTONE); if(!TextUtils.isEmpty(cRt)) { callerInfo.contactRingtoneUri = Uri.parse(cRt); } } if (cv.containsKey(PhoneLookup.PHOTO_ID) && cv.getAsLong(PhoneLookup.PHOTO_ID) != null) { callerInfo.photoId = cv.getAsLong(PhoneLookup.PHOTO_ID); } if (cv.containsKey(PhoneLookup.PHOTO_URI)) { String cPu = cv.getAsString(PhoneLookup.PHOTO_URI); if(!TextUtils.isEmpty(cPu)) { callerInfo.photoUri = Uri.parse(cPu); } } if (callerInfo.name != null && callerInfo.name.length() == 0) { callerInfo.name = null; } } } catch (Exception e) { Log.e(THIS_FILE, "Exception while retrieving cursor infos", e); } finally { cursor.close(); } } // if no query results were returned with a viable number, // fill in the original number value we used to query with. if (TextUtils.isEmpty(callerInfo.phoneNumber)) { callerInfo.phoneNumber = number; } return callerInfo; } private String getContactDataCustomProtocolFilter(String protocol) { return String.format(" %s='%s' AND %s=%s AND LOWER(%s)='%s'", Data.MIMETYPE, CommonDataKinds.Im.CONTENT_ITEM_TYPE, CommonDataKinds.Im.PROTOCOL, CommonDataKinds.Im.PROTOCOL_CUSTOM, CommonDataKinds.Im.CUSTOM_PROTOCOL, protocol.toLowerCase()); } @Override public CallerInfo findCallerInfoForUri(Context ctxt, String sipUri) { CallerInfo callerInfo = new CallerInfo(); String[] projection; if (Compatibility.isCompatible(11)) { projection = new String[] { Data._ID, Data.CONTACT_ID, Data.DATA1, Data.DISPLAY_NAME, Data.PHOTO_ID, Data.CUSTOM_RINGTONE, Data.LOOKUP_KEY, Data.PHOTO_URI }; } else { projection = new String[] { Data._ID, Data.CONTACT_ID, Data.DATA1, Data.DISPLAY_NAME, Data.PHOTO_ID, Data.CUSTOM_RINGTONE, Data.LOOKUP_KEY }; } Uri uri = Data.CONTENT_URI; // Has phone number String whereSipUriClause = String.format("(%s)", getContactDataCustomProtocolFilter(SipManager.PROTOCOL_SIP)); whereSipUriClause += String.format(" OR (%s)", getContactDataCustomProtocolFilter(SipManager.PROTOCOL_CSIP)); whereSipUriClause += String.format(" OR (%s)", getContactDataCustomProtocolFilter(SipManager.PROTOCOL_SIPS)); // Has sip uri if (Compatibility.isCompatible(9)) { whereSipUriClause += " OR " + Data.MIMETYPE + "='" + CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE + "'"; } String query = Contacts.DISPLAY_NAME + " IS NOT NULL " + " AND (" + whereSipUriClause + ") AND " + Data.DATA1 + "=?"; Cursor cursor = ctxt.getContentResolver().query(uri, projection, query, new String[] {sipUri}, Data.DISPLAY_NAME + " ASC"); if (cursor != null) { try { if (cursor.getCount() > 0) { cursor.moveToFirst(); ContentValues cv = new ContentValues(); DatabaseUtils.cursorRowToContentValues(cursor, cv); callerInfo.contactExists = true; if (cv.containsKey(Data.DISPLAY_NAME)) { callerInfo.name = cv.getAsString(Data.DISPLAY_NAME); } callerInfo.phoneNumber = sipUri; callerInfo.numberLabel = "sip"; callerInfo.phoneLabel = "sip"; if (cv.containsKey(Data.CONTACT_ID)) { callerInfo.personId = cv.getAsLong(Data.CONTACT_ID); callerInfo.contactContentUri = ContentUris.withAppendedId( Contacts.CONTENT_URI, callerInfo.personId); } if (cv.containsKey(Data.CUSTOM_RINGTONE)) { String cRt = cv.getAsString(Data.CUSTOM_RINGTONE); if(!TextUtils.isEmpty(cRt)) { callerInfo.contactRingtoneUri = Uri.parse(cRt); } } if (cv.containsKey(Data.PHOTO_ID) && cv.getAsLong(Data.PHOTO_ID) != null) { callerInfo.photoId = cv.getAsLong(Data.PHOTO_ID); } if (cv.containsKey(Data.PHOTO_URI)) { String cPu = cv.getAsString(Data.PHOTO_URI); if(!TextUtils.isEmpty(cPu)) { callerInfo.photoUri = Uri.parse(cPu); } } if (callerInfo.name != null && callerInfo.name.length() == 0) { callerInfo.name = null; } } } catch (Exception e) { Log.e(THIS_FILE, "Exception while retrieving cursor infos", e); } finally { cursor.close(); } } return callerInfo; } @Override public CallerInfo findSelfInfo(Context ctxt) { CallerInfo callerInfo = new CallerInfo(); return callerInfo; } @Override public Cursor getContactsPhones(Context ctxt, CharSequence constraint) { Uri uri = Data.CONTENT_URI; // Has phone number String isPhoneType = "(" + Data.MIMETYPE + "='" + CommonDataKinds.Phone.CONTENT_ITEM_TYPE + "' AND " + CommonDataKinds.Phone.NUMBER + " IS NOT NULL ) "; // Has sip uri if (Compatibility.isCompatible(9)) { isPhoneType += " OR (" + Data.MIMETYPE + "='" + CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE + "')"; } // Sip: IM custo (sip, csip, sips) isPhoneType += String.format(" OR ( %s)", getContactDataCustomProtocolFilter(SipManager.PROTOCOL_SIP)); isPhoneType += String.format(" OR ( %s)", getContactDataCustomProtocolFilter(SipManager.PROTOCOL_SIPS)); isPhoneType += String.format(" OR ( %s)", getContactDataCustomProtocolFilter(SipManager.PROTOCOL_CSIP)); String query = Contacts.DISPLAY_NAME + " IS NOT NULL " + " AND (" + isPhoneType + ")"; String[] projection; if (Compatibility.isCompatible(11)) { projection = new String[] { Data._ID, Data.CONTACT_ID, Data.DATA1, Data.DISPLAY_NAME, Data.PHOTO_ID, Data.LOOKUP_KEY, Data.PHOTO_URI, Data.MIMETYPE, Data.DATA2, Data.DATA3, Data.DATA5, Data.DATA6 }; } else { projection = new String[] { Data._ID, Data.CONTACT_ID, Data.DATA1, Data.DISPLAY_NAME, Data.PHOTO_ID, Data.LOOKUP_KEY, Data.MIMETYPE, Data.DATA2, Data.DATA3, Data.DATA5, Data.DATA6 }; } // Treat constraint String[] selectionArgs = null; if (!TextUtils.isEmpty(constraint)) { String phoneConstraint = null; boolean isDigitOnly = constraint.toString().matches("^[0-9\\-\\(\\)+ ]+$"); if (usefulAsDigits(constraint)) { phoneConstraint = PhoneNumberUtils.convertKeypadLettersToDigits(constraint.toString()); if (!phoneConstraint.equals(constraint.toString())) { phoneConstraint = phoneConstraint.trim(); } } // Start filter condition ArrayList<String> selectionArgsArray = new ArrayList<String>(); query += " AND ("; // Filter the data (aka phone number or sip uri) query += String.format("%s LIKE ?", Data.DATA1); selectionArgsArray.add(constraint + "%"); if(isDigitOnly) { query += String.format(" OR replace(replace(replace(replace(%s, ' ', ''), '-', ''), '(', ''), ')', '') LIKE ?", Data.DATA1); String cleanNumber = constraint.toString().replaceAll("[ \\-()]", ""); selectionArgsArray.add(cleanNumber + "%"); } if(!TextUtils.isEmpty(phoneConstraint) && !phoneConstraint.equals(constraint)) { query += String.format(" OR %s LIKE ?", Data.DATA1); selectionArgsArray.add(phoneConstraint + "%"); } // Filter the contact based on other data such as name if(!TextUtils.isEmpty(constraint) && !isDigitOnly) { query += " OR " + Data.RAW_CONTACT_ID + " IN " + "(SELECT name_data." + Data.RAW_CONTACT_ID + " FROM view_data AS name_data"+ " WHERE name_data." + Data.MIMETYPE + "='" + CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE + "'" + " AND (" + "name_data."+CommonDataKinds.StructuredName.FAMILY_NAME + " LIKE ? OR " + "name_data."+CommonDataKinds.StructuredName.GIVEN_NAME + " LIKE ?" + ")"+ ")"; selectionArgsArray.add(constraint + "%"); selectionArgsArray.add(constraint + "%"); } query += ")"; selectionArgs = selectionArgsArray.toArray(new String[selectionArgsArray.size()]); } Cursor resCursor = ctxt.getContentResolver().query(uri, projection, query, selectionArgs, Data.DISPLAY_NAME + " ASC"); return resCursor; } @Override public CharSequence transformToSipUri(Context ctxt, Cursor cursor) { String value = cursor.getString(cursor.getColumnIndex(Data.DATA1)); if (value == null) { return ""; } value = value.trim(); return value; } /* (non-Javadoc) * @see com.csipsimple.utils.contacts.ContactsWrapper#isExternalPhoneNumber(android.content.Context, android.database.Cursor) */ @Override public boolean isExternalPhoneNumber(Context context, Cursor cursor) { String mimeType = cursor.getString(cursor.getColumnIndex(Data.MIMETYPE)); return CommonDataKinds.Phone.CONTENT_ITEM_TYPE.equalsIgnoreCase(mimeType); } /* (non-Javadoc) * @see com.csipsimple.utils.contacts.ContactsWrapper#bindContactPhoneView(android.view.View, android.content.Context, android.database.Cursor) */ @Override public void bindContactPhoneView(View view, Context context, Cursor cursor) { // Get values String value = cursor.getString(cursor.getColumnIndex(Data.DATA1)); String displayName = cursor.getString(cursor.getColumnIndex(Data.DISPLAY_NAME)); Long contactId = cursor.getLong(cursor.getColumnIndex(Data.CONTACT_ID)); Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); Bitmap bitmap = getContactPhoto(context, uri, false, R.drawable.ic_contact_picture_holo_dark); CharSequence labelName = ""; String mimeType = cursor.getString(cursor.getColumnIndex(Data.MIMETYPE)); if(CommonDataKinds.Phone.CONTENT_ITEM_TYPE.equalsIgnoreCase(mimeType)) { int typeColumnIdx = cursor.getColumnIndex(CommonDataKinds.Phone.TYPE); int labelColumnIdx = cursor.getColumnIndex(CommonDataKinds.Phone.LABEL); if(labelColumnIdx != -1 && typeColumnIdx != -1) { String labelField = cursor.getString(labelColumnIdx); int typeField = cursor.getInt(typeColumnIdx); labelName = ContactsContract.CommonDataKinds.Phone.getTypeLabel(context.getResources(), typeField, labelField); } }else if(CommonDataKinds.Im.CONTENT_ITEM_TYPE.equalsIgnoreCase(mimeType)) { int typeColumnIdx = cursor.getColumnIndex(CommonDataKinds.Im.PROTOCOL); int labelColumnIdx = cursor.getColumnIndex(CommonDataKinds.Im.CUSTOM_PROTOCOL); if(typeColumnIdx != -1 && labelColumnIdx != -1) { String labelField = cursor.getString(labelColumnIdx); int typeField = cursor.getInt(typeColumnIdx); labelName = ContactsContract.CommonDataKinds.Im.getProtocolLabel(context.getResources(), typeField, labelField); } } // Get views TextView tv = (TextView) view.findViewById(R.id.name); TextView label = (TextView) view.findViewById(R.id.label); TextView sub = (TextView) view.findViewById(R.id.number); ImageView imageView = (ImageView) view.findViewById(R.id.contact_photo); // Bind view.setTag(value); tv.setText(displayName); sub.setText(value); // When there's no label, getDisplayLabel() returns a CharSequence of length==1 containing a unicode non-breaking space. // Need to check for that and consider that as "no label". if (TextUtils.isEmpty(labelName) || (labelName.length() == 1 && labelName.charAt(0) == '\u00A0')) { label.setVisibility(View.GONE); } else { label.setText(labelName); label.setVisibility(View.VISIBLE); } if(imageView != null) { imageView.setImageBitmap(bitmap); } // Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, cursor.getLong(CONTACT_ID_INDEX)); // ContactsAsyncHelper.updateImageViewWithContactAsync(context, imageView, uri, R.drawable.ic_contact_picture_holo_dark); } @Override public int getContactIndexableColumnIndex(Cursor c) { return c.getColumnIndex(Contacts.DISPLAY_NAME); } @Override public Cursor getContactsByGroup(Context ctxt, String groupName) { if (TextUtils.isEmpty(groupName)) { return null; } String[] projection; if (Compatibility.isCompatible(11)) { projection = new String[] { Contacts._ID, Contacts.DISPLAY_NAME, Contacts.PHOTO_ID, Contacts.CONTACT_STATUS_ICON, Contacts.CONTACT_STATUS, Contacts.CONTACT_PRESENCE, Contacts.PHOTO_URI }; } else { projection = new String[] { Contacts._ID, Contacts.DISPLAY_NAME, Contacts.PHOTO_ID, Contacts.CONTACT_STATUS, Contacts.CONTACT_PRESENCE }; } Uri searchUri = Uri.withAppendedPath(Contacts.CONTENT_GROUP_URI, Uri.encode(groupName)); Cursor c = null; try { c = ctxt.getContentResolver().query(searchUri, projection, null, null, Contacts.DISPLAY_NAME + " ASC"); } catch(Exception e) { Log.e(THIS_FILE, "Error while retrieving group", e); } return c; } @Override public List<String> getCSipPhonesByGroup(Context ctxt, String groupName) { Cursor contacts = getContactsByGroup(ctxt, groupName); ArrayList<String> results = new ArrayList<String>(); if (contacts != null) { try { while (contacts.moveToNext()) { List<String> res = getCSipPhonesContact(ctxt, contacts.getLong(contacts .getColumnIndex(Contacts._ID))); results.addAll(res); } } catch (Exception e) { Log.e(THIS_FILE, "Error while looping on contacts", e); } finally { contacts.close(); } } return results; } @Override public List<String> getCSipPhonesContact(Context ctxt, Long contactId) { ArrayList<String> results = new ArrayList<String>(); Uri dataUri = Data.CONTENT_URI; String dataQuery = Data.MIMETYPE + "='" + CommonDataKinds.Im.CONTENT_ITEM_TYPE + "' " + " AND " + CommonDataKinds.Im.PROTOCOL + "=" + CommonDataKinds.Im.PROTOCOL_CUSTOM + " AND " + " LOWER(" + CommonDataKinds.Im.CUSTOM_PROTOCOL + ")='"+SipManager.PROTOCOL_CSIP+"'"; // get csip data Cursor dataCursor = ctxt.getContentResolver() .query(dataUri, new String[] { CommonDataKinds.Im._ID, CommonDataKinds.Im.DATA, }, dataQuery + " AND " + CommonDataKinds.Im.CONTACT_ID + "=?", new String[] { Long.toString(contactId) }, null); try { if (dataCursor != null && dataCursor.getCount() > 0) { dataCursor.moveToFirst(); String val = dataCursor.getString(dataCursor .getColumnIndex(CommonDataKinds.Im.DATA)); if (!TextUtils.isEmpty(val)) { results.add(val); } } } catch (Exception e) { Log.e(THIS_FILE, "Error while looping on data", e); } finally { dataCursor.close(); } return results; } @TargetApi(Build.VERSION_CODES.FROYO) @Override public void updateCSipPresence(Context ctxt, String buddyUri, SipManager.PresenceStatus presStatus, String statusText) { if (Compatibility.isCompatible(8)) { // if(csipDatasId.containsKey(buddyUri)) { // long dataId = csipDatasId.get(buddyUri); int presence = StatusUpdates.OFFLINE; String correspondingPresence = ""; switch (presStatus) { case ONLINE: presence = StatusUpdates.AVAILABLE; correspondingPresence = ctxt.getString(R.string.online); break; case OFFLINE: presence = StatusUpdates.INVISIBLE; correspondingPresence = ctxt.getString(R.string.offline); break; case AWAY: presence = StatusUpdates.AWAY; correspondingPresence = ctxt.getString(R.string.away); break; case BUSY: presence = StatusUpdates.DO_NOT_DISTURB; correspondingPresence = ctxt.getString(R.string.busy); break; default: break; } if(TextUtils.isEmpty(statusText)) { statusText = correspondingPresence; } ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>(); ContentProviderOperation.Builder builder = ContentProviderOperation .newInsert(StatusUpdates.CONTENT_URI); // builder.withValue(StatusUpdates.DATA_ID, dataId); builder.withValue(StatusUpdates.CUSTOM_PROTOCOL, SipManager.PROTOCOL_CSIP); builder.withValue(StatusUpdates.PROTOCOL, CommonDataKinds.Im.PROTOCOL_CUSTOM); builder.withValue(StatusUpdates.IM_HANDLE, buddyUri); builder.withValue(StatusUpdates.STATUS, statusText); builder.withValue(StatusUpdates.PRESENCE, presence); if (Compatibility.isCompatible(11)) { builder.withValue(StatusUpdates.CHAT_CAPABILITY, StatusUpdates.CAPABILITY_HAS_VOICE); } String pkg = PreferencesProviderWrapper.getCurrentPackageInfos(ctxt).applicationInfo.packageName; builder.withValue(StatusUpdates.STATUS_RES_PACKAGE, pkg); builder.withValue(StatusUpdates.STATUS_LABEL, R.string.app_name); builder.withValue(StatusUpdates.STATUS_ICON, R.drawable.ic_launcher_phone); builder.withValue(StatusUpdates.STATUS_TIMESTAMP, System.currentTimeMillis()); operationList.add(builder.build()); /* * builder = ContentProviderOperation.newUpdate(Data.CONTENT_URI); * builder.withSelection(Data._ID + " = '" + dataId + "'", null); * builder.withValue(CommonDataKinds.Im.PRESENCE, presence); * operationList.add(builder.build()); */ try { ctxt.getContentResolver().applyBatch(ContactsContract.AUTHORITY, operationList); } catch (RemoteException e) { Log.e(THIS_FILE, "Can't update status", e); } catch (OperationApplicationException e) { Log.e(THIS_FILE, "Can't update status", e); } // } /* * Uri dataUri = Data.CONTENT_URI; String dataQuery = Data.MIMETYPE * + "='" + CommonDataKinds.Im.CONTENT_ITEM_TYPE + "' " + " AND " + * CommonDataKinds.Im.PROTOCOL + "=" + * CommonDataKinds.Im.PROTOCOL_CUSTOM + " AND " + * CommonDataKinds.Im.CUSTOM_PROTOCOL + "='csip'" + " AND " + * CommonDataKinds.Im.DATA + "=?"; ContentValues presenceValues = * new ContentValues(); int val = CommonDataKinds.Im.OFFLINE; switch * (presStatus) { case ONLINE: val = CommonDataKinds.Im.AVAILABLE; * break; case OFFLINE: // TODO -- is that good? val = * CommonDataKinds.Im.AWAY; break; default: break; } * presenceValues.put(CommonDataKinds.Im.PRESENCE_STATUS, val); * ctxt.getContentResolver().update(dataUri, presenceValues, * dataQuery, new String[] {buddyUri}); */ } } @Override public ContactInfo getContactInfo(Context context, Cursor cursor) { ContactInfo ci = new ContactInfo(); // Get values ci.displayName = cursor.getString(cursor.getColumnIndex(Contacts.DISPLAY_NAME)); ci.contactId = cursor.getLong(cursor.getColumnIndex(Contacts._ID)); ci.callerInfo.contactContentUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, ci.contactId); ci.callerInfo.photoId = cursor.getLong(cursor.getColumnIndex(Contacts.PHOTO_ID)); int photoUriColIndex = cursor.getColumnIndex(Contacts.PHOTO_ID); ci.status = cursor.getString(cursor.getColumnIndex(Contacts.CONTACT_STATUS)); ci.presence = cursor.getInt(cursor.getColumnIndex(Contacts.CONTACT_PRESENCE)); if (photoUriColIndex >= 0) { String photoUri = cursor.getString(photoUriColIndex); if (!TextUtils.isEmpty(photoUri)) { ci.callerInfo.photoUri = Uri.parse(photoUri); } } ci.hasPresence = !TextUtils.isEmpty(ci.status); return ci; } public int getPresenceIconResourceId(int presence) { return StatusUpdates.getPresenceIconResourceId(presence); } @Override public Intent getAddContactIntent(String displayName, String csipUri) { Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT, Contacts.CONTENT_URI); intent.setType(Contacts.CONTENT_ITEM_TYPE); if (!TextUtils.isEmpty(displayName)) { intent.putExtra(Insert.NAME, displayName); } if (!TextUtils.isEmpty(csipUri)) { ArrayList<ContentValues> data = new ArrayList<ContentValues>(); ContentValues csipProto = new ContentValues(); csipProto.put(Data.MIMETYPE, CommonDataKinds.Im.CONTENT_ITEM_TYPE); csipProto.put(CommonDataKinds.Im.PROTOCOL, CommonDataKinds.Im.PROTOCOL_CUSTOM); csipProto.put(CommonDataKinds.Im.CUSTOM_PROTOCOL, SipManager.PROTOCOL_CSIP); csipProto.put(CommonDataKinds.Im.DATA, SipUri.getCanonicalSipContact(csipUri, false)); data.add(csipProto); intent.putParcelableArrayListExtra(Insert.DATA, data); } return intent; } @Override public Intent getViewContactIntent(Long contactId) { Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId)); return intent; } @Override public Cursor getGroups(Context context) { Uri searchUri = Groups.CONTENT_URI; String[] projection = new String[] { Groups._ID, Groups.TITLE /* No need of as title, since already title */ }; return context.getContentResolver().query(searchUri, projection, null, null, Groups.TITLE + " ASC"); } @Override public boolean insertOrUpdateCSipUri(Context ctxt, long contactId, String uri) { ContentResolver cr = ctxt.getContentResolver(); long rawContactId = -1; Cursor c = cr.query(RawContacts.CONTENT_URI, new String[]{RawContacts._ID}, RawContacts.CONTACT_ID + "=?", new String[]{String.valueOf(contactId)}, null); try { if(c.moveToNext()) { rawContactId = c.getLong(c.getColumnIndex(RawContacts._ID)); } } catch (Exception e) { Log.e(THIS_FILE, "Error while looping on contacts", e); } finally { c.close(); } if(rawContactId != -1) { String csipUri = SipUri.getCanonicalSipContact(uri, false); // First try update ContentValues cv = new ContentValues(); cv.put(CommonDataKinds.Im.DATA, csipUri); Cursor cs = cr.query(Data.CONTENT_URI, new String[] {CommonDataKinds.Im._ID}, CommonDataKinds.Im.MIMETYPE + "=?" + " AND " + CommonDataKinds.Im.PROTOCOL + "=?" + " AND " + CommonDataKinds.Im.CUSTOM_PROTOCOL + "=?" + " AND " + CommonDataKinds.Im.RAW_CONTACT_ID + "=?", new String [] { CommonDataKinds.Im.CONTENT_ITEM_TYPE, Integer.toString(CommonDataKinds.Im.PROTOCOL_CUSTOM), SipManager.PROTOCOL_CSIP, Long.toString(rawContactId) }, null); if(cs != null) { int count = cs.getCount(); cs.close(); if(count > 0) { int updated = cr.update(Data.CONTENT_URI, cv, CommonDataKinds.Im.MIMETYPE + "=?" + " AND " + CommonDataKinds.Im.PROTOCOL + "=?" + " AND " + CommonDataKinds.Im.CUSTOM_PROTOCOL + "=?" + " AND " + CommonDataKinds.Im.RAW_CONTACT_ID + "=?", new String [] { CommonDataKinds.Im.CONTENT_ITEM_TYPE, Integer.toString(CommonDataKinds.Im.PROTOCOL_CUSTOM), SipManager.PROTOCOL_CSIP, Long.toString(rawContactId) }); Log.d(THIS_FILE, "Updated : " + updated); }else { cv.put(CommonDataKinds.Im.MIMETYPE, CommonDataKinds.Im.CONTENT_ITEM_TYPE); cv.put(CommonDataKinds.Im.PROTOCOL, CommonDataKinds.Im.PROTOCOL_CUSTOM); cv.put(CommonDataKinds.Im.CUSTOM_PROTOCOL, SipManager.PROTOCOL_CSIP); cv.put(CommonDataKinds.Im.RAW_CONTACT_ID, rawContactId); Uri insertedUri = cr.insert(Data.CONTENT_URI, cv); if(insertedUri == null) { return false; } Log.d(THIS_FILE, "Inserted : " + insertedUri.toString()); } return true; } } return false; } }