diff --git a/astrid/AndroidManifest.xml b/astrid/AndroidManifest.xml index 452ff2be2..16bf4d4ec 100644 --- a/astrid/AndroidManifest.xml +++ b/astrid/AndroidManifest.xml @@ -54,7 +54,7 @@ + android:label="@string/app_name" android:debuggable="true"> @@ -337,11 +337,7 @@ - - - - + android:theme="@style/Theme" /> diff --git a/astrid/common-src/com/todoroo/andlib/data/AbstractModel.java b/astrid/common-src/com/todoroo/andlib/data/AbstractModel.java index 7bba4e675..cd3a3f6db 100644 --- a/astrid/common-src/com/todoroo/andlib/data/AbstractModel.java +++ b/astrid/common-src/com/todoroo/andlib/data/AbstractModel.java @@ -338,7 +338,12 @@ public abstract class AbstractModel implements Parcelable { public synchronized void save(Property property, ContentValues newStore, Object value) { this.store = newStore; - property.accept(this, value); + + // we don't allow null values, as they indicate unset properties + // when the database was written + + if(value != null) + property.accept(this, value); } public Void visitDouble(Property property, Object value) { diff --git a/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevControlSet.java b/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevControlSet.java index 86e6de628..26e7feeac 100644 --- a/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevControlSet.java +++ b/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevControlSet.java @@ -59,6 +59,8 @@ public class ProducteevControlSet implements TaskEditControlSet { @Autowired MetadataService metadataService; + private int lastDashboardSelection = 0; + public ProducteevControlSet(final Activity activity, ViewGroup parent) { DependencyInjectionService.getInstance().inject(this); @@ -92,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) { @@ -127,6 +129,7 @@ public class ProducteevControlSet implements TaskEditControlSet { @Override public void onClick(DialogInterface dialog, int which) { dialog.cancel(); + dashboardSelector.setSelection(lastDashboardSelection); } }; dialogUtilites.viewDialog(ProducteevControlSet.this.activity, @@ -136,6 +139,7 @@ public class ProducteevControlSet implements TaskEditControlSet { cancelListener); } else { refreshResponsibleSpinner(dashboard.getUsers()); + lastDashboardSelection = position; } } diff --git a/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevDetailExposer.java b/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevDetailExposer.java index 5362eaa45..545a3f822 100644 --- a/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevDetailExposer.java +++ b/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevDetailExposer.java @@ -7,6 +7,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import com.timsu.astrid.R; import com.todoroo.andlib.data.TodorooCursor; import com.todoroo.astrid.adapter.TaskAdapter; import com.todoroo.astrid.api.AstridApiConstants; @@ -64,6 +65,9 @@ public class ProducteevDetailExposer extends BroadcastReceiver implements Detail long responsibleId = -1; if(metadata.containsNonNullValue(ProducteevTask.RESPONSIBLE_ID)) responsibleId = metadata.getValue(ProducteevTask.RESPONSIBLE_ID); + long creatorId = -1; + if(metadata.containsNonNullValue(ProducteevTask.CREATOR_ID)) + creatorId = metadata.getValue(ProducteevTask.CREATOR_ID); // display dashboard if not "no sync" or "default" StoreObject ownerDashboard = null; @@ -86,14 +90,21 @@ public class ProducteevDetailExposer extends BroadcastReceiver implements Detail // display responsible user if not current one if(responsibleId > 0 && ownerDashboard != null && responsibleId != Preferences.getLong(ProducteevUtilities.PREF_USER_ID, 0L)) { - String users = ";" + ownerDashboard.getValue(ProducteevDashboard.USERS); //$NON-NLS-1$ - int index = users.indexOf(";" + responsibleId + ","); //$NON-NLS-1$ //$NON-NLS-2$ - if(index > -1) { - String user = users.substring(users.indexOf(',', index) + 1, - users.indexOf(';', index + 1)); + String user = getUserFromDashboard(ownerDashboard, responsibleId); + if(user != null) builder.append(" ").append(user).append(TaskAdapter.DETAIL_SEPARATOR); //$NON-NLS-1$ - } } + + // display creator user if not the current one + if(creatorId > 0 && ownerDashboard != null && creatorId != + Preferences.getLong(ProducteevUtilities.PREF_USER_ID, 0L)) { + String user = getUserFromDashboard(ownerDashboard, creatorId); + if(user != null) + builder.append(" ").append( //$NON-NLS-1$ + context.getString(R.string.producteev_PDE_task_from, user)). + append(TaskAdapter.DETAIL_SEPARATOR); + } + } else { TodorooCursor notesCursor = ProducteevDataService.getInstance().getTaskNotesCursor(id); try { @@ -112,6 +123,16 @@ public class ProducteevDetailExposer extends BroadcastReceiver implements Detail return result.substring(0, result.length() - TaskAdapter.DETAIL_SEPARATOR.length()); } + /** Try and find user in the dashboard. return null if un-findable */ + private String getUserFromDashboard(StoreObject dashboard, long userId) { + String users = ";" + dashboard.getValue(ProducteevDashboard.USERS); //$NON-NLS-1$ + int index = users.indexOf(";" + userId + ","); //$NON-NLS-1$ //$NON-NLS-2$ + if(index > -1) + return users.substring(users.indexOf(',', index) + 1, + users.indexOf(';', index + 1)); + 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..9d2995f40 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 /** @@ -374,44 +399,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/ProducteevSyncProvider.java b/astrid/plugin-src/com/todoroo/astrid/producteev/sync/ProducteevSyncProvider.java index a1558ba1f..b4d7f5834 100644 --- a/astrid/plugin-src/com/todoroo/astrid/producteev/sync/ProducteevSyncProvider.java +++ b/astrid/plugin-src/com/todoroo/astrid/producteev/sync/ProducteevSyncProvider.java @@ -27,15 +27,19 @@ 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.model.Metadata; import com.todoroo.astrid.model.StoreObject; import com.todoroo.astrid.model.Task; +import com.todoroo.astrid.producteev.ProducteevFilterExposer; import com.todoroo.astrid.producteev.ProducteevLoginActivity; import com.todoroo.astrid.producteev.ProducteevPreferences; import com.todoroo.astrid.producteev.ProducteevUtilities; @@ -46,6 +50,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") @@ -85,6 +90,9 @@ public class ProducteevSyncProvider extends SyncProvider notificationsList = parseActivities(notifications); + // update lastIds + if (notifications.length() > 0) { + lastNotificationId = ""+notifications.getJSONObject(0).getJSONObject("activity").getLong("id_activity"); + } + +// JSONArray activities = invoker.activitiesShowActivities(null, (lastActivityId == 0 ? null : new Long(lastActivityId))); +// int activitiesCount = (activities == null ? 0 : activities.length()); +// ArrayList activitiesList = parseActivities(activities); +// // update lastIds +// if (activities.length() > 0) { +// lastActivityId = activities.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.size(); 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 @@ -255,6 +324,21 @@ public class ProducteevSyncProvider extends SyncProvider - - - + + Producteev @@ -15,6 +15,12 @@ Assigned To \'%s\' + + + from %s + + + Add a Comment @@ -33,10 +39,7 @@ Name for new Workspace - - new - - + Default Workspace @@ -89,9 +92,12 @@ - + Astrid: Producteev + + %s tasks updated / click for more details + Connection Error! Check your Internet connection. diff --git a/astrid/src/com/todoroo/astrid/activity/TaskEditActivity.java b/astrid/src/com/todoroo/astrid/activity/TaskEditActivity.java index f52d5020b..0f8d2881d 100644 --- a/astrid/src/com/todoroo/astrid/activity/TaskEditActivity.java +++ b/astrid/src/com/todoroo/astrid/activity/TaskEditActivity.java @@ -58,6 +58,7 @@ import android.widget.LinearLayout; import android.widget.RemoteViews; import android.widget.Spinner; import android.widget.TabHost; +import android.widget.TextView; import android.widget.TimePicker; import android.widget.Toast; import android.widget.ToggleButton; @@ -253,6 +254,8 @@ public final class TaskEditActivity extends TabActivity { AddOn producteevAddon = addOnService.getAddOn(AddOnService.PRODUCTEEV_PACKAGE, "Producteev"); //$NON-NLS-1$ if (addOnService.isInstalled(producteevAddon) && ProducteevUtilities.INSTANCE.isLoggedIn()) { controls.add(new ProducteevControlSet(this, addonsAddons)); + ((TextView)findViewById(R.id.notes)).setHint(R.string.producteev_TEA_notes); + ((TextView)findViewById(R.id.notes_label)).setHint(R.string.producteev_TEA_notes); } } catch (Exception e) { Log.e("astrid-error", "loading-control-set", e); //$NON-NLS-1$ //$NON-NLS-2$ @@ -790,7 +793,7 @@ public final class TaskEditActivity extends TabActivity { Task.URGENCY_TOMORROW); String dayAfterTomorrow = DateUtils.getDayOfWeekString( new Date(DateUtilities.now() + 2 * DateUtilities.ONE_DAY).getDay() + - Calendar.SUNDAY, 0); + Calendar.SUNDAY, DateUtils.FORMAT_ABBREV_ALL); urgencyValues[3] = new UrgencyValue(dayAfterTomorrow, Task.URGENCY_DAY_AFTER); urgencyValues[4] = new UrgencyValue(labels[4], diff --git a/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java b/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java index 2bbba4b56..a574089ca 100644 --- a/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java +++ b/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java @@ -78,6 +78,7 @@ import com.todoroo.astrid.reminders.ReminderService; import com.todoroo.astrid.reminders.ReminderService.AlarmScheduler; import com.todoroo.astrid.rmilk.MilkPreferences; import com.todoroo.astrid.service.AddOnService; +import com.todoroo.astrid.service.AstridDependencyInjector; import com.todoroo.astrid.service.MetadataService; import com.todoroo.astrid.service.StartupService; import com.todoroo.astrid.service.TaskService; @@ -161,6 +162,10 @@ public class TaskListActivity extends ListActivity implements OnScrollListener, * ======================================================= initialization * ====================================================================== */ + static { + AstridDependencyInjector.initialize(); + } + public TaskListActivity() { DependencyInjectionService.getInstance().inject(this); } diff --git a/astrid/src/com/todoroo/astrid/utility/Constants.java b/astrid/src/com/todoroo/astrid/utility/Constants.java index e700e0eaa..5d3cc181d 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; + } diff --git a/astrid/src/com/todoroo/astrid/widget/TasksWidget.java b/astrid/src/com/todoroo/astrid/widget/TasksWidget.java index 388f10984..3f18083ec 100644 --- a/astrid/src/com/todoroo/astrid/widget/TasksWidget.java +++ b/astrid/src/com/todoroo/astrid/widget/TasksWidget.java @@ -51,12 +51,13 @@ public class TasksWidget extends AppWidgetProvider { int[] appWidgetIds) { try { + ContextManager.setContext(context); super.onUpdate(context, appWidgetManager, appWidgetIds); // Start in service to prevent Application Not Responding timeout updateWidgets(context); - } catch (SecurityException e) { - // :( + } catch (Exception e) { + Log.e("astrid-update-widget", "widget update error", e); //$NON-NLS-1$ } } @@ -65,7 +66,7 @@ public class TasksWidget extends AppWidgetProvider { * @param id */ public static void updateWidgets(Context context) { - context.startService(new Intent(ContextManager.getContext(), + context.startService(new Intent(context, TasksWidget.UpdateService.class)); } @@ -178,7 +179,7 @@ public class TasksWidget extends AppWidgetProvider { } Intent listIntent = new Intent(context, TaskListActivity.class); - listIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + listIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); if(filter != null) { listIntent.putExtra(TaskListActivity.TOKEN_FILTER, filter); listIntent.setType(filter.sqlQuery); @@ -188,7 +189,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 | Intent.FLAG_ACTIVITY_CLEAR_TOP); + editIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); if(filter != null && filter.valuesForNewTasks != null) { String values = AndroidUtilities.contentValuesToSerializedString(filter.valuesForNewTasks); editIntent.putExtra(TaskEditActivity.TOKEN_VALUES, values);