From 62a81a556eae61d6d032d700e510b78b6e0d05d1 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 8 Jul 2019 10:26:22 +0200 Subject: [PATCH] Refresh notification drawer in a background thread. It also fixes the person and room avatar display --- .../features/notifications/BitmapLoader.kt | 72 +++---------------- .../features/notifications/IconLoader.kt | 72 +++---------------- .../NotificationDrawerManager.kt | 34 +++++---- 3 files changed, 42 insertions(+), 136 deletions(-) diff --git a/vector/src/main/java/im/vector/riotx/features/notifications/BitmapLoader.kt b/vector/src/main/java/im/vector/riotx/features/notifications/BitmapLoader.kt index d3506be7..02bf03bf 100644 --- a/vector/src/main/java/im/vector/riotx/features/notifications/BitmapLoader.kt +++ b/vector/src/main/java/im/vector/riotx/features/notifications/BitmapLoader.kt @@ -18,73 +18,38 @@ package im.vector.riotx.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) { +class BitmapLoader(val context: Context) { /** * Avatar Url -> Icon */ - private val cache = HashMap() - - // URLs to load - private val toLoad = HashSet() + private val cache = HashMap() // 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 */ + @WorkerThread 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 cache.getOrPut(path) { + loadRoomBitmap(path) } - - return null } @WorkerThread - private fun loadRoomBitmap(path: String) { + private fun loadRoomBitmap(path: String): Bitmap? { val bitmap = path.let { try { Glide.with(context) @@ -99,26 +64,11 @@ class BitmapLoader(val context: Context, } } - 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() - } - } + if (bitmap == null) { + // Add to the blacklist + blacklist.add(path) } - } - - interface BitmapLoaderListener { - fun onBitmapsLoaded() + return bitmap } -} \ No newline at end of file +} diff --git a/vector/src/main/java/im/vector/riotx/features/notifications/IconLoader.kt b/vector/src/main/java/im/vector/riotx/features/notifications/IconLoader.kt index 787ac604..6523ebc0 100644 --- a/vector/src/main/java/im/vector/riotx/features/notifications/IconLoader.kt +++ b/vector/src/main/java/im/vector/riotx/features/notifications/IconLoader.kt @@ -18,8 +18,6 @@ package im.vector.riotx.features.notifications import android.content.Context import android.os.Build -import android.os.Handler -import android.os.HandlerThread import androidx.annotation.WorkerThread import androidx.core.graphics.drawable.IconCompat import com.bumptech.glide.Glide @@ -27,67 +25,34 @@ import com.bumptech.glide.load.DecodeFormat import com.bumptech.glide.request.RequestOptions import timber.log.Timber -/** - * FIXME It works, but it does not refresh the notification, when it's already displayed - */ -class IconLoader(val context: Context, - val listener: IconLoaderListener) { +class IconLoader(val context: Context) { /** * Avatar Url -> Icon */ - private val cache = HashMap() - - // URLs to load - private val toLoad = HashSet() + private val cache = HashMap() // Black list of URLs (broken URL, etc.) private val blacklist = HashSet() - private var uiHandler = Handler() - - private val handlerThread: HandlerThread = HandlerThread("IconLoader", Thread.MIN_PRIORITY) - private var backgroundHandler: Handler - - init { - handlerThread.start() - backgroundHandler = Handler(handlerThread.looper) - } - /** * Get icon of a user. * If already in cache, use it, else load it and call IconLoaderListener.onIconsLoaded() when ready * Before Android P, this does nothing because the icon won't be used */ + @WorkerThread fun getUserIcon(path: String?): IconCompat? { if (path == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { 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 { - loadUserIcon(path) - } - } - } + return cache.getOrPut(path) { + loadUserIcon(path) } - - return null } @WorkerThread - private fun loadUserIcon(path: String) { + fun loadUserIcon(path: String): IconCompat? { val iconCompat = path.let { try { Glide.with(context) @@ -105,26 +70,11 @@ class IconLoader(val context: Context, } } - synchronized(cache) { - if (iconCompat == null) { - // Add to the blacklist - blacklist.add(path) - } else { - cache[path] = iconCompat - } - - toLoad.remove(path) - - if (toLoad.isEmpty()) { - uiHandler.post { - listener.onIconsLoaded() - } - } + if (iconCompat == null) { + // Add to the blacklist + blacklist.add(path) } - } - - interface IconLoaderListener { - fun onIconsLoaded() + return iconCompat } -} \ No newline at end of file +} diff --git a/vector/src/main/java/im/vector/riotx/features/notifications/NotificationDrawerManager.kt b/vector/src/main/java/im/vector/riotx/features/notifications/NotificationDrawerManager.kt index a704f672..2870b8ba 100644 --- a/vector/src/main/java/im/vector/riotx/features/notifications/NotificationDrawerManager.kt +++ b/vector/src/main/java/im/vector/riotx/features/notifications/NotificationDrawerManager.kt @@ -18,6 +18,9 @@ package im.vector.riotx.features.notifications import android.app.Notification import android.content.Context import android.graphics.Bitmap +import android.os.Handler +import android.os.HandlerThread +import androidx.annotation.WorkerThread import androidx.core.app.NotificationCompat import androidx.core.app.Person import im.vector.matrix.android.api.session.content.ContentUrlResolver @@ -44,6 +47,14 @@ class NotificationDrawerManager @Inject constructor(private val context: Context private val activeSessionHolder: ActiveSessionHolder, private val outdatedDetector: OutdatedEventDetector?) { + private val handlerThread: HandlerThread = HandlerThread("NotificationDrawerManager", Thread.MIN_PRIORITY) + private var backgroundHandler: Handler + + init { + handlerThread.start() + backgroundHandler = Handler(handlerThread.looper) + } + //The first time the notification drawer is refreshed, we force re-render of all notifications private var firstTime = true @@ -53,21 +64,9 @@ class NotificationDrawerManager @Inject constructor(private val context: Context private var currentRoomId: String? = null - private var iconLoader = IconLoader(context, - object : IconLoader.IconLoaderListener { - override fun onIconsLoaded() { - // Force refresh - refreshNotificationDrawer() - } - }) + private var iconLoader = IconLoader(context) - private var bitmapLoader = BitmapLoader(context, - object : BitmapLoader.BitmapLoaderListener { - override fun onBitmapsLoaded() { - // Force refresh - refreshNotificationDrawer() - } - }) + private var bitmapLoader = BitmapLoader(context) /** Should be called as soon as a new event is ready to be displayed. @@ -171,6 +170,13 @@ class NotificationDrawerManager @Inject constructor(private val context: Context fun refreshNotificationDrawer() { + backgroundHandler.post { + refreshNotificationDrawerBg() + } + } + + @WorkerThread + private fun refreshNotificationDrawerBg() { val session = activeSessionHolder.getActiveSession() val user = session.getUser(session.sessionParams.credentials.userId) val myUserDisplayName = user?.displayName ?: ""