e-ntfy-android/app/src/main/java/io/heckel/ntfy/ui/UserFragment.kt

193 lines
7.5 KiB
Kotlin
Raw Normal View History

2022-01-29 04:53:48 +01:00
package io.heckel.ntfy.ui
import android.app.AlertDialog
import android.app.Dialog
2022-01-30 04:25:39 +01:00
import android.content.Context
2022-01-29 04:53:48 +01:00
import android.os.Bundle
2022-01-29 22:59:51 +01:00
import android.text.Editable
import android.text.TextWatcher
import android.view.View
2022-01-29 04:53:48 +01:00
import android.view.WindowManager
2022-01-29 22:59:51 +01:00
import android.widget.Button
2022-01-29 04:53:48 +01:00
import android.widget.TextView
2022-01-29 22:59:51 +01:00
import androidx.core.content.ContextCompat
2022-01-29 04:53:48 +01:00
import androidx.fragment.app.DialogFragment
2022-01-29 22:59:51 +01:00
import com.google.android.material.textfield.TextInputEditText
2022-01-29 04:53:48 +01:00
import io.heckel.ntfy.R
2022-01-29 22:59:51 +01:00
import io.heckel.ntfy.db.User
2022-01-30 04:25:39 +01:00
import kotlin.random.Random
2022-01-29 04:53:48 +01:00
class UserFragment : DialogFragment() {
2022-01-29 22:59:51 +01:00
private var user: User? = null
private lateinit var baseUrlsInUse: ArrayList<String>
2022-01-30 04:25:39 +01:00
private lateinit var listener: UserDialogListener
2022-01-29 22:59:51 +01:00
private lateinit var baseUrlView: TextInputEditText
private lateinit var usernameView: TextInputEditText
private lateinit var passwordView: TextInputEditText
private lateinit var positiveButton: Button
2022-01-30 04:25:39 +01:00
interface UserDialogListener {
fun onAddUser(dialog: DialogFragment, user: User)
fun onUpdateUser(dialog: DialogFragment, user: User)
fun onDeleteUser(dialog: DialogFragment, baseUrl: String)
2022-01-30 04:25:39 +01:00
}
override fun onAttach(context: Context) {
super.onAttach(context)
listener = activity as UserDialogListener
}
2022-01-29 04:53:48 +01:00
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
2022-01-29 22:59:51 +01:00
// Reconstruct user (if it is present in the bundle)
val baseUrl = arguments?.getString(BUNDLE_BASE_URL)
val username = arguments?.getString(BUNDLE_USERNAME)
val password = arguments?.getString(BUNDLE_PASSWORD)
if (baseUrl != null && username != null && password != null) {
user = User(baseUrl, username, password)
2022-01-29 04:53:48 +01:00
}
// Required for validation
baseUrlsInUse = arguments?.getStringArrayList(BUNDLE_BASE_URLS_IN_USE) ?: arrayListOf()
2022-01-29 04:53:48 +01:00
// Build root view
val view = requireActivity().layoutInflater.inflate(R.layout.fragment_user_dialog, null)
2022-01-29 22:59:51 +01:00
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)
2022-01-29 04:53:48 +01:00
} else {
2022-01-29 22:59:51 +01:00
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)
2022-01-29 04:53:48 +01:00
}
// Build dialog
2022-01-29 22:59:51 +01:00
val builder = AlertDialog.Builder(activity)
2022-01-29 04:53:48 +01:00
.setView(view)
.setPositiveButton(positiveButtonTextResId) { _, _ ->
2022-01-30 04:25:39 +01:00
saveClicked()
2022-01-29 04:53:48 +01:00
}
.setNegativeButton(R.string.user_dialog_button_cancel) { _, _ ->
2022-01-30 04:25:39 +01:00
// Do nothing
2022-01-29 04:53:48 +01:00
}
2022-01-29 22:59:51 +01:00
if (user != null) {
builder.setNeutralButton(R.string.user_dialog_button_delete) { _, _ ->
if (this::listener.isInitialized) {
listener.onDeleteUser(this, user!!.baseUrl)
2022-01-30 04:25:39 +01:00
}
2022-01-29 04:53:48 +01:00
}
2022-01-29 22:59:51 +01:00
}
val dialog = builder.create()
dialog.setOnShowListener {
positiveButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE)
2022-01-29 04:53:48 +01:00
2022-01-29 22:59:51 +01:00
// Delete button should be red
if (user != null) {
dialog
.getButton(AlertDialog.BUTTON_NEUTRAL)
.setTextColor(ContextCompat.getColor(requireContext(), R.color.primaryDangerButtonColor))
}
2022-01-29 04:53:48 +01:00
2022-01-29 22:59:51 +01:00
// 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)
2022-01-29 04:53:48 +01:00
2022-02-05 01:52:34 +01:00
// Focus
if (user != null) {
usernameView.requestFocus()
if (usernameView.text != null) {
usernameView.setSelection(usernameView.text!!.length)
}
} else {
baseUrlView.requestFocus()
}
2022-01-29 22:59:51 +01:00
// Validate now!
validateInput()
2022-01-29 04:53:48 +01:00
}
2022-01-29 22:59:51 +01:00
// Show keyboard when the dialog is shown (see https://stackoverflow.com/a/19573049/1440785)
dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
2022-01-29 04:53:48 +01:00
return dialog
}
2022-01-30 04:25:39 +01:00
private fun saveClicked() {
if (!this::listener.isInitialized) return
val baseUrl = baseUrlView.text?.toString() ?: ""
val username = usernameView.text?.toString() ?: ""
val password = passwordView.text?.toString() ?: ""
if (user == null) {
user = User(baseUrl, username, password)
2022-01-30 04:25:39 +01:00
listener.onAddUser(this, user!!)
} else {
user = if (password.isNotEmpty()) {
user!!.copy(username = username, password = password)
} else {
user!!.copy(username = username)
}
listener.onUpdateUser(this, user!!)
}
}
2022-01-29 22:59:51 +01:00
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://"))
&& !baseUrlsInUse.contains(baseUrl)
2022-01-29 22:59:51 +01:00
&& username.isNotEmpty() && password.isNotEmpty()
} else {
positiveButton.isEnabled = username.isNotEmpty() // Unchanged if left blank
}
}
2022-01-29 04:53:48 +01:00
companion object {
const val TAG = "NtfyUserFragment"
2022-01-29 22:59:51 +01:00
private const val BUNDLE_BASE_URL = "baseUrl"
private const val BUNDLE_USERNAME = "username"
private const val BUNDLE_PASSWORD = "password"
private const val BUNDLE_BASE_URLS_IN_USE = "baseUrlsInUse"
2022-01-29 22:59:51 +01:00
fun newInstance(user: User?, baseUrlsInUse: ArrayList<String>): UserFragment {
2022-01-29 22:59:51 +01:00
val fragment = UserFragment()
val args = Bundle()
args.putStringArrayList(BUNDLE_BASE_URLS_IN_USE, baseUrlsInUse)
2022-01-29 22:59:51 +01:00
if (user != null) {
args.putString(BUNDLE_BASE_URL, user.baseUrl)
args.putString(BUNDLE_USERNAME, user.username)
args.putString(BUNDLE_PASSWORD, user.password)
}
fragment.arguments = args
2022-01-29 22:59:51 +01:00
return fragment
}
2022-01-29 04:53:48 +01:00
}
}