Skip to content

Commit

Permalink
Add collapsible checked section in checklists
Browse files Browse the repository at this point in the history
Enabled by default and unlike before, this works with custom sorting as well.

Related issue: #40
  • Loading branch information
naveensingh committed Jul 21, 2024
1 parent e71b8e6 commit e914935
Show file tree
Hide file tree
Showing 52 changed files with 320 additions and 200 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,10 @@ class NotesPagerAdapter(fm: FragmentManager, val notes: List<Note>, val activity
}

fun removeDoneCheckListItems(position: Int) {
(fragments[position] as? TasksFragment)?.removeDoneItems()
(fragments[position] as? TasksFragment)?.removeCheckedItems()
}

fun refreshChecklist(position: Int) {
(fragments[position] as? TasksFragment)?.refreshItems()
(fragments[position] as? TasksFragment)?.saveAndReload()
}
}
158 changes: 71 additions & 87 deletions app/src/main/kotlin/org/fossify/notes/adapters/TasksAdapter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,30 @@ import org.fossify.commons.adapters.MyRecyclerViewListAdapter
import org.fossify.commons.extensions.applyColorFilter
import org.fossify.commons.extensions.beVisibleIf
import org.fossify.commons.extensions.removeBit
import org.fossify.commons.helpers.SORT_BY_CUSTOM
import org.fossify.commons.interfaces.ItemMoveCallback
import org.fossify.commons.interfaces.ItemTouchHelperContract
import org.fossify.commons.interfaces.StartReorderDragListener
import org.fossify.commons.views.MyRecyclerView
import org.fossify.notes.R
import org.fossify.notes.databinding.ItemCheckedTasksBinding
import org.fossify.notes.databinding.ItemChecklistBinding
import org.fossify.notes.dialogs.EditTaskDialog
import org.fossify.notes.extensions.config
import org.fossify.notes.extensions.getPercentageFontSize
import org.fossify.notes.helpers.DONE_CHECKLIST_ITEM_ALPHA
import org.fossify.notes.interfaces.TasksActionListener
import org.fossify.notes.models.CompletedTasks
import org.fossify.notes.models.NoteItem
import org.fossify.notes.models.Task
import java.util.Collections

private const val TYPE_TASK = 0
private const val TYPE_COMPLETED_TASKS = 1

class TasksAdapter(
activity: BaseSimpleActivity,
val listener: TasksActionListener?,
recyclerView: MyRecyclerView,
itemClick: (Any) -> Unit = {},
) : MyRecyclerViewListAdapter<Task>(
) : MyRecyclerViewListAdapter<NoteItem>(
activity = activity, recyclerView = recyclerView, diffUtil = TaskDiffCallback(), itemClick = itemClick
), ItemTouchHelperContract {

Expand Down Expand Up @@ -67,20 +70,20 @@ class TasksAdapter(
when (id) {
R.id.cab_move_to_top -> moveSelectedItemsToTop()
R.id.cab_move_to_bottom -> moveSelectedItemsToBottom()
R.id.cab_rename -> renameChecklistItem()
R.id.cab_rename -> renameTask()
R.id.cab_delete -> deleteSelection()
}
}

override fun getItemId(position: Int) = currentList[position].id.toLong()
override fun getItemId(position: Int) = getItemKey(getItem(position)).toLong()

override fun getSelectableItemCount() = currentList.size
override fun getSelectableItemCount() = getSelectedItems().size

override fun getIsItemSelectable(position: Int) = true
override fun getIsItemSelectable(position: Int) = getItem(position) is Task

override fun getItemSelectionKey(position: Int) = currentList.getOrNull(position)?.id
override fun getItemSelectionKey(position: Int) = getItemKey(getItem(position))

override fun getItemKeyPosition(key: Int) = currentList.indexOfFirst { it.id == key }
override fun getItemKeyPosition(key: Int) = currentList.indexOfFirst { getItemKey(it) == key }

@SuppressLint("NotifyDataSetChanged")
override fun onActionModeCreated() = notifyDataSetChanged()
Expand All @@ -95,78 +98,61 @@ class TasksAdapter(
}

menu.findItem(R.id.cab_rename).isVisible = isOneItemSelected()
menu.findItem(R.id.cab_move_to_top).isVisible = selectedItems.none { it.isDone } || !activity.config.moveDoneChecklistItems
menu.findItem(R.id.cab_move_to_bottom).isVisible = selectedItems.none { it.isDone } || !activity.config.moveDoneChecklistItems
}

override fun getItemViewType(position: Int): Int {
return when (getItem(position)) {
is Task -> TYPE_TASK
is CompletedTasks -> TYPE_COMPLETED_TASKS
}
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return createViewHolder(ItemChecklistBinding.inflate(layoutInflater, parent, false).root)
return createViewHolder(
when (viewType) {
TYPE_TASK -> ItemChecklistBinding.inflate(layoutInflater, parent, false).root
TYPE_COMPLETED_TASKS -> ItemCheckedTasksBinding.inflate(layoutInflater, parent, false).root
else -> throw IllegalArgumentException("Unsupported view type: $viewType")
}
)
}

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = currentList[position]
val item = getItem(position)
holder.bindView(item, allowSingleClick = true, allowLongClick = true) { itemView, _ ->
setupView(itemView, item, holder)
when (item) {
is Task -> setupView(itemView, item, holder)
is CompletedTasks -> setupCompletedTasks(itemView, item)
}
}

bindViewHolder(holder)
}

private fun renameChecklistItem() {
val task = getSelectedItems().first()
EditTaskDialog(activity, task.title) { title ->
val tasks = currentList.toMutableList()
tasks[getSelectedItemPositions().first()] = task.copy(title = title)
saveTasks(tasks)
private fun renameTask() {
listener?.editTask(task = getSelectedItems().first()) {
finishActMode()
}
}

private fun deleteSelection() {
val tasks = currentList.toMutableList()
val tasksToRemove = ArrayList<Task>(selectedKeys.size)
selectedKeys.forEach { key ->
val position = tasks.indexOfFirst { it.id == key }
if (position != -1) {
val favorite = getItemWithKey(key)
if (favorite != null) {
tasksToRemove.add(favorite)
}
}
}

tasks.removeAll(tasksToRemove.toSet())
saveTasks(tasks)
listener?.deleteTasks(getSelectedItems())
finishActMode()
}

private fun moveSelectedItemsToTop() {
activity.config.sorting = SORT_BY_CUSTOM
val tasks = currentList.toMutableList()
selectedKeys.reversed().forEach { id ->
val position = tasks.indexOfFirst { it.id == id }
val tempItem = tasks[position]
tasks.removeAt(position)
tasks.add(0, tempItem)
}

saveTasks(tasks)
listener?.moveTasksToTop(taskIds = getSelectedItems().map { it.id })
}

private fun moveSelectedItemsToBottom() {
activity.config.sorting = SORT_BY_CUSTOM
val tasks = currentList.toMutableList()
selectedKeys.forEach { id ->
val position = tasks.indexOfFirst { it.id == id }
val tempItem = tasks[position]
tasks.removeAt(position)
tasks.add(tasks.size, tempItem)
}

saveTasks(tasks)
listener?.moveTasksToBottom(taskIds = getSelectedItems().map { it.id })
}

private fun getItemWithKey(key: Int): Task? = currentList.firstOrNull { it.id == key }

private fun getSelectedItems() = currentList.filter { selectedKeys.contains(it.id) }.toMutableList()
private fun getSelectedItems() = currentList.filterIsInstance<Task>().filter { selectedKeys.contains(it.id) }.toMutableList()

@SuppressLint("ClickableViewAccessibility")
private fun setupView(view: View, task: Task, holder: ViewHolder) {
val isSelected = selectedKeys.contains(task.id)
ItemChecklistBinding.bind(view).apply {
Expand All @@ -188,7 +174,8 @@ class TasksAdapter(
checklistCheckbox.isChecked = task.isDone
checklistHolder.isSelected = isSelected

checklistDragHandle.beVisibleIf(selectedKeys.isNotEmpty())
val canMoveTask = !task.isDone || !activity.config.moveDoneChecklistItems
checklistDragHandle.beVisibleIf(beVisible = canMoveTask && selectedKeys.isNotEmpty())
checklistDragHandle.applyColorFilter(textColor)
checklistDragHandle.setOnTouchListener { _, event ->
if (event.action == MotionEvent.ACTION_DOWN) {
Expand All @@ -199,46 +186,43 @@ class TasksAdapter(
}
}

override fun onRowMoved(fromPosition: Int, toPosition: Int) {
activity.config.sorting = SORT_BY_CUSTOM
val tasks = currentList.toMutableList()
if (fromPosition < toPosition) {
for (i in fromPosition until toPosition) {
Collections.swap(tasks, i, i + 1)
}
} else {
for (i in fromPosition downTo toPosition + 1) {
Collections.swap(tasks, i, i - 1)
}
private fun setupCompletedTasks(view: View, completedTasks: CompletedTasks) {
ItemCheckedTasksBinding.bind(view).apply {
numCheckedItems.text = activity.getString(R.string.num_checked_items, completedTasks.tasks.size)
expandCollapseIcon.setImageResource(
if (completedTasks.expanded) {
org.fossify.commons.R.drawable.ic_chevron_up_vector
} else {
org.fossify.commons.R.drawable.ic_chevron_down_vector
}
)
}
}

saveTasks(tasks)
override fun onRowMoved(fromPosition: Int, toPosition: Int) {
listener?.moveTask(fromPosition, toPosition)
}

override fun onRowSelected(myViewHolder: MyRecyclerViewAdapter.ViewHolder?) {}

override fun onRowClear(myViewHolder: MyRecyclerViewAdapter.ViewHolder?) {
saveTasks(currentList.toList())
listener?.saveAndReload()
}

private fun saveTasks(tasks: List<Task>) {
listener?.saveTasks(tasks) {
listener.refreshItems()
}
private fun getItemKey(item: NoteItem) = when (item) {
is Task -> item.id
is CompletedTasks -> item.id
}
}

private class TaskDiffCallback : DiffUtil.ItemCallback<Task>() {
override fun areItemsTheSame(
oldItem: Task,
newItem: Task
) = oldItem.id == newItem.id

override fun areContentsTheSame(
oldItem: Task,
newItem: Task
) = oldItem.id == newItem.id
&& oldItem.isDone == newItem.isDone
&& oldItem.title == newItem.title
&& oldItem.dateCreated == newItem.dateCreated
private class TaskDiffCallback : DiffUtil.ItemCallback<NoteItem>() {
override fun areItemsTheSame(oldItem: NoteItem, newItem: NoteItem): Boolean {
return if (oldItem is Task && newItem is Task) {
return oldItem.id == newItem.id
} else {
true
}
}

override fun areContentsTheSame(oldItem: NoteItem, newItem: NoteItem) = oldItem == newItem
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,10 @@ class SortChecklistDialog(private val activity: SimpleActivity, private val call

private fun setupSortRadio() {
val fieldRadio = binding.sortingDialogRadioSorting
fieldRadio.setOnCheckedChangeListener { group, checkedId ->
fieldRadio.setOnCheckedChangeListener { _, checkedId ->
val isCustomSorting = checkedId == binding.sortingDialogRadioCustom.id
binding.sortingDialogRadioOrder.beGoneIf(isCustomSorting)
binding.sortingDialogOrderDivider.beGoneIf(isCustomSorting)
binding.moveUndoneChecklistItemsDivider.beGoneIf(isCustomSorting)
binding.settingsMoveUndoneChecklistItemsHolder.beGoneIf(isCustomSorting)
}

var fieldBtn = binding.sortingDialogRadioTitle
Expand Down
Loading

0 comments on commit e914935

Please sign in to comment.