Adding better error reporting facilities via flurry, plus a few more flurry events.

pull/14/head
Tim Su 17 years ago
parent 349e50c6c4
commit eae64b19e9

@ -90,7 +90,7 @@ public class Invoker {
public static final String API_SIG_PARAM = "api_sig"; public static final String API_SIG_PARAM = "api_sig";
public static final long INVOCATION_INTERVAL = 300; public static final long INVOCATION_INTERVAL = 400;
private long lastInvocation; private long lastInvocation;
@ -108,9 +108,10 @@ public class Invoker {
private HttpClient httpClient; private HttpClient httpClient;
public Invoker(String serverHostName, int serverPortNumber, String serviceRelativeUri, ApplicationInfo applicationInfo) public Invoker(String serverHostName, int serverPortNumber,
throws ServiceInternalException String serviceRelativeUri, ApplicationInfo applicationInfo)
{ throws ServiceInternalException {
this.serviceRelativeUri = serviceRelativeUri; this.serviceRelativeUri = serviceRelativeUri;
new HttpHost(serverHostName, serverPortNumber); new HttpHost(serverHostName, serverPortNumber);
globalHttpParams = new BasicHttpParams(); globalHttpParams = new BasicHttpParams();
@ -134,33 +135,28 @@ public class Invoker {
lastInvocation = System.currentTimeMillis(); lastInvocation = System.currentTimeMillis();
this.applicationInfo = applicationInfo; this.applicationInfo = applicationInfo;
try try {
{
digest = MessageDigest.getInstance("md5"); digest = MessageDigest.getInstance("md5");
} } catch (NoSuchAlgorithmException e) {
catch (NoSuchAlgorithmException e) throw new ServiceInternalException(
{ "Could not create properly the MD5 digest", e);
throw new ServiceInternalException("Could not create properly the MD5 digest", e);
} }
} }
private StringBuffer computeRequestUri(Param... params) private StringBuffer computeRequestUri(Param... params)
throws ServiceInternalException throws ServiceInternalException {
{
final StringBuffer requestUri = new StringBuffer(serviceRelativeUri); final StringBuffer requestUri = new StringBuffer(serviceRelativeUri);
if (params.length > 0) if (params.length > 0) {
{
requestUri.append("?"); requestUri.append("?");
} }
for (Param param : params) for (Param param : params) {
{ try {
try requestUri.append(param.getName()).append("=").append(
{ URLEncoder.encode(param.getValue(), ENCODING)).append(
requestUri.append(param.getName()).append("=").append(URLEncoder.encode(param.getValue(), ENCODING)).append("&"); "&");
} } catch (Exception exception) {
catch (Exception exception) final StringBuffer message = new StringBuffer(
{ "Cannot encode properly the HTTP GET request URI: cannot execute query");
final StringBuffer message = new StringBuffer("Cannot encode properly the HTTP GET request URI: cannot execute query");
Log.e(TAG, message.toString(), exception); Log.e(TAG, message.toString(), exception);
throw new ServiceInternalException(message.toString()); throw new ServiceInternalException(message.toString());
} }
@ -175,46 +171,38 @@ public class Invoker {
} }
public Element invoke(boolean repeat, Param... params) public Element invoke(boolean repeat, Param... params)
throws ServiceException throws ServiceException {
{ long timeSinceLastInvocation = System.currentTimeMillis() -
long timeSinceLastInvocation = System.currentTimeMillis() - lastInvocation; lastInvocation;
if (timeSinceLastInvocation < INVOCATION_INTERVAL) if (timeSinceLastInvocation < INVOCATION_INTERVAL) {
{
// In order not to invoke the RTM service too often // In order not to invoke the RTM service too often
try try {
{
Thread.sleep(INVOCATION_INTERVAL - timeSinceLastInvocation); Thread.sleep(INVOCATION_INTERVAL - timeSinceLastInvocation);
} } catch (InterruptedException e) {
catch (InterruptedException e) return null;
{
throw new ServiceInternalException("Unexpected interruption while attempting to pause for some time before invoking the RTM service back", e);
} }
} }
// We prepare the network socket-based connection
//prepareConnection();
// We compute the URI // We compute the URI
final StringBuffer requestUri = computeRequestUri(params); final StringBuffer requestUri = computeRequestUri(params);
HttpResponse response = null; HttpResponse response = null;
final HttpGet request = new HttpGet("http://" + ServiceImpl.SERVER_HOST_NAME + requestUri.toString()); final HttpGet request = new HttpGet("http://"
+ ServiceImpl.SERVER_HOST_NAME + requestUri.toString());
request.setHeader(basicHeader); request.setHeader(basicHeader);
final String methodUri = request.getRequestLine().getUri(); final String methodUri = request.getRequestLine().getUri();
Element result; Element result;
try try {
{
Log.i(TAG, "Executing the method:" + methodUri); Log.i(TAG, "Executing the method:" + methodUri);
response = httpClient.execute(request); response = httpClient.execute(request);
// httpExecutor.execute(request, connection, context);
final int statusCode = response.getStatusLine().getStatusCode(); final int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) if (statusCode != HttpStatus.SC_OK) {
{
Log.e(TAG, "Method failed: " + response.getStatusLine()); Log.e(TAG, "Method failed: " + response.getStatusLine());
// Tim: HTTP error. Let's wait a little bit // Tim: HTTP error. Let's wait a little bit
if(!repeat) { if (!repeat) {
try { try {
Thread.sleep(1500); Thread.sleep(1500);
} catch (InterruptedException e) { } catch (InterruptedException e) {
@ -223,86 +211,68 @@ public class Invoker {
return invoke(true, params); return invoke(true, params);
} }
throw new ServiceInternalException("method failed: " + response.getStatusLine()); throw new ServiceInternalException("method failed: "
+ response.getStatusLine());
} }
// THINK: this method is deprecated, but the only way to get the body as a string, without consuming final Document responseDoc = builder.parse(response.getEntity()
// the body input stream: the HttpMethodBase issues a warning but does not let you call the "setResponseStream()" method! .getContent());
final String responseBodyAsString = "";//EntityUtils.toString(response.getEntity());
// Log.i(TAG, " Invocation response:\r\n" + responseBodyAsString);
final Document responseDoc = builder.parse(response.getEntity().getContent());
final Element wrapperElt = responseDoc.getDocumentElement(); final Element wrapperElt = responseDoc.getDocumentElement();
if (!wrapperElt.getNodeName().equals("rsp")) if (!wrapperElt.getNodeName().equals("rsp")) {
{ throw new ServiceInternalException(
throw new ServiceInternalException("unexpected response returned by RTM service: " + responseBodyAsString); "unexpected response returned by RTM service: "
} + wrapperElt.getNodeName());
else } else {
{
String stat = wrapperElt.getAttribute("stat"); String stat = wrapperElt.getAttribute("stat");
if (stat.equals("fail")) if (stat.equals("fail")) {
{
Node errElt = wrapperElt.getFirstChild(); Node errElt = wrapperElt.getFirstChild();
while (errElt != null && (errElt.getNodeType() != Node.ELEMENT_NODE || !errElt.getNodeName().equals("err"))) while (errElt != null
{ && (errElt.getNodeType() != Node.ELEMENT_NODE || !errElt
.getNodeName().equals("err"))) {
errElt = errElt.getNextSibling(); errElt = errElt.getNextSibling();
} }
if (errElt == null) if (errElt == null) {
{ throw new ServiceInternalException(
throw new ServiceInternalException("unexpected response returned by RTM service: " + responseBodyAsString); "unexpected response returned by RTM service: "
} + wrapperElt.getNodeValue());
else } else {
{ throw new ServiceException(Integer
throw new ServiceException(Integer.parseInt(((Element) errElt).getAttribute("code")), ((Element) errElt).getAttribute("msg")); .parseInt(((Element) errElt)
} .getAttribute("code")),
} ((Element) errElt).getAttribute("msg"));
else }
{ } else {
Node dataElt = wrapperElt.getFirstChild(); Node dataElt = wrapperElt.getFirstChild();
while (dataElt != null && (dataElt.getNodeType() != Node.ELEMENT_NODE || dataElt.getNodeName().equals("transaction") == true)) while (dataElt != null
{ && (dataElt.getNodeType() != Node.ELEMENT_NODE || dataElt
try .getNodeName().equals("transaction") == true)) {
{ try {
Node nextSibling = dataElt.getNextSibling(); Node nextSibling = dataElt.getNextSibling();
if (nextSibling == null) if (nextSibling == null) {
{
break; break;
} } else {
else
{
dataElt = nextSibling; dataElt = nextSibling;
} }
} } catch (IndexOutOfBoundsException exception) {
catch (IndexOutOfBoundsException exception) // Some implementation may throw this exception,
{ // instead of returning a null sibling
// Some implementation may throw this exception, instead of returning a null sibling
break; break;
} }
} }
if (dataElt == null) if (dataElt == null) {
{ throw new ServiceInternalException(
throw new ServiceInternalException("unexpected response returned by RTM service: " + responseBodyAsString); "unexpected response returned by RTM service: "
} + wrapperElt.getNodeValue());
else } else {
{
result = (Element) dataElt; result = (Element) dataElt;
} }
} }
} }
} } catch (IOException e) {
catch (IOException e)
{
throw new ServiceInternalException("Connection error", e); throw new ServiceInternalException("Connection error", e);
} } catch (SAXException e) {
catch (SAXException e)
{
throw new ServiceInternalException("XML Parse Exception", e); throw new ServiceInternalException("XML Parse Exception", e);
} } finally {
// catch (HttpException e)
// {
// throw new ServiceInternalException("", e);
// }
finally
{
httpClient.getConnectionManager().closeExpiredConnections(); httpClient.getConnectionManager().closeExpiredConnections();
} }
@ -310,44 +280,35 @@ public class Invoker {
return result; return result;
} }
final String calcApiSig(Param... params) final String calcApiSig(Param... params) throws ServiceInternalException {
throws ServiceInternalException try {
{
try
{
digest.reset(); digest.reset();
digest.update(applicationInfo.getSharedSecret().getBytes(ENCODING)); digest.update(applicationInfo.getSharedSecret().getBytes(ENCODING));
List<Param> sorted = Arrays.asList(params); List<Param> sorted = Arrays.asList(params);
Collections.sort(sorted); Collections.sort(sorted);
for (Param param : sorted) for (Param param : sorted) {
{
digest.update(param.getName().getBytes(ENCODING)); digest.update(param.getName().getBytes(ENCODING));
digest.update(param.getValue().getBytes(ENCODING)); digest.update(param.getValue().getBytes(ENCODING));
} }
return convertToHex(digest.digest()); return convertToHex(digest.digest());
} } catch (UnsupportedEncodingException e) {
catch (UnsupportedEncodingException e) throw new ServiceInternalException(
{ "cannot hahdle properly the encoding", e);
throw new ServiceInternalException("cannot hahdle properly the encoding", e);
} }
} }
private static String convertToHex(byte[] data) private static String convertToHex(byte[] data) {
{
StringBuffer buf = new StringBuffer(); StringBuffer buf = new StringBuffer();
for (int i = 0; i < data.length; i++) for (int i = 0; i < data.length; i++) {
{
int halfbyte = (data[i] >>> 4) & 0x0F; int halfbyte = (data[i] >>> 4) & 0x0F;
int two_halfs = 0; int two_halfs = 0;
do do {
{
if ((0 <= halfbyte) && (halfbyte <= 9)) if ((0 <= halfbyte) && (halfbyte <= 9))
buf.append((char) ('0' + halfbyte)); buf.append((char) ('0' + halfbyte));
else else
buf.append((char) ('a' + (halfbyte - 10))); buf.append((char) ('a' + (halfbyte - 10)));
halfbyte = data[i] & 0x0F; halfbyte = data[i] & 0x0F;
} } while (two_halfs++ < 1);
while (two_halfs++ < 1);
} }
return buf.toString(); return buf.toString();
} }

@ -55,6 +55,7 @@ import android.widget.Toast;
import android.widget.AdapterView.AdapterContextMenuInfo; import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemClickListener;
import com.flurry.android.FlurryAgent;
import com.timsu.astrid.R; import com.timsu.astrid.R;
import com.timsu.astrid.data.tag.TagIdentifier; import com.timsu.astrid.data.tag.TagIdentifier;
import com.timsu.astrid.data.tag.TagModelForView; import com.timsu.astrid.data.tag.TagModelForView;
@ -104,6 +105,8 @@ public class TagListSubActivity extends SubActivity {
fillData(); fillData();
} }
}).start(); }).start();
FlurryAgent.onEvent("view-tags");
} }
// --- stuff for sorting // --- stuff for sorting

@ -18,6 +18,7 @@
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ */
package com.timsu.astrid.activities; package com.timsu.astrid.activities;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.Date; import java.util.Date;
@ -148,6 +149,7 @@ public class TaskListSubActivity extends SubActivity {
TagModelForView filterTag = null; TagModelForView filterTag = null;
CharSequence windowTitle; CharSequence windowTitle;
} }
Handler handler = null; Handler handler = null;
Long selectedTaskId = null; Long selectedTaskId = null;
Runnable reLoadRunnable = null; Runnable reLoadRunnable = null;
@ -159,19 +161,21 @@ public class TaskListSubActivity extends SubActivity {
private static SortMode sortMode = SortMode.AUTO; private static SortMode sortMode = SortMode.AUTO;
private static boolean sortReverse = false; private static boolean sortReverse = false;
/* ====================================================================== /*
* ======================================================================
* ======================================================= initialization * ======================================================= initialization
* ====================================================================== */ * ======================================================================
*/
public TaskListSubActivity(TaskList parent, int code, View view) { public TaskListSubActivity(TaskList parent, int code, View view) {
super(parent, code, view); super(parent, code, view);
} }
@Override @Override
/** Called when loading up the activity */ /* Called when loading up the activity */
public void onDisplay(final Bundle variables) { public void onDisplay(final Bundle variables) {
// process task that's selected, if any // process task that's selected, if any
if(variables != null && variables.containsKey(LOAD_INSTANCE_TOKEN)) { if (variables != null && variables.containsKey(LOAD_INSTANCE_TOKEN)) {
selectedTaskId = variables.getLong(LOAD_INSTANCE_TOKEN); selectedTaskId = variables.getLong(LOAD_INSTANCE_TOKEN);
} else { } else {
selectedTaskId = null; selectedTaskId = null;
@ -184,7 +188,8 @@ public class TaskListSubActivity extends SubActivity {
public void run() { public void run() {
handler.post(new Runnable() { handler.post(new Runnable() {
public void run() { public void run() {
loadingText.setText(getParent().getResources().getString(R.string.updating)); loadingText.setText(getParent().getResources()
.getString(R.string.updating));
} }
}); });
@ -203,19 +208,24 @@ public class TaskListSubActivity extends SubActivity {
} }
context = new TaskListContext(); context = new TaskListContext();
if(selectedTaskId == null) if (selectedTaskId == null)
context.selectedTask = null; context.selectedTask = null;
// process tag to filter, if any (intercept UNTAGGED identifier, if applicable) // process tag to filter, if any (intercept UNTAGGED identifier, if
if(variables != null && variables.containsKey(TAG_TOKEN)) { // applicable)
TagIdentifier identifier = new TagIdentifier(variables.getLong(TAG_TOKEN)); if (variables != null && variables.containsKey(TAG_TOKEN)) {
TagIdentifier identifier = new TagIdentifier(variables
.getLong(TAG_TOKEN));
context.tagMap = getTagController().getAllTagsAsMap(); context.tagMap = getTagController().getAllTagsAsMap();
if(context.tagMap.containsKey(identifier)) if (context.tagMap.containsKey(identifier))
context.filterTag = context.tagMap.get(identifier); context.filterTag = context.tagMap.get(identifier);
else if(identifier.equals(TagModelForView.UNTAGGED_IDENTIFIER)) else if (identifier.equals(TagModelForView.UNTAGGED_IDENTIFIER))
context.filterTag = TagModelForView.getUntaggedModel(); context.filterTag = TagModelForView.getUntaggedModel();
else else
Toast.makeText(getParent(), R.string.missing_tag, Toast.LENGTH_SHORT).show(); Toast.makeText(getParent(), R.string.missing_tag,
Toast.LENGTH_SHORT).show();
FlurryAgent.onEvent("filter-by-tag");
} }
// time to go! creates a thread that loads the task list, then // time to go! creates a thread that loads the task list, then
@ -232,16 +242,18 @@ public class TaskListSubActivity extends SubActivity {
fillData(); fillData();
// open up reminder box // open up reminder box
if(variables != null && variables.containsKey(NOTIF_FLAGS_TOKEN) && if (variables != null
context.selectedTask != null) { && variables.containsKey(NOTIF_FLAGS_TOKEN)
&& context.selectedTask != null) {
FlurryAgent.onEvent("open-notification"); FlurryAgent.onEvent("open-notification");
handler.post(new Runnable() { handler.post(new Runnable() {
public void run() { public void run() {
long repeatInterval = 0; long repeatInterval = 0;
int flags = 0; int flags = 0;
if(variables.containsKey(NOTIF_REPEAT_TOKEN)) if (variables.containsKey(NOTIF_REPEAT_TOKEN))
repeatInterval = variables.getLong(NOTIF_REPEAT_TOKEN); repeatInterval = variables
.getLong(NOTIF_REPEAT_TOKEN);
flags = variables.getInt(NOTIF_FLAGS_TOKEN); flags = variables.getInt(NOTIF_FLAGS_TOKEN);
showNotificationAlert(context.selectedTask, showNotificationAlert(context.selectedTask,
repeatInterval, flags); repeatInterval, flags);
@ -257,22 +269,21 @@ public class TaskListSubActivity extends SubActivity {
public void setupUIComponents() { public void setupUIComponents() {
handler = new Handler(); handler = new Handler();
listView = (ListView)findViewById(R.id.tasklist); listView = (ListView) findViewById(R.id.tasklist);
loadingText = (TextView)findViewById(R.id.loading); loadingText = (TextView) findViewById(R.id.loading);
addButton = (Button)findViewById(R.id.addtask); addButton = (Button) findViewById(R.id.addtask);
addButton.setOnClickListener(new addButton.setOnClickListener(new View.OnClickListener() {
View.OnClickListener() {
public void onClick(View v) { public void onClick(View v) {
createTask(null); createTask(null);
} }
}); });
layout = getView(); layout = getView();
layout.setOnCreateContextMenuListener( layout
new OnCreateContextMenuListener() { .setOnCreateContextMenuListener(new OnCreateContextMenuListener() {
public void onCreateContextMenu(ContextMenu menu, View v, public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) { ContextMenuInfo menuInfo) {
if(menu.hasVisibleItems()) if (menu.hasVisibleItems())
return; return;
onCreateMoreOptionsMenu(menu); onCreateMoreOptionsMenu(menu);
} }
@ -280,7 +291,7 @@ public class TaskListSubActivity extends SubActivity {
} }
@Override @Override
/** Create options menu (displayed when user presses menu key) */ /* Create options menu (displayed when user presses menu key) */
public boolean onPrepareOptionsMenu(Menu menu) { public boolean onPrepareOptionsMenu(Menu menu) {
MenuItem item; MenuItem item;
@ -299,7 +310,7 @@ public class TaskListSubActivity extends SubActivity {
item.setIcon(android.R.drawable.ic_menu_myplaces); item.setIcon(android.R.drawable.ic_menu_myplaces);
item.setAlphabeticShortcut('t'); item.setAlphabeticShortcut('t');
if(Preferences.shouldDisplaySyncButton(getParent())){ if (Preferences.shouldDisplaySyncButton(getParent())) {
item = menu.add(Menu.NONE, SYNC_ID, Menu.NONE, item = menu.add(Menu.NONE, SYNC_ID, Menu.NONE,
R.string.taskList_menu_syncshortcut); R.string.taskList_menu_syncshortcut);
item.setIcon(android.R.drawable.ic_menu_upload); item.setIcon(android.R.drawable.ic_menu_upload);
@ -350,27 +361,29 @@ public class TaskListSubActivity extends SubActivity {
ALPHA { ALPHA {
@Override @Override
int compareTo(TaskModelForList arg0, TaskModelForList arg1) { int compareTo(TaskModelForList arg0, TaskModelForList arg1) {
return arg0.getName().toLowerCase().compareTo(arg1.getName().toLowerCase()); return arg0.getName().toLowerCase().compareTo(
arg1.getName().toLowerCase());
} }
}, },
DUEDATE { DUEDATE {
long getDueDate(TaskModelForList task) { long getDueDate(TaskModelForList task) {
Date definite = task.getDefiniteDueDate(); Date definite = task.getDefiniteDueDate();
Date preferred = task.getPreferredDueDate(); Date preferred = task.getPreferredDueDate();
if(definite != null && preferred != null) { if (definite != null && preferred != null) {
if(preferred.getTime() < System.currentTimeMillis()) if (preferred.getTime() < System.currentTimeMillis())
return definite.getTime(); return definite.getTime();
return preferred.getTime(); return preferred.getTime();
} else if(definite != null) } else if (definite != null)
return definite.getTime(); return definite.getTime();
else if(preferred != null) else if (preferred != null)
return preferred.getTime(); return preferred.getTime();
else else
return new Date(2020,1,1).getTime(); return new Date(2020, 1, 1).getTime();
} }
@Override @Override
int compareTo(TaskModelForList arg0, TaskModelForList arg1) { int compareTo(TaskModelForList arg0, TaskModelForList arg1) {
return (int)((getDueDate(arg0) - getDueDate(arg1))/1000); return (int) ((getDueDate(arg0) - getDueDate(arg1)) / 1000);
} }
}, },
AUTO { AUTO {
@ -383,10 +396,11 @@ public class TaskListSubActivity extends SubActivity {
abstract int compareTo(TaskModelForList arg0, TaskModelForList arg1); abstract int compareTo(TaskModelForList arg0, TaskModelForList arg1);
}; };
/*
/* ====================================================================== * ======================================================================
* ======================================================== notifications * ======================================================== notifications
* ====================================================================== */ * ======================================================================
*/
/** Called when user clicks on a notification to get here */ /** Called when user clicks on a notification to get here */
private void showNotificationAlert(final TaskModelForList task, private void showNotificationAlert(final TaskModelForList task,
@ -394,33 +408,38 @@ public class TaskListSubActivity extends SubActivity {
Resources r = getResources(); Resources r = getResources();
// clear notifications // clear notifications
Notifications.clearAllNotifications(getParent(), task.getTaskIdentifier()); Notifications.clearAllNotifications(getParent(), task
.getTaskIdentifier());
String response; String response;
if(Preferences.shouldShowNags(getParent())) { if (Preferences.shouldShowNags(getParent())) {
String[] responses = r.getStringArray(R.array.reminder_responses); String[] responses = r.getStringArray(R.array.reminder_responses);
response = responses[new Random().nextInt(responses.length)]; response = responses[new Random().nextInt(responses.length)];
} else } else
response = r.getString(R.string.taskList_nonag_reminder); response = r.getString(R.string.taskList_nonag_reminder);
new AlertDialog.Builder(getParent()) new AlertDialog.Builder(getParent()).setTitle(
.setTitle(R.string.taskView_notifyTitle) R.string.taskView_notifyTitle).setMessage(
.setMessage(task.getName() + "\n\n" + response) task.getName() + "\n\n" + response).setIcon(
.setIcon(android.R.drawable.ic_dialog_alert) android.R.drawable.ic_dialog_alert)
// yes, i will do it: just closes this dialog // yes, i will do it: just closes this dialog
.setPositiveButton(R.string.notify_yes, null) .setPositiveButton(R.string.notify_yes, null)
// no, i will ignore: quits application // no, i will ignore: quits application
.setNegativeButton(R.string.notify_no, new DialogInterface.OnClickListener() { .setNegativeButton(R.string.notify_no,
public void onClick(DialogInterface dialog, int which) { new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int which) {
TaskList.shouldCloseInstance = true; TaskList.shouldCloseInstance = true;
closeActivity(); closeActivity();
} }
}) })
// snooze: sets a new temporary alert, closes application // snooze: sets a new temporary alert, closes application
.setNeutralButton(R.string.notify_snooze, new DialogInterface.OnClickListener() { .setNeutralButton(R.string.notify_snooze,
public void onClick(DialogInterface dialog, int which) { new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int which) {
snoozeAlert(task, repeatInterval, flags); snoozeAlert(task, repeatInterval, flags);
} }
}) })
@ -428,8 +447,9 @@ public class TaskListSubActivity extends SubActivity {
.show(); .show();
} }
/** Helper method to "snooze" an alert (i.e. set a new one for some time /**
* from now. * Helper method to "snooze" an alert (i.e. set a new one for some time from
* now.
* *
* @param task * @param task
* @param repeatInterval * @param repeatInterval
@ -437,13 +457,12 @@ public class TaskListSubActivity extends SubActivity {
*/ */
private void snoozeAlert(final TaskModelForList task, private void snoozeAlert(final TaskModelForList task,
final long repeatInterval, final int flags) { final long repeatInterval, final int flags) {
DialogUtilities.hourMinutePicker(getParent(), DialogUtilities.hourMinutePicker(getParent(), getResources().getString(
getResources().getString(R.string.notify_snooze_title), R.string.notify_snooze_title), new OnNNumberPickedListener() {
new OnNNumberPickedListener() {
public void onNumbersPicked(int[] values) { public void onNumbersPicked(int[] values) {
int snoozeSeconds = values[0] * 3600 + values[1] * 60; int snoozeSeconds = values[0] * 3600 + values[1] * 60;
Notifications.createSnoozeAlarm(getParent(), Notifications.createSnoozeAlarm(getParent(), task
task.getTaskIdentifier(), snoozeSeconds, flags, .getTaskIdentifier(), snoozeSeconds, flags,
repeatInterval); repeatInterval);
TaskList.shouldCloseInstance = true; TaskList.shouldCloseInstance = true;
@ -452,20 +471,23 @@ public class TaskListSubActivity extends SubActivity {
}); });
} }
/* ====================================================================== /*
* ======================================================================
* ====================================================== populating list * ====================================================== populating list
* ====================================================================== */ * ======================================================================
*/
/** Helper method returns true if the task is considered 'hidden' */ /** Helper method returns true if the task is considered 'hidden' */
private boolean isTaskHidden(TaskModelForList task) { private boolean isTaskHidden(TaskModelForList task) {
if(task == context.selectedTask) if (task == context.selectedTask)
return false; return false;
if(task.isHidden()) if (task.isHidden())
return true; return true;
if(context.filterTag == null) { if (context.filterTag == null) {
if(context.taskTags.get(task).contains(TagModelForView.HIDDEN_FROM_MAIN_LIST_PREFIX)) if (context.taskTags.get(task).contains(
TagModelForView.HIDDEN_FROM_MAIN_LIST_PREFIX))
return true; return true;
} }
@ -486,7 +508,7 @@ public class TaskListSubActivity extends SubActivity {
try { try {
// get a cursor to the task list // get a cursor to the task list
Cursor tasksCursor; Cursor tasksCursor;
if(context.filterTag != null) { // Filter by TAG if (context.filterTag != null) { // Filter by TAG
LinkedList<TaskIdentifier> tasks; LinkedList<TaskIdentifier> tasks;
// Check "named" Tag vs. "Untagged" // Check "named" Tag vs. "Untagged"
@ -499,52 +521,55 @@ public class TaskListSubActivity extends SubActivity {
tasksCursor = getTaskController().getTaskListCursorById(tasks); tasksCursor = getTaskController().getTaskListCursorById(tasks);
} else { } else {
if(filterShowDone) if (filterShowDone)
tasksCursor = getTaskController().getAllTaskListCursor(); tasksCursor = getTaskController().getAllTaskListCursor();
else else
tasksCursor = getTaskController().getActiveTaskListCursor(); tasksCursor = getTaskController().getActiveTaskListCursor();
} }
startManagingCursor(tasksCursor); startManagingCursor(tasksCursor);
context.taskArray = Collections.synchronizedList(getTaskController(). context.taskArray = Collections
createTaskListFromCursor(tasksCursor)); .synchronizedList(getTaskController()
.createTaskListFromCursor(tasksCursor));
// read tags and apply filters // read tags and apply filters
context.tagMap = getTagController().getAllTagsAsMap(); context.tagMap = getTagController().getAllTagsAsMap();
context.taskTags = new HashMap<TaskModelForList, String>(); context.taskTags = new HashMap<TaskModelForList, String>();
StringBuilder tagBuilder = new StringBuilder(); StringBuilder tagBuilder = new StringBuilder();
context.tasksById = new HashMap<Long, TaskModelForList>(); context.tasksById = new HashMap<Long, TaskModelForList>();
for(Iterator<TaskModelForList> i = context.taskArray.iterator(); i.hasNext();) { for (Iterator<TaskModelForList> i = context.taskArray.iterator(); i
if(Thread.interrupted()) .hasNext();) {
if (Thread.interrupted())
return; return;
TaskModelForList task = i.next(); TaskModelForList task = i.next();
if(!filterShowDone) { if (!filterShowDone) {
if(task.isTaskCompleted()) { if (task.isTaskCompleted()) {
i.remove(); i.remove();
continue; continue;
} }
} }
if(selectedTaskId != null && task.getTaskIdentifier().getId() == selectedTaskId) { if (selectedTaskId != null
&& task.getTaskIdentifier().getId() == selectedTaskId) {
context.selectedTask = task; context.selectedTask = task;
} }
// get list of tags // get list of tags
LinkedList<TagIdentifier> tagIds = getTagController().getTaskTags( LinkedList<TagIdentifier> tagIds = getTagController()
task.getTaskIdentifier()); .getTaskTags(task.getTaskIdentifier());
tagBuilder.delete(0, tagBuilder.length()); tagBuilder.delete(0, tagBuilder.length());
for(Iterator<TagIdentifier> j = tagIds.iterator(); j.hasNext(); ) { for (Iterator<TagIdentifier> j = tagIds.iterator(); j.hasNext();) {
TagModelForView tag = context.tagMap.get(j.next()); TagModelForView tag = context.tagMap.get(j.next());
tagBuilder.append(tag.getName()); tagBuilder.append(tag.getName());
if(j.hasNext()) if (j.hasNext())
tagBuilder.append(", "); tagBuilder.append(", ");
} }
context.taskTags.put(task, tagBuilder.toString()); context.taskTags.put(task, tagBuilder.toString());
// hide hidden // hide hidden
if(!filterShowHidden) { if (!filterShowHidden) {
if(isTaskHidden(task)) { if (isTaskHidden(task)) {
hiddenTasks++; hiddenTasks++;
i.remove(); i.remove();
continue; continue;
@ -553,7 +578,7 @@ public class TaskListSubActivity extends SubActivity {
context.tasksById.put(task.getTaskIdentifier().getId(), task); context.tasksById.put(task.getTaskIdentifier().getId(), task);
if(task.isTaskCompleted()) if (task.isTaskCompleted())
completedTasks++; completedTasks++;
} }
@ -567,23 +592,23 @@ public class TaskListSubActivity extends SubActivity {
Log.w("astrid", "StaleDataException", e); Log.w("astrid", "StaleDataException", e);
return; return;
} catch (final IllegalStateException e) { } catch (final IllegalStateException e) {
FlurryAgent.onError("task-list-error", e.toString(), FlurryAgent.onError("task-list-error", e.toString(), e.getClass()
e.getClass().getSimpleName()); .getSimpleName());
// happens when you run out of memory usually // happens when you run out of memory usually
Log.e("astrid", "Error loading task list", e); Log.e("astrid", "Error loading task list", e);
handler.post(new Runnable() { handler.post(new Runnable() {
public void run() { public void run() {
if(!e.getMessage().contains("Couldn't init cursor window")) if (!e.getMessage().contains("Couldn't init cursor window"))
return; return;
DialogUtilities.okDialog(getParent(), "Ran out of memory! " + DialogUtilities.okDialog(getParent(), "Ran out of memory! "
"Try restarting Astrid...", null); + "Try restarting Astrid...", null);
} }
}); });
return; return;
} catch (final Exception e) { } catch (final Exception e) {
FlurryAgent.onError("task-list-error", e.toString(), FlurryAgent.onError("task-list-error", e.toString(), e.getClass()
e.getClass().getSimpleName()); .getSimpleName());
Log.e("astrid", "Error loading task list", e); Log.e("astrid", "Error loading task list", e);
return; return;
@ -596,7 +621,7 @@ public class TaskListSubActivity extends SubActivity {
return sortMode.compareTo(arg0, arg1); return sortMode.compareTo(arg0, arg1);
} }
}); });
if(sortReverse) if (sortReverse)
Collections.reverse(context.taskArray); Collections.reverse(context.taskArray);
final int finalCompleted = completedTasks; final int finalCompleted = completedTasks;
@ -606,26 +631,36 @@ public class TaskListSubActivity extends SubActivity {
handler.post(new Runnable() { handler.post(new Runnable() {
public void run() { public void run() {
Resources r = getResources(); Resources r = getResources();
StringBuilder title = new StringBuilder(). StringBuilder title = new StringBuilder().append(
append(r.getString(R.string.taskList_titlePrefix)).append(" "); r.getString(R.string.taskList_titlePrefix)).append(" ");
if(context.filterTag != null) { if (context.filterTag != null) {
if(TagModelForView.UNTAGGED_IDENTIFIER.equals(context.filterTag.getTagIdentifier())) { if (TagModelForView.UNTAGGED_IDENTIFIER
title.append(r.getString(R.string.taskList_titleUntagged)).append(" "); .equals(context.filterTag.getTagIdentifier())) {
title.append(
r.getString(R.string.taskList_titleUntagged))
.append(" ");
} else { } else {
title.append(r.getString(R.string.taskList_titleTagPrefix, title.append(
context.filterTag.getName())).append(" "); r.getString(R.string.taskList_titleTagPrefix,
context.filterTag.getName())).append(
" ");
} }
} }
if(finalCompleted > 0) if (finalCompleted > 0)
title.append(r.getQuantityString(R.plurals.NactiveTasks, title
finalActive, finalActive, context.taskArray.size())); .append(r.getQuantityString(R.plurals.NactiveTasks,
finalActive, finalActive, context.taskArray
.size()));
else else
title.append(r.getQuantityString(R.plurals.Ntasks, title
context.taskArray.size(), context.taskArray.size())); .append(r.getQuantityString(R.plurals.Ntasks,
if(finalHidden > 0) context.taskArray.size(), context.taskArray
title.append(" (+").append(finalHidden).append(" "). .size()));
append(r.getString(R.string.taskList_hiddenSuffix)).append(")"); if (finalHidden > 0)
title.append(" (+").append(finalHidden).append(" ").append(
r.getString(R.string.taskList_hiddenSuffix))
.append(")");
context.windowTitle = title; context.windowTitle = title;
} }
}); });
@ -639,11 +674,10 @@ public class TaskListSubActivity extends SubActivity {
public void run() { public void run() {
// hide "add" button if we have too many tasks // hide "add" button if we have too many tasks
int threshold = HIDE_ADD_BTN_PORTRAIT; int threshold = HIDE_ADD_BTN_PORTRAIT;
if(getParent().getResources().getConfiguration().orientation == if (getParent().getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)
Configuration.ORIENTATION_LANDSCAPE)
threshold = HIDE_ADD_BTN_LANDSCPE; threshold = HIDE_ADD_BTN_LANDSCPE;
if(context.taskArray.size() > threshold) if (context.taskArray.size() > threshold)
addButton.setVisibility(View.GONE); addButton.setVisibility(View.GONE);
else else
addButton.setVisibility(View.VISIBLE); addButton.setVisibility(View.VISIBLE);
@ -703,7 +737,7 @@ public class TaskListSubActivity extends SubActivity {
} }
public void setSelectedItem(TaskIdentifier taskId) { public void setSelectedItem(TaskIdentifier taskId) {
if(taskId == null) { if (taskId == null) {
selectedTaskId = null; selectedTaskId = null;
context.selectedTask = null; context.selectedTask = null;
} else } else
@ -719,58 +753,64 @@ public class TaskListSubActivity extends SubActivity {
listView.setAdapter(context.listAdapter); listView.setAdapter(context.listAdapter);
listView.setItemsCanFocus(true); listView.setItemsCanFocus(true);
if(context.selectedTask != null) { if (context.selectedTask != null) {
try { try {
int selectedPosition = context.listAdapter.getPosition(context.selectedTask); int selectedPosition = context.listAdapter
.getPosition(context.selectedTask);
View v = listView.getChildAt(selectedPosition); View v = listView.getChildAt(selectedPosition);
context.listAdapter.setExpanded(v, context.selectedTask, true); context.listAdapter.setExpanded(v, context.selectedTask, true);
listView.setSelection(selectedPosition); listView.setSelection(selectedPosition);
} catch (Exception e) { } catch (Exception e) {
FlurryAgent.onError("task-list-selected", e.toString(), FlurryAgent.onError("task-list-selected", e.toString(), e
e.getClass().getSimpleName()); .getClass().getSimpleName());
Log.e("astrid", "error with selected task", e); Log.e("astrid", "error with selected task", e);
} }
} }
// filters context menu // filters context menu
listView.setOnCreateContextMenuListener(new OnCreateContextMenuListener() { listView
.setOnCreateContextMenuListener(new OnCreateContextMenuListener() {
public void onCreateContextMenu(ContextMenu menu, View v, public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) { ContextMenuInfo menuInfo) {
if(menu.hasVisibleItems()) if (menu.hasVisibleItems())
return; return;
Resources r = getResources(); Resources r = getResources();
menu.setHeaderTitle(R.string.taskList_filter_title); menu.setHeaderTitle(R.string.taskList_filter_title);
MenuItem item = menu.add(Menu.NONE, CONTEXT_FILTER_HIDDEN, MenuItem item = menu.add(Menu.NONE,
Menu.NONE, R.string.taskList_filter_hidden); CONTEXT_FILTER_HIDDEN, Menu.NONE,
R.string.taskList_filter_hidden);
item.setCheckable(true); item.setCheckable(true);
item.setChecked(filterShowHidden); item.setChecked(filterShowHidden);
item = menu.add(Menu.NONE, CONTEXT_FILTER_DONE, Menu.NONE, item = menu.add(Menu.NONE, CONTEXT_FILTER_DONE,
R.string.taskList_filter_done); Menu.NONE, R.string.taskList_filter_done);
item.setCheckable(true); item.setCheckable(true);
item.setChecked(filterShowDone); item.setChecked(filterShowDone);
if(context.filterTag != null) { if (context.filterTag != null) {
item = menu.add(Menu.NONE, CONTEXT_FILTER_TAG, Menu.NONE, item = menu.add(Menu.NONE, CONTEXT_FILTER_TAG,
r.getString(R.string.taskList_filter_tagged, Menu.NONE, r.getString(
R.string.taskList_filter_tagged,
context.filterTag.getName())); context.filterTag.getName()));
item.setCheckable(true); item.setCheckable(true);
item.setChecked(true); item.setChecked(true);
} }
item = menu.add(CONTEXT_SORT_GROUP, CONTEXT_SORT_AUTO, Menu.NONE, item = menu.add(CONTEXT_SORT_GROUP, CONTEXT_SORT_AUTO,
R.string.taskList_sort_auto); Menu.NONE, R.string.taskList_sort_auto);
item.setChecked(sortMode == SortMode.AUTO); item.setChecked(sortMode == SortMode.AUTO);
item = menu.add(CONTEXT_SORT_GROUP, CONTEXT_SORT_ALPHA, Menu.NONE, item = menu.add(CONTEXT_SORT_GROUP, CONTEXT_SORT_ALPHA,
R.string.taskList_sort_alpha); Menu.NONE, R.string.taskList_sort_alpha);
item.setChecked(sortMode == SortMode.ALPHA); item.setChecked(sortMode == SortMode.ALPHA);
item = menu.add(CONTEXT_SORT_GROUP, CONTEXT_SORT_DUEDATE, Menu.NONE, item = menu.add(CONTEXT_SORT_GROUP,
CONTEXT_SORT_DUEDATE, Menu.NONE,
R.string.taskList_sort_duedate); R.string.taskList_sort_duedate);
item.setChecked(sortMode == SortMode.DUEDATE); item.setChecked(sortMode == SortMode.DUEDATE);
menu.setGroupCheckable(CONTEXT_SORT_GROUP, true, true); menu.setGroupCheckable(CONTEXT_SORT_GROUP, true, true);
item = menu.add(CONTEXT_SORT_GROUP, CONTEXT_SORT_REVERSE, Menu.NONE, item = menu.add(CONTEXT_SORT_GROUP,
CONTEXT_SORT_REVERSE, Menu.NONE,
R.string.taskList_sort_reverse); R.string.taskList_sort_reverse);
item.setCheckable(true); item.setCheckable(true);
item.setChecked(sortReverse); item.setChecked(sortReverse);
@ -781,7 +821,7 @@ public class TaskListSubActivity extends SubActivity {
} }
private void reloadList() { private void reloadList() {
if(context.loadingThread != null && context.loadingThread.isAlive()) { if (context.loadingThread != null && context.loadingThread.isAlive()) {
context.loadingThread.interrupt(); context.loadingThread.interrupt();
context.loadingThread.stop(); context.loadingThread.stop();
} }
@ -789,9 +829,11 @@ public class TaskListSubActivity extends SubActivity {
context.loadingThread.start(); context.loadingThread.start();
} }
/* ====================================================================== /*
* ======================================================================
* ======================================================= event handlers * ======================================================= event handlers
* ====================================================================== */ * ======================================================================
*/
@Override @Override
protected Object onRetainNonConfigurationInstance() { protected Object onRetainNonConfigurationInstance() {
@ -800,8 +842,8 @@ public class TaskListSubActivity extends SubActivity {
@Override @Override
protected boolean onKeyDown(int keyCode, KeyEvent event) { protected boolean onKeyDown(int keyCode, KeyEvent event) {
if(keyCode == KeyEvent.KEYCODE_BACK) { if (keyCode == KeyEvent.KEYCODE_BACK) {
if(context.filterTag != null) { if (context.filterTag != null) {
showTagsView(); showTagsView();
return true; return true;
} else { } else {
@ -810,8 +852,8 @@ public class TaskListSubActivity extends SubActivity {
} }
} }
if(keyCode >= KeyEvent.KEYCODE_A && keyCode <= KeyEvent.KEYCODE_Z) { if (keyCode >= KeyEvent.KEYCODE_A && keyCode <= KeyEvent.KEYCODE_Z) {
createTask((char)('A' + (keyCode - KeyEvent.KEYCODE_A))); createTask((char) ('A' + (keyCode - KeyEvent.KEYCODE_A)));
return true; return true;
} }
@ -820,7 +862,7 @@ public class TaskListSubActivity extends SubActivity {
@Override @Override
protected void onSaveInstanceState(Bundle outState) { protected void onSaveInstanceState(Bundle outState) {
if(context.loadingThread != null && context.loadingThread.isAlive()) if (context.loadingThread != null && context.loadingThread.isAlive())
context.loadingThread.stop(); context.loadingThread.stop();
} }
@ -831,10 +873,10 @@ public class TaskListSubActivity extends SubActivity {
if (hasFocus) { if (hasFocus) {
if (shouldRefreshTaskList) if (shouldRefreshTaskList)
reloadList(); reloadList();
else if(syncPreferencesOpened) { else if (syncPreferencesOpened) {
syncPreferencesOpened = false; syncPreferencesOpened = false;
if(TaskList.synchronizeNow) { if (TaskList.synchronizeNow) {
synchronize(); synchronize();
TaskList.synchronizeNow = false; TaskList.synchronizeNow = false;
} }
@ -843,9 +885,9 @@ public class TaskListSubActivity extends SubActivity {
SynchronizationService.stop(); SynchronizationService.stop();
SynchronizationService.start(); SynchronizationService.start();
} else if (context.taskArray != null && } else if (context.taskArray != null
context.taskArray.size() > 0 && && context.taskArray.size() > 0
context.taskArray.size() < AUTO_REFRESH_MAX_LIST_SIZE) { && context.taskArray.size() < AUTO_REFRESH_MAX_LIST_SIZE) {
// invalidate caches // invalidate caches
for (TaskModelForList task : context.taskArray) for (TaskModelForList task : context.taskArray)
@ -864,9 +906,9 @@ public class TaskListSubActivity extends SubActivity {
sync.setTaskController(getTaskController()); sync.setTaskController(getTaskController());
sync.synchronize(getParent(), new SynchronizerListener() { sync.synchronize(getParent(), new SynchronizerListener() {
public void onSynchronizerFinished(int numServicesSynced) { public void onSynchronizerFinished(int numServicesSynced) {
if(numServicesSynced == 0) { if (numServicesSynced == 0) {
DialogUtilities.okDialog(getParent(), getResources().getString( DialogUtilities.okDialog(getParent(), getResources()
R.string.sync_no_synchronizers), null); .getString(R.string.sync_no_synchronizers), null);
return; return;
} }
reloadList(); reloadList();
@ -876,9 +918,9 @@ public class TaskListSubActivity extends SubActivity {
@Override @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(resultCode == Constants.RESULT_SYNCHRONIZE) { if (resultCode == Constants.RESULT_SYNCHRONIZE) {
synchronize(); synchronize();
} else if(requestCode == ACTIVITY_TAGS) { } else if (requestCode == ACTIVITY_TAGS) {
switchToActivity(TaskList.AC_TAG_LIST, null); switchToActivity(TaskList.AC_TAG_LIST, null);
} }
} }
@ -886,42 +928,43 @@ public class TaskListSubActivity extends SubActivity {
/** Call an activity to create the given task */ /** Call an activity to create the given task */
private void createTask(Character startCharacter) { private void createTask(Character startCharacter) {
Intent intent = new Intent(getParent(), TaskEdit.class); Intent intent = new Intent(getParent(), TaskEdit.class);
if(context.filterTag != null) if (context.filterTag != null)
intent.putExtra(TaskEdit.TAG_NAME_TOKEN, context.filterTag.getName()); intent.putExtra(TaskEdit.TAG_NAME_TOKEN, context.filterTag
if(startCharacter != null) .getName());
if (startCharacter != null)
intent.putExtra(TaskEdit.START_CHAR_TOKEN, startCharacter); intent.putExtra(TaskEdit.START_CHAR_TOKEN, startCharacter);
launchActivity(intent, ACTIVITY_CREATE); launchActivity(intent, ACTIVITY_CREATE);
} }
/** Show a dialog box and delete the task specified */ /** Show a dialog box and delete the task specified */
private void deleteTask(final TaskModelForList task) { private void deleteTask(final TaskModelForList task) {
new AlertDialog.Builder(getParent()) new AlertDialog.Builder(getParent()).setTitle(R.string.delete_title)
.setTitle(R.string.delete_title) .setMessage(R.string.delete_this_task_title).setIcon(
.setMessage(R.string.delete_this_task_title) android.R.drawable.ic_dialog_alert).setPositiveButton(
.setIcon(android.R.drawable.ic_dialog_alert) android.R.string.ok,
.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() { new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog,
int which) {
context.listAdapter.remove(task); context.listAdapter.remove(task);
context.taskArray.remove(task); context.taskArray.remove(task);
getTaskController().deleteTask(task.getTaskIdentifier()); getTaskController().deleteTask(
task.getTaskIdentifier());
} }
}) }).setNegativeButton(android.R.string.cancel, null)
.setNegativeButton(android.R.string.cancel, null)
.show(); .show();
} }
/** Take you to the task edit page */ /** Take you to the task edit page */
private void editTask(TaskModelForList task) { private void editTask(TaskModelForList task) {
Intent intent = new Intent(getParent(), TaskEdit.class); Intent intent = new Intent(getParent(), TaskEdit.class);
intent.putExtra(TaskEdit.LOAD_INSTANCE_TOKEN, intent.putExtra(TaskEdit.LOAD_INSTANCE_TOKEN, task.getTaskIdentifier()
task.getTaskIdentifier().getId()); .getId());
launchActivity(intent, ACTIVITY_EDIT); launchActivity(intent, ACTIVITY_EDIT);
} }
/** Toggle the timer */ /** Toggle the timer */
private void toggleTimer(TaskModelForList task) { private void toggleTimer(TaskModelForList task) {
if(task.getTimerStart() == null) { if (task.getTimerStart() == null) {
FlurryAgent.onEvent("start-timer"); FlurryAgent.onEvent("start-timer");
task.setTimerStart(new Date()); task.setTimerStart(new Date());
} else { } else {
@ -929,7 +972,8 @@ public class TaskListSubActivity extends SubActivity {
task.stopTimerAndUpdateElapsedTime(); task.stopTimerAndUpdateElapsedTime();
} }
getTaskController().saveTask(task); getTaskController().saveTask(task);
context.listAdapter.refreshItem(listView, context.taskArray.indexOf(task)); context.listAdapter.refreshItem(listView, context.taskArray
.indexOf(task));
} }
/** Show the tags view */ /** Show the tags view */
@ -946,12 +990,12 @@ public class TaskListSubActivity extends SubActivity {
private void saveTaskListSort() { private void saveTaskListSort() {
int sortId = sortMode.ordinal() + 1; int sortId = sortMode.ordinal() + 1;
if(filterShowDone) if (filterShowDone)
sortId |= SORTFLAG_FILTERDONE; sortId |= SORTFLAG_FILTERDONE;
if(filterShowHidden) if (filterShowHidden)
sortId |= SORTFLAG_FILTERHIDDEN; sortId |= SORTFLAG_FILTERHIDDEN;
if(sortReverse) if (sortReverse)
sortId *= -1; sortId *= -1;
Preferences.setTaskListSort(getParent(), sortId); Preferences.setTaskListSort(getParent(), sortId);
@ -960,7 +1004,7 @@ public class TaskListSubActivity extends SubActivity {
/** Save the sorting mode to the preferences */ /** Save the sorting mode to the preferences */
private void loadTaskListSort() { private void loadTaskListSort() {
int sortId = Preferences.getTaskListSort(getParent()); int sortId = Preferences.getTaskListSort(getParent());
if(sortId == 0) if (sortId == 0)
return; return;
sortReverse = sortId < 0; sortReverse = sortId < 0;
sortId = Math.abs(sortId); sortId = Math.abs(sortId);
@ -976,11 +1020,11 @@ public class TaskListSubActivity extends SubActivity {
/** Compute date after postponing tasks */ /** Compute date after postponing tasks */
private Date computePostponeDate(Date input, long postponeMillis, private Date computePostponeDate(Date input, long postponeMillis,
boolean shiftFromTodayIfPast) { boolean shiftFromTodayIfPast) {
if(input != null) { if (input != null) {
if(shiftFromTodayIfPast && input.getTime() < System.currentTimeMillis()) if (shiftFromTodayIfPast
&& input.getTime() < System.currentTimeMillis())
input = new Date(); input = new Date();
input = new Date(input.getTime() + input = new Date(input.getTime() + postponeMillis);
postponeMillis);
} }
return input; return input;
@ -991,16 +1035,17 @@ public class TaskListSubActivity extends SubActivity {
final Resources r = getResources(); final Resources r = getResources();
new NumberPickerDialog(getParent(), new OnNumberPickedListener() { new NumberPickerDialog(getParent(), new OnNumberPickedListener() {
public void onNumberPicked(NumberPicker view, int number) { public void onNumberPicked(NumberPicker view, int number) {
Date date = new Date(System.currentTimeMillis() - 24L*3600*1000*number); Date date = new Date(System.currentTimeMillis() - 24L * 3600
int deleted = getTaskController().deleteCompletedTasksOlderThan(date); * 1000 * number);
DialogUtilities.okDialog(getParent(), r.getQuantityString(R.plurals.Ntasks, int deleted = getTaskController()
deleted, deleted) + " " + r.getString(R.string.taskList_deleted), .deleteCompletedTasksOlderThan(date);
null); DialogUtilities.okDialog(getParent(), r.getQuantityString(
if(TaskListSubActivity.filterShowDone) R.plurals.Ntasks, deleted, deleted)
+ " " + r.getString(R.string.taskList_deleted), null);
if (TaskListSubActivity.filterShowDone)
reloadList(); reloadList();
} }
}, r.getString(R.string.taskList_cleanup_dialog), }, r.getString(R.string.taskList_cleanup_dialog), 30, 5, 0, 999).show();
30, 5, 0, 999).show();
} }
/** Show a dialog box to postpone your tasks */ /** Show a dialog box to postpone your tasks */
@ -1008,38 +1053,43 @@ public class TaskListSubActivity extends SubActivity {
FlurryAgent.onEvent("postpone-task"); FlurryAgent.onEvent("postpone-task");
final Resources r = getResources(); final Resources r = getResources();
DialogUtilities.dayHourPicker(getParent(), r.getString(R.string.taskList_postpone_dialog), DialogUtilities.dayHourPicker(getParent(), r
.getString(R.string.taskList_postpone_dialog),
new OnNNumberPickedListener() { new OnNNumberPickedListener() {
public void onNumbersPicked(int[] values) { public void onNumbersPicked(int[] values) {
long postponeMillis = (values[0] * 24 + values[1]) * 3600L * 1000; long postponeMillis = (values[0] * 24 + values[1]) * 3600L * 1000;
if(postponeMillis <= 0) if (postponeMillis <= 0)
return; return;
task.setPreferredDueDate(computePostponeDate(task task.setPreferredDueDate(computePostponeDate(task
.getPreferredDueDate(), postponeMillis, .getPreferredDueDate(), postponeMillis, true));
true)); task.setDefiniteDueDate(computePostponeDate(task
task.setDefiniteDueDate(computePostponeDate( .getDefiniteDueDate(), postponeMillis, true));
task.getDefiniteDueDate(), postponeMillis, true)); task.setHiddenUntil(computePostponeDate(task
task.setHiddenUntil(computePostponeDate(task. .getHiddenUntil(), postponeMillis, false));
getHiddenUntil(), postponeMillis, false));
// show nag // show nag
int postponeCount = getTaskController().fetchTaskPostponeCount( int postponeCount = getTaskController()
.fetchTaskPostponeCount(
task.getTaskIdentifier()) + 1; task.getTaskIdentifier()) + 1;
if(Preferences.shouldShowNags(getParent())) { if (Preferences.shouldShowNags(getParent())) {
Random random = new Random(); Random random = new Random();
final String nagText; final String nagText;
if(postponeCount > 1 && random.nextFloat() < POSTPONE_STAT_PCT) { if (postponeCount > 1
nagText = r.getString(R.string.taskList_postpone_count, && random.nextFloat() < POSTPONE_STAT_PCT) {
nagText = r.getString(
R.string.taskList_postpone_count,
postponeCount); postponeCount);
} else { } else {
String[] nags = r.getStringArray(R.array.postpone_nags); String[] nags = r
.getStringArray(R.array.postpone_nags);
nagText = nags[random.nextInt(nags.length)]; nagText = nags[random.nextInt(nags.length)];
} }
handler.post(new Runnable() { handler.post(new Runnable() {
public void run() { public void run() {
Toast.makeText(getParent(), nagText, Toast.LENGTH_LONG).show(); Toast.makeText(getParent(), nagText,
Toast.LENGTH_LONG).show();
} }
}); });
} }
@ -1058,7 +1108,7 @@ public class TaskListSubActivity extends SubActivity {
public boolean onMenuItemSelected(int featureId, final MenuItem item) { public boolean onMenuItemSelected(int featureId, final MenuItem item) {
final TaskModelForList task; final TaskModelForList task;
switch(item.getItemId()) { switch (item.getItemId()) {
// --- options menu items // --- options menu items
case INSERT_ID: case INSERT_ID:
createTask(null); createTask(null);
@ -1070,7 +1120,8 @@ public class TaskListSubActivity extends SubActivity {
showTagsView(); showTagsView();
return true; return true;
case SYNC_ID: case SYNC_ID:
onActivityResult(ACTIVITY_SYNCHRONIZE, Constants.RESULT_SYNCHRONIZE, null); onActivityResult(ACTIVITY_SYNCHRONIZE,
Constants.RESULT_SYNCHRONIZE, null);
return true; return true;
case MORE_ID: case MORE_ID:
layout.showContextMenu(); layout.showContextMenu();
@ -1086,13 +1137,13 @@ public class TaskListSubActivity extends SubActivity {
launchActivity(new Intent(getParent(), EditPreferences.class), 0); launchActivity(new Intent(getParent(), EditPreferences.class), 0);
return true; return true;
case OPTIONS_HELP_ID: case OPTIONS_HELP_ID:
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri
Uri.parse(Constants.HELP_URL)); .parse(Constants.HELP_URL));
launchActivity(browserIntent, 0); launchActivity(browserIntent, 0);
return true; return true;
case OPTIONS_QUICK_TIPS: case OPTIONS_QUICK_TIPS:
DialogUtilities.okDialog(getParent(), DialogUtilities.okDialog(getParent(), getResources().getString(
getResources().getString(R.string.quick_tips), null); R.string.quick_tips), null);
return true; return true;
case OPTIONS_CLEANUP_ID: case OPTIONS_CLEANUP_ID:
cleanOldTasks(); cleanOldTasks();
@ -1100,15 +1151,15 @@ public class TaskListSubActivity extends SubActivity {
// --- list context menu items // --- list context menu items
case TaskListAdapter.CONTEXT_EDIT_ID: case TaskListAdapter.CONTEXT_EDIT_ID:
task = context.tasksById.get((long)item.getGroupId()); task = context.tasksById.get((long) item.getGroupId());
editTask(task); editTask(task);
return true; return true;
case TaskListAdapter.CONTEXT_DELETE_ID: case TaskListAdapter.CONTEXT_DELETE_ID:
task = context.tasksById.get((long)item.getGroupId()); task = context.tasksById.get((long) item.getGroupId());
deleteTask(task); deleteTask(task);
return true; return true;
case TaskListAdapter.CONTEXT_TIMER_ID: case TaskListAdapter.CONTEXT_TIMER_ID:
task = context.tasksById.get((long)item.getGroupId()); task = context.tasksById.get((long) item.getGroupId());
toggleTimer(task); toggleTimer(task);
return true; return true;
case TaskListAdapter.CONTEXT_POSTPONE_ID: case TaskListAdapter.CONTEXT_POSTPONE_ID:
@ -1131,7 +1182,7 @@ public class TaskListSubActivity extends SubActivity {
switchToActivity(TaskList.AC_TASK_LIST, null); switchToActivity(TaskList.AC_TASK_LIST, null);
return true; return true;
case CONTEXT_SORT_AUTO: case CONTEXT_SORT_AUTO:
if(sortMode == SortMode.AUTO) if (sortMode == SortMode.AUTO)
return true; return true;
TaskListSubActivity.sortReverse = false; TaskListSubActivity.sortReverse = false;
TaskListSubActivity.sortMode = SortMode.AUTO; TaskListSubActivity.sortMode = SortMode.AUTO;
@ -1139,7 +1190,7 @@ public class TaskListSubActivity extends SubActivity {
reloadList(); reloadList();
return true; return true;
case CONTEXT_SORT_ALPHA: case CONTEXT_SORT_ALPHA:
if(sortMode == SortMode.ALPHA) if (sortMode == SortMode.ALPHA)
return true; return true;
TaskListSubActivity.sortReverse = false; TaskListSubActivity.sortReverse = false;
TaskListSubActivity.sortMode = SortMode.ALPHA; TaskListSubActivity.sortMode = SortMode.ALPHA;
@ -1147,7 +1198,7 @@ public class TaskListSubActivity extends SubActivity {
reloadList(); reloadList();
return true; return true;
case CONTEXT_SORT_DUEDATE: case CONTEXT_SORT_DUEDATE:
if(sortMode == SortMode.DUEDATE) if (sortMode == SortMode.DUEDATE)
return true; return true;
TaskListSubActivity.sortReverse = false; TaskListSubActivity.sortReverse = false;
TaskListSubActivity.sortMode = SortMode.DUEDATE; TaskListSubActivity.sortMode = SortMode.DUEDATE;
@ -1164,9 +1215,11 @@ public class TaskListSubActivity extends SubActivity {
return false; return false;
} }
/* ====================================================================== /*
* ======================================================================
* ===================================================== getters / setters * ===================================================== getters / setters
* ====================================================================== */ * ======================================================================
*/
public TagModelForView getFilterTag() { public TagModelForView getFilterTag() {
return context.filterTag; return context.filterTag;

@ -56,6 +56,7 @@ import com.timsu.astrid.data.tag.TagController;
import com.timsu.astrid.data.tag.TagModelForView; import com.timsu.astrid.data.tag.TagModelForView;
import com.timsu.astrid.data.task.AbstractTaskModel; import com.timsu.astrid.data.task.AbstractTaskModel;
import com.timsu.astrid.data.task.TaskModelForSync; import com.timsu.astrid.data.task.TaskModelForSync;
import com.timsu.astrid.utilities.AstridUtilities;
import com.timsu.astrid.utilities.DialogUtilities; import com.timsu.astrid.utilities.DialogUtilities;
import com.timsu.astrid.utilities.Preferences; import com.timsu.astrid.utilities.Preferences;
@ -153,8 +154,8 @@ public class RTMSyncProvider extends SynchronizationProvider {
return true; return true;
} catch (final Exception e) { } catch (final Exception e) {
// didn't work // didn't work
FlurryAgent.onError("rtm-error-verify-login", e.toString(), FlurryAgent.onError("rtm-verify-login", AstridUtilities.throwableToString(e),
e.getClass().getSimpleName()); SynchronizationProvider.class.getSimpleName());
syncLoginHandler.post(new Runnable() { syncLoginHandler.post(new Runnable() {
@Override @Override
@ -178,8 +179,8 @@ public class RTMSyncProvider extends SynchronizationProvider {
} }
} catch (Exception e) { } catch (Exception e) {
FlurryAgent.onError("rtm-error-authenticate", e.toString(), FlurryAgent.onError("rtm-authenticate", AstridUtilities.throwableToString(e),
e.getClass().getSimpleName()); SynchronizationProvider.class.getSimpleName());
// IO Exception // IO Exception
if(e instanceof ServiceInternalException && if(e instanceof ServiceInternalException &&
@ -258,8 +259,8 @@ public class RTMSyncProvider extends SynchronizationProvider {
postUpdate(new ProgressUpdater(5, 5)); postUpdate(new ProgressUpdater(5, 5));
addTasksToList(context, tasks, remoteChanges); addTasksToList(context, tasks, remoteChanges);
} catch (Exception e) { } catch (Exception e) {
FlurryAgent.onError("rtm-error-quick-sync", e.toString(), FlurryAgent.onError("rtm-quick-sync", AstridUtilities.throwableToString(e),
e.getClass().getSimpleName()); SynchronizationProvider.class.getSimpleName());
Log.e("rtmsync", "Error sync-ing list!", e); Log.e("rtmsync", "Error sync-ing list!", e);
remoteChanges.clear(); remoteChanges.clear();
@ -279,8 +280,8 @@ public class RTMSyncProvider extends SynchronizationProvider {
filter, lastSyncDate); filter, lastSyncDate);
addTasksToList(context, tasks, remoteChanges); addTasksToList(context, tasks, remoteChanges);
} catch (Exception e) { } catch (Exception e) {
FlurryAgent.onError("rtm-error-indiv-sync", e.toString(), FlurryAgent.onError("rtm-indiv-sync", AstridUtilities.throwableToString(e),
e.getClass().getSimpleName()); SynchronizationProvider.class.getSimpleName());
Log.e("rtmsync", "Error sync-ing list!", e); Log.e("rtmsync", "Error sync-ing list!", e);
postUpdate(new Runnable() { postUpdate(new Runnable() {
@ -305,8 +306,8 @@ public class RTMSyncProvider extends SynchronizationProvider {
FlurryAgent.onEvent("rtm-sync-finished"); FlurryAgent.onEvent("rtm-sync-finished");
} catch (Exception e) { } catch (Exception e) {
FlurryAgent.onError("rtm-error-sync", e.toString(), FlurryAgent.onError("rtm-sync", AstridUtilities.throwableToString(e),
e.getClass().getSimpleName()); SynchronizationProvider.class.getSimpleName());
Log.e("rtmsync", "Error in synchronization", e); Log.e("rtmsync", "Error in synchronization", e);
showError(context, e, null); showError(context, e, null);

@ -43,6 +43,7 @@ import com.timsu.astrid.data.task.AbstractTaskModel;
import com.timsu.astrid.data.task.TaskController; import com.timsu.astrid.data.task.TaskController;
import com.timsu.astrid.data.task.TaskIdentifier; import com.timsu.astrid.data.task.TaskIdentifier;
import com.timsu.astrid.data.task.TaskModelForSync; import com.timsu.astrid.data.task.TaskModelForSync;
import com.timsu.astrid.utilities.AstridUtilities;
import com.timsu.astrid.utilities.DialogUtilities; import com.timsu.astrid.utilities.DialogUtilities;
import com.timsu.astrid.utilities.Notifications; import com.timsu.astrid.utilities.Notifications;
import com.timsu.astrid.utilities.Preferences; import com.timsu.astrid.utilities.Preferences;
@ -63,8 +64,7 @@ public abstract class SynchronizationProvider {
this.id = id; this.id = id;
} }
/** Called off the UI thread. does some setup and then invokes implemented /** Does some setup and then invokes implemented synchronize method
* synchronize method
* @param activity * @param activity
* @param caller * @param caller
*/ */
@ -72,7 +72,10 @@ public abstract class SynchronizationProvider {
this.synchronizer = caller; this.synchronizer = caller;
if(!isBackgroundService()) { if(!isBackgroundService()) {
syncHandler = new Handler(); syncHandler = caller.getHandler();
syncHandler.post(new Runnable() {
@Override
public void run() {
SynchronizationProvider.progressDialog = new ProgressDialog(activity); SynchronizationProvider.progressDialog = new ProgressDialog(activity);
progressDialog.setIcon(android.R.drawable.ic_dialog_alert); progressDialog.setIcon(android.R.drawable.ic_dialog_alert);
progressDialog.setTitle("Synchronization"); progressDialog.setTitle("Synchronization");
@ -83,6 +86,8 @@ public abstract class SynchronizationProvider {
progressDialog.setCancelable(false); progressDialog.setCancelable(false);
progressDialog.show(); progressDialog.show();
} }
});
}
synchronize(activity); synchronize(activity);
} }
@ -121,6 +126,8 @@ public abstract class SynchronizationProvider {
*/ */
void showError(final Context context, Throwable e, String message) { void showError(final Context context, Throwable e, String message) {
Log.e("astrid", "Synchronization Error", e); Log.e("astrid", "Synchronization Error", e);
FlurryAgent.onError("sync-error", AstridUtilities.throwableToString(e),
SynchronizationProvider.class.getSimpleName());
if(isBackgroundService()) if(isBackgroundService())
return; return;
@ -146,7 +153,7 @@ public abstract class SynchronizationProvider {
* to console if we're a background sync. * to console if we're a background sync.
*/ */
protected void postUpdate(Runnable updater) { protected void postUpdate(Runnable updater) {
if(isBackgroundService()) { if(isBackgroundService() || syncHandler == null) {
// only run jobs if they can actually be processed // only run jobs if they can actually be processed
if(updater instanceof ProgressLabelUpdater) if(updater instanceof ProgressLabelUpdater)
updater.run(); updater.run();
@ -301,8 +308,8 @@ public abstract class SynchronizationProvider {
else else
log.append("updated '" + task.getName() + "'\n"); log.append("updated '" + task.getName() + "'\n");
} catch (Exception e) { } catch (Exception e) {
FlurryAgent.onError("sync-error-push-task", e.toString(), FlurryAgent.onError("sync-push-task", AstridUtilities.throwableToString(e),
e.getClass().getSimpleName()); SynchronizationProvider.class.getSimpleName());
Log.e("astrid", "Exception pushing task", e); Log.e("astrid", "Exception pushing task", e);
log.append("error sending '" + task.getName() + "'\n"); log.append("error sending '" + task.getName() + "'\n");

@ -106,6 +106,7 @@ public class SynchronizationService extends Service {
performSynchronization(); performSynchronization();
} }
}, offset, interval); }, offset, interval);
Log.i("astrid", "Synchronization Service Started, Offset: " + offset/1000 + Log.i("astrid", "Synchronization Service Started, Offset: " + offset/1000 +
"s Interval: " + interval/1000); "s Interval: " + interval/1000);
} }

@ -24,6 +24,8 @@ import java.util.Date;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.util.Log; import android.util.Log;
import com.timsu.astrid.activities.TaskListSubActivity; import com.timsu.astrid.activities.TaskListSubActivity;
@ -68,15 +70,19 @@ public class Synchronizer {
void onSynchronizerFinished(int numServicesSynced); void onSynchronizerFinished(int numServicesSynced);
} }
/** Synchronize all activated sync services */ /** Synchronize all activated sync services. */
public synchronized void synchronize(Context context, SynchronizerListener listener) { public synchronized void synchronize(Context context,
SynchronizerListener listener) {
currentStep = ServiceWrapper._FIRST_SERVICE.ordinal(); currentStep = ServiceWrapper._FIRST_SERVICE.ordinal();
servicesSynced = 0; servicesSynced = 0;
callback = listener; callback = listener;
// if we're not the autosync service, stop it // if we're not the autosync service, stop it. also create handler
if(!isService) if(!isService) {
SynchronizationService.stop(); SynchronizationService.stop();
if(Looper.myLooper() != null)
handler = new Handler();
}
continueSynchronization(context); continueSynchronization(context);
} }
@ -134,6 +140,9 @@ public class Synchronizer {
// Internal state for the synchronization process // Internal state for the synchronization process
/** Handler for sending jobs to the UI thread */
private Handler handler = null;
/** Current step in the sync process */ /** Current step in the sync process */
private int currentStep = 0; private int currentStep = 0;
@ -157,6 +166,10 @@ public class Synchronizer {
return singleTaskForSync; return singleTaskForSync;
} }
Handler getHandler() {
return handler;
}
/** Called to do the next step of synchronization. */ /** Called to do the next step of synchronization. */
void continueSynchronization(Context context) { void continueSynchronization(Context context) {
ServiceWrapper serviceWrapper = ServiceWrapper serviceWrapper =
@ -185,12 +198,14 @@ public class Synchronizer {
if(callback != null) if(callback != null)
callback.onSynchronizerFinished(servicesSynced); callback.onSynchronizerFinished(servicesSynced);
if(getSingleTaskForSync() != null) if(getSingleTaskForSync() == null)
Preferences.setSyncLastSync(context, new Date()); Preferences.setSyncLastSync(context, new Date());
if(!isService) { if(!isService) {
SynchronizationService.start(); SynchronizationService.start();
TaskListSubActivity.shouldRefreshTaskList = true; TaskListSubActivity.shouldRefreshTaskList = true;
} }
Log.i("sync", "Synchronization Service Finished");
} }
// --- controller stuff // --- controller stuff

@ -0,0 +1,30 @@
package com.timsu.astrid.utilities;
import java.io.PrintWriter;
import java.io.StringWriter;
/**
* General purpose utilities for the Astrid project. Grab-bag of stuff.
*
* @author timsu
*
*/
public class AstridUtilities {
/**
* Converts a throwable's stack trace into a string
*
* @param input
* @return
*/
public static String throwableToString(Throwable input) {
StringWriter writer = new StringWriter();
PrintWriter writerPrinter = new PrintWriter(writer);
input.printStackTrace(writerPrinter);
writerPrinter.flush();
writerPrinter.close();
return writer.toString();
}
}
Loading…
Cancel
Save