package xmpp.client.account.contactsync; import java.util.ArrayList; import java.util.HashMap; import xmpp.client.Constants; import xmpp.client.R; import xmpp.client.service.handlers.SimpleMessageHandler; import xmpp.client.service.handlers.SimpleMessageHandlerClient; import xmpp.client.service.user.User; import xmpp.client.service.user.contact.Contact; import xmpp.client.service.user.contact.ContactList; import android.accounts.Account; import android.accounts.OperationCanceledException; import android.app.Service; import android.content.ComponentName; import android.content.ContentProviderClient; import android.content.ContentProviderOperation; import android.content.ContentUris; import android.content.Context; import android.content.Intent; import android.content.OperationApplicationException; import android.content.ServiceConnection; import android.content.SyncResult; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.os.Parcelable; import android.os.RemoteException; import android.provider.BaseColumns; import android.provider.ContactsContract; import android.provider.ContactsContract.Data; import android.provider.ContactsContract.RawContacts; import android.provider.ContactsContract.RawContacts.Entity; import android.util.Log; public class ContactsSyncAdapterService extends Service implements SimpleMessageHandlerClient, Constants { private static final String TAG = ContactsSyncAdapterService.class .getName(); private static ContactsSyncAdapter mSyncAdapter = null; boolean mIsBound; Messenger mMessenger = new Messenger(new SimpleMessageHandler(this));; Messenger mService; Contact mContactMe; private final ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName className, IBinder service) { mService = new Messenger(service); try { checkState(); } catch (final Exception e) { Log.e(TAG, "ServiceConnection.onServiceConnected", e); } } @Override public void onServiceDisconnected(ComponentName className) { mService = null; } }; public ContactsSyncAdapterService() { super(); } void checkState() { if (mService != null) { final Message msg = Message.obtain(null, Constants.SIG_IS_ONLINE); msg.replyTo = mMessenger; try { mService.send(msg); } catch (final RemoteException e) { Log.e(TAG, "checkState", e); } } } private String combineDatabaseContact(Contact contact) { final User user0 = contact.getUsers().get(0); if (user0.getUserContact() == null) { return null; } final Long id0 = Long.parseLong(Uri.parse(user0.getUserContact()) .getLastPathSegment()); final ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); for (final User user : contact.getUsers()) { if (user.getUserContact() == null || user.getUserContact().equals(user0.getUserContact())) { continue; } final Long id = Long.parseLong(Uri.parse(user.getUserContact()) .getLastPathSegment()); ops.add(ContentProviderOperation .newUpdate( ContactsContract.AggregationExceptions.CONTENT_URI) .withValue( Data.MIMETYPE, ContactsContract.AggregationExceptions.CONTENT_ITEM_TYPE) .withValue( ContactsContract.AggregationExceptions.RAW_CONTACT_ID1, id0) .withValue( ContactsContract.AggregationExceptions.RAW_CONTACT_ID2, id) .withValue( ContactsContract.AggregationExceptions.TYPE, ContactsContract.AggregationExceptions.TYPE_KEEP_TOGETHER) .build()); } try { getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops); } catch (final Exception e) { Log.e(TAG, "combineDatabaseContact", e); } return user0.getUserContact(); } void doBindService() { bindService(new Intent(this, xmpp.client.service.Service.class), mConnection, Context.BIND_AUTO_CREATE); mIsBound = true; } private synchronized void doNotify() { notify(); } public void doSync(ContactList p) { final HashMap<String, Long> localContacts = new HashMap<String, Long>(); final Uri rawContactUri = RawContacts.CONTENT_URI .buildUpon() .appendQueryParameter(RawContacts.ACCOUNT_NAME, mContactMe.getUserLogin()) .appendQueryParameter(RawContacts.ACCOUNT_TYPE, ACCOUNT_TYPE) .build(); final Cursor c1 = getContentResolver().query(rawContactUri, new String[] { BaseColumns._ID, RawContacts.SOURCE_ID }, null, null, null); while (c1.moveToNext()) { localContacts.put(c1.getString(1), c1.getLong(0)); } c1.close(); final ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); for (final Contact contact : p) { for (final User user : contact.getUsers()) { if (user.isInvisible()) { break; } if (!localContacts.containsKey(user.getUserLogin())) { final int back = insertDatabaseUser(ops, user); } else { updateDatabaseUser(ops, localContacts.get(user.getUserLogin()), user); } } } try { getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops); } catch (final RemoteException e) { Log.e(TAG, "sync", e); } catch (final OperationApplicationException e) { Log.e(TAG, "sync", e); } } void doUnbindService() { if (mIsBound) { unbindService(mConnection); mIsBound = false; } } private synchronized void doWait() throws InterruptedException { wait(); } int getProtocol(User user) { if (user.isTransported()) { switch (user.getTransportType()) { case User.TRANSPORT_ICQ: return ContactsContract.CommonDataKinds.Im.PROTOCOL_ICQ; case User.TRANSPORT_AIM: return ContactsContract.CommonDataKinds.Im.PROTOCOL_AIM; case User.TRANSPORT_MSN: return ContactsContract.CommonDataKinds.Im.PROTOCOL_MSN; } } return ContactsContract.CommonDataKinds.Im.PROTOCOL_JABBER; } private ContactsSyncAdapter getSyncAdapter() { if (mSyncAdapter == null) { mSyncAdapter = new ContactsSyncAdapter(this); } return mSyncAdapter; } @Override public void handleMessage(Message msg) { try { final Bundle b = msg.getData(); switch (msg.what) { case Constants.SIG_IS_ONLINE: b.setClassLoader(Contact.class.getClassLoader()); mContactMe = b.getParcelable("contact"); final Message msg2 = Message.obtain(null, Constants.SIG_ROSTER_GET_CONTACTS); msg2.replyTo = mMessenger; mService.send(msg2); break; case Constants.SIG_ROSTER_GET_CONTACTS: b.setClassLoader(ContactList.class.getClassLoader()); if (b.containsKey("contacts")) { final Parcelable p = b.getParcelable("contacts"); if (p instanceof ContactList) { doSync((ContactList) p); } } doNotify(); break; case Constants.SIG_ROSTER_GET_CONTACTS_ERROR: case Constants.SIG_IS_NOT_ONLINE: doNotify(); break; } } catch (final Exception e) { Log.e(TAG, "handleMessage", e); } } private int insertDatabaseUser(ArrayList<ContentProviderOperation> ops, User user) { final int rawContactInsertIndex = ops.size(); ops.add(ContentProviderOperation.newInsert(RawContacts.CONTENT_URI) .withValue(RawContacts.ACCOUNT_TYPE, ACCOUNT_TYPE) .withValue(RawContacts.ACCOUNT_NAME, mContactMe.getUserLogin()) .withValue(RawContacts.SOURCE_ID, user.getUserLogin()).build()); ops.add(ContentProviderOperation .newInsert(Data.CONTENT_URI) .withValueBackReference(Data.RAW_CONTACT_ID, rawContactInsertIndex) .withValue( Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) .withValue( ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, user.getDisplayName()).build()); ops.add(ContentProviderOperation .newInsert(Data.CONTENT_URI) .withValueBackReference(Data.RAW_CONTACT_ID, rawContactInsertIndex) .withValue(Data.MIMETYPE, ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE) .withValue(ContactsContract.CommonDataKinds.Im.PROTOCOL, getProtocol(user)) .withValue(ContactsContract.CommonDataKinds.Im.DATA, user.getNiceUserLogin()).build()); ops.add(ContentProviderOperation .newInsert(Data.CONTENT_URI) .withValueBackReference(Data.RAW_CONTACT_ID, rawContactInsertIndex) .withValue(Data.MIMETYPE, ACCOUNT_MIME) .withValue(Data.DATA1, user.getUserLogin()) .withValue(Data.DATA2, "Chat " + user.getUserLogin()).build()); return rawContactInsertIndex; } @Override public boolean isReady() { return true; } @Override public IBinder onBind(Intent intent) { IBinder ret = null; ret = getSyncAdapter().getSyncAdapterBinder(); return ret; } void performSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) throws OperationCanceledException { doBindService(); try { doWait(); } catch (final InterruptedException e) { Log.i(TAG, "performSync", e); } doUnbindService(); } void updateContact(Contact contact) { final Message msg = Message.obtain(null, Constants.SIG_UPDATE_CONTACT); final Bundle b = new Bundle(); b.putParcelable("contact", contact); msg.setData(b); msg.replyTo = mMessenger; try { mService.send(msg); } catch (final RemoteException e) { Log.e(TAG, "updateContact", e); } } private void updateDatabaseUser(ArrayList<ContentProviderOperation> ops, long rawContactID, User user) { final Uri rawContactUri = ContentUris.withAppendedId( RawContacts.CONTENT_URI, rawContactID); final Uri entityUri = Uri.withAppendedPath(rawContactUri, Entity.CONTENT_DIRECTORY); final Cursor c = getContentResolver().query( entityUri, new String[] { RawContacts.SOURCE_ID, Entity.DATA_ID, Entity.MIMETYPE, Entity.DATA1 }, null, null, null); while (c.moveToNext()) { if (!c.isNull(1) && c.getString(2).equals(ACCOUNT_MIME)) { ops.add(ContentProviderOperation .newInsert(ContactsContract.StatusUpdates.CONTENT_URI) .withValue(ContactsContract.StatusUpdates.DATA_ID, c.getLong(1)) .withValue(ContactsContract.StatusUpdates.IM_HANDLE, user.getUserLogin()) .withValue(ContactsContract.StatusUpdates.IM_ACCOUNT, mContactMe.getUserLogin()) .withValue( ContactsContract.StatusUpdates.PROTOCOL, ContactsContract.CommonDataKinds.Im.PROTOCOL_JABBER) .withValue(ContactsContract.StatusUpdates.PRESENCE, user.getUserState().getStatus()) .withValue(ContactsContract.StatusUpdates.STATUS, user.getUserState().getStatusText(this)) .withValue( ContactsContract.StatusUpdates.STATUS_RES_PACKAGE, "xmpp.client") .withValue(ContactsContract.StatusUpdates.STATUS_ICON, R.drawable.ic_launcher) .withValue(ContactsContract.StatusUpdates.STATUS_LABEL, R.string.app_name).build()); } } c.close(); } void updateUser(User user) { final Message msg = Message.obtain(null, Constants.SIG_UPDATE_USER); final Bundle b = new Bundle(); b.putParcelable("user", user); msg.setData(b); msg.replyTo = mMessenger; try { mService.send(msg); } catch (final RemoteException e) { Log.e(TAG, "updateUser", e); } } }