Expand and collapse subtasks

gtask_related_email
Alex Baker 6 years ago
parent b5e29b0ad9
commit 2e2b0a5fba

@ -50,6 +50,7 @@ import com.todoroo.astrid.api.GtasksFilter;
import com.todoroo.astrid.api.SearchFilter;
import com.todoroo.astrid.api.TagFilter;
import com.todoroo.astrid.core.BuiltInFilterExposer;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.service.TaskCreator;
import com.todoroo.astrid.service.TaskDeleter;
@ -129,6 +130,7 @@ public final class TaskListFragment extends InjectingFragment
@Inject ActionModeProvider actionModeProvider;
@Inject Toaster toaster;
@Inject TaskAdapterProvider taskAdapterProvider;
@Inject TaskDao taskDao;
@BindView(R.id.swipe_layout)
SwipeRefreshLayout swipeRefreshLayout;
@ -259,7 +261,8 @@ public final class TaskListFragment extends InjectingFragment
viewHolderFactory,
this,
actionModeProvider,
taskListViewModel.getValue());
taskListViewModel.getValue(),
taskDao);
taskAdapter.setHelper(recyclerAdapter);
((DefaultItemAnimator) recyclerView.getItemAnimator()).setSupportsChangeAnimations(false);
recyclerView.setLayoutManager(new LinearLayoutManager(context));

@ -162,6 +162,9 @@ public abstract class TaskDao {
@Query("UPDATE tasks SET modified = datetime('now', 'localtime') WHERE _id in (:ids)")
public abstract void touch(List<Long> ids);
@Query("UPDATE tasks SET collapsed = :collapsed WHERE _id = :id")
public abstract void setCollapsed(long id, boolean collapsed);
/**
* Saves the given task to the database.getDatabase(). Task must already exist. Returns true on
* success.

@ -622,6 +622,10 @@ public class Task implements Parcelable {
return getId() == NO_ID;
}
public boolean isCollapsed() {
return collapsed;
}
/** {@inheritDoc} */
@Override
public int describeContents() {

@ -239,4 +239,8 @@ public class TaskContainer {
public Location getLocation() {
return location;
}
public boolean isCollapsed() {
return task.isCollapsed();
}
}

@ -21,6 +21,7 @@ import com.todoroo.astrid.adapter.TaskAdapter;
import com.todoroo.astrid.api.CaldavFilter;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.api.GtasksFilter;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.utility.Flags;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
@ -43,6 +44,7 @@ public class TaskListRecyclerAdapter extends RecyclerView.Adapter<ViewHolder>
private final ActionModeProvider actionModeProvider;
private final boolean isRemoteList;
private final ItemTouchHelperCallback itemTouchHelperCallback;
private final TaskDao taskDao;
private ActionMode mode = null;
private List<TaskContainer> list;
private PublishSubject<List<TaskContainer>> publishSubject = PublishSubject.create();
@ -55,7 +57,8 @@ public class TaskListRecyclerAdapter extends RecyclerView.Adapter<ViewHolder>
ViewHolderFactory viewHolderFactory,
TaskListFragment taskList,
ActionModeProvider actionModeProvider,
List<TaskContainer> list) {
List<TaskContainer> list,
TaskDao taskDao) {
this.adapter = adapter;
this.recyclerView = recyclerView;
this.viewHolderFactory = viewHolderFactory;
@ -66,6 +69,7 @@ public class TaskListRecyclerAdapter extends RecyclerView.Adapter<ViewHolder>
|| taskList.getFilter() instanceof CaldavFilter;
this.list = list;
itemTouchHelperCallback = new ItemTouchHelperCallback(adapter, this, this::drainQueue);
this.taskDao = taskDao;
new ItemTouchHelper(itemTouchHelperCallback).attachToRecyclerView(recyclerView);
Pair<List<TaskContainer>, DiffResult> initial = Pair.create(list, null);
disposables.add(
@ -132,6 +136,12 @@ public class TaskListRecyclerAdapter extends RecyclerView.Adapter<ViewHolder>
return true;
}
@Override
public void toggleSubtasks(TaskContainer task, boolean collapsed) {
taskDao.setCollapsed(task.getId(), collapsed);
taskList.loadTaskListContent();
}
public void startActionMode() {
if (mode == null) {
mode = actionModeProvider.startActionMode(adapter, taskList, this);

@ -81,6 +81,9 @@ public class ViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.hidden_status)
ImageView hidden;
@BindView(R.id.subtasks_chip)
Chip subtasksChip;
private int indent;
private boolean selected;
private boolean moving;
@ -209,6 +212,19 @@ public class ViewHolder extends RecyclerView.ViewHolder {
hidden.setVisibility(task.isHidden() ? View.VISIBLE : View.GONE);
setupTitleAndCheckbox();
setupDueDate();
if (task.hasChildren()) {
subtasksChip.setVisibility(View.VISIBLE);
subtasksChip.setText(
context
.getResources()
.getQuantityString(R.plurals.subtask_count, task.children, task.children));
subtasksChip.setChipIconResource(
task.isCollapsed()
? R.drawable.ic_keyboard_arrow_up_black_24dp
: R.drawable.ic_keyboard_arrow_down_black_24dp);
} else {
subtasksChip.setVisibility(View.GONE);
}
if (preferences.getBoolean(R.string.p_show_list_indicators, true)) {
setupLocation();
setupTags();
@ -294,6 +310,11 @@ public class ViewHolder extends RecyclerView.ViewHolder {
}
}
@OnClick(R.id.subtasks_chip)
void toggleSubtasks() {
callback.toggleSubtasks(task, !task.isCollapsed());
}
@OnClick(R.id.rowBody)
void onRowBodyClick() {
callback.onClick(this);
@ -355,6 +376,8 @@ public class ViewHolder extends RecyclerView.ViewHolder {
void onClick(Filter filter);
void toggleSubtasks(TaskContainer task, boolean collapsed);
boolean onLongPress(ViewHolder viewHolder);
}
}

@ -120,8 +120,10 @@ public class TaskListViewModel extends ViewModel {
+ Tag.TASK;
fields.add(field("(" + tagQuery + ")").as("tags"));
fields.add(INDENT);
fields.add(field("(SELECT count(*) FROM recursive_tasks WHERE parent = tasks._id GROUP BY parent)").as("children"));
String joinedQuery = Join.inner(RECURSIVE, Task.ID.eq(RECURSIVE_TASK)) + JOINS;
String joinedQuery = Join.inner(RECURSIVE, Task.ID.eq(RECURSIVE_TASK)) + JOINS +
" WHERE recursive_tasks.hidden = 0";
String parentQuery;
QueryTemplate subtaskQuery = new QueryTemplate();
if (filter instanceof CaldavFilter) {
@ -198,17 +200,17 @@ public class TaskListViewModel extends ViewModel {
GoogleTask.PARENT.eq(RECURSIVE_TASK),
CaldavTask.PARENT.eq(RECURSIVE_TASK))))
.where(TaskCriteria.activeAndVisible());
joinedQuery += " WHERE indent = (select max(indent) from recursive_tasks where tasks._id = recursive_tasks.task) ";
joinedQuery += " AND indent = (select max(indent) from recursive_tasks where tasks._id = recursive_tasks.task) ";
}
String sortSelect = SortHelper.orderSelectForSortTypeRecursive(preferences.getSortMode());
String withClause = "CREATE TEMPORARY TABLE `recursive_tasks` AS\n"
+ "WITH RECURSIVE recursive_tasks (task, indent, title, sortField) AS (\n"
+ " SELECT tasks._id, 0 AS sort_indent, UPPER(title) AS sort_title, "
+ "WITH RECURSIVE recursive_tasks (task, parent, collapsed, hidden, indent, title, sortField) AS (\n"
+ " SELECT tasks._id, 0 as parent, tasks.collapsed as collapsed, 0 as hidden, 0 AS sort_indent, UPPER(title) AS sort_title, "
+ sortSelect
+ " FROM tasks\n"
+ parentQuery
+ "\nUNION ALL SELECT tasks._id, recursive_tasks.indent+1 AS sort_indent, UPPER(tasks.title) AS sort_title, "
+ "\nUNION ALL SELECT tasks._id, recursive_tasks.task as parent, tasks.collapsed as collapsed, CASE WHEN recursive_tasks.collapsed > 0 OR recursive_tasks.hidden > 0 THEN 1 ELSE 0 END as hidden, recursive_tasks.indent+1 AS sort_indent, UPPER(tasks.title) AS sort_title, "
+ sortSelect
+ " FROM tasks\n"
+ subtaskQuery
@ -216,11 +218,11 @@ public class TaskListViewModel extends ViewModel {
+ SortHelper.orderForSortTypeRecursive(preferences)
+ ") SELECT * FROM recursive_tasks";
return newArrayList(
"DROP TABLE IF EXISTS `temp`.`recursive_tasks`",
SortHelper.adjustQueryForFlags(preferences, withClause),
"CREATE INDEX `rtasks` ON `recursive_tasks` (`task`)",
"CREATE INDEX `r_tasks` ON `recursive_tasks` (`task`)",
"CREATE INDEX `r_parents` ON `recursive_tasks` (`parent`)",
Query.select(fields.toArray(new Field[0]))
.withQueryTemplate(PermaSql.replacePlaceholdersForQuery(joinedQuery))
.from(Task.TABLE)

@ -0,0 +1,5 @@
<vector android:autoMirrored="true" android:height="24dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M7.41,7.84L12,12.42l4.59,-4.58L18,9.25l-6,6 -6,-6z"/>
</vector>

@ -0,0 +1,5 @@
<vector android:autoMirrored="true" android:height="24dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M7.41,15.41L12,10.83l4.59,4.58L18,14l-6,-6 -6,6z"/>
</vector>

@ -126,4 +126,23 @@
app:chipSpacingHorizontal="@dimen/chip_spacing"
app:chipSpacingVertical="@dimen/chip_spacing"/>
<com.google.android.material.chip.Chip
android:id="@+id/subtasks_chip"
style="@style/ChipStyle"
android:layout_marginTop="@dimen/task_list_item_spacing"
android:layout_marginStart="@dimen/keyline_content_inset"
android:layout_marginEnd="@dimen/keyline_first"
android:layout_marginLeft="@dimen/keyline_content_inset"
android:layout_marginRight="@dimen/keyline_first"
android:layout_gravity="center_vertical|end"
android:textColor="?attr/colorAccent"
android:visibility="gone"
app:chipBackgroundColor="?android:attr/colorBackground"
app:chipIcon="@drawable/ic_outline_place_24px"
app:chipIconTint="?attr/colorAccent"
app:chipStrokeColor="?attr/colorAccent"
app:chipStrokeWidth="@dimen/chip_stroke"
app:iconEndPadding="0dp"
app:iconStartPadding="@dimen/chip_text_padding"/>
</LinearLayout>

@ -213,6 +213,10 @@ File %1$s contained %2$s.\n\n
<item quantity="one">%d task</item>
<item quantity="other">%d tasks</item>
</plurals>
<plurals name="subtask_count">
<item quantity="one">%d subtask</item>
<item quantity="other">%d subtasks</item>
</plurals>
<plurals name="repeat_times">
<item quantity="one">time</item>
<item quantity="other">times</item>

Loading…
Cancel
Save