Skip to content

Commit e914935

Browse files
committed
Add collapsible checked section in checklists
Enabled by default and unlike before, this works with custom sorting as well. Related issue: #40
1 parent e71b8e6 commit e914935

File tree

52 files changed

+320
-200
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+320
-200
lines changed

app/src/main/kotlin/org/fossify/notes/adapters/NotesPagerAdapter.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,10 @@ class NotesPagerAdapter(fm: FragmentManager, val notes: List<Note>, val activity
9393
}
9494

9595
fun removeDoneCheckListItems(position: Int) {
96-
(fragments[position] as? TasksFragment)?.removeDoneItems()
96+
(fragments[position] as? TasksFragment)?.removeCheckedItems()
9797
}
9898

9999
fun refreshChecklist(position: Int) {
100-
(fragments[position] as? TasksFragment)?.refreshItems()
100+
(fragments[position] as? TasksFragment)?.saveAndReload()
101101
}
102102
}

app/src/main/kotlin/org/fossify/notes/adapters/TasksAdapter.kt

Lines changed: 71 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -16,27 +16,30 @@ import org.fossify.commons.adapters.MyRecyclerViewListAdapter
1616
import org.fossify.commons.extensions.applyColorFilter
1717
import org.fossify.commons.extensions.beVisibleIf
1818
import org.fossify.commons.extensions.removeBit
19-
import org.fossify.commons.helpers.SORT_BY_CUSTOM
2019
import org.fossify.commons.interfaces.ItemMoveCallback
2120
import org.fossify.commons.interfaces.ItemTouchHelperContract
2221
import org.fossify.commons.interfaces.StartReorderDragListener
2322
import org.fossify.commons.views.MyRecyclerView
2423
import org.fossify.notes.R
24+
import org.fossify.notes.databinding.ItemCheckedTasksBinding
2525
import org.fossify.notes.databinding.ItemChecklistBinding
26-
import org.fossify.notes.dialogs.EditTaskDialog
2726
import org.fossify.notes.extensions.config
2827
import org.fossify.notes.extensions.getPercentageFontSize
2928
import org.fossify.notes.helpers.DONE_CHECKLIST_ITEM_ALPHA
3029
import org.fossify.notes.interfaces.TasksActionListener
30+
import org.fossify.notes.models.CompletedTasks
31+
import org.fossify.notes.models.NoteItem
3132
import org.fossify.notes.models.Task
32-
import java.util.Collections
33+
34+
private const val TYPE_TASK = 0
35+
private const val TYPE_COMPLETED_TASKS = 1
3336

3437
class TasksAdapter(
3538
activity: BaseSimpleActivity,
3639
val listener: TasksActionListener?,
3740
recyclerView: MyRecyclerView,
3841
itemClick: (Any) -> Unit = {},
39-
) : MyRecyclerViewListAdapter<Task>(
42+
) : MyRecyclerViewListAdapter<NoteItem>(
4043
activity = activity, recyclerView = recyclerView, diffUtil = TaskDiffCallback(), itemClick = itemClick
4144
), ItemTouchHelperContract {
4245

@@ -67,20 +70,20 @@ class TasksAdapter(
6770
when (id) {
6871
R.id.cab_move_to_top -> moveSelectedItemsToTop()
6972
R.id.cab_move_to_bottom -> moveSelectedItemsToBottom()
70-
R.id.cab_rename -> renameChecklistItem()
73+
R.id.cab_rename -> renameTask()
7174
R.id.cab_delete -> deleteSelection()
7275
}
7376
}
7477

75-
override fun getItemId(position: Int) = currentList[position].id.toLong()
78+
override fun getItemId(position: Int) = getItemKey(getItem(position)).toLong()
7679

77-
override fun getSelectableItemCount() = currentList.size
80+
override fun getSelectableItemCount() = getSelectedItems().size
7881

79-
override fun getIsItemSelectable(position: Int) = true
82+
override fun getIsItemSelectable(position: Int) = getItem(position) is Task
8083

81-
override fun getItemSelectionKey(position: Int) = currentList.getOrNull(position)?.id
84+
override fun getItemSelectionKey(position: Int) = getItemKey(getItem(position))
8285

83-
override fun getItemKeyPosition(key: Int) = currentList.indexOfFirst { it.id == key }
86+
override fun getItemKeyPosition(key: Int) = currentList.indexOfFirst { getItemKey(it) == key }
8487

8588
@SuppressLint("NotifyDataSetChanged")
8689
override fun onActionModeCreated() = notifyDataSetChanged()
@@ -95,78 +98,61 @@ class TasksAdapter(
9598
}
9699

97100
menu.findItem(R.id.cab_rename).isVisible = isOneItemSelected()
101+
menu.findItem(R.id.cab_move_to_top).isVisible = selectedItems.none { it.isDone } || !activity.config.moveDoneChecklistItems
102+
menu.findItem(R.id.cab_move_to_bottom).isVisible = selectedItems.none { it.isDone } || !activity.config.moveDoneChecklistItems
103+
}
104+
105+
override fun getItemViewType(position: Int): Int {
106+
return when (getItem(position)) {
107+
is Task -> TYPE_TASK
108+
is CompletedTasks -> TYPE_COMPLETED_TASKS
109+
}
98110
}
99111

100112
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
101-
return createViewHolder(ItemChecklistBinding.inflate(layoutInflater, parent, false).root)
113+
return createViewHolder(
114+
when (viewType) {
115+
TYPE_TASK -> ItemChecklistBinding.inflate(layoutInflater, parent, false).root
116+
TYPE_COMPLETED_TASKS -> ItemCheckedTasksBinding.inflate(layoutInflater, parent, false).root
117+
else -> throw IllegalArgumentException("Unsupported view type: $viewType")
118+
}
119+
)
102120
}
103121

104122
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
105-
val item = currentList[position]
123+
val item = getItem(position)
106124
holder.bindView(item, allowSingleClick = true, allowLongClick = true) { itemView, _ ->
107-
setupView(itemView, item, holder)
125+
when (item) {
126+
is Task -> setupView(itemView, item, holder)
127+
is CompletedTasks -> setupCompletedTasks(itemView, item)
128+
}
108129
}
109130

110131
bindViewHolder(holder)
111132
}
112133

113-
private fun renameChecklistItem() {
114-
val task = getSelectedItems().first()
115-
EditTaskDialog(activity, task.title) { title ->
116-
val tasks = currentList.toMutableList()
117-
tasks[getSelectedItemPositions().first()] = task.copy(title = title)
118-
saveTasks(tasks)
134+
private fun renameTask() {
135+
listener?.editTask(task = getSelectedItems().first()) {
119136
finishActMode()
120137
}
121138
}
122139

123140
private fun deleteSelection() {
124-
val tasks = currentList.toMutableList()
125-
val tasksToRemove = ArrayList<Task>(selectedKeys.size)
126-
selectedKeys.forEach { key ->
127-
val position = tasks.indexOfFirst { it.id == key }
128-
if (position != -1) {
129-
val favorite = getItemWithKey(key)
130-
if (favorite != null) {
131-
tasksToRemove.add(favorite)
132-
}
133-
}
134-
}
135-
136-
tasks.removeAll(tasksToRemove.toSet())
137-
saveTasks(tasks)
141+
listener?.deleteTasks(getSelectedItems())
142+
finishActMode()
138143
}
139144

140145
private fun moveSelectedItemsToTop() {
141-
activity.config.sorting = SORT_BY_CUSTOM
142-
val tasks = currentList.toMutableList()
143-
selectedKeys.reversed().forEach { id ->
144-
val position = tasks.indexOfFirst { it.id == id }
145-
val tempItem = tasks[position]
146-
tasks.removeAt(position)
147-
tasks.add(0, tempItem)
148-
}
149-
150-
saveTasks(tasks)
146+
listener?.moveTasksToTop(taskIds = getSelectedItems().map { it.id })
151147
}
152148

153149
private fun moveSelectedItemsToBottom() {
154-
activity.config.sorting = SORT_BY_CUSTOM
155-
val tasks = currentList.toMutableList()
156-
selectedKeys.forEach { id ->
157-
val position = tasks.indexOfFirst { it.id == id }
158-
val tempItem = tasks[position]
159-
tasks.removeAt(position)
160-
tasks.add(tasks.size, tempItem)
161-
}
162-
163-
saveTasks(tasks)
150+
listener?.moveTasksToBottom(taskIds = getSelectedItems().map { it.id })
164151
}
165152

166-
private fun getItemWithKey(key: Int): Task? = currentList.firstOrNull { it.id == key }
167-
168-
private fun getSelectedItems() = currentList.filter { selectedKeys.contains(it.id) }.toMutableList()
153+
private fun getSelectedItems() = currentList.filterIsInstance<Task>().filter { selectedKeys.contains(it.id) }.toMutableList()
169154

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

191-
checklistDragHandle.beVisibleIf(selectedKeys.isNotEmpty())
177+
val canMoveTask = !task.isDone || !activity.config.moveDoneChecklistItems
178+
checklistDragHandle.beVisibleIf(beVisible = canMoveTask && selectedKeys.isNotEmpty())
192179
checklistDragHandle.applyColorFilter(textColor)
193180
checklistDragHandle.setOnTouchListener { _, event ->
194181
if (event.action == MotionEvent.ACTION_DOWN) {
@@ -199,46 +186,43 @@ class TasksAdapter(
199186
}
200187
}
201188

202-
override fun onRowMoved(fromPosition: Int, toPosition: Int) {
203-
activity.config.sorting = SORT_BY_CUSTOM
204-
val tasks = currentList.toMutableList()
205-
if (fromPosition < toPosition) {
206-
for (i in fromPosition until toPosition) {
207-
Collections.swap(tasks, i, i + 1)
208-
}
209-
} else {
210-
for (i in fromPosition downTo toPosition + 1) {
211-
Collections.swap(tasks, i, i - 1)
212-
}
189+
private fun setupCompletedTasks(view: View, completedTasks: CompletedTasks) {
190+
ItemCheckedTasksBinding.bind(view).apply {
191+
numCheckedItems.text = activity.getString(R.string.num_checked_items, completedTasks.tasks.size)
192+
expandCollapseIcon.setImageResource(
193+
if (completedTasks.expanded) {
194+
org.fossify.commons.R.drawable.ic_chevron_up_vector
195+
} else {
196+
org.fossify.commons.R.drawable.ic_chevron_down_vector
197+
}
198+
)
213199
}
200+
}
214201

215-
saveTasks(tasks)
202+
override fun onRowMoved(fromPosition: Int, toPosition: Int) {
203+
listener?.moveTask(fromPosition, toPosition)
216204
}
217205

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

220208
override fun onRowClear(myViewHolder: MyRecyclerViewAdapter.ViewHolder?) {
221-
saveTasks(currentList.toList())
209+
listener?.saveAndReload()
222210
}
223211

224-
private fun saveTasks(tasks: List<Task>) {
225-
listener?.saveTasks(tasks) {
226-
listener.refreshItems()
227-
}
212+
private fun getItemKey(item: NoteItem) = when (item) {
213+
is Task -> item.id
214+
is CompletedTasks -> item.id
228215
}
229216
}
230217

231-
private class TaskDiffCallback : DiffUtil.ItemCallback<Task>() {
232-
override fun areItemsTheSame(
233-
oldItem: Task,
234-
newItem: Task
235-
) = oldItem.id == newItem.id
236-
237-
override fun areContentsTheSame(
238-
oldItem: Task,
239-
newItem: Task
240-
) = oldItem.id == newItem.id
241-
&& oldItem.isDone == newItem.isDone
242-
&& oldItem.title == newItem.title
243-
&& oldItem.dateCreated == newItem.dateCreated
218+
private class TaskDiffCallback : DiffUtil.ItemCallback<NoteItem>() {
219+
override fun areItemsTheSame(oldItem: NoteItem, newItem: NoteItem): Boolean {
220+
return if (oldItem is Task && newItem is Task) {
221+
return oldItem.id == newItem.id
222+
} else {
223+
true
224+
}
225+
}
226+
227+
override fun areContentsTheSame(oldItem: NoteItem, newItem: NoteItem) = oldItem == newItem
244228
}

app/src/main/kotlin/org/fossify/notes/dialogs/SortChecklistDialog.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,10 @@ class SortChecklistDialog(private val activity: SimpleActivity, private val call
3333

3434
private fun setupSortRadio() {
3535
val fieldRadio = binding.sortingDialogRadioSorting
36-
fieldRadio.setOnCheckedChangeListener { group, checkedId ->
36+
fieldRadio.setOnCheckedChangeListener { _, checkedId ->
3737
val isCustomSorting = checkedId == binding.sortingDialogRadioCustom.id
3838
binding.sortingDialogRadioOrder.beGoneIf(isCustomSorting)
3939
binding.sortingDialogOrderDivider.beGoneIf(isCustomSorting)
40-
binding.moveUndoneChecklistItemsDivider.beGoneIf(isCustomSorting)
41-
binding.settingsMoveUndoneChecklistItemsHolder.beGoneIf(isCustomSorting)
4240
}
4341

4442
var fieldBtn = binding.sortingDialogRadioTitle

0 commit comments

Comments
 (0)