mirror of https://github.com/tasks/tasks
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
628 lines
25 KiB
Java
628 lines
25 KiB
Java
/**
|
|
* See the file "LICENSE" for the full license governing this code.
|
|
*/
|
|
package org.weloveastrid.rmilk.sync;
|
|
|
|
import java.io.IOException;
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.Date;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
|
|
import org.weloveastrid.rmilk.MilkBackgroundService;
|
|
import org.weloveastrid.rmilk.MilkDependencyInjector;
|
|
import org.weloveastrid.rmilk.MilkLoginActivity;
|
|
import org.weloveastrid.rmilk.MilkPreferences;
|
|
import org.weloveastrid.rmilk.MilkUtilities;
|
|
import org.weloveastrid.rmilk.MilkLoginActivity.SyncLoginCallback;
|
|
import org.weloveastrid.rmilk.api.ApplicationInfo;
|
|
import org.weloveastrid.rmilk.api.ServiceImpl;
|
|
import org.weloveastrid.rmilk.api.ServiceInternalException;
|
|
import org.weloveastrid.rmilk.api.data.RtmList;
|
|
import org.weloveastrid.rmilk.api.data.RtmLists;
|
|
import org.weloveastrid.rmilk.api.data.RtmTask;
|
|
import org.weloveastrid.rmilk.api.data.RtmTaskList;
|
|
import org.weloveastrid.rmilk.api.data.RtmTaskNote;
|
|
import org.weloveastrid.rmilk.api.data.RtmTaskSeries;
|
|
import org.weloveastrid.rmilk.api.data.RtmTasks;
|
|
import org.weloveastrid.rmilk.api.data.RtmAuth.Perms;
|
|
import org.weloveastrid.rmilk.api.data.RtmTask.Priority;
|
|
import org.weloveastrid.rmilk.data.MilkListService;
|
|
import org.weloveastrid.rmilk.data.MilkMetadataService;
|
|
import org.weloveastrid.rmilk.data.MilkNoteFields;
|
|
|
|
import android.app.Activity;
|
|
import android.app.Notification;
|
|
import android.app.PendingIntent;
|
|
import android.app.Service;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.res.Resources;
|
|
import android.os.Handler;
|
|
import android.text.TextUtils;
|
|
import android.util.Log;
|
|
|
|
import com.timsu.astrid.R;
|
|
import com.todoroo.andlib.data.Property;
|
|
import com.todoroo.andlib.data.TodorooCursor;
|
|
import com.todoroo.andlib.service.Autowired;
|
|
import com.todoroo.andlib.service.ContextManager;
|
|
import com.todoroo.andlib.service.DependencyInjectionService;
|
|
import com.todoroo.andlib.utility.AndroidUtilities;
|
|
import com.todoroo.andlib.utility.DateUtilities;
|
|
import com.todoroo.andlib.utility.DialogUtilities;
|
|
import com.todoroo.astrid.api.AstridApiConstants;
|
|
import com.todoroo.astrid.data.Metadata;
|
|
import com.todoroo.astrid.data.Task;
|
|
import com.todoroo.astrid.sync.SyncProvider;
|
|
|
|
public class MilkSyncProvider extends SyncProvider<MilkTaskContainer> {
|
|
|
|
private ServiceImpl rtmService = null;
|
|
private String timeline = null;
|
|
|
|
@Autowired private MilkMetadataService milkMetadataService;
|
|
@Autowired private MilkListService milkListService;
|
|
|
|
static {
|
|
MilkDependencyInjector.initialize();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
// ------------------------------------------------------- public methods
|
|
// ----------------------------------------------------------------------
|
|
|
|
/**
|
|
* Sign out of RTM, deleting all synchronization metadata
|
|
*/
|
|
public void signOut(Context context) {
|
|
ContextManager.setContext(context);
|
|
MilkUtilities.INSTANCE.setToken(null);
|
|
MilkUtilities.INSTANCE.clearLastSyncDate();
|
|
|
|
DependencyInjectionService.getInstance().inject(this);
|
|
milkMetadataService.clearMetadata();
|
|
}
|
|
|
|
/**
|
|
* Deal with a synchronization exception. If requested, will show an error
|
|
* to the user (unless synchronization is happening in background)
|
|
*
|
|
* @param context
|
|
* @param tag
|
|
* error tag
|
|
* @param e
|
|
* exception
|
|
* @param showError
|
|
* whether to display a dialog
|
|
*/
|
|
@Override
|
|
protected void handleException(String tag, Exception e, boolean showError) {
|
|
final Context context = ContextManager.getContext();
|
|
MilkUtilities.INSTANCE.setLastError(e.toString());
|
|
|
|
String message = null;
|
|
|
|
// occurs when application was closed
|
|
if(e instanceof IllegalStateException) {
|
|
Log.e(tag, "caught", e); //$NON-NLS-1$
|
|
|
|
// occurs when network error
|
|
} else if(e instanceof ServiceInternalException &&
|
|
((ServiceInternalException)e).getEnclosedException() instanceof
|
|
IOException) {
|
|
Exception enclosedException = ((ServiceInternalException)e).getEnclosedException();
|
|
message = context.getString(R.string.rmilk_ioerror);
|
|
Log.e(tag, "ioexception", enclosedException); //$NON-NLS-1$
|
|
} else {
|
|
if(e instanceof ServiceInternalException)
|
|
e = ((ServiceInternalException)e).getEnclosedException();
|
|
if(e != null)
|
|
message = e.toString();
|
|
Log.e(tag, "unhandled", e); //$NON-NLS-1$
|
|
}
|
|
|
|
if(showError && context instanceof Activity && message != null) {
|
|
DialogUtilities.okDialog((Activity)context, message, null);
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
// ------------------------------------------------------ initiating sync
|
|
// ----------------------------------------------------------------------
|
|
|
|
/**
|
|
* set up service
|
|
*/
|
|
@SuppressWarnings("nls")
|
|
private void initializeService(String authToken) throws ServiceInternalException {
|
|
String appName = null;
|
|
String z = stripslashes(0,"q9883o3384n21snq17501qn38oo1r689", "b");
|
|
String v = stripslashes(16,"19o2n020345219os","a");
|
|
|
|
if(authToken == null)
|
|
rtmService = new ServiceImpl(new ApplicationInfo(
|
|
z, v, appName));
|
|
else
|
|
rtmService = new ServiceImpl(new ApplicationInfo(
|
|
z, v, appName, authToken));
|
|
}
|
|
|
|
/**
|
|
* initiate sync in background
|
|
*/
|
|
@Override
|
|
@SuppressWarnings("nls")
|
|
protected void initiateBackground(Service service) {
|
|
DependencyInjectionService.getInstance().inject(this);
|
|
|
|
try {
|
|
String authToken = MilkUtilities.INSTANCE.getToken();
|
|
|
|
// check if we have a token & it works
|
|
if(authToken != null) {
|
|
initializeService(authToken);
|
|
if(!rtmService.isServiceAuthorized()) // re-do login
|
|
authToken = null;
|
|
}
|
|
|
|
if(authToken == null) {
|
|
// try completing the authorization if it was partial
|
|
if(rtmService != null) {
|
|
try {
|
|
String token = rtmService.completeAuthorization();
|
|
MilkUtilities.INSTANCE.setToken(token);
|
|
performSync();
|
|
|
|
return;
|
|
} catch (Exception e) {
|
|
// didn't work. do the process again.
|
|
}
|
|
}
|
|
|
|
// can't do anything, user not logged in
|
|
|
|
} else {
|
|
performSync();
|
|
}
|
|
} catch (IllegalStateException e) {
|
|
// occurs when application was closed
|
|
} catch (Exception e) {
|
|
handleException("rtm-authenticate", e, true);
|
|
} finally {
|
|
MilkUtilities.INSTANCE.stopOngoing();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* If user isn't already signed in, show sign in dialog. Else perform sync.
|
|
*/
|
|
@SuppressWarnings("nls")
|
|
@Override
|
|
protected void initiateManual(final Activity activity) {
|
|
final Resources r = activity.getResources();
|
|
String authToken = MilkUtilities.INSTANCE.getToken();
|
|
MilkUtilities.INSTANCE.stopOngoing();
|
|
|
|
// check if we have a token & it works
|
|
if(authToken == null) {
|
|
// open up a dialog and have the user go to browser
|
|
final String url;
|
|
try {
|
|
initializeService(null);
|
|
url = rtmService.beginAuthorization(Perms.delete);
|
|
} catch (Exception e) {
|
|
handleException("rmilk-auth", e, true);
|
|
return;
|
|
}
|
|
|
|
Intent intent = new Intent(activity, MilkLoginActivity.class);
|
|
MilkLoginActivity.setCallback(new SyncLoginCallback() {
|
|
public String verifyLogin(final Handler syncLoginHandler) {
|
|
if(rtmService == null) {
|
|
return null;
|
|
}
|
|
try {
|
|
String token = rtmService.completeAuthorization();
|
|
MilkUtilities.INSTANCE.setToken(token);
|
|
synchronize(activity);
|
|
return null;
|
|
} catch (Exception e) {
|
|
// didn't work
|
|
handleException("rtm-verify-login", e, false);
|
|
rtmService = null;
|
|
if(e instanceof ServiceInternalException)
|
|
e = ((ServiceInternalException)e).getEnclosedException();
|
|
return r.getString(R.string.rmilk_MLA_error, e.getMessage());
|
|
}
|
|
}
|
|
});
|
|
intent.putExtra(MilkLoginActivity.URL_TOKEN, url);
|
|
activity.startActivityForResult(intent, 0);
|
|
} else {
|
|
activity.startService(new Intent(MilkBackgroundService.SYNC_ACTION, null,
|
|
activity, MilkBackgroundService.class));
|
|
activity.finish();
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
// ----------------------------------------------------- synchronization!
|
|
// ----------------------------------------------------------------------
|
|
|
|
protected void performSync() {
|
|
try {
|
|
// get RTM timeline
|
|
timeline = rtmService.timelines_create();
|
|
|
|
// load RTM lists
|
|
RtmLists lists = rtmService.lists_getList();
|
|
milkListService.setLists(lists);
|
|
|
|
// read all tasks
|
|
ArrayList<MilkTaskContainer> remoteChanges = new ArrayList<MilkTaskContainer>();
|
|
Date lastSyncDate = new Date(MilkUtilities.INSTANCE.getLastSyncDate());
|
|
boolean shouldSyncIndividualLists = false;
|
|
String filter = null;
|
|
if(lastSyncDate.getTime() == 0)
|
|
filter = "status:incomplete"; //$NON-NLS-1$ // 1st time sync: get unfinished tasks
|
|
|
|
// try the quick synchronization
|
|
try {
|
|
Thread.sleep(1000); // throttle
|
|
RtmTasks tasks = rtmService.tasks_getList(null, filter, lastSyncDate);
|
|
addTasksToList(tasks, remoteChanges);
|
|
} catch (Exception e) {
|
|
handleException("rtm-quick-sync", e, false); //$NON-NLS-1$
|
|
remoteChanges.clear();
|
|
shouldSyncIndividualLists = true;
|
|
}
|
|
|
|
if(shouldSyncIndividualLists) {
|
|
for(RtmList list : lists.getLists().values()) {
|
|
if(list.isSmart())
|
|
continue;
|
|
try {
|
|
Thread.sleep(1500);
|
|
RtmTasks tasks = rtmService.tasks_getList(list.getId(),
|
|
filter, lastSyncDate);
|
|
addTasksToList(tasks, remoteChanges);
|
|
} catch (Exception e) {
|
|
handleException("rtm-indiv-sync", e, true); //$NON-NLS-1$
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
SyncData<MilkTaskContainer> syncData = populateSyncData(remoteChanges);
|
|
try {
|
|
synchronizeTasks(syncData);
|
|
} finally {
|
|
syncData.localCreated.close();
|
|
syncData.localUpdated.close();
|
|
}
|
|
|
|
MilkUtilities.INSTANCE.recordSuccessfulSync();
|
|
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_EVENT_REFRESH);
|
|
ContextManager.getContext().sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
|
|
|
|
} catch (IllegalStateException e) {
|
|
// occurs when application was closed
|
|
} catch (Exception e) {
|
|
handleException("rtm-sync", e, true); //$NON-NLS-1$
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
// ------------------------------------------------------------ sync data
|
|
// ----------------------------------------------------------------------
|
|
|
|
// all synchronized properties
|
|
private static final Property<?>[] PROPERTIES = new Property<?>[] {
|
|
Task.ID,
|
|
Task.TITLE,
|
|
Task.IMPORTANCE,
|
|
Task.DUE_DATE,
|
|
Task.CREATION_DATE,
|
|
Task.COMPLETION_DATE,
|
|
Task.DELETION_DATE,
|
|
Task.NOTES,
|
|
};
|
|
|
|
/**
|
|
* Populate SyncData data structure
|
|
*/
|
|
private SyncData<MilkTaskContainer> populateSyncData(ArrayList<MilkTaskContainer> remoteTasks) {
|
|
// fetch locally created tasks
|
|
TodorooCursor<Task> localCreated = milkMetadataService.getLocallyCreated(PROPERTIES);
|
|
|
|
// fetch locally updated tasks
|
|
TodorooCursor<Task> localUpdated = milkMetadataService.getLocallyUpdated(PROPERTIES);
|
|
|
|
return new SyncData<MilkTaskContainer>(remoteTasks, localCreated, localUpdated);
|
|
}
|
|
|
|
/**
|
|
* Add the tasks read from RTM to the given list
|
|
*/
|
|
private void addTasksToList(RtmTasks tasks, ArrayList<MilkTaskContainer> list) {
|
|
for (RtmTaskList taskList : tasks.getLists()) {
|
|
for (RtmTaskSeries taskSeries : taskList.getSeries()) {
|
|
MilkTaskContainer remote = parseRemoteTask(taskSeries);
|
|
|
|
// update reminder flags for incoming remote tasks to prevent annoying
|
|
if(remote.task.hasDueDate() && remote.task.getValue(Task.DUE_DATE) < DateUtilities.now())
|
|
remote.task.setFlag(Task.REMINDER_FLAGS, Task.NOTIFY_AFTER_DEADLINE, false);
|
|
|
|
milkMetadataService.findLocalMatch(remote);
|
|
list.add(remote);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
// ------------------------------------------------- create / push / pull
|
|
// ----------------------------------------------------------------------
|
|
|
|
@Override
|
|
protected MilkTaskContainer create(MilkTaskContainer task) throws IOException {
|
|
String listId = null;
|
|
if(task.listId > 0)
|
|
listId = Long.toString(task.listId);
|
|
RtmTaskSeries rtmTask = rtmService.tasks_add(timeline, listId,
|
|
task.task.getValue(Task.TITLE));
|
|
MilkTaskContainer newRemoteTask = parseRemoteTask(rtmTask);
|
|
transferIdentifiers(newRemoteTask, task);
|
|
push(task, newRemoteTask);
|
|
return newRemoteTask;
|
|
}
|
|
|
|
/**
|
|
* Determine whether this task's property should be transmitted
|
|
* @param task task to consider
|
|
* @param property property to consider
|
|
* @param remoteTask remote task proxy
|
|
* @return
|
|
*/
|
|
private boolean shouldTransmit(MilkTaskContainer task, Property<?> property, MilkTaskContainer remoteTask) {
|
|
if(!task.task.containsValue(property))
|
|
return false;
|
|
|
|
if(remoteTask == null)
|
|
return true;
|
|
if(!remoteTask.task.containsValue(property))
|
|
return true;
|
|
|
|
// special cases - match if they're zero or nonzero
|
|
if(property == Task.COMPLETION_DATE ||
|
|
property == Task.DELETION_DATE)
|
|
return !AndroidUtilities.equals((Long)task.task.getValue(property) == 0,
|
|
(Long)remoteTask.task.getValue(property) == 0);
|
|
|
|
return !AndroidUtilities.equals(task.task.getValue(property),
|
|
remoteTask.task.getValue(property));
|
|
}
|
|
|
|
/**
|
|
* Send changes for the given Task across the wire. If a remoteTask is
|
|
* supplied, we attempt to intelligently only transmit the values that
|
|
* have changed.
|
|
*/
|
|
@Override
|
|
protected void push(MilkTaskContainer local, MilkTaskContainer remote) throws IOException {
|
|
boolean remerge = false;
|
|
|
|
// fetch remote task for comparison
|
|
if(remote == null)
|
|
remote = pull(local);
|
|
|
|
String listId = Long.toString(local.listId);
|
|
String taskSeriesId = Long.toString(local.taskSeriesId);
|
|
String taskId = Long.toString(local.taskId);
|
|
|
|
if(remote != null && !AndroidUtilities.equals(local.listId, remote.listId))
|
|
rtmService.tasks_moveTo(timeline, Long.toString(remote.listId),
|
|
listId, taskSeriesId, taskId);
|
|
|
|
// either delete or re-create if necessary
|
|
if(shouldTransmit(local, Task.DELETION_DATE, remote)) {
|
|
if(local.task.getValue(Task.DELETION_DATE) > 0)
|
|
rtmService.tasks_delete(timeline, listId, taskSeriesId, taskId);
|
|
else if(remote == null) {
|
|
RtmTaskSeries rtmTask = rtmService.tasks_add(timeline, listId,
|
|
local.task.getValue(Task.TITLE));
|
|
remote = parseRemoteTask(rtmTask);
|
|
transferIdentifiers(remote, local);
|
|
}
|
|
}
|
|
|
|
if(shouldTransmit(local, Task.TITLE, remote))
|
|
rtmService.tasks_setName(timeline, listId, taskSeriesId,
|
|
taskId, local.task.getValue(Task.TITLE));
|
|
if(shouldTransmit(local, Task.IMPORTANCE, remote))
|
|
rtmService.tasks_setPriority(timeline, listId, taskSeriesId,
|
|
taskId, Priority.values(local.task.getValue(Task.IMPORTANCE)));
|
|
if(shouldTransmit(local, Task.DUE_DATE, remote))
|
|
rtmService.tasks_setDueDate(timeline, listId, taskSeriesId,
|
|
taskId, DateUtilities.unixtimeToDate(local.task.getValue(Task.DUE_DATE)),
|
|
local.task.hasDueTime());
|
|
if(shouldTransmit(local, Task.COMPLETION_DATE, remote)) {
|
|
if(local.task.getValue(Task.COMPLETION_DATE) == 0)
|
|
rtmService.tasks_uncomplete(timeline, listId, taskSeriesId,
|
|
taskId);
|
|
else {
|
|
rtmService.tasks_complete(timeline, listId, taskSeriesId,
|
|
taskId);
|
|
// if repeating, pull and merge
|
|
if(local.repeating)
|
|
remerge = true;
|
|
}
|
|
}
|
|
|
|
// tags
|
|
HashSet<String> localTags = new HashSet<String>();
|
|
HashSet<String> remoteTags = new HashSet<String>();
|
|
for(Metadata item : local.metadata)
|
|
if(MilkMetadataService.TAG_KEY.equals(item.getValue(Metadata.KEY)))
|
|
localTags.add(item.getValue(Metadata.VALUE1));
|
|
if(remote != null && remote.metadata != null) {
|
|
for(Metadata item : remote.metadata)
|
|
if(MilkMetadataService.TAG_KEY.equals(item.getValue(Metadata.KEY)))
|
|
remoteTags.add(item.getValue(Metadata.VALUE1));
|
|
}
|
|
if(!localTags.equals(remoteTags)) {
|
|
String[] tags = localTags.toArray(new String[localTags.size()]);
|
|
rtmService.tasks_setTags(timeline, listId, taskSeriesId,
|
|
taskId, tags);
|
|
}
|
|
|
|
// notes
|
|
if(shouldTransmit(local, Task.NOTES, remote)) {
|
|
String[] titleAndText = MilkNoteFields.fromNoteField(local.task.getValue(Task.NOTES));
|
|
List<RtmTaskNote> notes = null;
|
|
if(remote != null && remote.remote.getNotes() != null)
|
|
notes = remote.remote.getNotes().getNotes();
|
|
if(notes != null && notes.size() > 0) {
|
|
String remoteNoteId = notes.get(0).getId();
|
|
rtmService.tasks_notes_edit(timeline, remoteNoteId, titleAndText[0],
|
|
titleAndText[1]);
|
|
} else {
|
|
rtmService.tasks_notes_add(timeline, listId, taskSeriesId,
|
|
taskId, titleAndText[0], titleAndText[1]);
|
|
}
|
|
}
|
|
|
|
if(remerge) {
|
|
remote = pull(local);
|
|
remote.task.setId(local.task.getId());
|
|
|
|
// transform local into remote
|
|
local.task = remote.task;
|
|
local.listId = remote.listId;
|
|
local.taskId = remote.taskId;
|
|
local.repeating = remote.repeating;
|
|
local.taskSeriesId = remote.taskSeriesId;
|
|
}
|
|
}
|
|
|
|
/** Create a task container for the given RtmTaskSeries */
|
|
private MilkTaskContainer parseRemoteTask(RtmTaskSeries rtmTaskSeries) {
|
|
Task task = new Task();
|
|
RtmTask rtmTask = rtmTaskSeries.getTask();
|
|
ArrayList<Metadata> metadata = new ArrayList<Metadata>();
|
|
|
|
task.setValue(Task.TITLE, rtmTaskSeries.getName());
|
|
task.setValue(Task.CREATION_DATE, DateUtilities.dateToUnixtime(rtmTask.getAdded()));
|
|
task.setValue(Task.COMPLETION_DATE, DateUtilities.dateToUnixtime(rtmTask.getCompleted()));
|
|
task.setValue(Task.DELETION_DATE, DateUtilities.dateToUnixtime(rtmTask.getDeleted()));
|
|
if(rtmTask.getDue() != null) {
|
|
task.setValue(Task.DUE_DATE,
|
|
task.createDueDate(rtmTask.getHasDueTime() ? Task.URGENCY_SPECIFIC_DAY_TIME :
|
|
Task.URGENCY_SPECIFIC_DAY, DateUtilities.dateToUnixtime(rtmTask.getDue())));
|
|
} else {
|
|
task.setValue(Task.DUE_DATE, 0L);
|
|
}
|
|
task.setValue(Task.IMPORTANCE, rtmTask.getPriority().ordinal());
|
|
|
|
if(rtmTaskSeries.getTags() != null) {
|
|
for(String tag : rtmTaskSeries.getTags()) {
|
|
Metadata tagData = new Metadata();
|
|
tagData.setValue(Metadata.KEY, MilkMetadataService.TAG_KEY);
|
|
tagData.setValue(Metadata.VALUE1, tag);
|
|
metadata.add(tagData);
|
|
}
|
|
}
|
|
|
|
task.setValue(Task.NOTES, ""); //$NON-NLS-1$
|
|
if(rtmTaskSeries.getNotes() != null && rtmTaskSeries.getNotes().getNotes().size() > 0) {
|
|
boolean firstNote = true;
|
|
Collections.reverse(rtmTaskSeries.getNotes().getNotes()); // reverse so oldest is first
|
|
for(RtmTaskNote note : rtmTaskSeries.getNotes().getNotes()) {
|
|
if(firstNote) {
|
|
firstNote = false;
|
|
task.setValue(Task.NOTES, MilkNoteFields.toNoteField(note));
|
|
} else
|
|
metadata.add(MilkNoteFields.create(note));
|
|
}
|
|
}
|
|
|
|
MilkTaskContainer container = new MilkTaskContainer(task, metadata, rtmTaskSeries);
|
|
|
|
return container;
|
|
}
|
|
|
|
@Override
|
|
protected MilkTaskContainer pull(MilkTaskContainer task) throws IOException {
|
|
if(task.taskSeriesId == 0)
|
|
throw new ServiceInternalException("Tried to read an invalid task"); //$NON-NLS-1$
|
|
RtmTaskSeries rtmTask = rtmService.tasks_getTask(Long.toString(task.taskSeriesId),
|
|
task.task.getValue(Task.TITLE));
|
|
if(rtmTask != null)
|
|
return parseRemoteTask(rtmTask);
|
|
return null;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
// --------------------------------------------------------- read / write
|
|
// ----------------------------------------------------------------------
|
|
|
|
@Override
|
|
protected MilkTaskContainer read(TodorooCursor<Task> cursor) throws IOException {
|
|
return milkMetadataService.readTaskAndMetadata(cursor);
|
|
}
|
|
|
|
@Override
|
|
protected void write(MilkTaskContainer task) throws IOException {
|
|
milkMetadataService.saveTaskAndMetadata(task);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
// --------------------------------------------------------- misc helpers
|
|
// ----------------------------------------------------------------------
|
|
|
|
@Override
|
|
protected int matchTask(ArrayList<MilkTaskContainer> tasks, MilkTaskContainer target) {
|
|
int length = tasks.size();
|
|
for(int i = 0; i < length; i++) {
|
|
MilkTaskContainer task = tasks.get(i);
|
|
if(AndroidUtilities.equals(task.listId, target.listId) &&
|
|
AndroidUtilities.equals(task.taskSeriesId, target.taskSeriesId) &&
|
|
AndroidUtilities.equals(task.taskId, target.taskId))
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
@Override
|
|
protected int updateNotification(Context context, Notification notification) {
|
|
String notificationTitle = context.getString(R.string.rmilk_notification_title);
|
|
Intent intent = new Intent(context, MilkPreferences.class);
|
|
PendingIntent notificationIntent = PendingIntent.getActivity(context, 0,
|
|
intent, 0);
|
|
notification.setLatestEventInfo(context,
|
|
notificationTitle, context.getString(R.string.SyP_progress),
|
|
notificationIntent);
|
|
return 0;
|
|
}
|
|
|
|
@Override
|
|
protected void transferIdentifiers(MilkTaskContainer source,
|
|
MilkTaskContainer destination) {
|
|
destination.listId = source.listId;
|
|
destination.taskSeriesId = source.taskSeriesId;
|
|
destination.taskId = source.taskId;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
// ------------------------------------------------------- helper classes
|
|
// ----------------------------------------------------------------------
|
|
|
|
private static final String stripslashes(int ____,String __,String ___) {
|
|
int _=__.charAt(____/92);_=_==115?_-1:_;_=((_>=97)&&(_<=123)?
|
|
((_-83)%27+97):_);return TextUtils.htmlEncode(____==31?___:
|
|
stripslashes(____+1,__.substring(1),___+((char)_)));
|
|
}
|
|
|
|
}
|