Made the action exposer

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

@ -214,7 +214,13 @@
<action android:name="com.todoroo.astrid.REQUEST_DECORATIONS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
</receiver>
<receiver android:name="com.todoroo.astrid.timers.TimerActionExposer">
<intent-filter>
<action android:name="com.todoroo.astrid.REQUEST_ACTIONS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
<!-- reminders -->
<activity android:name="com.todoroo.astrid.reminders.ReminderPreferences"

@ -3,7 +3,7 @@
*/
package com.todoroo.astrid.api;
import android.content.Intent;
import android.app.PendingIntent;
import android.os.Parcel;
import android.os.Parcelable;
@ -23,7 +23,7 @@ public class TaskAction implements Parcelable {
/**
* Intent to call when invoking this operation
*/
public Intent intent = null;
public PendingIntent intent = null;
/**
* Create an EditOperation object
@ -33,7 +33,7 @@ public class TaskAction implements Parcelable {
* @param intent
* intent to invoke. {@link EXTRAS_TASK_ID} will be passed
*/
public TaskAction(String text, Intent intent) {
public TaskAction(String text, PendingIntent intent) {
super();
this.text = text;
this.intent = intent;
@ -64,8 +64,8 @@ public class TaskAction implements Parcelable {
* {@inheritDoc}
*/
public TaskAction createFromParcel(Parcel source) {
return new TaskAction(source.readString(), (Intent)source.readParcelable(
Intent.class.getClassLoader()));
return new TaskAction(source.readString(), (PendingIntent)source.readParcelable(
PendingIntent.class.getClassLoader()));
}
/**

@ -3,6 +3,7 @@
*/
package com.todoroo.astrid.api;
import android.content.Intent;
import android.os.Parcel;
import android.os.Parcelable;
import android.widget.RemoteViews;
@ -36,6 +37,11 @@ public final class TaskDecoration implements Parcelable {
*/
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="run"/>
<stringAttribute key="ch.zork.quicklaunch.mode" value="debug"/>
<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"/>

@ -72,4 +72,9 @@ public class NoteDetailExposer extends BroadcastReceiver implements DetailExpose
return notes;
}
@Override
public String getPluginIdentifier() {
return NotesPlugin.IDENTIFIER;
}
}

@ -135,4 +135,9 @@ public class RepeatDetailExposer extends BroadcastReceiver implements DetailExpo
return null;
}
@Override
public String getPluginIdentifier() {
return RepeatsPlugin.IDENTIFIER;
}
}

@ -88,4 +88,9 @@ public class MilkDetailExposer extends BroadcastReceiver implements DetailExpose
return result.substring(0, result.length() - TaskAdapter.DETAIL_SEPARATOR.length());
}
@Override
public String getPluginIdentifier() {
return Utilities.IDENTIFIER;
}
}

@ -52,4 +52,9 @@ public class TagDetailExposer extends BroadcastReceiver implements DetailExposer
return context.getString(R.string.tag_TLA_detail, tagList);
}
@Override
public String getPluginIdentifier() {
return TagsPlugin.IDENTIFIER;
}
}

@ -0,0 +1,76 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.timers;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
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.model.Task;
import com.todoroo.astrid.service.TaskService;
/**
* Exposes {@link TaskDecoration} for timers
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class TimerActionExposer extends BroadcastReceiver {
@Autowired
private TaskService taskService;
@Override
public void onReceive(Context context, Intent intent) {
long taskId = intent.getLongExtra(AstridApiConstants.EXTRAS_TASK_ID, -1);
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);
// was part of a broadcast for actions
if(AstridApiConstants.BROADCAST_REQUEST_ACTIONS.equals(intent.getAction())) {
String label;
if(task.getValue(Task.TIMER_START) == 0)
label = context.getString(R.string.TAE_startTimer);
else
label = context.getString(R.string.TAE_stopTimer);
Intent newIntent = new Intent(context, TimerActionExposer.class);
TaskAction action = new TaskAction(label,
PendingIntent.getBroadcast(context, 0, newIntent, 0));
// transmit
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_ACTIONS);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, TimerPlugin.IDENTIFIER);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, action);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId);
context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
} else {
// 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);
}
}
}

@ -30,7 +30,7 @@ public class TimerDecorationExposer extends BroadcastReceiver {
private static final int TASK_BG_COLOR = Color.argb(200, 220, 50, 0);
private static TaskService staticTaskService;
static TaskService staticTaskService;
private static HashMap<Long, TaskDecoration> decorations =
new HashMap<Long, TaskDecoration>();
private static HashMap<Long, Timer> timers =

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- See the file "LICENSE" for the full license governing this code. -->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Resources for built-in timers plug-in -->
<!-- Start Timer button -->
<string name="TAE_startTimer">Start Timer</string>
<!-- Stop Timer button -->
<string name="TAE_stopTimer">Stop Timer</string>
<!-- Task Detail (%s => time) -->
<string name="TDE_label">Elapsed Time: %s</string>
</resources>

@ -52,6 +52,7 @@ import com.todoroo.astrid.adapter.TaskAdapter;
import com.todoroo.astrid.adapter.TaskAdapter.ViewHolder;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.api.TaskAction;
import com.todoroo.astrid.api.TaskDecoration;
import com.todoroo.astrid.core.CoreFilterExposer;
import com.todoroo.astrid.dao.Database;
@ -380,6 +381,8 @@ public class TaskListActivity extends ListActivity implements OnScrollListener {
new IntentFilter(AstridApiConstants.BROADCAST_SEND_DETAILS));
registerReceiver(detailReceiver,
new IntentFilter(AstridApiConstants.BROADCAST_SEND_DECORATIONS));
registerReceiver(detailReceiver,
new IntentFilter(AstridApiConstants.BROADCAST_SEND_ACTIONS));
}
@Override
@ -400,16 +403,20 @@ public class TaskListActivity extends ListActivity implements OnScrollListener {
try {
Bundle extras = intent.getExtras();
long taskId = extras.getLong(AstridApiConstants.EXTRAS_TASK_ID);
String addOn = extras.getString(AstridApiConstants.EXTRAS_ADDON);
if(AstridApiConstants.BROADCAST_SEND_DECORATIONS.equals(intent.getAction())) {
TaskDecoration deco = extras.getParcelable(AstridApiConstants.EXTRAS_RESPONSE);
taskAdapter.addDecorations(getListView(), taskId,
deco);
} else {
taskAdapter.decorationManager.addNew(taskId, addOn, deco);
} else if(AstridApiConstants.BROADCAST_SEND_DETAILS.equals(intent.getAction())) {
String detail = extras.getString(AstridApiConstants.EXTRAS_RESPONSE);
taskAdapter.addDetails(getListView(), taskId,
extras.getBoolean(AstridApiConstants.EXTRAS_EXTENDED),
detail);
if(extras.getBoolean(AstridApiConstants.EXTRAS_EXTENDED))
taskAdapter.detailManager.addNew(taskId, addOn, detail);
else
taskAdapter.extendedDetailManager.addNew(taskId, addOn, detail);
} else if(AstridApiConstants.BROADCAST_SEND_ACTIONS.equals(intent.getAction())) {
TaskAction action = extras.getParcelable(AstridApiConstants.BROADCAST_SEND_ACTIONS);
taskAdapter.taskActionManager.addNew(taskId, addOn, action);
}
} catch (Exception e) {
exceptionService.reportError("receive-detail-" + //$NON-NLS-1$

@ -1,19 +1,18 @@
package com.todoroo.astrid.adapter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import android.app.ListActivity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Color;
import android.graphics.Paint;
import android.text.Html;
import android.view.ContextMenu;
@ -41,9 +40,9 @@ import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.andlib.utility.DialogUtilities;
import com.todoroo.andlib.utility.SoftHashMap;
import com.todoroo.astrid.activity.TaskEditActivity;
import com.todoroo.astrid.activity.TaskListActivity;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.api.DetailExposer;
import com.todoroo.astrid.api.TaskAction;
import com.todoroo.astrid.api.TaskDecoration;
import com.todoroo.astrid.model.Task;
import com.todoroo.astrid.notes.NoteDetailExposer;
@ -111,9 +110,11 @@ public class TaskAdapter extends CursorAdapter {
// --- task detail and decoration soft caches
private static final TaskCache<String> detailCache = new TaskCache<String>();
private static final TaskCache<String> extendedDetailCache = new TaskCache<String>();
private static final TaskCache<TaskDecoration> decorationCache = new TaskCache<TaskDecoration>();
public final DetailManager detailManager = new DetailManager(false);
public final DetailManager extendedDetailManager = new DetailManager(true);
public final DecorationManager decorationManager =
new DecorationManager();
public final TaskActionManager taskActionManager = new TaskActionManager();
/**
* Constructor
@ -285,18 +286,9 @@ public class TaskAdapter extends CursorAdapter {
}
// details and decorations
viewHolder.details.setText(""); //$NON-NLS-1$
if(viewHolder.decorations != null) {
for(View decoration: viewHolder.decorations)
viewHolder.taskRow.removeView(decoration);
}
viewHolder.taskRow.setBackgroundDrawable(null);
if(!isFling) {
// task details - send out a request for it
retrieveDetails(viewHolder, false);
// task decoration - send out a request for it
retrieveDecorations(viewHolder);
detailManager.request(viewHolder);
decorationManager.request(viewHolder);
}
}
@ -321,217 +313,198 @@ public class TaskAdapter extends CursorAdapter {
* ============================================================== add-ons
* ====================================================================== */
private void retrieveDetails(final ViewHolder viewHolder, final boolean extended) {
final long taskId = viewHolder.task.getId();
final TaskCache<String> cache = extended ? extendedDetailCache : detailCache;
ArrayList<String> list = cache.initialize(taskId);
if(list != null) {
spanifyAndAdd(viewHolder, extended, list);
return;
}
// request details
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_REQUEST_DETAILS);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_EXTENDED, extended);
activity.sendOrderedBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
// load internal details
new Thread() {
@Override
public void run() {
for(DetailExposer exposer : EXPOSERS) {
final String detail = exposer.getTaskDetails(activity,
taskId, extended);
if(detail == null)
continue;
final ArrayList<String> cacheList = cache.addIfNotExists(taskId, detail);
if(cacheList != null) {
if(taskId != viewHolder.task.getId())
continue;
activity.runOnUiThread(new Runnable() {
public void run() {
spanifyAndAdd(viewHolder, extended, cacheList);
}
});
}
}
};
}.start();
}
@SuppressWarnings("nls")
private void spanifyAndAdd(ViewHolder viewHolder, boolean extended, ArrayList<String> details) {
if(details == null)
return;
TextView view = extended ? viewHolder.extendedDetails : viewHolder.details;
view.setVisibility(details.size() > 0 ? View.VISIBLE : View.GONE);
if(details.size() == 0 || (extended && !viewHolder.expanded)) {
view.setVisibility(View.GONE);
return;
} else {
view.setVisibility(View.VISIBLE);
}
StringBuilder detailText = new StringBuilder();
for(Iterator<String> iterator = details.iterator(); iterator.hasNext(); ) {
detailText.append(iterator.next());
if(iterator.hasNext())
detailText.append(DETAIL_SEPARATOR);
}
String string = detailText.toString();
if(string.contains("<"))
view.setText(Html.fromHtml(string.trim().replace("\n", "<br>")));
else
view.setText(string.trim());
}
/**
* Called to tell the cache to be cleared
*/
public void flushCaches() {
detailCache.clear();
extendedDetailCache.clear();
decorationCache.clear();
detailManager.clearCache();
extendedDetailManager.clearCache();
decorationManager.clearCache();
}
/**
* Respond to a request to add details for a task
* AddOnManager for Details
* @author Tim Su <tim@todoroo.com>
*
* @param taskId
*/
public synchronized void addDetails(ListView listView, long taskId,
boolean extended, String detail) {
if(detail == null)
return;
public class DetailManager extends AddOnManager<String> {
final TaskCache<String> cache = extended ? extendedDetailCache : detailCache;
ArrayList<String> cacheList = cache.addIfNotExists(taskId, detail);
if(cacheList != null) {
// update view if it is visible
int length = listView.getChildCount();
for(int i = 0; i < length; i++) {
ViewHolder viewHolder = (ViewHolder) listView.getChildAt(i).getTag();
if(viewHolder == null || viewHolder.task.getId() != taskId)
continue;
spanifyAndAdd(viewHolder, extended, cacheList);
break;
}
private final boolean extended;
public DetailManager(boolean extended) {
this.extended = extended;
}
}
private final View.OnClickListener completeBoxListener = new View.OnClickListener() {
public void onClick(View v) {
ViewHolder viewHolder = (ViewHolder)((View)v.getParent().getParent()).getTag();
Task task = viewHolder.task;
completeTask(task, ((CheckBox)v).isChecked());
// set check box to actual action item state
setTaskAppearance(viewHolder, task.isCompleted());
@Override
Intent createBroadcastIntent(long taskId) {
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_REQUEST_DETAILS);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_EXTENDED, extended);
return broadcastIntent;
}
};
private void retrieveDecorations(final ViewHolder viewHolder) {
final long taskId = viewHolder.task.getId();
ArrayList<TaskDecoration> list = decorationCache.initialize(taskId);
if(list != null) {
decorate(viewHolder, list);
return;
@Override
public boolean request(final ViewHolder viewHolder) {
if(super.request(viewHolder)) {
final long taskId = viewHolder.task.getId();
// load internal details
new Thread() {
@Override
public void run() {
for(DetailExposer exposer : EXPOSERS) {
final String detail = exposer.getTaskDetails(activity,
taskId, extended);
if(detail == null)
continue;
final Collection<String> cacheList =
addIfNotExists(taskId, exposer.getPluginIdentifier(),
detail);
if(cacheList != null) {
if(taskId != viewHolder.task.getId())
continue;
activity.runOnUiThread(new Runnable() {
public void run() {
draw(viewHolder, cacheList);
}
});
}
}
};
}.start();
return true;
}
return false;
}
// request details
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_REQUEST_DECORATIONS);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId);
activity.sendOrderedBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
if(timer == null) {
timer = new Timer();
timer.scheduleAtFixedRate(decorationUpdater, 0, 1000L);
@SuppressWarnings("nls")
@Override
void draw(ViewHolder viewHolder, Collection<String> details) {
if(details == null)
return;
TextView view = extended ? viewHolder.extendedDetails : viewHolder.details;
view.setVisibility(!details.isEmpty() ? View.VISIBLE : View.GONE);
if(details.isEmpty() || (extended && !viewHolder.expanded)) {
view.setVisibility(View.GONE);
return;
} else {
view.setVisibility(View.VISIBLE);
}
StringBuilder detailText = new StringBuilder();
for(Iterator<String> iterator = details.iterator(); iterator.hasNext(); ) {
detailText.append(iterator.next());
if(iterator.hasNext())
detailText.append(DETAIL_SEPARATOR);
}
String string = detailText.toString();
if(string.contains("<"))
view.setText(Html.fromHtml(string.trim().replace("\n", "<br>")));
else
view.setText(string.trim());
}
}
private Timer timer = null;
}
/**
* Task to update decorations every second
* AddOnManager for TaskDecorations
*
* @author Tim Su <tim@todoroo.com>
*
*/
private final TimerTask decorationUpdater = new TimerTask() {
public class DecorationManager extends AddOnManager<TaskDecoration> {
@Override
public void run() {
ListView listView = activity.getListView();
int length = listView.getChildCount();
for(int i = 0; i < length; i++) {
ViewHolder viewHolder = (ViewHolder) listView.getChildAt(i).getTag();
ArrayList<TaskDecoration> list = decorationCache.get(viewHolder.task.getId());
if(list == null)
continue;
for(int j = 0; j < list.size(); j++) {
final TaskDecoration decoration = list.get(j);
if(decoration.decoration == null)
continue;
final View view = viewHolder.decorations[j];
if(view == null)
continue;
activity.runOnUiThread(new Runnable() {
public void run() {
decoration.decoration.reapply(activity, view);
}
});
Intent createBroadcastIntent(long taskId) {
Intent intent = new Intent(AstridApiConstants.BROADCAST_REQUEST_DECORATIONS);
intent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId);
return intent;
}
@Override
void draw(ViewHolder viewHolder, Collection<TaskDecoration> decorations) {
if(decorations == null || decorations.size() == 0)
return;
if(viewHolder.decorations != null) {
for(View view : viewHolder.decorations)
viewHolder.taskRow.removeView(view);
((View)viewHolder.taskRow.getParent()).setBackgroundColor(Color.TRANSPARENT);
}
int i = 0;
boolean colorSet = false;
viewHolder.decorations = new View[decorations.size()];
for(TaskDecoration decoration : decorations) {
if(decoration.color != 0 && !colorSet) {
colorSet = true;
((View)viewHolder.taskRow.getParent()).setBackgroundColor(decoration.color);
}
if(decoration.decoration != null) {
View view = decoration.decoration.apply(activity, viewHolder.taskRow);
viewHolder.decorations[i] = view;
switch(decoration.position) {
case TaskDecoration.POSITION_LEFT:
viewHolder.taskRow.addView(view, 1);
break;
case TaskDecoration.POSITION_RIGHT:
viewHolder.taskRow.addView(view, viewHolder.taskRow.getChildCount() - 2);
}
}
i++;
}
}
};
}
/**
* Respond to a request to add details for a task
* AddOnManager for TaskActions
*
* @author Tim Su <tim@todoroo.com>
*
* @param taskId
*/
public synchronized void addDecorations(ListView listView, long taskId,
TaskDecoration taskDecoration) {
if(taskDecoration == null)
return;
ArrayList<TaskDecoration> cacheList = decorationCache.addIfNotExists(taskId,
taskDecoration);
if(cacheList != null) {
// update view if it is visible
int length = listView.getChildCount();
for(int i = 0; i < length; i++) {
ViewHolder viewHolder = (ViewHolder) listView.getChildAt(i).getTag();
if(viewHolder == null || viewHolder.task.getId() != taskId)
continue;
decorate(viewHolder, cacheList);
break;
}
public class TaskActionManager extends AddOnManager<TaskAction> {
@Override
Intent createBroadcastIntent(long taskId) {
Intent intent = new Intent(AstridApiConstants.BROADCAST_REQUEST_ACTIONS);
intent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId);
return intent;
}
}
private void decorate(ViewHolder viewHolder, ArrayList<TaskDecoration> decorations) {
if(decorations == null || decorations.size() == 0)
return;
@Override
void draw(final ViewHolder viewHolder, Collection<TaskAction> actions) {
if(actions == null)
return;
// apply decorations in reverse so top priority appears at the ends &
// color is set bu the most important decoration
viewHolder.decorations = new View[decorations.size()];
for(int i = decorations.size() - 1; i >= 0; i--) {
TaskDecoration deco = decorations.get(i);
if(deco.color != 0)
((View)viewHolder.taskRow.getParent()).setBackgroundColor(deco.color);
if(deco.decoration != null) {
View view = deco.decoration.apply(activity, viewHolder.taskRow);
viewHolder.decorations[i] = view;
switch(deco.position) {
case TaskDecoration.POSITION_LEFT:
viewHolder.taskRow.addView(view, 1);
break;
case TaskDecoration.POSITION_RIGHT:
viewHolder.taskRow.addView(view, viewHolder.taskRow.getChildCount() - 2);
if(!viewHolder.expanded) {
viewHolder.actions.setVisibility(View.GONE);
return;
}
viewHolder.actions.setVisibility(View.VISIBLE);
viewHolder.actions.removeAllViews();
LinearLayout.LayoutParams params =
new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT,
LayoutParams.FILL_PARENT, 1f);
Button editButton = new Button(activity);
editButton.setText(R.string.TAd_actionEditTask);
editButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
Intent intent = new Intent(activity, TaskEditActivity.class);
long taskId = viewHolder.task.getId();
intent.putExtra(TaskEditActivity.ID_TOKEN, taskId);
activity.startActivity(intent);
}
});
editButton.setLayoutParams(params);
viewHolder.actions.addView(editButton);
for(TaskAction action : actions) {
Button view = new Button(activity);
view.setText(action.text);
view.setOnClickListener(new ActionClickListener(action));
view.setLayoutParams(params);
viewHolder.actions.addView(view);
}
}
}
@ -545,11 +518,39 @@ public class TaskAdapter extends CursorAdapter {
fontSize = Preferences.getIntegerFromString(R.string.p_fontSize);
}
class TaskRowListener implements OnCreateContextMenuListener, OnClickListener {
private final View.OnClickListener completeBoxListener = new View.OnClickListener() {
public void onClick(View v) {
ViewHolder viewHolder = (ViewHolder)((View)v.getParent().getParent()).getTag();
Task task = viewHolder.task;
completeTask(task, ((CheckBox)v).isChecked());
// set check box to actual action item state
setTaskAppearance(viewHolder, task.isCompleted());
}
};
private final class ActionClickListener implements View.OnClickListener {
TaskAction action;
public ActionClickListener(TaskAction action) {
this.action = action;
}
public void onClick(View v) {
try {
action.intent.send();
} catch (Exception e) {
// oh too bad.
}
}
};
private class TaskRowListener implements OnCreateContextMenuListener, OnClickListener {
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
// this is all a big sham. it's actually handled in Task List Activity
// this is all a big sham. it's actually handled in Task List
// Activity. however, we need this to be here.
}
@Override
@ -557,34 +558,10 @@ public class TaskAdapter extends CursorAdapter {
// expand view
final ViewHolder viewHolder = (ViewHolder)v.getTag();
viewHolder.expanded = !viewHolder.expanded;
LinearLayout actions = viewHolder.actions;
TextView extendedDetails = viewHolder.extendedDetails;
actions.setVisibility(viewHolder.expanded ? View.VISIBLE : View.GONE);
if(!viewHolder.expanded) {
viewHolder.extendedDetails.setVisibility(View.GONE);
return;
}
final long taskId = viewHolder.task.getId();
extendedDetails.setText(""); //$NON-NLS-1$
retrieveDetails(viewHolder, true);
if(actions.getChildCount() == 0) {
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT, 1f);
Button edit = new Button(activity);
edit.setText(R.string.TAd_actionEditTask);
edit.setLayoutParams(params);
edit.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(activity, TaskEditActivity.class);
intent.putExtra(TaskEditActivity.ID_TOKEN, taskId);
activity.startActivityForResult(intent, TaskListActivity.ACTIVITY_EDIT_TASK);
}
});
actions.addView(edit);
if(viewHolder.expanded) {
extendedDetailManager.request(viewHolder);
taskActionManager.request(viewHolder);
}
}
}
@ -646,13 +623,71 @@ public class TaskAdapter extends CursorAdapter {
}
/* ======================================================================
* =============================================================== caches
* ========================================================= addon helper
* ====================================================================== */
private static class TaskCache<TYPE> {
abstract private class AddOnManager<TYPE> {
private final Map<Long, HashMap<String, TYPE>> cache =
Collections.synchronizedMap(new SoftHashMap<Long,
HashMap<String, TYPE>>());
// --- interface
/**
* Request add-ons for the given task
* @return true if cache miss, false if cache hit
*/
public boolean request(ViewHolder viewHolder) {
long taskId = viewHolder.task.getId();
Collection<TYPE> list = initialize(taskId);
if(list != null) {
draw(viewHolder, list);
return false;
}
// request details
Intent broadcastIntent = createBroadcastIntent(taskId);
activity.sendOrderedBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
draw(viewHolder, get(taskId));
return true;
}
/** creates a broadcast intent for requesting */
abstract Intent createBroadcastIntent(long taskId);
/** updates the given view */
abstract void draw(ViewHolder viewHolder, Collection<TYPE> list);
/** on receive an intent */
public void addNew(long taskId, String addOn, TYPE item) {
if(item == null)
return;
Collection<TYPE> cacheList = addIfNotExists(taskId, addOn, item);
if(cacheList != null) {
ListView listView = activity.getListView();
// update view if it is visible
int length = listView.getChildCount();
for(int i = 0; i < length; i++) {
ViewHolder viewHolder = (ViewHolder) listView.getChildAt(i).getTag();
if(viewHolder == null || viewHolder.task.getId() != taskId)
continue;
draw(viewHolder, cacheList);
break;
}
}
}
private final Map<Long, ArrayList<TYPE>> cache =
Collections.synchronizedMap(new SoftHashMap<Long, ArrayList<TYPE>>());
/**
* Clears the cache
*/
public void clearCache() {
cache.clear();
}
// --- internal goodies
/**
* Retrieves a list. If it doesn't exist, list is created, but
@ -660,10 +695,10 @@ public class TaskAdapter extends CursorAdapter {
* @param taskId
* @return list if there was already one
*/
public ArrayList<TYPE> initialize(long taskId) {
protected Collection<TYPE> initialize(long taskId) {
if(cache.containsKey(taskId))
return cache.get(taskId);
cache.put(taskId, new ArrayList<TYPE>());
return get(taskId);
cache.put(taskId, new HashMap<String, TYPE>());
return null;
}
@ -671,25 +706,26 @@ public class TaskAdapter extends CursorAdapter {
* Adds an item to the cache if it doesn't exist
* @param taskId
* @param item
* @return list if item was added, null if it already existed
* @return iterator if item was added, null if it already existed
*/
public ArrayList<TYPE> addIfNotExists(long taskId, TYPE item) {
ArrayList<TYPE> list = cache.get(taskId);
if(list == null || list.contains(item))
protected Collection<TYPE> addIfNotExists(long taskId, String addOn,
TYPE item) {
HashMap<String, TYPE> list = cache.get(taskId);
if(list == null)
return null;
list.add(item);
return list;
if(list.containsKey(addOn) && list.get(addOn).equals(item))
return null;
list.put(addOn, item);
return get(taskId);
}
/**
* Clears the cache
* Gets an item at the given index
* @param taskId
* @return
*/
public void clear() {
cache.clear();
}
public ArrayList<TYPE> get(long taskId) {
return cache.get(taskId);
protected Collection<TYPE> get(long taskId) {
return cache.get(taskId).values();
}
}

@ -21,4 +21,6 @@ public interface DetailExposer {
*/
public String getTaskDetails(Context context, long id, boolean extended);
public String getPluginIdentifier();
}

@ -84,7 +84,7 @@ public final class Task extends AbstractModel {
public static final IntegerProperty ELAPSED_SECONDS = new IntegerProperty(
TABLE, "elapsedSeconds");
public static final IntegerProperty TIMER_START = new IntegerProperty(
public static final LongProperty TIMER_START = new LongProperty(
TABLE, "timerStart");
public static final IntegerProperty POSTPONE_COUNT = new IntegerProperty(

Loading…
Cancel
Save