Awesome! timer works like it should. No notification yet...

pull/14/head
Tim Su 14 years ago
parent 1a331afa62
commit fa84414676

@ -218,6 +218,19 @@
<receiver android:name="com.todoroo.astrid.timers.TimerActionExposer">
<intent-filter>
<action android:name="com.todoroo.astrid.REQUEST_ACTIONS" />
<action android:name="com.todoroo.astrid.TIMER_BUTTON" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
<receiver android:name="com.todoroo.astrid.timers.TimerFilterExposer">
<intent-filter>
<action android:name="com.todoroo.astrid.REQUEST_FILTERS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
<receiver android:name="com.todoroo.astrid.timers.TimerTaskCompleteListener">
<intent-filter>
<action android:name="com.todoroo.astrid.TASK_COMPLETED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>

@ -22,6 +22,14 @@ import com.todoroo.andlib.sql.QueryTemplate;
*/
public final class Filter extends FilterListItem {
// --- constants
/** Constant for valuesForNewTasks to indicate the value should be replaced
* with the current time as long */
public static final long VALUE_NOW = Long.MIN_VALUE + 1;
// --- instance variables
/**
* Expanded title of this filter. This is displayed at the top
* of the screen when user is viewing this filter.

@ -3,7 +3,6 @@
*/
package com.todoroo.astrid.api;
import android.content.Intent;
import android.os.Parcel;
import android.os.Parcelable;
import android.widget.RemoteViews;
@ -32,16 +31,6 @@ public final class TaskDecoration implements Parcelable {
*/
public RemoteViews decoration = null;
/**
* Decoration update interval (minimum of 1000 millis), 0 to never update
*/
public long updateInterval = 0;
/**
* Intent to call to update the decoration
*/
public Intent updateIntent = null;
/**
* Decoration position
*/

@ -4,7 +4,7 @@
<booleanAttribute key="ch.zork.quicklaunch" value="true"/>
<stringAttribute key="ch.zork.quicklaunch.icon" value="14.gif"/>
<intAttribute key="ch.zork.quicklaunch.index" value="0"/>
<stringAttribute key="ch.zork.quicklaunch.mode" value="debug"/>
<stringAttribute key="ch.zork.quicklaunch.mode" value="run"/>
<intAttribute key="com.android.ide.eclipse.adt.action" value="0"/>
<stringAttribute key="com.android.ide.eclipse.adt.commandline" value="-scale 0.7"/>
<intAttribute key="com.android.ide.eclipse.adt.delay" value="0"/>

@ -0,0 +1,28 @@
package com.todoroo.astrid.core;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.astrid.service.TaskService;
public final class PluginServices {
@Autowired
TaskService taskService;
private static PluginServices instance;
private PluginServices() {
DependencyInjectionService.getInstance().inject(this);
}
private synchronized static PluginServices getInstance() {
if(instance == null)
instance = new PluginServices();
return instance;
}
public static TaskService getTaskService() {
return getInstance().taskService;
}
}

@ -9,17 +9,12 @@ import android.net.Uri;
import android.text.TextUtils;
import com.timsu.astrid.R;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.core.PluginServices;
import com.todoroo.astrid.model.Task;
import com.todoroo.astrid.service.TaskService;
public class GCalTaskCompleteListener extends BroadcastReceiver {
@Autowired
private TaskService taskService;
@SuppressWarnings("nls")
@Override
public void onReceive(Context context, Intent intent) {
@ -27,9 +22,7 @@ public class GCalTaskCompleteListener extends BroadcastReceiver {
if(taskId == -1)
return;
DependencyInjectionService.getInstance().inject(this);
Task task = taskService.fetchById(taskId, Task.ID, Task.TITLE, Task.CALENDAR_URI);
Task task = PluginServices.getTaskService().fetchById(taskId, Task.ID, Task.TITLE, Task.CALENDAR_URI);
if(task == null)
return;

@ -7,12 +7,10 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
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.service.TaskService;
/**
* Exposes Task Detail for notes
@ -22,11 +20,6 @@ import com.todoroo.astrid.service.TaskService;
*/
public class NoteDetailExposer extends BroadcastReceiver implements DetailExposer {
private static TaskService staticTaskService;
@Autowired
private TaskService taskService;
@Override
public void onReceive(Context context, Intent intent) {
// get tags associated with this task
@ -53,16 +46,7 @@ public class NoteDetailExposer extends BroadcastReceiver implements DetailExpose
if(!extended)
return null;
synchronized(NoteDetailExposer.class) {
if(staticTaskService == null) {
DependencyInjectionService.getInstance().inject(this);
staticTaskService = taskService;
} else {
taskService = staticTaskService;
}
}
Task task = taskService.fetchById(id, Task.NOTES);
Task task = PluginServices.getTaskService().fetchById(id, Task.NOTES);
if(task == null)
return null;
String notes = task.getValue(Task.NOTES);

@ -16,12 +16,10 @@ import com.google.ical.values.Frequency;
import com.google.ical.values.RRule;
import com.google.ical.values.WeekdayNum;
import com.timsu.astrid.R;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
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.service.TaskService;
/**
* Exposes Task Detail for repeats, i.e. "Repeats every 2 days"
@ -31,11 +29,6 @@ import com.todoroo.astrid.service.TaskService;
*/
public class RepeatDetailExposer extends BroadcastReceiver implements DetailExposer {
private static TaskService staticTaskService = null;
@Autowired
TaskService taskService;
@Override
public void onReceive(Context context, Intent intent) {
// get tags associated with this task
@ -61,16 +54,7 @@ public class RepeatDetailExposer extends BroadcastReceiver implements DetailExpo
if(extended)
return null;
synchronized(RepeatDetailExposer.class) {
if(staticTaskService == null) {
DependencyInjectionService.getInstance().inject(this);
staticTaskService = taskService;
} else {
taskService = staticTaskService;
}
}
Task task = taskService.fetchById(id, Task.FLAGS, Task.RECURRENCE);
Task task = PluginServices.getTaskService().fetchById(id, Task.FLAGS, Task.RECURRENCE);
if(task == null)
return null;

@ -9,14 +9,12 @@ import android.content.Context;
import android.content.Intent;
import com.timsu.astrid.R;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.api.TaskAction;
import com.todoroo.astrid.api.TaskDecoration;
import com.todoroo.astrid.core.PluginServices;
import com.todoroo.astrid.model.Task;
import com.todoroo.astrid.service.TaskService;
/**
* Exposes {@link TaskDecoration} for timers
@ -26,8 +24,7 @@ import com.todoroo.astrid.service.TaskService;
*/
public class TimerActionExposer extends BroadcastReceiver {
@Autowired
private TaskService taskService;
private static final String TIMER_ACTION = "com.todoroo.astrid.TIMER_BUTTON"; //$NON-NLS-1$
@Override
public void onReceive(Context context, Intent intent) {
@ -35,16 +32,8 @@ public class TimerActionExposer extends BroadcastReceiver {
if(taskId == -1)
return;
synchronized(TimerDecorationExposer.class) {
if(TimerDecorationExposer.staticTaskService == null) {
DependencyInjectionService.getInstance().inject(this);
TimerDecorationExposer.staticTaskService = taskService;
} else {
taskService = TimerDecorationExposer.staticTaskService;
}
}
Task task = taskService.fetchById(taskId, Task.TIMER_START);
Task task = PluginServices.getTaskService().fetchById(taskId, Task.ID, Task.TIMER_START,
Task.ELAPSED_SECONDS);
// was part of a broadcast for actions
if(AstridApiConstants.BROADCAST_REQUEST_ACTIONS.equals(intent.getAction())) {
@ -53,7 +42,8 @@ public class TimerActionExposer extends BroadcastReceiver {
label = context.getString(R.string.TAE_startTimer);
else
label = context.getString(R.string.TAE_stopTimer);
Intent newIntent = new Intent(context, TimerActionExposer.class);
Intent newIntent = new Intent(TIMER_ACTION);
newIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId);
TaskAction action = new TaskAction(label,
PendingIntent.getBroadcast(context, 0, newIntent, 0));
@ -63,13 +53,20 @@ public class TimerActionExposer extends BroadcastReceiver {
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, action);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId);
context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
} else {
} else if(TIMER_ACTION.equals(intent.getAction())) {
// toggle the timer
if(task.getValue(Task.TIMER_START) == 0)
task.setValue(Task.TIMER_START, DateUtilities.now());
else
task.setValue(Task.TIMER_START, 0L);
taskService.save(task, true);
else {
TimerTaskCompleteListener.stopTimer(task);
}
PluginServices.getTaskService().save(task, true);
TimerDecorationExposer.removeFromCache(taskId);
// transmit new intents TimerDecoration
new TimerDecorationExposer().onReceive(context, intent);
intent.setAction(AstridApiConstants.BROADCAST_REQUEST_ACTIONS);
onReceive(context, intent);
}
}

@ -4,21 +4,20 @@
package com.todoroo.astrid.timers;
import java.util.HashMap;
import java.util.Timer;
import java.util.TimerTask;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.os.SystemClock;
import android.widget.RemoteViews;
import com.timsu.astrid.R;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.api.TaskDecoration;
import com.todoroo.astrid.service.TaskService;
import com.todoroo.astrid.core.PluginServices;
import com.todoroo.astrid.model.Task;
/**
* Exposes {@link TaskDecoration} for timers
@ -28,50 +27,13 @@ import com.todoroo.astrid.service.TaskService;
*/
public class TimerDecorationExposer extends BroadcastReceiver {
private static final int TASK_BG_COLOR = Color.argb(200, 220, 50, 0);
private static final int TIMING_BG_COLOR = Color.argb(200, 220, 50, 0);
static TaskService staticTaskService;
private static HashMap<Long, TaskDecoration> decorations =
new HashMap<Long, TaskDecoration>();
private static HashMap<Long, Timer> timers =
new HashMap<Long, Timer>();
@Autowired
private TaskService taskService;
private static class TimerTimerTask extends TimerTask {
int time;
RemoteViews remoteViews;
public TimerTimerTask(int time, RemoteViews remoteViews) {
super();
this.time = time;
this.remoteViews = remoteViews;
}
@Override
public void run() {
time++;
int seconds = time % 60;
int minutes = time / 60;
if(minutes > 59) {
int hours = minutes / 60;
minutes %= 60;
remoteViews.setTextViewText(R.id.timer,
String.format("%02d:%02d:%02d", //$NON-NLS-1$
hours, minutes, seconds));
} else {
remoteViews.setTextViewText(R.id.timer,
String.format("%02d:%02d", //$NON-NLS-1$
minutes, seconds));
}
}
}
public void removeFromCache(long taskId) {
public static void removeFromCache(long taskId) {
decorations.remove(taskId);
timers.get(taskId).cancel();
timers.remove(taskId);
}
@Override
@ -80,31 +42,32 @@ public class TimerDecorationExposer extends BroadcastReceiver {
if(taskId == -1)
return;
synchronized(TimerDecorationExposer.class) {
if(staticTaskService == null) {
DependencyInjectionService.getInstance().inject(this);
staticTaskService = taskService;
} else {
taskService = staticTaskService;
}
}
Task task = PluginServices.getTaskService().fetchById(taskId, Task.ELAPSED_SECONDS, Task.TIMER_START);
if(task == null || (task.getValue(Task.ELAPSED_SECONDS) == 0 &&
task.getValue(Task.TIMER_START) == 0))
return;
TaskDecoration decoration;
if(!decorations.containsKey(taskId)) {
RemoteViews remoteViews = new RemoteViews(context.getPackageName(),
R.layout.timer_decoration);
decoration = new TaskDecoration(remoteViews,
TaskDecoration.POSITION_LEFT, TASK_BG_COLOR);
TaskDecoration.POSITION_LEFT, 0);
decorations.put(taskId, decoration);
Timer timer = new Timer();
timers.put(taskId, timer);
timer.scheduleAtFixedRate(new TimerTimerTask(0,
remoteViews), 0, 1000L);
} else {
decoration = decorations.get(taskId);
}
long elapsed = task.getValue(Task.ELAPSED_SECONDS) * 1000L;
if(task.getValue(Task.TIMER_START) != 0) {
decoration.color = TIMING_BG_COLOR;
elapsed += DateUtilities.now() - task.getValue(Task.TIMER_START);
}
// update timer
decoration.decoration.setChronometer(R.id.timer, SystemClock.elapsedRealtime() -
elapsed, null, decoration.color != 0);
// transmit
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_DECORATIONS);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, TimerPlugin.IDENTIFIER);

@ -0,0 +1,71 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.timers;
import android.content.BroadcastReceiver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.drawable.BitmapDrawable;
import com.timsu.astrid.R;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Query;
import com.todoroo.andlib.sql.QueryTemplate;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.activity.FilterListActivity;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.api.FilterListItem;
import com.todoroo.astrid.core.PluginServices;
import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.model.Task;
/**
* Exposes "working on" filter to the {@link FilterListActivity}
*
* @author Tim Su <tim@todoroo.com>
*
*/
public final class TimerFilterExposer extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Resources r = context.getResources();
if(PluginServices.getTaskService().count(Query.select(Task.ID).
where(Task.TIMER_START.gt(0))) == 0)
return;
ContentValues values = new ContentValues();
values.put(Task.TIMER_START.name, Filter.VALUE_NOW);
Filter workingOn = new Filter(r.getString(R.string.TFE_workingOn),
r.getString(R.string.TFE_workingOn),
new QueryTemplate().where(Task.TIMER_START.gt(0)),
values);
workingOn.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.tango_clock)).getBitmap();
// transmit filter list
FilterListItem[] list = new FilterListItem[1];
list[0] = workingOn;
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_FILTERS);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, list);
context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
}
/**
* Build inbox filter
* @return
*/
public static Filter buildInboxFilter(Resources r) {
Filter inbox = new Filter(r.getString(R.string.BFE_Active), r.getString(R.string.BFE_Active_title),
new QueryTemplate().where(Criterion.and(TaskCriteria.isActive(),
TaskCriteria.isVisible(DateUtilities.now()))),
null);
inbox.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.tango_home)).getBitmap();
return inbox;
}
}

@ -0,0 +1,40 @@
package com.todoroo.astrid.timers;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.core.PluginServices;
import com.todoroo.astrid.model.Task;
public class TimerTaskCompleteListener extends BroadcastReceiver {
/**
* stops timer and sets elapsed time. you still need to save the task.
* @param task
*/
public static void stopTimer(Task task) {
int newElapsed = (int)((DateUtilities.now() - task.getValue(Task.TIMER_START)) / 1000L);
task.setValue(Task.TIMER_START, 0L);
task.setValue(Task.ELAPSED_SECONDS,
task.getValue(Task.ELAPSED_SECONDS) + newElapsed);
}
@Override
public void onReceive(Context context, Intent intent) {
long taskId = intent.getLongExtra(AstridApiConstants.EXTRAS_TASK_ID, -1);
if(taskId == -1)
return;
Task task = PluginServices.getTaskService().fetchById(taskId, Task.ID, Task.ELAPSED_SECONDS,
Task.TIMER_START);
if(task == null || task.getValue(Task.TIMER_START) == 0)
return;
stopTimer(task);
PluginServices.getTaskService().save(task, true);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

@ -1,9 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- See the file "LICENSE" for the full license governing this code. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="fill_parent">
android:layout_height="fill_parent"
android:orientation="vertical"
android:paddingLeft="3px">
<ImageView
android:layout_width="fill_parent"
@ -11,7 +12,7 @@
android:scaleType="center"
android:src="@drawable/timers_decoration" />
<TextView android:id="@+id/timer"
<Chronometer android:id="@+id/timer"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="10sp" />

@ -12,5 +12,11 @@
<!-- Task Detail (%s => time) -->
<string name="TDE_label">Elapsed Time: %s</string>
<!-- Filter Header -->
<string name="TFE_category">Timer Filters</string>
<!-- Filter -->
<string name="TFE_workingOn">Tasks Being Timed</string>
</resources>

@ -46,6 +46,7 @@ import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.service.ExceptionService;
import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.andlib.utility.DialogUtilities;
import com.todoroo.andlib.utility.Pair;
import com.todoroo.astrid.adapter.TaskAdapter;
@ -325,6 +326,8 @@ public class TaskListActivity extends ListActivity implements OnScrollListener {
ContentValues forTask = new ContentValues();
forMetadata = new ContentValues();
for(Entry<String, Object> item : filter.valuesForNewTasks.valueSet()) {
if(Long.valueOf(Filter.VALUE_NOW).equals(item.getValue()))
item.setValue(DateUtilities.now());
if(item.getKey().startsWith(Task.TABLE.name))
AndroidUtilities.putInto(forTask, item.getKey(), item.getValue());
else
@ -415,7 +418,7 @@ public class TaskListActivity extends ListActivity implements OnScrollListener {
else
taskAdapter.extendedDetailManager.addNew(taskId, addOn, detail);
} else if(AstridApiConstants.BROADCAST_SEND_ACTIONS.equals(intent.getAction())) {
TaskAction action = extras.getParcelable(AstridApiConstants.BROADCAST_SEND_ACTIONS);
TaskAction action = extras.getParcelable(AstridApiConstants.EXTRAS_RESPONSE);
taskAdapter.taskActionManager.addNew(taskId, addOn, action);
}
} catch (Exception e) {

@ -15,6 +15,7 @@ import android.database.Cursor;
import android.graphics.Color;
import android.graphics.Paint;
import android.text.Html;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
@ -320,6 +321,7 @@ public class TaskAdapter extends CursorAdapter {
detailManager.clearCache();
extendedDetailManager.clearCache();
decorationManager.clearCache();
taskActionManager.clearCache();
}
/**
@ -524,6 +526,7 @@ public class TaskAdapter extends CursorAdapter {
Task task = viewHolder.task;
completeTask(task, ((CheckBox)v).isChecked());
// set check box to actual action item state
setTaskAppearance(viewHolder, task.isCompleted());
}
@ -539,8 +542,13 @@ public class TaskAdapter extends CursorAdapter {
public void onClick(View v) {
try {
action.intent.send();
// refresh ourselves
getCursor().requery();
notifyDataSetChanged();
} catch (Exception e) {
// oh too bad.
Log.i("astrid-action-error", "Error launching intent", e); //$NON-NLS-1$ //$NON-NLS-2$
}
}
};
@ -562,6 +570,9 @@ public class TaskAdapter extends CursorAdapter {
if(viewHolder.expanded) {
extendedDetailManager.request(viewHolder);
taskActionManager.request(viewHolder);
} else {
viewHolder.extendedDetails.setVisibility(View.GONE);
viewHolder.actions.setVisibility(View.GONE);
}
}
}

@ -10,8 +10,8 @@ import com.todoroo.andlib.sql.Query;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.dao.MetadataDao;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.model.Metadata;
import com.todoroo.astrid.model.Task;
@ -173,6 +173,19 @@ public class TaskService {
Task.IMPORTANCE + " + " + Task.COMPLETION_DATE);
}
/**
* @param query
* @return how many tasks are matched by this query
*/
public int count(Query query) {
TodorooCursor<Task> cursor = taskDao.query(query);
try {
return cursor.getCount();
} finally {
cursor.close();
}
}
}

Loading…
Cancel
Save