package org.dodgybits.shuffle.android.synchronisation.tracks; import java.io.IOException; import java.io.StringWriter; import java.text.ParseException; import java.util.LinkedList; import java.util.Map; import org.dodgybits.android.shuffle.R; import org.dodgybits.shuffle.android.core.model.EntityBuilder; 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.core.model.persistence.EntityPersister; import org.dodgybits.shuffle.android.core.util.DateUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import android.content.Context; import android.text.TextUtils; import android.util.Log; import android.util.Xml; /** * @author Morten Nielsen */ public final class TaskSynchronizer extends Synchronizer<Task> { private static final String cTag = "TaskSynchronizer"; private final String mTracksUrl; public TaskSynchronizer( EntityPersister<Task> persister, TracksSynchronizer tracksSynchronizer, WebClient client, Context context, int basePercent, String tracksUrl) { super(persister, tracksSynchronizer, client, context, basePercent); this.mTracksUrl = tracksUrl; } @Override protected EntityBuilder<Task> createBuilder() { return Task.newBuilder(); } @Override protected void verifyLocalEntities(Map<Id, Task> localEntities) { LinkedList<Id> tasksWithoutContext = new LinkedList<Id>(); for(Task t : localEntities.values()) { if(!t.getContextId().isInitialised()) { tasksWithoutContext.add(t.getLocalId()); } } if (tasksWithoutContext.size() > 0) { mTracksSynchronizer.postSyncMessage(R.string.cannotSyncTasksWithoutContext); for(Id id : tasksWithoutContext) { localEntities.remove(id); } } } @Override protected String readingRemoteText() { return mContext.getString(R.string.readingRemoteTasks); } @Override protected String processingText() { return mContext.getString(R.string.processingTasks); } @Override protected String readingLocalText() { return mContext.getString(R.string.readingLocalTasks); } @Override protected String stageFinishedText() { return mContext.getString(R.string.doneWithTasks); } protected Task createMergedLocalEntity(Task localTask, Task newTask) { Builder builder = Task.newBuilder(); builder.mergeFrom(localTask); builder .setDescription(newTask.getDescription()) .setDetails(newTask.getDetails()) .setContextId(newTask.getContextId()) .setProjectId(newTask.getProjectId()) .setModifiedDate(newTask.getModifiedDate()) .setStartDate(newTask.getStartDate()) .setDueDate(newTask.getDueDate()) .setAllDay(newTask.isAllDay()) .setTracksId(newTask.getTracksId()); return builder.build(); } protected String createDocumentForEntity(Task task) { XmlSerializer serializer = Xml.newSerializer(); StringWriter writer = new StringWriter(); try { serializer.setOutput(writer); //serializer.startDocument("UTF-8", true); serializer.startTag("", "todo"); if (task.isComplete()) { String completedDateStr = DateUtils.formatIso8601Date(task.getModifiedDate()); serializer.startTag("", "completed-at").attribute("", "type", "datetime").text(completedDateStr).endTag("", "completed-at"); } Id contextId = findTracksIdByContextId(task.getContextId()); if (contextId.isInitialised()) { serializer.startTag("", "context-id").attribute("", "type", "integer").text(contextId.toString()).endTag("", "context-id"); } String createdDateStr = DateUtils.formatIso8601Date(task.getCreatedDate()); serializer.startTag("", "created-at").attribute("", "type", "datetime").text(createdDateStr).endTag("", "created-at"); serializer.startTag("", "description").text(task.getDescription()).endTag("", "description"); if (task.getDueDate() != 0) { String dueDateStr = DateUtils.formatIso8601Date(task.getDueDate()); serializer.startTag("", "due").attribute("", "type", "datetime").text(dueDateStr).endTag("", "due"); } serializer.startTag("", "notes").text(task.getDetails() != null ? task.getDetails() : "").endTag("", "notes"); Id projectId = findTracksIdByProjectId(task.getProjectId()); if (projectId.isInitialised()) { serializer.startTag("", "project-id").attribute("", "type", "integer").text(projectId.toString()).endTag("", "project-id"); } if (task.getStartDate() != 0L) { serializer.startTag("", "show-from").attribute("", "type", "datetime").text(DateUtils.formatIso8601Date(task.getStartDate())).endTag("", "show-from"); } serializer.startTag("", "state").text(task.isComplete() ? "completed" : "active").endTag("", "state"); String updatedDateStr = DateUtils.formatIso8601Date(task.getModifiedDate()); serializer.startTag("", "updated-at").attribute("", "type", "datetime").text(updatedDateStr).endTag("", "updated-at"); serializer.endTag("", "todo"); // serializer.endDocument(); serializer.flush(); } catch (IOException ignored) { Log.d(cTag, "Failed to serialize task", ignored); } Log.d(cTag, writer.toString()); return writer.toString(); } protected Task parseSingleEntity(XmlPullParser parser) throws ParseException { Task task = null; final Builder builder = Task.newBuilder(); builder.setTimezone("UTC"); try { int eventType = parser.getEventType(); while (eventType != XmlPullParser.END_DOCUMENT && task == null) { final String name = parser.getName(); long startDate = 0L; long dueDate = 0L; switch (eventType) { case XmlPullParser.START_TAG: if (name.equalsIgnoreCase("description")) { builder.setDescription(parser.nextText()); } else if (name.equalsIgnoreCase("id")) { Id tracksId = Id.create(Long.parseLong(parser.nextText())); builder.setTracksId(tracksId); } else if (name.equalsIgnoreCase("updated-at")) { String dateStr = parser.nextText(); long modifiedDate = DateUtils.parseIso8601Date(dateStr); builder.setModifiedDate(modifiedDate); } else if (name.equalsIgnoreCase("context-id")) { String tokenValue = parser.nextText(); if (!TextUtils.isEmpty(tokenValue)) { Id tracksId = Id.create(Long.parseLong(tokenValue)); Id contextId = findContextIdByTracksId(tracksId); builder.setContextId(contextId); } } else if (name.equalsIgnoreCase("project-id")) { String tokenValue = parser.nextText(); if (!TextUtils.isEmpty(tokenValue)) { Id tracksId = Id.create(Long.parseLong(tokenValue)); Id projectId = findProjectIdByTracksId(tracksId); builder.setProjectId(projectId); } } else if (name.equalsIgnoreCase("notes")) { builder.setDetails(parser.nextText()); } else if (name.equalsIgnoreCase("created-at")) { String dateStr = parser.nextText(); if (!TextUtils.isEmpty(dateStr)) { long created = DateUtils.parseIso8601Date(dateStr); builder.setCreatedDate(created); } } else if (name.equalsIgnoreCase("due")) { String dateStr = parser.nextText(); if (!TextUtils.isEmpty(dateStr)) { dueDate = DateUtils.parseIso8601Date(dateStr); builder.setDueDate(dueDate); } } else if (name.equalsIgnoreCase("show-from")) { String dateStr = parser.nextText(); if (!TextUtils.isEmpty(dateStr)) { startDate = DateUtils.parseIso8601Date(dateStr); builder.setStartDate(startDate); } } break; case XmlPullParser.END_TAG: if (name.equalsIgnoreCase("todo")) { boolean allDay = startDate > 0L || dueDate > 0L; builder.setAllDay(allDay); task = builder.build(); } break; } eventType = parser.next(); } } catch (IOException e) { throw new ParseException("Unable to parse task:" + e.getMessage(), 0); } catch (XmlPullParserException e) { throw new ParseException("Unable to parse task:" + e.getMessage(), 0); } return task; } @Override protected String createEntityUrl(Task task) { return mTracksUrl + "/todos/" + task.getTracksId() + ".xml"; } @Override protected String endIndexTag() { return "todos"; } @Override protected String entityIndexUrl() { return mTracksUrl + "/todos.xml"; } }