Gtasks boilerplate. That's a lot of boilerplate, but it does get better with each iteration. In the future we should extract more of this type of functionality

pull/14/head
Tim Su 15 years ago
parent 2c6400ed89
commit 35f5e65adc

@ -0,0 +1,83 @@
/**
* 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.astrid.api.AstridApiConstants;
import com.todoroo.astrid.data.Metadata;
/**
* Exposes Task Details for Remember the Milk:
* - RTM list
* - RTM repeat information
* - RTM notes
*
* @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;
@Override
public void onReceive(Context context, Intent intent) {
ContextManager.setContext(context);
// if we aren't logged in, don't expose features
if(!GtasksUtilities.INSTANCE.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, GtasksUtilities.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;
DependencyInjectionService.getInstance().inject(this);
Metadata metadata = gtasksMetadataService.getTaskMetadata(id);
if(metadata == null)
return null;
StringBuilder builder = new StringBuilder();
long listId = metadata.getValue(GtasksMetadata.LIST_ID);
String listName = gtasksListService.getListName(listId);
// RTM list is out of date. don't display RTM stuff
if(listName == GtasksListService.LIST_NOT_FOUND)
return null;
builder.append("<img src='silk_folder'/> ").append(listName); //$NON-NLS-1$
return builder.toString();
}
}

@ -0,0 +1,96 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.gtasks;
import org.weloveastrid.rmilk.MilkUtilities;
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.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.Join;
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 RTM lists
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class GtasksFilterExposer extends BroadcastReceiver {
@Autowired
private GtasksListService gtasksListService;
private StoreObject[] lists;
private Filter filterFromList(StoreObject list) {
String listName = list.getValue(GtasksList.NAME);
ContentValues values = new ContentValues();
values.putAll(GtasksMetadata.createEmptyMetadata().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.activeAndVisible(),
GtasksMetadata.LIST_ID.eq(list.getValue(GtasksList.REMOTE_ID)))),
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(!GtasksUtilities.INSTANCE.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, MilkUtilities.IDENTIFIER);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, list);
context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
}
private boolean noListsToShow() {
return lists.length == 0;
}
}

@ -0,0 +1,31 @@
package com.todoroo.astrid.gtasks;
import com.todoroo.andlib.data.Property.IntegerProperty;
import com.todoroo.andlib.data.Property.LongProperty;
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 LongProperty REMOTE_ID = new LongProperty(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);
}

@ -0,0 +1,95 @@
package com.todoroo.astrid.gtasks;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
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;
public class GtasksListService {
public static final String LIST_NOT_FOUND = 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(long listId) {
readLists();
for(StoreObject list : lists)
if(list.getValue(GtasksList.REMOTE_ID).equals(listId))
return list.getValue(GtasksList.NAME);
return LIST_NOT_FOUND;
}
@SuppressWarnings("nls")
public void updateLists(JSONArray newLists) throws JSONException {
for(int i = 0; i < newLists.length(); i++) {
JSONObject remote = newLists.getJSONObject(i);
long id = remote.optLong("id");
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.getString("title"));
local.setValue(GtasksList.ORDER, i);
storeObjectDao.persist(local);
}
clearListCache();
}
private void clearListCache() {
lists = null;
}
}

@ -0,0 +1,47 @@
package com.todoroo.astrid.gtasks;
import com.todoroo.andlib.data.Property.IntegerProperty;
import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.utility.Preferences;
/**
* Metadata entries for a GTasks Task
* @author Tim Su <tim@todoroo.com>
*
*/
public class GtasksMetadata {
private static final int ORDERING_UNSET = -1;
/** metadata key */
public static final String METADATA_KEY = "gtasks"; //$NON-NLS-1$
/** task id in google */
public static final LongProperty ID = new LongProperty(Metadata.TABLE,
Metadata.VALUE1.name);
public static final LongProperty LIST_ID = new LongProperty(Metadata.TABLE,
Metadata.VALUE2.name);
public static final LongProperty OWNER_ID = new LongProperty(Metadata.TABLE,
Metadata.VALUE3.name);
public static final IntegerProperty INDENTATION = new IntegerProperty(Metadata.TABLE,
Metadata.VALUE4.name);
public static final IntegerProperty ORDERING = new IntegerProperty(Metadata.TABLE,
Metadata.VALUE5.name);
public static Metadata createEmptyMetadata() {
Metadata metadata = new Metadata();
metadata.setValue(Metadata.KEY, GtasksMetadata.METADATA_KEY);
metadata.setValue(ID, 0L);
metadata.setValue(LIST_ID, Preferences.getLong(GtasksUtilities.PREF_DEFAULT_LIST, 0));
metadata.setValue(OWNER_ID, 0L);
metadata.setValue(INDENTATION, 0);
metadata.setValue(ORDERING, ORDERING_UNSET);
return metadata;
}
}

@ -0,0 +1,102 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.gtasks;
import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Join;
import com.todoroo.andlib.sql.Query;
import com.todoroo.astrid.core.PluginServices;
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.service.MetadataService;
import com.todoroo.astrid.service.TaskService;
/**
* Service for working with GTasks metadata
*
* @author Tim Su <tim@todoroo.com>
*
*/
public final class GtasksMetadataService {
// --- constants
/** Utility for joining tasks with metadata */
public static final Join METADATA_JOIN = Join.left(Metadata.TABLE, Task.ID.eq(Metadata.TASK));
// --- instance variables
@Autowired
private TaskService taskService;
@Autowired
private MetadataService metadataService;
public GtasksMetadataService() {
DependencyInjectionService.getInstance().inject(this);
}
// --- task and metadata methods
/**
* Clears metadata information. Used when user logs out of service
*/
public void clearMetadata() {
metadataService.deleteWhere(Metadata.KEY.eq(GtasksMetadata.METADATA_KEY));
PluginServices.getTaskService().clearDetails(Task.ID.in(Query.select(Metadata.TASK).from(Metadata.TABLE).
where(MetadataCriteria.withKey(GtasksMetadata.METADATA_KEY))));
}
/**
* Gets tasks that were created since last sync
* @param properties
* @return
*/
public TodorooCursor<Task> getLocallyCreated(Property<?>[] properties) {
return
taskService.query(Query.select(properties).join(GtasksMetadataService.METADATA_JOIN).where(Criterion.and(
Criterion.not(Task.ID.in(Query.select(Metadata.TASK).from(Metadata.TABLE).
where(Criterion.and(MetadataCriteria.withKey(GtasksMetadata.METADATA_KEY), GtasksMetadata.ID.gt(0))))),
TaskCriteria.isActive())).groupBy(Task.ID));
}
/**
* Gets tasks that were modified since last sync
* @param properties
* @return null if never sync'd
*/
public TodorooCursor<Task> getLocallyUpdated(Property<?>[] properties) {
long lastSyncDate = GtasksUtilities.INSTANCE.getLastSyncDate();
if(lastSyncDate == 0)
return taskService.query(Query.select(Task.ID).where(Criterion.none));
return
taskService.query(Query.select(properties).join(GtasksMetadataService.METADATA_JOIN).
where(Criterion.and(MetadataCriteria.withKey(GtasksMetadata.METADATA_KEY),
Task.MODIFICATION_DATE.gt(lastSyncDate))).groupBy(Task.ID));
}
/**
* Reads metadata out of a task
* @return null if no metadata found
*/
public Metadata getTaskMetadata(long taskId) {
TodorooCursor<Metadata> cursor = metadataService.query(Query.select(
Metadata.PROPERTIES).where(
MetadataCriteria.byTaskAndwithKey(taskId, GtasksMetadata.METADATA_KEY)));
try {
if(cursor.getCount() == 0)
return null;
cursor.moveToFirst();
return new Metadata(cursor);
} finally {
cursor.close();
}
}
}

@ -0,0 +1,36 @@
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 GtasksUtilities extends SyncProviderUtilities {
/** add-on identifier */
public static final String IDENTIFIER = "gtasks"; //$NON-NLS-1$
public static final GtasksUtilities INSTANCE = new GtasksUtilities();
@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$
private GtasksUtilities() {
// prevent instantiation
}
}

@ -228,6 +228,10 @@
<string name="producteev_PPr_email">producteev_email</string>
<string name="producteev_PPr_password">producteev_password</string>
<string name="producteev_PPr_defaultdash_key">producteev_defaultdash</string>
<!-- =========================================================== GTASKS == -->
<string name="gtasks_GPr_interval_key">gtasks_sync_freq</string>
<!-- ============================================================ OTHER == -->

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- See the file "LICENSE" for the full license governing this code. -->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<!-- ============================================= Plugin Boilerplate == -->
<!-- filters header: GTasks -->
<string name="gtasks_FEx_header">Google Tasks</string>
<!-- filter category for GTasks lists -->
<string name="gtasks_FEx_list">By List</string>
<!-- ============================================== GtasksPreferences == -->
<!-- GTasks Preferences Title -->
<string name="gtasks_MPr_header">Google Tasks</string>
<!-- ================================================ Synchronization == -->
<!-- title for notification tray when synchronizing -->
<string name="gtasks_notification_title">Astrid: Google Tasks</string>
</resources>

@ -11,6 +11,8 @@ import com.todoroo.astrid.dao.Database;
import com.todoroo.astrid.dao.MetadataDao;
import com.todoroo.astrid.dao.StoreObjectDao;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.gtasks.GtasksListService;
import com.todoroo.astrid.gtasks.GtasksMetadataService;
import com.todoroo.astrid.utility.Constants;
/**
@ -61,6 +63,10 @@ public class AstridDependencyInjector extends AbstractDependencyInjector {
injectables.put("alertsTable", "alerts");
injectables.put("syncTable", "sync");
// com.todoroo.astrid.gtasks
injectables.put("gtasksListService", GtasksListService.class);
injectables.put("gtasksMetadataService", GtasksMetadataService.class);
// these make reference to fields defined above
injectables.put("errorReporters", new ErrorReporter[] {
new AndroidLogReporter(),

Loading…
Cancel
Save