package org.dodgybits.shuffle.android.core.model.persistence;
import android.content.ContentUris;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.text.TextUtils;
import android.text.format.Time;
import android.util.Log;
import android.util.SparseIntArray;
import com.google.inject.Inject;
import org.dodgybits.shuffle.android.core.activity.flurry.Analytics;
import org.dodgybits.shuffle.android.core.model.Id;
import org.dodgybits.shuffle.android.core.model.Task;
import org.dodgybits.shuffle.android.core.model.Task.Builder;
import org.dodgybits.shuffle.android.persistence.provider.TaskProvider;
import roboguice.inject.ContentResolverProvider;
import roboguice.inject.ContextScoped;
import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;
import static org.dodgybits.shuffle.android.core.util.Constants.*;
import static org.dodgybits.shuffle.android.persistence.provider.AbstractCollectionProvider.ShuffleTable.ACTIVE;
import static org.dodgybits.shuffle.android.persistence.provider.AbstractCollectionProvider.ShuffleTable.DELETED;
import static org.dodgybits.shuffle.android.persistence.provider.TaskProvider.Tasks.*;
@ContextScoped
public class TaskPersister extends AbstractEntityPersister<Task> {
private static final String cTag = "TaskPersister";
private static final int ID_INDEX = 0;
private static final int DESCRIPTION_INDEX = ID_INDEX + 1;
private static final int DETAILS_INDEX = DESCRIPTION_INDEX + 1;
private static final int PROJECT_INDEX = DETAILS_INDEX + 1;
private static final int CONTEXT_INDEX = PROJECT_INDEX + 1;
private static final int CREATED_INDEX = CONTEXT_INDEX + 1;
private static final int MODIFIED_INDEX = CREATED_INDEX + 1;
private static final int START_INDEX = MODIFIED_INDEX + 1;
private static final int DUE_INDEX = START_INDEX + 1;
private static final int TIMEZONE_INDEX = DUE_INDEX + 1;
private static final int CAL_EVENT_INDEX = TIMEZONE_INDEX + 1;
private static final int DISPLAY_ORDER_INDEX = CAL_EVENT_INDEX + 1;
private static final int COMPLETE_INDEX = DISPLAY_ORDER_INDEX + 1;
private static final int ALL_DAY_INDEX = COMPLETE_INDEX + 1;
private static final int HAS_ALARM_INDEX = ALL_DAY_INDEX + 1;
private static final int TASK_TRACK_INDEX = HAS_ALARM_INDEX + 1;
private static final int DELETED_INDEX = TASK_TRACK_INDEX +1;
private static final int ACTIVE_INDEX = DELETED_INDEX +1;
@Inject
public TaskPersister(ContentResolverProvider provider, Analytics analytics) {
super(provider.get(), analytics);
}
@Override
public Task read(Cursor cursor) {
Builder builder = Task.newBuilder();
builder
.setLocalId(readId(cursor, ID_INDEX))
.setDescription(readString(cursor, DESCRIPTION_INDEX))
.setDetails(readString(cursor, DETAILS_INDEX))
.setProjectId(readId(cursor, PROJECT_INDEX))
.setContextId(readId(cursor, CONTEXT_INDEX))
.setCreatedDate(readLong(cursor, CREATED_INDEX))
.setModifiedDate(readLong(cursor, MODIFIED_INDEX))
.setStartDate(readLong(cursor, START_INDEX))
.setDueDate(readLong(cursor, DUE_INDEX))
.setTimezone(readString(cursor, TIMEZONE_INDEX))
.setCalendarEventId(readId(cursor, CAL_EVENT_INDEX))
.setOrder(cursor.getInt(DISPLAY_ORDER_INDEX))
.setComplete(readBoolean(cursor, COMPLETE_INDEX))
.setAllDay(readBoolean(cursor, ALL_DAY_INDEX))
.setHasAlarm(readBoolean(cursor, HAS_ALARM_INDEX))
.setTracksId(readId(cursor, TASK_TRACK_INDEX))
.setDeleted(readBoolean(cursor, DELETED_INDEX))
.setActive(readBoolean(cursor, ACTIVE_INDEX));
return builder.build();
}
@Override
protected void writeContentValues(ContentValues values, Task task) {
// never write id since it's auto generated
writeString(values, DESCRIPTION, task.getDescription());
writeString(values, DETAILS, task.getDetails());
writeId(values, PROJECT_ID, task.getProjectId());
writeId(values, CONTEXT_ID, task.getContextId());
values.put(CREATED_DATE, task.getCreatedDate());
values.put(MODIFIED_DATE, task.getModifiedDate());
values.put(START_DATE, task.getStartDate());
values.put(DUE_DATE, task.getDueDate());
writeBoolean(values, DELETED, task.isDeleted());
writeBoolean(values, ACTIVE, task.isActive());
String timezone = task.getTimezone();
if (TextUtils.isEmpty(timezone))
{
if (task.isAllDay()) {
timezone = Time.TIMEZONE_UTC;
} else {
timezone = TimeZone.getDefault().getID();
}
}
values.put(TIMEZONE, timezone);
writeId(values, CAL_EVENT_ID, task.getCalendarEventId());
values.put(DISPLAY_ORDER, task.getOrder());
writeBoolean(values, COMPLETE, task.isComplete());
writeBoolean(values, ALL_DAY, task.isAllDay());
writeBoolean(values, HAS_ALARM, task.hasAlarms());
writeId(values, TRACKS_ID, task.getTracksId());
}
@Override
protected String getEntityName() {
return "task";
}
@Override
public Uri getContentUri() {
return TaskProvider.Tasks.CONTENT_URI;
}
@Override
public String[] getFullProjection() {
return TaskProvider.Tasks.FULL_PROJECTION;
}
public int deleteCompletedTasks() {
int deletedRows = moveToTrash(TaskProvider.Tasks.COMPLETE + " = 1", null);
Log.d(cTag, "Deleting " + deletedRows + " completed tasks.");
Map<String, String> params = new HashMap<String,String>(mFlurryParams);
params.put(cFlurryCountParam, String.valueOf(deletedRows));
mAnalytics.onEvent(cFlurryDeleteEntityEvent, params);
return deletedRows;
}
/**
* Toggle whether the task at the given cursor position is complete.
* The cursor is committed and re-queried after the update.
*
* @param cursor cursor positioned at task to update
* @return new value of task completeness
*/
public boolean toggleTaskComplete(Cursor cursor) {
Id taskId = readId(cursor, ID_INDEX);
boolean isComplete = !readBoolean(cursor, COMPLETE_INDEX);
ContentValues values = new ContentValues();
writeBoolean(values, COMPLETE, isComplete);
values.put(MODIFIED_DATE, System.currentTimeMillis());
mResolver.update(getContentUri(), values,
TaskProvider.Tasks._ID + "=?", new String[] { String.valueOf(taskId) });
if (isComplete) {
mAnalytics.onEvent(cFlurryCompleteTaskEvent);
}
return isComplete;
}
/**
* Swap the display order of two tasks at the given cursor positions.
* The cursor is committed and re-queried after the update.
*/
public void swapTaskPositions(Cursor cursor, int pos1, int pos2) {
cursor.moveToPosition(pos1);
Id id1 = readId(cursor, ID_INDEX);
int positionValue1 = cursor.getInt(DISPLAY_ORDER_INDEX);
cursor.moveToPosition(pos2);
Id id2 = readId(cursor, ID_INDEX);
int positionValue2 = cursor.getInt(DISPLAY_ORDER_INDEX);
Uri uri = ContentUris.withAppendedId(getContentUri(), id1.getId());
ContentValues values = new ContentValues();
values.put(DISPLAY_ORDER, positionValue2);
mResolver.update(uri, values, null, null);
uri = ContentUris.withAppendedId(getContentUri(), id2.getId());
values = new ContentValues();
values.put(DISPLAY_ORDER, positionValue1);
mResolver.update(uri, values, null, null);
mAnalytics.onEvent(cFlurryReorderTasksEvent);
}
private static final int TASK_COUNT_INDEX = 1;
public SparseIntArray readCountArray(Cursor cursor) {
SparseIntArray countMap = new SparseIntArray();
while (cursor.moveToNext()) {
Integer id = cursor.getInt(ID_INDEX);
Integer count = cursor.getInt(TASK_COUNT_INDEX);
countMap.put(id, count);
}
return countMap;
}
}