Skip to content

Commit

Permalink
Merge pull request #8813 from wmontwe/add-settings-change-notifier
Browse files Browse the repository at this point in the history
Add settings change notifier
  • Loading branch information
wmontwe authored Feb 13, 2025
2 parents 0065813 + 84c7678 commit 73628c1
Show file tree
Hide file tree
Showing 8 changed files with 169 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package com.fsck.k9.preferences

import app.k9mail.legacy.account.AccountManager
import app.k9mail.legacy.preferences.DefaultSettingsChangeBroker
import app.k9mail.legacy.preferences.GeneralSettingsManager
import app.k9mail.legacy.preferences.SettingsChangeBroker
import app.k9mail.legacy.preferences.SettingsChangePublisher
import com.fsck.k9.Preferences
import org.koin.core.qualifier.named
import org.koin.dsl.bind
import org.koin.dsl.binds
import org.koin.dsl.module

val preferencesModule = module {
Expand All @@ -24,12 +28,14 @@ val preferencesModule = module {
RealGeneralSettingsManager(
preferences = get(),
coroutineScope = get(named("AppCoroutineScope")),
changePublisher = get(),
)
} bind GeneralSettingsManager::class
single {
RealDrawerConfigManager(
preferences = get(),
coroutineScope = get(named("AppCoroutineScope")),
changeBroker = get(),
)
} bind DrawerConfigManager::class

Expand Down Expand Up @@ -77,4 +83,7 @@ val preferencesModule = module {
unifiedInboxConfigurator = get(),
)
}

single { DefaultSettingsChangeBroker() }
.binds(arrayOf(SettingsChangePublisher::class, SettingsChangeBroker::class))
}
Original file line number Diff line number Diff line change
@@ -1,36 +1,44 @@
package com.fsck.k9.preferences

import app.k9mail.feature.navigation.drawer.NavigationDrawerExternalContract.DrawerConfig
import app.k9mail.legacy.preferences.SettingsChangeBroker
import app.k9mail.legacy.preferences.SettingsChangeSubscriber
import com.fsck.k9.K9
import com.fsck.k9.Preferences
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.launch

internal class RealDrawerConfigManager(
private val preferences: Preferences,
private val coroutineScope: CoroutineScope,
private val changeBroker: SettingsChangeBroker,
) : DrawerConfigManager {
private val drawerConfigFlow = MutableSharedFlow<DrawerConfig>(replay = 1)
private var drawerConfig: DrawerConfig? = null

init {
coroutineScope.launch {
asSettingsFlow().collect { config ->
drawerConfigFlow.emit(config)
}
}
}

override fun save(config: DrawerConfig) {
saveDrawerConfig(config)
updateDrawerConfigFlow(config)
}

private fun loadDrawerConfig(): DrawerConfig {
val drawerConfig = DrawerConfig(
return DrawerConfig(
showAccountSelector = K9.isShowAccountSelector,
showStarredCount = K9.isShowStarredCount,
showUnifiedFolders = K9.isShowUnifiedInbox,
)

updateDrawerConfigFlow(drawerConfig)

return drawerConfig
}

private fun updateDrawerConfigFlow(config: DrawerConfig) {
Expand All @@ -41,14 +49,31 @@ internal class RealDrawerConfigManager(

@Synchronized
override fun getConfig(): DrawerConfig {
return drawerConfig ?: loadDrawerConfig().also { drawerConfig = it }
return loadDrawerConfig().also {
updateDrawerConfigFlow(it)
}
}

override fun getConfigFlow(): Flow<DrawerConfig> {
getConfig()
return drawerConfigFlow.distinctUntilChanged()
}

private fun asSettingsFlow(): Flow<DrawerConfig> {
return callbackFlow {
send(loadDrawerConfig())

val subscriber = SettingsChangeSubscriber {
drawerConfigFlow.tryEmit(loadDrawerConfig())
}

changeBroker.subscribe(subscriber)

awaitClose {
changeBroker.unsubscribe(subscriber)
}
}
}

@Synchronized
private fun saveDrawerConfig(config: DrawerConfig) {
val editor = preferences.createStorageEditor()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import app.k9mail.legacy.preferences.AppTheme
import app.k9mail.legacy.preferences.BackgroundSync
import app.k9mail.legacy.preferences.GeneralSettings
import app.k9mail.legacy.preferences.GeneralSettingsManager
import app.k9mail.legacy.preferences.SettingsChangePublisher
import app.k9mail.legacy.preferences.SubTheme
import com.fsck.k9.K9
import com.fsck.k9.Preferences
Expand All @@ -30,6 +31,7 @@ import timber.log.Timber
internal class RealGeneralSettingsManager(
private val preferences: Preferences,
private val coroutineScope: CoroutineScope,
private val changePublisher: SettingsChangePublisher,
private val backgroundDispatcher: CoroutineDispatcher = Dispatchers.IO,
) : GeneralSettingsManager {
private val settingsFlow = MutableSharedFlow<GeneralSettings>(replay = 1)
Expand Down Expand Up @@ -88,6 +90,8 @@ internal class RealGeneralSettingsManager(
K9.save(editor)
writeSettings(editor, settings)
editor.commit()

changePublisher.publish()
}

@Synchronized
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package app.k9mail.legacy.preferences

class DefaultSettingsChangeBroker(
private val subscribers: MutableSet<SettingsChangeSubscriber> = mutableSetOf(),
) : SettingsChangeBroker, SettingsChangePublisher {

private val lock = Any()

override fun subscribe(subscriber: SettingsChangeSubscriber) {
synchronized(lock) {
subscribers.add(subscriber)
}
}

override fun unsubscribe(subscriber: SettingsChangeSubscriber) {
synchronized(lock) {
subscribers.remove(subscriber)
}
}

override fun publish() {
val currentSubscribers = synchronized(lock) { HashSet(subscribers) }

for (subscriber in currentSubscribers) {
subscriber.receive()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package app.k9mail.legacy.preferences

/**
* Broker to manage subscribers and notify them about changes in the settings, when the
* [SettingsChangePublisher] publishes a change.
*/
interface SettingsChangeBroker {

/**
* Subscribe to settings changes.
*
* @param subscriber The subscriber to be notified about settings changes.
*/
fun subscribe(subscriber: SettingsChangeSubscriber)

/**
* Unsubscribe from settings changes.
*
* @param subscriber The subscriber that no longer wants to be notified about settings changes.
*/
fun unsubscribe(subscriber: SettingsChangeSubscriber)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package app.k9mail.legacy.preferences

/**
* Publishes changes in the settings.
*/
interface SettingsChangePublisher {

/**
* Publish a change in the settings.
*/
fun publish()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package app.k9mail.legacy.preferences

/**
* Subscribe to be notified about changes in the settings.
*/
fun interface SettingsChangeSubscriber {

/**
* Called when settings change.
*/
fun receive()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package app.k9mail.legacy.preferences

import assertk.assertThat
import assertk.assertions.contains
import assertk.assertions.doesNotContain
import assertk.assertions.isEqualTo
import kotlin.test.Test

class DefaultSettingsChangeBrokerTest {

@Test
fun `subscribe should add subscriber`() {
val subscriber = SettingsChangeSubscriber { }
val subscribers = mutableSetOf<SettingsChangeSubscriber>()
val broker = DefaultSettingsChangeBroker(subscribers)

broker.subscribe(subscriber)

assertThat(subscribers.size).isEqualTo(1)
assertThat(subscribers).contains(subscriber)
}

@Test
fun `unsubscribe should remove subscriber`() {
val subscriber = SettingsChangeSubscriber { }
val subscribers = mutableSetOf<SettingsChangeSubscriber>(subscriber)
val broker = DefaultSettingsChangeBroker(subscribers)

broker.unsubscribe(subscriber)

assertThat(subscribers.size).isEqualTo(0)
assertThat(subscribers).doesNotContain(subscriber)
}

@Test
fun `publish should notify subscribers`() {
var received = false
val subscriber = SettingsChangeSubscriber { received = true }
var receivedOther = false
val otherSubscriber = SettingsChangeSubscriber { receivedOther = true }
val subscribers = mutableSetOf<SettingsChangeSubscriber>(subscriber, otherSubscriber)
val broker = DefaultSettingsChangeBroker(subscribers)

broker.publish()

assertThat(received).isEqualTo(true)
assertThat(receivedOther).isEqualTo(true)
}
}

0 comments on commit 73628c1

Please sign in to comment.