2021-10-28 05:04:14 +02:00
|
|
|
package io.heckel.ntfy.ui
|
2021-10-28 04:25:02 +02:00
|
|
|
|
2022-01-28 17:42:44 +01:00
|
|
|
import android.app.Activity
|
2021-10-28 04:25:02 +02:00
|
|
|
import android.app.AlertDialog
|
|
|
|
import android.app.Dialog
|
2021-11-18 02:16:58 +01:00
|
|
|
import android.content.Context
|
2021-10-28 04:25:02 +02:00
|
|
|
import android.os.Bundle
|
|
|
|
import android.text.Editable
|
|
|
|
import android.text.TextWatcher
|
|
|
|
import android.view.View
|
2022-01-28 16:19:12 +01:00
|
|
|
import android.view.WindowManager
|
|
|
|
import android.view.inputmethod.InputMethodManager
|
2022-01-13 05:38:50 +01:00
|
|
|
import android.widget.*
|
2021-10-28 04:25:02 +02:00
|
|
|
import androidx.fragment.app.DialogFragment
|
2021-11-01 13:58:12 +01:00
|
|
|
import androidx.lifecycle.lifecycleScope
|
2021-10-28 04:25:02 +02:00
|
|
|
import com.google.android.material.textfield.TextInputEditText
|
2021-11-25 21:45:12 +01:00
|
|
|
import com.google.android.material.textfield.TextInputLayout
|
|
|
|
import io.heckel.ntfy.BuildConfig
|
2021-10-28 05:04:14 +02:00
|
|
|
import io.heckel.ntfy.R
|
2022-01-18 20:28:48 +01:00
|
|
|
import io.heckel.ntfy.db.Repository
|
2022-01-28 01:57:43 +01:00
|
|
|
import io.heckel.ntfy.db.User
|
|
|
|
import io.heckel.ntfy.log.Log
|
|
|
|
import io.heckel.ntfy.msg.ApiService
|
|
|
|
import io.heckel.ntfy.util.topicUrl
|
2021-11-01 13:58:12 +01:00
|
|
|
import kotlinx.coroutines.Dispatchers
|
|
|
|
import kotlinx.coroutines.launch
|
2022-01-28 01:57:43 +01:00
|
|
|
import kotlin.random.Random
|
2021-10-28 04:25:02 +02:00
|
|
|
|
2022-01-28 17:42:44 +01:00
|
|
|
|
2021-11-18 02:16:58 +01:00
|
|
|
class AddFragment : DialogFragment() {
|
2022-01-28 01:57:43 +01:00
|
|
|
private val api = ApiService()
|
|
|
|
|
2021-11-18 02:16:58 +01:00
|
|
|
private lateinit var repository: Repository
|
|
|
|
private lateinit var subscribeListener: SubscribeListener
|
|
|
|
|
2022-01-28 01:57:43 +01:00
|
|
|
private lateinit var subscribeView: View
|
|
|
|
private lateinit var loginView: View
|
2022-01-28 17:42:44 +01:00
|
|
|
private lateinit var positiveButton: Button
|
|
|
|
private lateinit var negativeButton: Button
|
2022-01-28 01:57:43 +01:00
|
|
|
|
2022-01-28 04:42:22 +01:00
|
|
|
// Subscribe page
|
2022-01-28 06:02:20 +01:00
|
|
|
private lateinit var subscribeTopicText: TextInputEditText
|
|
|
|
private lateinit var subscribeBaseUrlLayout: TextInputLayout
|
|
|
|
private lateinit var subscribeBaseUrlText: AutoCompleteTextView
|
|
|
|
private lateinit var subscribeUseAnotherServerCheckbox: CheckBox
|
|
|
|
private lateinit var subscribeUseAnotherServerDescription: TextView
|
|
|
|
private lateinit var subscribeInstantDeliveryBox: View
|
|
|
|
private lateinit var subscribeInstantDeliveryCheckbox: CheckBox
|
|
|
|
private lateinit var subscribeInstantDeliveryDescription: View
|
|
|
|
private lateinit var subscribeProgress: ProgressBar
|
|
|
|
private lateinit var subscribeErrorImage: View
|
2021-10-28 04:25:02 +02:00
|
|
|
|
2022-01-28 04:42:22 +01:00
|
|
|
// Login page
|
2022-01-28 01:57:43 +01:00
|
|
|
private lateinit var users: List<User>
|
2022-01-28 06:02:20 +01:00
|
|
|
private lateinit var loginUsersSpinner: Spinner
|
|
|
|
private lateinit var loginUsernameText: TextInputEditText
|
|
|
|
private lateinit var loginPasswordText: TextInputEditText
|
2022-01-28 04:42:22 +01:00
|
|
|
private lateinit var loginProgress: ProgressBar
|
2022-01-28 06:02:20 +01:00
|
|
|
private lateinit var loginErrorImage: View
|
2022-01-28 01:57:43 +01:00
|
|
|
|
2021-11-25 21:45:12 +01:00
|
|
|
private lateinit var baseUrls: List<String> // List of base URLs already used, excluding app_base_url
|
|
|
|
|
2021-11-18 02:16:58 +01:00
|
|
|
interface SubscribeListener {
|
2022-01-28 01:57:43 +01:00
|
|
|
fun onSubscribe(topic: String, baseUrl: String, instant: Boolean, authUserId: Long?)
|
2021-11-18 02:16:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
override fun onAttach(context: Context) {
|
|
|
|
super.onAttach(context)
|
|
|
|
subscribeListener = activity as SubscribeListener
|
|
|
|
}
|
|
|
|
|
2021-10-28 04:25:02 +02:00
|
|
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
2021-11-18 02:16:58 +01:00
|
|
|
if (activity == null) {
|
|
|
|
throw IllegalStateException("Activity cannot be null")
|
|
|
|
}
|
|
|
|
|
2022-01-14 19:58:40 +01:00
|
|
|
// Dependencies (Fragments need a default constructor)
|
|
|
|
repository = Repository.getInstance(requireActivity())
|
2021-11-18 02:16:58 +01:00
|
|
|
|
|
|
|
// Build root view
|
2021-11-22 21:45:43 +01:00
|
|
|
val view = requireActivity().layoutInflater.inflate(R.layout.fragment_add_dialog, null)
|
2022-01-28 01:57:43 +01:00
|
|
|
|
|
|
|
// Main "pages"
|
|
|
|
subscribeView = view.findViewById(R.id.add_dialog_subscribe_view)
|
2022-01-28 17:42:44 +01:00
|
|
|
subscribeView.visibility = View.VISIBLE
|
2022-01-28 01:57:43 +01:00
|
|
|
loginView = view.findViewById(R.id.add_dialog_login_view)
|
|
|
|
loginView.visibility = View.GONE
|
|
|
|
|
|
|
|
// Fields for "subscribe page"
|
2022-01-28 06:02:20 +01:00
|
|
|
subscribeTopicText = view.findViewById(R.id.add_dialog_topic_text)
|
|
|
|
subscribeBaseUrlLayout = view.findViewById(R.id.add_dialog_base_url_layout)
|
|
|
|
subscribeBaseUrlText = view.findViewById(R.id.add_dialog_base_url_text)
|
|
|
|
subscribeInstantDeliveryBox = view.findViewById(R.id.add_dialog_instant_delivery_box)
|
|
|
|
subscribeInstantDeliveryCheckbox = view.findViewById(R.id.add_dialog_instant_delivery_checkbox)
|
|
|
|
subscribeInstantDeliveryDescription = view.findViewById(R.id.add_dialog_instant_delivery_description)
|
|
|
|
subscribeUseAnotherServerCheckbox = view.findViewById(R.id.add_dialog_use_another_server_checkbox)
|
|
|
|
subscribeUseAnotherServerDescription = view.findViewById(R.id.add_dialog_use_another_server_description)
|
|
|
|
subscribeProgress = view.findViewById(R.id.add_dialog_progress)
|
|
|
|
subscribeErrorImage = view.findViewById(R.id.add_dialog_error_image)
|
2021-11-18 02:16:58 +01:00
|
|
|
|
2022-01-28 01:57:43 +01:00
|
|
|
// Fields for "login page"
|
2022-01-28 06:02:20 +01:00
|
|
|
loginUsersSpinner = view.findViewById(R.id.add_dialog_login_users_spinner)
|
|
|
|
loginUsernameText = view.findViewById(R.id.add_dialog_login_username)
|
|
|
|
loginPasswordText = view.findViewById(R.id.add_dialog_login_password)
|
2022-01-28 04:42:22 +01:00
|
|
|
loginProgress = view.findViewById(R.id.add_dialog_login_progress)
|
2022-01-28 06:02:20 +01:00
|
|
|
loginErrorImage = view.findViewById(R.id.add_dialog_login_error_image)
|
2022-01-28 01:57:43 +01:00
|
|
|
|
2022-01-13 05:38:50 +01:00
|
|
|
// Set "Use another server" description based on flavor
|
2022-01-28 06:02:20 +01:00
|
|
|
subscribeUseAnotherServerDescription.text = if (BuildConfig.FIREBASE_AVAILABLE) {
|
2022-01-13 05:38:50 +01:00
|
|
|
getString(R.string.add_dialog_use_another_server_description)
|
|
|
|
} else {
|
|
|
|
getString(R.string.add_dialog_use_another_server_description_noinstant)
|
|
|
|
}
|
|
|
|
|
2021-11-25 21:45:12 +01:00
|
|
|
// Base URL dropdown behavior; Oh my, why is this so complicated?!
|
|
|
|
val toggleEndIcon = {
|
2022-01-28 06:02:20 +01:00
|
|
|
if (subscribeBaseUrlText.text.isNotEmpty()) {
|
|
|
|
subscribeBaseUrlLayout.setEndIconDrawable(R.drawable.ic_cancel_gray_24dp)
|
2021-11-25 21:45:12 +01:00
|
|
|
} else if (baseUrls.isEmpty()) {
|
2022-01-28 06:02:20 +01:00
|
|
|
subscribeBaseUrlLayout.setEndIconDrawable(0)
|
2021-11-25 21:45:12 +01:00
|
|
|
} else {
|
2022-01-28 06:02:20 +01:00
|
|
|
subscribeBaseUrlLayout.setEndIconDrawable(R.drawable.ic_drop_down_gray_24dp)
|
2021-11-25 21:45:12 +01:00
|
|
|
}
|
|
|
|
}
|
2022-01-28 06:02:20 +01:00
|
|
|
subscribeBaseUrlLayout.setEndIconOnClickListener {
|
|
|
|
if (subscribeBaseUrlText.text.isNotEmpty()) {
|
|
|
|
subscribeBaseUrlText.text.clear()
|
2021-11-25 21:45:12 +01:00
|
|
|
if (baseUrls.isEmpty()) {
|
2022-01-28 06:02:20 +01:00
|
|
|
subscribeBaseUrlLayout.setEndIconDrawable(0)
|
2021-11-25 21:45:12 +01:00
|
|
|
} else {
|
2022-01-28 06:02:20 +01:00
|
|
|
subscribeBaseUrlLayout.setEndIconDrawable(R.drawable.ic_drop_down_gray_24dp)
|
2021-11-25 21:45:12 +01:00
|
|
|
}
|
2022-01-28 06:02:20 +01:00
|
|
|
} else if (subscribeBaseUrlText.text.isEmpty() && baseUrls.isNotEmpty()) {
|
|
|
|
subscribeBaseUrlLayout.setEndIconDrawable(R.drawable.ic_drop_up_gray_24dp)
|
|
|
|
subscribeBaseUrlText.showDropDown()
|
2021-11-25 21:45:12 +01:00
|
|
|
}
|
|
|
|
}
|
2022-01-28 06:02:20 +01:00
|
|
|
subscribeBaseUrlText.setOnDismissListener { toggleEndIcon() }
|
|
|
|
subscribeBaseUrlText.addTextChangedListener(object : TextWatcher {
|
2021-11-25 21:45:12 +01:00
|
|
|
override fun afterTextChanged(s: Editable?) {
|
|
|
|
toggleEndIcon()
|
|
|
|
}
|
|
|
|
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
|
|
|
// Nothing
|
|
|
|
}
|
|
|
|
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
|
|
|
|
// Nothing
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2022-01-28 01:57:43 +01:00
|
|
|
// Fill autocomplete for base URL & users drop-down
|
2021-11-25 21:45:12 +01:00
|
|
|
lifecycleScope.launch(Dispatchers.IO) {
|
2022-01-28 01:57:43 +01:00
|
|
|
// Auto-complete
|
2021-11-25 21:45:12 +01:00
|
|
|
val appBaseUrl = getString(R.string.app_base_url)
|
|
|
|
baseUrls = repository.getSubscriptions()
|
|
|
|
.groupBy { it.baseUrl }
|
2021-11-26 15:13:56 +01:00
|
|
|
.map { it.key }
|
2021-11-25 21:45:12 +01:00
|
|
|
.filterNot { it == appBaseUrl }
|
2021-11-26 15:13:56 +01:00
|
|
|
.sorted()
|
2022-01-28 17:42:44 +01:00
|
|
|
val activity = activity ?: return@launch // We may have pressed "Cancel"
|
|
|
|
val adapter = ArrayAdapter(activity, R.layout.fragment_add_dialog_dropdown_item, baseUrls)
|
|
|
|
activity.runOnUiThread {
|
2022-01-28 06:02:20 +01:00
|
|
|
subscribeBaseUrlText.threshold = 1
|
|
|
|
subscribeBaseUrlText.setAdapter(adapter)
|
2021-11-26 15:13:56 +01:00
|
|
|
if (baseUrls.count() == 1) {
|
2022-01-28 06:02:20 +01:00
|
|
|
subscribeBaseUrlLayout.setEndIconDrawable(R.drawable.ic_cancel_gray_24dp)
|
|
|
|
subscribeBaseUrlText.setText(baseUrls.first())
|
2021-11-27 22:18:09 +01:00
|
|
|
} else if (baseUrls.count() > 1) {
|
2022-01-28 06:02:20 +01:00
|
|
|
subscribeBaseUrlLayout.setEndIconDrawable(R.drawable.ic_drop_down_gray_24dp)
|
2021-11-27 22:18:09 +01:00
|
|
|
} else {
|
2022-01-28 06:02:20 +01:00
|
|
|
subscribeBaseUrlLayout.setEndIconDrawable(0)
|
2021-11-25 21:45:12 +01:00
|
|
|
}
|
|
|
|
}
|
2022-01-28 01:57:43 +01:00
|
|
|
|
|
|
|
// Users dropdown
|
|
|
|
users = repository.getUsers()
|
2021-11-25 21:45:12 +01:00
|
|
|
}
|
|
|
|
|
2021-11-24 22:12:51 +01:00
|
|
|
// Show/hide based on flavor
|
2022-01-28 06:02:20 +01:00
|
|
|
subscribeInstantDeliveryBox.visibility = if (BuildConfig.FIREBASE_AVAILABLE) View.VISIBLE else View.GONE
|
2021-11-24 22:12:51 +01:00
|
|
|
|
2022-01-28 16:19:12 +01:00
|
|
|
// Show/hide drop-down and username/password fields
|
2022-01-28 06:02:20 +01:00
|
|
|
loginUsersSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
2022-01-28 01:57:43 +01:00
|
|
|
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
|
|
|
if (position == 0) {
|
2022-01-28 06:02:20 +01:00
|
|
|
loginUsernameText.visibility = View.VISIBLE
|
2022-01-28 17:42:44 +01:00
|
|
|
loginUsernameText.isEnabled = true
|
2022-01-28 06:02:20 +01:00
|
|
|
loginPasswordText.visibility = View.VISIBLE
|
2022-01-28 17:42:44 +01:00
|
|
|
loginPasswordText.isEnabled = true
|
|
|
|
if (loginUsernameText.requestFocus()) {
|
|
|
|
val imm = activity?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
|
|
|
|
imm?.showSoftInput(loginUsernameText, InputMethodManager.SHOW_IMPLICIT)
|
|
|
|
}
|
2022-01-28 01:57:43 +01:00
|
|
|
} else {
|
2022-01-28 06:02:20 +01:00
|
|
|
loginUsernameText.visibility = View.GONE
|
2022-01-28 17:42:44 +01:00
|
|
|
loginUsernameText.isEnabled = false
|
2022-01-28 06:02:20 +01:00
|
|
|
loginPasswordText.visibility = View.GONE
|
2022-01-28 17:42:44 +01:00
|
|
|
loginPasswordText.isEnabled = false
|
2022-01-28 01:57:43 +01:00
|
|
|
}
|
2022-01-28 17:42:44 +01:00
|
|
|
validateInputLoginView()
|
2022-01-28 01:57:43 +01:00
|
|
|
}
|
|
|
|
override fun onNothingSelected(parent: AdapterView<*>?) {
|
|
|
|
// This should not happen, ha!
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-28 17:42:44 +01:00
|
|
|
// Username/password validation on type
|
|
|
|
val textWatcher = object : TextWatcher {
|
|
|
|
override fun afterTextChanged(s: Editable?) {
|
|
|
|
validateInputLoginView()
|
|
|
|
}
|
|
|
|
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
|
|
|
// Nothing
|
|
|
|
}
|
|
|
|
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
|
|
|
|
// Nothing
|
|
|
|
}
|
|
|
|
}
|
|
|
|
loginUsernameText.addTextChangedListener(textWatcher)
|
|
|
|
loginPasswordText.addTextChangedListener(textWatcher)
|
|
|
|
|
2021-11-18 02:16:58 +01:00
|
|
|
// Build dialog
|
2022-01-28 16:19:12 +01:00
|
|
|
val dialog = AlertDialog.Builder(activity)
|
2021-11-18 02:16:58 +01:00
|
|
|
.setView(view)
|
|
|
|
.setPositiveButton(R.string.add_dialog_button_subscribe) { _, _ ->
|
2022-01-28 01:57:43 +01:00
|
|
|
// This will be overridden below to avoid closing the dialog immediately
|
2021-11-18 02:16:58 +01:00
|
|
|
}
|
|
|
|
.setNegativeButton(R.string.add_dialog_button_cancel) { _, _ ->
|
2022-01-28 17:42:44 +01:00
|
|
|
// This will be overridden below
|
2021-11-18 02:16:58 +01:00
|
|
|
}
|
|
|
|
.create()
|
|
|
|
|
2022-01-28 16:19:12 +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);
|
2021-11-18 02:16:58 +01:00
|
|
|
|
2022-01-28 16:19:12 +01:00
|
|
|
// Add logic to disable "Subscribe" button on invalid input
|
|
|
|
dialog.setOnShowListener {
|
2022-01-28 17:42:44 +01:00
|
|
|
positiveButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE)
|
|
|
|
positiveButton.isEnabled = false
|
|
|
|
positiveButton.setOnClickListener {
|
|
|
|
positiveButtonClick()
|
|
|
|
}
|
|
|
|
negativeButton = dialog.getButton(AlertDialog.BUTTON_NEGATIVE)
|
|
|
|
negativeButton.setOnClickListener {
|
|
|
|
negativeButtonClick()
|
2022-01-28 01:57:43 +01:00
|
|
|
}
|
2021-11-18 02:16:58 +01:00
|
|
|
val textWatcher = object : TextWatcher {
|
|
|
|
override fun afterTextChanged(s: Editable?) {
|
2022-01-28 17:42:44 +01:00
|
|
|
validateInputSubscribeView()
|
2021-10-28 04:25:02 +02:00
|
|
|
}
|
2021-11-18 02:16:58 +01:00
|
|
|
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
|
|
|
// Nothing
|
2021-10-28 04:25:02 +02:00
|
|
|
}
|
2021-11-18 02:16:58 +01:00
|
|
|
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
|
|
|
|
// Nothing
|
2021-10-28 04:25:02 +02:00
|
|
|
}
|
2021-11-18 02:16:58 +01:00
|
|
|
}
|
2022-01-28 06:02:20 +01:00
|
|
|
subscribeTopicText.addTextChangedListener(textWatcher)
|
|
|
|
subscribeBaseUrlText.addTextChangedListener(textWatcher)
|
|
|
|
subscribeInstantDeliveryCheckbox.setOnCheckedChangeListener { _, isChecked ->
|
|
|
|
if (isChecked) subscribeInstantDeliveryDescription.visibility = View.VISIBLE
|
|
|
|
else subscribeInstantDeliveryDescription.visibility = View.GONE
|
2021-11-18 02:16:58 +01:00
|
|
|
}
|
2022-01-28 06:02:20 +01:00
|
|
|
subscribeUseAnotherServerCheckbox.setOnCheckedChangeListener { _, isChecked ->
|
2021-11-18 02:16:58 +01:00
|
|
|
if (isChecked) {
|
2022-01-28 06:02:20 +01:00
|
|
|
subscribeUseAnotherServerDescription.visibility = View.VISIBLE
|
|
|
|
subscribeBaseUrlLayout.visibility = View.VISIBLE
|
|
|
|
subscribeInstantDeliveryBox.visibility = View.GONE
|
|
|
|
subscribeInstantDeliveryDescription.visibility = View.GONE
|
2021-11-18 02:16:58 +01:00
|
|
|
} else {
|
2022-01-28 06:02:20 +01:00
|
|
|
subscribeUseAnotherServerDescription.visibility = View.GONE
|
|
|
|
subscribeBaseUrlLayout.visibility = View.GONE
|
|
|
|
subscribeInstantDeliveryBox.visibility = if (BuildConfig.FIREBASE_AVAILABLE) View.VISIBLE else View.GONE
|
|
|
|
if (subscribeInstantDeliveryCheckbox.isChecked) subscribeInstantDeliveryDescription.visibility = View.VISIBLE
|
|
|
|
else subscribeInstantDeliveryDescription.visibility = View.GONE
|
2021-11-14 01:26:37 +01:00
|
|
|
}
|
2022-01-28 17:42:44 +01:00
|
|
|
validateInputSubscribeView()
|
2021-10-28 04:25:02 +02:00
|
|
|
}
|
2022-01-28 16:19:12 +01:00
|
|
|
subscribeUseAnotherServerCheckbox.isChecked = this::baseUrls.isInitialized && baseUrls.count() == 1
|
|
|
|
|
|
|
|
// Focus topic text (keyboard is shown too, see above)
|
|
|
|
subscribeTopicText.requestFocus()
|
2021-11-18 02:16:58 +01:00
|
|
|
}
|
2021-10-28 04:25:02 +02:00
|
|
|
|
2022-01-28 16:19:12 +01:00
|
|
|
return dialog
|
2021-10-28 04:25:02 +02:00
|
|
|
}
|
2021-11-01 13:58:12 +01:00
|
|
|
|
2022-01-28 17:42:44 +01:00
|
|
|
private fun positiveButtonClick() {
|
2022-01-28 06:02:20 +01:00
|
|
|
val topic = subscribeTopicText.text.toString()
|
2022-01-28 01:57:43 +01:00
|
|
|
val baseUrl = getBaseUrl()
|
|
|
|
if (subscribeView.visibility == View.VISIBLE) {
|
|
|
|
checkAnonReadAndMaybeShowLogin(baseUrl, topic)
|
|
|
|
} else if (loginView.visibility == View.VISIBLE) {
|
2022-01-28 17:42:44 +01:00
|
|
|
loginAndMaybeDismiss(baseUrl, topic)
|
2022-01-28 01:57:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun checkAnonReadAndMaybeShowLogin(baseUrl: String, topic: String) {
|
2022-01-28 06:02:20 +01:00
|
|
|
subscribeProgress.visibility = View.VISIBLE
|
|
|
|
subscribeErrorImage.visibility = View.GONE
|
2022-01-28 17:42:44 +01:00
|
|
|
enableSubscribeView(false)
|
2022-01-28 01:57:43 +01:00
|
|
|
lifecycleScope.launch(Dispatchers.IO) {
|
|
|
|
Log.d(TAG, "Checking anonymous read access to topic ${topicUrl(baseUrl, topic)}")
|
2022-01-28 06:02:20 +01:00
|
|
|
try {
|
|
|
|
val authorized = api.checkAnonTopicRead(baseUrl, topic)
|
|
|
|
if (authorized) {
|
|
|
|
Log.d(TAG, "Anonymous access granted to topic ${topicUrl(baseUrl, topic)}")
|
|
|
|
dismiss(authUserId = null)
|
|
|
|
} else {
|
|
|
|
Log.w(TAG, "Anonymous access not allowed to topic ${topicUrl(baseUrl, topic)}, showing login dialog")
|
2022-01-28 17:42:44 +01:00
|
|
|
val activity = activity ?: return@launch // We may have pressed "Cancel"
|
|
|
|
activity.runOnUiThread {
|
|
|
|
showLoginView(activity, baseUrl)
|
2022-01-28 06:02:20 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (e: Exception) {
|
|
|
|
Log.w(TAG, "Connection to topic failed: ${e.message}", e)
|
2022-01-28 17:42:44 +01:00
|
|
|
val activity = activity ?: return@launch // We may have pressed "Cancel"
|
|
|
|
activity.runOnUiThread {
|
2022-01-28 06:02:20 +01:00
|
|
|
subscribeProgress.visibility = View.GONE
|
|
|
|
subscribeErrorImage.visibility = View.VISIBLE
|
2022-01-28 17:42:44 +01:00
|
|
|
enableSubscribeView(true)
|
2022-01-28 06:02:20 +01:00
|
|
|
Toast
|
|
|
|
.makeText(context, getString(R.string.add_dialog_error_connection_failed, e.message), Toast.LENGTH_LONG)
|
|
|
|
.show()
|
2022-01-28 01:57:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-28 17:42:44 +01:00
|
|
|
private fun loginAndMaybeDismiss(baseUrl: String, topic: String) {
|
2022-01-28 04:42:22 +01:00
|
|
|
loginProgress.visibility = View.VISIBLE
|
2022-01-28 06:02:20 +01:00
|
|
|
loginErrorImage.visibility = View.GONE
|
2022-01-28 17:42:44 +01:00
|
|
|
enableLoginView(false)
|
2022-01-28 06:02:20 +01:00
|
|
|
val existingUser = loginUsersSpinner.selectedItem != null && loginUsersSpinner.selectedItem is User && loginUsersSpinner.selectedItemPosition > 0
|
2022-01-28 01:57:43 +01:00
|
|
|
val user = if (existingUser) {
|
2022-01-28 06:02:20 +01:00
|
|
|
loginUsersSpinner.selectedItem as User
|
2022-01-28 01:57:43 +01:00
|
|
|
} else {
|
|
|
|
User(
|
|
|
|
id = Random.nextLong(),
|
2022-01-28 16:19:12 +01:00
|
|
|
baseUrl = baseUrl,
|
2022-01-28 06:02:20 +01:00
|
|
|
username = loginUsernameText.text.toString(),
|
|
|
|
password = loginPasswordText.text.toString()
|
2022-01-28 01:57:43 +01:00
|
|
|
)
|
|
|
|
}
|
|
|
|
lifecycleScope.launch(Dispatchers.IO) {
|
|
|
|
Log.d(TAG, "Checking read access for user ${user.username} to topic ${topicUrl(baseUrl, topic)}")
|
2022-01-28 06:02:20 +01:00
|
|
|
try {
|
|
|
|
val authorized = api.checkUserTopicRead(baseUrl, topic, user.username, user.password)
|
|
|
|
if (authorized) {
|
|
|
|
Log.d(TAG, "Access granted for user ${user.username} to topic ${topicUrl(baseUrl, topic)}")
|
|
|
|
if (!existingUser) {
|
|
|
|
Log.d(TAG, "Adding new user ${user.username} to database")
|
|
|
|
repository.addUser(user)
|
|
|
|
}
|
|
|
|
dismiss(authUserId = user.id)
|
|
|
|
} else {
|
|
|
|
Log.w(TAG, "Access not allowed for user ${user.username} to topic ${topicUrl(baseUrl, topic)}")
|
2022-01-28 17:42:44 +01:00
|
|
|
val activity = activity ?: return@launch // We may have pressed "Cancel"
|
|
|
|
activity.runOnUiThread {
|
2022-01-28 06:02:20 +01:00
|
|
|
loginProgress.visibility = View.GONE
|
|
|
|
loginErrorImage.visibility = View.VISIBLE
|
2022-01-28 17:42:44 +01:00
|
|
|
enableLoginView(true)
|
2022-01-28 06:02:20 +01:00
|
|
|
Toast
|
|
|
|
.makeText(context, getString(R.string.add_dialog_login_error_not_authorized), Toast.LENGTH_LONG)
|
|
|
|
.show()
|
|
|
|
}
|
2022-01-28 01:57:43 +01:00
|
|
|
}
|
2022-01-28 06:02:20 +01:00
|
|
|
} catch (e: Exception) {
|
2022-01-28 17:42:44 +01:00
|
|
|
val activity = activity ?: return@launch // We may have pressed "Cancel"
|
|
|
|
activity.runOnUiThread {
|
2022-01-28 04:42:22 +01:00
|
|
|
loginProgress.visibility = View.GONE
|
2022-01-28 06:02:20 +01:00
|
|
|
loginErrorImage.visibility = View.VISIBLE
|
2022-01-28 17:42:44 +01:00
|
|
|
enableLoginView(true)
|
2022-01-28 06:02:20 +01:00
|
|
|
Toast
|
|
|
|
.makeText(context, getString(R.string.add_dialog_error_connection_failed, e.message), Toast.LENGTH_LONG)
|
|
|
|
.show()
|
2022-01-28 04:42:22 +01:00
|
|
|
}
|
2022-01-28 01:57:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-28 17:42:44 +01:00
|
|
|
private fun negativeButtonClick() {
|
|
|
|
if (subscribeView.visibility == View.VISIBLE) {
|
|
|
|
dialog?.cancel()
|
|
|
|
} else if (loginView.visibility == View.VISIBLE) {
|
|
|
|
showSubscribeView()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun validateInputSubscribeView() {
|
|
|
|
lifecycleScope.launch(Dispatchers.IO) {
|
|
|
|
val baseUrl = getBaseUrl()
|
|
|
|
val topic = subscribeTopicText.text.toString()
|
|
|
|
val subscription = repository.getSubscription(baseUrl, topic)
|
|
|
|
|
|
|
|
activity?.let {
|
|
|
|
it.runOnUiThread {
|
|
|
|
if (subscription != null || DISALLOWED_TOPICS.contains(topic)) {
|
|
|
|
positiveButton.isEnabled = false
|
|
|
|
} else if (subscribeUseAnotherServerCheckbox.isChecked) {
|
|
|
|
positiveButton.isEnabled = topic.isNotBlank()
|
|
|
|
&& "[-_A-Za-z0-9]{1,64}".toRegex().matches(topic)
|
|
|
|
&& baseUrl.isNotBlank()
|
|
|
|
&& "^https?://.+".toRegex().matches(baseUrl)
|
|
|
|
} else {
|
|
|
|
positiveButton.isEnabled = topic.isNotBlank()
|
|
|
|
&& "[-_A-Za-z0-9]{1,64}".toRegex().matches(topic)
|
|
|
|
}
|
2021-11-01 13:58:12 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-28 17:42:44 +01:00
|
|
|
private fun validateInputLoginView() {
|
|
|
|
if (loginUsernameText.visibility == View.GONE) {
|
|
|
|
positiveButton.isEnabled = true
|
|
|
|
} else {
|
|
|
|
positiveButton.isEnabled = (loginUsernameText.text?.isNotEmpty() ?: false)
|
|
|
|
&& (loginPasswordText.text?.isNotEmpty() ?: false)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-28 01:57:43 +01:00
|
|
|
private fun dismiss(authUserId: Long?) {
|
|
|
|
Log.d(TAG, "Closing dialog and calling onSubscribe handler")
|
2022-01-28 17:42:44 +01:00
|
|
|
val activity = activity?: return // We may have pressed "Cancel"
|
|
|
|
activity.runOnUiThread {
|
2022-01-28 06:02:20 +01:00
|
|
|
val topic = subscribeTopicText.text.toString()
|
2022-01-28 01:57:43 +01:00
|
|
|
val baseUrl = getBaseUrl()
|
2022-01-28 06:02:20 +01:00
|
|
|
val instant = if (!BuildConfig.FIREBASE_AVAILABLE || subscribeUseAnotherServerCheckbox.isChecked) {
|
2022-01-28 01:57:43 +01:00
|
|
|
true
|
|
|
|
} else {
|
2022-01-28 06:02:20 +01:00
|
|
|
subscribeInstantDeliveryCheckbox.isChecked
|
2022-01-28 01:57:43 +01:00
|
|
|
}
|
|
|
|
subscribeListener.onSubscribe(topic, baseUrl, instant, authUserId = authUserId)
|
|
|
|
dialog?.dismiss()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-01 13:58:12 +01:00
|
|
|
private fun getBaseUrl(): String {
|
2022-01-28 06:02:20 +01:00
|
|
|
return if (subscribeUseAnotherServerCheckbox.isChecked) {
|
|
|
|
subscribeBaseUrlText.text.toString()
|
2021-11-01 13:58:12 +01:00
|
|
|
} else {
|
|
|
|
getString(R.string.app_base_url)
|
|
|
|
}
|
|
|
|
}
|
2021-11-17 21:30:57 +01:00
|
|
|
|
2022-01-28 17:42:44 +01:00
|
|
|
private fun showSubscribeView() {
|
|
|
|
resetSubscribeView()
|
|
|
|
positiveButton.text = getString(R.string.add_dialog_button_subscribe)
|
|
|
|
negativeButton.text = getString(R.string.add_dialog_button_cancel)
|
|
|
|
loginView.visibility = View.GONE
|
|
|
|
subscribeView.visibility = View.VISIBLE
|
|
|
|
if (subscribeTopicText.requestFocus()) {
|
|
|
|
val imm = activity?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
|
|
|
|
imm?.showSoftInput(subscribeTopicText, InputMethodManager.SHOW_IMPLICIT)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun showLoginView(activity: Activity, baseUrl: String) {
|
|
|
|
resetLoginView()
|
|
|
|
loginProgress.visibility = View.INVISIBLE
|
|
|
|
positiveButton.text = getString(R.string.add_dialog_button_login)
|
|
|
|
negativeButton.text = getString(R.string.add_dialog_button_back)
|
|
|
|
subscribeView.visibility = View.GONE
|
|
|
|
loginView.visibility = View.VISIBLE
|
|
|
|
|
|
|
|
// Show/hide dropdown
|
|
|
|
val relevantUsers = users.filter { it.baseUrl == baseUrl }
|
|
|
|
if (relevantUsers.isEmpty()) {
|
|
|
|
loginUsersSpinner.visibility = View.GONE
|
|
|
|
loginUsersSpinner.adapter = ArrayAdapter(activity, R.layout.fragment_add_dialog_dropdown_item, emptyArray<User>())
|
|
|
|
if (loginUsernameText.requestFocus()) {
|
|
|
|
val imm = activity.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
|
|
|
|
imm?.showSoftInput(loginUsernameText, InputMethodManager.SHOW_IMPLICIT)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
val spinnerEntries = relevantUsers.toMutableList()
|
|
|
|
spinnerEntries.add(0, User(0, "", getString(R.string.add_dialog_login_new_user), ""))
|
|
|
|
loginUsersSpinner.adapter = ArrayAdapter(activity, R.layout.fragment_add_dialog_dropdown_item, spinnerEntries)
|
|
|
|
loginUsersSpinner.setSelection(1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun enableSubscribeView(enable: Boolean) {
|
|
|
|
subscribeTopicText.isEnabled = enable
|
|
|
|
subscribeBaseUrlText.isEnabled = enable
|
|
|
|
subscribeInstantDeliveryCheckbox.isEnabled = enable
|
|
|
|
subscribeUseAnotherServerCheckbox.isEnabled = enable
|
|
|
|
positiveButton.isEnabled = enable
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun resetSubscribeView() {
|
|
|
|
subscribeProgress.visibility = View.GONE
|
|
|
|
subscribeErrorImage.visibility = View.GONE
|
|
|
|
enableSubscribeView(true)
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun enableLoginView(enable: Boolean) {
|
|
|
|
loginUsernameText.isEnabled = enable
|
|
|
|
loginPasswordText.isEnabled = enable
|
|
|
|
loginUsersSpinner.isEnabled = enable
|
|
|
|
positiveButton.isEnabled = enable
|
|
|
|
if (enable && loginUsernameText.requestFocus()) {
|
|
|
|
val imm = activity?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
|
|
|
|
imm?.showSoftInput(loginUsernameText, InputMethodManager.SHOW_IMPLICIT)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun resetLoginView() {
|
|
|
|
loginProgress.visibility = View.GONE
|
|
|
|
loginErrorImage.visibility = View.GONE
|
|
|
|
loginUsersSpinner.visibility = View.VISIBLE
|
|
|
|
loginUsernameText.visibility = View.VISIBLE
|
|
|
|
loginPasswordText.visibility = View.VISIBLE
|
|
|
|
enableLoginView(true)
|
|
|
|
}
|
|
|
|
|
2021-11-17 21:30:57 +01:00
|
|
|
companion object {
|
2021-11-24 22:12:51 +01:00
|
|
|
const val TAG = "NtfyAddFragment"
|
2021-12-13 02:03:53 +01:00
|
|
|
private val DISALLOWED_TOPICS = listOf("docs", "static")
|
2021-11-17 21:30:57 +01:00
|
|
|
}
|
2021-10-28 04:25:02 +02:00
|
|
|
}
|