From 778e15dd51a9917b97269bd38ffd5d050d855d4e Mon Sep 17 00:00:00 2001 From: Janne Koschinski <janne@kuschku.de> Date: Wed, 7 Nov 2018 22:23:16 +0100 Subject: [PATCH] Show user avatars in notifications on Android Pie --- .../service/QuasselNotificationBackend.kt | 68 +++++++++++++------ .../QuasseldroidNotificationManager.kt | 42 ++++++++---- .../kuschku/quasseldroid/service/SelfInfo.kt | 8 +++ .../quasseldroid/util/NotificationMessage.kt | 3 +- .../quasseldroid/util/avatars/AvatarHelper.kt | 4 +- app/src/main/res/values-de/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + .../persistence/QuasselDatabase.kt | 16 ++++- 8 files changed, 103 insertions(+), 40 deletions(-) create mode 100644 app/src/main/java/de/kuschku/quasseldroid/service/SelfInfo.kt diff --git a/app/src/main/java/de/kuschku/quasseldroid/service/QuasselNotificationBackend.kt b/app/src/main/java/de/kuschku/quasseldroid/service/QuasselNotificationBackend.kt index b095370ef..0295ca7fe 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/service/QuasselNotificationBackend.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/service/QuasselNotificationBackend.kt @@ -221,6 +221,7 @@ class QuasselNotificationBackend @Inject constructor( }.filter { it.messageId > session.bufferSyncer.lastSeenMsg(it.bufferInfo.bufferId) }.map { + val me = session.network(it.bufferInfo.networkId)?.me() QuasselDatabase.NotificationData( messageId = it.messageId, creationTime = now, @@ -235,7 +236,11 @@ class QuasselNotificationBackend @Inject constructor( senderPrefixes = it.senderPrefixes, realName = it.realName, avatarUrl = it.avatarUrl, - content = it.content + content = it.content, + ownNick = me?.nick() ?: "", + ownIdent = me?.user() ?: "", + ownRealName = me?.realName() ?: "", + ownAvatarUrl = "" ) } database.notifications().save(*results.toTypedArray()) @@ -279,18 +284,13 @@ class QuasselNotificationBackend @Inject constructor( networkId = it.networkId, groupId = 0 ) - val notificationData = data.map { - val nick = SpannableStringBuilder().apply { - append(contentFormatter.formatPrefix(it.senderPrefixes)) - append(contentFormatter.formatNick( - it.sender, - senderColors = senderColors, - selfColor = selfColor - )) - } - val content = contentFormatter.formatContent(it.content, false, it.networkId) - val nickName = HostmaskHelper.nick(it.sender) + val size = context.resources.getDimensionPixelSize(R.dimen.notification_avatar_width) + val radius = context.resources.getDimensionPixelSize(R.dimen.avatar_radius) + val unknownSenderLabel = context.getString(R.string.label_unknown_sender) + + fun obtainAvatar(nickName: String, ident: String, realName: String, avatarUrl: String, + self: Boolean): Drawable { val senderColorIndex = SenderColorUtil.senderColor(nickName) val rawInitial = nickName.trimStart(*EditorViewModel.IGNORED_CHARS) .firstOrNull() ?: nickName.firstOrNull() @@ -298,14 +298,12 @@ class QuasselNotificationBackend @Inject constructor( val senderColor = when (messageSettings.colorizeNicknames) { MessageSettings.ColorizeNicknamesMode.ALL -> senderColors[senderColorIndex] MessageSettings.ColorizeNicknamesMode.ALL_BUT_MINE -> - if (it.flag.hasFlag(Message_Flag.Self)) selfColor + if (self) selfColor else senderColors[senderColorIndex] MessageSettings.ColorizeNicknamesMode.NONE -> selfColor } - val size = context.resources.getDimensionPixelSize(R.dimen.notification_avatar_width) - val radius = context.resources.getDimensionPixelSize(R.dimen.avatar_radius) - val avatarList = AvatarHelper.avatar(messageSettings, it) + val avatarList = AvatarHelper.avatar(messageSettings, ident, realName, avatarUrl, size) val avatarResult = try { GlideApp.with(context).loadWithFallbacks(avatarList) ?.letIf(!messageSettings.squareAvatars, GlideRequest<Drawable>::optionalCircleCrop) @@ -320,22 +318,52 @@ class QuasselNotificationBackend @Inject constructor( } catch (_: Throwable) { null } - val avatar = avatarResult ?: TextDrawable.builder().beginConfig() + return avatarResult ?: TextDrawable.builder().beginConfig() .textColor((colorBackground and 0xFFFFFF) or (0x8A shl 24)).useFont(Typeface.DEFAULT_BOLD).endConfig().let { if (messageSettings.squareAvatars) it.buildRoundRect(initial, senderColor, radius) else it.buildRound(initial, senderColor) } + } + + val selfInfo = SelfInfo( + nick = if (it.ownNick.isNotEmpty()) it.ownNick else unknownSenderLabel, + avatar = obtainAvatar( + it.ownNick, + it.ownIdent, + it.ownRealName, + it.ownAvatarUrl, + true + ) + ) + + val notificationData = data.map { + val nick = SpannableStringBuilder().apply { + append(contentFormatter.formatPrefix(it.senderPrefixes)) + append(contentFormatter.formatNick( + it.sender, + senderColors = senderColors, + selfColor = selfColor + )) + } + val content = contentFormatter.formatContent(it.content, false, it.networkId) NotificationMessage( messageId = it.messageId, - sender = nick, + fullSender = it.sender, + sender = if (nick.isNotEmpty()) nick else unknownSenderLabel, content = content, time = it.time, - avatar = avatar + avatar = obtainAvatar( + HostmaskHelper.nick(it.sender), + HostmaskHelper.user(it.sender), + it.realName, + it.avatarUrl, + it.flag.hasFlag(Message_Flag.Self) + ) ) } val notification = notificationHandler.notificationMessage( - notificationSettings, bufferInfo, notificationData, isLoud, isConnected + notificationSettings, bufferInfo, selfInfo, notificationData, isLoud, isConnected ) notificationHandler.notify(notification) } ?: notificationHandler.remove(buffer) diff --git a/app/src/main/java/de/kuschku/quasseldroid/service/QuasseldroidNotificationManager.kt b/app/src/main/java/de/kuschku/quasseldroid/service/QuasseldroidNotificationManager.kt index f50291eff..ac7f01560 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/service/QuasseldroidNotificationManager.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/service/QuasseldroidNotificationManager.kt @@ -10,11 +10,11 @@ * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along - * with this program. If not, see <http://www.gnu.org/licenses/>. + * with this program. If not, see <http://www.gnu.org/licenses/>. */ package de.kuschku.quasseldroid.service @@ -33,7 +33,9 @@ import android.net.Uri import android.os.Build import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat +import androidx.core.app.Person import androidx.core.app.RemoteInput +import androidx.core.graphics.drawable.IconCompat import de.kuschku.libquassel.protocol.Buffer_Type import de.kuschku.libquassel.quassel.BufferInfo import de.kuschku.libquassel.util.flag.hasFlag @@ -108,8 +110,8 @@ class QuasseldroidNotificationManager @Inject constructor(private val context: C } fun notificationMessage(notificationSettings: NotificationSettings, bufferInfo: BufferInfo, - notifications: List<NotificationMessage>, isLoud: Boolean, - isConnected: Boolean): Handle { + selfInfo: SelfInfo, notifications: List<NotificationMessage>, + isLoud: Boolean, isConnected: Boolean): Handle { val pendingIntentOpen = PendingIntent.getActivity( context.applicationContext, System.currentTimeMillis().toInt(), @@ -187,17 +189,27 @@ class QuasseldroidNotificationManager @Inject constructor(private val context: C } .setCategory(NotificationCompat.CATEGORY_MESSAGE) .setPriority(NotificationCompat.PRIORITY_HIGH) - .setStyle(NotificationCompat.MessagingStyle("") - .setConversationTitle(bufferInfo.bufferName) - .also { - for (notification in notifications) { - it.addMessage( - notification.content, - notification.time.toEpochMilli(), - notification.sender - ) - } - } + .setStyle( + NotificationCompat.MessagingStyle( + Person.Builder() + .setKey("") + .setName(selfInfo.nick) + .setIcon(IconCompat.createWithBitmap(bitmapFromDrawable(selfInfo.avatar))) + .build() + ).setConversationTitle(bufferInfo.bufferName) + .also { + for (notification in notifications) { + it.addMessage( + notification.content, + notification.time.toEpochMilli(), + Person.Builder() + .setKey(notification.fullSender) + .setName(notification.sender) + .setIcon(IconCompat.createWithBitmap(bitmapFromDrawable(notification.avatar))) + .build() + ) + } + } ) .letIf(isConnected) { it.addAction(0, translatedLocale.getString(R.string.label_mark_read), markReadPendingIntent) diff --git a/app/src/main/java/de/kuschku/quasseldroid/service/SelfInfo.kt b/app/src/main/java/de/kuschku/quasseldroid/service/SelfInfo.kt new file mode 100644 index 000000000..ac974a028 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/service/SelfInfo.kt @@ -0,0 +1,8 @@ +package de.kuschku.quasseldroid.service + +import android.graphics.drawable.Drawable + +data class SelfInfo( + val nick: String, + val avatar: Drawable +) diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/NotificationMessage.kt b/app/src/main/java/de/kuschku/quasseldroid/util/NotificationMessage.kt index 3d0563758..807a5468a 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/util/NotificationMessage.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/util/NotificationMessage.kt @@ -25,8 +25,9 @@ import org.threeten.bp.Instant data class NotificationMessage( val messageId: MsgId, + val fullSender: String, val sender: CharSequence, val content: CharSequence, val time: Instant, - val avatar: Drawable? + val avatar: Drawable ) diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/avatars/AvatarHelper.kt b/app/src/main/java/de/kuschku/quasseldroid/util/avatars/AvatarHelper.kt index d88eb752d..140b2e421 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/util/avatars/AvatarHelper.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/util/avatars/AvatarHelper.kt @@ -35,8 +35,8 @@ import de.kuschku.quasseldroid.viewmodel.data.IrcUserItem import org.apache.commons.codec.digest.DigestUtils object AvatarHelper { - private fun avatar(settings: MessageSettings, ident: String, realName: String, - avatarUrl: String?, size: Int?) = + fun avatar(settings: MessageSettings, ident: String, realName: String, + avatarUrl: String?, size: Int?) = listOfNotNull( avatarUrl.notBlank()?.let { listOf(Avatar.NativeAvatar(it)) }, (settings.showAvatars && settings.showIRCCloudAvatars).letIf { diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index c7d039ca1..e364d5f50 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -100,6 +100,7 @@ <string name="label_topic">Kanal-Thema</string> <string name="label_translators">Übersetzer</string> <string name="label_unhide">Nicht mehr ausblenden</string> + <string name="label_unknown_sender"><Unbekannt></string> <string name="label_use_default">Standardwerte benutzen</string> <string name="label_website">Webseite</string> <string name="label_whitelist">Ignorieren</string> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6ab72eed0..0a942722b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -102,6 +102,7 @@ <string name="label_topic">Channel Topic</string> <string name="label_translators">Translators</string> <string name="label_unhide">Make Visible</string> + <string name="label_unknown_sender"><Unknown></string> <string name="label_use_default">Use Default</string> <string name="label_website">Website</string> <string name="label_whitelist">Ignore</string> diff --git a/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselDatabase.kt b/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselDatabase.kt index d624daaa4..1a585d567 100644 --- a/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselDatabase.kt +++ b/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselDatabase.kt @@ -32,7 +32,7 @@ import io.reactivex.Flowable import org.threeten.bp.Instant @Database(entities = [MessageData::class, Filtered::class, SslValidityWhitelistEntry::class, SslHostnameWhitelistEntry::class, NotificationData::class], - version = 16) + version = 17) @TypeConverters(MessageTypeConverter::class) abstract class QuasselDatabase : RoomDatabase() { abstract fun message(): MessageDao @@ -208,7 +208,11 @@ abstract class QuasselDatabase : RoomDatabase() { var senderPrefixes: String, var realName: String, var avatarUrl: String, - var content: String + var content: String, + var ownNick: String, + var ownIdent: String, + var ownRealName: String, + var ownAvatarUrl: String ) @Dao @@ -334,6 +338,14 @@ abstract class QuasselDatabase : RoomDatabase() { override fun migrate(database: SupportSQLiteDatabase) { database.execSQL("ALTER TABLE `notification` ADD `creationTime` INT DEFAULT 0 NOT NULL;") } + }, + object : Migration(16, 17) { + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE `notification` ADD `ownNick` TEXT DEFAULT '' NOT NULL;") + database.execSQL("ALTER TABLE `notification` ADD `ownIdent` TEXT DEFAULT '' NOT NULL;") + database.execSQL("ALTER TABLE `notification` ADD `ownRealName` TEXT DEFAULT '' NOT NULL;") + database.execSQL("ALTER TABLE `notification` ADD `ownAvatarUrl` TEXT DEFAULT '' NOT NULL;") + } } ).build() } -- GitLab