Skip to content
Merged
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 .claude/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
settings.local.json
tmp/
worktrees/
scheduled_tasks.lock
127 changes: 56 additions & 71 deletions app/src/main/java/eu/darken/sdmse/main/ui/settings/SettingsFragment.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package eu.darken.sdmse.main.ui.settings

import android.os.Bundle
import android.os.Parcelable
import android.view.View
import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.viewModels
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
Expand All @@ -14,7 +15,6 @@ import eu.darken.sdmse.common.uix.Fragment2
import eu.darken.sdmse.common.uix.ToolbarHost
import eu.darken.sdmse.common.viewbinding.viewBinding
import eu.darken.sdmse.databinding.SettingsFragmentBinding
import kotlinx.parcelize.Parcelize

@AndroidEntryPoint
class SettingsFragment : Fragment2(R.layout.settings_fragment),
Expand All @@ -27,93 +27,67 @@ class SettingsFragment : Fragment2(R.layout.settings_fragment),
override val toolbar: Toolbar
get() = ui.toolbar

private val screens = ArrayList<Screen>()

@Parcelize
data class Screen(
val fragmentClass: String,
val screenTitle: String?,
val screenSubtitle: String? = null,
) : Parcelable
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Drive the toolbar from fragment lifecycle + fragment arguments.
// We can't trust childFragmentManager.backStackEntryCount inside the resumed
// callback: on Android 16's predictive-back path, the new fragment becomes
// RESUMED before the back stack has actually been decremented, so reading
// the count here would give a stale value.
childFragmentManager.registerFragmentLifecycleCallbacks(
object : FragmentManager.FragmentLifecycleCallbacks() {
override fun onFragmentResumed(fm: FragmentManager, f: Fragment) {
if (f.id == R.id.content_frame && view != null) {
syncToolbarForFragment(f)
}
}
},
false,
)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
EdgeToEdgeHelper(requireActivity()).apply {
insetsPadding(ui.root, left = true, right = true)
insetsPadding(ui.appbarlayout, top = true)
}

childFragmentManager.addOnBackStackChangedListener {
val backStackCnt = childFragmentManager.backStackEntryCount
val newScreenInfo = when {
backStackCnt < screens.size -> {
// We popped the backstack, restore the underlying screen infos
// If there are none left, we are at the index again
screens.removeLastOrNull()
screens.lastOrNull() ?: Screen(
fragmentClass = SettingsIndexFragment::class.qualifiedName!!,
screenTitle = getString(eu.darken.sdmse.common.R.string.general_settings_title)
)
}

else -> {
// We added the current fragment to the stack, the new fragment's infos were already set, do nothing.
null
}
}

newScreenInfo?.let { setCurrentScreenInfo(it) }
if (savedInstanceState == null
&& childFragmentManager.findFragmentById(R.id.content_frame) == null
) {
childFragmentManager
.beginTransaction()
.add(R.id.content_frame, SettingsIndexFragment())
.commit()
}

if (savedInstanceState == null) {
val currentFragment = childFragmentManager.findFragmentById(R.id.content_frame)
if (currentFragment == null) {
childFragmentManager
.beginTransaction()
.add(R.id.content_frame, SettingsIndexFragment())
.commit()
}
} else {
@Suppress("DEPRECATION")
savedInstanceState.getParcelableArrayList<Screen>(BKEY_SCREEN_INFOS)?.let {
screens.addAll(it)
}
}

// Always restore toolbar title from current screen state (handles NavComponent view recreation)
screens.lastOrNull()?.let { setCurrentScreenInfo(it) }
// Sync on first view creation AND on NavComponent view recreation.
childFragmentManager.findFragmentById(R.id.content_frame)?.let { syncToolbarForFragment(it) }

ui.toolbar.setNavigationOnClickListener { requireActivity().onBackPressedDispatcher.onBackPressed() }

super.onViewCreated(view, savedInstanceState)
}

override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putParcelableArrayList(BKEY_SCREEN_INFOS, screens)
}

override fun onPreferenceStartFragment(caller: PreferenceFragmentCompat, pref: Preference): Boolean {
val screenInfo = Screen(
fragmentClass = pref.fragment!!,
screenTitle = pref.title?.toString(),
screenSubtitle = ui.toolbar.title?.toString(),
)

val args = Bundle().apply {
putAll(pref.extras)
putString(BKEY_SCREEN_TITLE, screenInfo.screenTitle)
}
val title = pref.title?.toString().orEmpty()

@Suppress("DEPRECATION")
val fragment = childFragmentManager.fragmentFactory
.instantiate(this::class.java.classLoader!!, pref.fragment!!)
.apply {
arguments = args
arguments = Bundle().apply {
putAll(pref.extras)
putString(BKEY_SCREEN_TITLE, title)
}
setTargetFragment(caller, 0)
}

setCurrentScreenInfo(screenInfo)
screens.add(screenInfo)
// Update toolbar synchronously before commit so the title matches the transition.
setToolbar(
title = title,
subtitle = getString(eu.darken.sdmse.common.R.string.general_settings_title),
)

childFragmentManager.beginTransaction().apply {
replace(R.id.content_frame, fragment)
Expand All @@ -123,16 +97,27 @@ class SettingsFragment : Fragment2(R.layout.settings_fragment),
return true
}


private fun setCurrentScreenInfo(info: Screen) {
ui.toolbar.apply {
title = info.screenTitle
subtitle = info.screenSubtitle
private fun syncToolbarForFragment(f: Fragment) {
val screenTitle = f.arguments?.getString(BKEY_SCREEN_TITLE)
if (screenTitle != null) {
setToolbar(
title = screenTitle,
subtitle = getString(eu.darken.sdmse.common.R.string.general_settings_title),
)
} else {
setToolbar(
title = getString(eu.darken.sdmse.common.R.string.general_settings_title),
subtitle = null,
)
}
}

private fun setToolbar(title: String?, subtitle: String?) {
ui.toolbar.title = title
ui.toolbar.subtitle = subtitle
}

companion object {
private const val BKEY_SCREEN_TITLE = "preferenceScreenTitle"
private const val BKEY_SCREEN_INFOS = "preferenceScreenInfos"
}
}