Producteev Shared Desktop initial work. what's here: synchronizing multiple workspaces, displaying this information, displaying task responsiblity info, switching things into multiple workspaces

pull/14/head
Tim Su 16 years ago
parent 8b4b79db88
commit aa2fe7db1b

@ -1,95 +1,118 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.producteev;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import com.timsu.astrid.R;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.astrid.adapter.TaskAdapter;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.api.DetailExposer;
import com.todoroo.astrid.model.Metadata;
import com.todoroo.astrid.producteev.sync.ProducteevDataService;
import com.todoroo.astrid.producteev.sync.ProducteevNote;
import com.todoroo.astrid.producteev.sync.ProducteevTask;
/**
* Exposes Task Details for Producteev:
* - notes
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class ProducteevDetailExposer extends BroadcastReceiver implements DetailExposer{
@Override
public void onReceive(Context context, Intent intent) {
// if we aren't logged in, don't expose features
if(!ProducteevUtilities.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(context, taskId, extended);
if(taskDetail == null)
return;
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_DETAILS);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, ProducteevUtilities.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);
}
@Override
public String getTaskDetails(Context context, long id, boolean extended) {
Metadata metadata = ProducteevDataService.getInstance().getTaskMetadata(id);
if(metadata == null)
return null;
StringBuilder builder = new StringBuilder();
if(!extended) {
long dashboardId = metadata.getValue(ProducteevTask.DASHBOARD_ID);
String dashboardName = ProducteevDataService.getInstance().getDashboardName(dashboardId);
// Prod dashboard is out of date. don't display Producteev stuff
if(dashboardName == null)
return null;
if(dashboardId > 0) {
builder.append(context.getString(R.string.producteev_TLA_dashboard,
dashboardId)).append(TaskAdapter.DETAIL_SEPARATOR);
}
} else {
TodorooCursor<Metadata> notesCursor = ProducteevDataService.getInstance().getTaskNotesCursor(id);
try {
for(notesCursor.moveToFirst(); !notesCursor.isAfterLast(); notesCursor.moveToNext()) {
metadata.readFromCursor(notesCursor);
builder.append(metadata.getValue(ProducteevNote.MESSAGE)).append(TaskAdapter.DETAIL_SEPARATOR);
}
} finally {
notesCursor.close();
}
}
if(builder.length() == 0)
return null;
String result = builder.toString();
return result.substring(0, result.length() - TaskAdapter.DETAIL_SEPARATOR.length());
}
@Override
public String getPluginIdentifier() {
return ProducteevUtilities.IDENTIFIER;
}
}
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.producteev;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import com.timsu.astrid.R;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.astrid.adapter.TaskAdapter;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.api.DetailExposer;
import com.todoroo.astrid.model.Metadata;
import com.todoroo.astrid.model.StoreObject;
import com.todoroo.astrid.producteev.sync.ProducteevDashboard;
import com.todoroo.astrid.producteev.sync.ProducteevDataService;
import com.todoroo.astrid.producteev.sync.ProducteevNote;
import com.todoroo.astrid.producteev.sync.ProducteevTask;
import com.todoroo.astrid.utility.Preferences;
/**
* Exposes Task Details for Producteev:
* - notes
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class ProducteevDetailExposer extends BroadcastReceiver implements DetailExposer{
@Override
public void onReceive(Context context, Intent intent) {
// if we aren't logged in, don't expose features
if(!ProducteevUtilities.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(context, taskId, extended);
if(taskDetail == null)
return;
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_DETAILS);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, ProducteevUtilities.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);
}
@Override
public String getTaskDetails(Context context, long id, boolean extended) {
Metadata metadata = ProducteevDataService.getInstance().getTaskMetadata(id);
if(metadata == null)
return null;
StringBuilder builder = new StringBuilder();
if(!extended) {
long dashboardId = metadata.getValue(ProducteevTask.DASHBOARD_ID);
long responsibleId = metadata.getValue(ProducteevTask.RESPONSIBLE_ID);
// display dashboard if not "no sync" or "default"
StoreObject ownerDashboard = null;
for(StoreObject dashboard : ProducteevDataService.getInstance().getDashboards()) {
if(dashboard.getValue(ProducteevDashboard.REMOTE_ID) == dashboardId) {
ownerDashboard = dashboard;
break;
}
}
if(dashboardId != ProducteevUtilities.DASHBOARD_NO_SYNC && dashboardId
!= Preferences.getLong(ProducteevUtilities.PREF_DEFAULT_DASHBOARD, 0L) &&
ownerDashboard != null) {
String dashboardName = ownerDashboard.getValue(ProducteevDashboard.NAME);
builder.append(context.getString(R.string.producteev_TLA_dashboard,
dashboardName)).append(TaskAdapter.DETAIL_SEPARATOR);
}
// display responsible user if not current one
if(responsibleId > 0 && ownerDashboard != null && responsibleId !=
Preferences.getLong(ProducteevUtilities.PREF_USER_ID, 0L)) {
String users = ownerDashboard.getValue(ProducteevDashboard.USERS);
int index = users.indexOf(";" + responsibleId + ","); //$NON-NLS-1$ //$NON-NLS-2$
if(index > -1) {
String user = users.substring(users.indexOf(',', index),
users.indexOf(';', index + 1));
builder.append(context.getString(R.string.producteev_TLA_responsible,
user)).append(TaskAdapter.DETAIL_SEPARATOR);
}
}
} else {
TodorooCursor<Metadata> notesCursor = ProducteevDataService.getInstance().getTaskNotesCursor(id);
try {
for(notesCursor.moveToFirst(); !notesCursor.isAfterLast(); notesCursor.moveToNext()) {
metadata.readFromCursor(notesCursor);
builder.append(metadata.getValue(ProducteevNote.MESSAGE)).append(TaskAdapter.DETAIL_SEPARATOR);
}
} finally {
notesCursor.close();
}
}
if(builder.length() == 0)
return null;
String result = builder.toString();
return result.substring(0, result.length() - TaskAdapter.DETAIL_SEPARATOR.length());
}
@Override
public String getPluginIdentifier() {
return ProducteevUtilities.IDENTIFIER;
}
}

@ -10,6 +10,9 @@ import com.timsu.astrid.R;
import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.astrid.common.SyncProviderPreferences;
import com.todoroo.astrid.common.SyncProviderUtilities;
import com.todoroo.astrid.model.StoreObject;
import com.todoroo.astrid.producteev.sync.ProducteevDashboard;
import com.todoroo.astrid.producteev.sync.ProducteevDataService;
import com.todoroo.astrid.producteev.sync.ProducteevSyncProvider;
/**
@ -47,16 +50,23 @@ public class ProducteevPreferences extends SyncProviderPreferences {
super.onCreate(savedInstanceState);
ListPreference defaultDash = (ListPreference)findPreference(getString(R.string.producteev_PPr_defaultdash_key));
String[] entries, entryValues;
if(ProducteevUtilities.INSTANCE.isLoggedIn()) {
//
StoreObject[] dashboards = ProducteevDataService.getInstance().getDashboards();
entries = new String[dashboards.length + 1];
entryValues = new String[dashboards.length + 1];
for(int i = 0; i < dashboards.length; i++) {
entries[i + 1] = dashboards[i].getValue(ProducteevDashboard.NAME);
entryValues[i + 1] = Long.toString(dashboards[i].getValue(ProducteevDashboard.REMOTE_ID));
}
} else {
entries = new String[2];
entries[1] = getString(R.string.producteev_default_dashboard);
entryValues = new String[2];
entryValues[1] = Integer.toString(ProducteevUtilities.DASHBOARD_DEFAULT);
}
String[] entries = new String[2];
entries[0] = getString(R.string.producteev_no_dashboard);
entries[1] = getString(R.string.producteev_default_dashboard);
String[] entryValues = new String[2];
entryValues[0] = Integer.toString(ProducteevUtilities.DASHBOARD_NO_SYNC);
entryValues[1] = Integer.toString(ProducteevUtilities.DASHBOARD_DEFAULT);
defaultDash.setEntries(entries);
defaultDash.setEntryValues(entryValues);
}

@ -1,9 +1,8 @@
package com.todoroo.astrid.producteev;
import android.content.SharedPreferences.Editor;
import com.timsu.astrid.R;
import com.todoroo.astrid.common.SyncProviderUtilities;
import com.todoroo.astrid.utility.Preferences;
/**
* Displays synchronization preferences and an action panel so users can
@ -37,22 +36,33 @@ public class ProducteevUtilities extends SyncProviderUtilities {
// --- producteev-specific preferences
private static final String PREF_SERVER_LAST_SYNC = "_last_server"; //$NON-NLS-1$
public static final String PREF_SERVER_LAST_SYNC = IDENTIFIER + "_last_server"; //$NON-NLS-1$
/** @return last sync date, or null if no last */
public String getLastServerSync() {
return getPrefs().getString(getIdentifier() + PREF_SERVER_LAST_SYNC, null);
}
public static final String PREF_EMAIL = IDENTIFIER + "_email"; //$NON-NLS-1$
public static final String PREF_PASSWORD = IDENTIFIER + "_password"; //$NON-NLS-1$
public static final String PREF_DEFAULT_DASHBOARD = IDENTIFIER + "_defaultdash"; //$NON-NLS-1$
public static final String PREF_USER_ID = IDENTIFIER + "_userid"; //$NON-NLS-1$
/** Deletes Last Successful Sync Date */
public void setLastServerSync(String value) {
Editor editor = getPrefs().edit();
editor.putString(getIdentifier() + PREF_SERVER_LAST_SYNC, value);
editor.commit();
/**
* Gets default dashboard from setting
* @return DASHBOARD_NO_SYNC if should not sync, otherwise remote id
*/
public long getDefaultDashboard() {
int defaultDashboard = Preferences.getIntegerFromString(R.string.producteev_PPr_defaultdash_key,
DASHBOARD_DEFAULT);
if(defaultDashboard == DASHBOARD_NO_SYNC)
return DASHBOARD_NO_SYNC;
else if(defaultDashboard == DASHBOARD_DEFAULT)
return Preferences.getLong(PREF_DEFAULT_DASHBOARD, 0);
else
return (long) defaultDashboard;
}
private ProducteevUtilities() {
//
// prevent instantiation
}
}

@ -96,6 +96,22 @@ public class ProducteevInvoker {
"fbuid", fbUid);
}
// --- dashboards
/**
* show list
*
* @param idResponsible (optional) if null return every task for current user
* @param since (optional) if not null, the function only returns tasks modified or created since this date
*
* @return array tasks/view
*/
public JSONArray dashboardsShowList(String since) throws ApiServiceException, IOException {
return getResponse(callAuthenticated("dashboards/show_list.json",
"token", token,
"since", since), "dashboards");
}
// --- tasks
/**
@ -212,6 +228,21 @@ public class ProducteevInvoker {
"deadline", deadline);
}
/**
* set a workspace
*
* @param idTask
* @param id_dashboard
*
* @return array tasks/view
*/
public JSONObject tasksSetWorkspace(long idTask, long idDashboard) throws ApiServiceException, IOException {
return callAuthenticated("tasks/set_workspace.json",
"token", token,
"id_task", idTask,
"id_dashboard", idDashboard);
}
/**
* delete a task
*

@ -1,86 +1,30 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.producteev.sync;
import android.content.ContentValues;
import com.todoroo.andlib.data.AbstractModel;
import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.Table;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.andlib.data.Property.StringProperty;
import com.todoroo.astrid.model.Task;
/**
* Data Model which represents a dashboard in Producteev
*
* @author Tim Su <tim@todoroo.com>
*
*/
@SuppressWarnings("nls")
public class ProducteevDashboard extends AbstractModel {
// --- table
public static final Table TABLE = new Table("dashboards", ProducteevDashboard.class);
// --- properties
/** ID (corresponds to RTM ID) */
public static final LongProperty ID = new LongProperty(
TABLE, ID_PROPERTY_NAME);
/** Name */
public static final StringProperty NAME = new StringProperty(
TABLE, "name");
/** List of all properties for this model */
public static final Property<?>[] PROPERTIES = generateProperties(ProducteevDashboard.class);
// --- defaults
/** Default values container */
private static final ContentValues defaultValues = new ContentValues();
// static {
// defaultValues.put(POSITION.name, 0);
// defaultValues.put(ARCHIVED.name, 0);
// }
@Override
public ContentValues getDefaultValues() {
return defaultValues;
}
// --- data access boilerplate
public ProducteevDashboard() {
super();
}
public ProducteevDashboard(TodorooCursor<ProducteevDashboard> cursor) {
this();
readPropertiesFromCursor(cursor);
}
public void readFromCursor(TodorooCursor<ProducteevDashboard> cursor) {
super.readPropertiesFromCursor(cursor);
}
@Override
public long getId() {
return getIdHelper(ID);
};
// --- parcelable helpers
private static final Creator<Task> CREATOR = new ModelCreator<Task>(Task.class);
@Override
protected Creator<? extends AbstractModel> getCreator() {
return CREATOR;
}
}
package com.todoroo.astrid.producteev.sync;
import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.andlib.data.Property.StringProperty;
import com.todoroo.astrid.model.StoreObject;
/**
* {@link StoreObject} entries for a Producteev Dashboard
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class ProducteevDashboard {
/** type*/
public static final String TYPE = "pdv-dash"; //$NON-NLS-1$
/** dashboard id in producteev */
public static final LongProperty REMOTE_ID = new LongProperty(StoreObject.TABLE,
StoreObject.ITEM.name);
/** dashboard name */
public static final StringProperty NAME = new StringProperty(StoreObject.TABLE,
StoreObject.VALUE1.name);
/** users (list in the format "id_user,name;id_user,name;") */
public static final StringProperty USERS = new StringProperty(StoreObject.TABLE,
StoreObject.VALUE2.name);
}

@ -1,235 +1,284 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.producteev.sync;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Map;
import java.util.Random;
import android.content.Context;
import com.todoroo.andlib.data.GenericDao;
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.sql.Criterion;
import com.todoroo.andlib.sql.Join;
import com.todoroo.andlib.sql.Query;
import com.todoroo.andlib.utility.SoftHashMap;
import com.todoroo.astrid.dao.MetadataDao;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.model.Metadata;
import com.todoroo.astrid.model.Task;
import com.todoroo.astrid.producteev.ProducteevUtilities;
import com.todoroo.astrid.rmilk.data.MilkNote;
import com.todoroo.astrid.tags.TagService;
public final class ProducteevDataService {
// --- constants
/** Utility for joining tasks with metadata */
public static final Join METADATA_JOIN = Join.left(Metadata.TABLE, Task.ID.eq(Metadata.TASK));
// --- singleton
private static ProducteevDataService instance = null;
public static synchronized ProducteevDataService getInstance() {
if(instance == null)
instance = new ProducteevDataService(ContextManager.getContext());
return instance;
}
// --- instance variables
protected final Context context;
private final ProducteevDatabase prodDatabase = new ProducteevDatabase();
private final GenericDao<ProducteevDashboard> prodDashboardDao;
@Autowired
private TaskDao taskDao;
@Autowired
private MetadataDao metadataDao;
private final ProducteevUtilities preferences = ProducteevUtilities.INSTANCE;
static final Random random = new Random();
private ProducteevDataService(Context context) {
this.context = context;
DependencyInjectionService.getInstance().inject(this);
prodDashboardDao = new GenericDao<ProducteevDashboard>(ProducteevDashboard.class, prodDatabase);
}
// --- task and metadata methods
/**
* Clears RTM metadata information. Used when user logs out of RTM
*/
public void clearMetadata() {
metadataDao.deleteWhere(Metadata.KEY.eq(ProducteevTask.METADATA_KEY));
metadataDao.deleteWhere(Metadata.KEY.eq(ProducteevNote.METADATA_KEY));
}
/**
* Gets tasks that were created since last sync
* @param properties
* @return
*/
public TodorooCursor<Task> getLocallyCreated(Property<?>[] properties) {
return
taskDao.query(Query.select(properties).join(ProducteevDataService.METADATA_JOIN).where(Criterion.and(
Criterion.not(Task.ID.in(Query.select(Metadata.TASK).from(Metadata.TABLE).
where(Criterion.and(MetadataCriteria.withKey(ProducteevTask.METADATA_KEY), ProducteevTask.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 = preferences.getLastSyncDate();
if(lastSyncDate == 0)
return taskDao.query(Query.select(Task.ID).where(Criterion.none));
return
taskDao.query(Query.select(properties).join(ProducteevDataService.METADATA_JOIN).
where(Criterion.and(MetadataCriteria.withKey(ProducteevTask.METADATA_KEY),
Task.MODIFICATION_DATE.gt(lastSyncDate))).groupBy(Task.ID));
}
/**
* Searches for a local task with same remote id, updates this task's id
* @param remoteTask
*/
public void findLocalMatch(ProducteevTaskContainer remoteTask) {
if(remoteTask.task.getId() != Task.NO_ID)
return;
TodorooCursor<Task> cursor = taskDao.query(Query.select(Task.ID).
join(ProducteevDataService.METADATA_JOIN).where(Criterion.and(MetadataCriteria.withKey(ProducteevTask.METADATA_KEY),
ProducteevTask.ID.eq(remoteTask.pdvTask.getValue(ProducteevTask.ID)))));
try {
if(cursor.getCount() == 0)
return;
cursor.moveToFirst();
remoteTask.task.setId(cursor.get(Task.ID));
} finally {
cursor.close();
}
}
/**
* Saves a task and its metadata
* @param task
*/
public void saveTaskAndMetadata(ProducteevTaskContainer task) {
taskDao.save(task.task, true);
metadataDao.deleteWhere(Criterion.and(MetadataCriteria.byTask(task.task.getId()),
Criterion.or(MetadataCriteria.withKey(ProducteevTask.METADATA_KEY),
MetadataCriteria.withKey(ProducteevNote.METADATA_KEY),
MetadataCriteria.withKey(TagService.KEY))));
task.metadata.add(task.pdvTask);
task.pdvTask.setValue(Metadata.KEY, ProducteevTask.METADATA_KEY);
for(Metadata metadata : task.metadata) {
metadata.setValue(Metadata.TASK, task.task.getId());
metadataDao.createNew(metadata);
}
}
/**
* Reads a task and its metadata
* @param task
* @return
*/
public ProducteevTaskContainer readTaskAndMetadata(TodorooCursor<Task> taskCursor) {
Task task = new Task(taskCursor);
// read tags, notes, etc
ArrayList<Metadata> metadata = new ArrayList<Metadata>();
TodorooCursor<Metadata> metadataCursor = metadataDao.query(Query.select(Metadata.PROPERTIES).
where(Criterion.and(MetadataCriteria.byTask(task.getId()),
Criterion.or(MetadataCriteria.withKey(TagService.KEY),
MetadataCriteria.withKey(ProducteevTask.METADATA_KEY),
// FIXME: Constant from other plugin shouldnt be used
MetadataCriteria.withKey(MilkNote.METADATA_KEY),
MetadataCriteria.withKey(ProducteevNote.METADATA_KEY)))));
try {
for(metadataCursor.moveToFirst(); !metadataCursor.isAfterLast(); metadataCursor.moveToNext()) {
metadata.add(new Metadata(metadataCursor));
}
} finally {
metadataCursor.close();
}
return new ProducteevTaskContainer(task, metadata);
}
/**
* Reads metadata out of a task
* @return null if no metadata found
*/
public Metadata getTaskMetadata(long taskId) {
TodorooCursor<Metadata> cursor = metadataDao.query(Query.select(
ProducteevTask.ID, ProducteevTask.DASHBOARD_ID).where(
MetadataCriteria.byTaskAndwithKey(taskId, ProducteevTask.METADATA_KEY)));
try {
if(cursor.getCount() == 0)
return null;
cursor.moveToFirst();
return new Metadata(cursor);
} finally {
cursor.close();
}
}
/**
* Reads task notes out of a task
*/
public TodorooCursor<Metadata> getTaskNotesCursor(long taskId) {
TodorooCursor<Metadata> cursor = metadataDao.query(Query.select(Metadata.PROPERTIES).
where(MetadataCriteria.byTaskAndwithKey(taskId, ProducteevNote.METADATA_KEY)));
return cursor;
}
// --- list methods
private final Map<Long, String> dashboardCache =
Collections.synchronizedMap(new SoftHashMap<Long, String>());
/**
* Get dashboard name by dashboard id
* @param dashboardId
* @return null if no dashboard by this id exists, otherwise dashboard name
*/
public String getDashboardName(long dashboardId) {
if(dashboardCache.containsKey(dashboardId))
return dashboardCache.get(dashboardId);
TodorooCursor<ProducteevDashboard> cursor = prodDashboardDao.query(Query.select(
ProducteevDashboard.NAME).where(ProducteevDashboard.ID.eq(dashboardId)));
try {
if(cursor.getCount() == 0) {
dashboardCache.put(dashboardId, null);
return null;
}
cursor.moveToFirst();
String name = cursor.get(ProducteevDashboard.NAME);
dashboardCache.put(dashboardId, name);
return name;
} finally {
cursor.close();
}
}
}
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.producteev.sync;
import java.util.ArrayList;
import java.util.Random;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.Context;
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.sql.Criterion;
import com.todoroo.andlib.sql.Join;
import com.todoroo.andlib.sql.Query;
import com.todoroo.astrid.dao.MetadataDao;
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
import com.todoroo.astrid.dao.StoreObjectDao;
import com.todoroo.astrid.dao.StoreObjectDao.StoreObjectCriteria;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.model.Metadata;
import com.todoroo.astrid.model.StoreObject;
import com.todoroo.astrid.model.Task;
import com.todoroo.astrid.producteev.ProducteevUtilities;
import com.todoroo.astrid.rmilk.data.MilkNote;
import com.todoroo.astrid.tags.TagService;
public final class ProducteevDataService {
// --- constants
/** Utility for joining tasks with metadata */
public static final Join METADATA_JOIN = Join.left(Metadata.TABLE, Task.ID.eq(Metadata.TASK));
// --- singleton
private static ProducteevDataService instance = null;
public static synchronized ProducteevDataService getInstance() {
if(instance == null)
instance = new ProducteevDataService(ContextManager.getContext());
return instance;
}
// --- instance variables
protected final Context context;
@Autowired
private TaskDao taskDao;
@Autowired
private MetadataDao metadataDao;
@Autowired
private StoreObjectDao storeObjectDao;
private final ProducteevUtilities preferences = ProducteevUtilities.INSTANCE;
static final Random random = new Random();
private ProducteevDataService(Context context) {
this.context = context;
DependencyInjectionService.getInstance().inject(this);
}
// --- task and metadata methods
/**
* Clears RTM metadata information. Used when user logs out of RTM
*/
public void clearMetadata() {
metadataDao.deleteWhere(Metadata.KEY.eq(ProducteevTask.METADATA_KEY));
metadataDao.deleteWhere(Metadata.KEY.eq(ProducteevNote.METADATA_KEY));
}
/**
* Gets tasks that were created since last sync
* @param properties
* @return
*/
public TodorooCursor<Task> getLocallyCreated(Property<?>[] properties) {
return
taskDao.query(Query.select(properties).join(ProducteevDataService.METADATA_JOIN).where(Criterion.and(
Criterion.not(Task.ID.in(Query.select(Metadata.TASK).from(Metadata.TABLE).
where(Criterion.and(MetadataCriteria.withKey(ProducteevTask.METADATA_KEY), ProducteevTask.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 = preferences.getLastSyncDate();
if(lastSyncDate == 0)
return taskDao.query(Query.select(Task.ID).where(Criterion.none));
return
taskDao.query(Query.select(properties).join(ProducteevDataService.METADATA_JOIN).
where(Criterion.and(MetadataCriteria.withKey(ProducteevTask.METADATA_KEY),
Task.MODIFICATION_DATE.gt(lastSyncDate))).groupBy(Task.ID));
}
/**
* Searches for a local task with same remote id, updates this task's id
* @param remoteTask
*/
public void findLocalMatch(ProducteevTaskContainer remoteTask) {
if(remoteTask.task.getId() != Task.NO_ID)
return;
TodorooCursor<Task> cursor = taskDao.query(Query.select(Task.ID).
join(ProducteevDataService.METADATA_JOIN).where(Criterion.and(MetadataCriteria.withKey(ProducteevTask.METADATA_KEY),
ProducteevTask.ID.eq(remoteTask.pdvTask.getValue(ProducteevTask.ID)))));
try {
if(cursor.getCount() == 0)
return;
cursor.moveToFirst();
remoteTask.task.setId(cursor.get(Task.ID));
} finally {
cursor.close();
}
}
/**
* Saves a task and its metadata
* @param task
*/
public void saveTaskAndMetadata(ProducteevTaskContainer task) {
taskDao.save(task.task, true);
metadataDao.deleteWhere(Criterion.and(MetadataCriteria.byTask(task.task.getId()),
Criterion.or(MetadataCriteria.withKey(ProducteevTask.METADATA_KEY),
MetadataCriteria.withKey(ProducteevNote.METADATA_KEY),
MetadataCriteria.withKey(TagService.KEY))));
task.metadata.add(task.pdvTask);
task.pdvTask.setValue(Metadata.KEY, ProducteevTask.METADATA_KEY);
for(Metadata metadata : task.metadata) {
metadata.setValue(Metadata.TASK, task.task.getId());
metadataDao.createNew(metadata);
}
}
/**
* Reads a task and its metadata
* @param task
* @return
*/
public ProducteevTaskContainer readTaskAndMetadata(TodorooCursor<Task> taskCursor) {
Task task = new Task(taskCursor);
// read tags, notes, etc
ArrayList<Metadata> metadata = new ArrayList<Metadata>();
TodorooCursor<Metadata> metadataCursor = metadataDao.query(Query.select(Metadata.PROPERTIES).
where(Criterion.and(MetadataCriteria.byTask(task.getId()),
Criterion.or(MetadataCriteria.withKey(TagService.KEY),
MetadataCriteria.withKey(ProducteevTask.METADATA_KEY),
MetadataCriteria.withKey(MilkNote.METADATA_KEY), // to sync rmilk notes
MetadataCriteria.withKey(ProducteevNote.METADATA_KEY)))));
try {
for(metadataCursor.moveToFirst(); !metadataCursor.isAfterLast(); metadataCursor.moveToNext()) {
metadata.add(new Metadata(metadataCursor));
}
} finally {
metadataCursor.close();
}
return new ProducteevTaskContainer(task, metadata);
}
/**
* Reads metadata out of a task
* @return null if no metadata found
*/
public Metadata getTaskMetadata(long taskId) {
TodorooCursor<Metadata> cursor = metadataDao.query(Query.select(
ProducteevTask.ID, ProducteevTask.DASHBOARD_ID).where(
MetadataCriteria.byTaskAndwithKey(taskId, ProducteevTask.METADATA_KEY)));
try {
if(cursor.getCount() == 0)
return null;
cursor.moveToFirst();
return new Metadata(cursor);
} finally {
cursor.close();
}
}
/**
* Reads task notes out of a task
*/
public TodorooCursor<Metadata> getTaskNotesCursor(long taskId) {
TodorooCursor<Metadata> cursor = metadataDao.query(Query.select(Metadata.PROPERTIES).
where(MetadataCriteria.byTaskAndwithKey(taskId, ProducteevNote.METADATA_KEY)));
return cursor;
}
// --- dashboard methods
private StoreObject[] dashboards = null;
/**
* Reads dashboards
*/
private void readDashboards() {
if(dashboards != null)
return;
TodorooCursor<StoreObject> cursor = storeObjectDao.query(Query.select(StoreObject.PROPERTIES).
where(StoreObjectCriteria.byType(ProducteevDashboard.TYPE)));
try {
dashboards = new StoreObject[cursor.getCount()];
for(int i = 0; i < dashboards.length; i++) {
cursor.moveToNext();
StoreObject dashboard = new StoreObject(cursor);
dashboards[i] = dashboard;
}
} finally {
cursor.close();
}
}
/**
* @return a list of dashboards
*/
public StoreObject[] getDashboards() {
readDashboards();
return dashboards;
}
/**
* Reads dashboards
* @throws JSONException
*/
@SuppressWarnings("nls")
public void updateDashboards(JSONArray changedDashboards) throws JSONException {
readDashboards();
for(int i = 0; i < changedDashboards.length(); i++) {
JSONObject remote = changedDashboards.getJSONObject(i).getJSONObject("dashboard");
long id = remote.getLong("id_dashboard");
StoreObject local = null;
for(StoreObject dashboard : dashboards) {
if(dashboard.getValue(ProducteevDashboard.REMOTE_ID).equals(id)) {
local = dashboard;
break;
}
}
if(remote.getInt("deleted") != 0) {
if(local != null)
storeObjectDao.delete(local.getId());
continue;
}
if(local == null)
local = new StoreObject();
local.setValue(StoreObject.TYPE, ProducteevDashboard.TYPE);
local.setValue(ProducteevDashboard.REMOTE_ID, id);
local.setValue(ProducteevDashboard.NAME, remote.getString("title"));
StringBuilder users = new StringBuilder();
JSONArray accessList = remote.getJSONArray("accesslist");
for(int j = 0; j < accessList.length(); j++) {
JSONObject user = accessList.getJSONObject(j).getJSONObject("user");
users.append(user.getLong("id_user")).append(',').
append(user.getString("firstName")).append(' ').
append(user.getString("lastName")).append(';');
}
local.setValue(ProducteevDashboard.USERS, users.toString());
storeObjectDao.persist(local);
}
// clear dashboard cache
dashboards = null;
}
}

@ -1,76 +0,0 @@
/*
* Copyright (c) 2009, Todoroo Inc
* All Rights Reserved
* http://www.todoroo.com
*/
package com.todoroo.astrid.producteev.sync;
import com.todoroo.andlib.data.AbstractDatabase;
import com.todoroo.andlib.data.GenericDao;
import com.todoroo.andlib.data.Table;
/**
* Database wrapper
*
* @author Tim Su <tim@todoroo.com>
*
*/
@SuppressWarnings("nls")
public class ProducteevDatabase extends AbstractDatabase {
// --- constants
/**
* Database version number. This variable must be updated when database
* tables are updated, as it determines whether a database needs updating.
*/
public static final int VERSION = 1;
/**
* Database name (must be unique)
*/
private static final String NAME = "producteev";
/**
* List of table/ If you're adding a new table, add it to this list and
* also make sure that our SQLite helper does the right thing.
*/
public static final Table[] TABLES = new Table[] {
ProducteevDashboard.TABLE,
};
// --- implementation
private final GenericDao<ProducteevDashboard> dao = new GenericDao<ProducteevDashboard>(ProducteevDashboard.class, this);
@Override
protected String getName() {
return NAME;
}
@Override
protected int getVersion() {
return VERSION;
}
@Override
public Table[] getTables() {
return TABLES;
}
public GenericDao<ProducteevDashboard> getDao() {
return dao;
}
@Override
protected void onCreateTables() {
// do nothing
}
@Override
protected boolean onUpgrade(int oldVersion, int newVersion) {
return false;
}
}

@ -1,24 +1,32 @@
package com.todoroo.astrid.producteev.sync;
import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.astrid.model.Metadata;
/**
* Metadata entries for a Producteev Task
* @author Tim Su <tim@todoroo.com>
*
*/
public class ProducteevTask {
/** metadata key */
public static final String METADATA_KEY = "producteev"; //$NON-NLS-1$
/** task id in producteev */
public static final LongProperty ID = new LongProperty(Metadata.TABLE,
Metadata.VALUE1.name);
/** dashboard id */
public static final LongProperty DASHBOARD_ID = new LongProperty(Metadata.TABLE,
Metadata.VALUE2.name);
}
package com.todoroo.astrid.producteev.sync;
import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.astrid.model.Metadata;
/**
* Metadata entries for a Producteev Task
* @author Tim Su <tim@todoroo.com>
*
*/
public class ProducteevTask {
/** metadata key */
public static final String METADATA_KEY = "producteev"; //$NON-NLS-1$
/** task id in producteev */
public static final LongProperty ID = new LongProperty(Metadata.TABLE,
Metadata.VALUE1.name);
/** dashboard id */
public static final LongProperty DASHBOARD_ID = new LongProperty(Metadata.TABLE,
Metadata.VALUE2.name);
/** creator id */
public static final LongProperty CREATOR_ID = new LongProperty(Metadata.TABLE,
Metadata.VALUE3.name);
/** responsible id */
public static final LongProperty RESPONSIBLE_ID = new LongProperty(Metadata.TABLE,
Metadata.VALUE4.name);
}

@ -35,6 +35,8 @@ public class ProducteevTaskContainer extends TaskContainer {
pdvTask.setValue(Metadata.KEY, ProducteevTask.METADATA_KEY);
pdvTask.setValue(ProducteevTask.ID, remoteTask.optLong("id_task"));
pdvTask.setValue(ProducteevTask.DASHBOARD_ID, remoteTask.optLong("id_dashboard"));
pdvTask.setValue(ProducteevTask.RESPONSIBLE_ID, remoteTask.optLong("id_responsible"));
pdvTask.setValue(ProducteevTask.CREATOR_ID, remoteTask.optLong("id_creator"));
}
public ProducteevTaskContainer(Task task, ArrayList<Metadata> metadata) {

@ -1,88 +1,88 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- See the file "LICENSE" for the full license governing this code. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="@android:drawable/list_selector_background"
android:paddingTop="4dip"
android:paddingBottom="4dip"
android:paddingLeft="4dip"
android:paddingRight="4dip">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="5dip"
android:orientation="vertical">
<!-- icon -->
<ImageView android:id="@+id/icon"
android:layout_width="48dip"
android:layout_height="48dip"
android:gravity="center"
android:scaleType="fitCenter"/>
<!-- free -->
<TextView android:id="@+id/free"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="10sp"
android:textColor="#00ff00"
android:text="@string/AOA_free" />
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="100"
android:paddingLeft="8dip"
android:paddingRight="3dip"
android:orientation="vertical">
<!-- add-on name -->
<TextView android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_toRightOf="@id/icon"
android:singleLine="true"
style="@style/TextAppearance.TAd_ItemTitle"/>
<!-- description -->
<TextView android:id="@+id/description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/title"
android:layout_toRightOf="@id/icon"
android:singleLine="false"
android:textSize="11sp"
style="@style/TextAppearance.TAd_ItemDetails"/>
</LinearLayout>
<!-- buttons -->
<ImageButton android:id="@+id/button_web"
android:layout_width="48dip"
android:layout_height="48dip"
android:scaleType="fitCenter"
android:layout_gravity="center" />
<ImageButton android:id="@+id/button_market"
android:layout_width="48dip"
android:layout_height="48dip"
android:scaleType="fitCenter"
android:layout_gravity="center" />
<ImageView android:id="@+id/check"
android:layout_width="48dip"
android:layout_height="48dip"
android:scaleType="center"
android:layout_gravity="center"
android:src="@android:drawable/checkbox_on_background"/>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<!-- See the file "LICENSE" for the full license governing this code. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="@android:drawable/list_selector_background"
android:paddingTop="4dip"
android:paddingBottom="4dip"
android:paddingLeft="4dip"
android:paddingRight="4dip">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="5dip"
android:orientation="vertical">
<!-- icon -->
<ImageView android:id="@+id/icon"
android:layout_width="48dip"
android:layout_height="48dip"
android:gravity="center"
android:scaleType="fitCenter"/>
<!-- free -->
<TextView android:id="@+id/free"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="10sp"
android:textColor="#00ff00"
android:text="@string/AOA_free" />
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="100"
android:paddingLeft="8dip"
android:paddingRight="3dip"
android:orientation="vertical">
<!-- add-on name -->
<TextView android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_toRightOf="@id/icon"
android:singleLine="true"
style="@style/TextAppearance.TAd_ItemTitle"/>
<!-- description -->
<TextView android:id="@+id/description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/title"
android:layout_toRightOf="@id/icon"
android:singleLine="false"
android:textSize="11sp"
style="@style/TextAppearance.TAd_ItemDetails"/>
</LinearLayout>
<!-- buttons -->
<ImageButton android:id="@+id/button_web"
android:layout_width="48dip"
android:layout_height="48dip"
android:scaleType="fitCenter"
android:layout_gravity="center" />
<ImageButton android:id="@+id/button_market"
android:layout_width="48dip"
android:layout_height="48dip"
android:scaleType="fitCenter"
android:layout_gravity="center" />
<ImageView android:id="@+id/check"
android:layout_width="48dip"
android:layout_height="48dip"
android:scaleType="center"
android:layout_gravity="center"
android:src="@android:drawable/checkbox_on_background"/>
</LinearLayout>

@ -1,84 +1,87 @@
<?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 ========================= -->
<!-- task detail showing Producteev dashboard information -->
<string name="producteev_TLA_dashboard">Workspace: %s</string>
<!-- ==================================================== Preferences == -->
<!-- Preferences Title: Producteev -->
<string name="producteev_PPr_header">Producteev</string>
<!-- dashboard title for producteev default dashboard -->
<string name="producteev_default_dashboard">Default Workspace</string>
<!-- dashboard title for tasks that are not synchronized -->
<string name="producteev_no_dashboard">Do Not Synchronize</string>
<!-- preference title for default dashboard -->
<string name="producteev_PPr_defaultdash_title">Default Workspace</string>
<!-- preference description for default dashboard (%s -> setting) -->
<string name="producteev_PPr_defaultdash_summary">New tasks will be added to: %s</string>
<!-- preference description for default dashboard (when set to 'not synchronized') -->
<string name="producteev_PPr_defaultdash_summary_none">New tasks will not be synchronized by default</string>
<!-- ================================================= Login Activity == -->
<!-- Activity Title: Producteev Login -->
<string name="producteev_PLA_title">Log In to Producteev</string>
<!-- Instructions: Producteev login -->
<string name="producteev_PLA_body">Sign in with your existing
Producteev account, or create a new account!</string>
<!-- Producteev Terms Link -->
<string name="producteev_PLA_terms">Terms &amp; Conditions</string>
<!-- Sign In Button -->
<string name="producteev_PLA_signIn">Sign In</string>
<!-- Create New User Button -->
<string name="producteev_PLA_createNew">Create New User</string>
<!-- E-mail Address Label -->
<string name="producteev_PLA_email">E-mail</string>
<!-- Password Label -->
<string name="producteev_PLA_password">Password</string>
<!-- Confirm Password Label -->
<string name="producteev_PLA_confirmPassword">Confirm Password</string>
<!-- First Name Label -->
<string name="producteev_PLA_firstName">First Name</string>
<!-- Last Name Label -->
<string name="producteev_PLA_lastName">Last Name</string>
<!-- Error Message when fields aren't filled out -->
<string name="producteev_PLA_errorEmpty">Error: fill out all fields!</string>
<!-- Error Message when passwords don't match -->
<string name="producteev_PLA_errorMatch">Error: passwords don\'t match!</string>
<!-- ================================================ Synchronization == -->
<!-- title for notification tray when synchronizing -->
<string name="producteev_notification_title">Astrid: Producteev</string>
<!-- Error msg when io exception -->
<string name="producteev_ioerror">Connection Error! Check your Internet connection.</string>
<!-- Prod Login email not specified-->
<string name="producteev_MLA_email_empty">E-Mail was not specified!</string>
<!-- Prod Login password not specified-->
<string name="producteev_MLA_password_empty">Password was not specified!</string>
</resources>
<?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 ========================= -->
<!-- task detail showing Producteev dashboard information (%s => workspace name) -->
<string name="producteev_TLA_dashboard">Workspace: %s</string>
<!-- task detail showing Producteev responsible information (%s => responsible user) -->
<string name="producteev_TLA_responsible">Responsible: %s</string>
<!-- ==================================================== Preferences == -->
<!-- Preferences Title: Producteev -->
<string name="producteev_PPr_header">Producteev</string>
<!-- dashboard title for producteev default dashboard -->
<string name="producteev_default_dashboard">Default Workspace</string>
<!-- dashboard title for tasks that are not synchronized -->
<string name="producteev_no_dashboard">Do Not Synchronize</string>
<!-- preference title for default dashboard -->
<string name="producteev_PPr_defaultdash_title">Default Workspace</string>
<!-- preference description for default dashboard (%s -> setting) -->
<string name="producteev_PPr_defaultdash_summary">New tasks will be added to: %s</string>
<!-- preference description for default dashboard (when set to 'not synchronized') -->
<string name="producteev_PPr_defaultdash_summary_none">New tasks will not be synchronized by default</string>
<!-- ================================================= Login Activity == -->
<!-- Activity Title: Producteev Login -->
<string name="producteev_PLA_title">Log In to Producteev</string>
<!-- Instructions: Producteev login -->
<string name="producteev_PLA_body">Sign in with your existing
Producteev account, or create a new account!</string>
<!-- Producteev Terms Link -->
<string name="producteev_PLA_terms">Terms &amp; Conditions</string>
<!-- Sign In Button -->
<string name="producteev_PLA_signIn">Sign In</string>
<!-- Create New User Button -->
<string name="producteev_PLA_createNew">Create New User</string>
<!-- E-mail Address Label -->
<string name="producteev_PLA_email">E-mail</string>
<!-- Password Label -->
<string name="producteev_PLA_password">Password</string>
<!-- Confirm Password Label -->
<string name="producteev_PLA_confirmPassword">Confirm Password</string>
<!-- First Name Label -->
<string name="producteev_PLA_firstName">First Name</string>
<!-- Last Name Label -->
<string name="producteev_PLA_lastName">Last Name</string>
<!-- Error Message when fields aren't filled out -->
<string name="producteev_PLA_errorEmpty">Error: fill out all fields!</string>
<!-- Error Message when passwords don't match -->
<string name="producteev_PLA_errorMatch">Error: passwords don\'t match!</string>
<!-- ================================================ Synchronization == -->
<!-- title for notification tray when synchronizing -->
<string name="producteev_notification_title">Astrid: Producteev</string>
<!-- Error msg when io exception -->
<string name="producteev_ioerror">Connection Error! Check your Internet connection.</string>
<!-- Prod Login email not specified-->
<string name="producteev_MLA_email_empty">E-Mail was not specified!</string>
<!-- Prod Login password not specified-->
<string name="producteev_MLA_password_empty">Password was not specified!</string>
</resources>

@ -19,8 +19,8 @@
*/
package com.todoroo.astrid.activity;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
@ -37,6 +37,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.os.Bundle;
import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
@ -735,8 +736,9 @@ public final class TaskEditActivity extends TabActivity {
Task.URGENCY_TODAY);
urgencyValues[2] = new UrgencyValue(labels[2],
Task.URGENCY_TOMORROW);
String dayAfterTomorrow = new SimpleDateFormat("EEEE").format( //$NON-NLS-1$
new Date(DateUtilities.now() + 2 * DateUtilities.ONE_DAY));
String dayAfterTomorrow = DateUtils.getDayOfWeekString(
new Date(DateUtilities.now() + 2 * DateUtilities.ONE_DAY).getDay() +
Calendar.SUNDAY, 0);
urgencyValues[3] = new UrgencyValue(dayAfterTomorrow,
Task.URGENCY_DAY_AFTER);
urgencyValues[4] = new UrgencyValue(labels[4],

@ -354,13 +354,13 @@ public class TaskListActivity extends ListActivity implements OnScrollListener,
ServiceConnection refreshConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
System.err.println("connected to service " + name);
System.err.println("connected to service " + name); //$NON-NLS-1$
//
}
@Override
public void onServiceDisconnected(ComponentName name) {
// service disconnected, let's refresh
System.err.println("your junk was done, refreshing");
System.err.println("your junk was done, refreshing"); //$NON-NLS-1$
loadTaskListContent(true);
}
};

@ -6,9 +6,11 @@
package com.todoroo.astrid.dao;
import com.todoroo.andlib.data.AbstractDatabase;
import com.todoroo.andlib.data.AbstractModel;
import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.Table;
import com.todoroo.astrid.model.Metadata;
import com.todoroo.astrid.model.StoreObject;
import com.todoroo.astrid.model.Task;
/**
@ -26,7 +28,7 @@ public class Database extends AbstractDatabase {
* Database version number. This variable must be updated when database
* tables are updated, as it determines whether a database needs updating.
*/
public static final int VERSION = 3;
public static final int VERSION = 4;
/**
* Database name (must be unique)
@ -40,6 +42,7 @@ public class Database extends AbstractDatabase {
public static final Table[] TABLES = new Table[] {
Task.TABLE,
Metadata.TABLE,
StoreObject.TABLE,
};
// --- implementation
@ -70,24 +73,52 @@ public class Database extends AbstractDatabase {
append(Metadata.TASK.name).
append(')');
database.execSQL(sql.toString());
sql.setLength(0);
sql.append("CREATE INDEX IF NOT EXISTS so_id ON ").
append(StoreObject.TABLE).append('(').
append(StoreObject.TYPE).append(',').
append(StoreObject.ITEM).
append(')');
database.execSQL(sql.toString());
sql.setLength(0);
}
@Override
@edu.umd.cs.findbugs.annotations.SuppressWarnings(value="SF_SWITCH_FALLTHROUGH")
protected synchronized boolean onUpgrade(int oldVersion, int newVersion) {
SqlConstructorVisitor visitor = new SqlConstructorVisitor();
switch(oldVersion) {
case 1: {
SqlConstructorVisitor visitor = new SqlConstructorVisitor();
database.execSQL("ALTER TABLE " + Task.TABLE.name + " ADD " +
Task.RECURRENCE.accept(visitor, null));
}
case 2: {
SqlConstructorVisitor visitor = new SqlConstructorVisitor();
for(Property<?> property : new Property<?>[] { Metadata.VALUE2,
Metadata.VALUE3, Metadata.VALUE4, Metadata.VALUE5 })
database.execSQL("ALTER TABLE " + Metadata.TABLE.name + " ADD " +
property.accept(visitor, null));
}
case 3: {
StringBuilder sql = new StringBuilder();
sql.append("CREATE TABLE IF NOT EXISTS ").append(StoreObject.TABLE.name).append('(').
append(AbstractModel.ID_PROPERTY).append(" INTEGER PRIMARY KEY AUTOINCREMENT");
for(Property<?> property : StoreObject.PROPERTIES) {
if(AbstractModel.ID_PROPERTY.name.equals(property.name))
continue;
sql.append(',').append(property.accept(visitor, null));
}
sql.append(')');
database.execSQL(sql.toString());
sql.setLength(0);
sql.append("CREATE INDEX IF NOT EXISTS so_id ON ").
append(StoreObject.TABLE).append('(').
append(StoreObject.TYPE).append(',').
append(StoreObject.ITEM).
append(')');
database.execSQL(sql.toString());
}
return true;
}

@ -0,0 +1,52 @@
/*
* Copyright (c) 2009, Todoroo Inc
* All Rights Reserved
* http://www.todoroo.com
*/
package com.todoroo.astrid.dao;
import com.todoroo.andlib.data.GenericDao;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.astrid.model.StoreObject;
/**
* Data Access layer for {@link StoreObject}-related operations.
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class StoreObjectDao extends GenericDao<StoreObject> {
@Autowired
private Database database;
@edu.umd.cs.findbugs.annotations.SuppressWarnings(value="UR_UNINIT_READ")
public StoreObjectDao() {
super(StoreObject.class);
DependencyInjectionService.getInstance().inject(this);
setDatabase(database);
}
// --- SQL clause generators
/**
* Generates SQL clauses
*/
public static class StoreObjectCriteria {
/** Returns all store objects with given type */
public static Criterion byType(String type) {
return StoreObject.TYPE.eq(type);
}
/** Returns store object with type and key */
public static Criterion byTypeAndItem(String type, String item) {
return Criterion.and(byType(type), StoreObject.ITEM.eq(item));
}
}
}

@ -8,10 +8,10 @@ import android.content.ContentValues;
import com.todoroo.andlib.data.AbstractModel;
import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.Table;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.andlib.data.Property.StringProperty;
import com.todoroo.andlib.data.Table;
import com.todoroo.andlib.data.TodorooCursor;
/**
* Data Model which represents a piece of metadata associated with a task
@ -48,15 +48,15 @@ public class Metadata extends AbstractModel {
public static final StringProperty VALUE2 = new StringProperty(
TABLE, "value2");
/** Metadata Text Value Column 1 */
/** Metadata Text Value Column 3 */
public static final StringProperty VALUE3 = new StringProperty(
TABLE, "value3");
/** Metadata Text Value Column 1 */
/** Metadata Text Value Column 4 */
public static final StringProperty VALUE4 = new StringProperty(
TABLE, "value4");
/** Metadata Text Value Column 1 */
/** Metadata Text Value Column 5 */
public static final StringProperty VALUE5 = new StringProperty(
TABLE, "value5");
@ -95,7 +95,7 @@ public class Metadata extends AbstractModel {
// --- parcelable helpers
private static final Creator<Task> CREATOR = new ModelCreator<Task>(Task.class);
private static final Creator<Metadata> CREATOR = new ModelCreator<Metadata>(Metadata.class);
@Override
protected Creator<? extends AbstractModel> getCreator() {

@ -0,0 +1,105 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.model;
import android.content.ContentValues;
import com.todoroo.andlib.data.AbstractModel;
import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.andlib.data.Property.StringProperty;
import com.todoroo.andlib.data.Table;
import com.todoroo.andlib.data.TodorooCursor;
/**
* Data Model which represents a piece of data unrelated to a task
*
* @author Tim Su <tim@todoroo.com>
*
*/
@SuppressWarnings("nls")
public class StoreObject extends AbstractModel {
// --- table
public static final Table TABLE = new Table("store", StoreObject.class);
// --- properties
/** ID */
public static final LongProperty ID = new LongProperty(
TABLE, ID_PROPERTY_NAME);
/** Store Type Key */
public static final StringProperty TYPE = new StringProperty(
TABLE, "type");
/** Store Item Key */
public static final StringProperty ITEM= new StringProperty(
TABLE, "item");
/** Store Value Column 1 */
public static final StringProperty VALUE1 = new StringProperty(
TABLE, "value");
/** Store Value Column 2 */
public static final StringProperty VALUE2 = new StringProperty(
TABLE, "value2");
/** Store Value Column 3 */
public static final StringProperty VALUE3 = new StringProperty(
TABLE, "value3");
/** Store Value Column 4 */
public static final StringProperty VALUE4 = new StringProperty(
TABLE, "value4");
/** Store Value Column 5 */
public static final StringProperty VALUE5 = new StringProperty(
TABLE, "value5");
/** List of all properties for this model */
public static final Property<?>[] PROPERTIES = generateProperties(StoreObject.class);
// --- defaults
/** Default values container */
private static final ContentValues defaultValues = new ContentValues();
@Override
public ContentValues getDefaultValues() {
return defaultValues;
}
// --- data access boilerplate
public StoreObject() {
super();
}
public StoreObject(TodorooCursor<StoreObject> cursor) {
this();
readPropertiesFromCursor(cursor);
}
public void readFromCursor(TodorooCursor<StoreObject> cursor) {
super.readPropertiesFromCursor(cursor);
}
@Override
public long getId() {
return getIdHelper(ID);
};
// --- parcelable helpers
private static final Creator<StoreObject> CREATOR = new ModelCreator<StoreObject>(StoreObject.class);
@Override
protected Creator<? extends AbstractModel> getCreator() {
return CREATOR;
}
}

@ -17,6 +17,7 @@ import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.andlib.utility.DialogUtilities;
import com.todoroo.astrid.dao.Database;
import com.todoroo.astrid.dao.MetadataDao;
import com.todoroo.astrid.dao.StoreObjectDao;
import com.todoroo.astrid.dao.TaskDao;
/**
@ -97,6 +98,7 @@ public class AstridDependencyInjector implements AbstractDependencyInjector {
injectables.put("database", Database.class);
injectables.put("taskDao", TaskDao.class);
injectables.put("metadataDao", MetadataDao.class);
injectables.put("storeObjectDao", StoreObjectDao.class);
// com.todoroo.astrid.service
injectables.put("taskService", TaskService.class);

Loading…
Cancel
Save