Skip to content

Conversation

@Vatsuki
Copy link

@Vatsuki Vatsuki commented Dec 27, 2025

Hello, I have fixed the issue Dialog view is destroyed (state not preserved) when rotating the screen. #744 for the Notes application. I hope my contribution is correct. Let me know if I have to change something ^^

Type of change(s)

  • Bug fix
  • Feature / enhancement
  • Infrastructure / tooling (CI, build, deps, tests)
  • Documentation

What changed and why

  • Replaced NewChecklistItemDialog and EditTaskDialog classes with ChecklistItemDialogFragment that inherits from DialogFragment.
  • Implemented onSaveInstanceState to manually save/restore the text of rows. This was necessary because the dynamic rows share the same XML View ID, which confused the automatic state restoration.
  • Replaced the previous lambda callbacks with setFragmentResult and setFragmentResultListener to safely pass data back to TasksFragment after the fragment recreation.

Tests performed

Tested on :

  • Android Studio emulated "Medium Phone",
  • Android 16,
  • API 36.1
  • White and Dark theme
  • Maximized font size, display size in accessibility settings

Before & after preview

Before :
Check the original issue #744

After :

output.mp4

Closes the following issue(s)

FossifyOrg/General-Discussion#744 for the Notes applications

Checklist

  • I read the contribution guidelines.
  • I manually tested my changes on device/emulator (if applicable).
  • I updated the "Unreleased" section in CHANGELOG.md (if applicable).
  • I have self-reviewed my pull request (no typos, formatting errors, etc.).
  • All checks are passing.

@Vatsuki Vatsuki requested a review from naveensingh as a code owner December 27, 2025 14:19
Copy link
Member

@naveensingh naveensingh left a comment

Choose a reason for hiding this comment

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

It'll be better to create a separate dialog for each concern. For example, EditTaskDialog becomes EditTaskDialogFragment:

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.*
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)
    }
}

Similarly, we refactor NewChecklistItemsDialog into NewChecklistItemsDialogFragment.

More issues:

  • Tapping the + button in ChecklistItemDialogFragment doesn't bring the new EditText into focus. It should.
  • While changing the soft input mode is technically correct, I think the current UI/UX works better without it.
  • The older NewChecklistItemsDialog, EditTaskDialog are now unused, but not deleted. This won't be an issue once they are refactored into separate fragments as proposed above.

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

Comment on lines +25 to +32
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"
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.

Comment on lines +298 to 306
private fun updateExistingTask(taskId: Int, newTitle: String) {
val taskIndex = tasks.indexOfFirst { it.id == taskId }
if (taskIndex != -1) {
val task = tasks[taskIndex]
val editedTask = task.copy(title = newTitle)
tasks[taskIndex] = editedTask
saveAndReload()
callback()
}
}
Copy link
Member

Choose a reason for hiding this comment

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

The callback() invocation was removed from here. It should be tracked and invoked when the result finally comes in.

CHANGELOG.md Outdated
## [Unreleased]

### Fixed
- Fixed checklist dialog disappearing on screen rotation ([#744])
Copy link
Member

Choose a reason for hiding this comment

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

Usually, this is correct, but we don't need to reference the general issue, as it gets erased by the release automation scripts (yet to be fixed) unless the reference points to this exact repository.

Suggested change
- Fixed checklist dialog disappearing on screen rotation ([#744])
- Fixed the checklist dialog disappearing on screen rotation

CHANGELOG.md Outdated
[#178]: https://github.com/FossifyOrg/Notes/issues/178
[#190]: https://github.com/FossifyOrg/Notes/issues/190
[#201]: https://github.com/FossifyOrg/Notes/issues/201
[#744]: https://github.com/FossifyOrg/General-Discussion/issues/744
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
[#744]: https://github.com/FossifyOrg/General-Discussion/issues/744

@naveensingh naveensingh added the waiting for author If the author does not respond, the issue will be closed. Otherwise, the label will be removed. label Dec 30, 2025
@naveensingh naveensingh changed the title Dialog view state preservation fix: dialog view state preservation Dec 30, 2025
@Vatsuki
Copy link
Author

Vatsuki commented Dec 31, 2025

Thanks for your feedback @naveensingh , and sorry if I do mistakes. I'm still a student learning CS, so thank you for your patience and guidance.
I tried to implement your suggestions and I noticed I also forgot some features of the original NewChecklistItemDialog. From the previous commit I added :

  • Add to the top checkbox visible only when custom sorting.
  • Creation of a new row below the current focused one when pressing the "Enter" key.
  • Added maybeRequestIncognito() support.
  • The view auto scrolling to the newly created row.
  • I also fixed the logic so the keyboard focus remains on the correct row after screen rotation.

I didn't notice any weird behavior when testing on emulator.

Note that I kept the unused class for now just in case. I can delete them and my comments if you say so.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

waiting for author If the author does not respond, the issue will be closed. Otherwise, the label will be removed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants