Base URL dropdown; working
This commit is contained in:
parent
29a40080db
commit
3dcf4939c8
5 changed files with 199 additions and 91 deletions
|
@ -20,9 +20,8 @@ import io.heckel.ntfy.BuildConfig
|
|||
import io.heckel.ntfy.R
|
||||
import io.heckel.ntfy.db.Repository
|
||||
import io.heckel.ntfy.db.User
|
||||
import io.heckel.ntfy.util.Log
|
||||
import io.heckel.ntfy.msg.ApiService
|
||||
import io.heckel.ntfy.util.topicUrl
|
||||
import io.heckel.ntfy.util.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
@ -51,7 +50,6 @@ class AddFragment : DialogFragment() {
|
|||
private lateinit var subscribeErrorTextImage: View
|
||||
|
||||
// Login page
|
||||
private lateinit var users: List<User>
|
||||
private lateinit var loginUsernameText: TextInputEditText
|
||||
private lateinit var loginPasswordText: TextInputEditText
|
||||
private lateinit var loginProgress: ProgressBar
|
||||
|
@ -90,6 +88,7 @@ class AddFragment : DialogFragment() {
|
|||
subscribeTopicText = view.findViewById(R.id.add_dialog_subscribe_topic_text)
|
||||
subscribeBaseUrlLayout = view.findViewById(R.id.add_dialog_subscribe_base_url_layout)
|
||||
subscribeBaseUrlLayout.background = view.background
|
||||
subscribeBaseUrlLayout.makeEndIconSmaller(resources) // Hack!
|
||||
subscribeBaseUrlText = view.findViewById(R.id.add_dialog_subscribe_base_url_text)
|
||||
subscribeBaseUrlText.background = view.background
|
||||
subscribeInstantDeliveryBox = view.findViewById(R.id.add_dialog_subscribe_instant_delivery_box)
|
||||
|
@ -103,13 +102,6 @@ class AddFragment : DialogFragment() {
|
|||
subscribeErrorTextImage = view.findViewById(R.id.add_dialog_subscribe_error_text_image)
|
||||
subscribeErrorTextImage.visibility = View.GONE
|
||||
|
||||
// Hack: Make end icon smaller, see https://stackoverflow.com/a/57098715/1440785
|
||||
val dimension = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 30f, resources.displayMetrics)
|
||||
val endIconImageView = subscribeBaseUrlLayout.findViewById<ImageView>(R.id.text_input_end_icon)
|
||||
endIconImageView.minimumHeight = dimension.toInt()
|
||||
endIconImageView.minimumWidth = dimension.toInt()
|
||||
subscribeBaseUrlLayout.requestLayout()
|
||||
|
||||
// Fields for "login page"
|
||||
loginUsernameText = view.findViewById(R.id.add_dialog_login_username)
|
||||
loginPasswordText = view.findViewById(R.id.add_dialog_login_password)
|
||||
|
@ -124,45 +116,11 @@ class AddFragment : DialogFragment() {
|
|||
getString(R.string.add_dialog_use_another_server_description_noinstant)
|
||||
}
|
||||
|
||||
// Base URL dropdown behavior; Oh my, why is this so complicated?!
|
||||
val toggleEndIcon = {
|
||||
if (subscribeBaseUrlText.text.isNotEmpty()) {
|
||||
subscribeBaseUrlLayout.setEndIconDrawable(R.drawable.ic_cancel_gray_24dp)
|
||||
} else if (baseUrls.isEmpty()) {
|
||||
subscribeBaseUrlLayout.setEndIconDrawable(0)
|
||||
} else {
|
||||
subscribeBaseUrlLayout.setEndIconDrawable(R.drawable.ic_drop_down_gray_24dp)
|
||||
}
|
||||
}
|
||||
subscribeBaseUrlLayout.setEndIconOnClickListener {
|
||||
if (subscribeBaseUrlText.text.isNotEmpty()) {
|
||||
subscribeBaseUrlText.text.clear()
|
||||
if (baseUrls.isEmpty()) {
|
||||
subscribeBaseUrlLayout.setEndIconDrawable(0)
|
||||
} else {
|
||||
subscribeBaseUrlLayout.setEndIconDrawable(R.drawable.ic_drop_down_gray_24dp)
|
||||
}
|
||||
} else if (subscribeBaseUrlText.text.isEmpty() && baseUrls.isNotEmpty()) {
|
||||
subscribeBaseUrlLayout.setEndIconDrawable(R.drawable.ic_drop_up_gray_24dp)
|
||||
subscribeBaseUrlText.showDropDown()
|
||||
}
|
||||
}
|
||||
subscribeBaseUrlText.setOnDismissListener { toggleEndIcon() }
|
||||
subscribeBaseUrlText.addTextChangedListener(object : TextWatcher {
|
||||
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
|
||||
}
|
||||
})
|
||||
// Show/hide based on flavor
|
||||
subscribeInstantDeliveryBox.visibility = if (BuildConfig.FIREBASE_AVAILABLE) View.VISIBLE else View.GONE
|
||||
|
||||
// Fill autocomplete for base URL & users drop-down
|
||||
// Add baseUrl auto-complete behavior
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
// Auto-complete
|
||||
val appBaseUrl = getString(R.string.app_base_url)
|
||||
baseUrls = repository.getSubscriptions()
|
||||
.groupBy { it.baseUrl }
|
||||
|
@ -170,27 +128,11 @@ class AddFragment : DialogFragment() {
|
|||
.filterNot { it == appBaseUrl }
|
||||
.sorted()
|
||||
val activity = activity ?: return@launch // We may have pressed "Cancel"
|
||||
val adapter = ArrayAdapter(activity, R.layout.fragment_add_dialog_dropdown_item, baseUrls)
|
||||
activity.runOnUiThread {
|
||||
subscribeBaseUrlText.threshold = 1
|
||||
subscribeBaseUrlText.setAdapter(adapter)
|
||||
if (baseUrls.count() == 1) {
|
||||
subscribeBaseUrlLayout.setEndIconDrawable(R.drawable.ic_cancel_gray_24dp)
|
||||
subscribeBaseUrlText.setText(baseUrls.first())
|
||||
} else if (baseUrls.count() > 1) {
|
||||
subscribeBaseUrlLayout.setEndIconDrawable(R.drawable.ic_drop_down_gray_24dp)
|
||||
} else {
|
||||
subscribeBaseUrlLayout.setEndIconDrawable(0)
|
||||
}
|
||||
initBaseUrlDropdown(baseUrls, subscribeBaseUrlText, subscribeBaseUrlLayout)
|
||||
}
|
||||
|
||||
// Users dropdown
|
||||
users = repository.getUsers()
|
||||
}
|
||||
|
||||
// Show/hide based on flavor
|
||||
subscribeInstantDeliveryBox.visibility = if (BuildConfig.FIREBASE_AVAILABLE) View.VISIBLE else View.GONE
|
||||
|
||||
// Username/password validation on type
|
||||
val loginTextWatcher = object : TextWatcher {
|
||||
override fun afterTextChanged(s: Editable?) {
|
||||
|
@ -384,13 +326,9 @@ class AddFragment : DialogFragment() {
|
|||
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)
|
||||
positiveButton.isEnabled = validTopic(topic) && validUrl(baseUrl)
|
||||
} else {
|
||||
positiveButton.isEnabled = topic.isNotBlank()
|
||||
&& "[-_A-Za-z0-9]{1,64}".toRegex().matches(topic)
|
||||
positiveButton.isEnabled = validTopic(topic)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
58
app/src/main/java/io/heckel/ntfy/ui/BaseUrl.kt
Normal file
58
app/src/main/java/io/heckel/ntfy/ui/BaseUrl.kt
Normal file
|
@ -0,0 +1,58 @@
|
|||
package io.heckel.ntfy.ui
|
||||
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.AutoCompleteTextView
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import io.heckel.ntfy.R
|
||||
|
||||
fun initBaseUrlDropdown(baseUrls: List<String>, textView: AutoCompleteTextView, layout: TextInputLayout) {
|
||||
// Base URL dropdown behavior; Oh my, why is this so complicated?!
|
||||
val toggleEndIcon = {
|
||||
if (textView.text.isNotEmpty()) {
|
||||
layout.setEndIconDrawable(R.drawable.ic_cancel_gray_24dp)
|
||||
} else if (baseUrls.isEmpty()) {
|
||||
layout.setEndIconDrawable(0)
|
||||
} else {
|
||||
layout.setEndIconDrawable(R.drawable.ic_drop_down_gray_24dp)
|
||||
}
|
||||
}
|
||||
layout.setEndIconOnClickListener {
|
||||
if (textView.text.isNotEmpty()) {
|
||||
textView.text.clear()
|
||||
if (baseUrls.isEmpty()) {
|
||||
layout.setEndIconDrawable(0)
|
||||
} else {
|
||||
layout.setEndIconDrawable(R.drawable.ic_drop_down_gray_24dp)
|
||||
}
|
||||
} else if (textView.text.isEmpty() && baseUrls.isNotEmpty()) {
|
||||
layout.setEndIconDrawable(R.drawable.ic_drop_up_gray_24dp)
|
||||
textView.showDropDown()
|
||||
}
|
||||
}
|
||||
textView.setOnDismissListener { toggleEndIcon() }
|
||||
textView.addTextChangedListener(object : TextWatcher {
|
||||
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
|
||||
}
|
||||
})
|
||||
|
||||
val adapter = ArrayAdapter(textView.context, R.layout.fragment_add_dialog_dropdown_item, baseUrls)
|
||||
textView.threshold = 1
|
||||
textView.setAdapter(adapter)
|
||||
if (baseUrls.count() == 1) {
|
||||
layout.setEndIconDrawable(R.drawable.ic_cancel_gray_24dp)
|
||||
textView.setText(baseUrls.first())
|
||||
} else if (baseUrls.count() > 1) {
|
||||
layout.setEndIconDrawable(R.drawable.ic_drop_down_gray_24dp)
|
||||
} else {
|
||||
layout.setEndIconDrawable(0)
|
||||
}
|
||||
}
|
|
@ -10,12 +10,10 @@ import android.text.TextWatcher
|
|||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import android.widget.*
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import io.heckel.ntfy.R
|
||||
import io.heckel.ntfy.app.Application
|
||||
import io.heckel.ntfy.msg.ApiService
|
||||
|
@ -30,6 +28,9 @@ class ShareActivity : AppCompatActivity() {
|
|||
// File to share
|
||||
private var fileUri: Uri? = null
|
||||
|
||||
// List of base URLs used, excluding app_base_url
|
||||
private lateinit var baseUrls: List<String>
|
||||
|
||||
// UI elements
|
||||
private lateinit var menu: Menu
|
||||
private lateinit var sendItem: MenuItem
|
||||
|
@ -39,6 +40,9 @@ class ShareActivity : AppCompatActivity() {
|
|||
private lateinit var contentFileIcon: ImageView
|
||||
private lateinit var contentText: TextView
|
||||
private lateinit var topicText: TextView
|
||||
private lateinit var baseUrlLayout: TextInputLayout
|
||||
private lateinit var baseUrlText: AutoCompleteTextView
|
||||
private lateinit var useAnotherServerCheckbox: CheckBox
|
||||
private lateinit var progress: ProgressBar
|
||||
private lateinit var errorText: TextView
|
||||
private lateinit var errorImage: ImageView
|
||||
|
@ -48,7 +52,7 @@ class ShareActivity : AppCompatActivity() {
|
|||
setContentView(R.layout.activity_share)
|
||||
|
||||
Log.init(this) // Init logs in all entry points
|
||||
Log.d(TAG, "Create $this")
|
||||
Log.d(TAG, "Create $this with intent $intent")
|
||||
|
||||
// Action bar
|
||||
title = getString(R.string.share_title)
|
||||
|
@ -63,6 +67,12 @@ class ShareActivity : AppCompatActivity() {
|
|||
contentFileInfo = findViewById(R.id.share_content_file_info)
|
||||
contentFileIcon = findViewById(R.id.share_content_file_icon)
|
||||
topicText = findViewById(R.id.share_topic_text)
|
||||
baseUrlLayout = findViewById(R.id.share_base_url_layout)
|
||||
//baseUrlLayout.background = window.background
|
||||
baseUrlLayout.makeEndIconSmaller(resources) // Hack!
|
||||
baseUrlText = findViewById(R.id.share_base_url_text)
|
||||
//baseUrlText.background = topicText.background
|
||||
useAnotherServerCheckbox = findViewById(R.id.share_use_another_server_checkbox)
|
||||
progress = findViewById(R.id.share_progress)
|
||||
progress.visibility = View.GONE
|
||||
errorText = findViewById(R.id.share_error_text)
|
||||
|
@ -84,10 +94,31 @@ class ShareActivity : AppCompatActivity() {
|
|||
contentText.addTextChangedListener(textWatcher)
|
||||
topicText.addTextChangedListener(textWatcher)
|
||||
|
||||
// Add behavior to "use another" checkbox
|
||||
useAnotherServerCheckbox.setOnCheckedChangeListener { _, isChecked ->
|
||||
baseUrlLayout.visibility = if (isChecked) View.VISIBLE else View.GONE
|
||||
validateInput()
|
||||
}
|
||||
|
||||
// Add baseUrl auto-complete behavior
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val appBaseUrl = getString(R.string.app_base_url)
|
||||
baseUrls = repository.getSubscriptions()
|
||||
.groupBy { it.baseUrl }
|
||||
.map { it.key }
|
||||
.filterNot { it == appBaseUrl }
|
||||
.sorted()
|
||||
val activity = this@ShareActivity
|
||||
activity.runOnUiThread {
|
||||
initBaseUrlDropdown(baseUrls, baseUrlText, baseUrlLayout)
|
||||
useAnotherServerCheckbox.isChecked = baseUrls.count() == 1
|
||||
}
|
||||
}
|
||||
|
||||
// Incoming intent
|
||||
val intent = intent ?: return
|
||||
if (intent.action != Intent.ACTION_SEND) return
|
||||
if ("text/plain" == intent.type) {
|
||||
if (intent.type == "text/plain") {
|
||||
handleSendText(intent)
|
||||
} else if (supportedImage(intent.type)) {
|
||||
handleSendImage(intent)
|
||||
|
@ -97,14 +128,19 @@ class ShareActivity : AppCompatActivity() {
|
|||
}
|
||||
|
||||
private fun handleSendText(intent: Intent) {
|
||||
intent.getStringExtra(Intent.EXTRA_TEXT)?.let { text ->
|
||||
contentText.text = text
|
||||
show()
|
||||
}
|
||||
val text = intent.getStringExtra(Intent.EXTRA_TEXT) ?: "(no text)"
|
||||
Log.d(TAG, "Shared content is text: $text")
|
||||
contentText.text = text
|
||||
show()
|
||||
}
|
||||
|
||||
private fun handleSendImage(intent: Intent) {
|
||||
fileUri = intent.getParcelableExtra<Parcelable>(Intent.EXTRA_STREAM) as? Uri ?: return
|
||||
fileUri = intent.getParcelableExtra<Parcelable>(Intent.EXTRA_STREAM) as? Uri
|
||||
Log.d(TAG, "Shared content is an image with URI $fileUri")
|
||||
if (fileUri == null) {
|
||||
Log.w(TAG, "Null URI is not allowed. Aborting.")
|
||||
return
|
||||
}
|
||||
try {
|
||||
val resolver = applicationContext.contentResolver
|
||||
val bitmapStream = resolver.openInputStream(fileUri!!)
|
||||
|
@ -121,7 +157,12 @@ class ShareActivity : AppCompatActivity() {
|
|||
}
|
||||
|
||||
private fun handleSendFile(intent: Intent) {
|
||||
fileUri = intent.getParcelableExtra<Parcelable>(Intent.EXTRA_STREAM) as? Uri ?: return
|
||||
fileUri = intent.getParcelableExtra<Parcelable>(Intent.EXTRA_STREAM) as? Uri
|
||||
Log.d(TAG, "Shared content is a file with URI $fileUri")
|
||||
if (fileUri == null) {
|
||||
Log.w(TAG, "Null URI is not allowed. Aborting.")
|
||||
return
|
||||
}
|
||||
try {
|
||||
val resolver = applicationContext.contentResolver
|
||||
val info = fileStat(this, fileUri)
|
||||
|
@ -130,7 +171,6 @@ class ShareActivity : AppCompatActivity() {
|
|||
contentFileInfo.text = "${info.filename}\n${formatBytes(info.size)}"
|
||||
contentFileIcon.setImageResource(mimeTypeToIconResource(mimeType))
|
||||
show(file = true)
|
||||
|
||||
} catch (e: Exception) {
|
||||
fileUri = null
|
||||
contentText.text = ""
|
||||
|
@ -170,7 +210,7 @@ class ShareActivity : AppCompatActivity() {
|
|||
}
|
||||
|
||||
private fun onShareClick() {
|
||||
val baseUrl = "https://ntfy.sh" // FIXME
|
||||
val baseUrl = getBaseUrl()
|
||||
val topic = topicText.text.toString()
|
||||
val message = contentText.text.toString()
|
||||
progress.visibility = View.VISIBLE
|
||||
|
@ -226,8 +266,21 @@ class ShareActivity : AppCompatActivity() {
|
|||
|
||||
private fun validateInput() {
|
||||
if (!this::sendItem.isInitialized) return // Initialized late in onCreateOptionsMenu
|
||||
sendItem.isEnabled = contentText.text.isNotEmpty() && topicText.text.isNotEmpty()
|
||||
sendItem.icon.alpha = if (sendItem.isEnabled) 255 else 130
|
||||
val enabled = if (useAnotherServerCheckbox.isChecked) {
|
||||
contentText.text.isNotEmpty() && validTopic(topicText.text.toString()) && validUrl(baseUrlText.text.toString())
|
||||
} else {
|
||||
contentText.text.isNotEmpty() && topicText.text.isNotEmpty()
|
||||
}
|
||||
sendItem.isEnabled = enabled
|
||||
sendItem.icon.alpha = if (enabled) 255 else 130
|
||||
}
|
||||
|
||||
private fun getBaseUrl(): String {
|
||||
return if (useAnotherServerCheckbox.isChecked) {
|
||||
baseUrlText.text.toString()
|
||||
} else {
|
||||
getString(R.string.app_base_url)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -6,11 +6,15 @@ import android.content.ContentResolver
|
|||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
||||
import android.content.res.Resources
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.PowerManager
|
||||
import android.provider.OpenableColumns
|
||||
import android.util.TypedValue
|
||||
import android.view.View
|
||||
import android.view.Window
|
||||
import android.widget.ImageView
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import io.heckel.ntfy.R
|
||||
import io.heckel.ntfy.db.Notification
|
||||
|
@ -40,6 +44,14 @@ fun shortUrl(url: String) = url
|
|||
.replace("http://", "")
|
||||
.replace("https://", "")
|
||||
|
||||
fun validTopic(topic: String): Boolean {
|
||||
return "[-_A-Za-z0-9]{1,64}".toRegex().matches(topic) // Must match server side!
|
||||
}
|
||||
|
||||
fun validUrl(url: String): Boolean {
|
||||
return "^https?://.+".toRegex().matches(url)
|
||||
}
|
||||
|
||||
fun formatDateShort(timestampSecs: Long): String {
|
||||
val date = Date(timestampSecs*1000)
|
||||
return DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT).format(date)
|
||||
|
@ -271,3 +283,12 @@ class ContentUriRequestBody(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Hack: Make end icon for drop down smaller, see https://stackoverflow.com/a/57098715/1440785
|
||||
fun View.makeEndIconSmaller(resources: Resources) {
|
||||
val dimension = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 30f, resources.displayMetrics)
|
||||
val endIconImageView = findViewById<ImageView>(R.id.text_input_end_icon)
|
||||
endIconImageView.minimumHeight = dimension.toInt()
|
||||
endIconImageView.minimumWidth = dimension.toInt()
|
||||
requestLayout()
|
||||
}
|
||||
|
|
|
@ -21,9 +21,9 @@
|
|||
android:paddingBottom="2dp"
|
||||
android:text="@string/share_content_title"
|
||||
android:textAlignment="viewStart"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||
android:textAppearance="@style/TextAppearance.Material3.TitleMedium"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
app:layout_constraintTop_toTopOf="parent" android:paddingStart="2dp"/>
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" app:srcCompat="@drawable/ic_cancel_gray_24dp"
|
||||
|
@ -75,9 +75,9 @@
|
|||
android:paddingBottom="3dp"
|
||||
android:text="Share to topic"
|
||||
android:textAlignment="viewStart"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||
android:textAppearance="@style/TextAppearance.Material3.TitleMedium"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/share_content_file_box" android:layout_marginTop="10dp"/>
|
||||
app:layout_constraintTop_toBottomOf="@id/share_content_file_box" android:layout_marginTop="15dp" android:paddingStart="2dp"/>
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/share_topic_text"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -86,6 +86,44 @@
|
|||
android:maxLines="1" android:inputType="text" android:maxLength="64"
|
||||
app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/share_topic_title"/>
|
||||
<CheckBox
|
||||
android:text="@string/add_dialog_use_another_server"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" android:id="@+id/share_use_another_server_checkbox"
|
||||
android:layout_marginStart="-3dp" app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/share_topic_text"/>
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox.Dense.ExposedDropdownMenu"
|
||||
android:id="@+id/share_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/share_use_another_server_checkbox">
|
||||
<AutoCompleteTextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/share_base_url_text"
|
||||
android:hint="@string/app_base_url"
|
||||
android:maxLines="1"
|
||||
android:layout_marginTop="0dp"
|
||||
android:layout_marginBottom="0dp"
|
||||
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>
|
||||
<TextView
|
||||
android:text="Unable to resolve host example.com"
|
||||
android:layout_width="0dp"
|
||||
|
@ -95,7 +133,7 @@
|
|||
android:paddingEnd="4dp"
|
||||
android:textAppearance="@style/DangerText"
|
||||
app:layout_constraintStart_toEndOf="@id/share_error_image"
|
||||
android:layout_marginTop="5dp" app:layout_constraintTop_toBottomOf="@+id/share_topic_text"/>
|
||||
android:layout_marginTop="5dp" app:layout_constraintTop_toBottomOf="@id/share_base_url_layout"/>
|
||||
<ImageView
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp" app:srcCompat="@drawable/ic_error_red_24dp"
|
||||
|
|
Loading…
Reference in a new issue