This looks reasonably nice

This commit is contained in:
Philipp Heckel 2022-02-12 23:02:42 -05:00
parent 8e333e55bc
commit 3a2e6cbf57
6 changed files with 244 additions and 157 deletions

View file

@ -353,7 +353,7 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas
} }
fun addLastShareTopic(topic: String) { fun addLastShareTopic(topic: String) {
val topics = (getLastShareTopics() + topic).takeLast(LAST_TOPICS_COUNT) val topics = (getLastShareTopics().filterNot { it == topic } + topic).takeLast(LAST_TOPICS_COUNT)
sharedPrefs.edit() sharedPrefs.edit()
.putString(SHARED_PREFS_LAST_TOPICS, topics.joinToString(separator = "\n")) .putString(SHARED_PREFS_LAST_TOPICS, topics.joinToString(separator = "\n"))
.apply() .apply()
@ -437,7 +437,7 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas
const val SHARED_PREFS_UNIFIED_PUSH_BASE_URL = "UnifiedPushBaseURL" const val SHARED_PREFS_UNIFIED_PUSH_BASE_URL = "UnifiedPushBaseURL"
const val SHARED_PREFS_LAST_TOPICS = "LastTopics" const val SHARED_PREFS_LAST_TOPICS = "LastTopics"
private const val LAST_TOPICS_COUNT = 5 private const val LAST_TOPICS_COUNT = 3
const val MUTED_UNTIL_SHOW_ALL = 0L const val MUTED_UNTIL_SHOW_ALL = 0L
const val MUTED_UNTIL_FOREVER = 1L const val MUTED_UNTIL_FOREVER = 1L

View file

@ -13,7 +13,6 @@ import io.heckel.ntfy.R
import io.heckel.ntfy.db.ConnectionState import io.heckel.ntfy.db.ConnectionState
import io.heckel.ntfy.db.Repository import io.heckel.ntfy.db.Repository
import io.heckel.ntfy.db.Subscription import io.heckel.ntfy.db.Subscription
import io.heckel.ntfy.util.isDarkThemeOn
import io.heckel.ntfy.util.topicShortUrl import io.heckel.ntfy.util.topicShortUrl
import java.text.DateFormat import java.text.DateFormat
import java.util.* import java.util.*

View file

@ -7,15 +7,15 @@ import android.os.Bundle
import android.os.Parcelable import android.os.Parcelable
import android.text.Editable import android.text.Editable
import android.text.TextWatcher import android.text.TextWatcher
import android.view.Menu import android.view.*
import android.view.MenuItem
import android.view.View
import android.widget.* import android.widget.*
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.textfield.TextInputLayout import com.google.android.material.textfield.TextInputLayout
import io.heckel.ntfy.R import io.heckel.ntfy.R
import io.heckel.ntfy.app.Application import io.heckel.ntfy.app.Application
import io.heckel.ntfy.db.Subscription
import io.heckel.ntfy.msg.ApiService import io.heckel.ntfy.msg.ApiService
import io.heckel.ntfy.util.* import io.heckel.ntfy.util.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -31,6 +31,9 @@ class ShareActivity : AppCompatActivity() {
// Lazy-loaded things from Repository // Lazy-loaded things from Repository
private lateinit var baseUrls: List<String> private lateinit var baseUrls: List<String>
// Context-dependent things
private lateinit var appBaseUrl: String
// UI elements // UI elements
private lateinit var menu: Menu private lateinit var menu: Menu
private lateinit var sendItem: MenuItem private lateinit var sendItem: MenuItem
@ -43,6 +46,7 @@ class ShareActivity : AppCompatActivity() {
private lateinit var baseUrlLayout: TextInputLayout private lateinit var baseUrlLayout: TextInputLayout
private lateinit var baseUrlText: AutoCompleteTextView private lateinit var baseUrlText: AutoCompleteTextView
private lateinit var useAnotherServerCheckbox: CheckBox private lateinit var useAnotherServerCheckbox: CheckBox
private lateinit var lastTopicsList: RecyclerView
private lateinit var progress: ProgressBar private lateinit var progress: ProgressBar
private lateinit var errorText: TextView private lateinit var errorText: TextView
private lateinit var errorImage: ImageView private lateinit var errorImage: ImageView
@ -60,6 +64,9 @@ class ShareActivity : AppCompatActivity() {
// Show 'Back' button // Show 'Back' button
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
// Context-dependent things
appBaseUrl = getString(R.string.app_base_url)
// UI elements // UI elements
contentText = findViewById(R.id.share_content_text) contentText = findViewById(R.id.share_content_text)
contentImage = findViewById(R.id.share_content_image) contentImage = findViewById(R.id.share_content_image)
@ -73,6 +80,7 @@ class ShareActivity : AppCompatActivity() {
baseUrlText = findViewById(R.id.share_base_url_text) baseUrlText = findViewById(R.id.share_base_url_text)
//baseUrlText.background = topicText.background //baseUrlText.background = topicText.background
useAnotherServerCheckbox = findViewById(R.id.share_use_another_server_checkbox) useAnotherServerCheckbox = findViewById(R.id.share_use_another_server_checkbox)
lastTopicsList = findViewById(R.id.share_last_topics)
progress = findViewById(R.id.share_progress) progress = findViewById(R.id.share_progress)
progress.visibility = View.GONE progress.visibility = View.GONE
errorText = findViewById(R.id.share_error_text) errorText = findViewById(R.id.share_error_text)
@ -93,6 +101,7 @@ class ShareActivity : AppCompatActivity() {
} }
contentText.addTextChangedListener(textWatcher) contentText.addTextChangedListener(textWatcher)
topicText.addTextChangedListener(textWatcher) topicText.addTextChangedListener(textWatcher)
baseUrlText.addTextChangedListener(textWatcher)
// Add behavior to "use another" checkbox // Add behavior to "use another" checkbox
useAnotherServerCheckbox.setOnCheckedChangeListener { _, isChecked -> useAnotherServerCheckbox.setOnCheckedChangeListener { _, isChecked ->
@ -100,9 +109,25 @@ class ShareActivity : AppCompatActivity() {
validateInput() validateInput()
} }
// Populate "last topics"
val reversedLastTopics = repository.getLastShareTopics().reversed()
lastTopicsList.adapter = TopicAdapter(reversedLastTopics) { topicUrl ->
try {
val (baseUrl, topic) = splitTopicUrl(topicUrl)
topicText.text = topic
if (baseUrl == appBaseUrl) {
useAnotherServerCheckbox.isChecked = false
} else {
useAnotherServerCheckbox.isChecked = true
baseUrlText.setText(baseUrl)
}
} catch (e: Exception) {
Log.w(TAG, "Invalid topicUrl $topicUrl", e)
}
}
// Add baseUrl auto-complete behavior // Add baseUrl auto-complete behavior
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
val appBaseUrl = getString(R.string.app_base_url)
baseUrls = repository.getSubscriptions() baseUrls = repository.getSubscriptions()
.groupBy { it.baseUrl } .groupBy { it.baseUrl }
.map { it.key } .map { it.key }
@ -111,14 +136,20 @@ class ShareActivity : AppCompatActivity() {
val activity = this@ShareActivity val activity = this@ShareActivity
activity.runOnUiThread { activity.runOnUiThread {
initBaseUrlDropdown(baseUrls, baseUrlText, baseUrlLayout) initBaseUrlDropdown(baseUrls, baseUrlText, baseUrlLayout)
useAnotherServerCheckbox.isChecked = baseUrls.count() == 1 useAnotherServerCheckbox.isChecked = if (reversedLastTopics.isNotEmpty()) {
try {
val (baseUrl, _) = splitTopicUrl(reversedLastTopics.first())
baseUrl != appBaseUrl
} catch (_: Exception) {
false
}
} else {
baseUrls.count() == 1
}
baseUrlLayout.visibility = if (useAnotherServerCheckbox.isChecked) View.VISIBLE else View.GONE
} }
} }
// Populate "last topics"
val lastTopics = repository.getLastShareTopics()
Log.d(TAG, "last topics: $lastTopics")
// Incoming intent // Incoming intent
val intent = intent ?: return val intent = intent ?: return
if (intent.action != Intent.ACTION_SEND) return if (intent.action != Intent.ACTION_SEND) return
@ -284,6 +315,24 @@ class ShareActivity : AppCompatActivity() {
} }
} }
class TopicAdapter(private val topicUrls: List<String>, val onClick: (String) -> Unit) : RecyclerView.Adapter<TopicAdapter.ViewHolder>() {
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(viewGroup.context).inflate(R.layout.fragment_share_item, viewGroup, false)
return ViewHolder(view)
}
override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
viewHolder.topicName.text = shortUrl(topicUrls[position])
viewHolder.view.setOnClickListener { onClick(topicUrls[position]) }
}
override fun getItemCount() = topicUrls.size
class ViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
val topicName: TextView = view.findViewById(R.id.share_item_text)
}
}
companion object { companion object {
const val TAG = "NtfyShareActivity" const val TAG = "NtfyShareActivity"
} }

View file

@ -44,6 +44,11 @@ fun shortUrl(url: String) = url
.replace("http://", "") .replace("http://", "")
.replace("https://", "") .replace("https://", "")
fun splitTopicUrl(topicUrl: String): Pair<String, String> {
if (topicUrl.lastIndexOf("/") == -1) throw Exception("Invalid argument $topicUrl")
return Pair(topicUrl.substringBeforeLast("/"), topicUrl.substringAfterLast("/"))
}
fun validTopic(topic: String): Boolean { fun validTopic(topic: String): Boolean {
return "[-_A-Za-z0-9]{1,64}".toRegex().matches(topic) // Must match server side! return "[-_A-Za-z0-9]{1,64}".toRegex().matches(topic) // Must match server side!
} }

View file

@ -1,159 +1,166 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout <ScrollView
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" 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="horizontal" android:paddingStart="15dp" android:paddingEnd="15dp" android:paddingTop="10dp" android:paddingBottom="10dp">
<ProgressBar
style="?android:attr/progressBarStyle"
android:layout_width="24dp"
android:layout_height="24dp"
android:id="@+id/share_progress"
app:layout_constraintEnd_toEndOf="parent"
android:indeterminate="true" android:visibility="visible" app:layout_constraintTop_toTopOf="parent" android:layout_marginTop="5dp"/>
<TextView
android:id="@+id/share_content_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:paddingTop="5dp"
android:paddingBottom="2dp"
android:text="@string/share_content_title"
android:textAlignment="viewStart"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
app:layout_constraintStart_toStartOf="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"
android:id="@+id/share_content_image" app:layout_constraintStart_toStartOf="parent"
android:scaleType="fitStart"
android:adjustViewBounds="true" android:maxHeight="150dp"
app:shapeAppearanceOverlay="@style/roundedCornersImageView" android:visibility="visible"
app:layout_constraintTop_toBottomOf="@id/share_content_title" android:layout_marginTop="5dp"/>
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/share_content_text"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:hint="@string/share_content_text_hint"
android:importantForAutofill="no"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"
android:lines="10" android:gravity="start|top" app:layout_constraintTop_toBottomOf="@id/share_content_image" android:minLines="1" android:layout_marginTop="5dp"/>
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" app:layout_constraintTop_toBottomOf="@id/share_content_text" android:layout_height="wrap_content"
android:id="@+id/share_content_file_box" app:layout_constraintStart_toStartOf="parent" android:orientation="horizontal" android:paddingStart="15dp" android:paddingEnd="15dp" android:paddingTop="10dp" android:paddingBottom="10dp">
app:layout_constraintEnd_toEndOf="parent"
android:visibility="visible" android:layout_marginTop="5dp" <ProgressBar
android:background="?android:attr/selectableItemBackground" style="?android:attr/progressBarStyle"
android:clickable="true" android:focusable="true" android:padding="4dp" android:paddingStart="0dp"> android:layout_width="24dp"
<ImageView android:layout_height="24dp"
android:layout_width="wrap_content" android:id="@+id/share_progress"
android:layout_height="wrap_content" app:srcCompat="@drawable/ic_cancel_gray_24dp" app:layout_constraintEnd_toEndOf="parent"
android:id="@+id/share_content_file_icon" app:layout_constraintStart_toStartOf="parent" android:indeterminate="true" android:visibility="visible" app:layout_constraintTop_toTopOf="parent" android:layout_marginTop="5dp"/>
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toStartOf="@+id/share_content_file_info" android:layout_marginEnd="5dp"
app:layout_constraintBottom_toBottomOf="parent"
/>
<TextView <TextView
android:text="some file.mp3\n7.1 MB" android:id="@+id/share_content_title"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:id="@+id/share_content_file_info" android:paddingTop="5dp"
android:textColor="?android:attr/textColorPrimary" android:paddingBottom="2dp"
android:textAppearance="@style/TextAppearance.AppCompat.Small" android:text="@string/share_content_title"
app:layout_constraintStart_toEndOf="@+id/share_content_file_icon" android:textAlignment="viewStart"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<com.google.android.material.imageview.ShapeableImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content" app:srcCompat="@drawable/ic_cancel_gray_24dp"
android:id="@+id/share_content_image" app:layout_constraintStart_toStartOf="parent"
android:scaleType="fitStart"
android:adjustViewBounds="true" android:maxHeight="150dp"
app:shapeAppearanceOverlay="@style/roundedCornersImageView" android:visibility="visible"
app:layout_constraintTop_toBottomOf="@id/share_content_title" android:layout_marginTop="5dp"/>
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/share_content_text"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:hint="@string/share_content_text_hint"
android:importantForAutofill="no"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"
android:lines="10" android:gravity="start|top" app:layout_constraintTop_toBottomOf="@id/share_content_image" android:minLines="1" android:layout_marginTop="5dp"/>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" app:layout_constraintTop_toBottomOf="@id/share_content_text"
android:id="@+id/share_content_file_box" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/share_content_file_icon" android:visibility="visible" android:layout_marginTop="5dp"
app:layout_constraintBottom_toBottomOf="@+id/share_content_file_icon"/> android:background="?android:attr/selectableItemBackground"
</androidx.constraintlayout.widget.ConstraintLayout> android:clickable="true" android:focusable="true" android:padding="4dp" android:paddingStart="0dp">
<TextView <ImageView
android:id="@+id/share_topic_title" android:layout_width="wrap_content"
android:layout_width="0dp" android:layout_height="wrap_content" app:srcCompat="@drawable/ic_cancel_gray_24dp"
android:layout_height="wrap_content" android:id="@+id/share_content_file_icon" app:layout_constraintStart_toStartOf="parent"
android:paddingTop="5dp" app:layout_constraintTop_toTopOf="parent"
android:paddingBottom="3dp" app:layout_constraintEnd_toStartOf="@+id/share_content_file_info" android:layout_marginEnd="5dp"
android:text="@string/share_topic_title" app:layout_constraintBottom_toBottomOf="parent"
android:textAlignment="viewStart" />
android:textAppearance="@style/TextAppearance.AppCompat.Large" <TextView
app:layout_constraintStart_toStartOf="parent" android:text="some file.mp3\n7.1 MB"
app:layout_constraintTop_toBottomOf="@id/share_content_file_box" android:layout_marginTop="15dp" android:paddingStart="2dp"/> android:layout_width="0dp"
<com.google.android.material.textfield.TextInputEditText android:layout_height="wrap_content"
android:id="@+id/share_topic_text" android:id="@+id/share_content_file_info"
android:layout_width="match_parent" android:textColor="?android:attr/textColorPrimary"
android:layout_height="wrap_content" android:hint="@string/add_dialog_topic_name_hint" android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:importantForAutofill="no" app:layout_constraintStart_toEndOf="@+id/share_content_file_icon"
android:maxLines="1" android:inputType="text" android:maxLength="64" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="@+id/share_content_file_icon"
app:layout_constraintTop_toBottomOf="@id/share_topic_title"/> app:layout_constraintBottom_toBottomOf="@+id/share_content_file_icon"/>
<CheckBox </androidx.constraintlayout.widget.ConstraintLayout>
android:text="@string/add_dialog_use_another_server" <TextView
android:layout_width="match_parent" android:id="@+id/share_topic_title"
android:layout_height="wrap_content" android:id="@+id/share_use_another_server_checkbox" android:layout_width="0dp"
android:layout_marginStart="-3dp" app:layout_constraintStart_toStartOf="parent" android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent" android:paddingTop="5dp"
app:layout_constraintTop_toBottomOf="@id/share_topic_text"/> android:paddingBottom="3dp"
<com.google.android.material.textfield.TextInputLayout android:text="@string/share_topic_title"
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox.Dense.ExposedDropdownMenu" android:textAlignment="viewStart"
android:id="@+id/share_base_url_layout" android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:layout_width="match_parent" app:layout_constraintStart_toStartOf="parent"
android:layout_height="wrap_content" app:layout_constraintTop_toBottomOf="@id/share_content_file_box" android:layout_marginTop="15dp"/>
android:layout_margin="0dp" <com.google.android.material.textfield.TextInputEditText
android:padding="0dp" android:id="@+id/share_topic_text"
android:visibility="gone" android:layout_width="match_parent"
app:endIconMode="custom" android:layout_height="wrap_content" android:hint="@string/add_dialog_topic_name_hint"
app:hintEnabled="false" android:importantForAutofill="no"
app:boxBackgroundColor="@null" app:layout_constraintStart_toStartOf="parent" android:maxLines="1" android:inputType="text|textNoSuggestions" android:maxLength="64"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/share_use_another_server_checkbox"> app:layout_constraintTop_toBottomOf="@id/share_topic_title" android:layout_marginStart="-3dp"/>
<AutoCompleteTextView <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"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/share_topic_text" android:paddingTop="-5dp" android:layout_marginTop="-5dp" android:layout_marginStart="-5dp"/>
<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_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:id="@+id/share_base_url_text" android:layout_margin="0dp"
android:hint="@string/app_base_url" android:padding="0dp"
android:maxLines="1" android:visibility="visible"
android:layout_marginTop="0dp" app:endIconMode="custom"
android:layout_marginBottom="0dp" app:hintEnabled="false"
android:inputType="textNoSuggestions" app:boxBackgroundColor="@null"
android:paddingStart="0dp" app:layout_constraintEnd_toEndOf="parent"
android:paddingEnd="0dp" app:layout_constraintTop_toBottomOf="@id/share_use_another_server_checkbox" app:layout_constraintStart_toStartOf="parent" android:layout_marginTop="-5dp">
<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="-5dp"
android:layout_marginBottom="0dp"
android:inputType="textUri|textNoSuggestions"
android:paddingStart="0dp"
android:paddingEnd="0dp"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:layout_marginStart="4dp" android:textAppearance="@style/TextAppearance.AppCompat.Medium"/>
</com.google.android.material.textfield.TextInputLayout>
<TextView
android:id="@+id/share_last_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:paddingTop="5dp" android:paddingTop="5dp"
android:paddingBottom="5dp" android:paddingBottom="3dp"
android:layout_marginStart="4dp" android:text="@string/share_previous_topics"
android:layout_marginEnd="4dp" android:textAlignment="viewStart"
android:textAppearance="?android:attr/textAppearanceMedium" android:textAppearance="@style/TextAppearance.AppCompat.Medium"
/> app:layout_constraintTop_toBottomOf="@id/share_base_url_layout" app:layout_constraintStart_toStartOf="parent" android:layout_marginTop="15dp"/>
</com.google.android.material.textfield.TextInputLayout> <androidx.recyclerview.widget.RecyclerView
<TextView android:layout_width="match_parent"
android:id="@+id/share_last_title" android:layout_height="0dp"
android:layout_width="0dp" android:id="@+id/share_last_topics"
android:layout_height="wrap_content" app:layout_constraintTop_toBottomOf="@id/share_last_title"
android:paddingTop="5dp" app:layout_constraintStart_toStartOf="parent"
android:paddingBottom="3dp" app:layout_constraintEnd_toEndOf="parent"
android:text="@string/share_previous_topics" android:clickable="true"
android:textAlignment="viewStart" android:focusable="true"
android:textAppearance="@style/TextAppearance.AppCompat.Large" android:background="?android:attr/selectableItemBackground"
android:paddingStart="2dp" app:layout_constraintTop_toBottomOf="@id/share_base_url_layout" app:layout_constraintStart_toStartOf="parent" android:layout_marginTop="15dp"/> app:layoutManager="LinearLayoutManager"/>
<LinearLayout <TextView
android:orientation="vertical" android:text="Unable to resolve host example.com"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:id="@+id/share_last_layout" app:layout_constraintTop_toBottomOf="@id/share_last_title" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" android:layout_marginTop="5dp"> android:layout_height="wrap_content" android:id="@+id/share_error_text"
</LinearLayout> android:paddingStart="4dp"
<TextView app:layout_constraintEnd_toEndOf="parent"
android:text="Unable to resolve host example.com" android:paddingEnd="4dp"
android:layout_width="0dp" android:textAppearance="@style/DangerText"
android:layout_height="wrap_content" android:id="@+id/share_error_text" app:layout_constraintStart_toEndOf="@id/share_error_image"
android:paddingStart="4dp" android:layout_marginTop="10dp" app:layout_constraintTop_toBottomOf="@id/share_last_topics"/>
app:layout_constraintEnd_toEndOf="parent" <ImageView
android:paddingEnd="4dp" android:layout_width="20dp"
android:textAppearance="@style/DangerText" android:layout_height="20dp" app:srcCompat="@drawable/ic_error_red_24dp"
app:layout_constraintStart_toEndOf="@id/share_error_image" android:id="@+id/share_error_image"
android:layout_marginTop="10dp" app:layout_constraintTop_toBottomOf="@id/share_last_title"/> android:visibility="visible"
<ImageView app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="@+id/share_error_text" android:layout_marginTop="2dp"/>
android:layout_width="20dp"
android:layout_height="20dp" app:srcCompat="@drawable/ic_error_red_24dp"
android:id="@+id/share_error_image"
android:visibility="visible"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="@+id/share_error_text" android:layout_marginTop="2dp"/>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>

View file

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:orientation="horizontal" android:clickable="true"
android:focusable="true"
>
<ImageView
android:layout_width="20dp"
android:layout_height="20dp" app:srcCompat="@drawable/ic_sms_gray_24dp"
android:id="@+id/share_item_image"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="@+id/share_item_text"
app:layout_constraintTop_toTopOf="@+id/share_item_text" android:layout_marginStart="2dp"/>
<TextView
android:text="ntfy.sh/example"
android:layout_width="0dp"
android:layout_height="wrap_content" android:id="@+id/share_item_text"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="@+id/share_item_image"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textColor="?android:attr/textColorPrimary" android:layout_marginTop="7dp"
app:layout_constraintBottom_toBottomOf="parent" android:layout_marginBottom="7dp" android:layout_marginStart="4dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>