List unmatched tags as strings

This commit is contained in:
Philipp Heckel 2021-11-28 19:28:58 -05:00
parent 8fd7bb5639
commit 1148e08b96
11 changed files with 108 additions and 77 deletions

View file

@ -12,8 +12,8 @@ android {
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 30 targetSdkVersion 30
versionCode 8 versionCode 9
versionName "1.2.1" versionName "1.2.2"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

View file

@ -96,7 +96,7 @@ abstract class Database : RoomDatabase() {
private val MIGRATION_3_4 = object : Migration(3, 4) { private val MIGRATION_3_4 = object : Migration(3, 4) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("CREATE TABLE Notification_New (id TEXT NOT NULL, subscriptionId INTEGER NOT NULL, timestamp INTEGER NOT NULL, title TEXT NOT NULL, message TEXT NOT NULL, notificationId INTEGER NOT NULL, priority INTEGER NOT NULL, tags TEXT NOT NULL, deleted INTEGER NOT NULL, PRIMARY KEY(id, subscriptionId))") db.execSQL("CREATE TABLE Notification_New (id TEXT NOT NULL, subscriptionId INTEGER NOT NULL, timestamp INTEGER NOT NULL, title TEXT NOT NULL, message TEXT NOT NULL, notificationId INTEGER NOT NULL, priority INTEGER NOT NULL DEFAULT(3), tags TEXT NOT NULL, deleted INTEGER NOT NULL, PRIMARY KEY(id, subscriptionId))")
db.execSQL("INSERT INTO Notification_New SELECT id, subscriptionId, timestamp, '', message, notificationId, 3, '', deleted FROM Notification") db.execSQL("INSERT INTO Notification_New SELECT id, subscriptionId, timestamp, '', message, notificationId, 3, '', deleted FROM Notification")
db.execSQL("DROP TABLE Notification") db.execSQL("DROP TABLE Notification")
db.execSQL("ALTER TABLE Notification_New RENAME TO Notification") db.execSQL("ALTER TABLE Notification_New RENAME TO Notification")

View file

@ -326,9 +326,12 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
try { try {
val possibleTags = listOf("warning", "skull", "success", "triangular_flag_on_post", "de", "dog", "rotating_light", "cat", "bike") val possibleTags = listOf(
"warning", "skull", "success", "triangular_flag_on_post", "de", "dog", "rotating_light", "cat", "bike", // Emojis
"backup", "rsync", "de-server1", "this-is-a-tag"
)
val priority = Random.nextInt(1, 6) val priority = Random.nextInt(1, 6)
val tags = possibleTags.shuffled().take(Random.nextInt(0, 3)) val tags = possibleTags.shuffled().take(Random.nextInt(0, 4))
val title = if (Random.nextBoolean()) getString(R.string.detail_test_title) else "" val title = if (Random.nextBoolean()) getString(R.string.detail_test_title) else ""
val message = getString(R.string.detail_test_message, priority) val message = getString(R.string.detail_test_message, priority)
api.publish(subscriptionBaseUrl, subscriptionTopic, message, title, priority, tags) api.publish(subscriptionBaseUrl, subscriptionTopic, message, title, priority, tags)

View file

@ -1,5 +1,6 @@
package io.heckel.ntfy.ui package io.heckel.ntfy.ui
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -11,8 +12,7 @@ import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import io.heckel.ntfy.R import io.heckel.ntfy.R
import io.heckel.ntfy.data.Notification import io.heckel.ntfy.data.Notification
import io.heckel.ntfy.util.formatMessage import io.heckel.ntfy.util.*
import io.heckel.ntfy.util.formatTitle
import java.util.* import java.util.*
class DetailAdapter(private val onClick: (Notification) -> Unit, private val onLongClick: (Notification) -> Unit) : class DetailAdapter(private val onClick: (Notification) -> Unit, private val onLongClick: (Notification) -> Unit) :
@ -48,10 +48,14 @@ class DetailAdapter(private val onClick: (Notification) -> Unit, private val onL
private val titleView: TextView = itemView.findViewById(R.id.detail_item_title_text) private val titleView: TextView = itemView.findViewById(R.id.detail_item_title_text)
private val messageView: TextView = itemView.findViewById(R.id.detail_item_message_text) private val messageView: TextView = itemView.findViewById(R.id.detail_item_message_text)
private val newImageView: View = itemView.findViewById(R.id.detail_item_new_dot) private val newImageView: View = itemView.findViewById(R.id.detail_item_new_dot)
private val tagsView: TextView = itemView.findViewById(R.id.detail_item_tags)
fun bind(notification: Notification) { fun bind(notification: Notification) {
this.notification = notification this.notification = notification
val ctx = itemView.context
val unmatchedTags = unmatchedTags(splitTags(notification.tags))
dateView.text = Date(notification.timestamp * 1000).toString() dateView.text = Date(notification.timestamp * 1000).toString()
messageView.text = formatMessage(notification) messageView.text = formatMessage(notification)
newImageView.visibility = if (notification.notificationId == 0) View.GONE else View.VISIBLE newImageView.visibility = if (notification.notificationId == 0) View.GONE else View.VISIBLE
@ -63,10 +67,15 @@ class DetailAdapter(private val onClick: (Notification) -> Unit, private val onL
} else { } else {
titleView.visibility = View.GONE titleView.visibility = View.GONE
} }
if (unmatchedTags.isNotEmpty()) {
tagsView.visibility = View.VISIBLE
tagsView.text = ctx.getString(R.string.detail_item_tags, unmatchedTags.joinToString(", "))
} else {
tagsView.visibility = View.GONE
}
if (selected.contains(notification.id)) { if (selected.contains(notification.id)) {
itemView.setBackgroundResource(R.color.primarySelectedRowColor); itemView.setBackgroundResource(R.color.primarySelectedRowColor);
} }
val ctx = itemView.context
when (notification.priority) { when (notification.priority) {
1 -> { 1 -> {
priorityImageView.visibility = View.VISIBLE priorityImageView.visibility = View.VISIBLE

View file

@ -31,21 +31,31 @@ fun joinTags(tags: List<String>?): String {
return tags?.joinToString(",") ?: "" return tags?.joinToString(",") ?: ""
} }
fun toTags(tags: String?): String { fun splitTags(tags: String?): List<String> {
return tags ?: "" return if (tags == null || tags == "") {
emptyList()
} else {
tags.split(",")
}
} }
fun emojify(tags: List<String>): List<String> { fun toEmojis(tags: List<String>): List<String> {
return tags.mapNotNull { return tags.mapNotNull { tag -> toEmoji(tag) }
when (it.toLowerCase()) { }
fun toEmoji(tag: String): String? {
return when (tag.toLowerCase()) {
"warn", "warning" -> "\u26A0\uFE0F" "warn", "warning" -> "\u26A0\uFE0F"
"success" -> "\u2714\uFE0F" "success" -> "\u2714\uFE0F"
"failure" -> "\u274C" "failure" -> "\u274C"
else -> EmojiManager.getForAlias(it)?.unicode else -> EmojiManager.getForAlias(tag)?.unicode
}
} }
} }
fun unmatchedTags(tags: List<String>): List<String> {
return tags.filter { tag -> toEmoji(tag) == null }
}
/** /**
* Prepend tags/emojis to message, but only if there is a non-empty title. * Prepend tags/emojis to message, but only if there is a non-empty title.
* Otherwise the tags will be prepended to the title. * Otherwise the tags will be prepended to the title.
@ -54,7 +64,7 @@ fun formatMessage(notification: Notification): String {
return if (notification.title != "") { return if (notification.title != "") {
notification.message notification.message
} else { } else {
val emojis = emojify(notification.tags.split(",")) val emojis = toEmojis(splitTags(notification.tags))
if (emojis.isEmpty()) { if (emojis.isEmpty()) {
notification.message notification.message
} else { } else {
@ -76,7 +86,7 @@ fun formatTitle(subscription: Subscription, notification: Notification): String
} }
fun formatTitle(notification: Notification): String { fun formatTitle(notification: Notification): String {
val emojis = emojify(notification.tags.split(",")) val emojis = toEmojis(splitTags(notification.tags))
return if (emojis.isEmpty()) { return if (emojis.isEmpty()) {
notification.title notification.title
} else { } else {

View file

@ -37,10 +37,20 @@
android:textColor="@color/primaryTextColor" android:textColor="@color/primaryTextColor"
android:textAppearance="@style/TextAppearance.AppCompat.Medium" android:textAppearance="@style/TextAppearance.AppCompat.Medium"
app:layout_constraintTop_toBottomOf="@id/detail_item_title_text" app:layout_constraintTop_toBottomOf="@id/detail_item_title_text"
app:layout_constraintStart_toStartOf="parent" android:layout_marginStart="10dp"
app:layout_constraintEnd_toEndOf="parent" android:layout_marginEnd="10dp"
app:layout_constraintBottom_toTopOf="@+id/detail_item_tags"/>
<TextView
android:text="Tags: ssh, zfs"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:id="@+id/detail_item_tags"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="10dp" android:layout_marginBottom="10dp"
app:layout_constraintStart_toStartOf="parent" android:layout_marginStart="10dp" app:layout_constraintStart_toStartOf="parent" android:layout_marginStart="10dp"
app:layout_constraintEnd_toEndOf="parent" android:layout_marginEnd="10dp"/> app:layout_constraintEnd_toEndOf="parent" android:layout_marginEnd="10dp"
app:layout_constraintTop_toBottomOf="@id/detail_item_message_text"/>
<TextView <TextView
android:text="This is an optional title. It can also be a little longer but not too long." android:text="This is an optional title. It can also be a little longer but not too long."
android:layout_width="0dp" android:layout_width="0dp"

View file

@ -99,6 +99,7 @@
<string name="detail_instant_delivery_enabled">Instant delivery enabled</string> <string name="detail_instant_delivery_enabled">Instant delivery enabled</string>
<string name="detail_instant_delivery_disabled">Instant delivery disabled</string> <string name="detail_instant_delivery_disabled">Instant delivery disabled</string>
<string name="detail_instant_info">Instant delivery is enabled</string> <string name="detail_instant_info">Instant delivery is enabled</string>
<string name="detail_item_tags">Tags: %1$s</string>
<!-- Detail activity: Action bar --> <!-- Detail activity: Action bar -->
<string name="detail_menu_notifications_enabled">Notifications enabled</string> <string name="detail_menu_notifications_enabled">Notifications enabled</string>

View file

@ -7,9 +7,7 @@ import io.heckel.ntfy.R
import io.heckel.ntfy.app.Application import io.heckel.ntfy.app.Application
import io.heckel.ntfy.data.Notification import io.heckel.ntfy.data.Notification
import io.heckel.ntfy.msg.NotificationService import io.heckel.ntfy.msg.NotificationService
import io.heckel.ntfy.util.joinTags
import io.heckel.ntfy.util.toPriority import io.heckel.ntfy.util.toPriority
import io.heckel.ntfy.util.toTags
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -55,7 +53,7 @@ class FirebaseService : FirebaseMessagingService() {
message = message, message = message,
notificationId = Random.nextInt(), notificationId = Random.nextInt(),
priority = toPriority(priority), priority = toPriority(priority),
tags = toTags(tags), tags = tags ?: "",
deleted = false deleted = false
) )
val shouldNotify = repository.addNotification(notification) val shouldNotify = repository.addNotification(notification)

View file

@ -23,9 +23,9 @@
inkscape:pageopacity="0" inkscape:pageopacity="0"
inkscape:pagecheckerboard="0" inkscape:pagecheckerboard="0"
showgrid="false" showgrid="false"
inkscape:zoom="26.806921" inkscape:zoom="20.517358"
inkscape:cx="18.894374" inkscape:cx="22.834323"
inkscape:cy="14.026229" inkscape:cy="15.742767"
inkscape:window-width="1863" inkscape:window-width="1863"
inkscape:window-height="1025" inkscape:window-height="1025"
inkscape:window-x="57" inkscape:window-x="57"
@ -33,7 +33,15 @@
inkscape:window-maximized="1" inkscape:window-maximized="1"
inkscape:current-layer="svg1428" /> inkscape:current-layer="svg1428" />
<path <path
d="M 16.748063,0.46162067 H 7.1927317 L 0.44251841,7.211834 v 9.555331 l 6.75021329,6.750213 h 9.5553313 l 6.750213,-6.750213 V 7.211834 Z M 11.970397,18.778139 c -0.922231,0 -1.665138,-0.742908 -1.665138,-1.665138 0,-0.92223 0.742907,-1.665138 1.665138,-1.665138 0.92223,0 1.665138,0.742908 1.665138,1.665138 0,0.92223 -0.742908,1.665138 -1.665138,1.665138 z m 1.280875,-5.507765 H 10.689521 V 5.5851222 h 2.561751 z" style="color:#000000;fill:#aa0000;fill-opacity:1;stroke-width:0.0919748;stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none"
id="path29312" d="M 12.116784,3.40514 A 1.2747098,1.2747098 0 0 0 11.455179,3.5903463 L 4.8085864,7.6275238 A 1.2745823,1.2745823 0 0 0 4.3810494,9.3786313 1.2745823,1.2745823 0 0 0 6.1319775,9.8063489 L 12.116784,6.1710217 18.101593,9.8063489 A 1.2745823,1.2745823 0 0 0 19.85252,9.3786313 1.2745823,1.2745823 0 0 0 19.424984,7.6275238 L 12.778569,3.5903463 A 1.2747098,1.2747098 0 0 0 12.116784,3.40514 Z"
style="fill:#ac0000;fill-opacity:1;stroke-width:1.28088" /> id="rect3554" />
<path
style="color:#000000;fill:#c60000;fill-opacity:1;stroke:none;stroke-width:0.0919748;stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none"
d="M 12.116784,8.5394415 A 1.2747098,1.2747098 0 0 0 11.455179,8.724648 l -6.6465926,4.037176 a 1.2745823,1.2745823 0 0 0 -0.427537,1.751108 1.2745823,1.2745823 0 0 0 1.7509281,0.427717 l 5.9848065,-3.635327 5.984809,3.635327 A 1.2745823,1.2745823 0 0 0 19.85252,14.512932 1.2745823,1.2745823 0 0 0 19.424984,12.761824 L 12.778569,8.724648 A 1.2747098,1.2747098 0 0 0 12.116784,8.5394415 Z"
id="path9314" />
<path
style="color:#000000;fill:#de0000;fill-opacity:1;stroke:none;stroke-width:0.0919748;stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none"
d="m 12.195014,13.806679 a 1.2747098,1.2747098 0 0 0 -0.661606,0.185205 l -6.6465924,4.037177 a 1.2745823,1.2745823 0 0 0 -0.427537,1.751108 1.2745823,1.2745823 0 0 0 1.750928,0.427718 l 5.9848074,-3.635327 5.984807,3.635327 a 1.2745823,1.2745823 0 0 0 1.750928,-0.427718 1.2745823,1.2745823 0 0 0 -0.427537,-1.751108 l -6.646414,-4.037177 a 1.2747098,1.2747098 0 0 0 -0.661784,-0.185205 z"
id="path9316" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View file

@ -1,47 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="24px"
viewBox="0 0 24 24"
width="24px"
fill="#000000"
version="1.1"
id="svg1428"
sodipodi:docname="priority_5_24dp.svg"
inkscape:version="1.1.1 (3bf5ae0, 2021-09-20)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1432" />
<sodipodi:namedview
id="namedview1430"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:pageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
showgrid="false"
inkscape:zoom="20.517358"
inkscape:cx="22.834323"
inkscape:cy="15.742767"
inkscape:window-width="1863"
inkscape:window-height="1025"
inkscape:window-x="57"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="svg1428" />
<path
style="color:#000000;fill:#aa0000;fill-opacity:1;stroke-width:0.0919748;stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none"
d="M 12.116784,3.40514 A 1.2747098,1.2747098 0 0 0 11.455179,3.5903463 L 4.8085864,7.6275238 A 1.2745823,1.2745823 0 0 0 4.3810494,9.3786313 1.2745823,1.2745823 0 0 0 6.1319775,9.8063489 L 12.116784,6.1710217 18.101593,9.8063489 A 1.2745823,1.2745823 0 0 0 19.85252,9.3786313 1.2745823,1.2745823 0 0 0 19.424984,7.6275238 L 12.778569,3.5903463 A 1.2747098,1.2747098 0 0 0 12.116784,3.40514 Z"
id="rect3554" />
<path
style="color:#000000;fill:#c60000;fill-opacity:1;stroke:none;stroke-width:0.0919748;stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none"
d="M 12.116784,8.5394415 A 1.2747098,1.2747098 0 0 0 11.455179,8.724648 l -6.6465926,4.037176 a 1.2745823,1.2745823 0 0 0 -0.427537,1.751108 1.2745823,1.2745823 0 0 0 1.7509281,0.427717 l 5.9848065,-3.635327 5.984809,3.635327 A 1.2745823,1.2745823 0 0 0 19.85252,14.512932 1.2745823,1.2745823 0 0 0 19.424984,12.761824 L 12.778569,8.724648 A 1.2747098,1.2747098 0 0 0 12.116784,8.5394415 Z"
id="path9314" />
<path
style="color:#000000;fill:#de0000;fill-opacity:1;stroke:none;stroke-width:0.0919748;stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none"
d="m 12.195014,13.806679 a 1.2747098,1.2747098 0 0 0 -0.661606,0.185205 l -6.6465924,4.037177 a 1.2745823,1.2745823 0 0 0 -0.427537,1.751108 1.2745823,1.2745823 0 0 0 1.750928,0.427718 l 5.9848074,-3.635327 5.984807,3.635327 a 1.2745823,1.2745823 0 0 0 1.750928,-0.427718 1.2745823,1.2745823 0 0 0 -0.427537,-1.751108 l -6.646414,-4.037177 a 1.2747098,1.2747098 0 0 0 -0.661784,-0.185205 z"
id="path9316" />
</svg>

Before

Width:  |  Height:  |  Size: 2.7 KiB

View file

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="24px"
viewBox="0 0 24 24"
width="24px"
fill="#000000"
version="1.1"
id="svg1428"
sodipodi:docname="priority_5_24dp.svg"
inkscape:version="1.1.1 (3bf5ae0, 2021-09-20)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1432" />
<sodipodi:namedview
id="namedview1430"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:pageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
showgrid="false"
inkscape:zoom="26.806921"
inkscape:cx="18.894374"
inkscape:cy="14.026229"
inkscape:window-width="1863"
inkscape:window-height="1025"
inkscape:window-x="57"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="svg1428" />
<path
d="M 16.748063,0.46162067 H 7.1927317 L 0.44251841,7.211834 v 9.555331 l 6.75021329,6.750213 h 9.5553313 l 6.750213,-6.750213 V 7.211834 Z M 11.970397,18.778139 c -0.922231,0 -1.665138,-0.742908 -1.665138,-1.665138 0,-0.92223 0.742907,-1.665138 1.665138,-1.665138 0.92223,0 1.665138,0.742908 1.665138,1.665138 0,0.92223 -0.742908,1.665138 -1.665138,1.665138 z m 1.280875,-5.507765 H 10.689521 V 5.5851222 h 2.561751 z"
id="path29312"
style="fill:#ac0000;fill-opacity:1;stroke-width:1.28088" />
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB