mirror of https://github.com/tasks/tasks
Removed gtasks plugin into its own project
parent
d2d4f8150b
commit
84fbfc1cbd
@ -1,37 +0,0 @@
|
|||||||
package com.todoroo.astrid.gtasks;
|
|
||||||
|
|
||||||
import com.flurry.android.FlurryAgent;
|
|
||||||
import com.todoroo.andlib.service.Autowired;
|
|
||||||
import com.todoroo.astrid.gtasks.sync.GtasksSyncProvider;
|
|
||||||
import com.todoroo.astrid.sync.SyncBackgroundService;
|
|
||||||
import com.todoroo.astrid.sync.SyncProvider;
|
|
||||||
import com.todoroo.astrid.sync.SyncProviderUtilities;
|
|
||||||
import com.todoroo.astrid.utility.Constants;
|
|
||||||
|
|
||||||
public class GtasksBackgroundService extends SyncBackgroundService {
|
|
||||||
|
|
||||||
@Autowired private GtasksPreferenceService gtasksPreferenceService;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected SyncProvider<?> getSyncProvider() {
|
|
||||||
return new GtasksSyncProvider();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected SyncProviderUtilities getSyncUtilities() {
|
|
||||||
return gtasksPreferenceService;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate() {
|
|
||||||
super.onCreate();
|
|
||||||
FlurryAgent.onStartSession(this, Constants.FLURRY_KEY);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroy() {
|
|
||||||
FlurryAgent.onEndSession(this);
|
|
||||||
super.onDestroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,64 +0,0 @@
|
|||||||
/**
|
|
||||||
* See the file "LICENSE" for the full license governing this code.
|
|
||||||
*/
|
|
||||||
package com.todoroo.astrid.gtasks;
|
|
||||||
|
|
||||||
import android.widget.RemoteViews;
|
|
||||||
|
|
||||||
import com.timsu.astrid.R;
|
|
||||||
import com.todoroo.andlib.service.Autowired;
|
|
||||||
import com.todoroo.andlib.service.ContextManager;
|
|
||||||
import com.todoroo.andlib.service.DependencyInjectionService;
|
|
||||||
import com.todoroo.astrid.api.TaskDecoration;
|
|
||||||
import com.todoroo.astrid.api.TaskDecorationExposer;
|
|
||||||
import com.todoroo.astrid.data.Metadata;
|
|
||||||
import com.todoroo.astrid.data.Task;
|
|
||||||
import com.todoroo.astrid.utility.Flags;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exposes {@link TaskDecoration} for GTasks indentation
|
|
||||||
*
|
|
||||||
* @author Tim Su <tim@todoroo.com>
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class GtasksDecorationExposer implements TaskDecorationExposer {
|
|
||||||
|
|
||||||
@Autowired private GtasksMetadataService gtasksMetadataService;
|
|
||||||
@Autowired private GtasksPreferenceService gtasksPreferenceService;
|
|
||||||
|
|
||||||
public GtasksDecorationExposer() {
|
|
||||||
DependencyInjectionService.getInstance().inject(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public TaskDecoration expose(Task task) {
|
|
||||||
if(!gtasksPreferenceService.isLoggedIn())
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if(!Flags.check(Flags.GTASKS))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return createDecoration(task);
|
|
||||||
}
|
|
||||||
|
|
||||||
private TaskDecoration createDecoration(Task task) {
|
|
||||||
Metadata metadata = gtasksMetadataService.getTaskMetadata(task.getId());
|
|
||||||
if(metadata == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
int indentation = metadata.getValue(GtasksMetadata.INDENT);
|
|
||||||
if(indentation <= 0)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
RemoteViews decoration = new RemoteViews(ContextManager.getContext().getPackageName(),
|
|
||||||
R.layout.gtasks_decoration);
|
|
||||||
decoration.setInt(R.id.indent, "setMinWidth", indentation * 20); //$NON-NLS-1$
|
|
||||||
return new TaskDecoration(decoration, TaskDecoration.POSITION_LEFT, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getAddon() {
|
|
||||||
return GtasksPreferenceService.IDENTIFIER;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,83 +0,0 @@
|
|||||||
/**
|
|
||||||
* See the file "LICENSE" for the full license governing this code.
|
|
||||||
*/
|
|
||||||
package com.todoroo.astrid.gtasks;
|
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
|
|
||||||
import com.todoroo.andlib.service.Autowired;
|
|
||||||
import com.todoroo.andlib.service.ContextManager;
|
|
||||||
import com.todoroo.andlib.service.DependencyInjectionService;
|
|
||||||
import com.todoroo.andlib.utility.Preferences;
|
|
||||||
import com.todoroo.astrid.api.AstridApiConstants;
|
|
||||||
import com.todoroo.astrid.data.Metadata;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exposes Task Details for Google TAsks:
|
|
||||||
* - list
|
|
||||||
*
|
|
||||||
* @author Tim Su <tim@todoroo.com>
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class GtasksDetailExposer extends BroadcastReceiver {
|
|
||||||
|
|
||||||
public static final String DETAIL_SEPARATOR = " | "; //$NON-NLS-1$
|
|
||||||
|
|
||||||
@Autowired private GtasksMetadataService gtasksMetadataService;
|
|
||||||
@Autowired private GtasksListService gtasksListService;
|
|
||||||
@Autowired private GtasksPreferenceService gtasksPreferenceService;
|
|
||||||
|
|
||||||
public GtasksDetailExposer() {
|
|
||||||
DependencyInjectionService.getInstance().inject(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
ContextManager.setContext(context);
|
|
||||||
|
|
||||||
// if we aren't logged in, don't expose features
|
|
||||||
if(!gtasksPreferenceService.isLoggedIn())
|
|
||||||
return;
|
|
||||||
|
|
||||||
long taskId = intent.getLongExtra(AstridApiConstants.EXTRAS_TASK_ID, -1);
|
|
||||||
if(taskId == -1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
boolean extended = intent.getBooleanExtra(AstridApiConstants.EXTRAS_EXTENDED, false);
|
|
||||||
String taskDetail = getTaskDetails(taskId, extended);
|
|
||||||
if(taskDetail == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_DETAILS);
|
|
||||||
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, GtasksPreferenceService.IDENTIFIER);
|
|
||||||
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId);
|
|
||||||
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_EXTENDED, extended);
|
|
||||||
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, taskDetail);
|
|
||||||
context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTaskDetails(long id, boolean extended) {
|
|
||||||
if(extended)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
Metadata metadata = gtasksMetadataService.getTaskMetadata(id);
|
|
||||||
if(metadata == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
StringBuilder builder = new StringBuilder();
|
|
||||||
|
|
||||||
String listId = metadata.getValue(GtasksMetadata.LIST_ID);
|
|
||||||
if(listId == null || listId.equals(Preferences.getStringValue(GtasksPreferenceService.PREF_DEFAULT_LIST)))
|
|
||||||
return null;
|
|
||||||
String listName = gtasksListService.getListName(listId);
|
|
||||||
if(listName == GtasksListService.LIST_NOT_FOUND)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
builder.append("<img src='silk_folder'/> ").append(listName); //$NON-NLS-1$
|
|
||||||
|
|
||||||
return builder.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,100 +0,0 @@
|
|||||||
/**
|
|
||||||
* See the file "LICENSE" for the full license governing this code.
|
|
||||||
*/
|
|
||||||
package com.todoroo.astrid.gtasks;
|
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.ContentValues;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
|
|
||||||
import com.timsu.astrid.R;
|
|
||||||
import com.todoroo.andlib.data.AbstractModel;
|
|
||||||
import com.todoroo.andlib.service.Autowired;
|
|
||||||
import com.todoroo.andlib.service.ContextManager;
|
|
||||||
import com.todoroo.andlib.service.DependencyInjectionService;
|
|
||||||
import com.todoroo.andlib.sql.Criterion;
|
|
||||||
import com.todoroo.andlib.sql.Functions;
|
|
||||||
import com.todoroo.andlib.sql.Join;
|
|
||||||
import com.todoroo.andlib.sql.Order;
|
|
||||||
import com.todoroo.andlib.sql.QueryTemplate;
|
|
||||||
import com.todoroo.astrid.api.AstridApiConstants;
|
|
||||||
import com.todoroo.astrid.api.Filter;
|
|
||||||
import com.todoroo.astrid.api.FilterCategory;
|
|
||||||
import com.todoroo.astrid.api.FilterListHeader;
|
|
||||||
import com.todoroo.astrid.api.FilterListItem;
|
|
||||||
import com.todoroo.astrid.data.Metadata;
|
|
||||||
import com.todoroo.astrid.data.StoreObject;
|
|
||||||
import com.todoroo.astrid.data.Task;
|
|
||||||
import com.todoroo.astrid.data.MetadataApiDao.MetadataCriteria;
|
|
||||||
import com.todoroo.astrid.data.TaskApiDao.TaskCriteria;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exposes filters based on lists
|
|
||||||
*
|
|
||||||
* @author Tim Su <tim@todoroo.com>
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class GtasksFilterExposer extends BroadcastReceiver {
|
|
||||||
|
|
||||||
@Autowired private GtasksListService gtasksListService;
|
|
||||||
@Autowired private GtasksPreferenceService gtasksPreferenceService;
|
|
||||||
|
|
||||||
private StoreObject[] lists;
|
|
||||||
|
|
||||||
public static Filter filterFromList(StoreObject list) {
|
|
||||||
String listName = list.getValue(GtasksList.NAME);
|
|
||||||
ContentValues values = new ContentValues();
|
|
||||||
values.putAll(GtasksMetadata.createEmptyMetadata(AbstractModel.NO_ID).getMergedValues());
|
|
||||||
values.remove(Metadata.TASK.name);
|
|
||||||
values.put(GtasksMetadata.LIST_ID.name, list.getValue(GtasksList.REMOTE_ID));
|
|
||||||
Filter filter = new Filter(listName, listName, new QueryTemplate().join(
|
|
||||||
Join.left(Metadata.TABLE, Task.ID.eq(Metadata.TASK))).where(Criterion.and(
|
|
||||||
MetadataCriteria.withKey(GtasksMetadata.METADATA_KEY),
|
|
||||||
TaskCriteria.isVisible(),
|
|
||||||
TaskCriteria.notDeleted(),
|
|
||||||
GtasksMetadata.LIST_ID.eq(list.getValue(GtasksList.REMOTE_ID)))).orderBy(
|
|
||||||
Order.asc(Functions.cast(GtasksMetadata.ORDER, "INTEGER"))), //$NON-NLS-1$
|
|
||||||
values);
|
|
||||||
|
|
||||||
return filter;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
ContextManager.setContext(context);
|
|
||||||
DependencyInjectionService.getInstance().inject(this);
|
|
||||||
|
|
||||||
// if we aren't logged in, don't expose features
|
|
||||||
if(!gtasksPreferenceService.isLoggedIn())
|
|
||||||
return;
|
|
||||||
|
|
||||||
lists = gtasksListService.getLists();
|
|
||||||
|
|
||||||
// If user does not have any lists, don't show this section at all
|
|
||||||
if(noListsToShow())
|
|
||||||
return;
|
|
||||||
|
|
||||||
Filter[] listFilters = new Filter[lists.length];
|
|
||||||
for(int i = 0; i < lists.length; i++)
|
|
||||||
listFilters[i] = filterFromList(lists[i]);
|
|
||||||
|
|
||||||
FilterListHeader header = new FilterListHeader(context.getString(R.string.gtasks_FEx_header));
|
|
||||||
FilterCategory listsCategory = new FilterCategory(context.getString(R.string.gtasks_FEx_list),
|
|
||||||
listFilters);
|
|
||||||
|
|
||||||
// transmit filter list
|
|
||||||
FilterListItem[] list = new FilterListItem[2];
|
|
||||||
list[0] = header;
|
|
||||||
list[1] = listsCategory;
|
|
||||||
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_FILTERS);
|
|
||||||
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, GtasksPreferenceService.IDENTIFIER);
|
|
||||||
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, list);
|
|
||||||
context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean noListsToShow() {
|
|
||||||
return lists.length == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,64 +0,0 @@
|
|||||||
package com.todoroo.astrid.gtasks;
|
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
|
|
||||||
import com.todoroo.andlib.service.Autowired;
|
|
||||||
import com.todoroo.andlib.service.ContextManager;
|
|
||||||
import com.todoroo.andlib.service.DependencyInjectionService;
|
|
||||||
import com.todoroo.astrid.api.AstridApiConstants;
|
|
||||||
import com.todoroo.astrid.data.Metadata;
|
|
||||||
import com.todoroo.astrid.utility.Flags;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Context Menu actions for changing indent level of a task
|
|
||||||
* @author Tim Su <tim@todoroo.com>
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
abstract public class GtasksIndentAction extends BroadcastReceiver {
|
|
||||||
|
|
||||||
@Autowired private GtasksMetadataService gtasksMetadataService;
|
|
||||||
@Autowired private GtasksTaskListUpdater gtasksTaskListUpdater;
|
|
||||||
|
|
||||||
abstract int getDelta();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
ContextManager.setContext(context);
|
|
||||||
DependencyInjectionService.getInstance().inject(this);
|
|
||||||
|
|
||||||
long taskId = intent.getLongExtra(AstridApiConstants.EXTRAS_TASK_ID, -1);
|
|
||||||
if(taskId == -1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Metadata metadata = gtasksMetadataService.getTaskMetadata(taskId);
|
|
||||||
if(metadata == null) {
|
|
||||||
metadata = GtasksMetadata.createEmptyMetadata(taskId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(metadata.getValue(GtasksMetadata.INDENT) + getDelta() < 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
String listId = metadata.getValue(GtasksMetadata.LIST_ID);
|
|
||||||
gtasksTaskListUpdater.indent(listId, taskId, getDelta());
|
|
||||||
gtasksTaskListUpdater.correctMetadataForList(listId);
|
|
||||||
|
|
||||||
Flags.set(Flags.REFRESH);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class GtasksIncreaseIndentAction extends GtasksIndentAction {
|
|
||||||
@Override
|
|
||||||
public int getDelta() {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class GtasksDecreaseIndentAction extends GtasksIndentAction {
|
|
||||||
@Override
|
|
||||||
public int getDelta() {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,30 +0,0 @@
|
|||||||
package com.todoroo.astrid.gtasks;
|
|
||||||
|
|
||||||
import com.todoroo.andlib.data.Property.IntegerProperty;
|
|
||||||
import com.todoroo.andlib.data.Property.StringProperty;
|
|
||||||
import com.todoroo.astrid.data.StoreObject;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link StoreObject} entries for a GTasks List
|
|
||||||
*
|
|
||||||
* @author Tim Su <tim@todoroo.com>
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class GtasksList {
|
|
||||||
|
|
||||||
/** type*/
|
|
||||||
public static final String TYPE = "gtasks-list"; //$NON-NLS-1$
|
|
||||||
|
|
||||||
/** list id in g-tasks */
|
|
||||||
public static final StringProperty REMOTE_ID = new StringProperty(StoreObject.TABLE,
|
|
||||||
StoreObject.ITEM.name);
|
|
||||||
|
|
||||||
/** list name */
|
|
||||||
public static final StringProperty NAME = new StringProperty(StoreObject.TABLE,
|
|
||||||
StoreObject.VALUE1.name);
|
|
||||||
|
|
||||||
/** list order */
|
|
||||||
public static final IntegerProperty ORDER = new IntegerProperty(StoreObject.TABLE,
|
|
||||||
StoreObject.VALUE2.name);
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,100 +0,0 @@
|
|||||||
package com.todoroo.astrid.gtasks;
|
|
||||||
|
|
||||||
import com.todoroo.andlib.data.TodorooCursor;
|
|
||||||
import com.todoroo.andlib.service.Autowired;
|
|
||||||
import com.todoroo.andlib.service.DependencyInjectionService;
|
|
||||||
import com.todoroo.andlib.sql.Query;
|
|
||||||
import com.todoroo.astrid.dao.StoreObjectDao;
|
|
||||||
import com.todoroo.astrid.dao.StoreObjectDao.StoreObjectCriteria;
|
|
||||||
import com.todoroo.astrid.data.StoreObject;
|
|
||||||
import com.todoroo.gtasks.GoogleTaskListInfo;
|
|
||||||
|
|
||||||
public class GtasksListService {
|
|
||||||
|
|
||||||
public static final String LIST_NOT_FOUND = null;
|
|
||||||
public static final StoreObject LIST_NOT_FOUND_OBJECT = null;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private StoreObjectDao storeObjectDao;
|
|
||||||
|
|
||||||
private StoreObject[] lists = null;
|
|
||||||
|
|
||||||
public GtasksListService() {
|
|
||||||
DependencyInjectionService.getInstance().inject(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void readLists() {
|
|
||||||
if(lists != null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
TodorooCursor<StoreObject> cursor = storeObjectDao.query(Query.select(StoreObject.PROPERTIES).
|
|
||||||
where(StoreObjectCriteria.byType(GtasksList.TYPE)));
|
|
||||||
try {
|
|
||||||
lists = new StoreObject[cursor.getCount()];
|
|
||||||
for(int i = 0; i < lists.length; i++) {
|
|
||||||
cursor.moveToNext();
|
|
||||||
StoreObject dashboard = new StoreObject(cursor);
|
|
||||||
lists[i] = dashboard;
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
cursor.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public StoreObject[] getLists() {
|
|
||||||
readLists();
|
|
||||||
return lists;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get list name
|
|
||||||
* @param listId
|
|
||||||
* @return NOT_FOUND if no list by this id exists, otherwise list name
|
|
||||||
*/
|
|
||||||
public String getListName(String listId) {
|
|
||||||
StoreObject list = getList(listId);
|
|
||||||
if(list != LIST_NOT_FOUND_OBJECT)
|
|
||||||
return list.getValue(GtasksList.NAME);
|
|
||||||
return LIST_NOT_FOUND;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateLists(GoogleTaskListInfo[] remoteLists) {
|
|
||||||
readLists();
|
|
||||||
for(int i = 0; i < remoteLists.length; i++) {
|
|
||||||
GoogleTaskListInfo remote = remoteLists[i];
|
|
||||||
|
|
||||||
String id = remote.getId();
|
|
||||||
StoreObject local = null;
|
|
||||||
for(StoreObject list : lists) {
|
|
||||||
if(list.getValue(GtasksList.REMOTE_ID).equals(id)) {
|
|
||||||
local = list;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(local == null)
|
|
||||||
local = new StoreObject();
|
|
||||||
|
|
||||||
local.setValue(StoreObject.TYPE, GtasksList.TYPE);
|
|
||||||
local.setValue(GtasksList.REMOTE_ID, id);
|
|
||||||
local.setValue(GtasksList.NAME, remote.getName());
|
|
||||||
local.setValue(GtasksList.ORDER, i);
|
|
||||||
storeObjectDao.persist(local);
|
|
||||||
}
|
|
||||||
|
|
||||||
clearListCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void clearListCache() {
|
|
||||||
lists = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public StoreObject getList(String listId) {
|
|
||||||
readLists();
|
|
||||||
for(StoreObject list : lists)
|
|
||||||
if(list.getValue(GtasksList.REMOTE_ID).equals(listId))
|
|
||||||
return list;
|
|
||||||
return LIST_NOT_FOUND_OBJECT;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,63 +0,0 @@
|
|||||||
package com.todoroo.astrid.gtasks;
|
|
||||||
|
|
||||||
import com.todoroo.andlib.data.AbstractModel;
|
|
||||||
import com.todoroo.andlib.data.Property.IntegerProperty;
|
|
||||||
import com.todoroo.andlib.data.Property.LongProperty;
|
|
||||||
import com.todoroo.andlib.data.Property.StringProperty;
|
|
||||||
import com.todoroo.andlib.utility.DateUtilities;
|
|
||||||
import com.todoroo.astrid.data.Metadata;
|
|
||||||
import com.todoroo.andlib.utility.Preferences;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Metadata entries for a GTasks Task
|
|
||||||
* @author Tim Su <tim@todoroo.com>
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class GtasksMetadata {
|
|
||||||
|
|
||||||
static final int VALUE_UNSET = -1;
|
|
||||||
|
|
||||||
/** metadata key */
|
|
||||||
public static final String METADATA_KEY = "gtasks"; //$NON-NLS-1$
|
|
||||||
|
|
||||||
/** task id in google */
|
|
||||||
public static final StringProperty ID = new StringProperty(Metadata.TABLE,
|
|
||||||
Metadata.VALUE1.name);
|
|
||||||
|
|
||||||
public static final StringProperty LIST_ID = new StringProperty(Metadata.TABLE,
|
|
||||||
Metadata.VALUE2.name);
|
|
||||||
|
|
||||||
/** parent task id, or 0 if top level task */
|
|
||||||
public static final LongProperty PARENT_TASK = new LongProperty(Metadata.TABLE,
|
|
||||||
Metadata.VALUE3.name);
|
|
||||||
|
|
||||||
public static final IntegerProperty INDENT = new IntegerProperty(Metadata.TABLE,
|
|
||||||
Metadata.VALUE4.name);
|
|
||||||
|
|
||||||
public static final IntegerProperty ORDER = new IntegerProperty(Metadata.TABLE,
|
|
||||||
Metadata.VALUE5.name);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates default GTasks metadata item
|
|
||||||
* @param taskId if > 0, will set metadata task field
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public static Metadata createEmptyMetadata(long taskId) {
|
|
||||||
Metadata metadata = new Metadata();
|
|
||||||
metadata.setValue(Metadata.KEY, GtasksMetadata.METADATA_KEY);
|
|
||||||
metadata.setValue(ID, ""); //$NON-NLS-1$
|
|
||||||
|
|
||||||
String defaultList = Preferences.getStringValue(GtasksPreferenceService.PREF_DEFAULT_LIST);
|
|
||||||
if(defaultList == null)
|
|
||||||
throw new NullPointerException("No default list has been set."); //$NON-NLS-1$
|
|
||||||
|
|
||||||
metadata.setValue(LIST_ID, defaultList);
|
|
||||||
metadata.setValue(PARENT_TASK, (long)VALUE_UNSET);
|
|
||||||
metadata.setValue(INDENT, 0);
|
|
||||||
metadata.setValue(ORDER, (int)(DateUtilities.now() / 1000L));
|
|
||||||
if(taskId > AbstractModel.NO_ID)
|
|
||||||
metadata.setValue(Metadata.TASK, taskId);
|
|
||||||
return metadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,65 +0,0 @@
|
|||||||
/**
|
|
||||||
* See the file "LICENSE" for the full license governing this code.
|
|
||||||
*/
|
|
||||||
package com.todoroo.astrid.gtasks;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
import com.todoroo.andlib.service.Autowired;
|
|
||||||
import com.todoroo.andlib.service.ContextManager;
|
|
||||||
import com.todoroo.andlib.service.DependencyInjectionService;
|
|
||||||
import com.todoroo.andlib.sql.Criterion;
|
|
||||||
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
|
|
||||||
import com.todoroo.astrid.data.Metadata;
|
|
||||||
import com.todoroo.astrid.data.Task;
|
|
||||||
import com.todoroo.astrid.gtasks.sync.GtasksTaskContainer;
|
|
||||||
import com.todoroo.astrid.sync.SyncMetadataService;
|
|
||||||
import com.todoroo.astrid.sync.SyncProviderUtilities;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Service for working with GTasks metadata
|
|
||||||
*
|
|
||||||
* @author Tim Su <tim@todoroo.com>
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public final class GtasksMetadataService extends SyncMetadataService<GtasksTaskContainer> {
|
|
||||||
|
|
||||||
@Autowired private GtasksPreferenceService gtasksPreferenceService;
|
|
||||||
|
|
||||||
public GtasksMetadataService() {
|
|
||||||
super(ContextManager.getContext());
|
|
||||||
DependencyInjectionService.getInstance().inject(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public GtasksTaskContainer createContainerFromLocalTask(Task task,
|
|
||||||
ArrayList<Metadata> metadata) {
|
|
||||||
return new GtasksTaskContainer(task, metadata);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Criterion getLocalMatchCriteria(GtasksTaskContainer remoteTask) {
|
|
||||||
return GtasksMetadata.ID.eq(remoteTask.gtaskMetadata.getValue(GtasksMetadata.ID));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Criterion getMetadataCriteria() {
|
|
||||||
return MetadataCriteria.withKey(getMetadataKey());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getMetadataKey() {
|
|
||||||
return GtasksMetadata.METADATA_KEY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SyncProviderUtilities getUtilities() {
|
|
||||||
return gtasksPreferenceService;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Criterion getMetadataWithRemoteId() {
|
|
||||||
return GtasksMetadata.ID.neq(""); //$NON-NLS-1$
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,61 +0,0 @@
|
|||||||
package com.todoroo.astrid.gtasks;
|
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
|
|
||||||
import com.todoroo.andlib.service.Autowired;
|
|
||||||
import com.todoroo.andlib.service.ContextManager;
|
|
||||||
import com.todoroo.andlib.service.DependencyInjectionService;
|
|
||||||
import com.todoroo.astrid.api.AstridApiConstants;
|
|
||||||
import com.todoroo.astrid.data.Metadata;
|
|
||||||
import com.todoroo.astrid.utility.Flags;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Context Menu actions for changing the order of a task
|
|
||||||
* @author Tim Su <tim@todoroo.com>
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
abstract public class GtasksOrderAction extends BroadcastReceiver {
|
|
||||||
|
|
||||||
@Autowired private GtasksMetadataService gtasksMetadataService;
|
|
||||||
@Autowired private GtasksTaskListUpdater gtasksTaskListUpdater;
|
|
||||||
|
|
||||||
abstract int getDelta();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
ContextManager.setContext(context);
|
|
||||||
DependencyInjectionService.getInstance().inject(this);
|
|
||||||
|
|
||||||
long taskId = intent.getLongExtra(AstridApiConstants.EXTRAS_TASK_ID, -1);
|
|
||||||
if(taskId == -1)
|
|
||||||
return;
|
|
||||||
Metadata metadata = gtasksMetadataService.getTaskMetadata(taskId);
|
|
||||||
if(metadata == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
metadata = gtasksMetadataService.getTaskMetadata(taskId);
|
|
||||||
|
|
||||||
String listId = metadata.getValue(GtasksMetadata.LIST_ID);
|
|
||||||
gtasksTaskListUpdater.move(listId, taskId, getDelta());
|
|
||||||
gtasksTaskListUpdater.correctMetadataForList(listId);
|
|
||||||
|
|
||||||
Flags.set(Flags.REFRESH);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class GtasksMoveUpAction extends GtasksOrderAction {
|
|
||||||
@Override
|
|
||||||
public int getDelta() {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class GtasksMoveDownAction extends GtasksOrderAction {
|
|
||||||
@Override
|
|
||||||
public int getDelta() {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,30 +0,0 @@
|
|||||||
package com.todoroo.astrid.gtasks;
|
|
||||||
|
|
||||||
import com.timsu.astrid.R;
|
|
||||||
import com.todoroo.astrid.sync.SyncProviderUtilities;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Methods for working with GTasks preferences
|
|
||||||
*
|
|
||||||
* @author timsu
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class GtasksPreferenceService extends SyncProviderUtilities {
|
|
||||||
|
|
||||||
/** add-on identifier */
|
|
||||||
public static final String IDENTIFIER = "gtasks"; //$NON-NLS-1$
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getIdentifier() {
|
|
||||||
return IDENTIFIER;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getSyncIntervalKey() {
|
|
||||||
return R.string.gtasks_GPr_interval_key;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** GTasks user's default list id */
|
|
||||||
public static final String PREF_DEFAULT_LIST = IDENTIFIER + "_defaultlist"; //$NON-NLS-1$
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,46 +0,0 @@
|
|||||||
package com.todoroo.astrid.gtasks;
|
|
||||||
|
|
||||||
import com.timsu.astrid.R;
|
|
||||||
import com.todoroo.andlib.service.Autowired;
|
|
||||||
import com.todoroo.andlib.service.DependencyInjectionService;
|
|
||||||
import com.todoroo.astrid.gtasks.sync.GtasksSyncProvider;
|
|
||||||
import com.todoroo.astrid.sync.SyncProviderPreferences;
|
|
||||||
import com.todoroo.astrid.sync.SyncProviderUtilities;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Displays synchronization preferences and an action panel so users can
|
|
||||||
* initiate actions from the menu.
|
|
||||||
*
|
|
||||||
* @author Tim Su <tim@todoroo.com>
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class GtasksPreferences extends SyncProviderPreferences {
|
|
||||||
|
|
||||||
@Autowired private GtasksPreferenceService gtasksPreferenceService;
|
|
||||||
|
|
||||||
public GtasksPreferences() {
|
|
||||||
super();
|
|
||||||
DependencyInjectionService.getInstance().inject(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getPreferenceResource() {
|
|
||||||
return R.xml.preferences_gtasks;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void startSync() {
|
|
||||||
new GtasksSyncProvider().synchronize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void logOut() {
|
|
||||||
new GtasksSyncProvider().signOut();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SyncProviderUtilities getUtilities() {
|
|
||||||
return gtasksPreferenceService;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,48 +0,0 @@
|
|||||||
/**
|
|
||||||
* See the file "LICENSE" for the full license governing this code.
|
|
||||||
*/
|
|
||||||
package com.todoroo.astrid.gtasks;
|
|
||||||
|
|
||||||
import android.app.PendingIntent;
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
|
|
||||||
import com.timsu.astrid.R;
|
|
||||||
import com.todoroo.andlib.service.Autowired;
|
|
||||||
import com.todoroo.andlib.service.ContextManager;
|
|
||||||
import com.todoroo.andlib.service.DependencyInjectionService;
|
|
||||||
import com.todoroo.astrid.api.AstridApiConstants;
|
|
||||||
import com.todoroo.astrid.api.SyncAction;
|
|
||||||
import com.todoroo.astrid.sync.SyncBackgroundService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exposes sync action
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class GtasksSyncActionExposer extends BroadcastReceiver {
|
|
||||||
|
|
||||||
@Autowired private GtasksPreferenceService gtasksPreferenceService;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
ContextManager.setContext(context);
|
|
||||||
DependencyInjectionService.getInstance().inject(this);
|
|
||||||
|
|
||||||
// if we aren't logged in, don't expose sync action
|
|
||||||
if(!gtasksPreferenceService.isLoggedIn())
|
|
||||||
return;
|
|
||||||
|
|
||||||
Intent syncIntent = new Intent(SyncBackgroundService.SYNC_ACTION, null,
|
|
||||||
context, GtasksBackgroundService.class);
|
|
||||||
PendingIntent pendingIntent = PendingIntent.getService(context, 0, syncIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
|
||||||
SyncAction syncAction = new SyncAction(context.getString(R.string.gtasks_GPr_header),
|
|
||||||
pendingIntent);
|
|
||||||
|
|
||||||
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_SYNC_ACTIONS);
|
|
||||||
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, GtasksPreferenceService.IDENTIFIER);
|
|
||||||
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, syncAction);
|
|
||||||
context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,389 +0,0 @@
|
|||||||
package com.todoroo.astrid.gtasks;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
|
||||||
|
|
||||||
import android.text.TextUtils;
|
|
||||||
|
|
||||||
import com.todoroo.andlib.data.TodorooCursor;
|
|
||||||
import com.todoroo.andlib.service.Autowired;
|
|
||||||
import com.todoroo.andlib.service.DependencyInjectionService;
|
|
||||||
import com.todoroo.andlib.utility.DateUtilities;
|
|
||||||
import com.todoroo.astrid.api.Filter;
|
|
||||||
import com.todoroo.astrid.core.PluginServices;
|
|
||||||
import com.todoroo.astrid.data.Metadata;
|
|
||||||
import com.todoroo.astrid.data.StoreObject;
|
|
||||||
import com.todoroo.astrid.data.Task;
|
|
||||||
import com.todoroo.astrid.gtasks.sync.GtasksTaskContainer;
|
|
||||||
|
|
||||||
public class GtasksTaskListUpdater {
|
|
||||||
|
|
||||||
@Autowired private GtasksListService gtasksListService;
|
|
||||||
@Autowired private GtasksMetadataService gtasksMetadataService;
|
|
||||||
|
|
||||||
/** map of task -> parent task */
|
|
||||||
final HashMap<Long, Long> parents = new HashMap<Long, Long>();
|
|
||||||
|
|
||||||
/** map of task -> prior sibling */
|
|
||||||
final HashMap<Long, Long> siblings = new HashMap<Long, Long>();
|
|
||||||
|
|
||||||
final HashMap<Long, String> localToRemoteIdMap =
|
|
||||||
new HashMap<Long, String>();
|
|
||||||
|
|
||||||
public GtasksTaskListUpdater() {
|
|
||||||
DependencyInjectionService.getInstance().inject(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- used during normal ui operations
|
|
||||||
|
|
||||||
public void debugPrint(String listId) {
|
|
||||||
StoreObject list = gtasksListService.getList(listId);
|
|
||||||
if(list == GtasksListService.LIST_NOT_FOUND_OBJECT)
|
|
||||||
return;
|
|
||||||
|
|
||||||
iterateThroughList(list, new ListIterator() {
|
|
||||||
public void processTask(long taskId, Metadata metadata) {
|
|
||||||
System.err.format("%d: %d, indent:%d, parent:%d\n", taskId, //$NON-NLS-1$
|
|
||||||
metadata.getValue(GtasksMetadata.ORDER),
|
|
||||||
metadata.getValue(GtasksMetadata.INDENT),
|
|
||||||
metadata.getValue(GtasksMetadata.PARENT_TASK));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indent a task and all its children
|
|
||||||
*/
|
|
||||||
public void indent(String listId, final long targetTaskId, final int delta) {
|
|
||||||
StoreObject list = gtasksListService.getList(listId);
|
|
||||||
if(list == GtasksListService.LIST_NOT_FOUND_OBJECT)
|
|
||||||
return;
|
|
||||||
|
|
||||||
updateParentSiblingMapsFor(list);
|
|
||||||
|
|
||||||
final AtomicInteger targetTaskIndent = new AtomicInteger(-1);
|
|
||||||
final AtomicInteger previousIndent = new AtomicInteger(-1);
|
|
||||||
final AtomicLong previousTask = new AtomicLong(-1);
|
|
||||||
final Task taskContainer = new Task();
|
|
||||||
|
|
||||||
iterateThroughList(list, new ListIterator() {
|
|
||||||
@Override
|
|
||||||
public void processTask(long taskId, Metadata metadata) {
|
|
||||||
int indent = metadata.getValue(GtasksMetadata.INDENT);
|
|
||||||
|
|
||||||
if(targetTaskId == taskId) {
|
|
||||||
// if indenting is warranted, indent me and my children
|
|
||||||
if(indent + delta <= previousIndent.get() + 1 && indent + delta >= 0) {
|
|
||||||
targetTaskIndent.set(indent);
|
|
||||||
metadata.setValue(GtasksMetadata.INDENT, indent + delta);
|
|
||||||
if(delta > 0)
|
|
||||||
metadata.setValue(GtasksMetadata.PARENT_TASK, previousTask.get());
|
|
||||||
else if(parents.containsKey(taskId))
|
|
||||||
metadata.setValue(GtasksMetadata.PARENT_TASK,
|
|
||||||
parents.get(parents.get(taskId)));
|
|
||||||
else
|
|
||||||
metadata.setValue(GtasksMetadata.PARENT_TASK, Task.NO_ID);
|
|
||||||
if(PluginServices.getMetadataService().save(metadata))
|
|
||||||
updateModifiedDate(taskContainer, taskId);
|
|
||||||
}
|
|
||||||
} else if(targetTaskIndent.get() > -1) {
|
|
||||||
// found first task that is not beneath target
|
|
||||||
if(indent <= targetTaskIndent.get())
|
|
||||||
targetTaskIndent.set(-1);
|
|
||||||
else {
|
|
||||||
metadata.setValue(GtasksMetadata.INDENT, indent + delta);
|
|
||||||
PluginServices.getMetadataService().save(metadata);
|
|
||||||
updateModifiedDate(taskContainer, taskId);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
previousIndent.set(indent);
|
|
||||||
previousTask.set(taskId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Move a task and all its children.
|
|
||||||
* <p>
|
|
||||||
* if moving up and first task in list or moving down and last,
|
|
||||||
* indents to same as task that we swapped with.
|
|
||||||
*
|
|
||||||
* @param delta -1 or 1
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public void move(String listId, final long targetTaskId, final int delta) {
|
|
||||||
StoreObject list = gtasksListService.getList(listId);
|
|
||||||
if(list == GtasksListService.LIST_NOT_FOUND_OBJECT)
|
|
||||||
return;
|
|
||||||
|
|
||||||
long taskToSwap = -1;
|
|
||||||
if(delta == -1) {
|
|
||||||
// use sibling / parent map to figure out prior task
|
|
||||||
updateParentSiblingMapsFor(list);
|
|
||||||
if(siblings.containsKey(targetTaskId) && siblings.get(targetTaskId) != -1L)
|
|
||||||
taskToSwap = siblings.get(targetTaskId);
|
|
||||||
else if(parents.containsKey(targetTaskId) && parents.get(targetTaskId) != -1L)
|
|
||||||
taskToSwap = parents.get(targetTaskId);
|
|
||||||
} else {
|
|
||||||
// walk through to find the next task
|
|
||||||
Filter filter = GtasksFilterExposer.filterFromList(list);
|
|
||||||
TodorooCursor<Task> cursor = PluginServices.getTaskService().fetchFiltered(filter.sqlQuery, null, Task.ID);
|
|
||||||
try {
|
|
||||||
int targetIndent = -1;
|
|
||||||
for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
|
|
||||||
long taskId = cursor.getLong(0);
|
|
||||||
|
|
||||||
if(targetIndent != -1) {
|
|
||||||
Metadata metadata = gtasksMetadataService.getTaskMetadata(taskId);
|
|
||||||
if(metadata.getValue(GtasksMetadata.INDENT) <= targetIndent) {
|
|
||||||
taskToSwap = taskId;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if(taskId == targetTaskId) {
|
|
||||||
Metadata metadata = gtasksMetadataService.getTaskMetadata(taskId);
|
|
||||||
targetIndent = metadata.getValue(GtasksMetadata.INDENT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
cursor.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(taskToSwap == -1L)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if(delta == -1) {
|
|
||||||
moveUp(list, targetTaskId, taskToSwap);
|
|
||||||
} else {
|
|
||||||
// adjust indent of target task to task to swap
|
|
||||||
Metadata targetTask = gtasksMetadataService.getTaskMetadata(targetTaskId);
|
|
||||||
Metadata nextTask = gtasksMetadataService.getTaskMetadata(taskToSwap);
|
|
||||||
int targetIndent = targetTask.getValue(GtasksMetadata.INDENT);
|
|
||||||
int nextIndent = nextTask.getValue(GtasksMetadata.INDENT);
|
|
||||||
if(targetIndent != nextIndent)
|
|
||||||
indent(listId, targetTaskId, nextIndent - targetIndent);
|
|
||||||
moveUp(list, taskToSwap, targetTaskId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void moveUp(StoreObject list, final long targetTaskId, final long priorTaskId) {
|
|
||||||
final AtomicInteger priorTaskOrder = new AtomicInteger(-1);
|
|
||||||
final AtomicInteger priorTaskIndent = new AtomicInteger(-1);
|
|
||||||
final AtomicInteger targetTaskOrder = new AtomicInteger(0);
|
|
||||||
final AtomicInteger targetTaskIndent = new AtomicInteger(-1);
|
|
||||||
final AtomicInteger tasksToMove = new AtomicInteger(1);
|
|
||||||
final AtomicBoolean finished = new AtomicBoolean(false);
|
|
||||||
|
|
||||||
// step 1. calculate tasks to move
|
|
||||||
iterateThroughList(list, new ListIterator() {
|
|
||||||
@Override
|
|
||||||
public void processTask(long taskId, Metadata metadata) {
|
|
||||||
if(finished.get() && priorTaskOrder.get() != -1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if(taskId == priorTaskId) {
|
|
||||||
priorTaskIndent.set(metadata.getValue(GtasksMetadata.INDENT));
|
|
||||||
priorTaskOrder.set(metadata.getValue(GtasksMetadata.ORDER));
|
|
||||||
} else if(targetTaskId == taskId) {
|
|
||||||
targetTaskIndent.set(metadata.getValue(GtasksMetadata.INDENT));
|
|
||||||
targetTaskOrder.set(metadata.getValue(GtasksMetadata.ORDER));
|
|
||||||
} else if(targetTaskIndent.get() > -1) {
|
|
||||||
// found first task that is not beneath target
|
|
||||||
if(metadata.getValue(GtasksMetadata.INDENT) <= targetTaskIndent.get())
|
|
||||||
finished.set(true);
|
|
||||||
else
|
|
||||||
tasksToMove.incrementAndGet();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
final AtomicBoolean priorFound = new AtomicBoolean(false);
|
|
||||||
final AtomicBoolean targetFound = new AtomicBoolean(false);
|
|
||||||
final Task taskContainer = new Task();
|
|
||||||
finished.set(false);
|
|
||||||
|
|
||||||
// step 2. swap the order of prior and our tasks
|
|
||||||
iterateThroughList(list, new ListIterator() {
|
|
||||||
@Override
|
|
||||||
public void processTask(long taskId, Metadata metadata) {
|
|
||||||
if(finished.get())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if(targetTaskId == taskId)
|
|
||||||
targetFound.set(true);
|
|
||||||
else if(taskId == priorTaskId)
|
|
||||||
priorFound.set(true);
|
|
||||||
|
|
||||||
if(targetFound.get()) {
|
|
||||||
if(targetTaskId != taskId && metadata.getValue(GtasksMetadata.INDENT) <= targetTaskIndent.get())
|
|
||||||
finished.set(true);
|
|
||||||
else {
|
|
||||||
int newOrder = metadata.getValue(GtasksMetadata.ORDER) -
|
|
||||||
targetTaskOrder.get() + priorTaskOrder.get();
|
|
||||||
int newIndent = metadata.getValue(GtasksMetadata.INDENT) -
|
|
||||||
targetTaskIndent.get() + priorTaskIndent.get();
|
|
||||||
|
|
||||||
metadata.setValue(GtasksMetadata.ORDER, newOrder);
|
|
||||||
metadata.setValue(GtasksMetadata.INDENT, newIndent);
|
|
||||||
PluginServices.getMetadataService().save(metadata);
|
|
||||||
updateModifiedDate(taskContainer, taskId);
|
|
||||||
}
|
|
||||||
} else if(priorFound.get()) {
|
|
||||||
int newOrder = metadata.getValue(GtasksMetadata.ORDER) +
|
|
||||||
tasksToMove.get();
|
|
||||||
metadata.setValue(GtasksMetadata.ORDER, newOrder);
|
|
||||||
PluginServices.getMetadataService().save(metadata);
|
|
||||||
updateModifiedDate(taskContainer, taskId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateModifiedDate(Task taskContainer, long taskId) {
|
|
||||||
taskContainer.setId(taskId);
|
|
||||||
taskContainer.setValue(Task.DETAILS_DATE, DateUtilities.now());
|
|
||||||
PluginServices.getTaskService().save(taskContainer);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- used during synchronization
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update order, parent, and indentation fields for all tasks in all lists
|
|
||||||
*/
|
|
||||||
public void updateAllMetadata() {
|
|
||||||
for(StoreObject list : gtasksListService.getLists()) {
|
|
||||||
correctMetadataForList(list.getValue(GtasksList.REMOTE_ID));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update order, parent, and indentation fields for all tasks in the given list
|
|
||||||
* @param listId
|
|
||||||
*/
|
|
||||||
public void correctMetadataForList(String listId) {
|
|
||||||
StoreObject list = gtasksListService.getList(listId);
|
|
||||||
if(list == GtasksListService.LIST_NOT_FOUND_OBJECT)
|
|
||||||
return;
|
|
||||||
|
|
||||||
updateParentSiblingMapsFor(list);
|
|
||||||
|
|
||||||
final AtomicInteger order = new AtomicInteger(0);
|
|
||||||
final AtomicInteger previousIndent = new AtomicInteger(-1);
|
|
||||||
|
|
||||||
iterateThroughList(list, new ListIterator() {
|
|
||||||
@Override
|
|
||||||
public void processTask(long taskId, Metadata metadata) {
|
|
||||||
metadata.setValue(GtasksMetadata.ORDER, order.getAndAdd(1));
|
|
||||||
int indent = metadata.getValue(GtasksMetadata.INDENT);
|
|
||||||
if(indent > previousIndent.get() + 1)
|
|
||||||
indent = previousIndent.get() + 1;
|
|
||||||
metadata.setValue(GtasksMetadata.INDENT, indent);
|
|
||||||
|
|
||||||
long parent = parents.get(taskId);
|
|
||||||
if(parent < 0)
|
|
||||||
parent = Task.NO_ID;
|
|
||||||
metadata.setValue(GtasksMetadata.PARENT_TASK, parent);
|
|
||||||
|
|
||||||
PluginServices.getMetadataService().save(metadata);
|
|
||||||
previousIndent.set(indent);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a local tree of tasks to expedite sibling and parent lookups
|
|
||||||
*/
|
|
||||||
public void createParentSiblingMaps() {
|
|
||||||
for(StoreObject list : gtasksListService.getLists()) {
|
|
||||||
updateParentSiblingMapsFor(list);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateParentSiblingMapsFor(StoreObject list) {
|
|
||||||
final AtomicLong previousTask = new AtomicLong(-1L);
|
|
||||||
final AtomicInteger previousIndent = new AtomicInteger(-1);
|
|
||||||
|
|
||||||
iterateThroughList(list, new ListIterator() {
|
|
||||||
@Override
|
|
||||||
public void processTask(long taskId, Metadata metadata) {
|
|
||||||
int indent = metadata.getValue(GtasksMetadata.INDENT);
|
|
||||||
|
|
||||||
long parent, sibling;
|
|
||||||
if(indent > previousIndent.get()) {
|
|
||||||
parent = previousTask.get();
|
|
||||||
sibling = -1L;
|
|
||||||
} else if(indent == previousIndent.get()) {
|
|
||||||
sibling = previousTask.get();
|
|
||||||
parent = parents.get(sibling);
|
|
||||||
} else {
|
|
||||||
// move up once for each indent
|
|
||||||
sibling = previousTask.get();
|
|
||||||
for(int i = indent; i < previousIndent.get(); i++)
|
|
||||||
sibling = parents.get(sibling);
|
|
||||||
parent = parents.get(sibling);
|
|
||||||
}
|
|
||||||
parents.put(taskId, parent);
|
|
||||||
siblings.put(taskId, sibling);
|
|
||||||
|
|
||||||
previousTask.set(taskId);
|
|
||||||
previousIndent.set(indent);
|
|
||||||
if(!TextUtils.isEmpty(metadata.getValue(GtasksMetadata.ID)))
|
|
||||||
localToRemoteIdMap.put(taskId, metadata.getValue(GtasksMetadata.ID));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Must be called after creating parent and sibling maps. Updates a
|
|
||||||
* task container's parent and sibling fields.
|
|
||||||
*
|
|
||||||
* @param container
|
|
||||||
*/
|
|
||||||
public void updateParentAndSibling(GtasksTaskContainer container) {
|
|
||||||
long taskId = container.task.getId();
|
|
||||||
if(parents.containsKey(taskId)) {
|
|
||||||
long parentId = parents.get(taskId);
|
|
||||||
if(localToRemoteIdMap.containsKey(parentId))
|
|
||||||
container.parentId = localToRemoteIdMap.get(parentId);
|
|
||||||
}
|
|
||||||
if(siblings.containsKey(taskId)) {
|
|
||||||
long siblingId = siblings.get(taskId);
|
|
||||||
if(localToRemoteIdMap.containsKey(siblingId))
|
|
||||||
container.priorSiblingId = localToRemoteIdMap.get(siblingId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addRemoteTaskMapping(long id, String remoteId) {
|
|
||||||
localToRemoteIdMap.put(id, remoteId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- private helpers
|
|
||||||
|
|
||||||
private interface ListIterator {
|
|
||||||
public void processTask(long taskId, Metadata metadata);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void iterateThroughList(StoreObject list, ListIterator iterator) {
|
|
||||||
Filter filter = GtasksFilterExposer.filterFromList(list);
|
|
||||||
TodorooCursor<Task> cursor = PluginServices.getTaskService().fetchFiltered(filter.sqlQuery, null, Task.ID);
|
|
||||||
try {
|
|
||||||
for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
|
|
||||||
long taskId = cursor.getLong(0);
|
|
||||||
Metadata metadata = gtasksMetadataService.getTaskMetadata(taskId);
|
|
||||||
if(metadata == null)
|
|
||||||
continue;
|
|
||||||
iterator.processTask(taskId, metadata);
|
|
||||||
}
|
|
||||||
|
|
||||||
} finally {
|
|
||||||
cursor.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -1,125 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010 Google Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
||||||
* use this file except in compliance with the License. You may obtain a copy of
|
|
||||||
* the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
* License for the specific language governing permissions and limitations under
|
|
||||||
* the License.
|
|
||||||
*/
|
|
||||||
package com.todoroo.astrid.gtasks.auth;
|
|
||||||
|
|
||||||
import android.accounts.Account;
|
|
||||||
import android.accounts.AccountManager;
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
|
|
||||||
import com.timsu.astrid.R;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Choose which account to upload track information to.
|
|
||||||
* @author Sandor Dornbush
|
|
||||||
*/
|
|
||||||
public class AccountChooser {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The last selected account.
|
|
||||||
*/
|
|
||||||
private int selectedAccountIndex = -1;
|
|
||||||
private Account selectedAccount = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An interface for receiving updates once the user has selected the account.
|
|
||||||
*/
|
|
||||||
public interface AccountHandler {
|
|
||||||
/**
|
|
||||||
* Handle the account being selected.
|
|
||||||
* @param account The selected account or null if none could be found
|
|
||||||
*/
|
|
||||||
public void handleAccountSelected(Account account);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Chooses the best account to upload to.
|
|
||||||
* If no account is found the user will be alerted.
|
|
||||||
* If only one account is found that will be used.
|
|
||||||
* If multiple accounts are found the user will be allowed to choose.
|
|
||||||
*
|
|
||||||
* @param activity The parent activity
|
|
||||||
* @param handler The handler to be notified when an account has been selected
|
|
||||||
*/
|
|
||||||
public void chooseAccount(final Activity activity,
|
|
||||||
final AccountHandler handler) {
|
|
||||||
final Account[] accounts = AccountManager.get(activity)
|
|
||||||
.getAccountsByType("com.google"); //$NON-NLS-1$
|
|
||||||
if (accounts.length < 1) {
|
|
||||||
alertNoAccounts(activity, handler);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (accounts.length == 1) {
|
|
||||||
handler.handleAccountSelected(accounts[0]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO This should be read out of a preference.
|
|
||||||
if (selectedAccount != null) {
|
|
||||||
handler.handleAccountSelected(selectedAccount);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Let the user choose.
|
|
||||||
final AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
|
||||||
builder.setTitle(R.string.choose_account_title);
|
|
||||||
builder.setCancelable(false);
|
|
||||||
builder.setPositiveButton(android.R.string.ok,
|
|
||||||
new DialogInterface.OnClickListener() {
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
selectedAccount = accounts[selectedAccountIndex];
|
|
||||||
handler.handleAccountSelected(selectedAccount);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
builder.setNegativeButton(android.R.string.cancel,
|
|
||||||
new DialogInterface.OnClickListener() {
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
handler.handleAccountSelected(null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
String[] choices = new String[accounts.length];
|
|
||||||
for (int i = 0; i < accounts.length; i++) {
|
|
||||||
choices[i] = accounts[i].name;
|
|
||||||
}
|
|
||||||
builder.setSingleChoiceItems(choices, selectedAccountIndex,
|
|
||||||
new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
selectedAccountIndex = which;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
builder.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Puts up a dialog alerting the user that no suitable account was found.
|
|
||||||
*/
|
|
||||||
private void alertNoAccounts(final Activity activity,
|
|
||||||
final AccountHandler handler) {
|
|
||||||
final AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
|
||||||
builder.setTitle(R.string.no_account_found_title);
|
|
||||||
builder.setMessage(R.string.no_account_found);
|
|
||||||
builder.setCancelable(true);
|
|
||||||
builder.setNegativeButton(android.R.string.ok,
|
|
||||||
new DialogInterface.OnClickListener() {
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
handler.handleAccountSelected(null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
builder.show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,77 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010 Google Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
||||||
* use this file except in compliance with the License. You may obtain a copy of
|
|
||||||
* the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
* License for the specific language governing permissions and limitations under
|
|
||||||
* the License.
|
|
||||||
*/
|
|
||||||
package com.todoroo.astrid.gtasks.auth;
|
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This interface describes a class that will fetch and maintain a Google
|
|
||||||
* authentication token.
|
|
||||||
*
|
|
||||||
* @author Sandor Dornbush
|
|
||||||
*/
|
|
||||||
public interface AuthManager {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the login process. The user should be asked to login if they
|
|
||||||
* haven't already. The {@link Runnable} provided will be executed when the
|
|
||||||
* auth token is successfully fetched.
|
|
||||||
*
|
|
||||||
* @param whenFinished A {@link Runnable} to execute when the auth token
|
|
||||||
* has been successfully fetched and is available via
|
|
||||||
* {@link #getAuthToken()}
|
|
||||||
*/
|
|
||||||
public abstract void doLogin(Runnable whenFinished, Object o);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The {@link android.app.Activity} owner of this class should call this
|
|
||||||
* function when it gets {@link android.app.Activity#onActivityResult} with
|
|
||||||
* the request code passed into the constructor. The resultCode and results
|
|
||||||
* should come directly from the {@link android.app.Activity#onActivityResult}
|
|
||||||
* function. This function will return true if an auth token was successfully
|
|
||||||
* fetched or the process is not finished.
|
|
||||||
*
|
|
||||||
* @param resultCode The result code passed in to the
|
|
||||||
* {@link android.app.Activity}'s
|
|
||||||
* {@link android.app.Activity#onActivityResult} function
|
|
||||||
* @param results The data passed in to the {@link android.app.Activity}'s
|
|
||||||
* {@link android.app.Activity#onActivityResult} function
|
|
||||||
* @return True if the auth token was fetched or we aren't done fetching
|
|
||||||
* the auth token, or False if there was an error or the request was
|
|
||||||
* canceled
|
|
||||||
*/
|
|
||||||
public abstract boolean authResult(int resultCode, Intent results);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current auth token. Response may be null if no valid auth
|
|
||||||
* token has been fetched.
|
|
||||||
*
|
|
||||||
* @return The current auth token or null if no auth token has been
|
|
||||||
* fetched
|
|
||||||
*/
|
|
||||||
public abstract String getAuthToken();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invalidates the existing auth token and request a new one. The
|
|
||||||
* {@link Runnable} provided will be executed when the new auth token is
|
|
||||||
* successfully fetched.
|
|
||||||
*
|
|
||||||
* @param whenFinished A {@link Runnable} to execute when a new auth token
|
|
||||||
* is successfully fetched
|
|
||||||
*/
|
|
||||||
public abstract void invalidateAndRefresh(Runnable whenFinished);
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,53 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010 Google Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
||||||
* use this file except in compliance with the License. You may obtain a copy of
|
|
||||||
* the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
* License for the specific language governing permissions and limitations under
|
|
||||||
* the License.
|
|
||||||
*/
|
|
||||||
package com.todoroo.astrid.gtasks.auth;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Bundle;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A factory for getting the platform specific AuthManager.
|
|
||||||
*
|
|
||||||
* @author Sandor Dornbush
|
|
||||||
*/
|
|
||||||
public class AuthManagerFactory {
|
|
||||||
|
|
||||||
private AuthManagerFactory() {
|
|
||||||
// don't construct me
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether the modern AuthManager should be used
|
|
||||||
*/
|
|
||||||
public static boolean useModernAuthManager() {
|
|
||||||
return Integer.parseInt(Build.VERSION.SDK) >= 7;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a right {@link AuthManager} for the platform.
|
|
||||||
* @return A new AuthManager
|
|
||||||
*/
|
|
||||||
public static AuthManager getAuthManager(Activity activity, int code,
|
|
||||||
Bundle extras, boolean requireGoogle, String service) {
|
|
||||||
if (useModernAuthManager()) {
|
|
||||||
return new ModernAuthManager(activity, code, extras, requireGoogle, service);
|
|
||||||
} else {
|
|
||||||
return new AuthManagerOld(activity, code, extras, requireGoogle, service);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,198 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2009 Google Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
||||||
* use this file except in compliance with the License. You may obtain a copy of
|
|
||||||
* the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
* License for the specific language governing permissions and limitations under
|
|
||||||
* the License.
|
|
||||||
*/
|
|
||||||
package com.todoroo.astrid.gtasks.auth;
|
|
||||||
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Vector;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
|
||||||
|
|
||||||
import com.google.android.googlelogindist.GoogleLoginServiceConstants;
|
|
||||||
import com.google.android.googlelogindist.GoogleLoginServiceHelper;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AuthManager keeps track of the current auth token for a user. The advantage
|
|
||||||
* over just passing around a String is that this class can renew the auth
|
|
||||||
* token if necessary, and it will change for all classes using this
|
|
||||||
* AuthManager.
|
|
||||||
*/
|
|
||||||
public class AuthManagerOld implements AuthManager {
|
|
||||||
/** The activity that will handle auth result callbacks. */
|
|
||||||
private final Activity activity;
|
|
||||||
|
|
||||||
/** The code used to tell the activity that it is an auth result. */
|
|
||||||
private final int code;
|
|
||||||
|
|
||||||
/** Extras to pass into the getCredentials function. */
|
|
||||||
private final Bundle extras;
|
|
||||||
|
|
||||||
/** True if the account must be a Google account (not a domain account). */
|
|
||||||
private final boolean requireGoogle;
|
|
||||||
|
|
||||||
/** The name of the service to authorize for. */
|
|
||||||
private final String service;
|
|
||||||
|
|
||||||
/** A list of handlers to call when a new auth token is fetched. */
|
|
||||||
private final Vector<Runnable> newTokenListeners = new Vector<Runnable>();
|
|
||||||
|
|
||||||
/** The most recently fetched auth token or null if none is available. */
|
|
||||||
private String authToken;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The number of handlers at the beginning of the above list that shouldn't
|
|
||||||
* be removed after they are called.
|
|
||||||
*/
|
|
||||||
private int stickyNewTokenListenerCount;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AuthManager requires many of the same parameters as
|
|
||||||
* {@link GoogleLoginServiceHelper#getCredentials(Activity, int, Bundle,
|
|
||||||
* boolean, String, boolean)}. The activity must have
|
|
||||||
* a handler in {@link Activity#onActivityResult} that calls
|
|
||||||
* {@link #authResult(int, Intent)} if the request code is the code given
|
|
||||||
* here.
|
|
||||||
*
|
|
||||||
* @param activity An activity with a handler in
|
|
||||||
* {@link Activity#onActivityResult} that calls
|
|
||||||
* {@link #authResult(int, Intent)} when {@literal code} is the request
|
|
||||||
* code
|
|
||||||
* @param code The request code to pass to
|
|
||||||
* {@link Activity#onActivityResult} when
|
|
||||||
* {@link #authResult(int, Intent)} should be called
|
|
||||||
* @param extras A {@link Bundle} of extras for
|
|
||||||
* {@link GoogleLoginServiceHelper}
|
|
||||||
* @param requireGoogle True if the account must be a Google account
|
|
||||||
* @param service The name of the service to authenticate as
|
|
||||||
*/
|
|
||||||
public AuthManagerOld(Activity activity, int code, Bundle extras,
|
|
||||||
boolean requireGoogle, String service) {
|
|
||||||
this.activity = activity;
|
|
||||||
this.code = code;
|
|
||||||
this.extras = extras;
|
|
||||||
this.requireGoogle = requireGoogle;
|
|
||||||
this.service = service;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see com.google.android.apps.mytracks.io.AuthManager#doLogin(java.lang.Runnable)
|
|
||||||
*/
|
|
||||||
public void doLogin(Runnable whenFinished, Object o) {
|
|
||||||
synchronized (newTokenListeners) {
|
|
||||||
if (whenFinished != null) {
|
|
||||||
newTokenListeners.add(whenFinished);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
activity.runOnUiThread(new LoginRunnable());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runnable which actually gets login credentials.
|
|
||||||
*/
|
|
||||||
private class LoginRunnable implements Runnable {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
GoogleLoginServiceHelper.getCredentials(
|
|
||||||
activity, code, extras, requireGoogle, service, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see com.google.android.apps.mytracks.io.AuthManager#authResult(int, android.content.Intent)
|
|
||||||
*/
|
|
||||||
public boolean authResult(int resultCode, Intent results) {
|
|
||||||
if (resultCode == Activity.RESULT_OK) {
|
|
||||||
authToken = results.getStringExtra(
|
|
||||||
GoogleLoginServiceConstants.AUTHTOKEN_KEY);
|
|
||||||
if (authToken == null) {
|
|
||||||
GoogleLoginServiceHelper.getCredentials(
|
|
||||||
activity, code, extras, requireGoogle, service, false);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
// Notify all active listeners that we have a new auth token.
|
|
||||||
synchronized (newTokenListeners) {
|
|
||||||
Iterator<Runnable> iter = newTokenListeners.iterator();
|
|
||||||
while (iter.hasNext()) {
|
|
||||||
iter.next().run();
|
|
||||||
}
|
|
||||||
iter = null;
|
|
||||||
// Remove anything not in the sticky part of the list.
|
|
||||||
newTokenListeners.setSize(stickyNewTokenListenerCount);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see com.google.android.apps.mytracks.io.AuthManager#getAuthToken()
|
|
||||||
*/
|
|
||||||
public String getAuthToken() {
|
|
||||||
return authToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see com.google.android.apps.mytracks.io.AuthManager#invalidateAndRefresh(java.lang.Runnable)
|
|
||||||
*/
|
|
||||||
public void invalidateAndRefresh(Runnable whenFinished) {
|
|
||||||
synchronized (newTokenListeners) {
|
|
||||||
if (whenFinished != null) {
|
|
||||||
newTokenListeners.add(whenFinished);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
activity.runOnUiThread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
GoogleLoginServiceHelper.invalidateAuthToken(activity, code, authToken);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a {@link Runnable} to be executed every time the auth token is
|
|
||||||
* updated. The {@link Runnable} will not be removed until manually removed
|
|
||||||
* with {@link #removeStickyNewTokenListener(Runnable)}.
|
|
||||||
*
|
|
||||||
* @param listener The {@link Runnable} to execute every time a new auth
|
|
||||||
* token is fetched
|
|
||||||
*/
|
|
||||||
public void addStickyNewTokenListener(Runnable listener) {
|
|
||||||
synchronized (newTokenListeners) {
|
|
||||||
newTokenListeners.add(0, listener);
|
|
||||||
stickyNewTokenListenerCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stops executing the given {@link Runnable} every time the auth token is
|
|
||||||
* updated. This {@link Runnable} must have been added with
|
|
||||||
* {@link #addStickyNewTokenListener(Runnable)} above. If the
|
|
||||||
* {@link Runnable} was added more than once, only the first occurrence
|
|
||||||
* will be removed.
|
|
||||||
*
|
|
||||||
* @param listener The {@link Runnable} to stop executing every time a new
|
|
||||||
* auth token is fetched
|
|
||||||
*/
|
|
||||||
public void removeStickyNewTokenListener(Runnable listener) {
|
|
||||||
synchronized (newTokenListeners) {
|
|
||||||
if (stickyNewTokenListenerCount > 0
|
|
||||||
&& newTokenListeners.remove(listener)) {
|
|
||||||
stickyNewTokenListenerCount--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,210 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010 Google Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
||||||
* use this file except in compliance with the License. You may obtain a copy of
|
|
||||||
* the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
* License for the specific language governing permissions and limitations under
|
|
||||||
* the License.
|
|
||||||
*/
|
|
||||||
package com.todoroo.astrid.gtasks.auth;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import android.accounts.Account;
|
|
||||||
import android.accounts.AccountManager;
|
|
||||||
import android.accounts.AccountManagerCallback;
|
|
||||||
import android.accounts.AccountManagerFuture;
|
|
||||||
import android.accounts.AuthenticatorException;
|
|
||||||
import android.accounts.OperationCanceledException;
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AuthManager keeps track of the current auth token for a user. The advantage
|
|
||||||
* over just passing around a String is that this class can renew the auth
|
|
||||||
* token if necessary, and it will change for all classes using this
|
|
||||||
* AuthManager.
|
|
||||||
*/
|
|
||||||
public class ModernAuthManager implements AuthManager {
|
|
||||||
protected static final int GET_LOGIN_REQUEST = 1;
|
|
||||||
|
|
||||||
/** The activity that will handle auth result callbacks. */
|
|
||||||
private final Activity activity;
|
|
||||||
|
|
||||||
/** The name of the service to authorize for. */
|
|
||||||
private final String service;
|
|
||||||
|
|
||||||
/** The most recently fetched auth token or null if none is available. */
|
|
||||||
private String authToken;
|
|
||||||
|
|
||||||
private final AccountManager accountManager;
|
|
||||||
|
|
||||||
private Runnable whenFinished;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AuthManager requires many of the same parameters as
|
|
||||||
* {@link com.google.android.googlelogindist.GoogleLoginServiceHelper
|
|
||||||
* #getCredentials(Activity, int, Bundle, boolean, String, boolean)}.
|
|
||||||
* The activity must have a handler in {@link Activity#onActivityResult} that
|
|
||||||
* calls {@link #authResult(int, Intent)} if the request code is the code
|
|
||||||
* given here.
|
|
||||||
*
|
|
||||||
* @param activity An activity with a handler in
|
|
||||||
* {@link Activity#onActivityResult} that calls
|
|
||||||
* {@link #authResult(int, Intent)} when {@literal code} is the request
|
|
||||||
* code
|
|
||||||
* @param code The request code to pass to
|
|
||||||
* {@link Activity#onActivityResult} when
|
|
||||||
* {@link #authResult(int, Intent)} should be called
|
|
||||||
* @param extras A {@link Bundle} of extras for
|
|
||||||
* {@link com.google.android.googlelogindist.GoogleLoginServiceHelper}
|
|
||||||
* @param requireGoogle True if the account must be a Google account
|
|
||||||
* @param service The name of the service to authenticate as
|
|
||||||
*/
|
|
||||||
public ModernAuthManager(Activity activity, int code, Bundle extras,
|
|
||||||
boolean requireGoogle, String service) {
|
|
||||||
this.activity = activity;
|
|
||||||
this.service = service;
|
|
||||||
this.accountManager = AccountManager.get(activity);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Call this to do the initial login. The user will be asked to login if
|
|
||||||
* they haven't already. The {@link Runnable} provided will be executed
|
|
||||||
* when the auth token is successfully fetched.
|
|
||||||
*
|
|
||||||
* @param runnable A {@link Runnable} to execute when the auth token
|
|
||||||
* has been successfully fetched and is available via
|
|
||||||
* {@link #getAuthToken()}
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("nls")
|
|
||||||
public void doLogin(final Runnable runnable, Object o) {
|
|
||||||
this.whenFinished = runnable;
|
|
||||||
if (!(o instanceof Account)) {
|
|
||||||
throw new IllegalArgumentException("FroyoAuthManager requires an account.");
|
|
||||||
}
|
|
||||||
Account account = (Account) o;
|
|
||||||
accountManager.getAuthToken(account, service, true,
|
|
||||||
new AccountManagerCallback<Bundle>() {
|
|
||||||
public void run(AccountManagerFuture<Bundle> future) {
|
|
||||||
try {
|
|
||||||
Bundle result = future.getResult();
|
|
||||||
|
|
||||||
// AccountManager needs user to grant permission
|
|
||||||
if (result.containsKey(AccountManager.KEY_INTENT)) {
|
|
||||||
Intent intent = (Intent) result.get(AccountManager.KEY_INTENT);
|
|
||||||
clearNewTaskFlag(intent);
|
|
||||||
activity.startActivityForResult(intent, GET_LOGIN_REQUEST);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
authToken = result.getString(
|
|
||||||
AccountManager.KEY_AUTHTOKEN);
|
|
||||||
Log.e("gtasks-auth", "Got auth token.");
|
|
||||||
runWhenFinished();
|
|
||||||
} catch (OperationCanceledException e) {
|
|
||||||
Log.e("gtasks-auth", "Operation Canceled", e);
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.e("gtasks-auth", "IOException", e);
|
|
||||||
} catch (AuthenticatorException e) {
|
|
||||||
Log.e("gtasks-auth", "Authentication Failed", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, null /* handler */);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void clearNewTaskFlag(Intent intent) {
|
|
||||||
int flags = intent.getFlags();
|
|
||||||
flags &= ~Intent.FLAG_ACTIVITY_NEW_TASK;
|
|
||||||
intent.setFlags(flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The {@link Activity} passed into the constructor should call this
|
|
||||||
* function when it gets {@link Activity#onActivityResult} with the request
|
|
||||||
* code passed into the constructor. The resultCode and results should
|
|
||||||
* come directly from the {@link Activity#onActivityResult} function. This
|
|
||||||
* function will return true if an auth token was successfully fetched or
|
|
||||||
* the process is not finished.
|
|
||||||
*
|
|
||||||
* @param resultCode The result code passed in to the {@link Activity}'s
|
|
||||||
* {@link Activity#onActivityResult} function
|
|
||||||
* @param results The data passed in to the {@link Activity}'s
|
|
||||||
* {@link Activity#onActivityResult} function
|
|
||||||
* @return True if the auth token was fetched or we aren't done fetching
|
|
||||||
* the auth token, or False if there was an error or the request was
|
|
||||||
* canceled
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("nls")
|
|
||||||
public boolean authResult(int resultCode, Intent results) {
|
|
||||||
if (results != null) {
|
|
||||||
authToken = results.getStringExtra(
|
|
||||||
AccountManager.KEY_AUTHTOKEN);
|
|
||||||
Log.w("google-auth", "authResult: " + authToken);
|
|
||||||
} else {
|
|
||||||
Log.e("google-auth", "No auth result results!!");
|
|
||||||
}
|
|
||||||
runWhenFinished();
|
|
||||||
return authToken != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current auth token. Response may be null if no valid auth
|
|
||||||
* token has been fetched.
|
|
||||||
*
|
|
||||||
* @return The current auth token or null if no auth token has been
|
|
||||||
* fetched
|
|
||||||
*/
|
|
||||||
public String getAuthToken() {
|
|
||||||
return authToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invalidates the existing auth token and request a new one. The
|
|
||||||
* {@link Runnable} provided will be executed when the new auth token is
|
|
||||||
* successfully fetched.
|
|
||||||
*
|
|
||||||
* @param runnable A {@link Runnable} to execute when a new auth token
|
|
||||||
* is successfully fetched
|
|
||||||
*/
|
|
||||||
public void invalidateAndRefresh(final Runnable runnable) {
|
|
||||||
this.whenFinished = runnable;
|
|
||||||
|
|
||||||
activity.runOnUiThread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
accountManager.invalidateAuthToken("com.google", authToken); //$NON-NLS-1$
|
|
||||||
new AccountChooser().chooseAccount(activity,
|
|
||||||
new AccountChooser.AccountHandler() {
|
|
||||||
@Override
|
|
||||||
public void handleAccountSelected(Account account) {
|
|
||||||
if (account != null) {
|
|
||||||
doLogin(whenFinished, account);
|
|
||||||
} else {
|
|
||||||
runWhenFinished();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void runWhenFinished() {
|
|
||||||
if (whenFinished != null) {
|
|
||||||
(new Thread() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
whenFinished.run();
|
|
||||||
}
|
|
||||||
}).start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,540 +0,0 @@
|
|||||||
/**
|
|
||||||
* See the file "LICENSE" for the full license governing this code.
|
|
||||||
*/
|
|
||||||
package com.todoroo.astrid.gtasks.sync;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.json.JSONException;
|
|
||||||
|
|
||||||
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.os.Bundle;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.flurry.android.FlurryAgent;
|
|
||||||
import com.timsu.astrid.R;
|
|
||||||
import com.todoroo.andlib.data.AbstractModel;
|
|
||||||
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.service.ExceptionService;
|
|
||||||
import com.todoroo.andlib.utility.AndroidUtilities;
|
|
||||||
import com.todoroo.andlib.utility.DateUtilities;
|
|
||||||
import com.todoroo.andlib.utility.DialogUtilities;
|
|
||||||
import com.todoroo.andlib.utility.Preferences;
|
|
||||||
import com.todoroo.astrid.data.Metadata;
|
|
||||||
import com.todoroo.astrid.data.StoreObject;
|
|
||||||
import com.todoroo.astrid.data.Task;
|
|
||||||
import com.todoroo.astrid.gtasks.GtasksBackgroundService;
|
|
||||||
import com.todoroo.astrid.gtasks.GtasksList;
|
|
||||||
import com.todoroo.astrid.gtasks.GtasksListService;
|
|
||||||
import com.todoroo.astrid.gtasks.GtasksMetadata;
|
|
||||||
import com.todoroo.astrid.gtasks.GtasksMetadataService;
|
|
||||||
import com.todoroo.astrid.gtasks.GtasksPreferenceService;
|
|
||||||
import com.todoroo.astrid.gtasks.GtasksPreferences;
|
|
||||||
import com.todoroo.astrid.gtasks.GtasksTaskListUpdater;
|
|
||||||
import com.todoroo.astrid.gtasks.auth.AuthManager;
|
|
||||||
import com.todoroo.astrid.gtasks.auth.AuthManagerFactory;
|
|
||||||
import com.todoroo.astrid.producteev.api.ApiServiceException;
|
|
||||||
import com.todoroo.astrid.service.AstridDependencyInjector;
|
|
||||||
import com.todoroo.astrid.sync.SyncBackgroundService;
|
|
||||||
import com.todoroo.astrid.sync.SyncContainer;
|
|
||||||
import com.todoroo.astrid.sync.SyncProvider;
|
|
||||||
import com.todoroo.astrid.utility.Constants;
|
|
||||||
import com.todoroo.gtasks.GoogleConnectionManager;
|
|
||||||
import com.todoroo.gtasks.GoogleTaskService;
|
|
||||||
import com.todoroo.gtasks.GoogleTaskTask;
|
|
||||||
import com.todoroo.gtasks.GoogleTaskView;
|
|
||||||
import com.todoroo.gtasks.GoogleTasksException;
|
|
||||||
import com.todoroo.gtasks.GoogleTaskService.ConvenientTaskCreator;
|
|
||||||
import com.todoroo.gtasks.actions.Actions;
|
|
||||||
import com.todoroo.gtasks.actions.ListAction;
|
|
||||||
import com.todoroo.gtasks.actions.ListActions;
|
|
||||||
import com.todoroo.gtasks.actions.ListActions.TaskBuilder;
|
|
||||||
import com.todoroo.gtasks.actions.ListActions.TaskModifier;
|
|
||||||
|
|
||||||
@SuppressWarnings("nls")
|
|
||||||
public class GtasksSyncProvider extends SyncProvider<GtasksTaskContainer> {
|
|
||||||
|
|
||||||
@Autowired private GtasksListService gtasksListService;
|
|
||||||
@Autowired private GtasksMetadataService gtasksMetadataService;
|
|
||||||
@Autowired private GtasksPreferenceService gtasksPreferenceService;
|
|
||||||
@Autowired private GtasksTaskListUpdater gtasksTaskListUpdater;
|
|
||||||
|
|
||||||
/** google task service fields */
|
|
||||||
private GoogleTaskService taskService = null;
|
|
||||||
private static final Actions a = new Actions();
|
|
||||||
private static final ListActions l = new ListActions();
|
|
||||||
|
|
||||||
static {
|
|
||||||
AstridDependencyInjector.initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
protected ExceptionService exceptionService;
|
|
||||||
|
|
||||||
public GtasksSyncProvider() {
|
|
||||||
super();
|
|
||||||
DependencyInjectionService.getInstance().inject(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
|
||||||
// ------------------------------------------------------ utility methods
|
|
||||||
// ----------------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sign out of service, deleting all synchronization metadata
|
|
||||||
*/
|
|
||||||
public void signOut() {
|
|
||||||
gtasksPreferenceService.clearLastSyncDate();
|
|
||||||
gtasksPreferenceService.setToken(null);
|
|
||||||
|
|
||||||
gtasksMetadataService.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 displayError) {
|
|
||||||
final Context context = ContextManager.getContext();
|
|
||||||
gtasksPreferenceService.setLastError(e.toString());
|
|
||||||
|
|
||||||
String message = null;
|
|
||||||
|
|
||||||
// occurs when application was closed
|
|
||||||
if(e instanceof IllegalStateException) {
|
|
||||||
exceptionService.reportError(tag + "-caught", e); //$NON-NLS-1$
|
|
||||||
|
|
||||||
// occurs when network error
|
|
||||||
} else if(!(e instanceof ApiServiceException) && e instanceof IOException) {
|
|
||||||
message = context.getString(R.string.producteev_ioerror);
|
|
||||||
} else {
|
|
||||||
message = context.getString(R.string.DLG_error, e.toString());
|
|
||||||
exceptionService.reportError(tag + "-unhandled", e); //$NON-NLS-1$
|
|
||||||
}
|
|
||||||
|
|
||||||
if(displayError && context instanceof Activity && message != null) {
|
|
||||||
DialogUtilities.okDialog((Activity)context,
|
|
||||||
message, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
|
||||||
// ------------------------------------------------------ initiating sync
|
|
||||||
// ----------------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* initiate sync in background
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void initiateBackground(Service service) {
|
|
||||||
try {
|
|
||||||
String authToken = gtasksPreferenceService.getToken();
|
|
||||||
|
|
||||||
final GoogleConnectionManager connectionManager;
|
|
||||||
if(authToken == null) {
|
|
||||||
Log.e("astrid-sync", "No token, unable to sync");
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
connectionManager = new GoogleConnectionManager(authToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
taskService = new GoogleTaskService(connectionManager);
|
|
||||||
performSync();
|
|
||||||
} catch (IllegalStateException e) {
|
|
||||||
// occurs when application was closed
|
|
||||||
} catch (Exception e) {
|
|
||||||
handleException("gtasks-authenticate", e, true);
|
|
||||||
} finally {
|
|
||||||
gtasksPreferenceService.stopOngoing();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If user isn't already signed in, show sign in dialog. Else perform sync.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void initiateManual(final Activity activity) {
|
|
||||||
String authToken = gtasksPreferenceService.getToken();
|
|
||||||
gtasksPreferenceService.stopOngoing();
|
|
||||||
|
|
||||||
// check if we have a token & it works
|
|
||||||
if(authToken == null) {
|
|
||||||
try {
|
|
||||||
final AuthManager authManager = AuthManagerFactory.getAuthManager(activity, 0, new Bundle(), true, "goanna_mobile");
|
|
||||||
authManager.invalidateAndRefresh(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
String token = authManager.getAuthToken();
|
|
||||||
if(token != null) {
|
|
||||||
gtasksPreferenceService.setToken(token);
|
|
||||||
//activity.startService(new Intent(SyncBackgroundService.SYNC_ACTION, null,
|
|
||||||
// activity, GtasksBackgroundService.class));
|
|
||||||
System.err.println("yay! " + token);
|
|
||||||
activity.finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (Exception e) {
|
|
||||||
handleException("auth", e, true);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
activity.startService(new Intent(SyncBackgroundService.SYNC_ACTION, null,
|
|
||||||
activity, GtasksBackgroundService.class));
|
|
||||||
activity.finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
|
||||||
// ----------------------------------------------------- synchronization!
|
|
||||||
// ----------------------------------------------------------------------
|
|
||||||
|
|
||||||
protected void performSync() {
|
|
||||||
FlurryAgent.onEvent("gtasks-started");
|
|
||||||
gtasksPreferenceService.recordSyncStart();
|
|
||||||
|
|
||||||
try {
|
|
||||||
GoogleTaskView taskView = taskService.getTaskView();
|
|
||||||
Preferences.setString(GtasksPreferenceService.PREF_DEFAULT_LIST,
|
|
||||||
taskView.getActiveTaskList().getInfo().getId());
|
|
||||||
|
|
||||||
gtasksListService.updateLists(taskView.getAllLists());
|
|
||||||
|
|
||||||
gtasksTaskListUpdater.createParentSiblingMaps();
|
|
||||||
|
|
||||||
// batched read tasks for each list
|
|
||||||
ArrayList<GtasksTaskContainer> remoteTasks = new ArrayList<GtasksTaskContainer>();
|
|
||||||
for(StoreObject dashboard : gtasksListService.getLists()) {
|
|
||||||
String listId = dashboard.getValue(GtasksList.REMOTE_ID);
|
|
||||||
List<GoogleTaskTask> list = taskService.getTasks(listId);
|
|
||||||
readTasksIntoRemoteTasks(list, remoteTasks);
|
|
||||||
}
|
|
||||||
|
|
||||||
SyncData<GtasksTaskContainer> syncData = populateSyncData(remoteTasks);
|
|
||||||
try {
|
|
||||||
synchronizeTasks(syncData);
|
|
||||||
} finally {
|
|
||||||
syncData.localCreated.close();
|
|
||||||
syncData.localUpdated.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
gtasksTaskListUpdater.updateAllMetadata();
|
|
||||||
|
|
||||||
gtasksPreferenceService.recordSuccessfulSync();
|
|
||||||
FlurryAgent.onEvent("gtasks-sync-finished"); //$NON-NLS-1$
|
|
||||||
} catch (IllegalStateException e) {
|
|
||||||
// occurs when application was closed
|
|
||||||
} catch (Exception e) {
|
|
||||||
handleException("gtasks-sync", e, true); //$NON-NLS-1$
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void readTasksIntoRemoteTasks(List<GoogleTaskTask> list,
|
|
||||||
ArrayList<GtasksTaskContainer> remoteTasks) {
|
|
||||||
|
|
||||||
int order = 0;
|
|
||||||
HashMap<String, String> parents = new HashMap<String, String>();
|
|
||||||
HashMap<String, Integer> indentation = new HashMap<String, Integer>();
|
|
||||||
HashMap<String, String> parentToPriorSiblingMap = new HashMap<String, String>();
|
|
||||||
|
|
||||||
for(GoogleTaskTask remoteTask : list) {
|
|
||||||
GtasksTaskContainer container = parseRemoteTask(remoteTask);
|
|
||||||
String id = remoteTask.getId();
|
|
||||||
|
|
||||||
// update parents, prior sibling
|
|
||||||
for(String child : remoteTask.getChild_ids())
|
|
||||||
parents.put(child, id);
|
|
||||||
String parent = parents.get(id); // can be null, which means top level task
|
|
||||||
container.parentId = parent;
|
|
||||||
if(parentToPriorSiblingMap.containsKey(parent))
|
|
||||||
container.priorSiblingId = parentToPriorSiblingMap.get(parent);
|
|
||||||
parentToPriorSiblingMap.put(parent, id);
|
|
||||||
|
|
||||||
// update order, indent
|
|
||||||
container.gtaskMetadata.setValue(GtasksMetadata.ORDER, order++);
|
|
||||||
int indent = findIndentation(parents, indentation, id);
|
|
||||||
indentation.put(id, indent);
|
|
||||||
container.gtaskMetadata.setValue(GtasksMetadata.INDENT, indent);
|
|
||||||
|
|
||||||
// update reminder flags for incoming remote tasks to prevent annoying
|
|
||||||
if(container.task.hasDueDate() && container.task.getValue(Task.DUE_DATE) < DateUtilities.now())
|
|
||||||
container.task.setFlag(Task.REMINDER_FLAGS, Task.NOTIFY_AFTER_DEADLINE, false);
|
|
||||||
gtasksMetadataService.findLocalMatch(container);
|
|
||||||
remoteTasks.add(container);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int findIndentation(HashMap<String, String> parents,
|
|
||||||
HashMap<String, Integer> indentation, String task) {
|
|
||||||
if(indentation.containsKey(task))
|
|
||||||
return indentation.get(task);
|
|
||||||
|
|
||||||
if(!parents.containsKey(task))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return findIndentation(parents, indentation, parents.get(task)) + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
|
||||||
// ------------------------------------------------------------ sync data
|
|
||||||
// ----------------------------------------------------------------------
|
|
||||||
|
|
||||||
// all synchronized properties
|
|
||||||
private static final Property<?>[] PROPERTIES = new Property<?>[] {
|
|
||||||
Task.ID,
|
|
||||||
Task.TITLE,
|
|
||||||
Task.DUE_DATE,
|
|
||||||
Task.CREATION_DATE,
|
|
||||||
Task.COMPLETION_DATE,
|
|
||||||
Task.DELETION_DATE,
|
|
||||||
Task.REMINDER_FLAGS,
|
|
||||||
Task.NOTES,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Populate SyncData data structure
|
|
||||||
* @throws JSONException
|
|
||||||
*/
|
|
||||||
private SyncData<GtasksTaskContainer> populateSyncData(ArrayList<GtasksTaskContainer> remoteTasks) throws JSONException {
|
|
||||||
// fetch locally created tasks
|
|
||||||
TodorooCursor<Task> localCreated = gtasksMetadataService.getLocallyCreated(PROPERTIES);
|
|
||||||
|
|
||||||
// fetch locally updated tasks
|
|
||||||
TodorooCursor<Task> localUpdated = gtasksMetadataService.getLocallyUpdated(PROPERTIES);
|
|
||||||
|
|
||||||
return new SyncData<GtasksTaskContainer>(remoteTasks, localCreated, localUpdated);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
|
||||||
// ------------------------------------------------- create / push / pull
|
|
||||||
// ----------------------------------------------------------------------
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected GtasksTaskContainer create(GtasksTaskContainer local) throws IOException {
|
|
||||||
String list = Preferences.getStringValue(GtasksPreferenceService.PREF_DEFAULT_LIST);
|
|
||||||
if(local.gtaskMetadata.containsNonNullValue(GtasksMetadata.LIST_ID))
|
|
||||||
list = local.gtaskMetadata.getValue(GtasksMetadata.LIST_ID);
|
|
||||||
gtasksTaskListUpdater.updateParentAndSibling(local);
|
|
||||||
|
|
||||||
ConvenientTaskCreator createdTask;
|
|
||||||
try {
|
|
||||||
createdTask = taskService.createTask(list, local.task.getValue(Task.TITLE));
|
|
||||||
createdTask.parentId(local.parentId);
|
|
||||||
} catch (JSONException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
String remoteId = updateTaskHelper(local, null, createdTask);
|
|
||||||
gtasksTaskListUpdater.addRemoteTaskMapping(local.task.getId(), remoteId);
|
|
||||||
local.gtaskMetadata.setValue(GtasksMetadata.LIST_ID, remoteId);
|
|
||||||
|
|
||||||
return local;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String updateTaskHelper(GtasksTaskContainer local,
|
|
||||||
GtasksTaskContainer remote, TaskBuilder<?> builder) throws IOException {
|
|
||||||
|
|
||||||
String idTask = local.gtaskMetadata.getValue(GtasksMetadata.ID);
|
|
||||||
String idList = local.gtaskMetadata.getValue(GtasksMetadata.LIST_ID);
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
// moving between lists
|
|
||||||
if(remote != null && !idList.equals(remote.gtaskMetadata.getValue(GtasksMetadata.LIST_ID))) {
|
|
||||||
taskService.executeActions(a.moveTask(idTask, idList, remote.gtaskMetadata.getValue(GtasksMetadata.LIST_ID), null));
|
|
||||||
}
|
|
||||||
|
|
||||||
// other properties
|
|
||||||
if(shouldTransmit(local, Task.DUE_DATE, remote))
|
|
||||||
builder.taskDate(local.task.getValue(Task.DUE_DATE));
|
|
||||||
if(shouldTransmit(local, Task.COMPLETION_DATE, remote))
|
|
||||||
builder.completed(local.task.isCompleted());
|
|
||||||
if(shouldTransmit(local, Task.DELETION_DATE, remote))
|
|
||||||
builder.deleted(local.task.isDeleted());
|
|
||||||
if(shouldTransmit(local, Task.NOTES, remote))
|
|
||||||
builder.notes(local.task.getValue(Task.NOTES));
|
|
||||||
|
|
||||||
String id = idList;
|
|
||||||
|
|
||||||
// write task
|
|
||||||
if(builder instanceof TaskModifier) {
|
|
||||||
ListAction moveAction = l.move(idTask, local.parentId, local.priorSiblingId);
|
|
||||||
ListAction action = ((TaskModifier) builder).done();
|
|
||||||
if(remote == null || local.parentId != remote.parentId || local.priorSiblingId != remote.priorSiblingId)
|
|
||||||
taskService.executeListActions(idList, action, moveAction);
|
|
||||||
else
|
|
||||||
taskService.executeListActions(idList, action);
|
|
||||||
} else {
|
|
||||||
id = ((ConvenientTaskCreator)builder).go();
|
|
||||||
ListAction moveAction = l.move(id, local.parentId, local.priorSiblingId);
|
|
||||||
taskService.executeListActions(idList, moveAction);
|
|
||||||
}
|
|
||||||
|
|
||||||
return id;
|
|
||||||
} catch (JSONException e) {
|
|
||||||
throw new GoogleTasksException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Create a task container for the given RtmTaskSeries
|
|
||||||
* @throws JSONException */
|
|
||||||
private GtasksTaskContainer parseRemoteTask(GoogleTaskTask remoteTask) {
|
|
||||||
Task task = new Task();
|
|
||||||
ArrayList<Metadata> metadata = new ArrayList<Metadata>();
|
|
||||||
|
|
||||||
task.setValue(Task.TITLE, remoteTask.getName());
|
|
||||||
task.setValue(Task.CREATION_DATE, DateUtilities.now());
|
|
||||||
task.setValue(Task.COMPLETION_DATE, remoteTask.getCompleted_date());
|
|
||||||
task.setValue(Task.DELETION_DATE, remoteTask.isDeleted() ? DateUtilities.now() : 0);
|
|
||||||
|
|
||||||
long dueDate = remoteTask.getTask_date();
|
|
||||||
task.setValue(Task.DUE_DATE, task.createDueDate(Task.URGENCY_SPECIFIC_DAY, dueDate));
|
|
||||||
task.setValue(Task.NOTES, remoteTask.getNotes());
|
|
||||||
|
|
||||||
Metadata gtasksMetadata = GtasksMetadata.createEmptyMetadata(AbstractModel.NO_ID);
|
|
||||||
gtasksMetadata.setValue(GtasksMetadata.ID, remoteTask.getId());
|
|
||||||
gtasksMetadata.setValue(GtasksMetadata.LIST_ID, remoteTask.getList_id());
|
|
||||||
|
|
||||||
GtasksTaskContainer container = new GtasksTaskContainer(task, metadata,
|
|
||||||
gtasksMetadata);
|
|
||||||
return container;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected GtasksTaskContainer pull(GtasksTaskContainer task) throws IOException {
|
|
||||||
if(!task.gtaskMetadata.containsNonNullValue(GtasksMetadata.ID) ||
|
|
||||||
!task.gtaskMetadata.containsNonNullValue(GtasksMetadata.LIST_ID))
|
|
||||||
throw new ApiServiceException("Tried to read an invalid task"); //$NON-NLS-1$
|
|
||||||
|
|
||||||
String idToMatch = task.gtaskMetadata.getValue(GtasksMetadata.ID);
|
|
||||||
List<GoogleTaskTask> tasks;
|
|
||||||
try {
|
|
||||||
tasks = taskService.getTasks(task.gtaskMetadata.getValue(GtasksMetadata.LIST_ID));
|
|
||||||
} catch (JSONException e) {
|
|
||||||
throw new GoogleTasksException(e);
|
|
||||||
}
|
|
||||||
for(GoogleTaskTask remoteTask : tasks) {
|
|
||||||
if(remoteTask.getId().equals(idToMatch)) {
|
|
||||||
return parseRemoteTask(remoteTask);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new GoogleTasksException("Could not find remote task to pull.");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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(GtasksTaskContainer local, GtasksTaskContainer remote) throws IOException {
|
|
||||||
try {
|
|
||||||
gtasksTaskListUpdater.updateParentAndSibling(local);
|
|
||||||
|
|
||||||
String id = local.gtaskMetadata.getValue(GtasksMetadata.ID);
|
|
||||||
TaskModifier modifyTask = l.modifyTask(id);
|
|
||||||
if(shouldTransmit(local, Task.TITLE, remote))
|
|
||||||
modifyTask.name(local.task.getValue(Task.TITLE));
|
|
||||||
updateTaskHelper(local, remote, modifyTask);
|
|
||||||
|
|
||||||
} catch (JSONException e) {
|
|
||||||
throw new GoogleTasksException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
|
||||||
// --------------------------------------------------------- read / write
|
|
||||||
// ----------------------------------------------------------------------
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected GtasksTaskContainer read(TodorooCursor<Task> cursor) throws IOException {
|
|
||||||
return gtasksMetadataService.readTaskAndMetadata(cursor);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void write(GtasksTaskContainer task) throws IOException {
|
|
||||||
gtasksMetadataService.saveTaskAndMetadata(task);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
|
||||||
// --------------------------------------------------------- misc helpers
|
|
||||||
// ----------------------------------------------------------------------
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected int matchTask(ArrayList<GtasksTaskContainer> tasks, GtasksTaskContainer target) {
|
|
||||||
int length = tasks.size();
|
|
||||||
for(int i = 0; i < length; i++) {
|
|
||||||
GtasksTaskContainer task = tasks.get(i);
|
|
||||||
if(AndroidUtilities.equals(task.gtaskMetadata.getValue(GtasksMetadata.ID),
|
|
||||||
target.gtaskMetadata.getValue(GtasksMetadata.ID)))
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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(SyncContainer task, Property<?> property, SyncContainer 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));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected int updateNotification(Context context, Notification notification) {
|
|
||||||
String notificationTitle = context.getString(R.string.gtasks_notification_title);
|
|
||||||
Intent intent = new Intent(context, GtasksPreferences.class);
|
|
||||||
PendingIntent notificationIntent = PendingIntent.getActivity(context, 0,
|
|
||||||
intent, 0);
|
|
||||||
notification.setLatestEventInfo(context,
|
|
||||||
notificationTitle, context.getString(R.string.SyP_progress),
|
|
||||||
notificationIntent);
|
|
||||||
return Constants.NOTIFICATION_SYNC;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void transferIdentifiers(GtasksTaskContainer source,
|
|
||||||
GtasksTaskContainer destination) {
|
|
||||||
destination.gtaskMetadata = source.gtaskMetadata;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,56 +0,0 @@
|
|||||||
package com.todoroo.astrid.gtasks.sync;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Iterator;
|
|
||||||
|
|
||||||
import com.todoroo.astrid.data.Metadata;
|
|
||||||
import com.todoroo.astrid.data.Task;
|
|
||||||
import com.todoroo.astrid.gtasks.GtasksMetadata;
|
|
||||||
import com.todoroo.astrid.sync.SyncContainer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* RTM Task Container
|
|
||||||
*
|
|
||||||
* @author Tim Su <tim@todoroo.com>
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class GtasksTaskContainer extends SyncContainer {
|
|
||||||
|
|
||||||
public Metadata gtaskMetadata;
|
|
||||||
|
|
||||||
// position information
|
|
||||||
public String parentId = null;
|
|
||||||
public String priorSiblingId = null;
|
|
||||||
|
|
||||||
public GtasksTaskContainer(Task task, ArrayList<Metadata> metadata, Metadata gtaskMetadata) {
|
|
||||||
this.task = task;
|
|
||||||
this.metadata = metadata;
|
|
||||||
this.gtaskMetadata = gtaskMetadata;
|
|
||||||
if(this.gtaskMetadata == null) {
|
|
||||||
this.gtaskMetadata = GtasksMetadata.createEmptyMetadata(task.getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public GtasksTaskContainer(Task task, ArrayList<Metadata> metadata) {
|
|
||||||
this.task = task;
|
|
||||||
this.metadata = metadata;
|
|
||||||
|
|
||||||
for(Iterator<Metadata> iterator = metadata.iterator(); iterator.hasNext(); ) {
|
|
||||||
Metadata item = iterator.next();
|
|
||||||
if(GtasksMetadata.METADATA_KEY.equals(item.getValue(Metadata.KEY))) {
|
|
||||||
gtaskMetadata = item;
|
|
||||||
iterator.remove();
|
|
||||||
// don't break, could be multiple
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(this.gtaskMetadata == null) {
|
|
||||||
this.gtaskMetadata = GtasksMetadata.createEmptyMetadata(task.getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void prepareForSaving() {
|
|
||||||
super.prepareForSaving();
|
|
||||||
metadata.add(gtaskMetadata);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue