mirror of https://github.com/tasks/tasks
Subtasks in task edit screen
parent
594441830a
commit
d81901b889
@ -0,0 +1,19 @@
|
|||||||
|
package org.tasks.tasklist;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.DiffUtil;
|
||||||
|
import org.tasks.data.TaskContainer;
|
||||||
|
|
||||||
|
class SubtaskDiffCallback extends DiffUtil.ItemCallback<TaskContainer> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean areItemsTheSame(@NonNull TaskContainer oldItem, @NonNull TaskContainer newItem) {
|
||||||
|
return oldItem.getId() == newItem.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean areContentsTheSame(
|
||||||
|
@NonNull TaskContainer oldItem, @NonNull TaskContainer newItem) {
|
||||||
|
return oldItem.equals(newItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,157 @@
|
|||||||
|
package org.tasks.tasklist;
|
||||||
|
|
||||||
|
import static com.todoroo.andlib.utility.AndroidUtilities.atLeastLollipop;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.util.DisplayMetrics;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.view.ViewGroup.MarginLayoutParams;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import butterknife.BindView;
|
||||||
|
import butterknife.ButterKnife;
|
||||||
|
import butterknife.OnClick;
|
||||||
|
import com.google.android.material.chip.Chip;
|
||||||
|
import com.todoroo.astrid.data.Task;
|
||||||
|
import com.todoroo.astrid.ui.CheckableImageView;
|
||||||
|
import org.tasks.R;
|
||||||
|
import org.tasks.data.TaskContainer;
|
||||||
|
import org.tasks.locale.Locale;
|
||||||
|
import org.tasks.ui.CheckBoxes;
|
||||||
|
|
||||||
|
public class SubtaskViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
|
||||||
|
private final Activity context;
|
||||||
|
private final Locale locale;
|
||||||
|
private final Callbacks callbacks;
|
||||||
|
private final DisplayMetrics metrics;
|
||||||
|
|
||||||
|
public TaskContainer task;
|
||||||
|
|
||||||
|
@BindView(R.id.rowBody)
|
||||||
|
ViewGroup rowBody;
|
||||||
|
|
||||||
|
@BindView(R.id.title)
|
||||||
|
TextView nameView;
|
||||||
|
|
||||||
|
@BindView(R.id.completeBox)
|
||||||
|
CheckableImageView completeBox;
|
||||||
|
|
||||||
|
@BindView(R.id.chip_button)
|
||||||
|
Chip chip;
|
||||||
|
|
||||||
|
private int indent;
|
||||||
|
|
||||||
|
SubtaskViewHolder(
|
||||||
|
Activity context,
|
||||||
|
Locale locale,
|
||||||
|
ViewGroup view,
|
||||||
|
Callbacks callbacks,
|
||||||
|
DisplayMetrics metrics) {
|
||||||
|
super(view);
|
||||||
|
this.context = context;
|
||||||
|
this.locale = locale;
|
||||||
|
this.callbacks = callbacks;
|
||||||
|
this.metrics = metrics;
|
||||||
|
ButterKnife.bind(this, view);
|
||||||
|
|
||||||
|
view.setTag(this);
|
||||||
|
for (int i = 0; i < view.getChildCount(); i++) {
|
||||||
|
view.getChildAt(i).setTag(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private float getShiftSize() {
|
||||||
|
return 20 * metrics.density;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getIndentSize(int indent) {
|
||||||
|
return Math.round(indent * getShiftSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
void bindView(TaskContainer task, boolean multiLevelSubtasks) {
|
||||||
|
this.task = task;
|
||||||
|
setIndent(multiLevelSubtasks ? task.indent : 0);
|
||||||
|
if (task.hasChildren()) {
|
||||||
|
chip.setText(locale.formatNumber(task.children));
|
||||||
|
chip.setVisibility(View.VISIBLE);
|
||||||
|
chip.setChipIconResource(
|
||||||
|
task.isCollapsed()
|
||||||
|
? R.drawable.ic_keyboard_arrow_up_black_24dp
|
||||||
|
: R.drawable.ic_keyboard_arrow_down_black_24dp);
|
||||||
|
} else {
|
||||||
|
chip.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
nameView.setText(task.getTitle());
|
||||||
|
setupTitleAndCheckbox();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupTitleAndCheckbox() {
|
||||||
|
if (task.isCompleted()) {
|
||||||
|
nameView.setEnabled(false);
|
||||||
|
nameView.setPaintFlags(nameView.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
|
||||||
|
} else {
|
||||||
|
nameView.setEnabled(!task.isHidden());
|
||||||
|
nameView.setPaintFlags(nameView.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG);
|
||||||
|
}
|
||||||
|
|
||||||
|
completeBox.setChecked(task.isCompleted());
|
||||||
|
completeBox.setImageDrawable(CheckBoxes.getCheckBox(context, task.getTask()));
|
||||||
|
completeBox.invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnClick(R.id.title)
|
||||||
|
void openSubtask(View v) {
|
||||||
|
callbacks.openSubtask(task.getTask());
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnClick(R.id.chip_button)
|
||||||
|
void toggleSubtasks(View v) {
|
||||||
|
callbacks.toggleSubtask(task.getId(), !task.isCollapsed());
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnClick(R.id.completeBox)
|
||||||
|
void onCompleteBoxClick(View v) {
|
||||||
|
if (task == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean newState = completeBox.isChecked();
|
||||||
|
|
||||||
|
if (newState != task.isCompleted()) {
|
||||||
|
callbacks.complete(task.getTask(), newState);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set check box to actual action item state
|
||||||
|
setupTitleAndCheckbox();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getIndent() {
|
||||||
|
return indent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NewApi")
|
||||||
|
public void setIndent(int indent) {
|
||||||
|
this.indent = indent;
|
||||||
|
int indentSize = getIndentSize(indent);
|
||||||
|
if (atLeastLollipop()) {
|
||||||
|
MarginLayoutParams layoutParams = (MarginLayoutParams) rowBody.getLayoutParams();
|
||||||
|
layoutParams.setMarginStart(indentSize);
|
||||||
|
rowBody.setLayoutParams(layoutParams);
|
||||||
|
} else {
|
||||||
|
rowBody.setPadding(indentSize, rowBody.getPaddingTop(), 0, rowBody.getPaddingBottom());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Callbacks {
|
||||||
|
void openSubtask(Task task);
|
||||||
|
|
||||||
|
void toggleSubtask(long taskId, boolean collapsed);
|
||||||
|
|
||||||
|
void complete(Task task, boolean completed);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,93 @@
|
|||||||
|
package org.tasks.tasklist;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.util.DisplayMetrics;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.recyclerview.widget.AsyncDifferConfig;
|
||||||
|
import androidx.recyclerview.widget.AsyncListDiffer;
|
||||||
|
import androidx.recyclerview.widget.ListUpdateCallback;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import java.util.List;
|
||||||
|
import org.tasks.R;
|
||||||
|
import org.tasks.data.TaskContainer;
|
||||||
|
import org.tasks.locale.Locale;
|
||||||
|
import org.tasks.tasklist.SubtaskViewHolder.Callbacks;
|
||||||
|
|
||||||
|
public class SubtasksRecyclerAdapter extends RecyclerView.Adapter<SubtaskViewHolder>
|
||||||
|
implements ListUpdateCallback {
|
||||||
|
|
||||||
|
private final DisplayMetrics metrics;
|
||||||
|
private final Activity activity;
|
||||||
|
private final Locale locale;
|
||||||
|
private final Callbacks callbacks;
|
||||||
|
private final AsyncListDiffer<TaskContainer> differ;
|
||||||
|
private boolean multiLevelSubtasks;
|
||||||
|
|
||||||
|
public SubtasksRecyclerAdapter(
|
||||||
|
Activity activity, Locale locale, SubtaskViewHolder.Callbacks callbacks) {
|
||||||
|
this.activity = activity;
|
||||||
|
this.locale = locale;
|
||||||
|
this.callbacks = callbacks;
|
||||||
|
differ =
|
||||||
|
new AsyncListDiffer<>(
|
||||||
|
this, new AsyncDifferConfig.Builder<>(new SubtaskDiffCallback()).build());
|
||||||
|
metrics = activity.getResources().getDisplayMetrics();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public SubtaskViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
ViewGroup view =
|
||||||
|
(ViewGroup)
|
||||||
|
LayoutInflater.from(activity).inflate(R.layout.subtask_adapter_row_body, parent, false);
|
||||||
|
return new SubtaskViewHolder(activity, locale, view, callbacks, metrics);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull SubtaskViewHolder holder, int position) {
|
||||||
|
TaskContainer task = getItem(position);
|
||||||
|
if (task != null) {
|
||||||
|
holder.bindView(task, multiLevelSubtasks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TaskContainer getItem(int position) {
|
||||||
|
return differ.getCurrentList().get(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void submitList(List<TaskContainer> list) {
|
||||||
|
differ.submitList(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onInserted(int position, int count) {
|
||||||
|
notifyItemRangeInserted(position, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRemoved(int position, int count) {
|
||||||
|
notifyDataSetChanged(); // remove animation is janky
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMoved(int fromPosition, int toPosition) {
|
||||||
|
notifyItemMoved(fromPosition, toPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChanged(int position, int count, @Nullable Object payload) {
|
||||||
|
notifyItemRangeChanged(position, count, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return differ.getCurrentList().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMultiLevelSubtasksEnabled(boolean enabled) {
|
||||||
|
multiLevelSubtasks = enabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,336 @@
|
|||||||
|
package org.tasks.ui;
|
||||||
|
|
||||||
|
import static com.todoroo.andlib.utility.DateUtilities.now;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.view.inputmethod.EditorInfo;
|
||||||
|
import android.view.inputmethod.InputMethodManager;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.lifecycle.ViewModelProviders;
|
||||||
|
import androidx.recyclerview.widget.DefaultItemAnimator;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import butterknife.BindView;
|
||||||
|
import butterknife.OnClick;
|
||||||
|
import com.google.common.base.Strings;
|
||||||
|
import com.todoroo.andlib.sql.Criterion;
|
||||||
|
import com.todoroo.andlib.sql.Join;
|
||||||
|
import com.todoroo.andlib.sql.QueryTemplate;
|
||||||
|
import com.todoroo.astrid.activity.MainActivity;
|
||||||
|
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.dao.TaskDao.TaskCriteria;
|
||||||
|
import com.todoroo.astrid.data.Task;
|
||||||
|
import com.todoroo.astrid.service.TaskCompleter;
|
||||||
|
import com.todoroo.astrid.service.TaskCreator;
|
||||||
|
import com.todoroo.astrid.ui.CheckableImageView;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import org.tasks.LocalBroadcastManager;
|
||||||
|
import org.tasks.R;
|
||||||
|
import org.tasks.data.CaldavDao;
|
||||||
|
import org.tasks.data.CaldavTask;
|
||||||
|
import org.tasks.data.GoogleTask;
|
||||||
|
import org.tasks.data.GoogleTaskDao;
|
||||||
|
import org.tasks.injection.FragmentComponent;
|
||||||
|
import org.tasks.locale.Locale;
|
||||||
|
import org.tasks.preferences.Preferences;
|
||||||
|
import org.tasks.tasklist.SubtaskViewHolder.Callbacks;
|
||||||
|
import org.tasks.tasklist.SubtasksRecyclerAdapter;
|
||||||
|
|
||||||
|
public class SubtaskControlSet extends TaskEditControlFragment implements Callbacks {
|
||||||
|
|
||||||
|
public static final int TAG = R.string.TEA_ctrl_subtask_pref;
|
||||||
|
private static final String EXTRA_NEW_SUBTASKS = "extra_new_subtasks";
|
||||||
|
|
||||||
|
@BindView(R.id.recycler_view)
|
||||||
|
RecyclerView recyclerView;
|
||||||
|
|
||||||
|
@BindView(R.id.add_subtask)
|
||||||
|
TextView addSubtask;
|
||||||
|
|
||||||
|
@BindView(R.id.new_subtasks)
|
||||||
|
LinearLayout newSubtaskContainer;
|
||||||
|
|
||||||
|
@Inject Activity activity;
|
||||||
|
@Inject TaskCompleter taskCompleter;
|
||||||
|
@Inject LocalBroadcastManager localBroadcastManager;
|
||||||
|
@Inject GoogleTaskDao googleTaskDao;
|
||||||
|
@Inject Toaster toaster;
|
||||||
|
@Inject Preferences preferences;
|
||||||
|
@Inject TaskCreator taskCreator;
|
||||||
|
@Inject CaldavDao caldavDao;
|
||||||
|
@Inject TaskDao taskDao;
|
||||||
|
@Inject Locale locale;
|
||||||
|
|
||||||
|
private TaskListViewModel viewModel;
|
||||||
|
private final RefreshReceiver refreshReceiver = new RefreshReceiver();
|
||||||
|
private Filter remoteList;
|
||||||
|
private GoogleTask googleTask;
|
||||||
|
private SubtasksRecyclerAdapter recyclerAdapter;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void inject(FragmentComponent component) {
|
||||||
|
component.inject(this);
|
||||||
|
viewModel = ViewModelProviders.of(this).get(TaskListViewModel.class);
|
||||||
|
component.inject(viewModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
|
||||||
|
outState.putParcelableArrayList(EXTRA_NEW_SUBTASKS, getNewSubtasks());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public View onCreateView(
|
||||||
|
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
final View view = super.onCreateView(inflater, container, savedInstanceState);
|
||||||
|
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
for (Task task : savedInstanceState.<Task>getParcelableArrayList(EXTRA_NEW_SUBTASKS)) {
|
||||||
|
addSubtask(task);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
recyclerAdapter = new SubtasksRecyclerAdapter(activity, locale, this);
|
||||||
|
if (task.getId() > 0) {
|
||||||
|
recyclerAdapter.submitList(viewModel.getValue());
|
||||||
|
viewModel.setFilter(new Filter("subtasks", getQueryTemplate(task)), true);
|
||||||
|
((DefaultItemAnimator) recyclerView.getItemAnimator()).setSupportsChangeAnimations(false);
|
||||||
|
recyclerView.setLayoutManager(new LinearLayoutManager(activity));
|
||||||
|
recyclerView.setNestedScrollingEnabled(false);
|
||||||
|
viewModel.observe(this, recyclerAdapter::submitList);
|
||||||
|
recyclerView.setAdapter(recyclerAdapter);
|
||||||
|
}
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static QueryTemplate getQueryTemplate(Task task) {
|
||||||
|
return new QueryTemplate()
|
||||||
|
.join(
|
||||||
|
Join.left(
|
||||||
|
GoogleTask.TABLE,
|
||||||
|
Criterion.and(
|
||||||
|
GoogleTask.PARENT.eq(task.getId()),
|
||||||
|
GoogleTask.TASK.eq(Task.ID),
|
||||||
|
GoogleTask.DELETED.eq(0))))
|
||||||
|
.join(
|
||||||
|
Join.left(
|
||||||
|
CaldavTask.TABLE,
|
||||||
|
Criterion.and(
|
||||||
|
CaldavTask.PARENT.eq(task.getId()),
|
||||||
|
CaldavTask.TASK.eq(Task.ID),
|
||||||
|
CaldavTask.DELETED.eq(0))))
|
||||||
|
.where(Criterion.and(TaskCriteria.activeAndVisible(),
|
||||||
|
Criterion.or(CaldavTask.TASK.gt(0), GoogleTask.TASK.gt(0))));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getLayout() {
|
||||||
|
return R.layout.control_set_subtasks;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getIcon() {
|
||||||
|
return R.drawable.ic_subdirectory_arrow_right_black_24dp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int controlId() {
|
||||||
|
return TAG;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(Task task) {
|
||||||
|
for (Task subtask: getNewSubtasks()) {
|
||||||
|
if (Strings.isNullOrEmpty(subtask.getTitle())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
taskDao.createNew(subtask);
|
||||||
|
if (remoteList instanceof GtasksFilter) {
|
||||||
|
GoogleTask googleTask =
|
||||||
|
new GoogleTask(subtask.getId(), ((GtasksFilter) remoteList).getRemoteId());
|
||||||
|
googleTask.setParent(task.getId());
|
||||||
|
googleTask.setMoved(true);
|
||||||
|
googleTaskDao.insertAndShift(googleTask, preferences.addGoogleTasksToTop());
|
||||||
|
} else if (remoteList instanceof CaldavFilter) {
|
||||||
|
CaldavTask caldavTask =
|
||||||
|
new CaldavTask(subtask.getId(), ((CaldavFilter) remoteList).getUuid());
|
||||||
|
caldavTask.setParent(task.getId());
|
||||||
|
caldavTask.setRemoteParent(caldavDao.getRemoteIdForTask(task.getId()));
|
||||||
|
caldavDao.insert(caldavTask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasChanges(Task original) {
|
||||||
|
return remoteList != null && !getNewSubtasks().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ArrayList<Task> getNewSubtasks() {
|
||||||
|
ArrayList<Task> subtasks = new ArrayList<>();
|
||||||
|
int children = newSubtaskContainer.getChildCount();
|
||||||
|
for (int i = 0 ; i < children ; i++) {
|
||||||
|
View view = newSubtaskContainer.getChildAt(i);
|
||||||
|
EditText title = view.findViewById(R.id.title);
|
||||||
|
CheckableImageView completed = view.findViewById(R.id.completeBox);
|
||||||
|
Task subtask = taskCreator.createWithValues(title.getText().toString());
|
||||||
|
if (completed.isChecked()) {
|
||||||
|
subtask.setCompletionDate(now());
|
||||||
|
}
|
||||||
|
subtasks.add(subtask);
|
||||||
|
}
|
||||||
|
return subtasks;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
|
||||||
|
localBroadcastManager.registerRefreshReceiver(refreshReceiver);
|
||||||
|
|
||||||
|
googleTask = googleTaskDao.getByTaskId(task.getId());
|
||||||
|
|
||||||
|
updateUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
|
||||||
|
localBroadcastManager.unregisterReceiver(refreshReceiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnClick(R.id.add_subtask)
|
||||||
|
void addSubtask() {
|
||||||
|
if (remoteList == null) {
|
||||||
|
toaster.longToast(R.string.subtasks_enable_synchronization);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isGoogleTaskChild()) {
|
||||||
|
toaster.longToast(R.string.subtasks_multilevel_google_task);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
EditText editText = addSubtask(taskCreator.createWithValues(""));
|
||||||
|
editText.requestFocus();
|
||||||
|
InputMethodManager imm =
|
||||||
|
(InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||||
|
imm.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private EditText addSubtask(Task task) {
|
||||||
|
ViewGroup view =
|
||||||
|
(ViewGroup)
|
||||||
|
LayoutInflater.from(activity)
|
||||||
|
.inflate(R.layout.editable_subtask_adapter_row_body, newSubtaskContainer, false);
|
||||||
|
view.findViewById(R.id.clear).setOnClickListener(v -> newSubtaskContainer.removeView(view));
|
||||||
|
EditText editText = view.findViewById(R.id.title);
|
||||||
|
editText.setTextKeepState(task.getTitle());
|
||||||
|
editText.setHorizontallyScrolling(false);
|
||||||
|
editText.setLines(1);
|
||||||
|
editText.setMaxLines(Integer.MAX_VALUE);
|
||||||
|
editText.setFocusable(true);
|
||||||
|
editText.setEnabled(true);
|
||||||
|
editText.setOnEditorActionListener(
|
||||||
|
(arg0, actionId, arg2) -> {
|
||||||
|
if (actionId == EditorInfo.IME_ACTION_NEXT) {
|
||||||
|
if (editText.getText().length() != 0) {
|
||||||
|
addSubtask();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
CheckableImageView completeBox = view.findViewById(R.id.completeBox);
|
||||||
|
completeBox.setChecked(task.isCompleted());
|
||||||
|
updateCompleteBox(task, completeBox, editText);
|
||||||
|
completeBox.setOnClickListener(v -> updateCompleteBox(task, completeBox, editText));
|
||||||
|
newSubtaskContainer.addView(view);
|
||||||
|
return editText;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateCompleteBox(Task task, CheckableImageView completeBox, EditText editText) {
|
||||||
|
boolean isComplete = completeBox.isChecked();
|
||||||
|
completeBox.setImageDrawable(
|
||||||
|
CheckBoxes.getCheckBox(activity, isComplete, false, task.getPriority()));
|
||||||
|
if (isComplete) {
|
||||||
|
editText.setPaintFlags(editText.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
|
||||||
|
} else {
|
||||||
|
editText.setPaintFlags(editText.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isGoogleTaskChild() {
|
||||||
|
return remoteList instanceof GtasksFilter
|
||||||
|
&& googleTask != null
|
||||||
|
&& googleTask.getParent() > 0
|
||||||
|
&& googleTask.getListId().equals(((GtasksFilter) remoteList).getRemoteId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateUI() {
|
||||||
|
if (remoteList == null || isGoogleTaskChild()) {
|
||||||
|
recyclerView.setVisibility(View.GONE);
|
||||||
|
newSubtaskContainer.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
recyclerView.setVisibility(View.VISIBLE);
|
||||||
|
newSubtaskContainer.setVisibility(View.VISIBLE);
|
||||||
|
recyclerAdapter.setMultiLevelSubtasksEnabled(!(remoteList instanceof GtasksFilter));
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onRemoteListChanged(@Nullable Filter filter) {
|
||||||
|
this.remoteList = filter;
|
||||||
|
|
||||||
|
if (recyclerView != null) {
|
||||||
|
updateUI();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refresh() {
|
||||||
|
viewModel.invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void openSubtask(Task task) {
|
||||||
|
((MainActivity) getActivity()).getTaskListFragment().onTaskListItemClicked(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void toggleSubtask(long taskId, boolean collapsed) {
|
||||||
|
taskDao.setCollapsed(taskId, collapsed);
|
||||||
|
localBroadcastManager.broadcastRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void complete(Task task, boolean completed) {
|
||||||
|
taskCompleter.setComplete(task, completed);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected class RefreshReceiver extends BroadcastReceiver {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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="M19,15l-6,6 -1.42,-1.42L15.17,16H4V4h2v10h9.17l-3.59,-3.58L13,9l6,6z"/>
|
||||||
|
</vector>
|
||||||
@ -0,0 +1,43 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/recycler_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentTop="true"
|
||||||
|
android:overScrollMode="never"
|
||||||
|
android:scrollbars="none"
|
||||||
|
app:elevation="@dimen/elevation_task_list" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/new_subtasks"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@id/recycler_view"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/add_subtask"
|
||||||
|
style="@style/TaskEditTextPrimary"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@id/new_subtasks"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:gravity="start"
|
||||||
|
android:hint="@string/TEA_add_subtask"
|
||||||
|
android:paddingStart="0dp"
|
||||||
|
android:paddingLeft="0dp"
|
||||||
|
android:paddingEnd="@dimen/keyline_second"
|
||||||
|
android:paddingRight="@dimen/keyline_second"
|
||||||
|
android:textAlignment="viewStart" />
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
@ -0,0 +1,51 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/rowBody"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="@dimen/keyline_first">
|
||||||
|
|
||||||
|
<com.todoroo.astrid.ui.CheckableImageView
|
||||||
|
android:id="@+id/completeBox"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
|
android:layout_alignParentLeft="true"
|
||||||
|
android:paddingStart="0dp"
|
||||||
|
android:paddingLeft="0dp"
|
||||||
|
android:paddingEnd="@dimen/keyline_first"
|
||||||
|
android:paddingRight="@dimen/keyline_first" />
|
||||||
|
|
||||||
|
<include
|
||||||
|
layout="@layout/control_set_clear_button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_alignParentRight="true" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/title"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_toRightOf="@id/completeBox"
|
||||||
|
android:layout_toEndOf="@id/completeBox"
|
||||||
|
android:layout_toLeftOf="@id/clear"
|
||||||
|
android:layout_toStartOf="@id/clear"
|
||||||
|
android:background="@null"
|
||||||
|
android:focusable="true"
|
||||||
|
android:gravity="start|top"
|
||||||
|
android:hint="@string/enter_title_hint"
|
||||||
|
android:imeOptions="flagNoExtractUi"
|
||||||
|
android:inputType="textCapSentences"
|
||||||
|
|
||||||
|
android:paddingStart="0dp"
|
||||||
|
android:paddingLeft="0dp"
|
||||||
|
android:paddingEnd="@dimen/keyline_first"
|
||||||
|
android:paddingRight="@dimen/keyline_first"
|
||||||
|
android:singleLine="false"
|
||||||
|
android:textAlignment="viewStart"
|
||||||
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
|
android:textSize="@dimen/task_edit_text_size" />
|
||||||
|
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:id="@+id/rowBody"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingBottom="@dimen/keyline_first">
|
||||||
|
|
||||||
|
<com.todoroo.astrid.ui.CheckableImageView
|
||||||
|
android:id="@+id/completeBox"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
|
android:layout_alignParentLeft="true"
|
||||||
|
android:paddingStart="0dp"
|
||||||
|
android:paddingLeft="0dp"
|
||||||
|
android:paddingEnd="@dimen/keyline_first"
|
||||||
|
android:paddingRight="@dimen/keyline_first" />
|
||||||
|
|
||||||
|
<com.google.android.material.chip.Chip
|
||||||
|
android:id="@+id/chip_button"
|
||||||
|
style="@style/ChipStyle"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
android:textColor="?android:textColorSecondary"
|
||||||
|
app:chipBackgroundColor="@color/window_background"
|
||||||
|
app:chipIconTint="@color/icon_tint"
|
||||||
|
app:chipStrokeColor="?android:textColorSecondary"
|
||||||
|
app:chipStrokeWidth="@dimen/chip_stroke"
|
||||||
|
app:iconEndPadding="0dp"
|
||||||
|
app:iconStartPadding="@dimen/chip_text_padding"
|
||||||
|
app:textEndPadding="@dimen/chip_text_padding"
|
||||||
|
app:textStartPadding="@dimen/chip_text_padding" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/title"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_toStartOf="@id/chip_button"
|
||||||
|
android:layout_toLeftOf="@id/chip_button"
|
||||||
|
android:layout_toEndOf="@id/completeBox"
|
||||||
|
android:layout_toRightOf="@id/completeBox"
|
||||||
|
android:background="?attr/selectableItemBackground"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:gravity="start|top"
|
||||||
|
android:paddingStart="0dp"
|
||||||
|
android:paddingLeft="0dp"
|
||||||
|
android:paddingEnd="@dimen/keyline_first"
|
||||||
|
android:paddingRight="@dimen/keyline_first"
|
||||||
|
android:textAlignment="viewStart"
|
||||||
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
|
android:textSize="@dimen/task_edit_text_size" />
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
Loading…
Reference in New Issue