diff --git a/astrid/AndroidManifest.xml b/astrid/AndroidManifest.xml
index 639091fda..c9673f522 100644
--- a/astrid/AndroidManifest.xml
+++ b/astrid/AndroidManifest.xml
@@ -54,7 +54,7 @@
+ android:label="@string/app_name" android:debuggable="true">
@@ -189,6 +189,12 @@
+
+
+
+
+
+
@@ -202,6 +208,12 @@
+
+
+
+
+
+
@@ -217,6 +229,12 @@
+
+
+
+
+
+
@@ -255,6 +273,12 @@
+
+
+
+
+
+
-
+
+
+
+
+
+
+
@@ -374,6 +404,12 @@
+
+
+
+
+
+
diff --git a/astrid/astrid.launch b/astrid/astrid.launch
index 19f7df00c..89ae0a49d 100644
--- a/astrid/astrid.launch
+++ b/astrid/astrid.launch
@@ -12,7 +12,7 @@
-
+
diff --git a/astrid/common-src/com/todoroo/andlib/data/GenericDao.java b/astrid/common-src/com/todoroo/andlib/data/GenericDao.java
index 7c80b6a4f..da3a59c22 100644
--- a/astrid/common-src/com/todoroo/andlib/data/GenericDao.java
+++ b/astrid/common-src/com/todoroo/andlib/data/GenericDao.java
@@ -196,6 +196,20 @@ public class GenericDao {
AbstractModel.ID_PROPERTY.eq(item.getId()).toString(), null) > 0;
}
+ /**
+ * Updates multiple rows of the database based on model set values
+ *
+ * @param item
+ * item model
+ * @param criterion
+ * @return returns true on success.
+ */
+ public int updateMultiple(ContentValues values, Criterion criterion) {
+ if(values.size() == 0) // nothing changed
+ return 0;
+ return database.update(table.name, values, criterion.toString(), null);
+ }
+
// --- helper methods
diff --git a/astrid/common-src/com/todoroo/andlib/widget/Api4GestureDetector.java b/astrid/common-src/com/todoroo/andlib/widget/Api4GestureDetector.java
index 957f81772..11a110081 100644
--- a/astrid/common-src/com/todoroo/andlib/widget/Api4GestureDetector.java
+++ b/astrid/common-src/com/todoroo/andlib/widget/Api4GestureDetector.java
@@ -34,7 +34,7 @@ public class Api4GestureDetector implements OnGesturePerformedListener {
if (predictions.size() > 0) {
Prediction prediction = predictions.get(0);
// We want at least some confidence in the result
- if (prediction.score > 1.0) {
+ if (prediction.score > 2.0) {
listener.gesturePerformed(prediction.name);
}
}
diff --git a/astrid/plugin-src/com/todoroo/astrid/alarms/AlarmDetailExposer.java b/astrid/plugin-src/com/todoroo/astrid/alarms/AlarmDetailExposer.java
index b8596da3b..4a25599e1 100644
--- a/astrid/plugin-src/com/todoroo/astrid/alarms/AlarmDetailExposer.java
+++ b/astrid/plugin-src/com/todoroo/astrid/alarms/AlarmDetailExposer.java
@@ -12,7 +12,6 @@ import com.timsu.astrid.R;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.api.AstridApiConstants;
-import com.todoroo.astrid.api.DetailExposer;
import com.todoroo.astrid.model.Metadata;
/**
@@ -21,7 +20,7 @@ import com.todoroo.astrid.model.Metadata;
* @author Tim Su
*
*/
-public class AlarmDetailExposer extends BroadcastReceiver implements DetailExposer {
+public class AlarmDetailExposer extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
@@ -44,7 +43,6 @@ public class AlarmDetailExposer extends BroadcastReceiver implements DetailExpos
context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
}
- @Override
public String getTaskDetails(Context context, long id, boolean extended) {
if(extended)
return null;
@@ -71,9 +69,4 @@ public class AlarmDetailExposer extends BroadcastReceiver implements DetailExpos
}
}
- @Override
- public String getPluginIdentifier() {
- return AlarmService.IDENTIFIER;
- }
-
}
diff --git a/astrid/plugin-src/com/todoroo/astrid/common/SyncProvider.java b/astrid/plugin-src/com/todoroo/astrid/common/SyncProvider.java
index a01b8287b..5b27684b7 100644
--- a/astrid/plugin-src/com/todoroo/astrid/common/SyncProvider.java
+++ b/astrid/plugin-src/com/todoroo/astrid/common/SyncProvider.java
@@ -295,6 +295,12 @@ public abstract class SyncProvider {
length = data.remoteUpdated.size();
for(int i = 0; i < length; i++) {
TYPE remote = data.remoteUpdated.get(i);
+
+ // don't synchronize new & deleted / completed tasks
+ if(!remote.task.isSaved() && (remote.task.isDeleted() ||
+ remote.task.isCompleted()))
+ continue;
+
try {
write(remote);
} catch (Exception e) {
diff --git a/astrid/plugin-src/com/todoroo/astrid/notes/NoteDetailExposer.java b/astrid/plugin-src/com/todoroo/astrid/notes/NoteDetailExposer.java
index ba9a879c0..29df48da9 100644
--- a/astrid/plugin-src/com/todoroo/astrid/notes/NoteDetailExposer.java
+++ b/astrid/plugin-src/com/todoroo/astrid/notes/NoteDetailExposer.java
@@ -10,7 +10,6 @@ import android.content.Intent;
import com.timsu.astrid.R;
import com.todoroo.astrid.api.AstridApiConstants;
-import com.todoroo.astrid.api.DetailExposer;
import com.todoroo.astrid.core.PluginServices;
import com.todoroo.astrid.model.Task;
import com.todoroo.astrid.utility.Preferences;
@@ -21,7 +20,7 @@ import com.todoroo.astrid.utility.Preferences;
* @author Tim Su
*
*/
-public class NoteDetailExposer extends BroadcastReceiver implements DetailExposer {
+public class NoteDetailExposer extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
@@ -44,7 +43,6 @@ public class NoteDetailExposer extends BroadcastReceiver implements DetailExpose
context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
}
- @Override
public String getTaskDetails(Context context, long id, boolean extended) {
if(Preferences.getBoolean(R.string.p_showNotes, false)) {
@@ -65,9 +63,4 @@ public class NoteDetailExposer extends BroadcastReceiver implements DetailExpose
return "
" + notes; //$NON-NLS-1$
}
- @Override
- public String getPluginIdentifier() {
- return NotesPlugin.IDENTIFIER;
- }
-
}
diff --git a/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevControlSet.java b/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevControlSet.java
index 29293bc62..26e7feeac 100644
--- a/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevControlSet.java
+++ b/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevControlSet.java
@@ -13,11 +13,11 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
+import android.widget.AdapterView.OnItemSelectedListener;
import com.timsu.astrid.R;
import com.todoroo.andlib.service.Autowired;
@@ -94,10 +94,10 @@ public class ProducteevControlSet implements TaskEditControlSet {
dialog.cancel();
} else {
// create the real dashboard, select it in the spinner and refresh responsiblespinner
- ProgressDialog progressDialog = null;
+ ProgressDialog progressDialog = dialogUtilites.progressDialog(context,
+ context.getString(R.string.DLG_wait));
try {
- progressDialog = dialogUtilites.progressDialog(context,
- context.getString(R.string.DLG_wait));
+ progressDialog.show();
JSONObject newDashJSON = ProducteevSyncProvider.getInvoker().dashboardsCreate(newDashboardName).getJSONObject("dashboard");
StoreObject local = ProducteevDataService.getInstance().updateDashboards(newDashJSON, true);
if (local != null) {
diff --git a/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevDetailExposer.java b/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevDetailExposer.java
index 545a3f822..69f14ccfd 100644
--- a/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevDetailExposer.java
+++ b/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevDetailExposer.java
@@ -11,7 +11,6 @@ import com.timsu.astrid.R;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.astrid.adapter.TaskAdapter;
import com.todoroo.astrid.api.AstridApiConstants;
-import com.todoroo.astrid.api.DetailExposer;
import com.todoroo.astrid.model.Metadata;
import com.todoroo.astrid.model.StoreObject;
import com.todoroo.astrid.producteev.sync.ProducteevDashboard;
@@ -27,7 +26,7 @@ import com.todoroo.astrid.utility.Preferences;
* @author Tim Su
*
*/
-public class ProducteevDetailExposer extends BroadcastReceiver implements DetailExposer{
+public class ProducteevDetailExposer extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
@@ -52,7 +51,6 @@ public class ProducteevDetailExposer extends BroadcastReceiver implements Detail
context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
}
- @Override
public String getTaskDetails(Context context, long id, boolean extended) {
Metadata metadata = ProducteevDataService.getInstance().getTaskMetadata(id);
if(metadata == null)
@@ -133,9 +131,4 @@ public class ProducteevDetailExposer extends BroadcastReceiver implements Detail
return null;
}
- @Override
- public String getPluginIdentifier() {
- return ProducteevUtilities.IDENTIFIER;
- }
-
}
diff --git a/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevFilterExposer.java b/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevFilterExposer.java
index a5bc993a0..384547c5c 100644
--- a/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevFilterExposer.java
+++ b/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevFilterExposer.java
@@ -3,8 +3,8 @@
*/
package com.todoroo.astrid.producteev;
-import java.util.Map.Entry;
import java.util.TreeMap;
+import java.util.Map.Entry;
import android.content.BroadcastReceiver;
import android.content.ContentValues;
@@ -39,7 +39,7 @@ public class ProducteevFilterExposer extends BroadcastReceiver {
/**
* @param context
*/
- private Filter filterFromList(Context context, ProducteevDashboard dashboard) {
+ public static Filter filterFromList(Context context, ProducteevDashboard dashboard) {
String dashboardTitle = dashboard.getName();
String title = dashboard.getName();
ContentValues values = new ContentValues();
diff --git a/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevUtilities.java b/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevUtilities.java
index f73a39823..6d3e0e669 100644
--- a/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevUtilities.java
+++ b/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevUtilities.java
@@ -41,6 +41,10 @@ public class ProducteevUtilities extends SyncProviderUtilities {
public static final String PREF_SERVER_LAST_SYNC = IDENTIFIER + "_last_server"; //$NON-NLS-1$
+ public static final String PREF_SERVER_LAST_NOTIFICATION = IDENTIFIER + "_last_notification"; //$NON-NLS-1$
+
+ public static final String PREF_SERVER_LAST_ACTIVITY = IDENTIFIER + "_last_activity"; //$NON-NLS-1$
+
/** Producteev user's default dashboard. This is different from the
* preference key, which indicates where user wants to put new tasks */
public static final String PREF_DEFAULT_DASHBOARD = IDENTIFIER + "_defaultdash"; //$NON-NLS-1$
diff --git a/astrid/plugin-src/com/todoroo/astrid/producteev/api/ProducteevInvoker.java b/astrid/plugin-src/com/todoroo/astrid/producteev/api/ProducteevInvoker.java
index 1448456b9..349ca292a 100644
--- a/astrid/plugin-src/com/todoroo/astrid/producteev/api/ProducteevInvoker.java
+++ b/astrid/plugin-src/com/todoroo/astrid/producteev/api/ProducteevInvoker.java
@@ -111,6 +111,31 @@ public class ProducteevInvoker {
"since", since), "dashboards");
}
+ /**
+ * create a dasbhoard
+ *
+ * @param name
+ * @return the new created dashboard as JSONObject
+ */
+ public JSONObject dashboardsCreate(String name) throws ApiServiceException, IOException {
+ return callAuthenticated("dashboards/create.json",
+ "token", token,
+ "title", name);
+ }
+
+ /**
+ * return the list of users who can access a specific dashboard
+ *
+ * @param idDashboard
+ * @param dashboard array-information about the dashboard, if this ...
+ */
+ public JSONArray dashboardsAccess(long idDashboard, String dashboard) throws ApiServiceException, IOException {
+ return getResponse(callAuthenticated("dashboards/access.json",
+ "token", token,
+ "id_dashboard", idDashboard,
+ "dashboard", dashboard),"dashboard");
+ }
+
// --- tasks
/**
@@ -217,14 +242,16 @@ public class ProducteevInvoker {
*
* @param idTask
* @param deadline
+ * @param allDay (optional), 1: all day task (time specified in deadline will be ignored), 0: deadline with time
*
* @return array tasks/view
*/
- public JSONObject tasksSetDeadline(long idTask, String deadline) throws ApiServiceException, IOException {
+ public JSONObject tasksSetDeadline(long idTask, String deadline, Integer allDay) throws ApiServiceException, IOException {
return callAuthenticated("tasks/set_deadline.json",
"token", token,
"id_task", idTask,
- "deadline", deadline);
+ "deadline", deadline,
+ "all_day", allDay);
}
/**
@@ -374,44 +401,49 @@ public class ProducteevInvoker {
"title", title);
}
- // --- users
+ // --- notifications/activities
/**
- * get a user
+ * get every activities
*
- * @param idColleague
- *
- * @return array information about the user
+ * @param dashboardId (optional) if not null, this function only returns notifications for this specific dashboard
+ * @param lastId (optional) this function returns only activities later than this id
*/
- public JSONObject usersView(Long idColleague) throws ApiServiceException, IOException {
- return callAuthenticated("users/view.json",
+ public JSONArray activitiesShowActivities(Long dashboardId, Long lastId) throws ApiResponseParseException, ApiServiceException, IOException {
+ return getResponse(callAuthenticated("activities/show_activities.json",
"token", token,
- "id_colleague", idColleague);
+ "id_dashboard", dashboardId,
+ "last_id", lastId), "activities");
}
/**
- * create a dasbhoard
- *
- * @param name
- * @return the new created dashboard as JSONObject
+ * get every notification for the current user
+ * @param dashboardId
+ * @param lastId
+ * @return
+ * @throws ApiResponseParseException
+ * @throws ApiServiceException
+ * @throws IOException
*/
- public JSONObject dashboardsCreate(String name) throws ApiServiceException, IOException {
- return callAuthenticated("dashboards/create.json",
- "token", token,
- "title", name);
+ public JSONArray activitiesShowNotifications(Long dashboardId, Long lastId) throws ApiResponseParseException, ApiServiceException, IOException {
+ return getResponse(callAuthenticated("activities/show_notifications.json",
+ "token", token,
+ "id_dashboard", dashboardId,
+ "last_id", lastId), "activities");
}
+ // --- users
/**
- * return the list of users who can access a specific dashboard
+ * get a user
*
- * @param idDashboard
- * @param dashboard array-information about the dashboard, if this ...
+ * @param idColleague
+ *
+ * @return array information about the user
*/
- public JSONArray dashboardsAccess(long idDashboard, String dashboard) throws ApiServiceException, IOException {
- return getResponse(callAuthenticated("dashboards/access.json",
- "token", token,
- "id_dashboard", idDashboard,
- "dashboard", dashboard),"dashboard");
+ public JSONObject usersView(Long idColleague) throws ApiServiceException, IOException {
+ return callAuthenticated("users/view.json",
+ "token", token,
+ "id_colleague", idColleague);
}
// --- invocation
diff --git a/astrid/plugin-src/com/todoroo/astrid/producteev/sync/ProducteevDataService.java b/astrid/plugin-src/com/todoroo/astrid/producteev/sync/ProducteevDataService.java
index 50df90fc8..19ca28fec 100644
--- a/astrid/plugin-src/com/todoroo/astrid/producteev/sync/ProducteevDataService.java
+++ b/astrid/plugin-src/com/todoroo/astrid/producteev/sync/ProducteevDataService.java
@@ -20,15 +20,17 @@ 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.MetadataCriteria;
+import com.todoroo.astrid.core.PluginServices;
import com.todoroo.astrid.dao.StoreObjectDao;
-import com.todoroo.astrid.dao.StoreObjectDao.StoreObjectCriteria;
import com.todoroo.astrid.dao.TaskDao;
+import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
+import com.todoroo.astrid.dao.StoreObjectDao.StoreObjectCriteria;
import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.model.Metadata;
import com.todoroo.astrid.model.StoreObject;
import com.todoroo.astrid.model.Task;
import com.todoroo.astrid.producteev.ProducteevUtilities;
+import com.todoroo.astrid.producteev.api.ApiUtilities;
import com.todoroo.astrid.rmilk.data.MilkNote;
import com.todoroo.astrid.service.MetadataService;
import com.todoroo.astrid.tags.TagService;
@@ -81,6 +83,7 @@ public final class ProducteevDataService {
metadataService.deleteWhere(Metadata.KEY.eq(ProducteevTask.METADATA_KEY));
metadataService.deleteWhere(Metadata.KEY.eq(ProducteevNote.METADATA_KEY));
storeObjectDao.deleteWhere(StoreObject.TYPE.eq(ProducteevDashboard.TYPE));
+ PluginServices.getTaskService().clearDetails();
}
/**
@@ -272,15 +275,15 @@ public final class ProducteevDataService {
local = new StoreObject();
local.setValue(StoreObject.TYPE, ProducteevDashboard.TYPE);
local.setValue(ProducteevDashboard.REMOTE_ID, id);
- local.setValue(ProducteevDashboard.NAME, remote.getString("title"));
+ local.setValue(ProducteevDashboard.NAME, ApiUtilities.decode(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(',');
- String name = user.optString("firstname", "") + ' ' +
- user.optString("lastname", "");
+ String name = ApiUtilities.decode(user.optString("firstname", "") + ' ' +
+ user.optString("lastname", ""));
users.append(name.trim()).append(';');
}
local.setValue(ProducteevDashboard.USERS, users.toString());
diff --git a/astrid/plugin-src/com/todoroo/astrid/producteev/sync/ProducteevSyncProvider.java b/astrid/plugin-src/com/todoroo/astrid/producteev/sync/ProducteevSyncProvider.java
index 2677d198f..35622814e 100644
--- a/astrid/plugin-src/com/todoroo/astrid/producteev/sync/ProducteevSyncProvider.java
+++ b/astrid/plugin-src/com/todoroo/astrid/producteev/sync/ProducteevSyncProvider.java
@@ -28,16 +28,21 @@ import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.service.ExceptionService;
+import com.todoroo.andlib.service.NotificationManager;
import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.andlib.utility.DialogUtilities;
+import com.todoroo.astrid.activity.ShortcutActivity;
import com.todoroo.astrid.api.AstridApiConstants;
+import com.todoroo.astrid.api.Filter;
+import com.todoroo.astrid.api.TaskContainer;
import com.todoroo.astrid.common.SyncProvider;
import com.todoroo.astrid.common.TaskContainer;
import com.todoroo.astrid.model.Metadata;
import com.todoroo.astrid.model.StoreObject;
import com.todoroo.astrid.model.Task;
import com.todoroo.astrid.producteev.ProducteevBackgroundService;
+import com.todoroo.astrid.producteev.ProducteevFilterExposer;
import com.todoroo.astrid.producteev.ProducteevLoginActivity;
import com.todoroo.astrid.producteev.ProducteevPreferences;
import com.todoroo.astrid.producteev.ProducteevUtilities;
@@ -48,6 +53,7 @@ import com.todoroo.astrid.producteev.api.ProducteevInvoker;
import com.todoroo.astrid.rmilk.data.MilkNote;
import com.todoroo.astrid.service.AstridDependencyInjector;
import com.todoroo.astrid.tags.TagService;
+import com.todoroo.astrid.utility.Constants;
import com.todoroo.astrid.utility.Preferences;
@SuppressWarnings("nls")
@@ -213,6 +219,8 @@ public class ProducteevSyncProvider extends SyncProvider 0) {
+ lastNotificationId = ""+notifications.getJSONObject(0).getJSONObject("activity").getLong("id_activity");
+ }
+
+ // display notifications from producteev-log
+ Context context = ContextManager.getContext();
+ final NotificationManager nm = new NotificationManager.AndroidNotificationManager(context);
+ for (int i = 0; i< notificationsList.length; i++) {
+ long id_dashboard = notifications.getJSONObject(i).getJSONObject("activity").getLong("id_dashboard");
+ String dashboardName = null;
+ StoreObject[] dashboardsData = ProducteevDataService.getInstance().getDashboards();
+ ProducteevDashboard dashboard = null;
+ if (dashboardsData != null) {
+ for (int j=0; i]+>", "");
+ PendingIntent contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);
+ notification.setLatestEventInfo(context, contentTitle, message, contentIntent);
+
+ nm.notify(Constants.NOTIFICATION_PRODUCTEEV_NOTIFICATIONS-i, notification);
+ }
+ }
+
+ // store lastIds in Preferences
+ Preferences.setString(ProducteevUtilities.PREF_SERVER_LAST_NOTIFICATION, lastNotificationId);
+ Preferences.setString(ProducteevUtilities.PREF_SERVER_LAST_ACTIVITY, lastActivityId);
+
FlurryAgent.onEvent("pdv-sync-finished"); //$NON-NLS-1$
} catch (IllegalStateException e) {
// occurs when application was closed
@@ -268,6 +326,23 @@ public class ProducteevSyncProvider extends SyncProvider
*
*/
-public class RepeatDetailExposer extends BroadcastReceiver implements DetailExposer {
+public class RepeatDetailExposer extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
@@ -119,7 +118,6 @@ public class RepeatDetailExposer extends BroadcastReceiver implements DetailExpo
return null;
}
- @Override
public String getPluginIdentifier() {
return RepeatsPlugin.IDENTIFIER;
}
diff --git a/astrid/plugin-src/com/todoroo/astrid/rmilk/MilkDetailExposer.java b/astrid/plugin-src/com/todoroo/astrid/rmilk/MilkDetailExposer.java
index 6f0eebdd4..4348f354d 100644
--- a/astrid/plugin-src/com/todoroo/astrid/rmilk/MilkDetailExposer.java
+++ b/astrid/plugin-src/com/todoroo/astrid/rmilk/MilkDetailExposer.java
@@ -11,7 +11,6 @@ import com.timsu.astrid.R;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.astrid.adapter.TaskAdapter;
import com.todoroo.astrid.api.AstridApiConstants;
-import com.todoroo.astrid.api.DetailExposer;
import com.todoroo.astrid.model.Metadata;
import com.todoroo.astrid.rmilk.data.MilkDataService;
import com.todoroo.astrid.rmilk.data.MilkNote;
@@ -26,7 +25,7 @@ import com.todoroo.astrid.rmilk.data.MilkTask;
* @author Tim Su
*
*/
-public class MilkDetailExposer extends BroadcastReceiver implements DetailExposer{
+public class MilkDetailExposer extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
@@ -51,7 +50,6 @@ public class MilkDetailExposer extends BroadcastReceiver implements DetailExpose
context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
}
- @Override
public String getTaskDetails(Context context, long id, boolean extended) {
Metadata metadata = MilkDataService.getInstance().getTaskMetadata(id);
if(metadata == null)
@@ -92,9 +90,4 @@ public class MilkDetailExposer extends BroadcastReceiver implements DetailExpose
return result.substring(0, result.length() - TaskAdapter.DETAIL_SEPARATOR.length());
}
- @Override
- public String getPluginIdentifier() {
- return MilkUtilities.IDENTIFIER;
- }
-
}
diff --git a/astrid/plugin-src/com/todoroo/astrid/tags/TagDetailExposer.java b/astrid/plugin-src/com/todoroo/astrid/tags/TagDetailExposer.java
index 21172fe2e..a4f0b26fe 100644
--- a/astrid/plugin-src/com/todoroo/astrid/tags/TagDetailExposer.java
+++ b/astrid/plugin-src/com/todoroo/astrid/tags/TagDetailExposer.java
@@ -8,7 +8,6 @@ import android.content.Context;
import android.content.Intent;
import com.todoroo.astrid.api.AstridApiConstants;
-import com.todoroo.astrid.api.DetailExposer;
/**
* Exposes Task Detail for tags, i.e. "Tags: frogs, animals"
@@ -16,7 +15,7 @@ import com.todoroo.astrid.api.DetailExposer;
* @author Tim Su
*
*/
-public class TagDetailExposer extends BroadcastReceiver implements DetailExposer {
+public class TagDetailExposer extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
@@ -39,7 +38,6 @@ public class TagDetailExposer extends BroadcastReceiver implements DetailExposer
context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
}
- @Override
public String getTaskDetails(Context context, long id, boolean extended) {
if(extended)
return null;
@@ -51,9 +49,4 @@ public class TagDetailExposer extends BroadcastReceiver implements DetailExposer
return "
" + tagList; //$NON-NLS-1$
}
- @Override
- public String getPluginIdentifier() {
- return TagsPlugin.IDENTIFIER;
- }
-
}
diff --git a/astrid/plugin-src/com/todoroo/astrid/timers/TimerDecorationExposer.java b/astrid/plugin-src/com/todoroo/astrid/timers/TimerDecorationExposer.java
index 499f78e64..45b5159ac 100644
--- a/astrid/plugin-src/com/todoroo/astrid/timers/TimerDecorationExposer.java
+++ b/astrid/plugin-src/com/todoroo/astrid/timers/TimerDecorationExposer.java
@@ -11,6 +11,7 @@ import android.content.Intent;
import android.graphics.Color;
import android.os.SystemClock;
import android.text.format.DateUtils;
+import android.view.View;
import android.widget.RemoteViews;
import com.timsu.astrid.R;
@@ -33,8 +34,12 @@ public class TimerDecorationExposer extends BroadcastReceiver {
private static HashMap decorations =
new HashMap();
- public static void removeFromCache(long taskId) {
+ private static HashMap tasksNeedingUpdate =
+ new HashMap();
+
+ public static void updateTimer(long taskId) {
decorations.remove(taskId);
+ tasksNeedingUpdate.put(taskId, true);
}
@Override
@@ -45,11 +50,15 @@ public class TimerDecorationExposer extends BroadcastReceiver {
Task task;
try {
- task = PluginServices.getTaskService().fetchById(taskId, Task.ELAPSED_SECONDS, Task.TIMER_START);
+ if(tasksNeedingUpdate.containsKey(taskId))
+ task = PluginServices.getTaskService().fetchById(taskId, Task.ID, Task.ELAPSED_SECONDS, Task.TIMER_START);
+ else
+ task = intent.getParcelableExtra("model"); //$NON-NLS-1$
} catch (IllegalStateException e) {
return;
}
- if(task == null || (task.getValue(Task.ELAPSED_SECONDS) == 0 &&
+
+ if(task == null || task.getId() != taskId || (task.getValue(Task.ELAPSED_SECONDS) == 0 &&
task.getValue(Task.TIMER_START) == 0))
return;
@@ -70,12 +79,16 @@ public class TimerDecorationExposer extends BroadcastReceiver {
elapsed += DateUtilities.now() - task.getValue(Task.TIMER_START);
decoration.decoration.setChronometer(R.id.timer, SystemClock.elapsedRealtime() -
elapsed, null, true);
+ decoration.decoration.setViewVisibility(R.id.timer, View.VISIBLE);
+ decoration.decoration.setViewVisibility(R.id.label, View.GONE);
} else {
// if timer is not started, make the chronometer just a text label,
// since we don't want the time to be displayed relative to elapsed
String format = buildFormat(elapsed);
- decoration.decoration.setChronometer(R.id.timer, SystemClock.elapsedRealtime() -
- elapsed, format, false);
+ decoration.color = 0;
+ decoration.decoration.setTextViewText(R.id.label, format);
+ decoration.decoration.setViewVisibility(R.id.timer, View.GONE);
+ decoration.decoration.setViewVisibility(R.id.label, View.VISIBLE);
}
diff --git a/astrid/plugin-src/com/todoroo/astrid/timers/TimerPlugin.java b/astrid/plugin-src/com/todoroo/astrid/timers/TimerPlugin.java
index 9a437ee2d..ab1439959 100644
--- a/astrid/plugin-src/com/todoroo/astrid/timers/TimerPlugin.java
+++ b/astrid/plugin-src/com/todoroo/astrid/timers/TimerPlugin.java
@@ -53,7 +53,7 @@ public class TimerPlugin extends BroadcastReceiver {
}
}
PluginServices.getTaskService().save(task);
- TimerDecorationExposer.removeFromCache(task.getId());
+ TimerDecorationExposer.updateTimer(task.getId());
// transmit new intents
Intent intent = new Intent(AstridApiConstants.BROADCAST_REQUEST_ACTIONS);
diff --git a/astrid/res/drawable/ic_producteev_notification.png b/astrid/res/drawable/ic_producteev_notification.png
new file mode 100644
index 000000000..368ae96b6
Binary files /dev/null and b/astrid/res/drawable/ic_producteev_notification.png differ
diff --git a/astrid/res/drawable/icon_producteev.png b/astrid/res/drawable/icon_producteev.png
index 51d8f09f0..5901763ca 100644
Binary files a/astrid/res/drawable/icon_producteev.png and b/astrid/res/drawable/icon_producteev.png differ
diff --git a/astrid/res/layout/task_adapter_row.xml b/astrid/res/layout/task_adapter_row.xml
index 40c201cac..5dcf7f89d 100644
--- a/astrid/res/layout/task_adapter_row.xml
+++ b/astrid/res/layout/task_adapter_row.xml
@@ -8,13 +8,13 @@
android:paddingBottom="4dip"
android:paddingLeft="4dip"
android:paddingRight="4dip"
- android:minHeight="40dip"
android:orientation="vertical">
@@ -62,6 +62,7 @@
+ android:paddingLeft="4dip"
+ android:orientation="horizontal">
+
+
+
+
+
+
diff --git a/astrid/res/layout/timer_decoration.xml b/astrid/res/layout/timer_decoration.xml
index 449a2707c..90677e84c 100644
--- a/astrid/res/layout/timer_decoration.xml
+++ b/astrid/res/layout/timer_decoration.xml
@@ -15,6 +15,14 @@
android:src="@drawable/timers_decoration" />
+
+
Undelete Task
+
+
+ Purge Task
diff --git a/astrid/res/values/strings-producteev.xml b/astrid/res/values/strings-producteev.xml
index 02288955e..8f643490f 100644
--- a/astrid/res/values/strings-producteev.xml
+++ b/astrid/res/values/strings-producteev.xml
@@ -3,8 +3,8 @@
-
-
+
+
Producteev
@@ -39,7 +39,7 @@
Name for new Workspace
-
+
Default Workspace
@@ -92,8 +92,11 @@
-
- Astrid: Producteev
+
+ Producteev
+
+
+ %s tasks updated / click for more details
Connection Error! Check your Internet connection.
diff --git a/astrid/src/com/todoroo/astrid/activity/EditPreferences.java b/astrid/src/com/todoroo/astrid/activity/EditPreferences.java
index 69866269b..f435100ba 100644
--- a/astrid/src/com/todoroo/astrid/activity/EditPreferences.java
+++ b/astrid/src/com/todoroo/astrid/activity/EditPreferences.java
@@ -14,9 +14,10 @@ import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.os.Bundle;
import android.preference.Preference;
-import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceCategory;
import android.preference.PreferenceScreen;
+import android.preference.Preference.OnPreferenceClickListener;
+import android.widget.Toast;
import com.timsu.astrid.R;
import com.todoroo.andlib.service.Autowired;
@@ -130,6 +131,18 @@ public class EditPreferences extends TodorooPreferences {
getPreferenceScreen().addPreference(group);
Preference preference = new Preference(this);
+ preference.setTitle("Flush detail cache");
+ preference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+ public boolean onPreferenceClick(Preference p) {
+ database.openForWriting();
+ Toast.makeText(EditPreferences.this, "" + taskService.clearDetails(),
+ Toast.LENGTH_LONG).show();
+ return false;
+ }
+ });
+ group.addPreference(preference);
+
+ preference = new Preference(this);
preference.setTitle("Make Lots of Tasks");
preference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference p) {
diff --git a/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java b/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java
index af2464038..ebec6b994 100644
--- a/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java
+++ b/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java
@@ -116,10 +116,11 @@ public class TaskListActivity extends ListActivity implements OnScrollListener,
private static final int CONTEXT_MENU_EDIT_TASK_ID = Menu.FIRST + 6;
private static final int CONTEXT_MENU_DELETE_TASK_ID = Menu.FIRST + 7;
private static final int CONTEXT_MENU_UNDELETE_TASK_ID = Menu.FIRST + 8;
- private static final int CONTEXT_MENU_ADDON_INTENT_ID = Menu.FIRST + 9;
+ private static final int CONTEXT_MENU_PURGE_TASK_ID = Menu.FIRST + 9;
+ private static final int CONTEXT_MENU_ADDON_INTENT_ID = Menu.FIRST + 10;
/** menu code indicating the end of the context menu */
- private static final int CONTEXT_MENU_DEBUG = Menu.FIRST + 10;
+ private static final int CONTEXT_MENU_DEBUG = Menu.FIRST + 11;
// --- constants
@@ -479,9 +480,9 @@ public class TaskListActivity extends ListActivity implements OnScrollListener,
} else if(AstridApiConstants.BROADCAST_SEND_DETAILS.equals(intent.getAction())) {
String detail = extras.getString(AstridApiConstants.EXTRAS_RESPONSE);
if(extras.getBoolean(AstridApiConstants.EXTRAS_EXTENDED))
- taskAdapter.detailManager.addNew(taskId, addOn, detail);
- else
taskAdapter.extendedDetailManager.addNew(taskId, addOn, detail);
+ else
+ taskAdapter.addDetails(taskId, detail);
} else if(AstridApiConstants.BROADCAST_SEND_ACTIONS.equals(intent.getAction())) {
TaskAction action = extras.getParcelable(AstridApiConstants.EXTRAS_RESPONSE);
taskAdapter.taskActionManager.addNew(taskId, addOn, action);
@@ -736,6 +737,9 @@ public class TaskListActivity extends ListActivity implements OnScrollListener,
if(task.isDeleted()) {
menu.add(id, CONTEXT_MENU_UNDELETE_TASK_ID, Menu.NONE,
R.string.TAd_contextUndeleteTask);
+
+ menu.add(id, CONTEXT_MENU_PURGE_TASK_ID, Menu.NONE,
+ R.string.TAd_contextPurgeTask);
} else {
menu.add(id, CONTEXT_MENU_EDIT_TASK_ID, Menu.NONE,
R.string.TAd_contextEditTask);
@@ -845,6 +849,13 @@ public class TaskListActivity extends ListActivity implements OnScrollListener,
return true;
}
+ case CONTEXT_MENU_PURGE_TASK_ID: {
+ itemId = item.getGroupId();
+ taskService.purge(itemId);
+ loadTaskListContent(true);
+ return true;
+ }
+
// --- debug
case CONTEXT_MENU_DEBUG: {
diff --git a/astrid/src/com/todoroo/astrid/adapter/TaskAdapter.java b/astrid/src/com/todoroo/astrid/adapter/TaskAdapter.java
index a36f16fab..f01e14a9a 100644
--- a/astrid/src/com/todoroo/astrid/adapter/TaskAdapter.java
+++ b/astrid/src/com/todoroo/astrid/adapter/TaskAdapter.java
@@ -1,6 +1,7 @@
package com.todoroo.astrid.adapter;
import java.util.Collection;
+import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
@@ -12,9 +13,11 @@ import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.database.Cursor;
+import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.text.Html;
+import android.text.TextUtils;
import android.text.Html.ImageGetter;
import android.text.util.Linkify;
import android.view.ContextMenu;
@@ -40,21 +43,14 @@ import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.service.ExceptionService;
import com.todoroo.andlib.utility.DateUtilities;
-import com.todoroo.andlib.utility.SoftHashMap;
import com.todoroo.astrid.activity.TaskEditActivity;
import com.todoroo.astrid.activity.TaskListActivity;
-import com.todoroo.astrid.alarms.AlarmDetailExposer;
import com.todoroo.astrid.api.AstridApiConstants;
-import com.todoroo.astrid.api.DetailExposer;
import com.todoroo.astrid.api.TaskAction;
import com.todoroo.astrid.api.TaskDecoration;
import com.todoroo.astrid.model.Task;
-import com.todoroo.astrid.notes.NoteDetailExposer;
-import com.todoroo.astrid.producteev.ProducteevDetailExposer;
-import com.todoroo.astrid.repeats.RepeatDetailExposer;
-import com.todoroo.astrid.rmilk.MilkDetailExposer;
+import com.todoroo.astrid.service.AddOnService;
import com.todoroo.astrid.service.TaskService;
-import com.todoroo.astrid.tags.TagDetailExposer;
import com.todoroo.astrid.utility.Constants;
import com.todoroo.astrid.utility.Preferences;
@@ -72,6 +68,8 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
public static final String DETAIL_SEPARATOR = " | "; //$NON-NLS-1$
+ public static final String BROADCAST_EXTRA_TASK = "model"; //$NON-NLS-1$
+
// --- other constants
/** Properties that need to be read from the action item */
@@ -83,16 +81,9 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
Task.COMPLETION_DATE,
Task.HIDE_UNTIL,
Task.DELETION_DATE,
- };
-
- /** Internal Task Detail exposers */
- public static final DetailExposer[] EXPOSERS = new DetailExposer[] {
- new TagDetailExposer(),
- new RepeatDetailExposer(),
- new NoteDetailExposer(),
- new MilkDetailExposer(),
- new ProducteevDetailExposer(),
- new AlarmDetailExposer(),
+ Task.DETAILS,
+ Task.ELAPSED_SECONDS,
+ Task.TIMER_START,
};
private static int[] IMPORTANCE_COLORS = null;
@@ -105,6 +96,9 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
@Autowired
private TaskService taskService;
+ @Autowired
+ private AddOnService addOnService;
+
protected final ListActivity activity;
protected final HashMap completedItems = new HashMap();
private OnCompletedTaskListener onCompletedTaskListener = null;
@@ -112,6 +106,7 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
private final int resource;
private final LayoutInflater inflater;
private int fontSize;
+ private DetailLoaderThread detailLoader;
private final AtomicReference query;
@@ -120,8 +115,7 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
// --- task detail and decoration soft caches
- public final DetailManager detailManager = new DetailManager(false);
- public final DetailManager extendedDetailManager = new DetailManager(true);
+ public final DetailManager extendedDetailManager = new DetailManager();
public final DecorationManager decorationManager =
new DecorationManager();
public final TaskActionManager taskActionManager = new TaskActionManager();
@@ -159,6 +153,9 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
if(IMPORTANCE_COLORS == null)
IMPORTANCE_COLORS = Task.getImportanceColors(activity.getResources());
}
+
+ detailLoader = new DetailLoaderThread();
+ detailLoader.start();
}
/* ======================================================================
@@ -221,6 +218,7 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
ViewHolder viewHolder = ((ViewHolder)view.getTag());
Task task = viewHolder.task;
+ task.clear();
task.readFromCursor(cursor);
setFieldContentsAndVisibility(view);
@@ -271,6 +269,8 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
if(hiddenUntil > DateUtilities.now())
nameValue = r.getString(R.string.TAd_hiddenFormat, nameValue);
nameView.setText(nameValue);
+
+ nameView.setMovementMethod(null);
Linkify.addLinks(nameView, Linkify.ALL);
}
@@ -324,9 +324,21 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
importanceView.setBackgroundColor(0);
}
+ String details;
+ if(taskDetailLoader.containsKey(task.getId()))
+ details = taskDetailLoader.get(task.getId()).toString();
+ else
+ details = task.getValue(Task.DETAILS);
+ if(TextUtils.isEmpty(details) || DETAIL_SEPARATOR.equals(details)) {
+ viewHolder.details.setVisibility(View.GONE);
+ } else {
+ viewHolder.details.setVisibility(View.VISIBLE);
+ viewHolder.details.setText(Html.fromHtml(details.trim().replace("\n", //$NON-NLS-1$
+ "
"), detailImageGetter, null)); //$NON-NLS-1$
+ }
+
// details and decorations, expanded
if(!isFling) {
- detailManager.request(viewHolder);
decorationManager.request(viewHolder);
if(expanded == task.getId()) {
extendedDetailManager.request(viewHolder);
@@ -337,8 +349,9 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
}
} else {
long taskId = viewHolder.task.getId();
- detailManager.reset(viewHolder, taskId);
decorationManager.reset(viewHolder, taskId);
+ viewHolder.extendedDetails.setVisibility(View.GONE);
+ viewHolder.actions.setVisibility(View.GONE);
}
}
@@ -348,9 +361,10 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
* created.
*/
private void addListeners(final View container) {
+ ViewHolder viewHolder = (ViewHolder)container.getTag();
+
// check box listener
- final CheckBox completeBox = ((CheckBox)container.findViewById(R.id.completeBox));
- completeBox.setOnClickListener(completeBoxListener);
+ viewHolder.completeBox.setOnClickListener(completeBoxListener);
// context menu listener
container.setOnCreateContextMenuListener(listener);
@@ -359,6 +373,93 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
container.setOnClickListener(listener);
}
+ /* ======================================================================
+ * ============================================================== details
+ * ====================================================================== */
+
+ // implementation note: this map is really costly if users have
+ // a large number of tasks to load, since it all goes into memory.
+ // it's best to do this, though, in order to append details to each other
+ private final Map taskDetailLoader = Collections.synchronizedMap(new HashMap());
+
+ private final Task taskDetailContainer = new Task();
+
+ public class DetailLoaderThread extends Thread {
+ @Override
+ public void run() {
+ // for all of the tasks returned by our cursor, verify details
+ TodorooCursor fetchCursor = taskService.fetchFiltered(
+ query.get(), null, Task.ID, Task.DETAILS, Task.COMPLETION_DATE);
+ activity.startManagingCursor(fetchCursor);
+ try {
+ Task task = new Task();
+ for(fetchCursor.moveToFirst(); !fetchCursor.isAfterLast(); fetchCursor.moveToNext()) {
+ task.clear();
+ task.readFromCursor(fetchCursor);
+ if(task.isCompleted())
+ continue;
+ if(TextUtils.isEmpty(task.getValue(Task.DETAILS))) {
+ Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_REQUEST_DETAILS);
+ broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, task.getId());
+ broadcastIntent.putExtra(AstridApiConstants.EXTRAS_EXTENDED, false);
+ activity.sendOrderedBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
+
+ taskDetailLoader.put(task.getId(), new StringBuilder());
+
+ task.setValue(Task.DETAILS, DETAIL_SEPARATOR);
+ taskService.save(task);
+ }
+ }
+ } catch (Exception e) {
+ // suppress silently
+ }
+ }
+ }
+
+ /**
+ * Add detail to a task
+ *
+ * @param id
+ * @param detail
+ */
+ public void addDetails(long id, String detail) {
+ final StringBuilder details = taskDetailLoader.get(id);
+ if(details == null)
+ return;
+ synchronized(details) {
+ if(details.toString().contains(detail))
+ return;
+ if(details.length() > 0)
+ details.append(DETAIL_SEPARATOR);
+ details.append(detail);
+ taskDetailContainer.setId(id);
+ taskDetailContainer.setValue(Task.DETAILS, details.toString());
+ taskService.save(taskDetailContainer);
+ }
+
+ activity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ ListView listView = activity.getListView();
+ int scrollPos = listView.getScrollY();
+ notifyDataSetInvalidated();
+ listView.scrollTo(0, scrollPos);
+ }
+ });
+ }
+
+ private final ImageGetter detailImageGetter = new ImageGetter() {
+ public Drawable getDrawable(String source) {
+ Resources r = activity.getResources();
+ int drawable = r.getIdentifier("drawable/" + source, null, Constants.PACKAGE); //$NON-NLS-1$
+ if(drawable == 0)
+ return null;
+ Drawable d = r.getDrawable(drawable);
+ d.setBounds(0,0,d.getIntrinsicWidth(),d.getIntrinsicHeight());
+ return d;
+ }
+ };
+
/* ======================================================================
* ============================================================== add-ons
* ====================================================================== */
@@ -367,10 +468,22 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
* Called to tell the cache to be cleared
*/
public void flushCaches() {
- detailManager.clearCache();
extendedDetailManager.clearCache();
decorationManager.clearCache();
taskActionManager.clearCache();
+ taskDetailLoader.clear();
+ detailLoader = new DetailLoaderThread();
+ detailLoader.start();
+ }
+
+ /**
+ * Called to tell the cache to be cleared
+ */
+ public void flushSpecific(long taskId) {
+ extendedDetailManager.clearCache(taskId);
+ decorationManager.clearCache(taskId);
+ taskActionManager.clearCache(taskId);
+ taskDetailLoader.remove(taskId);
}
/**
@@ -380,62 +493,21 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
*/
public class DetailManager extends AddOnManager {
- private final ImageGetter imageGetter = new ImageGetter() {
- public Drawable getDrawable(String source) {
- Resources r = activity.getResources();
- int drawable = r.getIdentifier("drawable/" + source, null, Constants.PACKAGE); //$NON-NLS-1$
- if(drawable == 0)
- return null;
- Drawable d = r.getDrawable(drawable);
- d.setBounds(0,0,d.getIntrinsicWidth(),d.getIntrinsicHeight());
- return d;
- }
- };
-
- private final boolean extended;
- public DetailManager(boolean extended) {
- this.extended = extended;
+ public DetailManager() {
+ //
}
@Override
- Intent createBroadcastIntent(long taskId) {
+ Intent createBroadcastIntent(Task task) {
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_REQUEST_DETAILS);
- broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId);
- broadcastIntent.putExtra(AstridApiConstants.EXTRAS_EXTENDED, extended);
+ broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, task.getId());
+ broadcastIntent.putExtra(AstridApiConstants.EXTRAS_EXTENDED, true);
return broadcastIntent;
}
@Override
- public boolean request(final ViewHolder viewHolder) {
- if(super.request(viewHolder)) {
- final long taskId = viewHolder.task.getId();
- // load internal details
- new Thread() {
- @Override
- public void run() {
- for(DetailExposer exposer : EXPOSERS) {
- final String detail = exposer.getTaskDetails(activity,
- taskId, extended);
- if(detail == null)
- continue;
- final Collection cacheList =
- addIfNotExists(taskId, exposer.getPluginIdentifier(),
- detail);
- if(cacheList != null) {
- if(taskId != viewHolder.task.getId())
- continue;
- activity.runOnUiThread(new Runnable() {
- public void run() {
- draw(viewHolder, taskId, cacheList);
- }
- });
- }
- }
- };
- }.start();
- return true;
- }
- return false;
+ public void addNew(long taskId, String addOn, String item) {
+ super.addNew(taskId, addOn, item);
}
@SuppressWarnings("nls")
@@ -443,9 +515,9 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
void draw(ViewHolder viewHolder, long taskId, Collection details) {
if(details == null || viewHolder.task.getId() != taskId)
return;
- TextView view = extended ? viewHolder.extendedDetails : viewHolder.details;
- reset(viewHolder, taskId);
- if(details.isEmpty() || (extended && expanded != taskId)) {
+ TextView view = viewHolder.extendedDetails;
+ if(details.isEmpty() || (expanded != taskId)) {
+ reset(viewHolder, taskId);
return;
}
view.setVisibility(View.VISIBLE);
@@ -457,7 +529,8 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
}
String string = detailText.toString();
if(string.contains("<"))
- view.setText(Html.fromHtml(string.trim().replace("\n", "
"), imageGetter, null));
+ view.setText(Html.fromHtml(string.trim().replace("\n", "
"),
+ detailImageGetter, null));
else
view.setText(string.trim());
Linkify.addLinks(view, Linkify.ALL);
@@ -465,7 +538,7 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
@Override
void reset(ViewHolder viewHolder, long taskId) {
- TextView view = extended ? viewHolder.extendedDetails : viewHolder.details;
+ TextView view = viewHolder.extendedDetails;
view.setVisibility(View.GONE);
}
}
@@ -478,12 +551,18 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
*/
public class DecorationManager extends AddOnManager {
@Override
- Intent createBroadcastIntent(long taskId) {
+ Intent createBroadcastIntent(Task task) {
Intent intent = new Intent(AstridApiConstants.BROADCAST_REQUEST_DECORATIONS);
- intent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId);
+ intent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, task.getId());
+ intent.putExtra(BROADCAST_EXTRA_TASK, task);
return intent;
}
+ @Override
+ public void addNew(long taskId, String addOn, TaskDecoration item) {
+ super.addNew(taskId, addOn, item);
+ }
+
@Override
void draw(ViewHolder viewHolder, long taskId, Collection decorations) {
if(decorations == null || viewHolder.task.getId() != taskId)
@@ -493,6 +572,7 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
if(decorations.size() == 0)
return;
+
int i = 0;
boolean colorSet = false;
viewHolder.decorations = new View[decorations.size()];
@@ -522,7 +602,10 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
for(View view : viewHolder.decorations)
viewHolder.taskRow.removeView(view);
}
- viewHolder.view.setBackgroundResource(android.R.drawable.list_selector_background);
+ if(taskId == expanded)
+ viewHolder.view.setBackgroundColor(Color.argb(20, 255, 255, 255));
+ else
+ viewHolder.view.setBackgroundResource(android.R.drawable.list_selector_background);
}
}
@@ -533,10 +616,15 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
*
*/
public class TaskActionManager extends AddOnManager {
+
+ private final LinearLayout.LayoutParams params =
+ new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT,
+ LayoutParams.FILL_PARENT, 1f);
+
@Override
- Intent createBroadcastIntent(long taskId) {
+ Intent createBroadcastIntent(Task task) {
Intent intent = new Intent(AstridApiConstants.BROADCAST_REQUEST_ACTIONS);
- intent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId);
+ intent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, task.getId());
return intent;
}
@@ -545,15 +633,25 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
if(actions == null || viewHolder.task.getId() != taskId)
return;
- reset(viewHolder, taskId);
+ // hack because we know we have > 1 button
+ if(addOnService.hasPowerPack() && actions.size() == 0)
+ return;
+
+ for(int i = viewHolder.actions.getChildCount(); i < actions.size() + 1; i++) {
+ Button editButton = new Button(activity);
+ editButton.setLayoutParams(params);
+ viewHolder.actions.addView(editButton);
+ }
+ for(int i = actions.size() + 1; i < viewHolder.actions.getChildCount(); i++) {
+ viewHolder.actions.getChildAt(i).setVisibility(View.GONE);
+ }
- LinearLayout.LayoutParams params =
- new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT,
- LayoutParams.FILL_PARENT, 1f);
+ int i = 0;
+ Button button = (Button) viewHolder.actions.getChildAt(i++);
- Button editButton = new Button(activity);
- editButton.setText(R.string.TAd_actionEditTask);
- editButton.setOnClickListener(new OnClickListener() {
+ button.setText(R.string.TAd_actionEditTask);
+ button.setVisibility(View.VISIBLE);
+ button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
Intent intent = new Intent(activity, TaskEditActivity.class);
@@ -561,17 +659,15 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
activity.startActivityForResult(intent, TaskListActivity.ACTIVITY_EDIT_TASK);
}
});
- editButton.setLayoutParams(params);
- viewHolder.actions.addView(editButton);
for(TaskAction action : actions) {
- Button view = new Button(activity);
- view.setText(action.text);
- view.setOnClickListener(new ActionClickListener(action));
- view.setLayoutParams(params);
- viewHolder.actions.addView(view);
+ button = (Button) viewHolder.actions.getChildAt(i++);
+ button.setText(action.text);
+ button.setVisibility(View.VISIBLE);
+ button.setOnClickListener(new ActionClickListener(action, viewHolder));
}
+ reset(viewHolder, taskId);
}
@Override
@@ -581,7 +677,6 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
return;
}
viewHolder.actions.setVisibility(View.VISIBLE);
- viewHolder.actions.removeAllViews();
}
}
@@ -609,19 +704,25 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
};
private final class ActionClickListener implements View.OnClickListener {
- TaskAction action;
+ private final TaskAction action;
+ private final ViewHolder viewHolder;
- public ActionClickListener(TaskAction action) {
+ public ActionClickListener(TaskAction action, ViewHolder viewHolder) {
this.action = action;
+ this.viewHolder = viewHolder;
}
public void onClick(View v) {
+ flushSpecific(viewHolder.task.getId());
try {
action.intent.send();
} catch (Exception e) {
exceptionService.displayAndReportError(activity,
"Error launching action", e); //$NON-NLS-1$
}
+ decorationManager.request(viewHolder);
+ extendedDetailManager.request(viewHolder);
+ taskActionManager.request(viewHolder);
}
};
@@ -718,7 +819,7 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
abstract public class AddOnManager {
private final Map> cache =
- new SoftHashMap>();
+ new HashMap>();
// --- interface
@@ -737,13 +838,13 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
// request details
draw(viewHolder, taskId, get(taskId));
- Intent broadcastIntent = createBroadcastIntent(taskId);
+ Intent broadcastIntent = createBroadcastIntent(viewHolder.task);
activity.sendOrderedBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
return true;
}
/** creates a broadcast intent for requesting */
- abstract Intent createBroadcastIntent(long taskId);
+ abstract Intent createBroadcastIntent(Task task);
/** updates the given view */
abstract void draw(ViewHolder viewHolder, long taskId, Collection list);
@@ -778,6 +879,13 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
cache.clear();
}
+ /**
+ * Clears single item from cache
+ */
+ public void clearCache(long taskId) {
+ cache.remove(taskId);
+ }
+
// --- internal goodies
/**
diff --git a/astrid/src/com/todoroo/astrid/api/DetailExposer.java b/astrid/src/com/todoroo/astrid/api/DetailExposer.java
deleted file mode 100644
index 90ef95b9d..000000000
--- a/astrid/src/com/todoroo/astrid/api/DetailExposer.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.todoroo.astrid.api;
-
-import android.content.Context;
-
-/**
- * Internal API for Task Details
- *
- * @author Tim Su
- *
- */
-public interface DetailExposer {
-
- /**
- * @param id
- * task id
- * @param extended
- * whether this request is for extended details (which are
- * displayed when user presses a task), or standard (which are
- * always displayed)
- * @return null if no details, or task details
- */
- public String getTaskDetails(Context context, long id, boolean extended);
-
- public String getPluginIdentifier();
-
-}
diff --git a/astrid/src/com/todoroo/astrid/dao/Database.java b/astrid/src/com/todoroo/astrid/dao/Database.java
index 6bef32257..949845695 100644
--- a/astrid/src/com/todoroo/astrid/dao/Database.java
+++ b/astrid/src/com/todoroo/astrid/dao/Database.java
@@ -28,7 +28,7 @@ public class Database extends AbstractDatabase {
* Database version number. This variable must be updated when database
* tables are updated, as it determines whether a database needs updating.
*/
- public static final int VERSION = 4;
+ public static final int VERSION = 5;
/**
* Database name (must be unique)
@@ -119,6 +119,10 @@ public class Database extends AbstractDatabase {
append(')');
database.execSQL(sql.toString());
}
+ case 4: {
+ database.execSQL("ALTER TABLE " + Task.TABLE.name + " ADD " +
+ Task.DETAILS.accept(visitor, null));
+ }
return true;
}
diff --git a/astrid/src/com/todoroo/astrid/dao/TaskDao.java b/astrid/src/com/todoroo/astrid/dao/TaskDao.java
index 6298f9af6..490525359 100644
--- a/astrid/src/com/todoroo/astrid/dao/TaskDao.java
+++ b/astrid/src/com/todoroo/astrid/dao/TaskDao.java
@@ -157,6 +157,10 @@ public class TaskDao extends GenericDao {
return false;
}
+ // clear task detail cache
+ if(values != null && !values.containsKey(Task.DETAILS.name))
+ values.put(Task.DETAILS.name, ""); //$NON-NLS-1$
+
if (task.getId() == Task.NO_ID) {
saveSuccessful = createNew(task);
} else {
diff --git a/astrid/src/com/todoroo/astrid/model/Task.java b/astrid/src/com/todoroo/astrid/model/Task.java
index b0130614d..913987ac9 100644
--- a/astrid/src/com/todoroo/astrid/model/Task.java
+++ b/astrid/src/com/todoroo/astrid/model/Task.java
@@ -14,11 +14,11 @@ import android.content.res.Resources;
import com.timsu.astrid.R;
import com.todoroo.andlib.data.AbstractModel;
import com.todoroo.andlib.data.Property;
+import com.todoroo.andlib.data.Table;
+import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.data.Property.IntegerProperty;
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;
import com.todoroo.andlib.utility.DateUtilities;
/**
@@ -72,7 +72,13 @@ public final class Task extends AbstractModel {
public static final LongProperty DELETION_DATE = new LongProperty(
TABLE, "deleted");
- // --- for migration purposes from astrid 2 (eventually we will want to
+ /** Cached Details Column - built from add-on detail exposers. A null
+ * value means there is no value in the cache and it needs to be
+ * refreshed */
+ public static final StringProperty DETAILS = new StringProperty(
+ TABLE, "details");
+
+ // --- for migration purposes from astrid 2 (eventually we may want to
// move these into the metadata table and treat them as plug-ins
public static final StringProperty NOTES = new StringProperty(
@@ -179,6 +185,7 @@ public final class Task extends AbstractModel {
defaultValues.put(NOTES.name, "");
defaultValues.put(FLAGS.name, 0);
defaultValues.put(TIMER_START.name, 0);
+ defaultValues.put(DETAILS.name, (String)null);
}
@Override
@@ -208,7 +215,7 @@ public final class Task extends AbstractModel {
// --- parcelable helpers
- private static final Creator CREATOR = new ModelCreator(Task.class);
+ public static final Creator CREATOR = new ModelCreator(Task.class);
@Override
protected Creator extends AbstractModel> getCreator() {
@@ -391,6 +398,8 @@ public final class Task extends AbstractModel {
* Checks whether this due date has a due time or only a date
*/
public boolean hasDueTime() {
+ if(!hasDueDate())
+ return false;
return hasDueTime(getValue(Task.DUE_DATE));
}
diff --git a/astrid/src/com/todoroo/astrid/service/TaskService.java b/astrid/src/com/todoroo/astrid/service/TaskService.java
index 96ef546a9..91e412f55 100644
--- a/astrid/src/com/todoroo/astrid/service/TaskService.java
+++ b/astrid/src/com/todoroo/astrid/service/TaskService.java
@@ -1,5 +1,7 @@
package com.todoroo.astrid.service;
+import android.content.ContentValues;
+
import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired;
@@ -12,8 +14,8 @@ import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.api.PermaSql;
import com.todoroo.astrid.dao.MetadataDao;
-import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
import com.todoroo.astrid.dao.TaskDao;
+import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.model.Metadata;
import com.todoroo.astrid.model.Task;
@@ -132,6 +134,15 @@ public class TaskService {
}
}
+ /**
+ * Permanently delete the given task.
+ *
+ * @param model
+ */
+ public void purge(long taskId) {
+ taskDao.delete(taskId);
+ }
+
/**
* Clean up tasks. Typically called on startup
*/
@@ -212,6 +223,18 @@ public class TaskService {
}
}
+ /**
+ * Clear details cache. Useful if user performs some operation that
+ * affects details
+ *
+ * @return # of affected rows
+ */
+ public int clearDetails() {
+ ContentValues values = new ContentValues();
+ values.put(Task.DETAILS.name, (String) null);
+ return taskDao.updateMultiple(values, Criterion.all);
+ }
+
/**
* Update database based on selection and values
* @param selection
diff --git a/astrid/src/com/todoroo/astrid/utility/Constants.java b/astrid/src/com/todoroo/astrid/utility/Constants.java
index 34aa19312..ddd4ec295 100644
--- a/astrid/src/com/todoroo/astrid/utility/Constants.java
+++ b/astrid/src/com/todoroo/astrid/utility/Constants.java
@@ -53,4 +53,7 @@ public final class Constants {
/** Notification Manager id for locale */
public static final int NOTIFICATION_LOCALE = -3;
+ /** Notification Manager id for producteev notifications*/
+ public static final int NOTIFICATION_PRODUCTEEV_NOTIFICATIONS = -4;
+
}