You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tasks/astrid/src/com/todoroo/astrid/adapter/TaskAdapter.java

947 lines
34 KiB
Java

package com.todoroo.astrid.adapter;
import greendroid.widget.AsyncImageView;
import greendroid.widget.QuickAction;
import greendroid.widget.QuickActionBar;
import greendroid.widget.QuickActionWidget;
import greendroid.widget.QuickActionWidget.OnQuickActionClickListener;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.atomic.AtomicReference;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.ListActivity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Paint;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.text.Html;
import android.text.Html.ImageGetter;
import android.text.Html.TagHandler;
import android.text.Spannable;
import android.text.Spanned;
import android.text.TextUtils;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnCreateContextMenuListener;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.CursorAdapter;
import android.widget.Filterable;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import com.timsu.astrid.R;
import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.service.ExceptionService;
import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.andlib.utility.Preferences;
import com.todoroo.astrid.activity.TaskEditActivity;
import com.todoroo.astrid.activity.TaskListActivity;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.api.TaskAction;
import com.todoroo.astrid.api.TaskDecoration;
import com.todoroo.astrid.api.TaskDecorationExposer;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.helper.TaskAdapterAddOnManager;
import com.todoroo.astrid.notes.NoteViewingActivity;
import com.todoroo.astrid.notes.NotesDecorationExposer;
import com.todoroo.astrid.service.TaskService;
import com.todoroo.astrid.timers.TimerDecorationExposer;
import com.todoroo.astrid.utility.Constants;
/**
* Adapter for displaying a user's tasks as a list
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class TaskAdapter extends CursorAdapter implements Filterable {
public interface OnCompletedTaskListener {
public void onCompletedTask(Task item, boolean newState);
}
public static final String DETAIL_SEPARATOR = " | "; //$NON-NLS-1$
public static final String BROADCAST_EXTRA_TASK = "model"; //$NON-NLS-1$
// --- other constants
/** Properties that need to be read from the action item */
public static final Property<?>[] PROPERTIES = new Property<?>[] {
Task.ID,
Task.TITLE,
Task.FLAGS,
Task.IMPORTANCE,
Task.DUE_DATE,
Task.COMPLETION_DATE,
Task.HIDE_UNTIL,
Task.DELETION_DATE,
Task.DETAILS,
Task.ELAPSED_SECONDS,
Task.TIMER_START,
Task.NOTES,
Task.USER_ID,
Task.USER
};
private static int[] IMPORTANCE_COLORS = null;
// --- instance variables
@Autowired
private ExceptionService exceptionService;
@Autowired
private TaskService taskService;
protected final ListActivity activity;
protected final HashMap<Long, Boolean> completedItems = new HashMap<Long, Boolean>(0);
private OnCompletedTaskListener onCompletedTaskListener = null;
public boolean isFling = false;
private final int resource;
private final LayoutInflater inflater;
private int fontSize;
private DetailLoaderThread detailLoader;
private final AtomicReference<String> query;
// the task that's expanded
private long expanded = -1;
// quick action bar
private QuickActionWidget mBar;
private final QuickActionListener mBarListener = new QuickActionListener();
// --- task detail and decoration soft caches
public final DecorationManager decorationManager;
public final TaskActionManager taskActionManager;
/**
* Constructor
*
* @param activity
* @param resource
* layout resource to inflate
* @param c
* database cursor
* @param autoRequery
* whether cursor is automatically re-queried on changes
* @param onCompletedTaskListener
* task listener. can be null
*/
public TaskAdapter(ListActivity activity, int resource,
Cursor c, AtomicReference<String> query, boolean autoRequery,
OnCompletedTaskListener onCompletedTaskListener) {
super(activity, c, autoRequery);
DependencyInjectionService.getInstance().inject(this);
inflater = (LayoutInflater) activity.getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
this.query = query;
this.resource = resource;
this.activity = activity;
this.onCompletedTaskListener = onCompletedTaskListener;
fontSize = Preferences.getIntegerFromString(R.string.p_fontSize, 20);
synchronized(TaskAdapter.class) {
if(IMPORTANCE_COLORS == null)
IMPORTANCE_COLORS = Task.getImportanceColors(activity.getResources());
}
detailLoader = new DetailLoaderThread();
detailLoader.start();
decorationManager = new DecorationManager();
taskActionManager = new TaskActionManager();
}
/* ======================================================================
* =========================================================== filterable
* ====================================================================== */
@Override
public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
if (getFilterQueryProvider() != null) {
return getFilterQueryProvider().runQuery(constraint);
}
// perform query
TodorooCursor<Task> newCursor = taskService.fetchFiltered(
query.get(), constraint, TaskAdapter.PROPERTIES);
activity.startManagingCursor(newCursor);
return newCursor;
}
/* ======================================================================
* =========================================================== view setup
* ====================================================================== */
/** Creates a new view for use in the list view */
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
ViewGroup view = (ViewGroup)inflater.inflate(resource, parent, false);
// create view holder
ViewHolder viewHolder = new ViewHolder();
viewHolder.task = new Task();
viewHolder.view = view;
viewHolder.nameView = (TextView)view.findViewById(R.id.title);
viewHolder.picture = (AsyncImageView)view.findViewById(R.id.picture);
viewHolder.completeBox = (CheckBox)view.findViewById(R.id.completeBox);
viewHolder.dueDate = (TextView)view.findViewById(R.id.dueDate);
viewHolder.details = (TextView)view.findViewById(R.id.details);
viewHolder.extendedDetails = (TextView)view.findViewById(R.id.extendedDetails);
viewHolder.actions = (LinearLayout)view.findViewById(R.id.actions);
viewHolder.taskRow = (LinearLayout)view.findViewById(R.id.task_row);
viewHolder.importance = (View)view.findViewById(R.id.importance);
view.setTag(viewHolder);
for(int i = 0; i < view.getChildCount(); i++)
view.getChildAt(i).setTag(viewHolder);
if(viewHolder.details != null)
viewHolder.details.setTag(viewHolder);
// add UI component listeners
addListeners(view);
// populate view content
bindView(view, context, cursor);
return view;
}
/** Populates a view with content */
@Override
public void bindView(View view, Context context, Cursor c) {
TodorooCursor<Task> cursor = (TodorooCursor<Task>)c;
ViewHolder viewHolder = ((ViewHolder)view.getTag());
Task task = viewHolder.task;
task.clear();
task.readFromCursor(cursor);
setFieldContentsAndVisibility(view);
setTaskAppearance(viewHolder, task);
}
/** Helper method to set the visibility based on if there's stuff inside */
private static void setVisibility(TextView v) {
if(v.getText().length() > 0)
v.setVisibility(View.VISIBLE);
else
v.setVisibility(View.GONE);
}
/**
* View Holder saves a lot of findViewById lookups.
*
* @author Tim Su <tim@todoroo.com>
*
*/
public static class ViewHolder {
public Task task;
public View view;
public TextView nameView;
public CheckBox completeBox;
public AsyncImageView picture;
public TextView dueDate;
public TextView details;
public TextView extendedDetails;
public View importance;
public LinearLayout actions;
public LinearLayout taskRow;
public View[] decorations;
}
/** Helper method to set the contents and visibility of each field */
public synchronized void setFieldContentsAndVisibility(View view) {
Resources r = activity.getResources();
ViewHolder viewHolder = (ViewHolder)view.getTag();
Task task = viewHolder.task;
// name
final TextView nameView = viewHolder.nameView; {
String nameValue = task.getValue(Task.TITLE);
long hiddenUntil = task.getValue(Task.HIDE_UNTIL);
if(task.getValue(Task.DELETION_DATE) > 0)
nameValue = r.getString(R.string.TAd_deletedFormat, nameValue);
if(hiddenUntil > DateUtilities.now())
nameValue = r.getString(R.string.TAd_hiddenFormat, nameValue);
nameView.setText(nameValue);
}
// due date / completion date
final TextView dueDateView = viewHolder.dueDate; {
if(!task.isCompleted() && task.hasDueDate()) {
long dueDate = task.getValue(Task.DUE_DATE);
long secondsLeft = dueDate - DateUtilities.now();
if(secondsLeft > 0) {
dueDateView.setTextAppearance(activity, R.style.TextAppearance_TAd_ItemDueDate);
} else {
dueDateView.setTextAppearance(activity, R.style.TextAppearance_TAd_ItemDueDate_Overdue);
}
String dateValue = formatDate(dueDate);
dueDateView.setText(dateValue);
setVisibility(dueDateView);
} else if(task.isCompleted()) {
String dateValue = DateUtilities.getDateStringWithTime(activity, new Date(task.getValue(Task.COMPLETION_DATE)));
dueDateView.setText(r.getString(R.string.TAd_completed, dateValue));
dueDateView.setTextAppearance(activity, R.style.TextAppearance_TAd_ItemDueDate_Completed);
setVisibility(dueDateView);
} else {
dueDateView.setVisibility(View.GONE);
}
}
// complete box
final CheckBox completeBox = viewHolder.completeBox; {
// show item as completed if it was recently checked
if(completedItems.get(task.getId()) != null) {
task.setValue(Task.COMPLETION_DATE,
completedItems.get(task.getId()) ? DateUtilities.now() : 0);
}
completeBox.setChecked(task.isCompleted());
// disable checkbox if task is readonly
completeBox.setEnabled(!viewHolder.task.getFlag(Task.FLAGS, Task.FLAG_IS_READONLY));
}
// image view
final AsyncImageView pictureView = viewHolder.picture; {
if(task.getValue(Task.USER_ID) == 0) {
pictureView.setVisibility(View.GONE);
} else {
pictureView.setVisibility(View.VISIBLE);
pictureView.setUrl(null);
try {
JSONObject user = new JSONObject(task.getValue(Task.USER));
pictureView.setUrl(user.optString("picture")); //$NON-NLS-1$
} catch (JSONException e) {
Log.w("astrid", "task-adapter-image", e); //$NON-NLS-1$ //$NON-NLS-2$
}
}
}
// importance bar
final View importanceView = viewHolder.importance; {
int value = task.getValue(Task.IMPORTANCE);
if(value < IMPORTANCE_COLORS.length)
importanceView.setBackgroundColor(IMPORTANCE_COLORS[value]);
else
importanceView.setBackgroundColor(0);
}
String details;
if(viewHolder.details != null) {
if(taskDetailLoader.containsKey(task.getId()))
details = taskDetailLoader.get(task.getId()).toString();
else
details = task.getValue(Task.DETAILS);
if(TextUtils.isEmpty(details) || DETAIL_SEPARATOR.equals(details) || task.isCompleted()) {
viewHolder.details.setVisibility(View.GONE);
} else {
viewHolder.details.setVisibility(View.VISIBLE);
while(details.startsWith(DETAIL_SEPARATOR))
details = details.substring(DETAIL_SEPARATOR.length());
viewHolder.details.setText(convertToHtml(details.trim().replace("\n", //$NON-NLS-1$
"<br>"), detailImageGetter, null)); //$NON-NLS-1$
}
}
// details and decorations, expanded
decorationManager.request(viewHolder);
}
protected TaskRowListener listener = new TaskRowListener();
/**
* Set listeners for this view. This is called once per view when it is
* created.
*/
protected void addListeners(final View container) {
ViewHolder viewHolder = (ViewHolder)container.getTag();
// check box listener
viewHolder.completeBox.setOnClickListener(completeBoxListener);
// context menu listener
container.setOnCreateContextMenuListener(listener);
// tap listener
container.setOnClickListener(listener);
}
/* ======================================================================
* ============================================================== details
* ====================================================================== */
private final HashMap<String, Spanned> htmlCache = new HashMap<String, Spanned>(8);
private Spanned convertToHtml(String string, ImageGetter imageGetter, TagHandler tagHandler) {
if(!htmlCache.containsKey(string)) {
Spanned html;
try {
html = Html.fromHtml(string, imageGetter, tagHandler);
} catch (RuntimeException e) {
html = Spannable.Factory.getInstance().newSpannable(string);
}
htmlCache.put(string, html);
return html;
}
return htmlCache.get(string);
}
private final HashMap<Long, String> dateCache = new HashMap<Long, String>(8);
private String formatDate(long date) {
if(dateCache.containsKey(date))
return dateCache.get(date);
String string;
if(Task.hasDueTime(date))
string = DateUtilities.getDateStringWithTimeAndWeekday(activity, new Date(date));
else
string = DateUtilities.getDateStringWithWeekday(activity, new Date(date));
dateCache.put(date, string);
return string;
}
// implementation note: this map is really costly if users have
// a large number of tasks to load, since it all goes into memory.
// it's best to do this, though, in order to append details to each other
private final Map<Long, StringBuilder> taskDetailLoader = Collections.synchronizedMap(new HashMap<Long, StringBuilder>(0));
public class DetailLoaderThread extends Thread {
@Override
public void run() {
// for all of the tasks returned by our cursor, verify details
AndroidUtilities.sleepDeep(500L);
TodorooCursor<Task> fetchCursor = taskService.fetchFiltered(
query.get(), null, Task.ID, Task.DETAILS, Task.DETAILS_DATE,
Task.MODIFICATION_DATE, Task.COMPLETION_DATE);
try {
Random random = new Random();
Task task = new Task();
for(fetchCursor.moveToFirst(); !fetchCursor.isAfterLast(); fetchCursor.moveToNext()) {
task.clear();
task.readFromCursor(fetchCursor);
if(task.isCompleted())
continue;
if(detailsAreRecentAndUpToDate(task)) {
// even if we are up to date, randomly load a fraction
if(random.nextFloat() < 0.1) {
taskDetailLoader.put(task.getId(),
new StringBuilder(task.getValue(Task.DETAILS)));
requestNewDetails(task);
if(Constants.DEBUG)
System.err.println("Refreshing details: " + task.getId()); //$NON-NLS-1$
}
continue;
} else if(Constants.DEBUG) {
System.err.println("Forced loading of details: " + task.getId() + //$NON-NLS-1$
"\n details: " + new Date(task.getValue(Task.DETAILS_DATE)) + //$NON-NLS-1$
"\n modified: " + new Date(task.getValue(Task.MODIFICATION_DATE))); //$NON-NLS-1$
}
addTaskToLoadingArray(task);
task.setValue(Task.DETAILS, DETAIL_SEPARATOR);
task.setValue(Task.DETAILS_DATE, DateUtilities.now());
taskService.save(task);
requestNewDetails(task);
}
} catch (Exception e) {
// suppress silently
} finally {
fetchCursor.close();
}
}
private boolean detailsAreRecentAndUpToDate(Task task) {
return task.getValue(Task.DETAILS_DATE) >= task.getValue(Task.MODIFICATION_DATE) &&
!TextUtils.isEmpty(task.getValue(Task.DETAILS));
}
private void addTaskToLoadingArray(Task task) {
StringBuilder detailStringBuilder = new StringBuilder();
taskDetailLoader.put(task.getId(), detailStringBuilder);
}
private void requestNewDetails(Task task) {
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_REQUEST_DETAILS);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, task.getId());
activity.sendOrderedBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
}
}
/**
* Add detail to a task
*
* @param id
* @param detail
*/
public void addDetails(long id, String detail) {
final StringBuilder details = taskDetailLoader.get(id);
if(details == null)
return;
synchronized(details) {
if(details.toString().contains(detail))
return;
if(details.length() > 0)
details.append(DETAIL_SEPARATOR);
details.append(detail);
Task task = new Task();
task.setId(id);
task.setValue(Task.DETAILS, details.toString());
task.setValue(Task.DETAILS_DATE, DateUtilities.now());
taskService.save(task);
}
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
notifyDataSetChanged();
}
});
}
private final ImageGetter detailImageGetter = new ImageGetter() {
private final HashMap<Integer, Drawable> cache =
new HashMap<Integer, Drawable>(3);
public Drawable getDrawable(String source) {
Resources r = activity.getResources();
int drawable = r.getIdentifier("drawable/" + source, null, Constants.PACKAGE); //$NON-NLS-1$
if(drawable == 0)
return null;
Drawable d;
if(!cache.containsKey(drawable)) {
d = r.getDrawable(drawable);
d.setBounds(0,0,d.getIntrinsicWidth(),d.getIntrinsicHeight());
cache.put(drawable, d);
} else
d = cache.get(drawable);
return d;
}
};
/* ======================================================================
* ============================================================== add-ons
* ====================================================================== */
/**
* Called to tell the cache to be cleared
*/
public void flushCaches() {
completedItems.clear();
decorationManager.clearCache();
taskActionManager.clearCache();
taskDetailLoader.clear();
detailLoader = new DetailLoaderThread();
detailLoader.start();
}
/**
* Called to tell the cache to be cleared
*/
public void flushSpecific(long taskId) {
completedItems.put(taskId, null);
decorationManager.clearCache(taskId);
taskActionManager.clearCache(taskId);
taskDetailLoader.remove(taskId);
}
/**
* AddOnManager for TaskDecorations
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class DecorationManager extends TaskAdapterAddOnManager<TaskDecoration> {
public DecorationManager() {
super(activity);
}
private final TaskDecorationExposer[] exposers = new TaskDecorationExposer[] {
new TimerDecorationExposer(),
new NotesDecorationExposer(),
};
/**
* Request add-ons for the given task
* @return true if cache miss, false if cache hit
*/
@Override
public boolean request(ViewHolder viewHolder) {
long taskId = viewHolder.task.getId();
Collection<TaskDecoration> list = initialize(taskId);
if(list != null) {
draw(viewHolder, taskId, list);
return false;
}
// request details
draw(viewHolder, taskId, get(taskId));
for(TaskDecorationExposer exposer : exposers) {
TaskDecoration deco = exposer.expose(viewHolder.task);
if(deco != null) {
addNew(viewHolder.task.getId(), exposer.getAddon(), deco);
}
}
return true;
}
@Override
protected void draw(ViewHolder viewHolder, long taskId, Collection<TaskDecoration> decorations) {
if(decorations == null || viewHolder.task.getId() != taskId)
return;
reset(viewHolder, taskId);
if(decorations.size() == 0)
return;
int i = 0;
boolean colorSet = false;
if(viewHolder.decorations == null || viewHolder.decorations.length != decorations.size())
viewHolder.decorations = new View[decorations.size()];
for(TaskDecoration decoration : decorations) {
if(decoration.color != 0 && !colorSet) {
colorSet = true;
viewHolder.view.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, 2);
break;
case TaskDecoration.POSITION_RIGHT:
viewHolder.taskRow.addView(view, viewHolder.taskRow.getChildCount());
}
}
i++;
}
}
@Override
protected void reset(ViewHolder viewHolder, long taskId) {
if(viewHolder.decorations != null) {
for(View view : viewHolder.decorations)
viewHolder.taskRow.removeView(view);
}
if(taskId == expanded)
viewHolder.view.setBackgroundResource(R.drawable.list_selector_highlighted);
else
viewHolder.view.setBackgroundResource(android.R.drawable.list_selector_background);
}
@Override
protected Intent createBroadcastIntent(Task task) {
return null;
}
}
/**
* AddOnManager for TaskActions
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class TaskActionManager extends TaskAdapterAddOnManager<TaskAction> {
private final Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_REQUEST_ACTIONS);
public TaskActionManager() {
super(activity);
}
@Override
protected Intent createBroadcastIntent(Task task) {
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, task.getId());
return broadcastIntent;
}
@Override
public synchronized void addNew(long taskId, String addOn, final TaskAction item) {
addIfNotExists(taskId, addOn, item);
if(mBar != null) {
ListView listView = activity.getListView();
ViewHolder myHolder = 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;
myHolder = viewHolder;
break;
}
if(myHolder != null) {
final ViewHolder viewHolder = myHolder;
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
mBarListener.addWithAction(item);
if (!viewHolder.task.getFlag(Task.FLAGS, Task.FLAG_IS_READONLY))
mBar.show(viewHolder.view);
}
});
}
}
}
@Override
public Collection<TaskAction> get(long taskId) {
return super.get(taskId);
}
@Override
protected void draw(final ViewHolder viewHolder, final long taskId, Collection<TaskAction> actions) {
// do not draw
}
@Override
protected void reset(ViewHolder viewHolder, long taskId) {
// do not draw
}
}
/* ======================================================================
* ======================================================= event handlers
* ====================================================================== */
@Override
public void notifyDataSetChanged() {
super.notifyDataSetChanged();
fontSize = Preferences.getIntegerFromString(R.string.p_fontSize, 20);
}
protected 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);
}
};
private final class QuickActionListener implements OnQuickActionClickListener {
private final HashMap<Integer, TaskAction> positionActionMap =
new HashMap<Integer, TaskAction>(2);
private long taskId;
private int itemCount = 0;
private int iconWidth;
public void initialize(long newTaskId) {
this.taskId = newTaskId;
itemCount = 0;
positionActionMap.clear();
mBar.setOnQuickActionClickListener(this);
iconWidth = activity.getResources().getDrawable(R.drawable.tango_edit).getIntrinsicHeight();
}
public void addWithAction(TaskAction item) {
Bitmap scaledBitmap = Bitmap.createScaledBitmap(item.icon, iconWidth, iconWidth, true);
Drawable drawable = new BitmapDrawable(activity.getResources(), scaledBitmap);
addWithAction(new QuickAction(drawable, item.text), item);
}
public void addWithAction(QuickAction quickAction, TaskAction taskAction) {
positionActionMap.put(itemCount++, taskAction);
mBar.addQuickAction(quickAction);
}
public void onQuickActionClicked(QuickActionWidget widget, int position){
if(mBar != null)
mBar.dismiss();
mBar = null;
if(position == 0) {
Intent intent = new Intent(activity, TaskEditActivity.class);
intent.putExtra(TaskEditActivity.TOKEN_ID, taskId);
activity.startActivityForResult(intent, TaskListActivity.ACTIVITY_EDIT_TASK);
} else {
flushSpecific(taskId);
try {
TaskAction taskAction = positionActionMap.get(position);
if(taskAction != null) {
taskAction.intent.send();
}
} catch (Exception e) {
exceptionService.displayAndReportError(activity,
"Error launching action", e); //$NON-NLS-1$
}
}
clearSelection();
notifyDataSetChanged();
}
}
private class TaskRowListener implements OnCreateContextMenuListener, OnClickListener {
// prepare quick action bar
private void prepareQuickActionBar(ViewHolder viewHolder, Collection<TaskAction> collection){
mBar = new QuickActionBar(viewHolder.view.getContext());
QuickAction editAction = new QuickAction(activity, R.drawable.tango_edit,
activity.getString(R.string.TAd_actionEditTask));
mBarListener.initialize(viewHolder.task.getId());
mBarListener.addWithAction(editAction, null);
if(collection != null) {
for(TaskAction item : collection) {
mBarListener.addWithAction(item);
}
}
}
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
// this is all a big sham. it's actually handled in Task List
// Activity. however, we need this to be here.
}
@Override
public void onClick(View v) {
// expand view (unless deleted)
final ViewHolder viewHolder = (ViewHolder)v.getTag();
if(viewHolder.task.isDeleted())
return;
long taskId = viewHolder.task.getId();
if(isIntroTask(viewHolder.task)) {
Intent intent = new Intent(ContextManager.getContext(), NoteViewingActivity.class);
intent.putExtra(NoteViewingActivity.EXTRA_TASK, viewHolder.task);
activity.startActivity(intent);
return;
}
Collection<TaskAction> actions = taskActionManager.get(taskId);
prepareQuickActionBar(viewHolder, actions);
//mBarAnchor = v;
if(actions != null && !viewHolder.task.getFlag(Task.FLAGS, Task.FLAG_IS_READONLY))
mBar.show(v);
taskActionManager.request(viewHolder);
notifyDataSetChanged();
}
private boolean isIntroTask(Task task) {
if(task.getId() <= 3)
return true;
return false;
}
}
/**
* Call me when the parent presses trackpad
*/
public void onTrackpadPressed(View container) {
if(container == null)
return;
final CheckBox completeBox = ((CheckBox)container.findViewById(R.id.completeBox));
completeBox.performClick();
}
/** Helper method to adjust a tasks' appearance if the task is completed or
* uncompleted.
*
* @param actionItem
* @param name
* @param progress
*/
void setTaskAppearance(ViewHolder viewHolder, Task task) {
boolean state = task.isCompleted();
viewHolder.completeBox.setChecked(state);
viewHolder.completeBox.setEnabled(!viewHolder.task.getFlag(Task.FLAGS, Task.FLAG_IS_READONLY));
TextView name = viewHolder.nameView;
if(state) {
name.setPaintFlags(name.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
name.setTextAppearance(activity, R.style.TextAppearance_TAd_ItemTitle_Completed);
} else {
name.setPaintFlags(name.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG);
name.setTextAppearance(activity, R.style.TextAppearance_TAd_ItemTitle);
}
name.setTextSize(fontSize);
float detailTextSize = Math.max(12, fontSize * 14 / 20);
if(viewHolder.details != null)
viewHolder.details.setTextSize(detailTextSize);
if(viewHolder.dueDate != null)
viewHolder.dueDate.setTextSize(detailTextSize);
}
/**
* This method is called when user completes a task via check box or other
* means
*
* @param container
* container for the action item
* @param newState
* state that this task should be set to
* @param completeBox
* the box that was clicked. can be null
*/
protected void completeTask(final Task task, final boolean newState) {
if(task == null)
return;
if (newState != task.isCompleted()) {
completedItems.put(task.getId(), newState);
taskService.setComplete(task, newState);
if(onCompletedTaskListener != null)
onCompletedTaskListener.onCompletedTask(task, newState);
}
}
public void clearSelection() {
expanded = -1;
}
}