diff --git a/app/src/main/java/io/heckel/ntfy/ui/AddFragment.kt b/app/src/main/java/io/heckel/ntfy/ui/AddFragment.kt index 1b14b3a..a901d31 100644 --- a/app/src/main/java/io/heckel/ntfy/ui/AddFragment.kt +++ b/app/src/main/java/io/heckel/ntfy/ui/AddFragment.kt @@ -26,7 +26,6 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlin.random.Random - class AddFragment : DialogFragment() { private val api = ApiService() diff --git a/app/src/main/java/io/heckel/ntfy/ui/SettingsActivity.kt b/app/src/main/java/io/heckel/ntfy/ui/SettingsActivity.kt index 6577ecd..ed8a1ac 100644 --- a/app/src/main/java/io/heckel/ntfy/ui/SettingsActivity.kt +++ b/app/src/main/java/io/heckel/ntfy/ui/SettingsActivity.kt @@ -563,13 +563,28 @@ class SettingsActivity : AppCompatActivity(), PreferenceFragmentCompat.OnPrefere } preference.onPreferenceClickListener = OnPreferenceClickListener { _ -> activity?.let { - UserFragment().show(it.supportFragmentManager, UserFragment.TAG) + UserFragment + .newInstance(user.user) + .show(it.supportFragmentManager, UserFragment.TAG) } true } preferenceCategory.addPreference(preference) } } + + // Add user + val preference = Preference(preferenceScreen.context) + preference.title = getString(R.string.settings_users_prefs_user_add) + preference.onPreferenceClickListener = OnPreferenceClickListener { _ -> + activity?.let { + UserFragment + .newInstance(user = null) + .show(it.supportFragmentManager, UserFragment.TAG) + } + true + } + preferenceScreen.addPreference(preference) } } diff --git a/app/src/main/java/io/heckel/ntfy/ui/UserFragment.kt b/app/src/main/java/io/heckel/ntfy/ui/UserFragment.kt index b76af6c..96e03fa 100644 --- a/app/src/main/java/io/heckel/ntfy/ui/UserFragment.kt +++ b/app/src/main/java/io/heckel/ntfy/ui/UserFragment.kt @@ -3,31 +3,63 @@ package io.heckel.ntfy.ui import android.app.AlertDialog import android.app.Dialog import android.os.Bundle +import android.text.Editable +import android.text.TextWatcher +import android.view.View import android.view.WindowManager +import android.widget.Button import android.widget.TextView +import androidx.core.content.ContextCompat import androidx.fragment.app.DialogFragment +import com.google.android.material.textfield.TextInputEditText import io.heckel.ntfy.R +import io.heckel.ntfy.db.User class UserFragment : DialogFragment() { + private var user: User? = null + + private lateinit var baseUrlView: TextInputEditText + private lateinit var usernameView: TextInputEditText + private lateinit var passwordView: TextInputEditText + private lateinit var positiveButton: Button + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - if (activity == null) { - throw IllegalStateException("Activity cannot be null") + // Reconstruct user (if it is present in the bundle) + val userId = arguments?.getLong(BUNDLE_USER_ID) + val baseUrl = arguments?.getString(BUNDLE_BASE_URL) + val username = arguments?.getString(BUNDLE_USERNAME) + val password = arguments?.getString(BUNDLE_PASSWORD) + + if (userId != null && baseUrl != null && username != null && password != null) { + user = User(userId, baseUrl, username, password) } // Build root view val view = requireActivity().layoutInflater.inflate(R.layout.fragment_user_dialog, null) - val addMode = false // FIXME - val positiveButtonTextResId = if (addMode) R.string.user_dialog_button_add else R.string.user_dialog_button_save - val titleText = view.findViewById(R.id.user_dialog_title) as TextView - titleText.text = if (addMode) { - getString(R.string.user_dialog_title_add) + val positiveButtonTextResId = if (user == null) R.string.user_dialog_button_add else R.string.user_dialog_button_save + val titleView = view.findViewById(R.id.user_dialog_title) as TextView + val descriptionView = view.findViewById(R.id.user_dialog_description) as TextView + + baseUrlView = view.findViewById(R.id.user_dialog_base_url) + usernameView = view.findViewById(R.id.user_dialog_username) + passwordView = view.findViewById(R.id.user_dialog_password) + + if (user == null) { + titleView.text = getString(R.string.user_dialog_title_add) + descriptionView.text = getString(R.string.user_dialog_description_add) + baseUrlView.visibility = View.VISIBLE + passwordView.hint = getString(R.string.user_dialog_password_hint_add) } else { - getString(R.string.user_dialog_title_edit) + titleView.text = getString(R.string.user_dialog_title_edit) + descriptionView.text = getString(R.string.user_dialog_description_edit) + baseUrlView.visibility = View.GONE + usernameView.setText(user!!.username) + passwordView.hint = getString(R.string.user_dialog_password_hint_edit) } // Build dialog - val dialog = AlertDialog.Builder(activity) + val builder = AlertDialog.Builder(activity) .setView(view) .setPositiveButton(positiveButtonTextResId) { _, _ -> // This will be overridden below to avoid closing the dialog immediately @@ -35,23 +67,78 @@ class UserFragment : DialogFragment() { .setNegativeButton(R.string.user_dialog_button_cancel) { _, _ -> // This will be overridden below } - .setNeutralButton(R.string.user_dialog_button_delete) { _, _ -> + if (user != null) { + builder.setNeutralButton(R.string.user_dialog_button_delete) { _, _ -> // This will be overridden below } - .create() + } + val dialog = builder.create() + dialog.setOnShowListener { + positiveButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE) + + // Delete button should be red + if (user != null) { + dialog + .getButton(AlertDialog.BUTTON_NEUTRAL) + .setTextColor(ContextCompat.getColor(requireContext(), R.color.primaryDangerButtonColor)) + } + + // Validate input when typing + val textWatcher = object : TextWatcher { + override fun afterTextChanged(s: Editable?) { + validateInput() + } + override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { + // Nothing + } + override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { + // Nothing + } + } + baseUrlView.addTextChangedListener(textWatcher) + usernameView.addTextChangedListener(textWatcher) + passwordView.addTextChangedListener(textWatcher) + + // Validate now! + validateInput() + } // 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 { - - } - return dialog } + private fun validateInput() { + val baseUrl = baseUrlView.text?.toString() ?: "" + val username = usernameView.text?.toString() ?: "" + val password = passwordView.text?.toString() ?: "" + if (user == null) { + positiveButton.isEnabled = (baseUrl.startsWith("http://") || baseUrl.startsWith("https://")) + && username.isNotEmpty() && password.isNotEmpty() + } else { + positiveButton.isEnabled = username.isNotEmpty() // Unchanged if left blank + } + } + companion object { const val TAG = "NtfyUserFragment" + private const val BUNDLE_USER_ID = "userId" + private const val BUNDLE_BASE_URL = "baseUrl" + private const val BUNDLE_USERNAME = "username" + private const val BUNDLE_PASSWORD = "password" + + fun newInstance(user: User?): UserFragment { + val fragment = UserFragment() + val args = Bundle() + if (user != null) { + args.putLong(BUNDLE_USER_ID, user.id) + args.putString(BUNDLE_BASE_URL, user.baseUrl) + args.putString(BUNDLE_USERNAME, user.username) + args.putString(BUNDLE_PASSWORD, user.password) + fragment.arguments = args + } + return fragment + } } } diff --git a/app/src/main/res/layout/fragment_user_dialog.xml b/app/src/main/res/layout/fragment_user_dialog.xml index 3676206..c1905e7 100644 --- a/app/src/main/res/layout/fragment_user_dialog.xml +++ b/app/src/main/res/layout/fragment_user_dialog.xml @@ -8,6 +8,13 @@ android:paddingLeft="16dp" android:paddingRight="16dp" android:visibility="visible"> + + + android:layout_marginTop="6dp" app:layout_constraintTop_toBottomOf="@id/user_dialog_base_url"/> Not used by any topics Used by topic %1$s Used by topics %1$s + Add user UnifiedPush Allows other apps to use ntfy as a message distributor. Find out more at unifiedpush.org. UnifiedPushEnabled @@ -297,8 +298,12 @@ Add user Edit user + You can add a user here that you can later associate with a specific topic. + You may edit username/password for the selected user, or delete it entirely. + Server URL Username - Password + Password + Password (unchanged if blank) Add user Cancel Delete user