New layout for the task adapter row (task_adapter_row_simple). Need to A/B test

pull/14/head
Sam Bosley 13 years ago
parent a73776b5ea
commit ec8afe8bd2

@ -158,6 +158,11 @@ public abstract class Property<TYPE> extends Field implements Cloneable {
return visitor.visitString(this, data); return visitor.visitString(this, data);
} }
@Override
public StringProperty as(String newAlias) {
return (StringProperty) super.as(newAlias);
}
public Criterion in(final String[] value) { public Criterion in(final String[] value) {
final Field field = this; final Field field = this;
return new Criterion(Operator.in) { return new Criterion(Operator.in) {

@ -68,7 +68,6 @@ public class ReusableTaskAdapter extends TaskAdapter {
task.readFromCursor(cursor); task.readFromCursor(cursor);
viewHolder.title.setText(task.getValue(Task.TITLE)); viewHolder.title.setText(task.getValue(Task.TITLE));
view.setMinimumHeight(minRowHeight);
} }
public static class ReusableTaskViewHolder { public static class ReusableTaskViewHolder {

@ -0,0 +1,146 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
** Copyright (c) 2012 Todoroo Inc
**
** See the file "LICENSE" for the full license governing this code.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:astrid="http://schemas.android.com/apk/res/com.timsu.astrid"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="?attr/asTaskRowSelector"
android:orientation="horizontal">
<RelativeLayout
android:id="@+id/rowBody"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dip"
android:layout_marginBottom="5dip"
android:minHeight="40dip">
<ImageView
android:id="@+id/pictureBorder"
android:layout_width="35dip"
android:layout_height="35dip"
android:layout_marginLeft="8dip"
android:layout_centerVertical="true"
android:background="@drawable/icn_default_person_image"
android:scaleType="center"
android:visibility="gone" >
</ImageView>
<com.todoroo.astrid.helper.AsyncImageView
android:id="@+id/picture"
android:layout_width="35dip"
android:layout_height="35dip"
android:padding="3dip"
android:layout_marginLeft="8dip"
android:layout_centerVertical="true"
astrid:defaultSrc="@drawable/icn_default_person_image"
android:scaleType="fitCenter"
android:visibility="gone" />
<com.todoroo.astrid.ui.CheckableImageView
android:id="@+id/completeBox"
android:layout_width="45dip"
android:layout_marginLeft="4dip"
android:layout_height="wrap_content"
android:minHeight="40dip"
android:scaleType="center"
android:gravity="center"
android:layout_centerVertical="true" />
<LinearLayout
android:id="@+id/task_row"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="52dip"
android:layout_centerVertical="true"
android:orientation="horizontal"
android:gravity="center_vertical"
android:paddingLeft="4dip" >
<!-- task name -->
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="100"
android:orientation="vertical">
<TextView
android:id="@+id/title"
style="@style/TextAppearance.TAd_ItemTitle"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:maxLines="1"
android:singleLine="true"
android:ellipsize="end" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/dueDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:ellipsize="end"
android:gravity="center_vertical" />
<TextView
android:id="@+id/tagsDisplay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:ellipsize="end"
android:gravity="center_vertical"
style="@style/TextAppearance.TAd_ItemDueDate"/>
</LinearLayout>
</LinearLayout>
<!-- due date -->
<LinearLayout
android:id="@+id/taskActionContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dip"
android:orientation="vertical"
android:gravity="right|center_vertical">
<ImageView
android:id="@+id/taskActionIcon"
android:layout_width="20dip"
android:layout_height="20dip"
android:scaleType="fitCenter"
android:visibility="gone"/>
</LinearLayout>
</LinearLayout>
<!-- details line 1 -->
<TextView
android:id="@+id/details1"
style="@style/TextAppearance.TAd_ItemDetails"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@id/task_row"
android:layout_below="@id/task_row"
android:visibility="gone" />
<!-- details line 2 -->
<TextView
android:id="@+id/details2"
style="@style/TextAppearance.TAd_ItemDetails"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@id/task_row"
android:layout_below="@id/details1"
android:layout_marginLeft="5dip"
android:visibility="gone" />
</RelativeLayout>
</LinearLayout>

@ -101,6 +101,7 @@ import com.todoroo.astrid.service.ThemeService;
import com.todoroo.astrid.service.UpgradeService; import com.todoroo.astrid.service.UpgradeService;
import com.todoroo.astrid.subtasks.SubtasksListFragment; import com.todoroo.astrid.subtasks.SubtasksListFragment;
import com.todoroo.astrid.sync.SyncProviderPreferences; import com.todoroo.astrid.sync.SyncProviderPreferences;
import com.todoroo.astrid.tags.TagService;
import com.todoroo.astrid.taskrabbit.TaskRabbitMetadata; import com.todoroo.astrid.taskrabbit.TaskRabbitMetadata;
import com.todoroo.astrid.timers.TimerPlugin; import com.todoroo.astrid.timers.TimerPlugin;
import com.todoroo.astrid.ui.QuickAddBar; import com.todoroo.astrid.ui.QuickAddBar;
@ -872,7 +873,7 @@ public class TaskListFragment extends ListFragment implements OnScrollListener,
} }
protected TaskAdapter createTaskAdapter(TodorooCursor<Task> cursor) { protected TaskAdapter createTaskAdapter(TodorooCursor<Task> cursor) {
return new TaskAdapter(this, R.layout.task_adapter_row, return new TaskAdapter(this, R.layout.task_adapter_row_simple,
cursor, sqlQueryTemplate, false, cursor, sqlQueryTemplate, false,
new OnCompletedTaskListener() { new OnCompletedTaskListener() {
@Override @Override
@ -885,25 +886,49 @@ public class TaskListFragment extends ListFragment implements OnScrollListener,
public static final String TR_METADATA_JOIN = "for_taskrab"; //$NON-NLS-1$ public static final String TR_METADATA_JOIN = "for_taskrab"; //$NON-NLS-1$
public static final String TAGS_METADATA_JOIN = "for_tags"; //$NON-NLS-1$
/** /**
* Fill in the Task List with current items * Fill in the Task List with current items
* *
* @param withCustomId * @param withCustomId
* force task with given custom id to be part of list * force task with given custom id to be part of list
*/ */
@SuppressWarnings("nls")
protected void setUpTaskList() { protected void setUpTaskList() {
if (filter == null) if (filter == null)
return; return;
// TODO: For now, we'll modify the query to join and include the task rabbit data here. String tagName = null;
if (getActiveTagData() != null)
tagName = getActiveTagData().getValue(TagData.NAME);
Criterion tagsJoinCriterion = Criterion.and(Field.field(TAGS_METADATA_JOIN + "." + Metadata.KEY.name).eq(TagService.KEY), //$NON-NLS-1$
Task.ID.eq(Field.field(TAGS_METADATA_JOIN + "." + Metadata.TASK.name)));
if (tagName != null)
tagsJoinCriterion = Criterion.and(tagsJoinCriterion, Field.field(TAGS_METADATA_JOIN + "." + TagService.TAG.name).neq(tagName));
// TODO: For now, we'll modify the query to join and include the task rabbit and tag data here.
// Eventually, we might consider restructuring things so that this query is constructed elsewhere. // Eventually, we might consider restructuring things so that this query is constructed elsewhere.
String joinedTaskRabbitQuery = Join.left(Metadata.TABLE.as(TR_METADATA_JOIN), String joinedQuery =
Criterion.and(Field.field(TR_METADATA_JOIN + "." + Metadata.KEY.name).eq(TaskRabbitMetadata.METADATA_KEY), //$NON-NLS-1$ Join.left(Metadata.TABLE.as(TR_METADATA_JOIN),
Task.ID.eq(Field.field(TR_METADATA_JOIN) + "." + Metadata.TASK.name))) //$NON-NLS-1$ Criterion.and(Field.field(TR_METADATA_JOIN + "." + Metadata.KEY.name).eq(TaskRabbitMetadata.METADATA_KEY), //$NON-NLS-1$
Task.ID.eq(Field.field(TR_METADATA_JOIN + "." + Metadata.TASK.name)))).toString() //$NON-NLS-1$
+ Join.left(Metadata.TABLE.as(TAGS_METADATA_JOIN),
tagsJoinCriterion).toString() //$NON-NLS-1$
+ filter.getSqlQuery(); + filter.getSqlQuery();
sqlQueryTemplate.set(SortHelper.adjustQueryForFlagsAndSort( sqlQueryTemplate.set(SortHelper.adjustQueryForFlagsAndSort(
joinedTaskRabbitQuery, sortFlags, sortSort)); joinedQuery, sortFlags, sortSort));
String groupedQuery;
if (sqlQueryTemplate.get().contains("GROUP BY"))
groupedQuery = sqlQueryTemplate.get();
else if (sqlQueryTemplate.get().contains("ORDER BY")) //$NON-NLS-1$
groupedQuery = sqlQueryTemplate.get().replace("ORDER BY", "GROUP BY " + Task.ID + " ORDER BY"); //$NON-NLS-1$
else
groupedQuery = sqlQueryTemplate.get() + " GROUP BY " + Task.ID;
sqlQueryTemplate.set(groupedQuery);
// perform query // perform query
TodorooCursor<Task> currentCursor; TodorooCursor<Task> currentCursor;

@ -58,6 +58,7 @@ import android.widget.TextView;
import com.timsu.astrid.R; import com.timsu.astrid.R;
import com.todoroo.andlib.data.Property; import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.Property.LongProperty; import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.andlib.data.Property.StringProperty;
import com.todoroo.andlib.data.TodorooCursor; import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired; import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.ContextManager; import com.todoroo.andlib.service.ContextManager;
@ -90,6 +91,7 @@ import com.todoroo.astrid.service.StatisticsConstants;
import com.todoroo.astrid.service.StatisticsService; import com.todoroo.astrid.service.StatisticsService;
import com.todoroo.astrid.service.TaskService; import com.todoroo.astrid.service.TaskService;
import com.todoroo.astrid.service.ThemeService; import com.todoroo.astrid.service.ThemeService;
import com.todoroo.astrid.tags.TagService;
import com.todoroo.astrid.timers.TimerDecorationExposer; import com.todoroo.astrid.timers.TimerDecorationExposer;
import com.todoroo.astrid.ui.CheckableImageView; import com.todoroo.astrid.ui.CheckableImageView;
import com.todoroo.astrid.utility.Constants; import com.todoroo.astrid.utility.Constants;
@ -113,6 +115,9 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
private static final LongProperty TASK_RABBIT_ID = new LongProperty(Metadata.TABLE.as(TaskListFragment.TR_METADATA_JOIN), private static final LongProperty TASK_RABBIT_ID = new LongProperty(Metadata.TABLE.as(TaskListFragment.TR_METADATA_JOIN),
Metadata.ID.name).as("taskRabId"); //$NON-NLS-1$ Metadata.ID.name).as("taskRabId"); //$NON-NLS-1$
@SuppressWarnings("nls")
private static final StringProperty TAGS = new StringProperty(null, "group_concat(" + TaskListFragment.TAGS_METADATA_JOIN + "." + TagService.TAG.name + ", ' | ')").as("tags");
// --- other constants // --- other constants
/** Properties that need to be read from the action item */ /** Properties that need to be read from the action item */
@ -133,7 +138,8 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
Task.NOTES, Task.NOTES,
Task.USER_ID, Task.USER_ID,
Task.USER, Task.USER,
TASK_RABBIT_ID // Task rabbit metadata id (non-zero means it exists) TASK_RABBIT_ID, // Task rabbit metadata id (non-zero means it exists)
TAGS // Concatenated list of tags
}; };
public static int[] IMPORTANCE_RESOURCES = new int[] { public static int[] IMPORTANCE_RESOURCES = new int[] {
@ -189,12 +195,12 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
private final AtomicReference<String> query; private final AtomicReference<String> query;
protected final int minRowHeight;
// measure utilities // measure utilities
protected final Paint paint; protected final Paint paint;
protected final DisplayMetrics displayMetrics; protected final DisplayMetrics displayMetrics;
private final boolean simpleLayout;
// --- task detail and decoration soft caches // --- task detail and decoration soft caches
public final DecorationManager decorationManager; public final DecorationManager decorationManager;
@ -232,7 +238,7 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
displayMetrics = new DisplayMetrics(); displayMetrics = new DisplayMetrics();
fragment.getActivity().getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); fragment.getActivity().getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
this.minRowHeight = (int) (45 * displayMetrics.density); this.simpleLayout = (resource == R.layout.task_adapter_row_simple);
startDetailThread(); startDetailThread();
startTaskActionsThread(); startTaskActionsThread();
@ -246,7 +252,7 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
} }
private void startDetailThread() { private void startDetailThread() {
if (Preferences.getBoolean(R.string.p_showNotes, false)) { if (Preferences.getBoolean(R.string.p_showNotes, false) && !simpleLayout) {
detailLoader = new DetailLoaderThread(); detailLoader = new DetailLoaderThread();
detailLoader.start(); detailLoader.start();
} }
@ -296,6 +302,7 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
viewHolder.pictureBorder = (ImageView)view.findViewById(R.id.pictureBorder); viewHolder.pictureBorder = (ImageView)view.findViewById(R.id.pictureBorder);
viewHolder.completeBox = (CheckableImageView)view.findViewById(R.id.completeBox); viewHolder.completeBox = (CheckableImageView)view.findViewById(R.id.completeBox);
viewHolder.dueDate = (TextView)view.findViewById(R.id.dueDate); viewHolder.dueDate = (TextView)view.findViewById(R.id.dueDate);
viewHolder.tagsView = (TextView)view.findViewById(R.id.tagsDisplay);
viewHolder.details1 = (TextView)view.findViewById(R.id.details1); viewHolder.details1 = (TextView)view.findViewById(R.id.details1);
viewHolder.details2 = (TextView)view.findViewById(R.id.details2); viewHolder.details2 = (TextView)view.findViewById(R.id.details2);
viewHolder.taskRow = (LinearLayout)view.findViewById(R.id.task_row); viewHolder.taskRow = (LinearLayout)view.findViewById(R.id.task_row);
@ -333,6 +340,7 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
ViewHolder viewHolder = ((ViewHolder)view.getTag()); ViewHolder viewHolder = ((ViewHolder)view.getTag());
viewHolder.isTaskRabbit = (cursor.get(TASK_RABBIT_ID) > 0); viewHolder.isTaskRabbit = (cursor.get(TASK_RABBIT_ID) > 0);
viewHolder.tagsString = cursor.get(TAGS);
Task task = viewHolder.task; Task task = viewHolder.task;
task.clear(); task.clear();
@ -365,11 +373,13 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
public AsyncImageView picture; public AsyncImageView picture;
public ImageView pictureBorder; public ImageView pictureBorder;
public TextView dueDate; public TextView dueDate;
public TextView tagsView;
public TextView details1, details2; public TextView details1, details2;
public LinearLayout taskRow; public LinearLayout taskRow;
public View taskActionContainer; public View taskActionContainer;
public ImageView taskActionIcon; public ImageView taskActionIcon;
public boolean isTaskRabbit; public boolean isTaskRabbit; // From join query, not part of the task model
public String tagsString; // From join query, not part of the task model
public View[] decorations; public View[] decorations;
} }
@ -378,7 +388,6 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
public synchronized void setFieldContentsAndVisibility(View view) { public synchronized void setFieldContentsAndVisibility(View view) {
ViewHolder viewHolder = (ViewHolder)view.getTag(); ViewHolder viewHolder = (ViewHolder)view.getTag();
Task task = viewHolder.task; Task task = viewHolder.task;
viewHolder.rowBody.setMinimumHeight(minRowHeight);
// name // name
final TextView nameView = viewHolder.nameView; { final TextView nameView = viewHolder.nameView; {
@ -392,32 +401,7 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
nameView.setText(nameValue); nameView.setText(nameValue);
} }
// due date / completion date float dueDateTextWidth = setupDueDateAndTags(viewHolder, task);
float dueDateTextWidth = 0;
final TextView dueDateView = viewHolder.dueDate; {
Activity activity = fragment.getActivity();
if (activity != null) {
if(!task.isCompleted() && task.hasDueDate()) {
long dueDate = task.getValue(Task.DUE_DATE);
if(task.isOverdue())
dueDateView.setTextAppearance(fragment.getActivity(), R.style.TextAppearance_TAd_ItemDueDate_Overdue);
else
dueDateView.setTextAppearance(fragment.getActivity(), R.style.TextAppearance_TAd_ItemDueDate);
String dateValue = formatDate(dueDate);
dueDateView.setText(dateValue);
dueDateTextWidth = paint.measureText(dateValue);
setVisibility(dueDateView);
} else if(task.isCompleted()) {
String dateValue = formatDate(task.getValue(Task.COMPLETION_DATE));
dueDateView.setText(resources.getString(R.string.TAd_completed, dateValue));
dueDateView.setTextAppearance(fragment.getActivity(), R.style.TextAppearance_TAd_ItemDueDate_Completed);
dueDateTextWidth = paint.measureText(dateValue);
setVisibility(dueDateView);
} else {
dueDateView.setVisibility(View.GONE);
}
}
}
String details; String details;
if(viewHolder.details1 != null) { if(viewHolder.details1 != null) {
@ -616,13 +600,15 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
private final HashMap<Long, String> dateCache = new HashMap<Long, String>(8); private final HashMap<Long, String> dateCache = new HashMap<Long, String>(8);
@SuppressWarnings("nls")
private String formatDate(long date) { private String formatDate(long date) {
if(dateCache.containsKey(date)) if(dateCache.containsKey(date))
return dateCache.get(date); return dateCache.get(date);
String formatString = "%s" + (simpleLayout ? " " : "\n") + "%s";
String string = DateUtilities.getRelativeDay(fragment.getActivity(), date); String string = DateUtilities.getRelativeDay(fragment.getActivity(), date);
if(Task.hasDueTime(date)) if(Task.hasDueTime(date))
string = String.format("%s\n%s", string, //$NON-NLS-1$ string = String.format(formatString, string, //$NON-NLS-1$
DateUtilities.getTimeString(fragment.getActivity(), new Date(date))); DateUtilities.getTimeString(fragment.getActivity(), new Date(date)));
dateCache.put(date, string); dateCache.put(date, string);
@ -728,13 +714,7 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
@Override @Override
public void run() { public void run() {
AndroidUtilities.sleepDeep(500L); AndroidUtilities.sleepDeep(500L);
String groupedQuery; String groupedQuery = query.get();
if (query.get().contains("GROUP BY"))
groupedQuery = query.get();
else if (query.get().contains("ORDER BY")) //$NON-NLS-1$
groupedQuery = query.get().replace("ORDER BY", "GROUP BY " + Task.ID + " ORDER BY"); //$NON-NLS-1$
else
groupedQuery = query.get() + " GROUP BY " + Task.ID;
groupedQuery = PermaSql.replacePlaceholders(groupedQuery); groupedQuery = PermaSql.replacePlaceholders(groupedQuery);
@ -1055,6 +1035,8 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
return; return;
boolean state = task.isCompleted(); boolean state = task.isCompleted();
setupDueDateAndTags(viewHolder, task);
TextView name = viewHolder.nameView; TextView name = viewHolder.nameView;
if(state) { if(state) {
name.setPaintFlags(name.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); name.setPaintFlags(name.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
@ -1071,6 +1053,8 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
viewHolder.details2.setTextSize(detailTextSize); viewHolder.details2.setTextSize(detailTextSize);
if(viewHolder.dueDate != null) if(viewHolder.dueDate != null)
viewHolder.dueDate.setTextSize(detailTextSize); viewHolder.dueDate.setTextSize(detailTextSize);
if (viewHolder.tagsView != null)
viewHolder.tagsView.setTextSize(detailTextSize);
paint.setTextSize(detailTextSize); paint.setTextSize(detailTextSize);
// image view // image view
@ -1138,6 +1122,49 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
checkBoxView.setVisibility(View.VISIBLE); checkBoxView.setVisibility(View.VISIBLE);
} }
} }
}
// Returns due date text width
private float setupDueDateAndTags(ViewHolder viewHolder, Task task) {
// due date / completion date
float dueDateTextWidth = 0;
final TextView dueDateView = viewHolder.dueDate; {
Activity activity = fragment.getActivity();
if (activity != null) {
if(!task.isCompleted() && task.hasDueDate()) {
long dueDate = task.getValue(Task.DUE_DATE);
if(task.isOverdue())
dueDateView.setTextAppearance(activity, R.style.TextAppearance_TAd_ItemDueDate_Overdue);
else
dueDateView.setTextAppearance(activity, R.style.TextAppearance_TAd_ItemDueDate);
String dateValue = formatDate(dueDate);
dueDateView.setText(dateValue);
dueDateTextWidth = paint.measureText(dateValue);
setVisibility(dueDateView);
} else if(task.isCompleted()) {
String dateValue = formatDate(task.getValue(Task.COMPLETION_DATE));
dueDateView.setText(resources.getString(R.string.TAd_completed, dateValue));
dueDateView.setTextAppearance(activity, R.style.TextAppearance_TAd_ItemDueDate_Completed);
dueDateTextWidth = paint.measureText(dateValue);
setVisibility(dueDateView);
} else {
dueDateView.setVisibility(View.GONE);
}
if (viewHolder.tagsView != null) {
String tags = viewHolder.tagsString;
if (tags != null && task.hasDueDate())
tags = " | " + tags; //$NON-NLS-1$
if (!task.isCompleted())
viewHolder.tagsView.setText(tags);
else
viewHolder.tagsView.setText(null);
setVisibility(viewHolder.tagsView);
}
}
}
return dueDateTextWidth;
} }
/** /**

Loading…
Cancel
Save