lifting the persistence out of the notification state

This commit is contained in:
Adam Brown 2021-11-19 09:33:28 +00:00
parent 55fd362b3d
commit 7693f3bc6c
3 changed files with 43 additions and 39 deletions

View File

@ -38,12 +38,15 @@ import javax.inject.Singleton
* Events can be grouped into the same notification, old (already read) events can be removed to do some cleaning. * Events can be grouped into the same notification, old (already read) events can be removed to do some cleaning.
*/ */
@Singleton @Singleton
class NotificationDrawerManager @Inject constructor(private val context: Context, class NotificationDrawerManager @Inject constructor(
private val notificationDisplayer: NotificationDisplayer, private val context: Context,
private val vectorPreferences: VectorPreferences, private val notificationDisplayer: NotificationDisplayer,
private val activeSessionDataSource: ActiveSessionDataSource, private val vectorPreferences: VectorPreferences,
private val notifiableEventProcessor: NotifiableEventProcessor, private val activeSessionDataSource: ActiveSessionDataSource,
private val notificationRenderer: NotificationRenderer) { private val notifiableEventProcessor: NotifiableEventProcessor,
private val notificationRenderer: NotificationRenderer,
private val notificationEventPersistence: NotificationEventPersistence
) {
private val handlerThread: HandlerThread = HandlerThread("NotificationDrawerManager", Thread.MIN_PRIORITY) private val handlerThread: HandlerThread = HandlerThread("NotificationDrawerManager", Thread.MIN_PRIORITY)
private var backgroundHandler: Handler private var backgroundHandler: Handler
@ -55,7 +58,7 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
/** /**
* Lazily initializes the NotificationState as we rely on having a current session in order to fetch the persisted queue of events * Lazily initializes the NotificationState as we rely on having a current session in order to fetch the persisted queue of events
*/ */
private val notificationState by lazy { NotificationState.createInitialNotificationState(context, currentSession) } private val notificationState by lazy { createInitialNotificationState() }
private val avatarSize = context.resources.getDimensionPixelSize(R.dimen.profile_avatar_size) private val avatarSize = context.resources.getDimensionPixelSize(R.dimen.profile_avatar_size)
private var currentRoomId: String? = null private var currentRoomId: String? = null
private val firstThrottler = FirstThrottler(200) private val firstThrottler = FirstThrottler(200)
@ -67,6 +70,14 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
backgroundHandler = Handler(handlerThread.looper) backgroundHandler = Handler(handlerThread.looper)
} }
private fun createInitialNotificationState(): NotificationState {
val queuedEvents = notificationEventPersistence.loadEvents(currentSession, factory = { rawEvents ->
NotificationEventQueue(rawEvents.toMutableList(), seenEventIds = CircularCache.create(cacheSize = 25))
})
val renderedEvents = queuedEvents.rawEvents().map { ProcessedEvent(ProcessedEvent.Type.KEEP, it) }.toMutableList()
return NotificationState(queuedEvents, renderedEvents)
}
/** /**
Should be called as soon as a new event is ready to be displayed. 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 The notification corresponding to this event will not be displayed until
@ -161,7 +172,13 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
notificationState.clearAndAddRenderedEvents(eventsToRender) notificationState.clearAndAddRenderedEvents(eventsToRender)
val session = currentSession ?: return val session = currentSession ?: return
renderEvents(session, eventsToRender) renderEvents(session, eventsToRender)
notificationState.persist(context, session) persistEvents(session)
}
}
private fun persistEvents(session: Session) {
notificationState.queuedEvents { queuedEvents ->
notificationEventPersistence.persistEvents(queuedEvents, session)
} }
} }

View File

@ -21,14 +21,15 @@ import org.matrix.android.sdk.api.session.Session
import timber.log.Timber import timber.log.Timber
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
import javax.inject.Inject
// TODO Multi-account // TODO Multi-account
private const val ROOMS_NOTIFICATIONS_FILE_NAME = "im.vector.notifications.cache" private const val ROOMS_NOTIFICATIONS_FILE_NAME = "im.vector.notifications.cache"
private const val KEY_ALIAS_SECRET_STORAGE = "notificationMgr" private const val KEY_ALIAS_SECRET_STORAGE = "notificationMgr"
object NotificationEventPersistence { class NotificationEventPersistence @Inject constructor(private val context: Context) {
fun loadEvents(context: Context, currentSession: Session?, factory: (List<NotifiableEvent>) -> NotificationEventQueue): NotificationEventQueue { fun loadEvents(currentSession: Session?, factory: (List<NotifiableEvent>) -> NotificationEventQueue): NotificationEventQueue {
try { try {
val file = File(context.applicationContext.cacheDir, ROOMS_NOTIFICATIONS_FILE_NAME) val file = File(context.applicationContext.cacheDir, ROOMS_NOTIFICATIONS_FILE_NAME)
if (file.exists()) { if (file.exists()) {
@ -45,21 +46,19 @@ object NotificationEventPersistence {
return factory(emptyList()) return factory(emptyList())
} }
fun persistEvents(queuedEvents: NotificationEventQueue, context: Context, currentSession: Session) { fun persistEvents(queuedEvents: NotificationEventQueue, currentSession: Session) {
synchronized(queuedEvents) { if (queuedEvents.isEmpty()) {
if (queuedEvents.isEmpty()) { deleteCachedRoomNotifications(context)
deleteCachedRoomNotifications(context) return
return }
} try {
try { val file = File(context.applicationContext.cacheDir, ROOMS_NOTIFICATIONS_FILE_NAME)
val file = File(context.applicationContext.cacheDir, ROOMS_NOTIFICATIONS_FILE_NAME) if (!file.exists()) file.createNewFile()
if (!file.exists()) file.createNewFile() FileOutputStream(file).use {
FileOutputStream(file).use { currentSession.securelyStoreObject(queuedEvents.rawEvents(), KEY_ALIAS_SECRET_STORAGE, it)
currentSession.securelyStoreObject(queuedEvents.rawEvents(), KEY_ALIAS_SECRET_STORAGE, it)
}
} catch (e: Throwable) {
Timber.e(e, "## Failed to save cached notification info")
} }
} catch (e: Throwable) {
Timber.e(e, "## Failed to save cached notification info")
} }
} }

View File

@ -16,9 +16,6 @@
package im.vector.app.features.notifications package im.vector.app.features.notifications
import android.content.Context
import org.matrix.android.sdk.api.session.Session
class NotificationState( class NotificationState(
/** /**
* The notifiable events queued for rendering or currently rendered * The notifiable events queued for rendering or currently rendered
@ -53,18 +50,9 @@ class NotificationState(
fun hasAlreadyRendered(eventsToRender: List<ProcessedEvent<NotifiableEvent>>) = renderedEvents == eventsToRender fun hasAlreadyRendered(eventsToRender: List<ProcessedEvent<NotifiableEvent>>) = renderedEvents == eventsToRender
fun persist(context: Context, session: Session) { fun queuedEvents(block: (NotificationEventQueue) -> Unit) {
NotificationEventPersistence.persistEvents(queuedEvents, context, session) synchronized(queuedEvents) {
} block(queuedEvents)
companion object {
fun createInitialNotificationState(context: Context, currentSession: Session?): NotificationState {
val queuedEvents = NotificationEventPersistence.loadEvents(context, currentSession, factory = { rawEvents ->
NotificationEventQueue(rawEvents.toMutableList(), seenEventIds = CircularCache.create(cacheSize = 25))
})
val renderedEvents = queuedEvents.rawEvents().map { ProcessedEvent(ProcessedEvent.Type.KEEP, it) }.toMutableList()
return NotificationState(queuedEvents, renderedEvents)
} }
} }
} }