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) {
val topics = (getLastShareTopics() + topic).takeLast(LAST_TOPICS_COUNT)
val topics = (getLastShareTopics().filterNot { it == topic } + topic).takeLast(LAST_TOPICS_COUNT)
sharedPrefs.edit()
.putString(SHARED_PREFS_LAST_TOPICS, topics.joinToString(separator = "\n"))
.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_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_FOREVER = 1L

View file

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

View file

@ -7,15 +7,15 @@ import android.os.Bundle
import android.os.Parcelable
import android.text.Editable
import android.text.TextWatcher
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.*
import android.widget.*
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.textfield.TextInputLayout
import io.heckel.ntfy.R
import io.heckel.ntfy.app.Application
import io.heckel.ntfy.db.Subscription
import io.heckel.ntfy.msg.ApiService
import io.heckel.ntfy.util.*
import kotlinx.coroutines.Dispatchers
@ -31,6 +31,9 @@ class ShareActivity : AppCompatActivity() {
// Lazy-loaded things from Repository
private lateinit var baseUrls: List<String>
// Context-dependent things
private lateinit var appBaseUrl: String
// UI elements
private lateinit var menu: Menu
private lateinit var sendItem: MenuItem
@ -43,6 +46,7 @@ class ShareActivity : AppCompatActivity() {
private lateinit var baseUrlLayout: TextInputLayout
private lateinit var baseUrlText: AutoCompleteTextView
private lateinit var useAnotherServerCheckbox: CheckBox
private lateinit var lastTopicsList: RecyclerView
private lateinit var progress: ProgressBar
private lateinit var errorText: TextView
private lateinit var errorImage: ImageView
@ -60,6 +64,9 @@ class ShareActivity : AppCompatActivity() {
// Show 'Back' button
supportActionBar?.setDisplayHomeAsUpEnabled(true)
// Context-dependent things
appBaseUrl = getString(R.string.app_base_url)
// UI elements
contentText = findViewById(R.id.share_content_text)
contentImage = findViewById(R.id.share_content_image)
@ -73,6 +80,7 @@ class ShareActivity : AppCompatActivity() {
baseUrlText = findViewById(R.id.share_base_url_text)
//baseUrlText.background = topicText.background
useAnotherServerCheckbox = findViewById(R.id.share_use_another_server_checkbox)
lastTopicsList = findViewById(R.id.share_last_topics)
progress = findViewById(R.id.share_progress)
progress.visibility = View.GONE
errorText = findViewById(R.id.share_error_text)
@ -93,6 +101,7 @@ class ShareActivity : AppCompatActivity() {
}
contentText.addTextChangedListener(textWatcher)
topicText.addTextChangedListener(textWatcher)
baseUrlText.addTextChangedListener(textWatcher)
// Add behavior to "use another" checkbox
useAnotherServerCheckbox.setOnCheckedChangeListener { _, isChecked ->
@ -100,9 +109,25 @@ class ShareActivity : AppCompatActivity() {
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
lifecycleScope.launch(Dispatchers.IO) {
val appBaseUrl = getString(R.string.app_base_url)
baseUrls = repository.getSubscriptions()
.groupBy { it.baseUrl }
.map { it.key }
@ -111,14 +136,20 @@ class ShareActivity : AppCompatActivity() {
val activity = this@ShareActivity
activity.runOnUiThread {
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
val intent = intent ?: 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 {
const val TAG = "NtfyShareActivity"
}

View file

@ -44,6 +44,11 @@ fun shortUrl(url: String) = url
.replace("http://", "")
.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 {
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"?>
<androidx.constraintlayout.widget.ConstraintLayout
<ScrollView
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" android:layout_width="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"/>
android:layout_height="match_parent">
<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"
android:visibility="visible" android:layout_marginTop="5dp"
android:background="?android:attr/selectableItemBackground"
android:clickable="true" android:focusable="true" android:padding="4dp" android:paddingStart="0dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content" app:srcCompat="@drawable/ic_cancel_gray_24dp"
android:id="@+id/share_content_file_icon" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toStartOf="@+id/share_content_file_info" android:layout_marginEnd="5dp"
app:layout_constraintBottom_toBottomOf="parent"
/>
android:layout_height="wrap_content"
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:text="some file.mp3\n7.1 MB"
android:id="@+id/share_content_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:id="@+id/share_content_file_info"
android:textColor="?android:attr/textColorPrimary"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
app:layout_constraintStart_toEndOf="@+id/share_content_file_icon"
android:paddingTop="5dp"
android:paddingBottom="2dp"
android:text="@string/share_content_title"
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_constraintTop_toTopOf="@+id/share_content_file_icon"
app:layout_constraintBottom_toBottomOf="@+id/share_content_file_icon"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:id="@+id/share_topic_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:paddingTop="5dp"
android:paddingBottom="3dp"
android:text="@string/share_topic_title"
android:textAlignment="viewStart"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
app:layout_constraintStart_toStartOf="parent"
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"
android:layout_height="wrap_content" android:hint="@string/add_dialog_topic_name_hint"
android:importantForAutofill="no"
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:visibility="visible" android:layout_marginTop="5dp"
android:background="?android:attr/selectableItemBackground"
android:clickable="true" android:focusable="true" android:padding="4dp" android:paddingStart="0dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content" app:srcCompat="@drawable/ic_cancel_gray_24dp"
android:id="@+id/share_content_file_icon" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toStartOf="@+id/share_content_file_info" android:layout_marginEnd="5dp"
app:layout_constraintBottom_toBottomOf="parent"
/>
<TextView
android:text="some file.mp3\n7.1 MB"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:id="@+id/share_content_file_info"
android:textColor="?android:attr/textColorPrimary"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
app:layout_constraintStart_toEndOf="@+id/share_content_file_icon"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/share_content_file_icon"
app:layout_constraintBottom_toBottomOf="@+id/share_content_file_icon"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:id="@+id/share_topic_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:paddingTop="5dp"
android:paddingBottom="3dp"
android:text="@string/share_topic_title"
android:textAlignment="viewStart"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/share_content_file_box" android:layout_marginTop="15dp"/>
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/share_topic_text"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:hint="@string/add_dialog_topic_name_hint"
android:importantForAutofill="no"
android:maxLines="1" android:inputType="text|textNoSuggestions" android:maxLength="64"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/share_topic_title" android:layout_marginStart="-3dp"/>
<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_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:layout_margin="0dp"
android:padding="0dp"
android:visibility="visible"
app:endIconMode="custom"
app:hintEnabled="false"
app:boxBackgroundColor="@null"
app:layout_constraintEnd_toEndOf="parent"
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:paddingBottom="5dp"
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
android:textAppearance="?android:attr/textAppearanceMedium"
/>
</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:paddingBottom="3dp"
android:text="@string/share_previous_topics"
android:textAlignment="viewStart"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
android:paddingStart="2dp" app:layout_constraintTop_toBottomOf="@id/share_base_url_layout" app:layout_constraintStart_toStartOf="parent" android:layout_marginTop="15dp"/>
<LinearLayout
android:orientation="vertical"
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">
</LinearLayout>
<TextView
android:text="Unable to resolve host example.com"
android:layout_width="0dp"
android:layout_height="wrap_content" android:id="@+id/share_error_text"
android:paddingStart="4dp"
app:layout_constraintEnd_toEndOf="parent"
android:paddingEnd="4dp"
android:textAppearance="@style/DangerText"
app:layout_constraintStart_toEndOf="@id/share_error_image"
android:layout_marginTop="10dp" app:layout_constraintTop_toBottomOf="@id/share_last_title"/>
<ImageView
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"/>
android:paddingBottom="3dp"
android:text="@string/share_previous_topics"
android:textAlignment="viewStart"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
app:layout_constraintTop_toBottomOf="@id/share_base_url_layout" app:layout_constraintStart_toStartOf="parent" android:layout_marginTop="15dp"/>
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="0dp"
android:id="@+id/share_last_topics"
app:layout_constraintTop_toBottomOf="@id/share_last_title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:clickable="true"
android:focusable="true"
android:background="?android:attr/selectableItemBackground"
app:layoutManager="LinearLayoutManager"/>
<TextView
android:text="Unable to resolve host example.com"
android:layout_width="0dp"
android:layout_height="wrap_content" android:id="@+id/share_error_text"
android:paddingStart="4dp"
app:layout_constraintEnd_toEndOf="parent"
android:paddingEnd="4dp"
android:textAppearance="@style/DangerText"
app:layout_constraintStart_toEndOf="@id/share_error_image"
android:layout_marginTop="10dp" app:layout_constraintTop_toBottomOf="@id/share_last_topics"/>
<ImageView
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>