mirror of
https://github.com/binwiederhier/ntfy-android.git
synced 2024-06-03 03:04:34 +12:00
Continued dialogs
This commit is contained in:
parent
a263246060
commit
e7ecdc266e
|
@ -19,7 +19,6 @@ import io.heckel.ntfy.db.User
|
||||||
import io.heckel.ntfy.log.Log
|
import io.heckel.ntfy.log.Log
|
||||||
import io.heckel.ntfy.msg.ApiService
|
import io.heckel.ntfy.msg.ApiService
|
||||||
import io.heckel.ntfy.util.topicUrl
|
import io.heckel.ntfy.util.topicUrl
|
||||||
import kotlinx.android.synthetic.main.fragment_add_dialog.*
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
@ -34,23 +33,25 @@ class AddFragment : DialogFragment() {
|
||||||
private lateinit var loginView: View
|
private lateinit var loginView: View
|
||||||
|
|
||||||
// Subscribe page
|
// Subscribe page
|
||||||
private lateinit var topicNameText: TextInputEditText
|
private lateinit var subscribeTopicText: TextInputEditText
|
||||||
private lateinit var baseUrlLayout: TextInputLayout
|
private lateinit var subscribeBaseUrlLayout: TextInputLayout
|
||||||
private lateinit var baseUrlText: AutoCompleteTextView
|
private lateinit var subscribeBaseUrlText: AutoCompleteTextView
|
||||||
private lateinit var useAnotherServerCheckbox: CheckBox
|
private lateinit var subscribeUseAnotherServerCheckbox: CheckBox
|
||||||
private lateinit var useAnotherServerDescription: TextView
|
private lateinit var subscribeUseAnotherServerDescription: TextView
|
||||||
private lateinit var instantDeliveryBox: View
|
private lateinit var subscribeInstantDeliveryBox: View
|
||||||
private lateinit var instantDeliveryCheckbox: CheckBox
|
private lateinit var subscribeInstantDeliveryCheckbox: CheckBox
|
||||||
private lateinit var instantDeliveryDescription: View
|
private lateinit var subscribeInstantDeliveryDescription: View
|
||||||
|
private lateinit var subscribeProgress: ProgressBar
|
||||||
|
private lateinit var subscribeErrorImage: View
|
||||||
private lateinit var subscribeButton: Button
|
private lateinit var subscribeButton: Button
|
||||||
|
|
||||||
// Login page
|
// Login page
|
||||||
private lateinit var users: List<User>
|
private lateinit var users: List<User>
|
||||||
private lateinit var usersSpinner: Spinner
|
private lateinit var loginUsersSpinner: Spinner
|
||||||
private lateinit var usernameText: TextInputEditText
|
private lateinit var loginUsernameText: TextInputEditText
|
||||||
private lateinit var passwordText: TextInputEditText
|
private lateinit var loginPasswordText: TextInputEditText
|
||||||
private lateinit var loginProgress: ProgressBar
|
private lateinit var loginProgress: ProgressBar
|
||||||
private lateinit var loginError: TextView
|
private lateinit var loginErrorImage: View
|
||||||
|
|
||||||
private lateinit var baseUrls: List<String> // List of base URLs already used, excluding app_base_url
|
private lateinit var baseUrls: List<String> // List of base URLs already used, excluding app_base_url
|
||||||
|
|
||||||
|
@ -80,24 +81,26 @@ class AddFragment : DialogFragment() {
|
||||||
loginView.visibility = View.GONE
|
loginView.visibility = View.GONE
|
||||||
|
|
||||||
// Fields for "subscribe page"
|
// Fields for "subscribe page"
|
||||||
topicNameText = view.findViewById(R.id.add_dialog_topic_text)
|
subscribeTopicText = view.findViewById(R.id.add_dialog_topic_text)
|
||||||
baseUrlLayout = view.findViewById(R.id.add_dialog_base_url_layout)
|
subscribeBaseUrlLayout = view.findViewById(R.id.add_dialog_base_url_layout)
|
||||||
baseUrlText = view.findViewById(R.id.add_dialog_base_url_text)
|
subscribeBaseUrlText = view.findViewById(R.id.add_dialog_base_url_text)
|
||||||
instantDeliveryBox = view.findViewById(R.id.add_dialog_instant_delivery_box)
|
subscribeInstantDeliveryBox = view.findViewById(R.id.add_dialog_instant_delivery_box)
|
||||||
instantDeliveryCheckbox = view.findViewById(R.id.add_dialog_instant_delivery_checkbox)
|
subscribeInstantDeliveryCheckbox = view.findViewById(R.id.add_dialog_instant_delivery_checkbox)
|
||||||
instantDeliveryDescription = view.findViewById(R.id.add_dialog_instant_delivery_description)
|
subscribeInstantDeliveryDescription = view.findViewById(R.id.add_dialog_instant_delivery_description)
|
||||||
useAnotherServerCheckbox = view.findViewById(R.id.add_dialog_use_another_server_checkbox)
|
subscribeUseAnotherServerCheckbox = view.findViewById(R.id.add_dialog_use_another_server_checkbox)
|
||||||
useAnotherServerDescription = view.findViewById(R.id.add_dialog_use_another_server_description)
|
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)
|
||||||
|
|
||||||
// Fields for "login page"
|
// Fields for "login page"
|
||||||
usersSpinner = view.findViewById(R.id.add_dialog_login_users_spinner)
|
loginUsersSpinner = view.findViewById(R.id.add_dialog_login_users_spinner)
|
||||||
usernameText = view.findViewById(R.id.add_dialog_login_username)
|
loginUsernameText = view.findViewById(R.id.add_dialog_login_username)
|
||||||
passwordText = view.findViewById(R.id.add_dialog_login_password)
|
loginPasswordText = view.findViewById(R.id.add_dialog_login_password)
|
||||||
loginProgress = view.findViewById(R.id.add_dialog_login_progress)
|
loginProgress = view.findViewById(R.id.add_dialog_login_progress)
|
||||||
loginError = view.findViewById(R.id.add_dialog_login_error)
|
loginErrorImage = view.findViewById(R.id.add_dialog_login_error_image)
|
||||||
|
|
||||||
// Set "Use another server" description based on flavor
|
// Set "Use another server" description based on flavor
|
||||||
useAnotherServerDescription.text = if (BuildConfig.FIREBASE_AVAILABLE) {
|
subscribeUseAnotherServerDescription.text = if (BuildConfig.FIREBASE_AVAILABLE) {
|
||||||
getString(R.string.add_dialog_use_another_server_description)
|
getString(R.string.add_dialog_use_another_server_description)
|
||||||
} else {
|
} else {
|
||||||
getString(R.string.add_dialog_use_another_server_description_noinstant)
|
getString(R.string.add_dialog_use_another_server_description_noinstant)
|
||||||
|
@ -105,29 +108,29 @@ class AddFragment : DialogFragment() {
|
||||||
|
|
||||||
// Base URL dropdown behavior; Oh my, why is this so complicated?!
|
// Base URL dropdown behavior; Oh my, why is this so complicated?!
|
||||||
val toggleEndIcon = {
|
val toggleEndIcon = {
|
||||||
if (baseUrlText.text.isNotEmpty()) {
|
if (subscribeBaseUrlText.text.isNotEmpty()) {
|
||||||
baseUrlLayout.setEndIconDrawable(R.drawable.ic_cancel_gray_24dp)
|
subscribeBaseUrlLayout.setEndIconDrawable(R.drawable.ic_cancel_gray_24dp)
|
||||||
} else if (baseUrls.isEmpty()) {
|
} else if (baseUrls.isEmpty()) {
|
||||||
baseUrlLayout.setEndIconDrawable(0)
|
subscribeBaseUrlLayout.setEndIconDrawable(0)
|
||||||
} else {
|
} else {
|
||||||
baseUrlLayout.setEndIconDrawable(R.drawable.ic_drop_down_gray_24dp)
|
subscribeBaseUrlLayout.setEndIconDrawable(R.drawable.ic_drop_down_gray_24dp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
baseUrlLayout.setEndIconOnClickListener {
|
subscribeBaseUrlLayout.setEndIconOnClickListener {
|
||||||
if (baseUrlText.text.isNotEmpty()) {
|
if (subscribeBaseUrlText.text.isNotEmpty()) {
|
||||||
baseUrlText.text.clear()
|
subscribeBaseUrlText.text.clear()
|
||||||
if (baseUrls.isEmpty()) {
|
if (baseUrls.isEmpty()) {
|
||||||
baseUrlLayout.setEndIconDrawable(0)
|
subscribeBaseUrlLayout.setEndIconDrawable(0)
|
||||||
} else {
|
} else {
|
||||||
baseUrlLayout.setEndIconDrawable(R.drawable.ic_drop_down_gray_24dp)
|
subscribeBaseUrlLayout.setEndIconDrawable(R.drawable.ic_drop_down_gray_24dp)
|
||||||
}
|
}
|
||||||
} else if (baseUrlText.text.isEmpty() && baseUrls.isNotEmpty()) {
|
} else if (subscribeBaseUrlText.text.isEmpty() && baseUrls.isNotEmpty()) {
|
||||||
baseUrlLayout.setEndIconDrawable(R.drawable.ic_drop_up_gray_24dp)
|
subscribeBaseUrlLayout.setEndIconDrawable(R.drawable.ic_drop_up_gray_24dp)
|
||||||
baseUrlText.showDropDown()
|
subscribeBaseUrlText.showDropDown()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
baseUrlText.setOnDismissListener { toggleEndIcon() }
|
subscribeBaseUrlText.setOnDismissListener { toggleEndIcon() }
|
||||||
baseUrlText.addTextChangedListener(object : TextWatcher {
|
subscribeBaseUrlText.addTextChangedListener(object : TextWatcher {
|
||||||
override fun afterTextChanged(s: Editable?) {
|
override fun afterTextChanged(s: Editable?) {
|
||||||
toggleEndIcon()
|
toggleEndIcon()
|
||||||
}
|
}
|
||||||
|
@ -150,41 +153,34 @@ class AddFragment : DialogFragment() {
|
||||||
.sorted()
|
.sorted()
|
||||||
val adapter = ArrayAdapter(requireActivity(), R.layout.fragment_add_dialog_dropdown_item, baseUrls)
|
val adapter = ArrayAdapter(requireActivity(), R.layout.fragment_add_dialog_dropdown_item, baseUrls)
|
||||||
requireActivity().runOnUiThread {
|
requireActivity().runOnUiThread {
|
||||||
baseUrlText.threshold = 1
|
subscribeBaseUrlText.threshold = 1
|
||||||
baseUrlText.setAdapter(adapter)
|
subscribeBaseUrlText.setAdapter(adapter)
|
||||||
if (baseUrls.count() == 1) {
|
if (baseUrls.count() == 1) {
|
||||||
baseUrlLayout.setEndIconDrawable(R.drawable.ic_cancel_gray_24dp)
|
subscribeBaseUrlLayout.setEndIconDrawable(R.drawable.ic_cancel_gray_24dp)
|
||||||
baseUrlText.setText(baseUrls.first())
|
subscribeBaseUrlText.setText(baseUrls.first())
|
||||||
} else if (baseUrls.count() > 1) {
|
} else if (baseUrls.count() > 1) {
|
||||||
baseUrlLayout.setEndIconDrawable(R.drawable.ic_drop_down_gray_24dp)
|
subscribeBaseUrlLayout.setEndIconDrawable(R.drawable.ic_drop_down_gray_24dp)
|
||||||
} else {
|
} else {
|
||||||
baseUrlLayout.setEndIconDrawable(0)
|
subscribeBaseUrlLayout.setEndIconDrawable(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Users dropdown
|
// Users dropdown
|
||||||
users = repository.getUsers()
|
users = repository.getUsers()
|
||||||
if (users.isEmpty()) {
|
|
||||||
usersSpinner.visibility = View.GONE
|
|
||||||
} else {
|
|
||||||
val spinnerEntries = users.toMutableList()
|
|
||||||
spinnerEntries.add(0, User(0, "Create new", "")) // FIXME
|
|
||||||
usersSpinner.adapter = ArrayAdapter(requireActivity(), R.layout.fragment_add_dialog_dropdown_item, spinnerEntries)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show/hide based on flavor
|
// Show/hide based on flavor
|
||||||
instantDeliveryBox.visibility = if (BuildConfig.FIREBASE_AVAILABLE) View.VISIBLE else View.GONE
|
subscribeInstantDeliveryBox.visibility = if (BuildConfig.FIREBASE_AVAILABLE) View.VISIBLE else View.GONE
|
||||||
|
|
||||||
// Show/hide spinner and username/password fields
|
// Show/hide spinner and username/password fields
|
||||||
usersSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
loginUsersSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||||
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
||||||
if (position == 0) {
|
if (position == 0) {
|
||||||
usernameText.visibility = View.VISIBLE
|
loginUsernameText.visibility = View.VISIBLE
|
||||||
passwordText.visibility = View.VISIBLE
|
loginPasswordText.visibility = View.VISIBLE
|
||||||
} else {
|
} else {
|
||||||
usernameText.visibility = View.GONE
|
loginUsernameText.visibility = View.GONE
|
||||||
passwordText.visibility = View.GONE
|
loginPasswordText.visibility = View.GONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
override fun onNothingSelected(parent: AdapterView<*>?) {
|
override fun onNothingSelected(parent: AdapterView<*>?) {
|
||||||
|
@ -224,24 +220,24 @@ class AddFragment : DialogFragment() {
|
||||||
// Nothing
|
// Nothing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
topicNameText.addTextChangedListener(textWatcher)
|
subscribeTopicText.addTextChangedListener(textWatcher)
|
||||||
baseUrlText.addTextChangedListener(textWatcher)
|
subscribeBaseUrlText.addTextChangedListener(textWatcher)
|
||||||
instantDeliveryCheckbox.setOnCheckedChangeListener { _, isChecked ->
|
subscribeInstantDeliveryCheckbox.setOnCheckedChangeListener { _, isChecked ->
|
||||||
if (isChecked) instantDeliveryDescription.visibility = View.VISIBLE
|
if (isChecked) subscribeInstantDeliveryDescription.visibility = View.VISIBLE
|
||||||
else instantDeliveryDescription.visibility = View.GONE
|
else subscribeInstantDeliveryDescription.visibility = View.GONE
|
||||||
}
|
}
|
||||||
useAnotherServerCheckbox.setOnCheckedChangeListener { _, isChecked ->
|
subscribeUseAnotherServerCheckbox.setOnCheckedChangeListener { _, isChecked ->
|
||||||
if (isChecked) {
|
if (isChecked) {
|
||||||
useAnotherServerDescription.visibility = View.VISIBLE
|
subscribeUseAnotherServerDescription.visibility = View.VISIBLE
|
||||||
baseUrlLayout.visibility = View.VISIBLE
|
subscribeBaseUrlLayout.visibility = View.VISIBLE
|
||||||
instantDeliveryBox.visibility = View.GONE
|
subscribeInstantDeliveryBox.visibility = View.GONE
|
||||||
instantDeliveryDescription.visibility = View.GONE
|
subscribeInstantDeliveryDescription.visibility = View.GONE
|
||||||
} else {
|
} else {
|
||||||
useAnotherServerDescription.visibility = View.GONE
|
subscribeUseAnotherServerDescription.visibility = View.GONE
|
||||||
baseUrlLayout.visibility = View.GONE
|
subscribeBaseUrlLayout.visibility = View.GONE
|
||||||
instantDeliveryBox.visibility = if (BuildConfig.FIREBASE_AVAILABLE) View.VISIBLE else View.GONE
|
subscribeInstantDeliveryBox.visibility = if (BuildConfig.FIREBASE_AVAILABLE) View.VISIBLE else View.GONE
|
||||||
if (instantDeliveryCheckbox.isChecked) instantDeliveryDescription.visibility = View.VISIBLE
|
if (subscribeInstantDeliveryCheckbox.isChecked) subscribeInstantDeliveryDescription.visibility = View.VISIBLE
|
||||||
else instantDeliveryDescription.visibility = View.GONE
|
else subscribeInstantDeliveryDescription.visibility = View.GONE
|
||||||
}
|
}
|
||||||
validateInput()
|
validateInput()
|
||||||
}
|
}
|
||||||
|
@ -251,7 +247,7 @@ class AddFragment : DialogFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun subscribeButtonClick() {
|
private fun subscribeButtonClick() {
|
||||||
val topic = topicNameText.text.toString()
|
val topic = subscribeTopicText.text.toString()
|
||||||
val baseUrl = getBaseUrl()
|
val baseUrl = getBaseUrl()
|
||||||
if (subscribeView.visibility == View.VISIBLE) {
|
if (subscribeView.visibility == View.VISIBLE) {
|
||||||
checkAnonReadAndMaybeShowLogin(baseUrl, topic)
|
checkAnonReadAndMaybeShowLogin(baseUrl, topic)
|
||||||
|
@ -261,19 +257,44 @@ class AddFragment : DialogFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkAnonReadAndMaybeShowLogin(baseUrl: String, topic: String) {
|
private fun checkAnonReadAndMaybeShowLogin(baseUrl: String, topic: String) {
|
||||||
|
subscribeProgress.visibility = View.VISIBLE
|
||||||
|
subscribeErrorImage.visibility = View.GONE
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
Log.d(TAG, "Checking anonymous read access to topic ${topicUrl(baseUrl, topic)}")
|
Log.d(TAG, "Checking anonymous read access to topic ${topicUrl(baseUrl, topic)}")
|
||||||
val authorized = api.checkAnonTopicRead(baseUrl, topic)
|
try {
|
||||||
if (authorized) {
|
val authorized = api.checkAnonTopicRead(baseUrl, topic)
|
||||||
Log.d(TAG, "Anonymous access granted to topic ${topicUrl(baseUrl, topic)}")
|
if (authorized) {
|
||||||
dismiss(authUserId = null)
|
Log.d(TAG, "Anonymous access granted to topic ${topicUrl(baseUrl, topic)}")
|
||||||
} else {
|
dismiss(authUserId = null)
|
||||||
Log.w(TAG, "Anonymous access not allowed to topic ${topicUrl(baseUrl, topic)}, showing login dialog")
|
} else {
|
||||||
|
Log.w(TAG, "Anonymous access not allowed to topic ${topicUrl(baseUrl, topic)}, showing login dialog")
|
||||||
|
requireActivity().runOnUiThread {
|
||||||
|
// Show/hide users dropdown
|
||||||
|
if (users.isEmpty()) {
|
||||||
|
loginUsersSpinner.visibility = View.GONE
|
||||||
|
} else {
|
||||||
|
val spinnerEntries = users.toMutableList()
|
||||||
|
spinnerEntries.add(0, User(0, getString(R.string.add_dialog_login_new_user), ""))
|
||||||
|
loginUsersSpinner.adapter = ArrayAdapter(requireActivity(), R.layout.fragment_add_dialog_dropdown_item, spinnerEntries)
|
||||||
|
loginUsersSpinner.setSelection(1)
|
||||||
|
/*loginUsernameText.visibility = View.GONE
|
||||||
|
loginPasswordText.visibility = View.GONE*/
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show login page
|
||||||
|
subscribeView.visibility = View.GONE
|
||||||
|
loginProgress.visibility = View.INVISIBLE
|
||||||
|
loginView.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.w(TAG, "Connection to topic failed: ${e.message}", e)
|
||||||
requireActivity().runOnUiThread {
|
requireActivity().runOnUiThread {
|
||||||
subscribeView.visibility = View.GONE
|
subscribeProgress.visibility = View.GONE
|
||||||
loginError.visibility = View.INVISIBLE
|
subscribeErrorImage.visibility = View.VISIBLE
|
||||||
loginProgress.visibility = View.INVISIBLE
|
Toast
|
||||||
loginView.visibility = View.VISIBLE
|
.makeText(context, getString(R.string.add_dialog_error_connection_failed, e.message), Toast.LENGTH_LONG)
|
||||||
|
.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -281,32 +302,45 @@ class AddFragment : DialogFragment() {
|
||||||
|
|
||||||
private fun checkAuthAndMaybeDismiss(baseUrl: String, topic: String) {
|
private fun checkAuthAndMaybeDismiss(baseUrl: String, topic: String) {
|
||||||
loginProgress.visibility = View.VISIBLE
|
loginProgress.visibility = View.VISIBLE
|
||||||
loginError.visibility = View.INVISIBLE
|
loginErrorImage.visibility = View.GONE
|
||||||
val existingUser = usersSpinner.selectedItem != null && usersSpinner.selectedItem is User && usersSpinner.selectedItemPosition > 0
|
val existingUser = loginUsersSpinner.selectedItem != null && loginUsersSpinner.selectedItem is User && loginUsersSpinner.selectedItemPosition > 0
|
||||||
val user = if (existingUser) {
|
val user = if (existingUser) {
|
||||||
usersSpinner.selectedItem as User
|
loginUsersSpinner.selectedItem as User
|
||||||
} else {
|
} else {
|
||||||
User(
|
User(
|
||||||
id = Random.nextLong(),
|
id = Random.nextLong(),
|
||||||
username = usernameText.text.toString(),
|
username = loginUsernameText.text.toString(),
|
||||||
password = passwordText.text.toString()
|
password = loginPasswordText.text.toString()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
Log.d(TAG, "Checking read access for user ${user.username} to topic ${topicUrl(baseUrl, topic)}")
|
Log.d(TAG, "Checking read access for user ${user.username} to topic ${topicUrl(baseUrl, topic)}")
|
||||||
val authorized = api.checkUserTopicRead(baseUrl, topic, user.username, user.password)
|
try {
|
||||||
if (authorized) {
|
val authorized = api.checkUserTopicRead(baseUrl, topic, user.username, user.password)
|
||||||
Log.d(TAG, "Access granted for user ${user.username} to topic ${topicUrl(baseUrl, topic)}")
|
if (authorized) {
|
||||||
if (!existingUser) {
|
Log.d(TAG, "Access granted for user ${user.username} to topic ${topicUrl(baseUrl, topic)}")
|
||||||
Log.d(TAG, "Adding new user ${user.username} to database")
|
if (!existingUser) {
|
||||||
repository.addUser(user)
|
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)}")
|
||||||
|
requireActivity().runOnUiThread {
|
||||||
|
loginProgress.visibility = View.GONE
|
||||||
|
loginErrorImage.visibility = View.VISIBLE
|
||||||
|
Toast
|
||||||
|
.makeText(context, getString(R.string.add_dialog_login_error_not_authorized), Toast.LENGTH_LONG)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
dismiss(authUserId = user.id)
|
} catch (e: Exception) {
|
||||||
} else {
|
|
||||||
Log.w(TAG, "Access not allowed for user ${user.username} to topic ${topicUrl(baseUrl, topic)}")
|
|
||||||
requireActivity().runOnUiThread {
|
requireActivity().runOnUiThread {
|
||||||
loginProgress.visibility = View.GONE
|
loginProgress.visibility = View.GONE
|
||||||
loginError.visibility = View.VISIBLE
|
loginErrorImage.visibility = View.VISIBLE
|
||||||
|
Toast
|
||||||
|
.makeText(context, getString(R.string.add_dialog_error_connection_failed, e.message), Toast.LENGTH_LONG)
|
||||||
|
.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -314,14 +348,14 @@ class AddFragment : DialogFragment() {
|
||||||
|
|
||||||
private fun validateInput() = lifecycleScope.launch(Dispatchers.IO) {
|
private fun validateInput() = lifecycleScope.launch(Dispatchers.IO) {
|
||||||
val baseUrl = getBaseUrl()
|
val baseUrl = getBaseUrl()
|
||||||
val topic = topicNameText.text.toString()
|
val topic = subscribeTopicText.text.toString()
|
||||||
val subscription = repository.getSubscription(baseUrl, topic)
|
val subscription = repository.getSubscription(baseUrl, topic)
|
||||||
|
|
||||||
activity?.let {
|
activity?.let {
|
||||||
it.runOnUiThread {
|
it.runOnUiThread {
|
||||||
if (subscription != null || DISALLOWED_TOPICS.contains(topic)) {
|
if (subscription != null || DISALLOWED_TOPICS.contains(topic)) {
|
||||||
subscribeButton.isEnabled = false
|
subscribeButton.isEnabled = false
|
||||||
} else if (useAnotherServerCheckbox.isChecked) {
|
} else if (subscribeUseAnotherServerCheckbox.isChecked) {
|
||||||
subscribeButton.isEnabled = topic.isNotBlank()
|
subscribeButton.isEnabled = topic.isNotBlank()
|
||||||
&& "[-_A-Za-z0-9]{1,64}".toRegex().matches(topic)
|
&& "[-_A-Za-z0-9]{1,64}".toRegex().matches(topic)
|
||||||
&& baseUrl.isNotBlank()
|
&& baseUrl.isNotBlank()
|
||||||
|
@ -337,12 +371,12 @@ class AddFragment : DialogFragment() {
|
||||||
private fun dismiss(authUserId: Long?) {
|
private fun dismiss(authUserId: Long?) {
|
||||||
Log.d(TAG, "Closing dialog and calling onSubscribe handler")
|
Log.d(TAG, "Closing dialog and calling onSubscribe handler")
|
||||||
requireActivity().runOnUiThread {
|
requireActivity().runOnUiThread {
|
||||||
val topic = topicNameText.text.toString()
|
val topic = subscribeTopicText.text.toString()
|
||||||
val baseUrl = getBaseUrl()
|
val baseUrl = getBaseUrl()
|
||||||
val instant = if (!BuildConfig.FIREBASE_AVAILABLE || useAnotherServerCheckbox.isChecked) {
|
val instant = if (!BuildConfig.FIREBASE_AVAILABLE || subscribeUseAnotherServerCheckbox.isChecked) {
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
instantDeliveryCheckbox.isChecked
|
subscribeInstantDeliveryCheckbox.isChecked
|
||||||
}
|
}
|
||||||
subscribeListener.onSubscribe(topic, baseUrl, instant, authUserId = authUserId)
|
subscribeListener.onSubscribe(topic, baseUrl, instant, authUserId = authUserId)
|
||||||
dialog?.dismiss()
|
dialog?.dismiss()
|
||||||
|
@ -350,8 +384,8 @@ class AddFragment : DialogFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getBaseUrl(): String {
|
private fun getBaseUrl(): String {
|
||||||
return if (useAnotherServerCheckbox.isChecked) {
|
return if (subscribeUseAnotherServerCheckbox.isChecked) {
|
||||||
baseUrlText.text.toString()
|
subscribeBaseUrlText.text.toString()
|
||||||
} else {
|
} else {
|
||||||
getString(R.string.app_base_url)
|
getString(R.string.app_base_url)
|
||||||
}
|
}
|
||||||
|
|
9
app/src/main/res/drawable/ic_error_black_24dp.xml
Normal file
9
app/src/main/res/drawable/ic_error_black_24dp.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-2h2v2zM13,13h-2L11,7h2v6z"
|
||||||
|
android:fillColor="#F44336"/>
|
||||||
|
</vector>
|
|
@ -1,48 +1,71 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout 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"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:paddingLeft="16dp"
|
android:paddingLeft="16dp"
|
||||||
android:paddingRight="16dp">
|
android:paddingRight="16dp">
|
||||||
|
|
||||||
<LinearLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent" android:id="@+id/add_dialog_subscribe_view" tools:visibility="gone">
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:id="@+id/add_dialog_subscribe_view"
|
||||||
|
android:visibility="visible">
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/add_dialog_title_text"
|
android:id="@+id/add_dialog_title_text"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingTop="16dp"
|
android:paddingTop="16dp"
|
||||||
android:paddingBottom="3dp"
|
android:paddingBottom="3dp"
|
||||||
android:text="@string/add_dialog_title"
|
android:text="@string/add_dialog_title"
|
||||||
android:textAlignment="viewStart"
|
android:textAlignment="viewStart"
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Large" android:paddingStart="4dp"/>
|
android:textAppearance="@style/TextAppearance.AppCompat.Large" android:paddingStart="4dp"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/add_dialog_error_image"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:text="@string/add_dialog_description_below"
|
android:text="@string/add_dialog_description_below"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content" android:id="@+id/add_dialog_description_below"
|
android:layout_height="wrap_content" android:id="@+id/add_dialog_description_below"
|
||||||
android:paddingStart="4dp" android:paddingTop="3dp"/>
|
android:paddingStart="4dp" android:paddingTop="3dp" app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/add_dialog_title_text"/>
|
||||||
<com.google.android.material.textfield.TextInputEditText
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
android:id="@+id/add_dialog_topic_text"
|
android:id="@+id/add_dialog_topic_text"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content" android:hint="@string/add_dialog_topic_name_hint"
|
android:layout_height="wrap_content" android:hint="@string/add_dialog_topic_name_hint"
|
||||||
android:importantForAutofill="no"
|
android:importantForAutofill="no"
|
||||||
android:maxLines="1" android:inputType="text" android:maxLength="64"/>
|
android:maxLines="1" android:inputType="text" android:maxLength="64"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/add_dialog_description_below"/>
|
||||||
<CheckBox
|
<CheckBox
|
||||||
android:text="@string/add_dialog_use_another_server"
|
android:text="@string/add_dialog_use_another_server"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content" android:id="@+id/add_dialog_use_another_server_checkbox"
|
android:layout_height="wrap_content" android:id="@+id/add_dialog_use_another_server_checkbox"
|
||||||
android:layout_marginTop="-5dp" android:layout_marginBottom="-5dp" android:layout_marginStart="-3dp"/>
|
android:layout_marginStart="-3dp" app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/add_dialog_topic_text"
|
||||||
|
android:layout_marginTop="-3dp"/>
|
||||||
|
<ProgressBar
|
||||||
|
style="?android:attr/progressBarStyle"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:id="@+id/add_dialog_progress"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/add_dialog_error_image"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/add_dialog_description_below"
|
||||||
|
android:indeterminate="true" android:layout_marginBottom="5dp" android:visibility="gone"/>
|
||||||
<TextView
|
<TextView
|
||||||
android:text="@string/add_dialog_use_another_server_description"
|
android:text="@string/add_dialog_use_another_server_description"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content" android:id="@+id/add_dialog_use_another_server_description"
|
android:layout_height="wrap_content" android:id="@+id/add_dialog_use_another_server_description"
|
||||||
android:paddingStart="4dp" android:paddingTop="0dp" android:layout_marginTop="-5dp"
|
android:paddingStart="4dp" android:paddingTop="0dp"
|
||||||
android:visibility="gone"/>
|
android:visibility="gone" app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/add_dialog_use_another_server_checkbox"/>
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox.Dense.ExposedDropdownMenu"
|
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox.Dense.ExposedDropdownMenu"
|
||||||
android:id="@+id/add_dialog_base_url_layout"
|
android:id="@+id/add_dialog_base_url_layout"
|
||||||
|
@ -54,7 +77,9 @@
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
app:endIconMode="custom"
|
app:endIconMode="custom"
|
||||||
app:hintEnabled="false"
|
app:hintEnabled="false"
|
||||||
app:boxBackgroundColor="@android:color/transparent">
|
app:boxBackgroundColor="@android:color/transparent" app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/add_dialog_use_another_server_description">
|
||||||
<AutoCompleteTextView
|
<AutoCompleteTextView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -76,7 +101,9 @@
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content" android:id="@+id/add_dialog_instant_delivery_box">
|
android:layout_height="wrap_content" android:id="@+id/add_dialog_instant_delivery_box"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/add_dialog_base_url_layout" android:layout_marginTop="-3dp">
|
||||||
<CheckBox
|
<CheckBox
|
||||||
android:text="@string/add_dialog_instant_delivery"
|
android:text="@string/add_dialog_instant_delivery"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
@ -95,14 +122,24 @@
|
||||||
android:text="@string/add_dialog_instant_delivery_description"
|
android:text="@string/add_dialog_instant_delivery_description"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content" android:id="@+id/add_dialog_instant_delivery_description"
|
android:layout_height="wrap_content" android:id="@+id/add_dialog_instant_delivery_description"
|
||||||
android:paddingStart="4dp" android:paddingTop="0dp" android:layout_marginTop="-5dp"
|
android:paddingStart="4dp" android:paddingTop="0dp"
|
||||||
android:visibility="gone"/>
|
android:visibility="gone" app:layout_constraintStart_toStartOf="parent"
|
||||||
</LinearLayout>
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/add_dialog_instant_delivery_box"/>
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp" app:srcCompat="@drawable/ic_error_black_24dp"
|
||||||
|
android:id="@+id/add_dialog_error_image"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/add_dialog_description_below"
|
||||||
|
android:layout_marginBottom="5dp" app:layout_constraintEnd_toStartOf="@+id/add_dialog_progress"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/add_dialog_title_text" android:visibility="gone"/>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:id="@+id/add_dialog_login_view"
|
android:id="@+id/add_dialog_login_view"
|
||||||
|
android:visibility="gone"
|
||||||
>
|
>
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/add_dialog_login_title"
|
android:id="@+id/add_dialog_login_title"
|
||||||
|
@ -115,7 +152,7 @@
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Large" android:paddingStart="4dp"
|
android:textAppearance="@style/TextAppearance.AppCompat.Large" android:paddingStart="4dp"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintEnd_toStartOf="@id/add_dialog_login_progress"/>
|
app:layout_constraintEnd_toStartOf="@id/add_dialog_login_error_image"/>
|
||||||
<TextView
|
<TextView
|
||||||
android:text="This topic requires you to login. Please pick an existing user or type in a username and password."
|
android:text="This topic requires you to login. Please pick an existing user or type in a username and password."
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -144,24 +181,22 @@
|
||||||
android:maxLines="1" android:inputType="textPassword" app:layout_constraintStart_toStartOf="parent"
|
android:maxLines="1" android:inputType="textPassword" app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/add_dialog_login_username"/>
|
app:layout_constraintTop_toBottomOf="@id/add_dialog_login_username"/>
|
||||||
<TextView
|
|
||||||
android:text="Login failed"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content" android:id="@+id/add_dialog_login_error"
|
|
||||||
android:paddingStart="4dp" android:paddingTop="3dp"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/add_dialog_login_password"
|
|
||||||
app:layout_constraintStart_toStartOf="parent" android:visibility="invisible"/>
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
style="?android:attr/progressBarStyle"
|
style="?android:attr/progressBarStyle"
|
||||||
android:layout_width="25dp"
|
android:layout_width="25dp"
|
||||||
android:layout_height="25dp"
|
android:layout_height="25dp"
|
||||||
android:id="@+id/add_dialog_login_progress"
|
android:id="@+id/add_dialog_login_progress"
|
||||||
app:layout_constraintTop_toTopOf="parent" app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toEndOf="@id/add_dialog_login_title"
|
app:layout_constraintStart_toEndOf="@id/add_dialog_login_error_image"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/add_dialog_login_description" android:layout_marginTop="5dp"
|
app:layout_constraintBottom_toTopOf="@+id/add_dialog_login_description"
|
||||||
android:indeterminate="true"/>
|
android:indeterminate="true" android:layout_marginBottom="5dp"/>
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp" app:srcCompat="@drawable/ic_error_black_24dp"
|
||||||
|
android:id="@+id/add_dialog_login_error_image"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/add_dialog_login_description"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/add_dialog_login_progress" android:layout_marginBottom="5dp"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/add_dialog_login_title"/>
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
|
@ -78,14 +78,13 @@
|
||||||
<!-- Add dialog -->
|
<!-- Add dialog -->
|
||||||
<string name="add_dialog_title">Subscribe to topic</string>
|
<string name="add_dialog_title">Subscribe to topic</string>
|
||||||
<string name="add_dialog_description_below">
|
<string name="add_dialog_description_below">
|
||||||
Topics are not password-protected, so choose a name that\'s not easy to
|
Topics may not be password-protected, so choose a name that\'s not easy to
|
||||||
guess. Once subscribed, you can PUT/POST to receive notifications on your phone.
|
guess. Once subscribed, you can PUT/POST to receive notifications.
|
||||||
</string>
|
</string>
|
||||||
<string name="add_dialog_topic_name_hint">Topic name, e.g. phils_alerts</string>
|
<string name="add_dialog_topic_name_hint">Topic name, e.g. phils_alerts</string>
|
||||||
<string name="add_dialog_use_another_server">Use another server</string>
|
<string name="add_dialog_use_another_server">Use another server</string>
|
||||||
<string name="add_dialog_use_another_server_description">
|
<string name="add_dialog_use_another_server_description">
|
||||||
You can subscribe to topics from your own server. This option requires a foreground service and
|
You can subscribe to topics from your own server. This option requires a foreground service.
|
||||||
consumes more power, but also delivers notifications faster.
|
|
||||||
</string>
|
</string>
|
||||||
<string name="add_dialog_use_another_server_description_noinstant">
|
<string name="add_dialog_use_another_server_description_noinstant">
|
||||||
You can subscribe to topics from your own server. Simply type in the base
|
You can subscribe to topics from your own server. Simply type in the base
|
||||||
|
@ -98,6 +97,9 @@
|
||||||
</string>
|
</string>
|
||||||
<string name="add_dialog_button_cancel">Cancel</string>
|
<string name="add_dialog_button_cancel">Cancel</string>
|
||||||
<string name="add_dialog_button_subscribe">Subscribe</string>
|
<string name="add_dialog_button_subscribe">Subscribe</string>
|
||||||
|
<string name="add_dialog_error_connection_failed">Connection failed: %1$s</string>
|
||||||
|
<string name="add_dialog_login_error_not_authorized">Login failed. User not authorized.</string>
|
||||||
|
<string name="add_dialog_login_new_user">New user</string>
|
||||||
|
|
||||||
<!-- Detail activity -->
|
<!-- Detail activity -->
|
||||||
<string name="detail_deep_link_subscribed_toast_message">Subscribed to topic %1$s</string>
|
<string name="detail_deep_link_subscribed_toast_message">Subscribed to topic %1$s</string>
|
||||||
|
|
1
assets/error_black_24dp.svg
Normal file
1
assets/error_black_24dp.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"/></svg>
|
After Width: | Height: | Size: 216 B |
Loading…
Reference in a new issue