Full size dialog

This commit is contained in:
Philipp Heckel 2025-12-02 21:58:38 -05:00
parent 5a1b63d2d1
commit ab44372e1a
7 changed files with 435 additions and 390 deletions

View file

@ -1,17 +1,18 @@
package io.heckel.ntfy.ui
import android.app.Activity
import android.app.AlertDialog
import android.app.Dialog
import android.content.Context
import android.os.Bundle
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.view.inputmethod.InputMethodManager
import android.widget.*
import androidx.fragment.app.DialogFragment
import androidx.lifecycle.lifecycleScope
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.appbar.MaterialToolbar
import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout
import io.heckel.ntfy.BuildConfig
@ -31,10 +32,10 @@ class AddFragment : DialogFragment() {
private lateinit var appBaseUrl: String
private var defaultBaseUrl: String? = null
private lateinit var toolbar: MaterialToolbar
private lateinit var actionMenuItem: MenuItem
private lateinit var subscribeView: View
private lateinit var loginView: View
private lateinit var positiveButton: Button
private lateinit var negativeButton: Button
// Subscribe page
private lateinit var subscribeTopicText: TextInputEditText
@ -79,6 +80,21 @@ class AddFragment : DialogFragment() {
// Build root view
val view = requireActivity().layoutInflater.inflate(R.layout.fragment_add_dialog, null)
// Setup toolbar
toolbar = view.findViewById(R.id.add_dialog_toolbar)
toolbar.setNavigationOnClickListener {
dismiss()
}
toolbar.setOnMenuItemClickListener { menuItem ->
if (menuItem.itemId == R.id.add_dialog_action_button) {
onActionButtonClick()
true
} else {
false
}
}
actionMenuItem = toolbar.menu.findItem(R.id.add_dialog_action_button)
// Main "pages"
subscribeView = view.findViewById(R.id.add_dialog_subscribe_view)
subscribeView.visibility = View.VISIBLE
@ -137,6 +153,19 @@ class AddFragment : DialogFragment() {
}
}
// Subscribe view validation
val subscribeTextWatcher = AfterChangedTextWatcher {
validateInputSubscribeView()
}
subscribeTopicText.addTextChangedListener(subscribeTextWatcher)
subscribeBaseUrlText.addTextChangedListener(subscribeTextWatcher)
subscribeInstantDeliveryCheckbox.setOnCheckedChangeListener { _, _ ->
validateInputSubscribeView()
}
subscribeUseAnotherServerCheckbox.setOnCheckedChangeListener { _, _ ->
validateInputSubscribeView()
}
// Username/password validation on type
val loginTextWatcher = AfterChangedTextWatcher {
validateInputLoginView()
@ -145,57 +174,36 @@ class AddFragment : DialogFragment() {
loginPasswordText.addTextChangedListener(loginTextWatcher)
// Build dialog
// Build fullscreen dialog
val dialog = Dialog(requireContext(), android.R.style.Theme_Material_Light_NoActionBar_Fullscreen)
val dialog = Dialog(requireContext(), R.style.Theme_App_FullScreenDialog)
dialog.setContentView(view)
dialog.setCanceledOnTouchOutside(false)
// Force fullscreen size
dialog.window?.setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT )
// Add your buttons manually since we no longer use a builder
positiveButton = view.findViewById<Button?>(R.id.add_dialog_positive_button)
?: Button(requireContext()).also {
// If you don't have dedicated buttons in layout, create them dynamically
}
negativeButton = view.findViewById<Button?>(R.id.add_dialog_negative_button)
?: Button(requireContext())
// Show keyboard when the dialog is shown (see https://stackoverflow.com/a/19573049/1440785)
dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)
// Add logic to disable "Subscribe" button on invalid input
dialog.setOnShowListener {
positiveButton.isEnabled = false
positiveButton.setOnClickListener {
positiveButtonClick()
}
negativeButton.setOnClickListener {
negativeButtonClick()
}
val subscribeTextWatcher = AfterChangedTextWatcher {
validateInputSubscribeView()
}
subscribeTopicText.addTextChangedListener(subscribeTextWatcher)
subscribeBaseUrlText.addTextChangedListener(subscribeTextWatcher)
subscribeInstantDeliveryCheckbox.setOnCheckedChangeListener { _, _ ->
validateInputSubscribeView()
}
subscribeUseAnotherServerCheckbox.setOnCheckedChangeListener { _, _ ->
validateInputSubscribeView()
}
validateInputSubscribeView()
// Focus topic text (keyboard is shown too, see above)
subscribeTopicText.requestFocus()
}
// Initial validation
validateInputSubscribeView()
return dialog
}
private fun positiveButtonClick() {
override fun onStart() {
super.onStart()
dialog?.window?.apply {
setLayout(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
}
}
override fun onResume() {
super.onResume()
// Show keyboard after the dialog is fully visible
subscribeTopicText.postDelayed({
subscribeTopicText.requestFocus()
val imm = requireContext().getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
imm?.showSoftInput(subscribeTopicText, InputMethodManager.SHOW_FORCED)
}, 200)
}
private fun onActionButtonClick() {
val topic = subscribeTopicText.text.toString()
val baseUrl = getBaseUrl()
if (subscribeView.visibility == View.VISIBLE) {
@ -287,16 +295,8 @@ class AddFragment : DialogFragment() {
}
}
private fun negativeButtonClick() {
if (subscribeView.visibility == View.VISIBLE) {
dialog?.cancel()
} else if (loginView.visibility == View.VISIBLE) {
showSubscribeView()
}
}
private fun validateInputSubscribeView() {
if (!this::positiveButton.isInitialized) return // As per crash seen in Google Play
if (!this::actionMenuItem.isInitialized) return // As per crash seen in Google Play
// Show/hide things: This logic is intentionally kept simple. Do not simplify "just because it's pretty".
val instantToggleAllowed = if (!BuildConfig.FIREBASE_AVAILABLE) {
@ -334,11 +334,11 @@ class AddFragment : DialogFragment() {
activity?.let {
it.runOnUiThread {
if (subscription != null || DISALLOWED_TOPICS.contains(topic)) {
positiveButton.isEnabled = false
actionMenuItem.isEnabled = false
} else if (subscribeUseAnotherServerCheckbox.isChecked) {
positiveButton.isEnabled = validTopic(topic) && validUrl(baseUrl)
actionMenuItem.isEnabled = validTopic(topic) && validUrl(baseUrl)
} else {
positiveButton.isEnabled = validTopic(topic)
actionMenuItem.isEnabled = validTopic(topic)
}
}
}
@ -346,13 +346,13 @@ class AddFragment : DialogFragment() {
}
private fun validateInputLoginView() {
if (!this::positiveButton.isInitialized || !this::loginUsernameText.isInitialized || !this::loginPasswordText.isInitialized) {
if (!this::actionMenuItem.isInitialized || !this::loginUsernameText.isInitialized || !this::loginPasswordText.isInitialized) {
return // As per crash seen in Google Play
}
if (loginUsernameText.visibility == View.GONE) {
positiveButton.isEnabled = true
actionMenuItem.isEnabled = true
} else {
positiveButton.isEnabled = (loginUsernameText.text?.isNotEmpty() ?: false)
actionMenuItem.isEnabled = (loginUsernameText.text?.isNotEmpty() ?: false)
&& (loginPasswordText.text?.isNotEmpty() ?: false)
}
}
@ -379,8 +379,11 @@ class AddFragment : DialogFragment() {
private fun showSubscribeView() {
resetSubscribeView()
positiveButton.text = getString(R.string.add_dialog_button_subscribe)
negativeButton.text = getString(R.string.add_dialog_button_cancel)
toolbar.setTitle(R.string.add_dialog_title)
actionMenuItem.setTitle(R.string.add_dialog_button_subscribe)
toolbar.setNavigationOnClickListener {
dismiss()
}
loginView.visibility = View.GONE
subscribeView.visibility = View.VISIBLE
if (subscribeTopicText.requestFocus()) {
@ -392,8 +395,11 @@ class AddFragment : DialogFragment() {
private fun showLoginView(activity: Activity) {
resetLoginView()
loginProgress.visibility = View.INVISIBLE
positiveButton.text = getString(R.string.add_dialog_button_login)
negativeButton.text = getString(R.string.add_dialog_button_back)
toolbar.setTitle(R.string.add_dialog_login_title)
actionMenuItem.setTitle(R.string.add_dialog_button_login)
toolbar.setNavigationOnClickListener {
showSubscribeView()
}
subscribeView.visibility = View.GONE
loginView.visibility = View.VISIBLE
if (loginUsernameText.requestFocus()) {
@ -407,7 +413,7 @@ class AddFragment : DialogFragment() {
subscribeBaseUrlText.isEnabled = enable
subscribeInstantDeliveryCheckbox.isEnabled = enable
subscribeUseAnotherServerCheckbox.isEnabled = enable
positiveButton.isEnabled = enable
actionMenuItem.isEnabled = enable
}
private fun resetSubscribeView() {
@ -420,7 +426,7 @@ class AddFragment : DialogFragment() {
private fun enableLoginView(enable: Boolean) {
loginUsernameText.isEnabled = enable
loginPasswordText.isEnabled = enable
positiveButton.isEnabled = enable
actionMenuItem.isEnabled = enable
if (enable && loginUsernameText.requestFocus()) {
val imm = activity?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
imm?.showSoftInput(loginUsernameText, InputMethodManager.SHOW_IMPLICIT)

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:interpolator/decelerate_cubic">
<translate
android:duration="300"
android:fromYDelta="100%"
android:toYDelta="0" />
</set>

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:interpolator/accelerate_cubic">
<translate
android:duration="250"
android:fromYDelta="0"
android:toYDelta="100%" />
</set>

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
</vector>

View file

@ -1,355 +1,340 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingHorizontal="?dialogPreferredPadding">
<LinearLayout
android:id="@+id/add_dialog_button_bar"
android:background="?attr/colorSurface"
android:fitsSystemWindows="true">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/add_dialog_app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="12dp">
android:background="?attr/colorSurface"
app:elevation="0dp"
app:liftOnScroll="false">
<Button
android:id="@+id/add_dialog_negative_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/add_dialog_button_cancel"
android:layout_weight="1"/>
<Button
android:id="@+id/add_dialog_positive_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/add_dialog_button_subscribe"
android:layout_weight="1"/>
</LinearLayout>
<ScrollView
android:id="@+id/add_dialog_subscribe_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/add_dialog_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorSurface"
app:navigationIcon="@drawable/ic_close_white_24dp"
app:navigationIconTint="?attr/colorOnSurface"
app:title="@string/add_dialog_title"
app:titleTextColor="?attr/colorOnSurface"
app:menu="@menu/menu_add_dialog" />
<TextView
android:id="@+id/add_dialog_subscribe_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:paddingTop="16dp"
android:paddingBottom="3dp"
android:text="@string/add_dialog_title"
android:textAlignment="viewStart"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
android:paddingStart="4dp"
android:paddingEnd="4dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</com.google.android.material.appbar.AppBarLayout>
<ProgressBar
style="?android:attr/progressBarStyle"
android:layout_width="24dp"
android:layout_height="24dp"
android:id="@+id/add_dialog_subscribe_progress"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@+id/add_dialog_subscribe_description"
android:indeterminate="true"
android:layout_marginBottom="5dp"
android:visibility="gone"/>
<TextView
android:id="@+id/add_dialog_subscribe_description"
android:text="@string/add_dialog_description_below"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="4dp"
android:paddingEnd="4dp"
android:paddingTop="3dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/add_dialog_subscribe_title"/>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/add_dialog_subscribe_topic_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/add_dialog_subscribe_description"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="10dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/add_dialog_subscribe_topic_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/add_dialog_topic_name_hint"
android:importantForAutofill="no"
android:maxLines="1"
android:inputType="text"
android:maxLength="64"/>
</com.google.android.material.textfield.TextInputLayout>
<CheckBox
android:id="@+id/add_dialog_subscribe_use_another_server_checkbox"
android:text="@string/add_dialog_use_another_server"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="-3dp"
android:layout_marginTop="-3dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/add_dialog_subscribe_topic_layout"/>
<TextView
android:id="@+id/add_dialog_subscribe_use_another_server_description"
android:text="@string/add_dialog_use_another_server_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="-5dp"
android:paddingStart="4dp"
android:paddingEnd="4dp"
android:paddingTop="0dp"
android:visibility="gone"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/add_dialog_subscribe_use_another_server_checkbox" />
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox.Dense.ExposedDropdownMenu"
android:id="@+id/add_dialog_subscribe_base_url_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="0dp"
android:padding="0dp"
android:visibility="gone"
app:endIconMode="custom"
app:hintEnabled="false"
app:boxBackgroundColor="@null"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/add_dialog_subscribe_use_another_server_description">
<AutoCompleteTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/add_dialog_subscribe_base_url_text"
android:hint="@string/app_base_url"
android:maxLines="1"
android:layout_marginTop="4dp"
android:layout_marginBottom="10dp"
android:inputType="textNoSuggestions"
android:paddingStart="0dp"
android:paddingEnd="0dp"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
android:textAppearance="?android:attr/textAppearanceMedium"/>
</com.google.android.material.textfield.TextInputLayout>
<LinearLayout
android:id="@+id/add_dialog_subscribe_instant_delivery_box"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="-3dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/add_dialog_subscribe_base_url_layout">
<CheckBox
android:id="@+id/add_dialog_subscribe_instant_delivery_checkbox"
android:text="@string/add_dialog_instant_delivery"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="-8dp"
android:layout_marginBottom="-5dp"
android:layout_marginStart="-3dp"/>
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginTop="3dp"
android:paddingTop="3dp"
android:id="@+id/add_dialog_subscribe_instant_image"
app:srcCompat="@drawable/ic_bolt_gray_24dp"
app:layout_constraintTop_toTopOf="@+id/main_item_text"
app:layout_constraintEnd_toStartOf="@+id/main_item_date"/>
</LinearLayout>
<TextView
android:id="@+id/add_dialog_subscribe_instant_delivery_description"
android:text="@string/add_dialog_instant_delivery_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="4dp"
android:paddingEnd="4dp"
android:paddingTop="0dp"
android:visibility="gone"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/add_dialog_subscribe_instant_delivery_box"/>
<TextView
android:id="@+id/add_dialog_subscribe_foreground_description"
android:text="@string/add_dialog_foreground_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="4dp"
android:paddingEnd="4dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/add_dialog_subscribe_instant_delivery_description"/>
<ImageView
android:id="@+id/add_dialog_subscribe_error_text_image"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginTop="1dp"
android:visibility="gone"
app:srcCompat="@drawable/ic_error_red_24dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/add_dialog_subscribe_error_text"/>
<TextView
android:id="@+id/add_dialog_subscribe_error_text"
android:text="Unable to resolve host example.com"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:paddingStart="4dp"
android:paddingEnd="4dp"
android:textAppearance="@style/DangerText"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/add_dialog_subscribe_foreground_description"
app:layout_constraintStart_toEndOf="@id/add_dialog_subscribe_error_text_image"
tools:visibility="gone"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
<ScrollView
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/add_dialog_login_view">
android:orientation="vertical"
android:paddingHorizontal="?dialogPreferredPadding"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<androidx.constraintlayout.widget.ConstraintLayout
<ScrollView
android:id="@+id/add_dialog_subscribe_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
android:layout_height="match_parent">
<TextView
android:id="@+id/add_dialog_login_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:paddingTop="16dp"
android:paddingBottom="3dp"
android:text="@string/add_dialog_login_title"
android:textAlignment="viewStart"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
android:paddingStart="4dp"
android:paddingEnd="4dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:text="@string/add_dialog_login_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/add_dialog_login_description"
android:paddingStart="4dp"
android:paddingTop="3dp"
android:paddingEnd="4dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/add_dialog_login_title" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/add_dialog_login_username_layout"
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/add_dialog_login_description"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="10dp">
android:orientation="horizontal">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/add_dialog_login_username"
<TextView
android:id="@+id/add_dialog_subscribe_description"
android:text="@string/add_dialog_description_below"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/add_dialog_login_username_hint"
android:importantForAutofill="no"
android:maxLines="1"
android:inputType="text"
android:maxLength="64"/>
android:paddingStart="4dp"
android:paddingEnd="4dp"
android:paddingTop="16dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</com.google.android.material.textfield.TextInputLayout>
<ProgressBar
style="?android:attr/progressBarStyle"
android:layout_width="24dp"
android:layout_height="24dp"
android:id="@+id/add_dialog_subscribe_progress"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:indeterminate="true"
android:layout_marginTop="16dp"
android:visibility="gone"/>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/add_dialog_login_password_layout"
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/add_dialog_subscribe_topic_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/add_dialog_subscribe_description"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="10dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/add_dialog_subscribe_topic_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/add_dialog_topic_name_hint"
android:importantForAutofill="no"
android:maxLines="1"
android:inputType="text"
android:maxLength="64"/>
</com.google.android.material.textfield.TextInputLayout>
<CheckBox
android:id="@+id/add_dialog_subscribe_use_another_server_checkbox"
android:text="@string/add_dialog_use_another_server"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="-3dp"
android:layout_marginTop="-3dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/add_dialog_subscribe_topic_layout"/>
<TextView
android:id="@+id/add_dialog_subscribe_use_another_server_description"
android:text="@string/add_dialog_use_another_server_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="-5dp"
android:paddingStart="4dp"
android:paddingEnd="4dp"
android:paddingTop="0dp"
android:visibility="gone"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/add_dialog_subscribe_use_another_server_checkbox" />
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox.Dense.ExposedDropdownMenu"
android:id="@+id/add_dialog_subscribe_base_url_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="0dp"
android:padding="0dp"
android:visibility="gone"
app:endIconMode="custom"
app:hintEnabled="false"
app:boxBackgroundColor="@null"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/add_dialog_subscribe_use_another_server_description">
<AutoCompleteTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/add_dialog_subscribe_base_url_text"
android:hint="@string/app_base_url"
android:maxLines="1"
android:layout_marginTop="4dp"
android:layout_marginBottom="10dp"
android:inputType="textNoSuggestions"
android:paddingStart="0dp"
android:paddingEnd="0dp"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
android:textAppearance="?android:attr/textAppearanceMedium"/>
</com.google.android.material.textfield.TextInputLayout>
<LinearLayout
android:id="@+id/add_dialog_subscribe_instant_delivery_box"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="-3dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/add_dialog_subscribe_base_url_layout">
<CheckBox
android:id="@+id/add_dialog_subscribe_instant_delivery_checkbox"
android:text="@string/add_dialog_instant_delivery"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="-8dp"
android:layout_marginBottom="-5dp"
android:layout_marginStart="-3dp"/>
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginTop="3dp"
android:paddingTop="3dp"
android:id="@+id/add_dialog_subscribe_instant_image"
app:srcCompat="@drawable/ic_bolt_gray_24dp"
app:layout_constraintTop_toTopOf="@+id/main_item_text"
app:layout_constraintEnd_toStartOf="@+id/main_item_date"/>
</LinearLayout>
<TextView
android:id="@+id/add_dialog_subscribe_instant_delivery_description"
android:text="@string/add_dialog_instant_delivery_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="4dp"
android:paddingEnd="4dp"
android:paddingTop="0dp"
android:visibility="gone"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/add_dialog_subscribe_instant_delivery_box"/>
<TextView
android:id="@+id/add_dialog_subscribe_foreground_description"
android:text="@string/add_dialog_foreground_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="4dp"
android:paddingEnd="4dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/add_dialog_subscribe_instant_delivery_description"/>
<ImageView
android:id="@+id/add_dialog_subscribe_error_text_image"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginTop="1dp"
android:visibility="gone"
app:srcCompat="@drawable/ic_error_red_24dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/add_dialog_subscribe_error_text"/>
<TextView
android:id="@+id/add_dialog_subscribe_error_text"
android:text="Unable to resolve host example.com"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:paddingStart="4dp"
android:paddingEnd="4dp"
android:textAppearance="@style/DangerText"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/add_dialog_subscribe_foreground_description"
app:layout_constraintStart_toEndOf="@id/add_dialog_subscribe_error_text_image"
tools:visibility="gone"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/add_dialog_login_username_layout"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="10dp">
android:layout_height="match_parent"
android:id="@+id/add_dialog_login_view"
android:visibility="gone">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/add_dialog_login_password"
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/add_dialog_login_password_hint"
android:importantForAutofill="no"
android:maxLines="1"
android:inputType="textPassword"/>
android:orientation="horizontal">
</com.google.android.material.textfield.TextInputLayout>
<TextView
android:text="@string/add_dialog_login_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/add_dialog_login_description"
android:paddingStart="4dp"
android:paddingTop="16dp"
android:paddingEnd="4dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:id="@+id/add_dialog_login_error_text_image"
android:visibility="visible"
app:srcCompat="@drawable/ic_error_red_24dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="@+id/add_dialog_login_error_text"
app:layout_constraintTop_toTopOf="@+id/add_dialog_login_error_text"/>
<ProgressBar
style="?android:attr/progressBarStyle"
android:layout_width="25dp"
android:layout_height="25dp"
android:id="@+id/add_dialog_login_progress"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:indeterminate="true"
android:layout_marginTop="16dp"
android:visibility="gone"/>
<TextView
android:id="@+id/add_dialog_login_error_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Login failed. User not authorized."
android:paddingStart="4dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/add_dialog_login_password_layout"
android:paddingEnd="4dp"
android:textAppearance="@style/DangerText"
app:layout_constraintStart_toEndOf="@id/add_dialog_login_error_text_image"/>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/add_dialog_login_username_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/add_dialog_login_description"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="10dp">
<ProgressBar
style="?android:attr/progressBarStyle"
android:layout_width="25dp"
android:layout_height="25dp"
android:id="@+id/add_dialog_login_progress"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@+id/add_dialog_login_description"
android:indeterminate="true"
android:layout_marginBottom="5dp"/>
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/add_dialog_login_username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/add_dialog_login_username_hint"
android:importantForAutofill="no"
android:maxLines="1"
android:inputType="text"
android:maxLength="64"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.textfield.TextInputLayout>
</ScrollView>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/add_dialog_login_password_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/add_dialog_login_username_layout"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="10dp">
</LinearLayout>
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/add_dialog_login_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/add_dialog_login_password_hint"
android:importantForAutofill="no"
android:maxLines="1"
android:inputType="textPassword"/>
</com.google.android.material.textfield.TextInputLayout>
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:id="@+id/add_dialog_login_error_text_image"
android:visibility="gone"
app:srcCompat="@drawable/ic_error_red_24dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="@+id/add_dialog_login_error_text"
app:layout_constraintTop_toTopOf="@+id/add_dialog_login_error_text"/>
<TextView
android:id="@+id/add_dialog_login_error_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Login failed. User not authorized."
android:paddingStart="4dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/add_dialog_login_password_layout"
android:paddingEnd="4dp"
android:visibility="gone"
android:textAppearance="@style/DangerText"
app:layout_constraintStart_toEndOf="@id/add_dialog_login_error_text_image"
tools:visibility="visible"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/add_dialog_action_button"
android:title="@string/add_dialog_button_subscribe"
android:enabled="false"
app:showAsAction="always" />
</menu>

View file

@ -106,4 +106,19 @@
<item name="cornerFamily">rounded</item>
<item name="cornerSize">8dp</item>
</style>
<!-- Full-screen dialog style for Material 3 compliance -->
<style name="Theme.App.FullScreenDialog" parent="AppTheme">
<item name="android:windowNoTitle">true</item>
<item name="android:windowIsFloating">false</item>
<item name="android:statusBarColor">?attr/colorSurface</item>
<item name="android:windowBackground">?attr/colorSurface</item>
<item name="android:windowAnimationStyle">@style/Animation.App.FullScreenDialog</item>
</style>
<!-- Slide animation for full-screen dialog -->
<style name="Animation.App.FullScreenDialog" parent="Animation.AppCompat.Dialog">
<item name="android:windowEnterAnimation">@anim/slide_in_bottom</item>
<item name="android:windowExitAnimation">@anim/slide_out_bottom</item>
</style>
</resources>