Refine error dialog

This commit is contained in:
Philipp Heckel 2026-01-11 15:33:17 -05:00
parent feb2907cd4
commit 4359019dae
4 changed files with 109 additions and 129 deletions

View file

@ -5,15 +5,15 @@ import android.graphics.Color
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.AutoCompleteTextView
import android.widget.HorizontalScrollView
import android.widget.Spinner
import android.widget.TextView
import android.widget.Toast
import androidx.fragment.app.DialogFragment
import com.google.android.material.appbar.MaterialToolbar
import com.google.android.material.chip.Chip
import com.google.android.material.color.MaterialColors
import com.google.android.material.textfield.TextInputLayout
import io.heckel.ntfy.R
import io.heckel.ntfy.db.ConnectionError
import io.heckel.ntfy.db.Repository
@ -23,14 +23,13 @@ class ConnectionErrorFragment : DialogFragment() {
private lateinit var repository: Repository
private var connectionErrors: Map<String, ConnectionError> = emptyMap()
private var selectedBaseUrl: String? = null
private var detailsVisible = false
private var filterBaseUrl: String? = null
private lateinit var toolbar: MaterialToolbar
private lateinit var serverLabel: TextView
private lateinit var serverSpinner: Spinner
private lateinit var serverLayout: TextInputLayout
private lateinit var serverDropdown: AutoCompleteTextView
private lateinit var errorTextView: TextView
private lateinit var showDetailsTextView: TextView
private lateinit var detailsChip: Chip
private lateinit var detailsScrollView: HorizontalScrollView
private lateinit var stackTraceTextView: TextView
@ -75,41 +74,35 @@ class ConnectionErrorFragment : DialogFragment() {
copyMenuItem?.icon?.setTint(iconColor)
// Get view references
serverLabel = view.findViewById(R.id.connection_error_dialog_server_label)
serverSpinner = view.findViewById(R.id.connection_error_dialog_server_spinner)
serverLayout = view.findViewById(R.id.connection_error_dialog_server_layout)
serverDropdown = view.findViewById(R.id.connection_error_dialog_server_dropdown)
errorTextView = view.findViewById(R.id.connection_error_dialog_error_text)
showDetailsTextView = view.findViewById(R.id.connection_error_dialog_show_details)
detailsChip = view.findViewById(R.id.connection_error_dialog_details_chip)
detailsScrollView = view.findViewById(R.id.connection_error_dialog_details_scroll)
stackTraceTextView = view.findViewById(R.id.connection_error_dialog_stack_trace)
// Setup server spinner if multiple errors
// Setup server dropdown if multiple errors
val baseUrls = connectionErrors.keys.toList()
if (baseUrls.size > 1) {
serverLabel.visibility = View.VISIBLE
serverSpinner.visibility = View.VISIBLE
val adapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, baseUrls)
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
serverSpinner.adapter = adapter
serverSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
selectedBaseUrl = baseUrls[position]
updateErrorDisplay()
}
override fun onNothingSelected(parent: AdapterView<*>?) {}
serverLayout.visibility = View.VISIBLE
val adapter = ArrayAdapter(requireContext(), android.R.layout.simple_dropdown_item_1line, baseUrls)
serverDropdown.setAdapter(adapter)
serverDropdown.setText(baseUrls.first(), false)
serverDropdown.setOnItemClickListener { _, _, position, _ ->
selectedBaseUrl = baseUrls[position]
updateErrorDisplay()
}
} else {
serverLabel.visibility = View.GONE
serverSpinner.visibility = View.GONE
serverLayout.visibility = View.GONE
}
// Select first error by default
selectedBaseUrl = baseUrls.firstOrNull()
updateErrorDisplay()
// Toggle details visibility
showDetailsTextView.setOnClickListener {
detailsVisible = !detailsVisible
updateDetailsVisibility()
// Toggle details visibility using chip checked state
detailsChip.setOnCheckedChangeListener { _, isChecked ->
updateDetailsVisibility(isChecked)
}
// Build dialog
@ -140,17 +133,11 @@ class ConnectionErrorFragment : DialogFragment() {
errorTextView.text = getString(R.string.connection_error_dialog_no_error)
stackTraceTextView.text = ""
}
updateDetailsVisibility()
updateDetailsVisibility(detailsChip.isChecked)
}
private fun updateDetailsVisibility() {
if (detailsVisible) {
detailsScrollView.visibility = View.VISIBLE
showDetailsTextView.text = getString(R.string.connection_error_dialog_hide_details)
} else {
detailsScrollView.visibility = View.GONE
showDetailsTextView.text = getString(R.string.connection_error_dialog_show_details)
}
private fun updateDetailsVisibility(visible: Boolean) {
detailsScrollView.visibility = if (visible) View.VISIBLE else View.GONE
}
private fun copyErrorToClipboard() {
@ -163,7 +150,6 @@ class ConnectionErrorFragment : DialogFragment() {
append(error.getStackTraceString().ifEmpty { "No stack trace available" })
}
copyToClipboard(requireContext(), "connection error", text)
Toast.makeText(context, R.string.connection_error_dialog_copied, Toast.LENGTH_SHORT).show()
}
companion object {

View file

@ -36,89 +36,82 @@
android:paddingHorizontal="?dialogPreferredPadding"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="16dp">
<!-- Server selector (only visible when multiple servers have errors) -->
<!-- Description text (left aligned like UserFragment) -->
<TextView
android:id="@+id/connection_error_dialog_server_label"
android:id="@+id/connection_error_dialog_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/connection_error_dialog_server_label"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorSecondary"
android:paddingStart="4dp"
android:paddingEnd="4dp"
android:paddingTop="16dp"
android:paddingBottom="4dp"
android:visibility="gone" />
android:text="@string/connection_error_dialog_message"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Spinner
android:id="@+id/connection_error_dialog_server_spinner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone" />
<!-- Warning icon and message -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center_horizontal"
android:paddingTop="24dp"
android:paddingBottom="16dp">
<ImageView
android:id="@+id/connection_error_dialog_icon"
android:layout_width="64dp"
android:layout_height="64dp"
android:src="@drawable/ic_warning_amber_24dp"
android:contentDescription="@string/connection_error_dialog_title" />
<TextView
android:id="@+id/connection_error_dialog_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/connection_error_dialog_message"
android:textAlignment="center"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorSecondary" />
</LinearLayout>
<!-- Error message -->
<TextView
android:id="@+id/connection_error_dialog_error_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/connection_error_dialog_error_label"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorSecondary"
android:textStyle="bold"
android:paddingTop="8dp"
android:paddingBottom="4dp" />
<TextView
android:id="@+id/connection_error_dialog_error_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorPrimary"
android:padding="8dp" />
<!-- Show/Hide details toggle -->
<TextView
android:id="@+id/connection_error_dialog_show_details"
<!-- Server dropdown (Material 3 style, only visible when multiple servers have errors) -->
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/connection_error_dialog_server_layout"
style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/connection_error_dialog_show_details"
android:textColor="?android:attr/colorAccent"
android:textStyle="bold"
android:padding="8dp"
android:background="?android:attr/selectableItemBackground" />
android:hint="@string/connection_error_dialog_server_label"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/connection_error_dialog_description">
<AutoCompleteTextView
android:id="@+id/connection_error_dialog_server_dropdown"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="none"
android:editable="false" />
</com.google.android.material.textfield.TextInputLayout>
<!-- Error icon -->
<ImageView
android:id="@+id/connection_error_dialog_error_icon"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginTop="16dp"
app:srcCompat="@drawable/ic_error_red_24dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/connection_error_dialog_server_layout" />
<!-- Error message text (red, like AddFragment) -->
<TextView
android:id="@+id/connection_error_dialog_error_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:paddingStart="4dp"
android:paddingEnd="4dp"
android:textAppearance="@style/DangerText"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/connection_error_dialog_error_icon"
app:layout_constraintTop_toBottomOf="@id/connection_error_dialog_server_layout" />
<!-- Details chip (right aligned) -->
<com.google.android.material.chip.Chip
android:id="@+id/connection_error_dialog_details_chip"
style="@style/Widget.Material3.Chip.Filter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/connection_error_dialog_details"
app:chipBackgroundColor="@color/chip_background_state"
app:chipStrokeWidth="0dp"
app:checkedIconVisible="false"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/connection_error_dialog_error_text" />
<!-- Stack trace (scrollable horizontally, no word wrap) -->
<HorizontalScrollView
@ -126,8 +119,11 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:fillViewport="true"
android:visibility="gone"
android:fillViewport="true">
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/connection_error_dialog_details_chip">
<ScrollView
android:layout_width="wrap_content"
@ -138,20 +134,20 @@
android:id="@+id/connection_error_dialog_stack_trace"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="12dp"
android:background="?attr/colorSurfaceVariant"
android:fontFamily="monospace"
android:padding="12dp"
android:scrollHorizontally="true"
android:singleLine="false"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorPrimary"
android:fontFamily="monospace"
android:textSize="10sp"
android:scrollHorizontally="true"
android:singleLine="false" />
android:textSize="10sp" />
</ScrollView>
</HorizontalScrollView>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>

View file

@ -368,12 +368,12 @@
<string name="settings_general_dynamic_colors_summary_disabled">Verwendung der ntfy-Themenfarben</string>
<string name="publish_dialog_title">Veröffentlichen unter %1$s</string>
<string name="publish_dialog_title_hint">Titel</string>
<string name="publish_dialog_title_placeholder">z. B. Jemand steht vor der Tür</string>
<string name="publish_dialog_title_placeholder">z.B. Jemand steht vor der Tür</string>
<string name="settings_general_language_title">Sprache</string>
<string name="settings_general_language_summary_system">Systemstandard verwenden</string>
<string name="settings_general_language_system_default">Systemstandard</string>
<string name="publish_dialog_message_hint">Nachricht</string>
<string name="publish_dialog_tags_placeholder">z. B. Warnung, Totenkopf</string>
<string name="publish_dialog_tags_placeholder">z.B. Warnung, Totenkopf</string>
<string name="publish_dialog_priority_hint">Priorität</string>
<string name="publish_dialog_button_publish">Veröffentlichen</string>
<string name="publish_dialog_error_sending">Nachricht kann nicht veröffentlicht werden: %1$s</string>
@ -392,17 +392,17 @@
<string name="publish_dialog_chip_attach_file">Lokale Datei anhängen</string>
<string name="publish_dialog_chip_phone_call">Telefonanruf</string>
<string name="publish_dialog_click_url_hint">URL anklicken</string>
<string name="publish_dialog_click_url_placeholder">z. B. https://example.com/alerts/1234</string>
<string name="publish_dialog_click_url_placeholder">z.B. https://example.com/alerts/1234</string>
<string name="publish_dialog_email_hint">E-Mail</string>
<string name="publish_dialog_email_placeholder">z. B. phil@example.com</string>
<string name="publish_dialog_email_placeholder">z.B. phil@example.com</string>
<string name="publish_dialog_delay_hint">Übertragungsverzögerung</string>
<string name="publish_dialog_delay_placeholder">z. B. 30m, 1h, today 9pm (nur auf Englisch)</string>
<string name="publish_dialog_delay_placeholder">z.B. 30m, 1h, today 9pm (nur auf Englisch)</string>
<string name="publish_dialog_attach_url_hint">Anhang-URL</string>
<string name="publish_dialog_attach_url_placeholder">z. B. https://example.com/flowers.jpg</string>
<string name="publish_dialog_attach_url_placeholder">z.B. https://example.com/flowers.jpg</string>
<string name="publish_dialog_attach_filename_hint">Dateiname der Anlage</string>
<string name="publish_dialog_attach_filename_placeholder">z. B. Lilien.jpg</string>
<string name="publish_dialog_attach_filename_placeholder">z.B. Lilien.jpg</string>
<string name="publish_dialog_phone_call_hint">Telefonanruf</string>
<string name="publish_dialog_phone_call_placeholder">z. B. +1234567890</string>
<string name="publish_dialog_phone_call_placeholder">z.B. +1234567890</string>
<string name="publish_dialog_docs_text">Beispiele und eine detaillierte Beschreibung aller Veröffentlichungsfunktionen findest du in der <a href="https://docs.ntfy.sh/publish/">Dokumentation</a>.</string>
<string name="message_bar_hint">Nachricht hier eingeben</string>
<string name="message_bar_publish_button_description">Nachricht veröffentlichen</string>

View file

@ -290,16 +290,14 @@
<string name="notification_dialog_forever">Until resumed</string>
<!-- Connection error dialog -->
<string name="connection_error_dialog_title">Connection Error</string>
<string name="connection_error_dialog_message">There was a problem connecting to the server. The app will keep trying to reconnect.</string>
<string name="connection_error_dialog_title">Connection error</string>
<string name="connection_error_dialog_message">There was a problem connecting to the server. The app will keep trying to reconnect in the background.</string>
<string name="connection_error_dialog_server_label">Server</string>
<string name="connection_error_dialog_error_label">Error message</string>
<string name="connection_error_dialog_show_details">Show details</string>
<string name="connection_error_dialog_hide_details">Hide details</string>
<string name="connection_error_dialog_details">Details</string>
<string name="connection_error_dialog_no_stack_trace">No additional details available</string>
<string name="connection_error_dialog_no_error">No error</string>
<string name="connection_error_dialog_copy">Copy</string>
<string name="connection_error_dialog_copied">Error details copied to clipboard</string>
<!-- Notification popup -->
<string name="notification_popup_action_open">Open</string>