Arbitrary attachments
This commit is contained in:
parent
8100e68b8d
commit
9afdf5e6e7
9 changed files with 134 additions and 53 deletions
|
@ -29,7 +29,7 @@ class ApiService {
|
|||
.build()
|
||||
private val parser = NotificationParser()
|
||||
|
||||
fun publish(baseUrl: String, topic: String, user: User?, message: String, title: String, priority: Int, tags: List<String>, delay: String, body: RequestBody? = null) {
|
||||
fun publish(baseUrl: String, topic: String, user: User?, message: String, title: String, priority: Int, tags: List<String>, delay: String, body: RequestBody? = null, filename: String = "") {
|
||||
val url = topicUrl(baseUrl, topic)
|
||||
Log.d(TAG, "Publishing to $url")
|
||||
|
||||
|
@ -46,6 +46,9 @@ class ApiService {
|
|||
if (delay.isNotEmpty()) {
|
||||
builder.addHeader("X-Delay", delay)
|
||||
}
|
||||
if (filename.isNotEmpty()) {
|
||||
builder.addHeader("X-Filename", filename)
|
||||
}
|
||||
if (body != null) {
|
||||
builder
|
||||
.addHeader("X-Message", message)
|
||||
|
|
|
@ -18,7 +18,7 @@ import io.heckel.ntfy.R
|
|||
import io.heckel.ntfy.app.Application
|
||||
import io.heckel.ntfy.db.*
|
||||
import io.heckel.ntfy.util.Log
|
||||
import io.heckel.ntfy.util.queryFilename
|
||||
import io.heckel.ntfy.util.fileName
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
|
@ -132,7 +132,7 @@ class DownloadWorker(private val context: Context, params: WorkerParameters) : W
|
|||
}
|
||||
}
|
||||
Log.d(TAG, "Attachment download: successful response, proceeding with download")
|
||||
val actualName = queryFilename(context, uri.toString(), attachment.name)
|
||||
val actualName = fileName(context, uri.toString(), attachment.name)
|
||||
save(attachment.copy(
|
||||
name = actualName,
|
||||
size = bytesCopied,
|
||||
|
|
|
@ -110,7 +110,6 @@ class AddFragment : DialogFragment() {
|
|||
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)
|
||||
|
|
|
@ -68,9 +68,9 @@ class DetailAdapter(private val activity: Activity, private val repository: Repo
|
|||
private val tagsView: TextView = itemView.findViewById(R.id.detail_item_tags_text)
|
||||
private val menuButton: ImageButton = itemView.findViewById(R.id.detail_item_menu_button)
|
||||
private val attachmentImageView: ImageView = itemView.findViewById(R.id.detail_item_attachment_image)
|
||||
private val attachmentBoxView: View = itemView.findViewById(R.id.detail_item_attachment_box)
|
||||
private val attachmentIconView: ImageView = itemView.findViewById(R.id.detail_item_attachment_icon)
|
||||
private val attachmentInfoView: TextView = itemView.findViewById(R.id.detail_item_attachment_info)
|
||||
private val attachmentBoxView: View = itemView.findViewById(R.id.share_content_file_box)
|
||||
private val attachmentIconView: ImageView = itemView.findViewById(R.id.share_content_file_icon)
|
||||
private val attachmentInfoView: TextView = itemView.findViewById(R.id.share_content_file_info)
|
||||
|
||||
fun bind(notification: Notification) {
|
||||
this.notification = notification
|
||||
|
@ -157,17 +157,7 @@ class DetailAdapter(private val activity: Activity, private val repository: Repo
|
|||
return
|
||||
}
|
||||
attachmentInfoView.text = formatAttachmentDetails(context, attachment, exists)
|
||||
attachmentIconView.setImageResource(if (attachment.type?.startsWith("image/") == true) {
|
||||
R.drawable.ic_file_image_red_24dp
|
||||
} else if (attachment.type?.startsWith("video/") == true) {
|
||||
R.drawable.ic_file_video_orange_24dp
|
||||
} else if (attachment.type?.startsWith("audio/") == true) {
|
||||
R.drawable.ic_file_audio_purple_24dp
|
||||
} else if ("application/vnd.android.package-archive" == attachment.type) {
|
||||
R.drawable.ic_file_app_gray_24dp
|
||||
} else {
|
||||
R.drawable.ic_file_document_blue_24dp
|
||||
})
|
||||
attachmentIconView.setImageResource(mimeTypeToIconResource(attachment.type))
|
||||
val attachmentBoxPopupMenu = createAttachmentPopup(context, attachmentBoxView, notification, attachment, exists) // Heavy lifting not during on-click
|
||||
if (attachmentBoxPopupMenu != null) {
|
||||
attachmentBoxView.setOnClickListener { attachmentBoxPopupMenu.show() }
|
||||
|
@ -275,7 +265,7 @@ class DetailAdapter(private val activity: Activity, private val repository: Repo
|
|||
}
|
||||
|
||||
private fun formatAttachmentDetails(context: Context, attachment: Attachment, exists: Boolean): String {
|
||||
val name = queryFilename(context, attachment.contentUri, attachment.name)
|
||||
val name = fileName(context, attachment.contentUri, attachment.name)
|
||||
val notYetDownloaded = !exists && attachment.progress == PROGRESS_NONE
|
||||
val downloading = !exists && attachment.progress in 0..99
|
||||
val deleted = !exists && (attachment.progress == PROGRESS_DONE || attachment.progress == PROGRESS_DELETED)
|
||||
|
|
|
@ -13,14 +13,13 @@ import android.view.View
|
|||
import android.widget.ImageView
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import io.heckel.ntfy.R
|
||||
import io.heckel.ntfy.app.Application
|
||||
import io.heckel.ntfy.msg.ApiService
|
||||
import io.heckel.ntfy.util.ContentUriRequestBody
|
||||
import io.heckel.ntfy.util.Log
|
||||
import io.heckel.ntfy.util.supportedImage
|
||||
import io.heckel.ntfy.util.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
@ -35,6 +34,9 @@ class ShareActivity : AppCompatActivity() {
|
|||
private lateinit var menu: Menu
|
||||
private lateinit var sendItem: MenuItem
|
||||
private lateinit var contentImage: ImageView
|
||||
private lateinit var contentFileBox: View
|
||||
private lateinit var contentFileInfo: TextView
|
||||
private lateinit var contentFileIcon: ImageView
|
||||
private lateinit var contentText: TextView
|
||||
private lateinit var topicText: TextView
|
||||
private lateinit var progress: ProgressBar
|
||||
|
@ -57,6 +59,9 @@ class ShareActivity : AppCompatActivity() {
|
|||
// UI elements
|
||||
contentText = findViewById(R.id.share_content_text)
|
||||
contentImage = findViewById(R.id.share_content_image)
|
||||
contentFileBox = findViewById(R.id.share_content_file_box)
|
||||
contentFileInfo = findViewById(R.id.share_content_file_info)
|
||||
contentFileIcon = findViewById(R.id.share_content_file_icon)
|
||||
topicText = findViewById(R.id.share_topic_text)
|
||||
progress = findViewById(R.id.share_progress)
|
||||
progress.visibility = View.GONE
|
||||
|
@ -93,8 +98,8 @@ class ShareActivity : AppCompatActivity() {
|
|||
|
||||
private fun handleSendText(intent: Intent) {
|
||||
intent.getStringExtra(Intent.EXTRA_TEXT)?.let { text ->
|
||||
contentImage.visibility = View.GONE
|
||||
contentText.text = text
|
||||
show()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -105,18 +110,33 @@ class ShareActivity : AppCompatActivity() {
|
|||
val bitmapStream = resolver.openInputStream(fileUri!!)
|
||||
val bitmap = BitmapFactory.decodeStream(bitmapStream)
|
||||
contentImage.setImageBitmap(bitmap)
|
||||
contentImage.visibility = View.VISIBLE
|
||||
contentText.text = getString(R.string.share_content_image_text)
|
||||
} catch (_: Exception) {
|
||||
show(image = true)
|
||||
} catch (e: Exception) {
|
||||
fileUri = null
|
||||
contentImage.visibility = View.GONE
|
||||
contentText.text = getString(R.string.share_content_image_error)
|
||||
contentText.text = ""
|
||||
errorText.text = getString(R.string.share_content_image_error, e.message)
|
||||
show(error = true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleSendFile(intent: Intent) {
|
||||
fileUri = intent.getParcelableExtra<Parcelable>(Intent.EXTRA_STREAM) as? Uri ?: return
|
||||
contentText.text = getString(R.string.share_content_file_text)
|
||||
try {
|
||||
val resolver = applicationContext.contentResolver
|
||||
val info = fileStat(this, fileUri)
|
||||
val mimeType = resolver.getType(fileUri!!)
|
||||
contentText.text = getString(R.string.share_content_file_text)
|
||||
contentFileInfo.text = "${info.filename}\n${formatBytes(info.size)}"
|
||||
contentFileIcon.setImageResource(mimeTypeToIconResource(mimeType))
|
||||
show(file = true)
|
||||
|
||||
} catch (e: Exception) {
|
||||
fileUri = null
|
||||
contentText.text = ""
|
||||
errorText.text = getString(R.string.share_content_file_error, e.message)
|
||||
show(error = true)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSupportNavigateUp(): Boolean {
|
||||
|
@ -142,6 +162,13 @@ class ShareActivity : AppCompatActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun show(image: Boolean = false, file: Boolean = false, error: Boolean = false) {
|
||||
contentImage.visibility = if (image) View.VISIBLE else View.GONE
|
||||
contentFileBox.visibility = if (file) View.VISIBLE else View.GONE
|
||||
errorImage.visibility = if (error) View.VISIBLE else View.GONE
|
||||
errorText.visibility = if (error) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
private fun onShareClick() {
|
||||
val baseUrl = "https://ntfy.sh" // FIXME
|
||||
val topic = topicText.text.toString()
|
||||
|
@ -150,6 +177,11 @@ class ShareActivity : AppCompatActivity() {
|
|||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val user = repository.getUser(baseUrl)
|
||||
try {
|
||||
val filename = if (fileUri != null) {
|
||||
fileStat(this@ShareActivity, fileUri).filename
|
||||
} else {
|
||||
""
|
||||
}
|
||||
val body = if (fileUri != null) {
|
||||
val resolver = applicationContext.contentResolver
|
||||
ContentUriRequestBody(resolver, fileUri!!)
|
||||
|
@ -165,10 +197,14 @@ class ShareActivity : AppCompatActivity() {
|
|||
priority = 3,
|
||||
tags = emptyList(),
|
||||
delay = "",
|
||||
body = body // May be null
|
||||
body = body, // May be null
|
||||
filename = filename // May be empty
|
||||
)
|
||||
runOnUiThread {
|
||||
finish()
|
||||
Toast
|
||||
.makeText(this@ShareActivity, getString(R.string.share_successful), Toast.LENGTH_LONG)
|
||||
.show()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
runOnUiThread {
|
||||
|
|
|
@ -12,6 +12,7 @@ import android.os.PowerManager
|
|||
import android.provider.OpenableColumns
|
||||
import android.view.Window
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import io.heckel.ntfy.R
|
||||
import io.heckel.ntfy.db.Notification
|
||||
import io.heckel.ntfy.db.Repository
|
||||
import io.heckel.ntfy.db.Subscription
|
||||
|
@ -129,7 +130,7 @@ fun formatTitle(notification: Notification): String {
|
|||
// Checks in the most horrible way if a content URI exists; I couldn't find a better way
|
||||
fun fileExists(context: Context, contentUri: String?): Boolean {
|
||||
return try {
|
||||
queryFilenameInternal(context, contentUri) // Throws if the file does not exist
|
||||
fileStat(context, Uri.parse(contentUri)) // Throws if the file does not exist
|
||||
true
|
||||
} catch (_: Exception) {
|
||||
false
|
||||
|
@ -137,25 +138,35 @@ fun fileExists(context: Context, contentUri: String?): Boolean {
|
|||
}
|
||||
|
||||
// Queries the filename of a content URI
|
||||
fun queryFilename(context: Context, contentUri: String?, fallbackName: String): String {
|
||||
fun fileName(context: Context, contentUri: String?, fallbackName: String): String {
|
||||
return try {
|
||||
queryFilenameInternal(context, contentUri)
|
||||
val info = fileStat(context, Uri.parse(contentUri))
|
||||
info.filename
|
||||
} catch (_: Exception) {
|
||||
fallbackName
|
||||
}
|
||||
}
|
||||
|
||||
fun queryFilenameInternal(context: Context, contentUri: String?): String {
|
||||
fun fileStat(context: Context, contentUri: Uri?): FileInfo {
|
||||
if (contentUri == null) throw Exception("URI is null")
|
||||
val resolver = context.applicationContext.contentResolver
|
||||
val cursor = resolver.query(Uri.parse(contentUri), null, null, null, null) ?: throw Exception("Query returned null")
|
||||
val cursor = resolver.query(contentUri, null, null, null, null) ?: throw Exception("Query returned null")
|
||||
return cursor.use { c ->
|
||||
val nameIndex = c.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME)
|
||||
val sizeIndex = c.getColumnIndexOrThrow(OpenableColumns.SIZE)
|
||||
c.moveToFirst()
|
||||
c.getString(nameIndex)
|
||||
FileInfo(
|
||||
filename = c.getString(nameIndex),
|
||||
size = c.getLong(sizeIndex)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data class FileInfo(
|
||||
val filename: String,
|
||||
val size: Long,
|
||||
)
|
||||
|
||||
// Status bar color fading to match action bar, see https://stackoverflow.com/q/51150077/1440785
|
||||
fun fadeStatusBarColor(window: Window, fromColor: Int, toColor: Int) {
|
||||
val statusBarColorAnimation = ValueAnimator.ofObject(ArgbEvaluator(), fromColor, toColor)
|
||||
|
@ -195,6 +206,20 @@ fun formatBytes(bytes: Long, decimals: Int = 1): String {
|
|||
return java.lang.String.format("%.${decimals}f %cB", value / 1024.0, ci.current())
|
||||
}
|
||||
|
||||
fun mimeTypeToIconResource(mimeType: String?): Int {
|
||||
return if (mimeType?.startsWith("image/") == true) {
|
||||
R.drawable.ic_file_image_red_24dp
|
||||
} else if (mimeType?.startsWith("video/") == true) {
|
||||
R.drawable.ic_file_video_orange_24dp
|
||||
} else if (mimeType?.startsWith("audio/") == true) {
|
||||
R.drawable.ic_file_audio_purple_24dp
|
||||
} else if (mimeType == "application/vnd.android.package-archive") {
|
||||
R.drawable.ic_file_app_gray_24dp
|
||||
} else {
|
||||
R.drawable.ic_file_document_blue_24dp
|
||||
}
|
||||
}
|
||||
|
||||
fun supportedImage(mimeType: String?): Boolean {
|
||||
return listOf("image/jpeg", "image/png").contains(mimeType)
|
||||
}
|
||||
|
@ -231,12 +256,10 @@ class ContentUriRequestBody(
|
|||
private val contentResolver: ContentResolver,
|
||||
private val contentUri: Uri
|
||||
) : RequestBody() {
|
||||
|
||||
override fun contentType(): MediaType? {
|
||||
val contentType = contentResolver.getType(contentUri)
|
||||
return contentType?.toMediaTypeOrNull()
|
||||
}
|
||||
|
||||
override fun writeTo(sink: BufferedSink) {
|
||||
val inputStream = contentResolver.openInputStream(contentUri) ?: throw IOException("Couldn't open content URI for reading")
|
||||
inputStream.source().use { source ->
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
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:padding="10dp">
|
||||
android:orientation="horizontal" android:paddingStart="15dp" android:paddingEnd="15dp" android:paddingTop="10dp" android:paddingBottom="10dp">
|
||||
|
||||
<ProgressBar
|
||||
style="?android:attr/progressBarStyle"
|
||||
|
@ -21,7 +21,7 @@
|
|||
android:paddingBottom="2dp"
|
||||
android:text="@string/share_content_title"
|
||||
android:textAlignment="viewStart"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium" android:paddingStart="4dp"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
|
@ -31,7 +31,7 @@
|
|||
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_marginStart="3dp"/>
|
||||
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"
|
||||
|
@ -39,6 +39,34 @@
|
|||
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"
|
||||
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"
|
||||
|
@ -47,9 +75,9 @@
|
|||
android:paddingBottom="3dp"
|
||||
android:text="Share to topic"
|
||||
android:textAlignment="viewStart"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium" android:paddingStart="4dp"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/share_content_text" android:layout_marginTop="10dp"/>
|
||||
app:layout_constraintTop_toBottomOf="@id/share_content_file_box" android:layout_marginTop="10dp"/>
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/share_topic_text"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
@ -87,13 +87,13 @@
|
|||
app:layout_constraintStart_toStartOf="parent" android:layout_marginStart="10dp"
|
||||
app:layout_constraintEnd_toEndOf="parent" android:layout_marginEnd="10dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/detail_item_attachment_image"
|
||||
app:layout_constraintBottom_toTopOf="@id/detail_item_attachment_box"
|
||||
app:layout_constraintBottom_toTopOf="@id/share_content_file_box"
|
||||
app:layout_constraintHorizontal_bias="0.0" android:layout_marginTop="2dp"
|
||||
/>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" app:layout_constraintTop_toBottomOf="@id/detail_item_tags_text"
|
||||
android:id="@+id/detail_item_attachment_box" app:layout_constraintStart_toStartOf="parent"
|
||||
android:id="@+id/share_content_file_box" app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" android:layout_marginStart="10dp" android:layout_marginEnd="10dp"
|
||||
app:layout_constraintBottom_toTopOf="@id/detail_item_padding_bottom"
|
||||
android:visibility="visible" android:layout_marginTop="2dp"
|
||||
|
@ -102,27 +102,27 @@
|
|||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" app:srcCompat="@drawable/ic_cancel_gray_24dp"
|
||||
android:id="@+id/detail_item_attachment_icon" app:layout_constraintStart_toStartOf="parent"
|
||||
android:id="@+id/share_content_file_icon" app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/detail_item_attachment_info" android:layout_marginEnd="5dp"
|
||||
app:layout_constraintEnd_toStartOf="@+id/share_content_file_info" android:layout_marginEnd="5dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
/>
|
||||
<TextView
|
||||
android:text="attachment.jpg\n58 MB, not downloaded, expires 1/2/2022 10:30 PM"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/detail_item_attachment_info"
|
||||
android:id="@+id/share_content_file_info"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||
app:layout_constraintStart_toEndOf="@+id/detail_item_attachment_icon"
|
||||
app:layout_constraintStart_toEndOf="@+id/share_content_file_icon"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/detail_item_attachment_icon"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/detail_item_attachment_icon"/>
|
||||
app:layout_constraintTop_toTopOf="@+id/share_content_file_icon"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/share_content_file_icon"/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="5dp" android:id="@+id/detail_item_padding_bottom"
|
||||
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/detail_item_attachment_box"/>
|
||||
app:layout_constraintTop_toBottomOf="@id/share_content_file_box"/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
|
|
@ -191,8 +191,10 @@
|
|||
<string name="share_content_title">Message preview</string>
|
||||
<string name="share_content_text_hint">Add the content you\'d like to share here</string>
|
||||
<string name="share_content_image_text">An image was shared with you</string>
|
||||
<string name="share_content_image_error">Cannot read image</string>
|
||||
<string name="share_content_file_text">An file was shared with you</string>
|
||||
<string name="share_content_image_error">Cannot read image: %1$s</string>
|
||||
<string name="share_content_file_text">A file was shared with you</string>
|
||||
<string name="share_content_file_error">Cannot read file infos: %1$s</string>
|
||||
<string name="share_successful">Message successfully published</string>
|
||||
|
||||
<!-- Notification dialog -->
|
||||
<string name="notification_dialog_title">Pause notifications</string>
|
||||
|
|
Loading…
Reference in a new issue