/* * CDDL HEADER START * * The contents of this file are subject to the terms of the Common Development * and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at * src/com/vodafone360/people/VODAFONE.LICENSE.txt or * http://github.com/360/360-Engine-for-Android * See the License for the specific language governing permissions and * limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each file and * include the License file at src/com/vodafone360/people/VODAFONE.LICENSE.txt. * If applicable, add the following below this CDDL HEADER, with the fields * enclosed by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * Copyright 2010 Vodafone Sales & Services Ltd. All rights reserved. * Use is subject to license terms. */ package com.vodafone360.people.engine.contactsync; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import com.vodafone360.people.Settings; import com.vodafone360.people.database.DatabaseHelper; import com.vodafone360.people.database.tables.ContactsTable; import com.vodafone360.people.database.tables.StateTable; import com.vodafone360.people.datatypes.BaseDataType; import com.vodafone360.people.datatypes.Contact; import com.vodafone360.people.datatypes.ContactChanges; import com.vodafone360.people.datatypes.ContactDetail; import com.vodafone360.people.datatypes.VCardHelper; import com.vodafone360.people.engine.BaseEngine; import com.vodafone360.people.engine.contactsync.SyncStatus.Task; import com.vodafone360.people.engine.contactsync.SyncStatus.TaskStatus; import com.vodafone360.people.service.ServiceStatus; import com.vodafone360.people.service.agent.NetworkAgent; import com.vodafone360.people.service.io.QueueManager; import com.vodafone360.people.service.io.ResponseQueue.DecodedResponse; import com.vodafone360.people.service.io.api.Contacts; import com.vodafone360.people.utils.LogUtils; /** * Handles download of contacts from People server. */ public class DownloadServerContacts extends BaseSyncProcessor { /** * Helper constant for converting between milliseconds and nanoseconds. */ private static final long NANOSECONDS_IN_MS = 1000000L; /** * Enumeration which holds the various states for the download server * processor. */ public enum InternalState { INITIALISING, FETCHING_SERVER_ID_LIST, FETCHING_FIRST_PAGE, FETCHING_NEXT_BATCH } /** * The maximum number of contacts that should be sent by the server for each * page request. */ protected static final int MAX_DOWN_PAGE_SIZE = 25; /** * The number of pages that should be requested in a single RPG request * (using batching). */ private static final int MAX_PAGES_PER_BATCH = 1; /** * Maximum number of database updates for each engine run (to ensure the * processor doesn't block the worker thread for too long). */ private static final int MAX_CONTACT_CHANGES_PER_PAGE = 5; /** * Timeout between each run of the engine. Normally set to 0 to ensure the * engine will run as soon as possible. This is used to allow other engines * to be run between lengthy sync operations. */ private static final long TIMEOUT_BETWEEN_PAGES_MS = 0; /** * Set to true for extra logcat trace (debug only) */ private static final boolean EXTRA_DEBUG_TRACE = true; /** * This will be set to the current revision of the contacts in the NowPlus * database. For first time sync will be set to 0. The current revision * number is persisted in the database. */ private Integer mFromRevision = null; /** * The revision that we require from the server. For the first page this is * set to -1 to indicate we want the latest revision. For subsequent pages * will be set to the version anchor received from the server (to ensure all * pages fetched are for the same version). */ private Integer mToRevision = null; /** * Total number of pages we are expecting from the server. This is received * as part of the server response for the first page. */ private Integer mTotalNoOfPages = null; /** * First page number requested in the batch. The remaining page numbers will * follow from this sequentially. */ private int mBatchFirstPageNo; /** * Total number of pages requested by batch. */ private int mBatchNoOfPages; /** * Number of pages received so far from the batch */ private int mBatchPageReceivedCount; /** * Total number of pages done (used with {@link #mTotalNoOfPages} for * calculating progress) */ private int mNoOfPagesDone = 0; /*** * Number of contacts contained in the last page. This is only known once * the last page have been received and is a major flaw in the protocol as * it makes it difficult to calculate sync progress information. */ private int mLastPageSize = -1; /** * Set of contact server IDs from the NowPlus database. * Used to quickly determine if a contact received from the server is a new * or modified contact. */ private final HashSet<Long> mServerIdSet = new HashSet<Long>(); /** * List of all contacts received from server which are also present in the * local database. This is a list of all contacts received from the server * excluding new contacts. */ private final ArrayList<Contact> mContactsChangedList = new ArrayList<Contact>(); /** * List of all contacts received from the server that need to be added to * the database. */ private final ArrayList<Contact> mAddContactList = new ArrayList<Contact>(); /** * List of all contacts received from the server that need to be modified in * the database. */ private final ArrayList<Contact> mModifyContactList = new ArrayList<Contact>(); /** * List of all contacts received from the server that need to be deleted * from the database. */ private final ArrayList<ContactsTable.ContactIdInfo> mDeleteContactList = new ArrayList<ContactsTable.ContactIdInfo>(); /** * List of all contact details received from the server that need to be * added to the local database. */ private final ArrayList<ContactDetail> mAddDetailList = new ArrayList<ContactDetail>(); /** * List of all contact details received from the server that need to be * modified in the local database. */ private final ArrayList<ContactDetail> mModifyDetailList = new ArrayList<ContactDetail>(); /** * List of all contact details received from the server that need to be * deleted from the local database. */ private final ArrayList<ContactDetail> mDeleteDetailList = new ArrayList<ContactDetail>(); /** * Maintains all the request IDs when sending a batch of requests. */ protected final Map<Integer, Integer> mPageReqIds = new HashMap<Integer, Integer>(); /** * Flag indicating that there is some data in either the * {@link #mAddContactList}, {@link #mModifyContactList}, * {@link #mDeleteContactList}, {@link #mAddDetailList}, * {@link #mModifyDetailList} or {@link #mDeleteDetailList} list. */ private boolean mSyncDataPending = false; /** * Current internal state of the processor. */ protected InternalState mInternalState = InternalState.INITIALISING; /** * Used to monitor how much time is spent reading or writing to the NowPlus * database. This is for displaying logcat information which can be used * when profiling the contact sync engine. */ private long mDbSyncTime = 0; /** * Set to true when there are no more pages to fetch from the server. */ private boolean mIsComplete; /** * Counts the number of contacts added (for use when profiling) */ private long mTotalContactsAdded; /** * Processor constructor. * * @param callback Callback interface for accessing the contact sync engine. * @param db Database used for modifying contacts */ protected DownloadServerContacts(IContactSyncCallback callback, DatabaseHelper db) { super(callback, db); } /** * For debug only - to show when the garbage collector destroys the * processor */ @Override protected void finalize() { LogUtils.logD("DownloadServerContacts.finalize() - processor deleted"); } /** * Called by framework to start the processor running. Requests the first * page of contact changes from the server. */ @Override protected void doStart() { setSyncStatus(new SyncStatus(0, "", Task.DOWNLOAD_SERVER_CONTACTS)); mIsComplete = false; mSyncDataPending = false; mFromRevision = StateTable.fetchContactRevision(mDb.getReadableDatabase()); if (mFromRevision == null) { mFromRevision = 0; // Use base } mToRevision = -1; // Sync with head revision mTotalNoOfPages = null; mBatchNoOfPages = 1; mBatchFirstPageNo = 0; mBatchPageReceivedCount = 0; mDbSyncTime = 0; mNoOfPagesDone = 0; mTotalContactsAdded = 0; mInternalState = InternalState.FETCHING_SERVER_ID_LIST; long startTime = System.nanoTime(); ServiceStatus status = ContactsTable.fetchContactServerIdList(mServerIdSet, mDb.getReadableDatabase()); if (ServiceStatus.SUCCESS != status) { complete(status); } mDbSyncTime += (System.nanoTime() - startTime); setTimeout(TIMEOUT_BETWEEN_PAGES_MS); } /** * Requests first page of contact changes from the server. * * @return SUCCESS or a suitable error code. */ private ServiceStatus fetchFirstBatch() { if (NetworkAgent.getAgentState() != NetworkAgent.AgentState.CONNECTED) { return NetworkAgent.getServiceStatusfromDisconnectReason(); } mInternalState = InternalState.FETCHING_FIRST_PAGE; LogUtils.logD("DownloadServerContacts.fetchFirstBatch - from rev " + mFromRevision + ", to rev " + mToRevision + ", page size " + MAX_DOWN_PAGE_SIZE); int reqId = Contacts.getContactsChanges(getEngine(), 0, MAX_DOWN_PAGE_SIZE, mFromRevision .longValue(), mToRevision.longValue(), false); setReqId(reqId); return ServiceStatus.SUCCESS; } /** * Requests the next batch of contact change pages from the server * * @return SUCCESS or a suitable error code */ private ServiceStatus fetchNextBatch() { mBatchNoOfPages = Math.min(mTotalNoOfPages - mBatchFirstPageNo, MAX_PAGES_PER_BATCH); if (mBatchNoOfPages == 0) { return ServiceStatus.SUCCESS; } mBatchPageReceivedCount = 0; if (NetworkAgent.getAgentState() != NetworkAgent.AgentState.CONNECTED) { return NetworkAgent.getServiceStatusfromDisconnectReason(); } mPageReqIds.clear(); LogUtils.logD("DownloadServerContacts.fetchNextBatch - from rev " + mFromRevision + ", to rev " + mToRevision + ", page size " + MAX_DOWN_PAGE_SIZE); for (int i = 0; i < mBatchNoOfPages; i++) { int reqId = Contacts.getContactsChanges(getEngine(), i + mBatchFirstPageNo, MAX_DOWN_PAGE_SIZE, mFromRevision.longValue(), mToRevision.longValue(), true); mPageReqIds.put(reqId, i + mBatchFirstPageNo); } // AA: see if we can do that inside the Queue QueueManager.getInstance().fireQueueStateChanged(); return ServiceStatus.SUCCESS; } /** * Called by framework when contact sync is cancelled. Not used, the server * response will simply be ignored. */ @Override protected void doCancel() { LogUtils.logD("DownloadServerContacts.doCancel()"); } /** * Called by framework when a response is received from the server. In case * of the first page this will only be called if the request ID matches. * Processes the page and if the page is the last one in the batch, sends a * new batch of requests to the server. * * @param response from server */ @Override public void processCommsResponse(DecodedResponse resp) { Integer pageNo = 0; if (mInternalState == InternalState.FETCHING_NEXT_BATCH) { pageNo = mPageReqIds.remove(resp.mReqId); if (pageNo == null) { LogUtils.logD("DownloadServerContacts.processCommsResponse: Req ID not known"); return; } } LogUtils.logD("DownloadServerContacts.processCommsResponse() - Page " + pageNo); ServiceStatus status = BaseEngine.getResponseStatus(BaseDataType.CONTACT_CHANGES_DATA_TYPE, resp.mDataTypes); if (status == ServiceStatus.SUCCESS) { ContactChanges contactChanges = (ContactChanges)resp.mDataTypes.get(0); LogUtils.logI("DownloadServerContacts.processCommsResponse - No of contacts = " + contactChanges.mContacts.size()); if (contactChanges.mContacts.size() == 0 && pageNo > 0) { LogUtils.logW("DownloadServerContacts.processCommsResponse - " + "Error a page with 0 contacts was received"); LogUtils.logW("DownloadServerContacts.processCommsResponse - Changes = " + contactChanges); } mBatchPageReceivedCount++; if (mBatchPageReceivedCount == mBatchNoOfPages) { mLastPageSize = contactChanges.mContacts.size(); // Page batch is now complete if (mInternalState == InternalState.FETCHING_FIRST_PAGE) { mTotalNoOfPages = contactChanges.mNumberOfPages; mToRevision = contactChanges.mVersionAnchor; mInternalState = InternalState.FETCHING_NEXT_BATCH; } mBatchFirstPageNo += mBatchNoOfPages; if (mTotalNoOfPages == null || mToRevision == null) { complete(ServiceStatus.ERROR_COMMS_BAD_RESPONSE); return; } if (mBatchFirstPageNo < mTotalNoOfPages.intValue()) { status = fetchNextBatch(); if (ServiceStatus.SUCCESS != status) { complete(status); return; } } else { mIsComplete = true; } } status = syncContactChangesPage(contactChanges); mNoOfPagesDone++; LogUtils.logI("DownloadServerContacts.processCommsResponse() - Contact changes page " + mNoOfPagesDone + "/" + mTotalNoOfPages + " received, no of contacts = " + contactChanges.mContacts.size()); if (ServiceStatus.SUCCESS != status) { LogUtils.logE("DownloadServerContacts.processCommsResponse() - Error syncing page: " + status); complete(status); return; } if (mIsComplete && mContactsChangedList.size() == 0 && !mSyncDataPending) { downloadSyncSuccessful(); } return; } complete(status); } /*** * Check the given contact to see if it has a valid name, and if so send it * to be shown in the contacts sync progress UI. * * @param contact Contact which has been downloaded. * @param incOfCurrentPage Number of contacts that have so far been * downloaded for the current page. */ private void updateProgressUi(Contact contact, int incOfCurrentPage) { String name = ""; if (contact != null) { ContactDetail contactDetail = contact.getContactDetail(ContactDetail.DetailKeys.VCARD_NAME); if (contactDetail != null) { VCardHelper.Name vCardHelperName = contactDetail.getName(); if (vCardHelperName != null) { name = vCardHelperName.toString(); } } } int totalNumberOfContacts = ((mTotalNoOfPages*10)-1)*MAX_DOWN_PAGE_SIZE/10; if (mLastPageSize != -1) { totalNumberOfContacts = (mTotalNoOfPages-1) * MAX_DOWN_PAGE_SIZE + mLastPageSize; } int progress = (incOfCurrentPage + (mNoOfPagesDone * MAX_DOWN_PAGE_SIZE))*100 / totalNumberOfContacts; setSyncStatus(new SyncStatus(progress, name, Task.DOWNLOAD_SERVER_CONTACTS, TaskStatus.RECEIVED_CONTACTS, incOfCurrentPage + mNoOfPagesDone * MAX_DOWN_PAGE_SIZE, totalNumberOfContacts)); } /** * Processes the response from the server. First separates new contacts from * existing ones, this is to optimise adding new contacts during first time * sync. * * @param changes The contact change information received from the server * @return SUCCESS or a suitable error code */ private ServiceStatus syncContactChangesPage(ContactChanges changes) { mContactsChangedList.clear(); int i = 0; for (Contact contact : changes.mContacts) { i += 1; updateProgressUi(contact, i); if (Settings.ENABLED_CONTACTS_SYNC_TRACE) { if (contact.details.size() == 0 || contact.deleted != null) { LogUtils.logD("Contact In: " + contact.contactID + ", Del: " + contact.deleted + ", details: " + contact.details.size()); } for (ContactDetail detail : contact.details) { LogUtils.logD("Contact In: " + contact.contactID + ", Detail: " + detail.key + ", " + detail.unique_id + ", " + detail.keyType + " = " + detail.value + ", del = " + detail.deleted); } } if (mServerIdSet.contains(contact.contactID)) { mContactsChangedList.add(contact); } else { if (contact.deleted != null && contact.deleted.booleanValue()) { if (EXTRA_DEBUG_TRACE) { LogUtils.logD("DownloadServerContacts.syncDownContact() " + "Delete Contact (nothing to do) " + contact.contactID); } } else if (contact.details.size() > 0) { if (EXTRA_DEBUG_TRACE) { LogUtils.logD("DownloadServerContacts.syncDownContact() Adding " + contact.contactID); } mAddContactList.add(contact); mSyncDataPending = true; } else { if (EXTRA_DEBUG_TRACE) { LogUtils.logD("DownloadServerContacts.syncDownContact() Empty " + contact.contactID); } } } } if (mSyncDataPending || mContactsChangedList.size() > 0) { setTimeout(TIMEOUT_BETWEEN_PAGES_MS); } return ServiceStatus.SUCCESS; } /** * Processes next page of modified contacts in the * {@link #mContactsChangedList}. */ private void processContactChangesNextPage() { final int count = Math.min(mContactsChangedList.size(), MAX_CONTACT_CHANGES_PER_PAGE); for (int i = 0; i < count; i++) { final Contact srcContact = mContactsChangedList.get(0); mContactsChangedList.remove(0); final Contact destContact = new Contact(); long startTime = System.nanoTime(); ServiceStatus status = mDb.fetchContactByServerId(srcContact.contactID, destContact); if (ServiceStatus.SUCCESS != status) { LogUtils .logE("DownloadServerContacts.processContactChangesNextPage() - Error syncing page: " + status); complete(status); return; } mDbSyncTime += (System.nanoTime() - startTime); status = syncDownContact(srcContact, destContact); if (ServiceStatus.SUCCESS != status) { LogUtils .logE("DownloadServerContacts.processContactChangesNextPage() - Error syncing page: " + status); complete(status); return; } } if (mContactsChangedList.size() == 0 && !mSyncDataPending) { if (mIsComplete) { downloadSyncSuccessful(); } return; } setTimeout(TIMEOUT_BETWEEN_PAGES_MS); } /** * Modifies a contact from the NowPlus database to match the one received * from the server * * @param srcContact Contact received from the server * @param destContact Contact as stored in the database * @return SUCCESS or a suitable error code. */ private ServiceStatus syncDownContact(Contact srcContact, Contact destContact) { final long localContactId = destContact.localContactID; final Integer nativeContactId = destContact.nativeContactId; if (srcContact.deleted != null && srcContact.deleted.booleanValue()) { if (EXTRA_DEBUG_TRACE) { LogUtils.logD("DownloadServerContacts.syncDownContact - Deleting contact " + srcContact.contactID); } ContactsTable.ContactIdInfo idInfo = new ContactsTable.ContactIdInfo(); idInfo.localId = localContactId; idInfo.serverId = srcContact.contactID; idInfo.nativeId = nativeContactId; mDeleteContactList.add(idInfo); mSyncDataPending = true; return ServiceStatus.SUCCESS; } if (checkContactMods(srcContact, destContact)) { if (EXTRA_DEBUG_TRACE) { LogUtils.logD("DownloadServerContacts.syncDownContact - Modifying contact " + srcContact.contactID); } srcContact.localContactID = localContactId; srcContact.nativeContactId = nativeContactId; mModifyContactList.add(srcContact); mSyncDataPending = true; } if (srcContact.details == null) { if (EXTRA_DEBUG_TRACE) { LogUtils.logD("DownloadServerContacts.syncDownContact - No details changed " + srcContact.contactID); } return ServiceStatus.SUCCESS; } for (ContactDetail srcDetail : srcContact.details) { srcDetail.localContactID = localContactId; srcDetail.nativeContactId = nativeContactId; boolean detailFound = false; for (ContactDetail destDetail : destContact.details) { if (DatabaseHelper.doDetailsMatch(srcDetail, destDetail)) { detailFound = true; srcDetail.localDetailID = destDetail.localDetailID; srcDetail.nativeContactId = destDetail.nativeContactId; srcDetail.nativeDetailId = destDetail.nativeDetailId; srcDetail.serverContactId = srcContact.contactID; if (srcDetail.deleted != null && srcDetail.deleted.booleanValue()) { if (EXTRA_DEBUG_TRACE) { LogUtils .logD("DownloadServerContacts.syncDownContact - Deleting detail " + srcDetail.key + " contact ID = " + srcContact.contactID); } mDeleteDetailList.add(srcDetail); mSyncDataPending = true; } else if (DatabaseHelper.hasDetailChanged(destDetail, srcDetail)) { if (EXTRA_DEBUG_TRACE) { LogUtils .logD("DownloadServerContacts.syncDownContact - Modifying detail " + srcDetail.key + " local detail ID = " + srcDetail.localDetailID); } srcDetail.serverContactId = srcContact.contactID; mModifyDetailList.add(srcDetail); mSyncDataPending = true; } break; } } if (!detailFound) { if (srcDetail.deleted == null || !srcDetail.deleted.booleanValue()) { if (EXTRA_DEBUG_TRACE) { LogUtils .logD("DownloadServerContacts.syncDownContact - Adding detail " + srcDetail.key + " local contact ID = " + srcDetail.localContactID); } mAddDetailList.add(srcDetail); mSyncDataPending = true; } else { if (EXTRA_DEBUG_TRACE) { LogUtils .logD("DownloadServerContacts.syncDownContact - Detail already deleted (nothing to do) " + srcDetail.key + " local detail ID = " + srcDetail.localDetailID); } } } } return ServiceStatus.SUCCESS; } /** * Compares two contacts to check for any differences. Implemented to avoid * updating the database when nothing has changed (to improve performance). * * @param srcContact Contact received from the server * @param destContact Contact as it is in the database. * @return true if the contacts are different, false otherwise. */ private boolean checkContactMods(Contact srcContact, Contact destContact) { boolean contactModified = false; if (srcContact.friendOfMine != null && !srcContact.friendOfMine.equals(destContact.friendOfMine)) { destContact.friendOfMine = srcContact.friendOfMine; contactModified = true; } if (srcContact.gender != null && !srcContact.gender.equals(destContact.gender)) { destContact.gender = srcContact.gender; contactModified = true; } if (srcContact.sources != null) { boolean changed = false; if (srcContact.sources.size() != destContact.sources.size()) { changed = true; } else { for (int i = 0; i < srcContact.sources.size(); i++) { if (srcContact.sources.get(i) != null && !srcContact.sources.get(i).equals(destContact.sources.get(i))) { changed = true; } } } if (changed) { destContact.sources.clear(); destContact.sources.addAll(srcContact.sources); contactModified = true; } } if (srcContact.userID != null && !srcContact.userID.equals(destContact.userID)) { destContact.userID = srcContact.userID; contactModified = true; } if (srcContact.aboutMe != null && !srcContact.aboutMe.equals(destContact.aboutMe)) { destContact.aboutMe = srcContact.aboutMe; contactModified = true; } if (srcContact.groupList != null) { boolean changed = false; if (srcContact.groupList.size() != destContact.groupList.size()) { changed = true; } else { for (int i = 0; i < srcContact.groupList.size(); i++) { if (srcContact.groupList.get(i) != null && !srcContact.groupList.get(i).equals(destContact.groupList.get(i))) { changed = true; } } } if (changed) { destContact.groupList.clear(); destContact.groupList.addAll(srcContact.groupList); contactModified = true; } } if (srcContact.synctophone != null && !srcContact.synctophone.equals(destContact.synctophone)) { destContact.synctophone = srcContact.synctophone; contactModified = true; } return contactModified; } /** * Called by framework when a timeout expires. Timeouts are used by this * processor to break up lengthy database update tasks. A timeout of 0 is * normally used just to give other engines a chance to run. */ public void onTimeoutEvent() { switch (mInternalState) { case FETCHING_SERVER_ID_LIST: ServiceStatus status = fetchFirstBatch(); if (ServiceStatus.SUCCESS != status) { complete(status); return; } break; case FETCHING_FIRST_PAGE: case FETCHING_NEXT_BATCH: if (mContactsChangedList.size() > 0) { processContactChangesNextPage(); return; } if (mSyncDataPending) { if (addContactList()) { setTimeout(TIMEOUT_BETWEEN_PAGES_MS); return; } if (modifyContactList()) { setTimeout(TIMEOUT_BETWEEN_PAGES_MS); return; } if (deleteContactList()) { setTimeout(TIMEOUT_BETWEEN_PAGES_MS); return; } if (addDetailList()) { setTimeout(TIMEOUT_BETWEEN_PAGES_MS); return; } if (modifyDetailList()) { setTimeout(TIMEOUT_BETWEEN_PAGES_MS); return; } if (deleteDetailList()) { setTimeout(TIMEOUT_BETWEEN_PAGES_MS); return; } mSyncDataPending = false; if (mIsComplete) { downloadSyncSuccessful(); return; } } break; default: // do nothing. break; } } /** * Adds contacts received from the server (listed in the * {@link #mAddContactList} list) into the local database. * * @return true if > 0 contacts were added, false otherwise. */ private boolean addContactList() { if (mAddContactList.size() == 0) { return false; } LogUtils.logI("DownloadServerContacts.addContactList " + mAddContactList.size() + " contacts..."); long startTime = System.nanoTime(); ServiceStatus status = mDb.syncAddContactList(mAddContactList, false, true); if (ServiceStatus.SUCCESS != status) { complete(status); return true; } markDbChanged(); long timeDiff = System.nanoTime() - startTime; mDbSyncTime += timeDiff; LogUtils.logI("DownloadServerContacts.addContactList - time = " + (timeDiff / NANOSECONDS_IN_MS) + "ms, total = " + (mDbSyncTime / NANOSECONDS_IN_MS) + "ms"); setTimeout(TIMEOUT_BETWEEN_PAGES_MS); mTotalContactsAdded += mAddContactList.size(); mAddContactList.clear(); return true; } /** * Updates the local database with contacts received from the server (listed * in the {@link #mModifyContactList} list). * * @return true if > 0 contacts were modified, false otherwise. */ private boolean modifyContactList() { if (mModifyContactList.size() == 0) { return false; } LogUtils.logI("DownloadServerContacts.modifyContactList " + mModifyContactList.size() + " contacts..."); long startTime = System.nanoTime(); ServiceStatus status = mDb.syncModifyContactList(mModifyContactList, false, true); if (ServiceStatus.SUCCESS != status) { complete(status); return true; } markDbChanged(); long timeDiff = System.nanoTime() - startTime; mDbSyncTime += timeDiff; LogUtils.logI("DownloadServerContacts.modifyContactList - time = " + (timeDiff / NANOSECONDS_IN_MS) + "ms, total = " + (mDbSyncTime / NANOSECONDS_IN_MS) + "ms"); mModifyContactList.clear(); return true; } /** * Deletes contacts from the local database based on changes received from * the server (listed in the {@link #mDeleteContactList} list). * * @return true if > 0 contacts were deleted, false otherwise. */ private boolean deleteContactList() { if (mDeleteContactList.size() == 0) { return false; } LogUtils.logI("DownloadServerContacts.deleteContactList " + mDeleteContactList.size() + " contacts..."); long startTime = System.nanoTime(); ServiceStatus status = mDb.syncDeleteContactList(mDeleteContactList, false, true); if (ServiceStatus.SUCCESS != status) { complete(status); return true; } markDbChanged(); long timeDiff = System.nanoTime() - startTime; mDbSyncTime += timeDiff; LogUtils.logI("DownloadServerContacts.deleteContactList - time = " + (timeDiff / NANOSECONDS_IN_MS) + "ms, total = " + (mDbSyncTime / NANOSECONDS_IN_MS) + "ms"); mDeleteContactList.clear(); return true; } /** * Adds contact details to the local database based on changes received from * the server (listed in the {@link #mAddDetailList} list). * * @return true if > 0 contact details were added, false otherwise. */ private boolean addDetailList() { if (mAddDetailList.size() == 0) { return false; } LogUtils.logI("DownloadServerContacts.addDetailList " + mAddDetailList.size() + " contact details..."); long startTime = System.nanoTime(); ServiceStatus status = mDb.syncAddContactDetailList(mAddDetailList, false, true); if (ServiceStatus.SUCCESS != status) { complete(status); return true; } markDbChanged(); long timeDiff = System.nanoTime() - startTime; mDbSyncTime += timeDiff; LogUtils.logI("DownloadServerContacts.addDetailList - time = " + (timeDiff / NANOSECONDS_IN_MS) + "ms, total = " + (mDbSyncTime / NANOSECONDS_IN_MS) + "ms"); mAddDetailList.clear(); return true; } /** * Modifies contact details in the local database based on changes received * from the server (listed in the {@link #mModifyDetailList} list). * * @return true if > 0 contact details were modified, false otherwise. */ private boolean modifyDetailList() { if (mModifyDetailList.size() == 0) { return false; } LogUtils.logI("DownloadServerContacts.modifyDetailList " + mModifyDetailList.size() + " contact details..."); long startTime = System.nanoTime(); ServiceStatus status = mDb.syncModifyContactDetailList(mModifyDetailList, false, true); if (ServiceStatus.SUCCESS != status) { complete(status); return true; } markDbChanged(); long timeDiff = System.nanoTime() - startTime; mDbSyncTime += timeDiff; LogUtils.logI("DownloadServerContacts.modifyDetailList - time = " + (timeDiff / NANOSECONDS_IN_MS) + "ms, total = " + (mDbSyncTime / NANOSECONDS_IN_MS) + "ms"); mModifyDetailList.clear(); return true; } /** * Deletes contact details from the local database based on changes received * from the server (listed in the {@link #mDeleteDetailList} list). * * @return true if > 0 contact details were deleted, false otherwise. */ private boolean deleteDetailList() { if (mDeleteDetailList.size() == 0) { return false; } LogUtils.logI("DownloadServerContacts.deleteDetailList " + mDeleteDetailList.size() + " contact details..."); long startTime = System.nanoTime(); ServiceStatus status = mDb.syncDeleteContactDetailList(mDeleteDetailList, false, true); if (ServiceStatus.SUCCESS != status) { complete(status); return true; } markDbChanged(); long timeDiff = System.nanoTime() - startTime; mDbSyncTime += timeDiff; LogUtils.logI("DownloadServerContacts.deleteDetailList - time = " + (timeDiff / NANOSECONDS_IN_MS) + "ms, total = " + (mDbSyncTime / NANOSECONDS_IN_MS) + "ms"); mDeleteDetailList.clear(); return true; } /** * Called when the processor has finished downloading the contact changes. * Notifies the contact sync engine. */ private void downloadSyncSuccessful() { LogUtils.logI("DownloadServerContacts.downloadSyncSuccessful() - Total DB access time = " + (mDbSyncTime / NANOSECONDS_IN_MS) + "ms, no of contacts added = " + mTotalContactsAdded); StateTable.modifyContactRevision(mToRevision, mDb.getWritableDatabase()); complete(ServiceStatus.SUCCESS); } }