Merge branch 'master' into dev

Conflicts:
	astrid/AndroidManifest.xml
	astrid/plugin-src/com/todoroo/astrid/producteev/sync/ProducteevDataService.java
	astrid/plugin-src/com/todoroo/astrid/rmilk/MilkBackgroundService.java
	astrid/plugin-src/com/todoroo/astrid/rmilk/MilkLoginActivity.java
	astrid/plugin-src/com/todoroo/astrid/rmilk/data/MilkDataService.java
	astrid/plugin-src/com/todoroo/astrid/rmilk/sync/RTMSyncProvider.java
	astrid/src/com/todoroo/astrid/service/StartupService.java
pull/14/head
Tim Su 16 years ago
commit da7f87a4e0

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.timsu.astrid"
android:versionName="3.2.2 (build your own filters, easy sorting, customizable widget, ui improvements)"
android:versionCode="149">
android:versionName="3.2.5 (build your own filters, easy sorting, customizable widget, ui improvements)"
android:versionCode="152">
<!-- widgets, alarms, and services will break if Astrid is installed on SD card -->
<!-- android:installLocation="internalOnly"> -->
@ -343,6 +343,12 @@
</intent-filter>
</activity>
<service android:name="com.todoroo.astrid.producteev.ProducteevBackgroundService"/>
<receiver android:name="com.todoroo.astrid.producteev.ProducteevStartupReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
</application>

@ -15,6 +15,7 @@ import android.util.Log;
import com.timsu.astrid.R;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.core.PluginServices;
import com.todoroo.astrid.utility.Preferences;
@ -92,7 +93,7 @@ public class BackupService extends Service {
if (!Preferences.getBoolean(R.string.backup_BPr_auto_key, true)) {
return;
}
am.setInexactRepeating(AlarmManager.RTC, System.currentTimeMillis() + BACKUP_OFFSET,
am.setInexactRepeating(AlarmManager.RTC, DateUtilities.now() + BACKUP_OFFSET,
BACKUP_INTERVAL, pendingIntent);
}

@ -248,7 +248,7 @@ public class TasksXmlImporter {
currentTask.setId(Task.NO_ID);
// Save the task to the database.
taskService.save(currentTask, false);
taskService.save(currentTask);
importCount++;
}
@ -460,7 +460,7 @@ public class TasksXmlImporter {
}
// Save the task to the database.
taskService.save(task, false);
taskService.save(task);
importCount++;
return task;
}

@ -15,8 +15,8 @@ import android.content.Context;
import android.widget.Toast;
import com.timsu.astrid.R;
import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.service.ExceptionService;
@ -72,9 +72,11 @@ public abstract class SyncProvider<TYPE extends TaskContainer> {
/**
* Create a task on the remote server.
*
* @return task to create
* @param task
* task to create
* @return task created on remote server
*/
abstract protected void create(TYPE task) throws IOException;
abstract protected TYPE create(TYPE task) throws IOException;
/**
* Push variables from given task to the remote server.
@ -231,10 +233,6 @@ public abstract class SyncProvider<TYPE extends TaskContainer> {
remote.task.setId(local.task.getId());
data.remoteUpdated.set(remoteIndex, remote);
// if remote is deleted, undelete it, since we just created
if(remote.task.isDeleted())
remote.task.setValue(Task.DELETION_DATE, 0L);
} else {
create(local);
}

@ -8,11 +8,13 @@ import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
import com.flurry.android.FlurryAgent;
import com.timsu.astrid.R;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.core.PluginServices;
import com.todoroo.astrid.producteev.sync.ProducteevSyncProvider;
import com.todoroo.astrid.utility.Constants;
import com.todoroo.astrid.utility.Preferences;
/**
@ -54,7 +56,9 @@ public class ProducteevBackgroundService extends Service {
return;
PluginServices.getTaskService();
FlurryAgent.onStartSession(context, Constants.FLURRY_KEY);
new ProducteevSyncProvider().synchronize(context);
FlurryAgent.onEndSession(context);
}
// --- alarm management

@ -87,7 +87,7 @@ public class ProducteevControlSet implements TaskEditControlSet {
private void refreshResponsibleSpinner(ArrayList<ProducteevUser> newUsers) {
Metadata metadata = ProducteevDataService.getInstance().getTaskMetadata(myTask.getId());
long responsibleId = -1;
if(metadata.containsNonNullValue(ProducteevTask.RESPONSIBLE_ID))
if(metadata != null && metadata.containsNonNullValue(ProducteevTask.RESPONSIBLE_ID))
responsibleId = metadata.getValue(ProducteevTask.RESPONSIBLE_ID);
refreshResponsibleSpinner(newUsers, responsibleId);
}
@ -188,7 +188,7 @@ public class ProducteevControlSet implements TaskEditControlSet {
else
metadata.setValue(ProducteevTask.RESPONSIBLE_ID, responsibleUser.getId());
if(metadata.getSetValues().size() > 0 ) {
if(metadata.getSetValues().size() > 0) {
metadataService.save(metadata);
task.setValue(Task.MODIFICATION_DATE, DateUtilities.now());
}

@ -0,0 +1,26 @@
/**
* 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.todoroo.andlib.service.ContextManager;
import com.todoroo.astrid.service.AstridDependencyInjector;
public class ProducteevStartupReceiver extends BroadcastReceiver {
static {
AstridDependencyInjector.initialize();
}
@Override
/** Called when device is restarted */
public void onReceive(final Context context, Intent intent) {
ContextManager.setContext(context);
ProducteevBackgroundService.scheduleService();
}
}

@ -2,13 +2,12 @@ package com.todoroo.astrid.producteev.api;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
import java.util.TreeMap;
import org.apache.commons.codec.digest.DigestUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@ -509,8 +508,7 @@ public class ProducteevInvoker {
}
sigBuilder.append(apiSecret);
byte[] digest = MessageDigest.getInstance("MD5").digest(sigBuilder.toString().getBytes("UTF-8"));
String signature = new BigInteger(1, digest).toString(16);
String signature = DigestUtils.md5Hex(sigBuilder.toString());
requestBuilder.append("api_sig").append('=').append(signature);
return requestBuilder.toString();
}

@ -20,7 +20,6 @@ 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;
@ -30,6 +29,7 @@ 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.service.MetadataService;
import com.todoroo.astrid.tags.TagService;
public final class ProducteevDataService {
@ -59,7 +59,7 @@ public final class ProducteevDataService {
private TaskDao taskDao;
@Autowired
private MetadataDao metadataDao;
private MetadataService metadataService;
@Autowired
private StoreObjectDao storeObjectDao;
@ -76,11 +76,11 @@ public final class ProducteevDataService {
// --- task and metadata methods
/**
* Clears RTM metadata information. Used when user logs out of RTM
* Clears metadata information. Used when user logs out of service
*/
public void clearMetadata() {
metadataDao.deleteWhere(Metadata.KEY.eq(ProducteevTask.METADATA_KEY));
metadataDao.deleteWhere(Metadata.KEY.eq(ProducteevNote.METADATA_KEY));
metadataService.deleteWhere(Metadata.KEY.eq(ProducteevTask.METADATA_KEY));
metadataService.deleteWhere(Metadata.KEY.eq(ProducteevNote.METADATA_KEY));
storeObjectDao.deleteWhere(StoreObject.TYPE.eq(ProducteevDashboard.TYPE));
}
@ -139,18 +139,13 @@ public final class ProducteevDataService {
* @param task
*/
public void saveTaskAndMetadata(ProducteevTaskContainer task) {
taskDao.save(task.task, true);
taskDao.save(task.task);
metadataDao.deleteWhere(Criterion.and(MetadataCriteria.byTask(task.task.getId()),
task.metadata.add(task.pdvTask);
metadataService.synchronizeMetadata(task.task.getId(), task.metadata,
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.persist(metadata);
}
MetadataCriteria.withKey(TagService.KEY)));
}
/**
@ -163,7 +158,7 @@ public final class ProducteevDataService {
// read tags, notes, etc
ArrayList<Metadata> metadata = new ArrayList<Metadata>();
TodorooCursor<Metadata> metadataCursor = metadataDao.query(Query.select(Metadata.PROPERTIES).
TodorooCursor<Metadata> metadataCursor = metadataService.query(Query.select(Metadata.PROPERTIES).
where(Criterion.and(MetadataCriteria.byTask(task.getId()),
Criterion.or(MetadataCriteria.withKey(TagService.KEY),
MetadataCriteria.withKey(ProducteevTask.METADATA_KEY),
@ -185,7 +180,7 @@ public final class ProducteevDataService {
* @return null if no metadata found
*/
public Metadata getTaskMetadata(long taskId) {
TodorooCursor<Metadata> cursor = metadataDao.query(Query.select(
TodorooCursor<Metadata> cursor = metadataService.query(Query.select(
Metadata.PROPERTIES).where(
MetadataCriteria.byTaskAndwithKey(taskId, ProducteevTask.METADATA_KEY)));
try {
@ -202,7 +197,7 @@ public final class ProducteevDataService {
* Reads task notes out of a task
*/
public TodorooCursor<Metadata> getTaskNotesCursor(long taskId) {
TodorooCursor<Metadata> cursor = metadataDao.query(Query.select(Metadata.PROPERTIES).
TodorooCursor<Metadata> cursor = metadataService.query(Query.select(Metadata.PROPERTIES).
where(MetadataCriteria.byTaskAndwithKey(taskId, ProducteevNote.METADATA_KEY)));
return cursor;
}
@ -286,4 +281,4 @@ public final class ProducteevDataService {
// clear dashboard cache
dashboards = null;
}
}
}

@ -50,6 +50,7 @@ import com.todoroo.astrid.utility.Preferences;
@SuppressWarnings("nls")
public class ProducteevSyncProvider extends SyncProvider<ProducteevTaskContainer> {
private static final long TASK_ID_UNSYNCED = 1L;
private ProducteevDataService dataService = null;
private ProducteevInvoker invoker = null;
private final ProducteevUtilities preferences = ProducteevUtilities.INSTANCE;
@ -201,8 +202,6 @@ public class ProducteevSyncProvider extends SyncProvider<ProducteevTaskContainer
long userId = user.getLong("id_user");
String lastServerSync = Preferences.getStringValue(ProducteevUtilities.PREF_SERVER_LAST_SYNC);
if(lastServerSync != null)
lastServerSync = lastServerSync.substring(0, lastServerSync.lastIndexOf(' '));
// read dashboards
JSONArray dashboards = invoker.dashboardsShowList(lastServerSync);
@ -298,7 +297,7 @@ public class ProducteevSyncProvider extends SyncProvider<ProducteevTaskContainer
// ----------------------------------------------------------------------
@Override
protected void create(ProducteevTaskContainer local) throws IOException {
protected ProducteevTaskContainer create(ProducteevTaskContainer local) throws IOException {
Task localTask = local.task;
long dashboard = ProducteevUtilities.INSTANCE.getDefaultDashboard();
if(local.pdvTask.containsNonNullValue(ProducteevTask.DASHBOARD_ID))
@ -306,8 +305,8 @@ public class ProducteevSyncProvider extends SyncProvider<ProducteevTaskContainer
if(dashboard == ProducteevUtilities.DASHBOARD_NO_SYNC) {
// set a bogus task id, then return without creating
local.pdvTask.setValue(ProducteevTask.ID, 1L);
return;
local.pdvTask.setValue(ProducteevTask.ID, TASK_ID_UNSYNCED);
return local;
}
JSONObject response = invoker.tasksCreate(localTask.getValue(Task.TITLE),
@ -321,6 +320,7 @@ public class ProducteevSyncProvider extends SyncProvider<ProducteevTaskContainer
}
transferIdentifiers(newRemoteTask, local);
push(local, newRemoteTask);
return newRemoteTask;
}
/** Create a task container for the given RtmTaskSeries
@ -402,14 +402,22 @@ public class ProducteevSyncProvider extends SyncProvider<ProducteevTaskContainer
if(shouldTransmit(local, Task.DELETION_DATE, remote)) {
if(local.task.getValue(Task.DELETION_DATE) > 0)
invoker.tasksDelete(idTask);
else
create(local);
else {
// if we create, we transfer identifiers to old remote
// in case it is used by caller for other purposes
ProducteevTaskContainer newRemote = create(local);
transferIdentifiers(newRemote, remote);
remote = newRemote;
}
}
// dashboard
if(remote != null && idDashboard != remote.pdvTask.getValue(ProducteevTask.DASHBOARD_ID)) {
invoker.tasksSetWorkspace(idTask, idDashboard);
remote = pull(local);
} else if(remote == null && idTask == TASK_ID_UNSYNCED) {
// was un-synced, create remote
remote = create(local);
}
// core properties

@ -58,11 +58,11 @@ public class RepeatTaskCompleteListener extends BroadcastReceiver {
clone.setValue(Task.COMPLETION_DATE, 0L);
clone.setValue(Task.TIMER_START, 0L);
clone.setValue(Task.ELAPSED_SECONDS, 0);
PluginServices.getTaskService().save(clone, false);
PluginServices.getTaskService().save(clone);
// clear recurrence from completed task so it can be re-completed
task.setValue(Task.RECURRENCE, ""); //$NON-NLS-1$
PluginServices.getTaskService().save(task, false);
PluginServices.getTaskService().save(task);
// send a broadcast
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_EVENT_TASK_REPEATED);

@ -111,7 +111,7 @@ public class TagFilterExposer extends BroadcastReceiver {
Filter[] filters = new Filter[sortedTagSet.size()];
int index = 0;
for(Tag tag : sortedTagSet) {
filters[index++] = filterFromTag(context, tag, TaskCriteria.isActive());
filters[index++] = filterFromTag(context, tag, TaskCriteria.activeAndVisible());
}
FilterCategory tagsFilter = new FilterCategory(context.getString(R.string.tag_FEx_by_size), filters);
list[2] = tagsFilter;

@ -52,7 +52,7 @@ public class TimerPlugin extends BroadcastReceiver {
task.getValue(Task.ELAPSED_SECONDS) + newElapsed);
}
}
PluginServices.getTaskService().save(task, true);
PluginServices.getTaskService().save(task);
TimerDecorationExposer.removeFromCache(task.getId());
// transmit new intents

@ -130,7 +130,7 @@ public class EditPreferences extends TodorooPreferences {
for(int i = 0; i < 100; i++) {
task.clear();
task.setValue(Task.TITLE, Integer.toString(i));
taskService.save(task, false);
taskService.save(task);
}
dialogUtilities.okDialog(EditPreferences.this, "done", null);
return false;

@ -26,10 +26,12 @@ import android.content.ContentValues;
import android.content.Intent;
import android.os.Bundle;
import com.timsu.astrid.R;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.sql.QueryTemplate;
import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.model.Task;
/**
* This activity is launched when a user opens up a notification from the
@ -42,6 +44,9 @@ public class ShortcutActivity extends Activity {
// --- constants
/** token for passing a task id through extras for viewing a single task */
public static final String TOKEN_SINGLE_TASK = "id"; //$NON-NLS-1$
/** token for passing a {@link Filter}'s title through extras */
public static final String TOKEN_FILTER_TITLE = "title"; //$NON-NLS-1$
@ -105,13 +110,20 @@ public class ShortcutActivity extends Activity {
}
}
Filter filter = new Filter("", title, new QueryTemplate(), values); //$NON-NLS-1$
filter.sqlQuery = sql;
Filter filter = new Filter("", title, sql, values); //$NON-NLS-1$
Intent taskListIntent = new Intent(this, TaskListActivity.class);
taskListIntent.putExtra(TaskListActivity.TOKEN_FILTER, filter);
startActivity(taskListIntent);
} else if(extras != null && extras.containsKey(TOKEN_SINGLE_TASK)) {
Filter filter = new Filter("", getString(R.string.TLA_custom), //$NON-NLS-1$
new QueryTemplate().where(Task.ID.eq(extras.getLong(TOKEN_SINGLE_TASK, -1))), null);
Intent taskListIntent = new Intent(this, TaskListActivity.class);
taskListIntent.putExtra(TaskListActivity.TOKEN_FILTER, filter);
startActivity(taskListIntent);
}
finish();
}

@ -104,13 +104,18 @@ public final class TaskEditActivity extends TabActivity {
/**
* Task ID
*/
public static final String TOKEN_ID = "i"; //$NON-NLS-1$
public static final String TOKEN_ID = "id"; //$NON-NLS-1$
/**
* Content Values to set
*/
public static final String TOKEN_VALUES = "v"; //$NON-NLS-1$
/**
* Task in progress (during orientation change)
*/
private static final String TASK_IN_PROGRESS = "task_in_progress"; //$NON-NLS-1$
// --- request codes
@SuppressWarnings("unused")
@ -185,6 +190,16 @@ public final class TaskEditActivity extends TabActivity {
// disable keyboard until user requests it
AndroidUtilities.suppressVirtualKeyboard(title);
// if we were editing a task already, restore it
if(savedInstanceState != null && savedInstanceState.containsKey(TASK_IN_PROGRESS)) {
Task task = savedInstanceState.getParcelable(TASK_IN_PROGRESS);
if(task != null) {
model = task;
}
}
setResult(RESULT_OK);
}
/* ======================================================================
@ -304,6 +319,12 @@ public final class TaskEditActivity extends TabActivity {
*/
@SuppressWarnings("nls")
protected void loadItem(Intent intent) {
if(model != null) {
// came from bundle
isNewTask = (model.getValue(Task.TITLE).length() == 0);
return;
}
long idParam = intent.getLongExtra(TOKEN_ID, -1L);
database.openForReading();
@ -323,6 +344,9 @@ public final class TaskEditActivity extends TabActivity {
if(model.getValue(Task.TITLE).length() == 0) {
FlurryAgent.onEvent("create-task");
isNewTask = true;
// set deletion date until task gets a title
model.setValue(Task.DELETION_DATE, DateUtilities.now());
} else {
FlurryAgent.onEvent("edit-task");
}
@ -354,7 +378,10 @@ public final class TaskEditActivity extends TabActivity {
for(TaskEditControlSet controlSet : controls)
controlSet.writeToModel(model);
if(taskService.save(model, false))
if(title.getText().length() > 0)
model.setValue(Task.DELETION_DATE, 0L);
if(taskService.save(model) && title.getText().length() > 0)
showSaveToast();
}
@ -362,11 +389,11 @@ public final class TaskEditActivity extends TabActivity {
public void finish() {
super.finish();
// abandon editing in this case
if(title.getText().length() == 0 && isNewTask) {
// abandon editing and delete the newly created task if
// no title was entered
if(title.getText().length() == 0 && isNewTask && model.isSaved()) {
taskService.delete(model);
showCancelToast();
setResult(RESULT_CANCELED);
}
}
@ -418,10 +445,6 @@ public final class TaskEditActivity extends TabActivity {
* precision
*/
private void showSaveToast() {
// if we have no title, or nothing's changed, don't show toast
if(isNewTask)
return;
int stringResource;
long due = model.getValue(Task.DUE_DATE);
@ -492,7 +515,6 @@ public final class TaskEditActivity extends TabActivity {
Toast.LENGTH_SHORT).show();
}
@Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
switch(item.getItemId()) {
@ -532,10 +554,11 @@ public final class TaskEditActivity extends TabActivity {
@Override
protected void onPause() {
if(shouldSaveState)
save();
super.onPause();
unregisterReceiver(controlReceiver);
if(shouldSaveState)
save();
}
@Override
@ -546,6 +569,14 @@ public final class TaskEditActivity extends TabActivity {
populateFields();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// stick our task into the outState
outState.putParcelable(TASK_IN_PROGRESS, model);
}
@Override
protected void onStart() {
super.onStart();

@ -286,7 +286,7 @@ public class TaskListActivity extends ListActivity implements OnScrollListener,
int importance = event.getNumber() - '1';
Task task = ((ViewHolder)selected.getTag()).task;
task.setValue(Task.IMPORTANCE, importance);
taskService.save(task, false);
taskService.save(task);
taskAdapter.setFieldContentsAndVisibility(selected);
}
// filter
@ -683,7 +683,7 @@ public class TaskListActivity extends ListActivity implements OnScrollListener,
}
task.mergeWith(forTask);
}
taskService.save(task, false);
taskService.save(task);
if(forMetadata != null && forMetadata.size() > 0) {
Metadata metadata = new Metadata();
metadata.setValue(Metadata.TASK, task.getId());
@ -827,7 +827,7 @@ public class TaskListActivity extends ListActivity implements OnScrollListener,
Task task = new Task();
task.setId(itemId);
task.setValue(Task.DELETION_DATE, 0L);
taskService.save(task, false);
taskService.save(task);
loadTaskListContent(true);
return true;
}

@ -146,12 +146,9 @@ public class TaskDao extends GenericDao<Task> {
* exist. Returns true on success.
*
* @param task
* @param skipHooks
* Whether pre and post hooks should run. This should be set
* to true if tasks are created as part of synchronization
* @return true if save occurred, false otherwise (i.e. nothing changed)
*/
public boolean save(Task task, boolean skipHooks) {
public boolean save(Task task) {
boolean saveSuccessful;
ContentValues values = task.getSetValues();

@ -1,11 +1,17 @@
package com.todoroo.astrid.service;
import java.util.ArrayList;
import java.util.HashSet;
import android.content.ContentValues;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Query;
import com.todoroo.astrid.dao.MetadataDao;
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
import com.todoroo.astrid.model.Metadata;
/**
@ -67,4 +73,51 @@ public class MetadataService {
public void save(Metadata metadata) {
metadataDao.persist(metadata);
}
/**
* Synchronize metadata for given task id
* @param id
* @param metadata
* @param metadataKeys
*/
public void synchronizeMetadata(long taskId, ArrayList<Metadata> metadata,
Criterion metadataCriterion) {
HashSet<ContentValues> newMetadataValues = new HashSet<ContentValues>();
for(Metadata metadatum : metadata) {
metadatum.setValue(Metadata.TASK, taskId);
metadatum.clearValue(Metadata.ID);
newMetadataValues.add(metadatum.getMergedValues());
}
Metadata item = new Metadata();
TodorooCursor<Metadata> cursor = metadataDao.query(Query.select(Metadata.PROPERTIES).where(Criterion.and(MetadataCriteria.byTask(taskId),
metadataCriterion)));
try {
// try to find matches within our metadata list
for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
item.readFromCursor(cursor);
long id = item.getId();
// clear item id when matching with incoming values
item.clearValue(Metadata.ID);
ContentValues itemMergedValues = item.getMergedValues();
if(newMetadataValues.contains(itemMergedValues)) {
newMetadataValues.remove(itemMergedValues);
continue;
}
// not matched. cut it
metadataDao.delete(id);
}
} finally {
cursor.close();
}
// everything that remains shall be written
for(ContentValues values : newMetadataValues) {
item.clear();
item.mergeWith(values);
metadataDao.persist(item);
}
}
}

@ -20,7 +20,9 @@ import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.service.ExceptionService;
import com.todoroo.andlib.service.ExceptionService.TodorooUncaughtExceptionHandler;
import com.todoroo.astrid.backup.BackupService;
import com.todoroo.astrid.dao.Database;
import com.todoroo.astrid.producteev.ProducteevBackgroundService;
import com.todoroo.astrid.producteev.ProducteevUtilities;
import com.todoroo.astrid.utility.Constants;
import com.todoroo.astrid.utility.Preferences;
@ -94,6 +96,8 @@ public class StartupService {
Preferences.setCurrentVersion(version);
}
upgradeService.performSecondaryUpgrade(context);
// perform startup activities in a background thread
new Thread(new Runnable() {
public void run() {
@ -115,6 +119,10 @@ public class StartupService {
// if sync ongoing flag was set, clear it
ProducteevUtilities.INSTANCE.stopOngoing();
ProducteevBackgroundService.scheduleService();
BackupService.scheduleService(context);
MilkBackgroundService.scheduleService();
// check for task killers
if(!Constants.OEM)
showTaskKillerHelp(context);

@ -67,7 +67,7 @@ public class TaskService {
item.setValue(Task.COMPLETION_DATE, DateUtilities.now());
else
item.setValue(Task.COMPLETION_DATE, 0L);
taskDao.save(item, false);
taskDao.save(item);
}
/**
@ -78,8 +78,8 @@ public class TaskService {
* Whether pre and post hooks should run. This should be set
* to true if tasks are created as part of synchronization
*/
public boolean save(Task item, boolean runHooks) {
return taskDao.save(item, runHooks);
public boolean save(Task item) {
return taskDao.save(item);
}
/**
@ -122,12 +122,13 @@ public class TaskService {
return;
else if(item.containsValue(Task.TITLE) && item.getValue(Task.TITLE).length() == 0) {
taskDao.delete(item.getId());
item.setId(Task.NO_ID);
} else {
long id = item.getId();
item.clear();
item.setId(id);
item.setValue(Task.DELETION_DATE, DateUtilities.now());
taskDao.save(item, false);
taskDao.save(item);
}
}
@ -224,7 +225,7 @@ public class TaskService {
try {
for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
taskValues.setValue(Task.ID, cursor.get(Task.ID));
taskDao.save(taskValues, false);
taskDao.save(taskValues);
}
return cursor.getCount();
} finally {
@ -233,7 +234,9 @@ public class TaskService {
}
public int countTasks(Filter filter) {
TodorooCursor<Task> cursor = query(Query.select(Task.ID).withQueryTemplate(filter.sqlQuery));
String queryTemplate = PermaSql.replacePlaceholders(filter.sqlQuery);
TodorooCursor<Task> cursor = query(Query.select(Task.ID).withQueryTemplate(
queryTemplate));
try {
return cursor.getCount();
} finally {

@ -11,18 +11,25 @@ import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.utility.DialogUtilities;
import com.todoroo.astrid.activity.TaskListActivity;
import com.todoroo.astrid.dao.Database;
public final class UpgradeService {
private static final int V3_2_4 = 151;
private static final int V3_2_3 = 150;
private static final int V3_1_0 = 146;
private static final int V3_0_6 = 145;
private static final int V3_0_5 = 144;
private static final int V3_0_0 = 136;
private static final int V2_14_4 = 135;
@Autowired
private DialogUtilities dialogUtilities;
@Autowired
private Database database;
public UpgradeService() {
DependencyInjectionService.getInstance().inject(this);
}
@ -102,6 +109,15 @@ public final class UpgradeService {
"If you liked the old version, you can also go back by " +
"<a href='http://bit.ly/oldastrid'>clicking here</a>",
});
if(from > V2_14_4 && from <= V3_2_4)
newVersionString(changeLog, "3.2.5 (8/18/10)", new String[] {
"Fix for duplicated tasks created in RTM",
});
if(from > V2_14_4 && from <= V3_2_3)
newVersionString(changeLog, "3.2.5 (8/18/10)", new String[] {
"Fix for duplicated tasks created in Producteev",
"Fix for being able to create tasks without title",
});
if(from > V2_14_4 && from <= V3_1_0)
newVersionString(changeLog, "3.2.0 (8/16/10)", new String[] {
"Build your own custom filters from the Filter page",
@ -110,7 +126,6 @@ public final class UpgradeService {
"Synchronize with Producteev! (producteev.com)",
"Select tags by drop-down box",
"Cosmetic improvements, calendar & sync bug fixes",
"... enjoy! - we <3 astrid team",
});
if(from > V2_14_4 && from <= V3_0_6)
newVersionString(changeLog, "3.1.0 (8/9/10)", new String[] {
@ -121,7 +136,6 @@ public final class UpgradeService {
"Restored tag hiding when tag begins with underscore (_)",
"FROYO: disabled moving app to SD card, it would break alarms and widget",
"Also gone: a couple force closes, bugs with repeating tasks",
"... enjoy! - we <3 astrid team",
});
if(from > V2_14_4 && from <= V3_0_5)
newVersionString(changeLog, "3.0.6 (8/4/10)", new String[] {
@ -134,7 +148,7 @@ public final class UpgradeService {
if(changeLog.length() == 0)
return;
changeLog.append("</body></html>");
changeLog.append("Enjoy!</body></html>");
String changeLogHtml = "<html><body style='color: white'>" + changeLog;
WebView webView = new WebView(context);
@ -163,6 +177,18 @@ public final class UpgradeService {
changeLog.append("</ul>");
}
// --- database upgrade logic
// --- secondary upgrade
/**
* If primary upgrade doesn't work for some reason (corrupt SharedPreferences,
* for example), this will catch some cases
*/
public void performSecondaryUpgrade(Context context) {
if(!context.getDatabasePath(database.getName()).exists() &&
context.getDatabasePath("tasks").exists()) { //$NON-NLS-1$
new Astrid2To3UpgradeHelper().upgrade2To3(context, 1);
}
}
}

@ -188,7 +188,7 @@ public class TasksWidget extends AppWidgetProvider {
views.setOnClickPendingIntent(R.id.taskbody, pendingIntent);
Intent editIntent = new Intent(context, TaskEditActivity.class);
editIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
editIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
if(filter != null && filter.valuesForNewTasks != null) {
String values = AndroidUtilities.contentValuesToSerializedString(filter.valuesForNewTasks);
editIntent.putExtra(TaskEditActivity.TOKEN_VALUES, values);

@ -32,7 +32,7 @@ public class TaskDaoTests extends DatabaseTestCase {
// create task "happy"
Task task = new Task();
task.setValue(Task.TITLE, "happy");
assertTrue(taskDao.save(task, false));
assertTrue(taskDao.save(task));
cursor = taskDao.query(
Query.select(IDS));
assertEquals(1, cursor.getCount());
@ -45,7 +45,7 @@ public class TaskDaoTests extends DatabaseTestCase {
// create task "sad"
task = new Task();
task.setValue(Task.TITLE, "sad");
assertTrue(taskDao.save(task, false));
assertTrue(taskDao.save(task));
cursor = taskDao.query(
Query.select(IDS));
assertEquals(2, cursor.getCount());
@ -55,7 +55,7 @@ public class TaskDaoTests extends DatabaseTestCase {
long sadId = task.getId();
assertNotSame(Task.NO_ID, sadId);
task.setValue(Task.TITLE, "melancholy");
assertTrue(taskDao.save(task, false));
assertTrue(taskDao.save(task));
cursor = taskDao.query(
Query.select(IDS));
assertEquals(2, cursor.getCount());
@ -75,35 +75,35 @@ public class TaskDaoTests extends DatabaseTestCase {
// create normal task
Task task = new Task();
task.setValue(Task.TITLE, "normal");
assertTrue(taskDao.save(task, false));
assertTrue(taskDao.save(task));
// create blank task
task = new Task();
task.setValue(Task.TITLE, "");
assertTrue(taskDao.save(task, false));
assertTrue(taskDao.save(task));
// create hidden task
task = new Task();
task.setValue(Task.TITLE, "hidden");
task.setValue(Task.HIDE_UNTIL, DateUtilities.now() + 10000);
assertTrue(taskDao.save(task, false));
assertTrue(taskDao.save(task));
// create task with deadlines
task = new Task();
task.setValue(Task.TITLE, "deadlineInFuture");
task.setValue(Task.DUE_DATE, DateUtilities.now() + 10000);
assertTrue(taskDao.save(task, false));
assertTrue(taskDao.save(task));
task = new Task();
task.setValue(Task.TITLE, "deadlineInPast");
task.setValue(Task.DUE_DATE, DateUtilities.now() - 10000);
assertTrue(taskDao.save(task, false));
assertTrue(taskDao.save(task));
// create completed task
task = new Task();
task.setValue(Task.TITLE, "completed");
task.setValue(Task.COMPLETION_DATE, DateUtilities.now() - 10000);
assertTrue(taskDao.save(task, false));
assertTrue(taskDao.save(task));
// check has no name
TodorooCursor<Task> cursor = taskDao.query(
@ -165,7 +165,7 @@ public class TaskDaoTests extends DatabaseTestCase {
// create task "happy"
Task task = new Task();
task.setValue(Task.TITLE, "happy");
assertTrue(taskDao.save(task, false));
assertTrue(taskDao.save(task));
cursor = taskDao.query(
Query.select(IDS));
assertEquals(1, cursor.getCount());
@ -191,7 +191,7 @@ public class TaskDaoTests extends DatabaseTestCase {
task.setValue(Task.TITLE, "happy");
task.setValue(Task.ID, 1L);
assertFalse(taskDao.save(task, false));
assertFalse(taskDao.save(task));
cursor = taskDao.query(
Query.select(IDS));

@ -46,7 +46,7 @@ public class TaskTests extends DatabaseTestCase {
/** Check task gets a creation date at some point */
public void checkCreationDate() {
Task task = new Task();
taskService.save(task, false);
taskService.save(task);
assertTrue(task.getValue(Task.CREATION_DATE) > 0);
}

@ -37,7 +37,7 @@ public class ReminderServiceTests extends DatabaseTestCase {
Task task = new Task();
task.setValue(Task.TITLE, "water");
task.setValue(Task.REMINDER_FLAGS, 0);
taskDao.save(task, false);
taskDao.save(task);
service.scheduleAlarm(task);
}
@ -49,7 +49,7 @@ public class ReminderServiceTests extends DatabaseTestCase {
task.setValue(Task.TITLE, "water");
task.setValue(Task.DUE_DATE, DateUtilities.now() - DateUtilities.ONE_DAY);
task.setValue(Task.REMINDER_FLAGS, Task.NOTIFY_AT_DEADLINE);
taskDao.save(task, false);
taskDao.save(task);
// test due date in the future
task.setValue(Task.DUE_DATE, DateUtilities.now() + DateUtilities.ONE_DAY);
@ -61,7 +61,7 @@ public class ReminderServiceTests extends DatabaseTestCase {
assertEquals(type, ReminderService.TYPE_DUE);
}
});
taskDao.save(task, false);
taskDao.save(task);
assertTrue(((AlarmExpected)service.getScheduler()).alarmCreated);
}
@ -80,7 +80,7 @@ public class ReminderServiceTests extends DatabaseTestCase {
assertEquals(type, ReminderService.TYPE_RANDOM);
}
});
taskDao.save(task, false);
taskDao.save(task);
assertTrue(((AlarmExpected)service.getScheduler()).alarmCreated);
}
@ -92,7 +92,7 @@ public class ReminderServiceTests extends DatabaseTestCase {
task.setValue(Task.TITLE, "water");
task.setValue(Task.DUE_DATE, DateUtilities.now() + DateUtilities.ONE_DAY);
task.setValue(Task.REMINDER_FLAGS, Task.NOTIFY_AFTER_DEADLINE);
taskDao.save(task, false);
taskDao.save(task);
// test due date in the past
task.setValue(Task.DUE_DATE, DateUtilities.now() - DateUtilities.ONE_DAY);
@ -105,7 +105,7 @@ public class ReminderServiceTests extends DatabaseTestCase {
assertEquals(type, ReminderService.TYPE_OVERDUE);
}
});
taskDao.save(task, false);
taskDao.save(task);
assertTrue(((AlarmExpected)service.getScheduler()).alarmCreated);
}
@ -126,7 +126,7 @@ public class ReminderServiceTests extends DatabaseTestCase {
assertEquals(type, ReminderService.TYPE_RANDOM);
}
});
taskDao.save(task, false);
taskDao.save(task);
assertTrue(((AlarmExpected)service.getScheduler()).alarmCreated);
// now set the due date in the past
@ -145,7 +145,7 @@ public class ReminderServiceTests extends DatabaseTestCase {
assertEquals(type, ReminderService.TYPE_DUE);
}
});
taskDao.save(task, false);
taskDao.save(task);
assertTrue(((AlarmExpected)service.getScheduler()).alarmCreated);
}

@ -36,10 +36,10 @@ public class RepeatTests extends DatabaseTestCase {
public void testNoRepeats() throws Exception{
Task task = new Task();
task.setValue(Task.TITLE, "nothing");
taskDao.save(task, false);
taskDao.save(task);
task.setValue(Task.COMPLETION_DATE, DateUtilities.now());
taskDao.save(task, false);
taskDao.save(task);
// wait for repeat handler
Thread.sleep(REPEAT_WAIT);
@ -60,10 +60,10 @@ public class RepeatTests extends DatabaseTestCase {
rrule.setInterval(5);
rrule.setFreq(Frequency.DAILY);
task.setValue(Task.RECURRENCE, rrule.toIcal());
taskDao.save(task, false);
taskDao.save(task);
task.setValue(Task.COMPLETION_DATE, DateUtilities.now());
taskDao.save(task, false);
taskDao.save(task);
// wait for repeat handler
Thread.sleep(REPEAT_WAIT);
@ -102,10 +102,10 @@ public class RepeatTests extends DatabaseTestCase {
task.setValue(Task.RECURRENCE, rrule.toIcal());
long originalDueDate = (DateUtilities.now() - 3 * DateUtilities.ONE_DAY) / 1000L * 1000L;
task.setValue(Task.DUE_DATE, task.createDueDate(Task.URGENCY_SPECIFIC_DAY_TIME, originalDueDate));
taskDao.save(task, false);
taskDao.save(task);
task.setValue(Task.COMPLETION_DATE, DateUtilities.now());
taskDao.save(task, false);
taskDao.save(task);
// wait for repeat handler
Thread.sleep(REPEAT_WAIT);
@ -144,10 +144,10 @@ public class RepeatTests extends DatabaseTestCase {
task.setValue(Task.RECURRENCE, rrule.toIcal());
long originalDueDate = (DateUtilities.now() + DateUtilities.ONE_DAY) / 1000L * 1000L;
task.setValue(Task.DUE_DATE, task.createDueDate(Task.URGENCY_SPECIFIC_DAY_TIME, originalDueDate));
taskDao.save(task, false);
taskDao.save(task);
task.setValue(Task.COMPLETION_DATE, DateUtilities.now());
taskDao.save(task, false);
taskDao.save(task);
// wait for repeat handler
Thread.sleep(2 * REPEAT_WAIT);
@ -187,10 +187,10 @@ public class RepeatTests extends DatabaseTestCase {
long originalDueDate = (DateUtilities.now() - 3 * DateUtilities.ONE_DAY) / 1000L * 1000L;
task.setValue(Task.DUE_DATE, task.createDueDate(Task.URGENCY_SPECIFIC_DAY, originalDueDate));
task.setFlag(Task.FLAGS, Task.FLAG_REPEAT_AFTER_COMPLETION, true);
taskDao.save(task, false);
taskDao.save(task);
task.setValue(Task.COMPLETION_DATE, DateUtilities.now());
taskDao.save(task, false);
taskDao.save(task);
// wait for repeat handler
Thread.sleep(REPEAT_WAIT);
@ -226,7 +226,7 @@ public class RepeatTests extends DatabaseTestCase {
rrule.setInterval(5);
rrule.setFreq(Frequency.DAILY);
task.setValue(Task.RECURRENCE, rrule.toIcal());
taskDao.save(task, false);
taskDao.save(task);
Metadata metadata = new Metadata();
metadata.setValue(Metadata.KEY, "special");
@ -235,7 +235,7 @@ public class RepeatTests extends DatabaseTestCase {
metadataDao.persist(metadata);
task.setValue(Task.COMPLETION_DATE, DateUtilities.now());
taskDao.save(task, false);
taskDao.save(task);
// wait for repeat handler
Thread.sleep(REPEAT_WAIT);
@ -266,10 +266,10 @@ public class RepeatTests extends DatabaseTestCase {
task.setValue(Task.RECURRENCE, rrule.toIcal());
task.setValue(Task.DUE_DATE, task.createDueDate(Task.URGENCY_TODAY, 0));
task.setValue(Task.HIDE_UNTIL, task.createHideUntil(Task.HIDE_UNTIL_DAY_BEFORE, 0));
taskDao.save(task, false);
taskDao.save(task);
task.setValue(Task.COMPLETION_DATE, DateUtilities.now());
taskDao.save(task, false);
taskDao.save(task);
// wait for repeat handler
Thread.sleep(REPEAT_WAIT);

Loading…
Cancel
Save