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

@ -1,9 +1,8 @@
package com.todoroo.astrid.producteev; package com.todoroo.astrid.producteev;
import android.content.SharedPreferences.Editor;
import com.timsu.astrid.R; import com.timsu.astrid.R;
import com.todoroo.astrid.common.SyncProviderUtilities; import com.todoroo.astrid.common.SyncProviderUtilities;
import com.todoroo.astrid.utility.Preferences;
/** /**
* Displays synchronization preferences and an action panel so users can * Displays synchronization preferences and an action panel so users can
@ -37,22 +36,33 @@ public class ProducteevUtilities extends SyncProviderUtilities {
// --- producteev-specific preferences // --- 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 static final String PREF_EMAIL = IDENTIFIER + "_email"; //$NON-NLS-1$
public String getLastServerSync() {
return getPrefs().getString(getIdentifier() + PREF_SERVER_LAST_SYNC, null); 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) { * Gets default dashboard from setting
Editor editor = getPrefs().edit(); * @return DASHBOARD_NO_SYNC if should not sync, otherwise remote id
editor.putString(getIdentifier() + PREF_SERVER_LAST_SYNC, value); */
editor.commit(); 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() { private ProducteevUtilities() {
// // prevent instantiation
} }
} }

@ -96,6 +96,22 @@ public class ProducteevInvoker {
"fbuid", fbUid); "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 // --- tasks
/** /**
@ -212,6 +228,21 @@ public class ProducteevInvoker {
"deadline", deadline); "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 * delete a task
* *

@ -1,86 +1,30 @@
/** package com.todoroo.astrid.producteev.sync;
* See the file "LICENSE" for the full license governing this code.
*/ import com.todoroo.andlib.data.Property.LongProperty;
package com.todoroo.astrid.producteev.sync; import com.todoroo.andlib.data.Property.StringProperty;
import com.todoroo.astrid.model.StoreObject;
import android.content.ContentValues; /**
* {@link StoreObject} entries for a Producteev Dashboard
import com.todoroo.andlib.data.AbstractModel; *
import com.todoroo.andlib.data.Property; * @author Tim Su <tim@todoroo.com>
import com.todoroo.andlib.data.Table; *
import com.todoroo.andlib.data.TodorooCursor; */
import com.todoroo.andlib.data.Property.LongProperty; public class ProducteevDashboard {
import com.todoroo.andlib.data.Property.StringProperty;
import com.todoroo.astrid.model.Task; /** type*/
public static final String TYPE = "pdv-dash"; //$NON-NLS-1$
/**
* Data Model which represents a dashboard in Producteev /** dashboard id in producteev */
* public static final LongProperty REMOTE_ID = new LongProperty(StoreObject.TABLE,
* @author Tim Su <tim@todoroo.com> StoreObject.ITEM.name);
*
*/ /** dashboard name */
@SuppressWarnings("nls") public static final StringProperty NAME = new StringProperty(StoreObject.TABLE,
public class ProducteevDashboard extends AbstractModel { StoreObject.VALUE1.name);
// --- table /** users (list in the format "id_user,name;id_user,name;") */
public static final StringProperty USERS = new StringProperty(StoreObject.TABLE,
public static final Table TABLE = new Table("dashboards", ProducteevDashboard.class); StoreObject.VALUE2.name);
// --- 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;
}
}

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

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

@ -1,84 +1,87 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- See the file "LICENSE" for the full license governing this code. --> <!-- See the file "LICENSE" for the full license governing this code. -->
<resources xmlns:android="http://schemas.android.com/apk/res/android"> <resources xmlns:android="http://schemas.android.com/apk/res/android">
<!-- ====================== Plugin Boilerplate ========================= --> <!-- ====================== Plugin Boilerplate ========================= -->
<!-- task detail showing Producteev dashboard information --> <!-- task detail showing Producteev dashboard information (%s => workspace name) -->
<string name="producteev_TLA_dashboard">Workspace: %s</string> <string name="producteev_TLA_dashboard">Workspace: %s</string>
<!-- ==================================================== Preferences == --> <!-- task detail showing Producteev responsible information (%s => responsible user) -->
<string name="producteev_TLA_responsible">Responsible: %s</string>
<!-- Preferences Title: Producteev -->
<string name="producteev_PPr_header">Producteev</string> <!-- ==================================================== Preferences == -->
<!-- dashboard title for producteev default dashboard --> <!-- Preferences Title: Producteev -->
<string name="producteev_default_dashboard">Default Workspace</string> <string name="producteev_PPr_header">Producteev</string>
<!-- dashboard title for tasks that are not synchronized --> <!-- dashboard title for producteev default dashboard -->
<string name="producteev_no_dashboard">Do Not Synchronize</string> <string name="producteev_default_dashboard">Default Workspace</string>
<!-- preference title for default dashboard --> <!-- dashboard title for tasks that are not synchronized -->
<string name="producteev_PPr_defaultdash_title">Default Workspace</string> <string name="producteev_no_dashboard">Do Not Synchronize</string>
<!-- preference description for default dashboard (%s -> setting) --> <!-- preference title for default dashboard -->
<string name="producteev_PPr_defaultdash_summary">New tasks will be added to: %s</string> <string name="producteev_PPr_defaultdash_title">Default Workspace</string>
<!-- preference description for default dashboard (when set to 'not synchronized') --> <!-- preference description for default dashboard (%s -> setting) -->
<string name="producteev_PPr_defaultdash_summary_none">New tasks will not be synchronized by default</string> <string name="producteev_PPr_defaultdash_summary">New tasks will be added to: %s</string>
<!-- ================================================= Login Activity == --> <!-- 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>
<!-- Activity Title: Producteev Login -->
<string name="producteev_PLA_title">Log In to Producteev</string> <!-- ================================================= Login Activity == -->
<!-- Instructions: Producteev login --> <!-- Activity Title: Producteev Login -->
<string name="producteev_PLA_body">Sign in with your existing <string name="producteev_PLA_title">Log In to Producteev</string>
Producteev account, or create a new account!</string>
<!-- Instructions: Producteev login -->
<!-- Producteev Terms Link --> <string name="producteev_PLA_body">Sign in with your existing
<string name="producteev_PLA_terms">Terms &amp; Conditions</string> Producteev account, or create a new account!</string>
<!-- Sign In Button --> <!-- Producteev Terms Link -->
<string name="producteev_PLA_signIn">Sign In</string> <string name="producteev_PLA_terms">Terms &amp; Conditions</string>
<!-- Create New User Button --> <!-- Sign In Button -->
<string name="producteev_PLA_createNew">Create New User</string> <string name="producteev_PLA_signIn">Sign In</string>
<!-- E-mail Address Label --> <!-- Create New User Button -->
<string name="producteev_PLA_email">E-mail</string> <string name="producteev_PLA_createNew">Create New User</string>
<!-- Password Label --> <!-- E-mail Address Label -->
<string name="producteev_PLA_password">Password</string> <string name="producteev_PLA_email">E-mail</string>
<!-- Confirm Password Label --> <!-- Password Label -->
<string name="producteev_PLA_confirmPassword">Confirm Password</string> <string name="producteev_PLA_password">Password</string>
<!-- First Name Label --> <!-- Confirm Password Label -->
<string name="producteev_PLA_firstName">First Name</string> <string name="producteev_PLA_confirmPassword">Confirm Password</string>
<!-- Last Name Label --> <!-- First Name Label -->
<string name="producteev_PLA_lastName">Last Name</string> <string name="producteev_PLA_firstName">First Name</string>
<!-- Error Message when fields aren't filled out --> <!-- Last Name Label -->
<string name="producteev_PLA_errorEmpty">Error: fill out all fields!</string> <string name="producteev_PLA_lastName">Last Name</string>
<!-- Error Message when passwords don't match --> <!-- Error Message when fields aren't filled out -->
<string name="producteev_PLA_errorMatch">Error: passwords don\'t match!</string> <string name="producteev_PLA_errorEmpty">Error: fill out all fields!</string>
<!-- ================================================ Synchronization == --> <!-- Error Message when passwords don't match -->
<string name="producteev_PLA_errorMatch">Error: passwords don\'t match!</string>
<!-- title for notification tray when synchronizing -->
<string name="producteev_notification_title">Astrid: Producteev</string> <!-- ================================================ Synchronization == -->
<!-- Error msg when io exception --> <!-- title for notification tray when synchronizing -->
<string name="producteev_ioerror">Connection Error! Check your Internet connection.</string> <string name="producteev_notification_title">Astrid: Producteev</string>
<!-- Prod Login email not specified--> <!-- Error msg when io exception -->
<string name="producteev_MLA_email_empty">E-Mail was not specified!</string> <string name="producteev_ioerror">Connection Error! Check your Internet connection.</string>
<!-- Prod Login password not specified--> <!-- Prod Login email not specified-->
<string name="producteev_MLA_password_empty">Password was not specified!</string> <string name="producteev_MLA_email_empty">E-Mail was not specified!</string>
</resources> <!-- 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; package com.todoroo.astrid.activity;
import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -37,6 +37,7 @@ import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.res.Resources; import android.content.res.Resources;
import android.os.Bundle; import android.os.Bundle;
import android.text.format.DateUtils;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
@ -735,8 +736,9 @@ public final class TaskEditActivity extends TabActivity {
Task.URGENCY_TODAY); Task.URGENCY_TODAY);
urgencyValues[2] = new UrgencyValue(labels[2], urgencyValues[2] = new UrgencyValue(labels[2],
Task.URGENCY_TOMORROW); Task.URGENCY_TOMORROW);
String dayAfterTomorrow = new SimpleDateFormat("EEEE").format( //$NON-NLS-1$ String dayAfterTomorrow = DateUtils.getDayOfWeekString(
new Date(DateUtilities.now() + 2 * DateUtilities.ONE_DAY)); new Date(DateUtilities.now() + 2 * DateUtilities.ONE_DAY).getDay() +
Calendar.SUNDAY, 0);
urgencyValues[3] = new UrgencyValue(dayAfterTomorrow, urgencyValues[3] = new UrgencyValue(dayAfterTomorrow,
Task.URGENCY_DAY_AFTER); Task.URGENCY_DAY_AFTER);
urgencyValues[4] = new UrgencyValue(labels[4], urgencyValues[4] = new UrgencyValue(labels[4],

@ -354,13 +354,13 @@ public class TaskListActivity extends ListActivity implements OnScrollListener,
ServiceConnection refreshConnection = new ServiceConnection() { ServiceConnection refreshConnection = new ServiceConnection() {
@Override @Override
public void onServiceConnected(ComponentName name, IBinder service) { 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 @Override
public void onServiceDisconnected(ComponentName name) { public void onServiceDisconnected(ComponentName name) {
// service disconnected, let's refresh // 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); loadTaskListContent(true);
} }
}; };

@ -6,9 +6,11 @@
package com.todoroo.astrid.dao; package com.todoroo.astrid.dao;
import com.todoroo.andlib.data.AbstractDatabase; import com.todoroo.andlib.data.AbstractDatabase;
import com.todoroo.andlib.data.AbstractModel;
import com.todoroo.andlib.data.Property; import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.Table; import com.todoroo.andlib.data.Table;
import com.todoroo.astrid.model.Metadata; import com.todoroo.astrid.model.Metadata;
import com.todoroo.astrid.model.StoreObject;
import com.todoroo.astrid.model.Task; 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 * Database version number. This variable must be updated when database
* tables are updated, as it determines whether a database needs updating. * 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) * Database name (must be unique)
@ -40,6 +42,7 @@ public class Database extends AbstractDatabase {
public static final Table[] TABLES = new Table[] { public static final Table[] TABLES = new Table[] {
Task.TABLE, Task.TABLE,
Metadata.TABLE, Metadata.TABLE,
StoreObject.TABLE,
}; };
// --- implementation // --- implementation
@ -70,24 +73,52 @@ public class Database extends AbstractDatabase {
append(Metadata.TASK.name). append(Metadata.TASK.name).
append(')'); append(')');
database.execSQL(sql.toString()); 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 @Override
@edu.umd.cs.findbugs.annotations.SuppressWarnings(value="SF_SWITCH_FALLTHROUGH") @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="SF_SWITCH_FALLTHROUGH")
protected synchronized boolean onUpgrade(int oldVersion, int newVersion) { protected synchronized boolean onUpgrade(int oldVersion, int newVersion) {
SqlConstructorVisitor visitor = new SqlConstructorVisitor();
switch(oldVersion) { switch(oldVersion) {
case 1: { case 1: {
SqlConstructorVisitor visitor = new SqlConstructorVisitor();
database.execSQL("ALTER TABLE " + Task.TABLE.name + " ADD " + database.execSQL("ALTER TABLE " + Task.TABLE.name + " ADD " +
Task.RECURRENCE.accept(visitor, null)); Task.RECURRENCE.accept(visitor, null));
} }
case 2: { case 2: {
SqlConstructorVisitor visitor = new SqlConstructorVisitor();
for(Property<?> property : new Property<?>[] { Metadata.VALUE2, for(Property<?> property : new Property<?>[] { Metadata.VALUE2,
Metadata.VALUE3, Metadata.VALUE4, Metadata.VALUE5 }) Metadata.VALUE3, Metadata.VALUE4, Metadata.VALUE5 })
database.execSQL("ALTER TABLE " + Metadata.TABLE.name + " ADD " + database.execSQL("ALTER TABLE " + Metadata.TABLE.name + " ADD " +
property.accept(visitor, null)); 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; 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.AbstractModel;
import com.todoroo.andlib.data.Property; 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.LongProperty;
import com.todoroo.andlib.data.Property.StringProperty; 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 * 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( public static final StringProperty VALUE2 = new StringProperty(
TABLE, "value2"); TABLE, "value2");
/** Metadata Text Value Column 1 */ /** Metadata Text Value Column 3 */
public static final StringProperty VALUE3 = new StringProperty( public static final StringProperty VALUE3 = new StringProperty(
TABLE, "value3"); TABLE, "value3");
/** Metadata Text Value Column 1 */ /** Metadata Text Value Column 4 */
public static final StringProperty VALUE4 = new StringProperty( public static final StringProperty VALUE4 = new StringProperty(
TABLE, "value4"); TABLE, "value4");
/** Metadata Text Value Column 1 */ /** Metadata Text Value Column 5 */
public static final StringProperty VALUE5 = new StringProperty( public static final StringProperty VALUE5 = new StringProperty(
TABLE, "value5"); TABLE, "value5");
@ -95,7 +95,7 @@ public class Metadata extends AbstractModel {
// --- parcelable helpers // --- 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 @Override
protected Creator<? extends AbstractModel> getCreator() { 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.andlib.utility.DialogUtilities;
import com.todoroo.astrid.dao.Database; import com.todoroo.astrid.dao.Database;
import com.todoroo.astrid.dao.MetadataDao; import com.todoroo.astrid.dao.MetadataDao;
import com.todoroo.astrid.dao.StoreObjectDao;
import com.todoroo.astrid.dao.TaskDao; import com.todoroo.astrid.dao.TaskDao;
/** /**
@ -97,6 +98,7 @@ public class AstridDependencyInjector implements AbstractDependencyInjector {
injectables.put("database", Database.class); injectables.put("database", Database.class);
injectables.put("taskDao", TaskDao.class); injectables.put("taskDao", TaskDao.class);
injectables.put("metadataDao", MetadataDao.class); injectables.put("metadataDao", MetadataDao.class);
injectables.put("storeObjectDao", StoreObjectDao.class);
// com.todoroo.astrid.service // com.todoroo.astrid.service
injectables.put("taskService", TaskService.class); injectables.put("taskService", TaskService.class);

Loading…
Cancel
Save