From 9fa3a75fb6b40e40440018460948e4fce9051391 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Jun 2019 17:31:53 +0200 Subject: [PATCH] Notification: display room avatar --- .../features/notifications/BitmapLoader.kt | 124 ++++++++++++++++++ .../NotificationDrawerManager.kt | 25 ++-- .../notifications/RoomEventGroupInfo.kt | 1 - 3 files changed, 135 insertions(+), 15 deletions(-) create mode 100644 vector/src/main/java/im/vector/riotredesign/features/notifications/BitmapLoader.kt diff --git a/vector/src/main/java/im/vector/riotredesign/features/notifications/BitmapLoader.kt b/vector/src/main/java/im/vector/riotredesign/features/notifications/BitmapLoader.kt new file mode 100644 index 00000000..9a52ea5d --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/features/notifications/BitmapLoader.kt @@ -0,0 +1,124 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.riotredesign.features.notifications + +import android.content.Context +import android.graphics.Bitmap +import android.os.Handler +import android.os.HandlerThread +import androidx.annotation.WorkerThread +import com.bumptech.glide.Glide +import com.bumptech.glide.load.DecodeFormat +import timber.log.Timber + +/** + * FIXME It works, but it does not refresh the notification, when it's already displayed + */ +class BitmapLoader(val context: Context, + val listener: BitmapLoaderListener) { + + /** + * Avatar Url -> Icon + */ + private val cache = HashMap() + + // URLs to load + private val toLoad = HashSet() + + // Black list of URLs (broken URL, etc.) + private val blacklist = HashSet() + + private var uiHandler = Handler() + + private val handlerThread: HandlerThread = HandlerThread("BitmapLoader", Thread.MIN_PRIORITY) + private var backgroundHandler: Handler + + init { + handlerThread.start() + backgroundHandler = Handler(handlerThread.looper) + } + + /** + * Get icon of a room. + * If already in cache, use it, else load it and call BitmapLoaderListener.onBitmapsLoaded() when ready + */ + fun getRoomBitmap(path: String?): Bitmap? { + if (path == null) { + return null + } + + synchronized(cache) { + if (cache[path] != null) { + return cache[path] + } + + // Add to the queue, if not blacklisted + if (!blacklist.contains(path)) { + if (toLoad.contains(path)) { + // Wait + } else { + toLoad.add(path) + + backgroundHandler.post { + loadRoomBitmap(path) + } + } + } + } + + return null + } + + @WorkerThread + private fun loadRoomBitmap(path: String) { + val bitmap = path.let { + try { + Glide.with(context) + .asBitmap() + .load(path) + .format(DecodeFormat.PREFER_ARGB_8888) + .submit() + .get() + } catch (e: Exception) { + Timber.e(e, "decodeFile failed") + null + } + } + + synchronized(cache) { + if (bitmap == null) { + // Add to the blacklist + blacklist.add(path) + } else { + cache[path] = bitmap + } + + toLoad.remove(path) + + if (toLoad.isEmpty()) { + uiHandler.post { + listener.onBitmapsLoaded() + } + } + } + } + + + interface BitmapLoaderListener { + fun onBitmapsLoaded() + } +} \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/features/notifications/NotificationDrawerManager.kt b/vector/src/main/java/im/vector/riotredesign/features/notifications/NotificationDrawerManager.kt index fe477642..67c4aa85 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/notifications/NotificationDrawerManager.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/notifications/NotificationDrawerManager.kt @@ -18,8 +18,6 @@ package im.vector.riotredesign.features.notifications import android.app.Notification import android.content.Context import android.graphics.Bitmap -import android.graphics.BitmapFactory -import android.text.TextUtils import androidx.core.app.NotificationCompat import androidx.core.app.Person import im.vector.matrix.android.api.Matrix @@ -59,6 +57,14 @@ class NotificationDrawerManager(val context: Context, } }) + private var bitmapLoader = BitmapLoader(context, + object : BitmapLoader.BitmapLoaderListener { + override fun onBitmapsLoaded() { + // Force refresh + refreshNotificationDrawer() + } + }) + /** Should be called as soon as a new event is ready to be displayed. The notification corresponding to this event will not be displayed until @@ -372,19 +378,10 @@ class NotificationDrawerManager(val context: Context, if (events.isEmpty()) return null //Use the last event (most recent?) - val roomAvatarPath = events[events.size - 1].roomAvatarPath - ?: events[events.size - 1].senderAvatarPath - if (!TextUtils.isEmpty(roomAvatarPath)) { - val options = BitmapFactory.Options() - options.inPreferredConfig = Bitmap.Config.ARGB_8888 - try { - return BitmapFactory.decodeFile(roomAvatarPath, options) - } catch (oom: OutOfMemoryError) { - Timber.e(oom, "decodeFile failed with an oom") - } + val roomAvatarPath = events.last().roomAvatarPath + ?: events.last().senderAvatarPath - } - return null + return bitmapLoader.getRoomBitmap(roomAvatarPath) } private fun shouldIgnoreMessageEventInRoom(roomId: String?): Boolean { diff --git a/vector/src/main/java/im/vector/riotredesign/features/notifications/RoomEventGroupInfo.kt b/vector/src/main/java/im/vector/riotredesign/features/notifications/RoomEventGroupInfo.kt index 0256ded6..eac6e4d9 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/notifications/RoomEventGroupInfo.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/notifications/RoomEventGroupInfo.kt @@ -22,7 +22,6 @@ package im.vector.riotredesign.features.notifications data class RoomEventGroupInfo( val roomId: String, val roomDisplayName: String = "", - val roomAvatarPath: String? = null, val isDirect: Boolean = false ) { // An event in the list has not yet been display