Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

### Fixed
- Fixed the checklist dialog disappearing on screen rotation
- Fixed inconsistent checklist sorting when the "Move checked items to the bottom" option is enabled ([#59])

## [1.6.0] - 2025-10-29
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package org.fossify.notes.dialogs

import android.app.Dialog
import android.os.Bundle
import android.view.View
import android.view.WindowManager
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.AppCompatEditText
import androidx.fragment.app.DialogFragment
import org.fossify.notes.R
import org.fossify.notes.databinding.DialogNewChecklistItemBinding
import org.fossify.notes.databinding.ItemAddChecklistBinding
import org.fossify.commons.extensions.getAlertDialogBuilder
import org.fossify.commons.extensions.getContrastColor
import org.fossify.commons.extensions.getProperPrimaryColor
import org.fossify.commons.extensions.showKeyboard
import org.fossify.commons.R as CommonsR
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can keep using the fully qualified R class name for this as that's the pattern everywhere in this project.

Suggested change
import org.fossify.commons.R as CommonsR


class ChecklistItemDialogFragment : DialogFragment() {

private val activeInputFields = mutableListOf<AppCompatEditText>()
private var binding: DialogNewChecklistItemBinding? = null

companion object {
const val DIALOG_TAG = "ChecklistItemDialogFragment"
const val REQUEST_KEY = "ChecklistItemRequest"
const val RESULT_TEXT_KEY = "ResultText"
const val RESULT_TASK_ID_KEY = "ResultTaskId"

private const val ARG_TEXT = "ArgText"
private const val ARG_TASK_ID = "ArgTaskId"
private const val SAVED_STATE_TEXTS = "SavedStateTexts"
Comment on lines +25 to +32
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IDs should use snake case for consistency.


fun newInstance(taskId: Int = -1, text: String = ""): ChecklistItemDialogFragment {
val fragment = ChecklistItemDialogFragment()
val args = Bundle()
args.putInt(ARG_TASK_ID, taskId)
args.putString(ARG_TEXT, text)
fragment.arguments = args
return fragment
}
}

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val activity = requireActivity()
val taskId = arguments?.getInt(ARG_TASK_ID) ?: -1

binding = DialogNewChecklistItemBinding.inflate(activity.layoutInflater)

activeInputFields.clear()

// Restore rows
if (savedInstanceState != null) {
val savedTexts = savedInstanceState.getStringArrayList(SAVED_STATE_TEXTS)
if (!savedTexts.isNullOrEmpty()) {
savedTexts.forEach { text -> addNewRow(text) }
} else {
addNewRow("")
}
} else {
val initialText = arguments?.getString(ARG_TEXT) ?: ""
addNewRow(initialText)
}

val isNewTaskMode = (taskId == -1)
if (isNewTaskMode) {
val contrastColor = activity.getProperPrimaryColor().getContrastColor()
binding!!.addItem.setColorFilter(contrastColor)

binding!!.addItem.setOnClickListener {
addNewRow("")
}
} else {
binding!!.addItem.visibility = View.GONE
binding!!.settingsAddChecklistTop.visibility = View.GONE
}

val titleRes = if (isNewTaskMode) R.string.add_new_checklist_items else R.string.rename_note

val builder = activity.getAlertDialogBuilder()
.setTitle(titleRes)
.setView(binding!!.root)
.setPositiveButton(CommonsR.string.ok, null)
.setNegativeButton(CommonsR.string.cancel, null)

val dialog = builder.create()
dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)

dialog.setOnShowListener {
if (activeInputFields.isNotEmpty()) {
dialog.showKeyboard(activeInputFields.last())
}

val positiveButton = (dialog as AlertDialog).getButton(AlertDialog.BUTTON_POSITIVE)
positiveButton.setOnClickListener {
val combinedText = activeInputFields
.map { it.text.toString().trim() }
.filter { it.isNotEmpty() }
.joinToString("\n")

if (combinedText.isNotEmpty()) {
val resultBundle = Bundle().apply {
putString(RESULT_TEXT_KEY, combinedText)
putInt(RESULT_TASK_ID_KEY, taskId)
}
parentFragmentManager.setFragmentResult(REQUEST_KEY, resultBundle)
dialog.dismiss()
} else {
dialog.dismiss()
}
}
}

return dialog
}

private fun addNewRow(text: String) {
val rowBinding = ItemAddChecklistBinding.inflate(layoutInflater)

// We disable automatic state saving for this view.
// This prevents Android from confusing the multiple EditTexts (which all share the same ID)
// and overwriting our manually restored text with the last view's text.
rowBinding.titleEditText.isSaveEnabled = false

rowBinding.titleEditText.setText(text)

if (text.isNotEmpty()) {
rowBinding.titleEditText.setSelection(text.length)
}

val inputField = rowBinding.titleEditText as AppCompatEditText
activeInputFields.add(inputField)

binding?.checklistHolder?.addView(rowBinding.root)
}

override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
val currentTexts = ArrayList(activeInputFields.map { it.text.toString() })
outState.putStringArrayList(SAVED_STATE_TEXTS, currentTexts)
}

override fun onDestroyView() {
super.onDestroyView()
binding = null
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package org.fossify.notes.dialogs

import android.content.DialogInterface
import android.os.Bundle
import androidx.appcompat.app.AlertDialog
import androidx.core.os.bundleOf
import androidx.fragment.app.DialogFragment
import org.fossify.commons.extensions.getAlertDialogBuilder
import org.fossify.commons.extensions.setupDialogStuff
import org.fossify.commons.extensions.showKeyboard
import org.fossify.commons.extensions.toast
import org.fossify.notes.databinding.DialogRenameChecklistItemBinding
import org.fossify.notes.extensions.maybeRequestIncognito
import org.fossify.notes.models.Task

class EditTaskDialogFragment : DialogFragment() {

companion object {
const val TAG = "EditTaskDialog"
const val ARG_TASK_ID = "arg_task_id"
const val ARG_OLD_TITLE = "arg_old_title"
const val REQUEST_KEY = "edit_task_request"
const val RESULT_TITLE = "result_title"
const val RESULT_TASK_ID = "result_task_id"
private const val STATE_TEXT = "state_text"

fun show(
host: androidx.fragment.app.FragmentManager,
task: Task
) = EditTaskDialogFragment().apply {
arguments = bundleOf(ARG_OLD_TITLE to task.title, ARG_TASK_ID to task.id)
}.show(host, TAG)
}

private lateinit var binding: DialogRenameChecklistItemBinding

override fun onCreateDialog(savedInstanceState: Bundle?): AlertDialog {
val activity = requireActivity()
binding = DialogRenameChecklistItemBinding.inflate(activity.layoutInflater).also {
val restored = savedInstanceState?.getString(STATE_TEXT)
it.checklistItemTitle.setText(
restored ?: requireArguments().getString(ARG_OLD_TITLE).orEmpty()
)
it.checklistItemTitle.maybeRequestIncognito()
}

val builder = activity.getAlertDialogBuilder()
.setPositiveButton(org.fossify.commons.R.string.ok, null)
.setNegativeButton(org.fossify.commons.R.string.cancel, null)

var dialog: AlertDialog? = null
activity.setupDialogStuff(binding.root, builder) { alert ->
alert.showKeyboard(binding.checklistItemTitle)
alert.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener {
val newTitle = binding.checklistItemTitle.text?.toString().orEmpty()
if (newTitle.isEmpty()) {
activity.toast(org.fossify.commons.R.string.empty_name)
} else {
val taskId = requireArguments().getInt(ARG_TASK_ID)
parentFragmentManager
.setFragmentResult(
REQUEST_KEY, bundleOf(RESULT_TASK_ID to taskId, RESULT_TITLE to newTitle)
)
alert.dismiss()
}
}
dialog = alert
}

return dialog!!
}

override fun onSaveInstanceState(outState: Bundle) {
val text = binding.checklistItemTitle.text?.toString().orEmpty()
outState.putString(STATE_TEXT, text)
super.onSaveInstanceState(outState)
}
}
Loading
Loading