/* * 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.database.tables; import java.util.List; import android.content.ContentValues; import android.database.Cursor; import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteException; import android.text.format.Time; import com.vodafone360.people.Settings; import com.vodafone360.people.database.DatabaseHelper; import com.vodafone360.people.datatypes.ContactDetail; import com.vodafone360.people.utils.CloseUtils; import com.vodafone360.people.utils.LogUtils; /** * Contains all the functionality related to the contact change log database * table. This table is used to persist database changes that have not yet been * synced to the server. This class is never instantiated hence all methods must * be static. * * @version %I%, %G% */ public abstract class ContactChangeLogTable { /** * Name of the table as it appears in the database */ public static final String TABLE_NAME = "ContactChangeLog"; /** * Name of a temporary table created and used to batch the deletion of sync * data from the change log table. */ private static final String TEMP_CONTACT_CHANGE_TABLE = "SyncContactInfo"; /** * Primary key for the temporary table */ private static final String TEMP_CONTACT_CHANGE_TABLE_ID = "Id"; /** * Secondary key which links to the primary key of the change log table */ private static final String TEMP_CONTACT_CHANGE_LOG_ID = "LogId"; /** * An enumeration of all the field names in the database. */ private static enum Field { CONTACTCHANGEID("ContactChangeId"), CHANGETYPE("ChangeType"), LOCALCHANGECONTACTID("LocalChangeContactId"), SERVERCHANGECONTACTID("ServerChangeContactId"), LOCALCHANGEDETAILID("LocalChangeDetailId"), SERVERDETAILKEY("ServerDetailKey"), SERVERDETAILID("ServerDetailId"), ZYBGROUPORRELID("ZybGroupOrRelId"), TIMESTAMP("Timestamp"); /** * The name of the field as it appears in the database */ private String mField; /** * Constructor * * @param field - The name of the field (see list above) */ private Field(String field) { mField = field; } /** * @return the name of the field as it appears in the database. */ public String toString() { return mField; } } /** * Enumerates the type of changes supported by this table, including: * <ul> * <li>DELETE_CONTACT: A contact has been deleted from the database and * needs to be deleted on the server. The local Contact ID, server Contact * ID fields are required</li> * <li>DELETE_DETAIL: A contact detail has been deleted from the database * and needs to be deleted on the server. The local Contact ID, server * Contact ID, local Detail ID, server Detail ID (if the detail has one) and * detail key are required</li> </li> * <li>ADD_GROUP_REL: A contact has been associated with a group on the * client and needs to be synced on the server. The local Contact ID, server * Contact ID and group ID are required</li> * <li>DELETE_GROUP_REL: A contact has been disassociated with a group on * the client and needs to be synced on the server. The local Contact ID, * server Contact ID and group ID are required</li> * </ul> */ public static enum ContactChangeType { DELETE_CONTACT, DELETE_DETAIL, ADD_GROUP_REL, DELETE_GROUP_REL } /** * Wraps up the data present in the change log table */ public static class ContactChangeInfo { public Long mContactChangeId = null; public ContactChangeType mType = null; public Long mLocalContactId = null; public Long mServerContactId = null; public Long mLocalDetailId = null; public ContactDetail.DetailKeys mServerDetailKey = null; public Long mServerDetailId = null; public Long mGroupOrRelId = null; /** * Converts the encapsulated data into a string that can be displayed * for debug purposes. */ @Override public String toString() { return "Contact Change ID: " + mContactChangeId + "\n" + "Contact Change Type: " + mType + "\n" + "Local Contact ID: " + mLocalContactId + "\n" + "Server Contact ID: " + mServerContactId + "\n" + "Local Detail ID: " + mLocalDetailId + "\n" + "Detail Key: " + mServerDetailKey + "\n" + "Server Detail ID: " + mServerDetailId + "\n" + "Group/Relation ID: " + mGroupOrRelId; } } /** * Create Server Change Log Table. * * @param writeableDb A writable SQLite database * @throws SQLException If an SQL compilation error occurs */ public static void create(SQLiteDatabase writableDb) throws SQLException { DatabaseHelper.trace(true, "ContactChangeLogTable.create()"); writableDb.execSQL("CREATE TABLE " + TABLE_NAME + " (" + Field.CONTACTCHANGEID + " INTEGER PRIMARY KEY, " + Field.CHANGETYPE + " INTEGER NOT NULL, " + Field.LOCALCHANGECONTACTID + " LONG NOT NULL, " + Field.SERVERCHANGECONTACTID + " LONG, " + Field.LOCALCHANGEDETAILID + " LONG, " + Field.SERVERDETAILID + " LONG, " + Field.SERVERDETAILKEY + " INTEGER, " + Field.ZYBGROUPORRELID + " LONG, " + Field.TIMESTAMP + " DATE);"); } /** * Fetches a comma separated list of table fields which can be used in an * SQL SELECT statement as the query projection. The * {@link #getQueryData(Cursor)} method can used to fetch data from the * cursor. * * @param whereClause A where clause (without the where) to filter the * result. Cannot be null. * @return SQL string */ private static String getQueryStringSql(String whereClause) { DatabaseHelper.trace(false, "ContactChangeLogTable.getQueryStringSql()"); return "SELECT " + Field.CONTACTCHANGEID + ", " + Field.CHANGETYPE + ", " + Field.LOCALCHANGECONTACTID + ", " + Field.SERVERCHANGECONTACTID + ", " + Field.LOCALCHANGEDETAILID + ", " + Field.SERVERDETAILID + ", " + Field.SERVERDETAILKEY + ", " + Field.ZYBGROUPORRELID + ", " + Field.TIMESTAMP + " FROM " + TABLE_NAME + " WHERE " + whereClause; } /** * Column indices for database queries */ private static final int CONTACTCHANGEID = 0; private static final int TYPE = 1; private static final int LOCALCONTACTID = 2; private static final int SERVERCONTACTID = 3; private static final int LOCALDETAILID = 4; private static final int SERVERDETAILID = 5; private static final int SERVERDETAILKEY = 6; private static final int GROUPORRELID = 7; /** * Fetches change log information from a cursor at the current position. The * {@link #getQueryStringSql(String)} method should be used to make the * query. * * @param c The cursor from the query * @return A filled in ContactChangeInfo object */ private static ContactChangeInfo getQueryData(Cursor c) { DatabaseHelper.trace(false, "ContactChangeLogTable.getQueryData()"); ContactChangeInfo info = new ContactChangeInfo(); if (!c.isNull(CONTACTCHANGEID)) { info.mContactChangeId = c.getLong(CONTACTCHANGEID); } if (!c.isNull(TYPE)) { final int typeIdx = c.getInt(TYPE); if (typeIdx < ContactChangeType.values().length) { info.mType = ContactChangeType.values()[typeIdx]; } } if (!c.isNull(LOCALCONTACTID)) { info.mLocalContactId = c.getLong(LOCALCONTACTID); } if (!c.isNull(SERVERCONTACTID)) { info.mServerContactId = c.getLong(SERVERCONTACTID); } if (!c.isNull(LOCALDETAILID)) { info.mLocalDetailId = c.getLong(LOCALDETAILID); } if (!c.isNull(SERVERDETAILID)) { info.mServerDetailId = c.getLong(SERVERDETAILID); } if (!c.isNull(SERVERDETAILKEY)) { final int keyIdx = c.getInt(SERVERDETAILKEY); if (keyIdx < ContactDetail.DetailKeys.values().length) { info.mServerDetailKey = ContactDetail.DetailKeys.values()[keyIdx]; } } if (!c.isNull(GROUPORRELID)) { info.mGroupOrRelId = c.getLong(GROUPORRELID); } // Ignore timestamp return info; } /** * Inserts "Add contact to group" operation into the change log. * * @param localContactId Local contact ID from the Contacts table * @param serverContactId Server contact ID or null if the contact has not * yet been synced. * @param groupId server group ID * @param writableDb Writable SQLite database * @return true if successful, false otherwise */ public static boolean addGroupRel(long localContactId, Long serverContactId, Long groupId, SQLiteDatabase writableDb) { if (Settings.ENABLED_DATABASE_TRACE) DatabaseHelper.trace(false, "ContactChangeLogTable.addZybGroupRel() localContactId[" + localContactId + "] serverContactId[" + serverContactId + "] groupId[" + groupId + "]"); if (groupId == null) { LogUtils.logE("ContactChangeLogTable.addGroupRel() Invalid parameter"); return false; } if (isContactChangeInList(localContactId, ContactChangeType.DELETE_CONTACT, writableDb)) { LogUtils .logE("ContactChangeLogTable.addGroupRel() Associated contact has already been deleted"); return false; } ContactChangeInfo info = new ContactChangeInfo(); info.mType = ContactChangeType.ADD_GROUP_REL; info.mLocalContactId = localContactId; info.mServerContactId = serverContactId; info.mGroupOrRelId = groupId; if (!addContactChange(info, writableDb)) { return false; } return true; } /** * Inserts "Remove contact from group" operation into the change log. * * @param localContactId Local contact ID from the Contacts table * @param serverContactId Server contact ID or null if the contact has not * yet been synced. * @param groupId server group ID * @param writableDb Writable SQLite database * @return true if successful, false otherwise */ public static boolean deleteGroupRel(long localContactId, Long serverContactId, Long groupId, SQLiteDatabase writableDb) { if (Settings.ENABLED_DATABASE_TRACE) DatabaseHelper.trace(false, "ContactChangeLogTable.deleteGroupRel() localContactId[" + localContactId + "] serverContactId[" + serverContactId + "] groupId[" + groupId + "]"); if (groupId == null) { LogUtils.logE("ContactChangeLogTable.deleteGroupRel() Invalid parameter"); return false; } if (isContactChangeInList(localContactId, ContactChangeType.DELETE_CONTACT, writableDb)) { LogUtils .logE("ContactChangeLogTable.deleteGroupRel() Associated contact has already been deleted"); return false; } Long addGroupId = findContactGroup(localContactId, groupId, writableDb); if (addGroupId != null) { return removeContactChange(addGroupId, writableDb); } if (isContactChangeInList(localContactId, ContactChangeType.ADD_GROUP_REL, writableDb)) { removeContactChanges(localContactId, writableDb); return true; } ContactChangeInfo info = new ContactChangeInfo(); info.mType = ContactChangeType.DELETE_GROUP_REL; info.mLocalContactId = localContactId; info.mServerContactId = serverContactId; info.mGroupOrRelId = groupId; if (!addContactChange(info, writableDb)) { return false; } return true; } /** * Inserts "Delete Contact" operation into the change log. * * @param localContactId Local contact ID from the Contacts table * @param serverContactId Server contact ID. If this is null it is assumed * that the contact hasn't yet been synced with the server, so * the new contact change is removed from the log and the * function returns. * @param addToLog False - removes all changes associated with the contact * from the change log True - to also insert deletion in change * log. * @param writableDb Writable SQLite database * @return true if successful, false otherwise */ public static boolean addDeletedContactChange(Long localContactId, Long serverContactId, boolean addToLog, SQLiteDatabase writableDb) { if (Settings.ENABLED_DATABASE_TRACE) DatabaseHelper.trace(false, "ContactChangeLogTable.addDeletedContactChange() localContactId[" + localContactId + "] serverContactId[" + serverContactId + "] addToLog[" + addToLog + "]"); if (localContactId == null) { LogUtils.logE("ContactChangeLogTable.addDeletedContactChange() Invalid parameter"); return false; } if (addToLog) { if (serverContactId == null) { addToLog = false; } } removeContactChanges(localContactId, writableDb); if (!addToLog) { return true; } ContactChangeInfo info = new ContactChangeInfo(); info.mType = ContactChangeType.DELETE_CONTACT; info.mLocalContactId = localContactId; info.mServerContactId = serverContactId; if (!addContactChange(info, writableDb)) { return false; } return true; } /** * Inserts "Delete Contact Detail" operation into the change log. * * @param detail Must have a valid local contact ID, local detail ID and * key. Will not be added to the change log if the contact server * ID is null. * @param addToLog False - removes all changes associated with the detail * from the change log True - to also insert deletion in change * log. * @param writableDb Writable SQLite database * @return true if successful, false otherwise */ public static boolean addDeletedContactDetailChange(ContactDetail detail, boolean addToLog, SQLiteDatabase writableDb) { if (Settings.ENABLED_DATABASE_TRACE) DatabaseHelper.trace(false, "ContactChangeLogTable.addDeletedContactDetailChange() addToLog[" + addToLog + "]"); if (detail.localContactID == null || detail.localDetailID == null || detail.key == null) { LogUtils .logE("ContactChangeLogTable.addDeletedContactDetailChange() Invalid parameter"); return false; } if (addToLog) { if (isContactChangeInList(detail.localContactID, ContactChangeType.DELETE_CONTACT, writableDb)) { LogUtils .logE("ContactChangeLogTable.addDeletedContactDetailChange() Associated contact has already been deleted"); return false; } if (isContactDetailChangeInList(detail.localDetailID, ContactChangeType.DELETE_DETAIL, writableDb)) { LogUtils .logE("ContactChangeLogTable.addDeletedContactDetailChange() Associated contact detail has already been deleted"); return false; } } removeContactDetailChanges(detail.localDetailID, writableDb); if (!addToLog || detail.serverContactId == null) { return true; } ContactChangeInfo info = new ContactChangeInfo(); info.mLocalContactId = detail.localContactID; info.mServerContactId = detail.serverContactId; info.mLocalDetailId = detail.localDetailID; info.mServerDetailKey = detail.key; info.mServerDetailId = detail.unique_id; info.mType = ContactChangeType.DELETE_DETAIL; if (!addContactChange(info, writableDb)) { return false; } return true; } /** * Inserts a contact change into the table. * * @param info contact change info. This must have a valid type field and * depending on type other fields may also be required (see * {@link ContactChangeType}). * @param writableDb Writable SQLite database * @return true if successful, false otherwise */ private static boolean addContactChange(ContactChangeInfo info, SQLiteDatabase writableDb) { if (Settings.ENABLED_DATABASE_TRACE) DatabaseHelper.trace(true, "ContactChangeLogTable.addContactChange()"); try { ContentValues changeValues = new ContentValues(); changeValues.put(Field.CHANGETYPE.toString(), info.mType.ordinal()); changeValues.put(Field.LOCALCHANGECONTACTID.toString(), info.mLocalContactId); if (info.mServerContactId != null) { changeValues.put(Field.SERVERCHANGECONTACTID.toString(), info.mServerContactId); } if (info.mLocalDetailId != null) { changeValues.put(Field.LOCALCHANGEDETAILID.toString(), info.mLocalDetailId); } if (info.mGroupOrRelId != null) { changeValues.put(Field.ZYBGROUPORRELID.toString(), info.mGroupOrRelId); } if (info.mServerDetailKey != null) { changeValues.put(Field.SERVERDETAILKEY.toString(), info.mServerDetailKey.ordinal()); } if (info.mServerDetailId != null) { changeValues.put(Field.SERVERDETAILID.toString(), info.mServerDetailId); } Time time = new Time(); time.setToNow(); changeValues.put(Field.TIMESTAMP.toString(), time.format2445()); long id = writableDb.insertOrThrow(TABLE_NAME, null, changeValues); if (id < 0) { LogUtils .logE("ContactChangeLogTable.addContactChange() Unable to add contact change to log table - a database error has occurred"); return false; } info.mContactChangeId = id; return true; } catch (SQLException e) { LogUtils.logE("ContactChangeLogTable.addContactChange() SQLException" + "- Unable to add contact change to log table", e); return false; } } /** * Determines if a specific contact and change type exists in the change * log. * * @param localContactId Local contact ID from the Contacts table * @param type The change type to find * @param readableDb Readable SQLite database * @return true if the change is found, false otherwise */ private static boolean isContactChangeInList(Long localContactId, ContactChangeType type, SQLiteDatabase readableDb) { if (Settings.ENABLED_DATABASE_TRACE) DatabaseHelper.trace(false, "ContactChangeLogTable.isContactChangeInList() localContactId[" + localContactId + "]"); Cursor c = null; try { c = readableDb.rawQuery("SELECT " + Field.LOCALCHANGECONTACTID + " FROM " + TABLE_NAME + " WHERE " + Field.LOCALCHANGECONTACTID + "=" + localContactId + " AND " + Field.CHANGETYPE + "=" + type.ordinal(), null); if (c.moveToFirst()) { return true; } return false; } catch (SQLException e) { return false; } finally { CloseUtils.close(c); } } /** * Determines if a specific contact detail and change type exists in the * change log. * * @param localDetailId Local contact detail ID from ContactDetail table * @param type The change type to find * @param readableDb Readable SQLite database * @return true if the change is found, false otherwise */ private static boolean isContactDetailChangeInList(Long localDetailId, ContactChangeType type, SQLiteDatabase readableDb) { if (Settings.ENABLED_DATABASE_TRACE) DatabaseHelper.trace(false, "ContactChangeLogTable.isContactDetailChangeInList() localDetailId[" + localDetailId + "]"); Cursor c = null; try { c = readableDb.rawQuery("SELECT " + Field.LOCALCHANGEDETAILID + " FROM " + TABLE_NAME + " WHERE " + Field.LOCALCHANGEDETAILID + "=" + localDetailId + " AND " + Field.CHANGETYPE + "=" + type.ordinal(), null); if (c.moveToFirst()) { return true; } return false; } catch (SQLException e) { return false; } finally { CloseUtils.close(c); } } /** * Searches the change log for an association between contact and group * * @param localContactId Local contact ID from the Contacts table * @param groupId Server group ID * @param readableDb Readable SQLite database * @return Contact Change ID (primary key) of the record found, or NULL if * no such logs exist in the table. */ private static Long findContactGroup(long localContactId, long groupId, SQLiteDatabase readableDb) { if (Settings.ENABLED_DATABASE_TRACE) DatabaseHelper.trace(false, "ContactChangeLogTable.findAddGroup() localContactId[" + localContactId + "] groupId[" + groupId + "]"); Cursor c = null; try { Long id = null; c = readableDb.rawQuery("SELECT " + Field.CONTACTCHANGEID + " FROM " + TABLE_NAME + " WHERE " + Field.LOCALCHANGECONTACTID + "=" + localContactId + " AND " + Field.ZYBGROUPORRELID + "=" + groupId + " AND " + Field.CHANGETYPE + "=" + ContactChangeType.ADD_GROUP_REL.ordinal(), null); if (c.moveToFirst()) { id = c.getLong(0); } return id; } catch (SQLException e) { return null; } finally { CloseUtils.close(c); c = null; } } /** * Removes all changes from the change log associated with a particular * contact. * * @param localContactId Local contact ID from the Contacts table * @param writableDb Writable SQLite database * @return true if successful, false otherwise */ private static boolean removeContactChanges(Long localContactId, SQLiteDatabase writableDb) { if (Settings.ENABLED_DATABASE_TRACE) DatabaseHelper.trace(true, "ContactChangeLogTable.removeContactChanges() localContactId[" + localContactId + "]"); try { int result = writableDb.delete(TABLE_NAME, Field.LOCALCHANGECONTACTID + "=" + localContactId, null); if (result <= 0) { return false; } } catch (SQLException e) { return false; } return true; } /** * Deletes a specific contact change record from the table * * @param ContactChangeId The primary ID identifying the record * @param writableDb Writable SQLite database * @return true if successful, false otherwise */ private static boolean removeContactChange(long ContactChangeId, SQLiteDatabase writableDb) { if (Settings.ENABLED_DATABASE_TRACE) DatabaseHelper.trace(true, "ContactChangeLogTable.removeContactChange() ContactChangeId[" + ContactChangeId + "]"); try { int result = writableDb.delete(TABLE_NAME, Field.CONTACTCHANGEID + "=" + ContactChangeId, null); if (result <= 0) { return false; } } catch (SQLException e) { return false; } return true; } /** * Removes all changes from the change log associated with a particular * contact detail. * * @param localDetailId Local contact detail ID from the Contact details * table * @param writableDb Writable SQLite database * @return true if successful, false otherwise */ private static boolean removeContactDetailChanges(Long localDetailId, SQLiteDatabase writableDb) { if (Settings.ENABLED_DATABASE_TRACE) DatabaseHelper.trace(true, "ContactChangeLogTable.removeContactDetailChanges() localDetailId[" + localDetailId + "]"); try { if (writableDb .delete(TABLE_NAME, Field.LOCALCHANGEDETAILID + "=" + localDetailId, null) < 0) { return false; } // We 0 rows are deleted we still should return success } catch (SQLException e) { return false; } return true; } /** * Fetches the number of changes listed in the table. * * @param type The ContactChangeType to count, if NULL then all change types * are included. * @param readableDb Readable SQLite database * @return Total number of records */ public static int fetchNoOfContactDetailChanges( final ContactChangeType type, final SQLiteDatabase readableDb) { if (Settings.ENABLED_DATABASE_TRACE) { DatabaseHelper.trace(false, "ContactChangeLogTable." + "fetchNoOfContactDetailChanges()"); } Cursor cursor = null; try { String query = "SELECT COUNT(*) FROM " + TABLE_NAME; if (type != null) { query += " WHERE " + Field.CHANGETYPE.toString() + "=" + type.ordinal(); } cursor = readableDb.rawQuery(query, null); if (cursor.moveToFirst()) { return cursor.getInt(0); } else { LogUtils.logE("ContactChangeLogTable." + "fetchNoOfContactDetailChanges() COUNT(*) " + "should not return an empty cursor, returning 0"); return 0; } } finally { CloseUtils.close(cursor); } } /** * Removes a list of changes from the change log table. This method is used * once the server has been successfully synced with the client. * * @param changeInfoList A list of {@link ContactChangeInfo} objects. Note * that only the mContactChangeId field of this object is * actually used. The object is passed in as a convenience. * @param writableDb Writable SQLite database * @return true if successful, false otherwise */ public static boolean deleteContactChanges(List<ContactChangeInfo> changeInfoList, SQLiteDatabase writableDb) { if (Settings.ENABLED_DATABASE_TRACE) { DatabaseHelper.trace(false, "ContactChangeLogTable.deleteContactChanges() Change Log List for deletion:"); for (ContactChangeLogTable.ContactChangeInfo info : changeInfoList) { DatabaseHelper.trace(false, "ContactChangeLogTable.deleteContactChanges() mContactChangeId[" + info.mContactChangeId + "]"); } } try { writableDb.beginTransaction(); writableDb.execSQL("DROP TABLE IF EXISTS " + TEMP_CONTACT_CHANGE_TABLE); writableDb.execSQL("CREATE TEMPORARY TABLE " + TEMP_CONTACT_CHANGE_TABLE + "(" + TEMP_CONTACT_CHANGE_TABLE_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + TEMP_CONTACT_CHANGE_LOG_ID + " LONG);"); ContentValues values = new ContentValues(); for (ContactChangeInfo info : changeInfoList) { values.put(TEMP_CONTACT_CHANGE_LOG_ID, info.mContactChangeId); if (writableDb.insertOrThrow(TEMP_CONTACT_CHANGE_TABLE, null, values) < 0) { return false; } } writableDb.execSQL("DELETE FROM " + TABLE_NAME + " WHERE " + Field.CONTACTCHANGEID + " IN (SELECT " + TEMP_CONTACT_CHANGE_LOG_ID + " FROM " + TEMP_CONTACT_CHANGE_TABLE + ");"); writableDb.setTransactionSuccessful(); return true; } catch (SQLException e) { LogUtils .logE( "ContactChangeLogTable.deleteContactChanges() SQLException - Unable to remove contact detail change from log", e); return false; } finally { writableDb.endTransaction(); } } /** * Fetches a list of changes from the table for a specific type. The list is * ordered by local contact ID. * * @param contactChangeList The list to be populated * @param type The type of change to return * @param firstIndex An index of the first record to return (0 based) * @param count The number of records to return (or -1 to return all) * @param readableDb Readable SQLite database * @return true if successful, false otherwise */ public static boolean fetchContactChangeLog(List<ContactChangeInfo> contactChangeList, ContactChangeType type, long firstIndex, long count, SQLiteDatabase readableDb) { if (Settings.ENABLED_DATABASE_TRACE) DatabaseHelper.trace(false, "ContactChangeLogTable.fetchContactChangeLog() " + "firstIndex[" + firstIndex + "] count[" + count + "]"); Cursor c1 = null; try { c1 = readableDb.rawQuery(getQueryStringSql(Field.CHANGETYPE + "=" + type.ordinal() + " ORDER BY " + Field.LOCALCHANGECONTACTID + " LIMIT " + firstIndex + "," + count), null); contactChangeList.clear(); while (c1.moveToNext()) { contactChangeList.add(getQueryData(c1)); } } catch (SQLiteException e) { LogUtils .logE( "ContactChangeLogTable.fetchContactChangeLog() throw e; - Unable to fetch main contact change log", e); return false; } finally { CloseUtils.close(c1); } return true; } /** * Fetches a list of changes on the contact details for the me-profile. * * @param changeList The list to populate with contact changes. * @param type The type of change to fetch from the contact. * @param readableDb The database to read from. * @param meProfileServerId The server id of the me-profile. * @return True if fetching succeeded, false otherwise. */ public static boolean fetchMeProfileChangeLog(List<ContactChangeInfo> changeList, ContactChangeType type, SQLiteDatabase readableDb, Long meProfileServerId) { if (Settings.ENABLED_DATABASE_TRACE) DatabaseHelper.trace(false, "ContactChangeLogTable.fetchMeProfileChangeLog()"); Cursor c1 = null; try { c1 = readableDb.rawQuery(getQueryStringSql(Field.CHANGETYPE + "=" + type.ordinal() + " AND " + Field.SERVERCHANGECONTACTID + "=" + meProfileServerId.longValue()), null); while (c1.moveToNext()) { changeList.add(getQueryData(c1)); } } catch (SQLiteException e) { LogUtils.logE("ContactChangeLogTable.fetchMeProfileChangeLog() throw e; - " + "Unable to fetch main contact change log", e); return false; } finally { CloseUtils.close(c1); } return true; } }