Simplify settings navigation and copy controls

This commit is contained in:
ajp_anton
2026-07-26 14:38:40 +00:00
parent 14a96046f7
commit c5f3dade9f
5 changed files with 105 additions and 49 deletions
@@ -104,9 +104,6 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
} }
override fun onNavigationItemSelected(item: MenuItem): Boolean { override fun onNavigationItemSelected(item: MenuItem): Boolean {
if (item.itemId == R.id.nav_settings_header) {
return false
}
navigateTo(item.itemId) navigateTo(item.itemId)
if (!permanentSidebar) { if (!permanentSidebar) {
binding.drawerLayout.closeDrawer(GravityCompat.START) binding.drawerLayout.closeDrawer(GravityCompat.START)
@@ -149,12 +146,8 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
val menu = navigationView.menu ?: return val menu = navigationView.menu ?: return
for (index in 0 until menu.size()) { for (index in 0 until menu.size()) {
val item = menu.getItem(index) val item = menu.getItem(index)
if (item.itemId == R.id.nav_settings_header) { item.isCheckable = true
item.isCheckable = false item.isChecked = item.itemId == itemId
} else {
item.isCheckable = true
item.isChecked = item.itemId == itemId
}
} }
navigationView.invalidate() navigationView.invalidate()
} }
@@ -5,12 +5,18 @@ import android.content.ClipData
import android.net.Uri import android.net.Uri
import android.content.ComponentName import android.content.ComponentName
import android.content.Intent import android.content.Intent
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.provider.Settings import android.provider.Settings
import android.view.View import android.view.View
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
import android.widget.CheckBox
import android.widget.LinearLayout
import android.widget.PopupWindow
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import com.google.android.material.switchmaterial.SwitchMaterial import com.google.android.material.switchmaterial.SwitchMaterial
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.color.MaterialColors
import se.ajpanton.notificationlog.databinding.FragmentSettingsBinding import se.ajpanton.notificationlog.databinding.FragmentSettingsBinding
import se.ajpanton.notificationlog.settings.LogField import se.ajpanton.notificationlog.settings.LogField
import se.ajpanton.notificationlog.settings.LogViewSettingsStore import se.ajpanton.notificationlog.settings.LogViewSettingsStore
@@ -122,48 +128,80 @@ class SettingsFragment : Fragment(R.layout.fragment_settings) {
} }
private fun setupCopyControls() { private fun setupCopyControls() {
val labels = listOf("From") + LoggingType.entries.map { it.label() } val labels = listOf("Source") + LoggingType.entries.map { it.label() }
binding!!.copyFrom.adapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_dropdown_item, labels) binding!!.copyFrom.adapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_dropdown_item, labels)
binding!!.copyFrom.onItemSelectedListener = object : android.widget.AdapterView.OnItemSelectedListener { binding!!.copyFrom.onItemSelectedListener = object : android.widget.AdapterView.OnItemSelectedListener {
override fun onNothingSelected(parent: android.widget.AdapterView<*>?) = Unit override fun onNothingSelected(parent: android.widget.AdapterView<*>?) = Unit
override fun onItemSelected(parent: android.widget.AdapterView<*>?, view: View?, position: Int, id: Long) { override fun onItemSelected(parent: android.widget.AdapterView<*>?, view: View?, position: Int, id: Long) {
selectedCopyTargets = emptySet() selectedCopyTargets = emptySet()
binding!!.copyTo.isEnabled = position > 0 binding!!.copyTo.isEnabled = position > 0
updateCopyButton() binding!!.copyTo.text = "Destination"
updateCopyControls()
} }
} }
binding!!.copyTo.setOnClickListener { chooseCopyTargets(LoggingType.entries[binding!!.copyFrom.selectedItemPosition - 1]) } binding!!.copyTo.setOnClickListener { showDestinationDropdown(LoggingType.entries[binding!!.copyFrom.selectedItemPosition - 1]) }
binding!!.copyEventSettings.setOnClickListener { binding!!.copyEventSettings.setOnClickListener {
LoggingRuleStore(requireContext()).copy(LoggingType.entries[binding!!.copyFrom.selectedItemPosition - 1], selectedCopyTargets) LoggingRuleStore(requireContext()).copy(LoggingType.entries[binding!!.copyFrom.selectedItemPosition - 1], selectedCopyTargets)
} }
} }
private fun chooseCopyTargets(source: LoggingType) { private fun showDestinationDropdown(source: LoggingType) {
val targets = LoggingType.entries.filterNot { it == source } val targets = LoggingType.entries.filterNot { it == source }
val checked = BooleanArray(targets.size) { targets[it] in selectedCopyTargets } val popupContent = LinearLayout(requireContext()).apply {
val dialog = MaterialAlertDialogBuilder(requireContext()) orientation = LinearLayout.VERTICAL
.setTitle("Copy ${source.label()} settings to") setPadding(dp(8), dp(8), dp(8), dp(8))
.setMultiChoiceItems(targets.map { it.label() }.toTypedArray(), checked) { _, index, selected -> checked[index] = selected } }
.setNegativeButton("Cancel", null) val popup = PopupWindow(popupContent, binding!!.copyTo.width.coerceAtLeast(dp(200)), LinearLayout.LayoutParams.WRAP_CONTENT, true).apply {
.setNeutralButton("All", null) isOutsideTouchable = true
.setPositiveButton("Done") { _, _ -> elevation = dp(8).toFloat()
selectedCopyTargets = targets.filterIndexed { index, _ -> checked[index] }.toSet() setBackgroundDrawable(ColorDrawable(MaterialColors.getColor(requireContext(), com.google.android.material.R.attr.colorSurface, Color.WHITE)))
updateCopyButton() }
val boxes = mutableListOf<CheckBox>()
popupContent.addView(android.widget.Button(requireContext()).apply {
text = "All"
setOnClickListener {
val selectAll = selectedCopyTargets.size != targets.size
selectedCopyTargets = if (selectAll) targets.toSet() else emptySet()
boxes.forEachIndexed { index, box -> box.isChecked = targets[index] in selectedCopyTargets }
updateCopyControls()
} }
.create() })
dialog.setOnShowListener { popupContent.addView(View(requireContext()).apply {
dialog.getButton(androidx.appcompat.app.AlertDialog.BUTTON_NEUTRAL).setOnClickListener { layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 1).apply {
val selectAll = checked.any { !it } setMargins(0, dp(4), 0, dp(4))
checked.indices.forEach { index -> }
checked[index] = selectAll setBackgroundColor(0x33000000)
dialog.listView.setItemChecked(index, selectAll) })
targets.forEach { target ->
val box = CheckBox(requireContext()).apply {
text = target.label()
isChecked = target in selectedCopyTargets
setOnCheckedChangeListener { _, checked ->
selectedCopyTargets = selectedCopyTargets.toMutableSet().apply {
if (checked) add(target) else remove(target)
}
updateCopyControls()
} }
} }
boxes += box
popupContent.addView(box)
} }
dialog.show() popup.showAsDropDown(binding!!.copyTo)
} }
private fun updateCopyButton() { binding?.copyEventSettings?.isEnabled = selectedCopyTargets.isNotEmpty() } private fun updateCopyControls() {
val currentBinding = binding ?: return
val sourceChosen = currentBinding.copyFrom.selectedItemPosition > 0
currentBinding.copyTo.isEnabled = sourceChosen
currentBinding.copyTo.text = destinationLabel()
currentBinding.copyEventSettings.isEnabled = sourceChosen && selectedCopyTargets.isNotEmpty()
}
private fun destinationLabel(): String = when (selectedCopyTargets.size) {
0 -> "Destination"
1 -> selectedCopyTargets.first().label()
else -> "${selectedCopyTargets.sortedBy { it.ordinal }.first().label()}..."
}
private fun isListenerEnabled(): Boolean = requireContext().getSystemService(android.app.NotificationManager::class.java) private fun isListenerEnabled(): Boolean = requireContext().getSystemService(android.app.NotificationManager::class.java)
.isNotificationListenerAccessGranted(ComponentName(requireContext(), NotificationCaptureService::class.java)) .isNotificationListenerAccessGranted(ComponentName(requireContext(), NotificationCaptureService::class.java))
@@ -229,5 +267,7 @@ class SettingsFragment : Fragment(R.layout.fragment_settings) {
private fun LoggingType.label() = name.lowercase().replace('_', ' ').replaceFirstChar(Char::uppercase) private fun LoggingType.label() = name.lowercase().replace('_', ' ').replaceFirstChar(Char::uppercase)
private fun dp(value: Int): Int = (value * resources.displayMetrics.density).toInt()
private enum class ExportFormat(val extension: String) { CSV("csv"), FORMATTED("txt"), HTML_ZIP("zip") } private enum class ExportFormat(val extension: String) { CSV("csv"), FORMATTED("txt"), HTML_ZIP("zip") }
} }
+39 -10
View File
@@ -54,19 +54,48 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="24dp" android:layout_marginTop="24dp"
android:text="Copy event settings" /> android:text="Copy settings between pages" />
<Spinner <LinearLayout
android:id="@+id/copy_from"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/copy_to"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:enabled="false" android:gravity="center_vertical"
android:text="Choose targets" /> android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Copy from" />
<Spinner
android:id="@+id/copy_from"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_weight="1" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="to" />
<Button
android:id="@+id/copy_to"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_weight="1"
android:enabled="false"
android:gravity="start|center_vertical"
android:text="Destination" />
</LinearLayout>
<Button <Button
android:id="@+id/copy_event_settings" android:id="@+id/copy_event_settings"
+1 -6
View File
@@ -5,15 +5,10 @@
android:id="@+id/nav_view_logs" android:id="@+id/nav_view_logs"
android:title="@string/page_view_logs" /> android:title="@string/page_view_logs" />
</group> </group>
<item
android:id="@+id/nav_settings_header"
android:checkable="false"
android:enabled="false"
android:title="@string/navigation_settings_header" />
<group android:checkableBehavior="single"> <group android:checkableBehavior="single">
<item <item
android:id="@+id/nav_settings" android:id="@+id/nav_settings"
android:title="@string/navigation_indented_settings" /> android:title="@string/navigation_settings_header" />
<item <item
android:id="@+id/nav_appearing" android:id="@+id/nav_appearing"
android:title="@string/navigation_indented_appearing" /> android:title="@string/navigation_indented_appearing" />
+1 -2
View File
@@ -5,7 +5,7 @@
<string name="notification_listener_label">Notification Log listener</string> <string name="notification_listener_label">Notification Log listener</string>
<string name="navigation_open">Open navigation</string> <string name="navigation_open">Open navigation</string>
<string name="navigation_close">Close navigation</string> <string name="navigation_close">Close navigation</string>
<string name="navigation_settings_header">Settings:</string> <string name="navigation_settings_header">Settings</string>
<string name="page_view_logs">View logs</string> <string name="page_view_logs">View logs</string>
<string name="page_settings">Settings</string> <string name="page_settings">Settings</string>
<string name="page_appearing">Appearing</string> <string name="page_appearing">Appearing</string>
@@ -13,7 +13,6 @@
<string name="page_text_content">Text content</string> <string name="page_text_content">Text content</string>
<string name="page_image_content">Image content</string> <string name="page_image_content">Image content</string>
<string name="page_edits">Edits</string> <string name="page_edits">Edits</string>
<string name="navigation_indented_settings">&#160;&#160;&#160;&#160;Settings</string>
<string name="navigation_indented_appearing">&#160;&#160;&#160;&#160;Appearing</string> <string name="navigation_indented_appearing">&#160;&#160;&#160;&#160;Appearing</string>
<string name="navigation_indented_disappearing">&#160;&#160;&#160;&#160;Disappearing</string> <string name="navigation_indented_disappearing">&#160;&#160;&#160;&#160;Disappearing</string>
<string name="navigation_indented_text_content">&#160;&#160;&#160;&#160;Text content</string> <string name="navigation_indented_text_content">&#160;&#160;&#160;&#160;Text content</string>