package com.stxnext.management.android.sync; import java.util.ArrayList; import java.util.List; import android.annotation.SuppressLint; import android.app.IntentService; import android.content.ContentProviderOperation; import android.content.ContentProviderResult; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; import android.content.OperationApplicationException; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.Bitmap.CompressFormat; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.RemoteException; import android.provider.ContactsContract; import android.provider.ContactsContract.CommonDataKinds.Phone; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; import android.util.Log; import android.support.v4.app.LoaderManager; import com.google.common.base.Strings; import com.stxnext.management.android.dto.local.IntranetUser; import com.stxnext.management.android.ui.dependencies.BitmapUtils; public class ContactSyncManager implements LoaderManager.LoaderCallbacks<Cursor> { public static final String CONTACT_LABEL = "STXNext"; public interface SyncManagerListener { public void onPhoneQueryComplete(List<ProviderPhone> phones); } private List<SyncManagerListener> listeners = new ArrayList<ContactSyncManager.SyncManagerListener>(); private Context context; public void addListener(SyncManagerListener listeners) { this.listeners.add(listeners); } private ContactSyncManager(){}; public ContactSyncManager(IntentService service) { this.context = service; }; public <T extends Context & SyncManagerListener> ContactSyncManager(T context) { this.context = context; this.listeners.add(context); } public boolean removeListener(SyncManagerListener listener) { boolean hasObject = this.listeners.contains(listener); if (hasObject) this.listeners.remove(listeners); return hasObject; } private void rollBackMultipleMerge(List<ProviderPhone> phones) { // TODO: } /** * This should be done only in thread if you want the app to be responsive * @param phones * @param user * @return */ public boolean mergeContacts(List<ProviderPhone> phones, IntranetUser user) { ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); for (ProviderPhone phone : phones) { try { if (phone.getContactId() == null) { ops.add(ContentProviderOperation .newInsert(ContactsContract.RawContacts.CONTENT_URI) .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, ContactsContract.CommonDataKinds.Email.TYPE_CUSTOM) .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, phone.getDisplayName()) .build()); ops.add(ContentProviderOperation .newInsert(ContactsContract.Data.CONTENT_URI) .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) .withValue( ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) .withValue( ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, phone.getDisplayName()) .build()); ops.add(ContentProviderOperation .newInsert(ContactsContract.Data.CONTENT_URI) .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE) .withValue(ContactsContract.CommonDataKinds.Email.DATA, user.getEmail()) .withValue(ContactsContract.CommonDataKinds.Email.TYPE, ContactsContract.CommonDataKinds.Email.TYPE_CUSTOM) .build()); ContentProviderResult[] result = context.getContentResolver().applyBatch( ContactsContract.AUTHORITY, ops); ops.clear(); Uri contactUri = result[0].uri; String stringId = contactUri.getPathSegments().get(1); Long id = Long.parseLong(stringId); phone.setContactId(id); } //else { ops.add(ContentProviderOperation .newDelete(ContactsContract.Data.CONTENT_URI) .withSelection( Phone.LABEL + " LIKE ? AND " + ContactsContract.Data.RAW_CONTACT_ID + "=?", new String[] { "%" + CONTACT_LABEL + " Mobile" + "%", String.valueOf(phone.getContactId().intValue()) }).build()); ContentProviderOperation.Builder operation = ContentProviderOperation .newInsert(ContactsContract.Data.CONTENT_URI) .withValue(ContactsContract.Data.RAW_CONTACT_ID, phone.getContactId().intValue()) .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone.getNumberToUpdate()) .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM) .withValue(Phone.LABEL, CONTACT_LABEL + " Mobile"); ops.add(operation.build()); Bitmap bmp = BitmapUtils.getTempBitmap(context, user.getId().toString()); if(bmp!=null){ byte[] imageBytes = BitmapUtils.bitmapToBytes(bmp, CompressFormat.PNG); ContentValues values = new ContentValues(); values.put(ContactsContract.Data.RAW_CONTACT_ID, phone.getContactId().intValue()); values.put(ContactsContract.Data.IS_SUPER_PRIMARY, 1); values.put(ContactsContract.CommonDataKinds.Photo.PHOTO, imageBytes); values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE); context.getContentResolver().insert( ContactsContract.Data.CONTENT_URI, values); } ContentProviderResult[] results = context.getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops); //} String b=""; } catch (RemoteException e) { Log.e("", "SAVING ERROR", e); } catch (OperationApplicationException e) { Log.e("", "SAVING ERROR", e); } } return true; } boolean loaderInitialized; boolean activeQuery; public List<ProviderPhone> query(String phoneTerm, String nameTerm){ SqlArgs args = resolveSqlParams(phoneTerm, nameTerm); Cursor c = context.getContentResolver().query(Phone.CONTENT_URI, PROJECTION, args.selection, args.params, null); return resolvePhonesFromCursor(c); } public void launchQueryAsync(LoaderManager loader, String phoneTerm, String nameTerm) { activeQuery = true; mNameSearchQuery = nameTerm; mPhoneSearchQuery = phoneTerm; if (!loaderInitialized) { loader.initLoader(0, null, this); loaderInitialized = true; } else { loader.restartLoader(0, null, this); } } private static final String PhoneNameColumn = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ? Phone.DISPLAY_NAME_PRIMARY : Phone.DISPLAY_NAME; @SuppressLint("InlinedApi") private static final String[] PROJECTION = { Phone._ID, Phone.LOOKUP_KEY, Phone.RAW_CONTACT_ID, Phone.TYPE, PhoneNameColumn, Phone.NUMBER, Phone.LABEL }; private String mNameSearchQuery; private String mPhoneSearchQuery; Uri mSelectedContactUri; @Override public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) { SqlArgs args = resolveSqlParams(mPhoneSearchQuery, mNameSearchQuery); return new CursorLoader( context, Phone.CONTENT_URI, PROJECTION, args.selection, args.params, null); } private SqlArgs resolveSqlParams(String phone, String name){ List<String> selectionArgs = new ArrayList<String>(); String SELECTION = ""; if (!Strings.isNullOrEmpty(name)) { SELECTION += PhoneNameColumn + " LIKE ?"; selectionArgs.add("%" + name + "%"); } if (!Strings.isNullOrEmpty(phone)) { if (selectionArgs.size() > 0) { SELECTION = SELECTION + " OR "; } SELECTION += "(" + Phone.NUMBER + " LIKE ? AND " + Phone.LABEL + " LIKE ?)"; selectionArgs.add("%" + phone + "%"); selectionArgs.add("%" + CONTACT_LABEL + "%"); } String[] array = new String[selectionArgs.size()]; selectionArgs.toArray(array); SqlArgs args = new SqlArgs(SELECTION, array); return args; } List<ProviderPhone> resolvePhonesFromCursor(Cursor cursor){ List<ProviderPhone> phones = new ArrayList<ProviderPhone>(); if (cursor.getCount() > 0) { int columnName = cursor.getColumnIndex(PhoneNameColumn); int columnNumber = cursor.getColumnIndex(Phone.NUMBER); int columnContactId = cursor.getColumnIndex(Phone.RAW_CONTACT_ID); int columnType = cursor.getColumnIndex(Phone.TYPE); int columnId = cursor.getColumnIndex(Phone._ID); int columnLabel = cursor.getColumnIndex(Phone.LABEL); while (cursor.moveToNext()) { ProviderPhone phone = new ProviderPhone(); if (!cursor.isNull(columnName)) phone.setDisplayName(cursor.getString(columnName)); if (!cursor.isNull(columnNumber)) phone.setPhoneNumber(cursor.getString(columnNumber)); if (!cursor.isNull(columnId)) phone.setId(cursor.getLong(columnId)); if (!cursor.isNull(columnContactId)) phone.setContactId(cursor.getLong(columnContactId)); if (!cursor.isNull(columnType)) phone.setType(cursor.getString(columnContactId)); if (!cursor.isNull(columnLabel)) phone.setLabel(cursor.getString(columnLabel)); phones.add(phone); } } return phones; } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { if (!activeQuery) return; List<ProviderPhone> phones = resolvePhonesFromCursor(cursor); for (SyncManagerListener listener : listeners) { listener.onPhoneQueryComplete(phones); } activeQuery = false; } @Override public void onLoaderReset(Loader<Cursor> loader) { // TODO Auto-generated method stub } private class SqlArgs{ public String selection; public String[] params; public SqlArgs(String selection, String[] params){ this.selection = selection; this.params = params; } } }