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
targetSdkVersion 30
versionCode 8
versionName "1.2.1"
versionCode 9
versionName "1.2.2"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

View file

@ -96,7 +96,7 @@ abstract class Database : RoomDatabase() {
private val MIGRATION_3_4 = object : Migration(3, 4) {
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("DROP TABLE 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) {
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 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 message = getString(R.string.detail_test_message, priority)
api.publish(subscriptionBaseUrl, subscriptionTopic, message, title, priority, tags)

View file

@ -1,5 +1,6 @@
package io.heckel.ntfy.ui
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@ -11,8 +12,7 @@ import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import io.heckel.ntfy.R
import io.heckel.ntfy.data.Notification
import io.heckel.ntfy.util.formatMessage
import io.heckel.ntfy.util.formatTitle
import io.heckel.ntfy.util.*
import java.util.*
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 messageView: TextView = itemView.findViewById(R.id.detail_item_message_text)
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) {
this.notification = notification
val ctx = itemView.context
val unmatchedTags = unmatchedTags(splitTags(notification.tags))
dateView.text = Date(notification.timestamp * 1000).toString()
messageView.text = formatMessage(notification)
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 {
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)) {
itemView.setBackgroundResource(R.color.primarySelectedRowColor);
}
val ctx = itemView.context
when (notification.priority) {
1 -> {
priorityImageView.visibility = View.VISIBLE

View file

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

View file

@ -37,10 +37,20 @@
android:textColor="@color/primaryTextColor"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
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"
android:layout_marginBottom="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
android:text="This is an optional title. It can also be a little longer but not too long."
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_disabled">Instant delivery disabled</string>
<string name="detail_instant_info">Instant delivery is enabled</string>
<string name="detail_item_tags">Tags: %1$s</string>
<!-- Detail activity: Action bar -->
<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.data.Notification
import io.heckel.ntfy.msg.NotificationService
import io.heckel.ntfy.util.joinTags
import io.heckel.ntfy.util.toPriority
import io.heckel.ntfy.util.toTags
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
@ -55,7 +53,7 @@ class FirebaseService : FirebaseMessagingService() {
message = message,
notificationId = Random.nextInt(),
priority = toPriority(priority),
tags = toTags(tags),
tags = tags ?: "",
deleted = false
)
val shouldNotify = repository.addNotification(notification)

View file

@ -23,9 +23,9 @@
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
showgrid="false"
inkscape:zoom="26.806921"
inkscape:cx="18.894374"
inkscape:cy="14.026229"
inkscape:zoom="20.517358"
inkscape:cx="22.834323"
inkscape:cy="15.742767"
inkscape:window-width="1863"
inkscape:window-height="1025"
inkscape:window-x="57"
@ -33,7 +33,15 @@
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" />
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: 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