Skip to content

Commit 97a63fb

Browse files
committed
fix: keep jobs only for enabled widgets
1 parent ceafe3a commit 97a63fb

File tree

1 file changed

+99
-45
lines changed

1 file changed

+99
-45
lines changed

app/src/main/java/to/bitkit/repositories/WidgetsRepo.kt

Lines changed: 99 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@ package to.bitkit.repositories
22

33
import kotlinx.coroutines.CoroutineDispatcher
44
import kotlinx.coroutines.CoroutineScope
5+
import kotlinx.coroutines.Job
56
import kotlinx.coroutines.SupervisorJob
67
import kotlinx.coroutines.delay
78
import kotlinx.coroutines.flow.MutableStateFlow
89
import kotlinx.coroutines.flow.StateFlow
910
import kotlinx.coroutines.flow.asStateFlow
11+
import kotlinx.coroutines.flow.distinctUntilChanged
1012
import kotlinx.coroutines.flow.first
1113
import kotlinx.coroutines.flow.map
1214
import kotlinx.coroutines.flow.update
15+
import kotlinx.coroutines.isActive
1316
import kotlinx.coroutines.launch
1417
import kotlinx.coroutines.withContext
1518
import to.bitkit.data.SettingsStore
@@ -30,6 +33,7 @@ import to.bitkit.models.widget.HeadlinePreferences
3033
import to.bitkit.models.widget.PricePreferences
3134
import to.bitkit.models.widget.WeatherPreferences
3235
import to.bitkit.utils.Logger
36+
import java.util.concurrent.ConcurrentHashMap
3337
import javax.inject.Inject
3438
import javax.inject.Singleton
3539

@@ -45,6 +49,7 @@ class WidgetsRepo @Inject constructor(
4549
private val settingsStore: SettingsStore,
4650
) {
4751
private val repoScope = CoroutineScope(bgDispatcher + SupervisorJob())
52+
private val widgetJobs = ConcurrentHashMap<WidgetType, Job>()
4853

4954
val widgetsDataFlow = widgetsStore.data
5055
val showWidgetTitles = settingsStore.data.map { it.showWidgetTitles }
@@ -61,7 +66,99 @@ class WidgetsRepo @Inject constructor(
6166
val refreshStates: StateFlow<Map<WidgetType, Boolean>> = _refreshStates.asStateFlow()
6267

6368
init {
64-
startPeriodicUpdates()
69+
observeWidgetStateChanges()
70+
}
71+
72+
/**
73+
* Observe widget enable/disable changes and manage coroutine lifecycle
74+
*/
75+
private fun observeWidgetStateChanges() {
76+
repoScope.launch {
77+
widgetsDataFlow
78+
.map { it.widgets.map { widget -> widget.type }.toSet() }
79+
.distinctUntilChanged()
80+
.collect { enabledWidgetTypes ->
81+
updateWidgetJobs(enabledWidgetTypes)
82+
}
83+
}
84+
}
85+
86+
/**
87+
* Sync running jobs with enabled widgets
88+
* - Cancel jobs for disabled widgets
89+
* - Start jobs for newly enabled widgets
90+
*/
91+
private fun updateWidgetJobs(enabledWidgetTypes: Set<WidgetType>) {
92+
val widgetTypesWithServices = WidgetType.entries.filter {
93+
it != WidgetType.CALCULATOR
94+
}
95+
96+
widgetTypesWithServices.forEach { widgetType ->
97+
val isEnabled = widgetType in enabledWidgetTypes
98+
val hasRunningJob = widgetJobs.containsKey(widgetType) &&
99+
widgetJobs[widgetType]?.isActive == true
100+
101+
when {
102+
isEnabled && !hasRunningJob -> startWidgetRefresh(widgetType)
103+
!isEnabled && hasRunningJob -> stopWidgetRefresh(widgetType)
104+
}
105+
}
106+
}
107+
108+
/**
109+
* Start periodic refresh for a specific widget type
110+
*/
111+
private fun startWidgetRefresh(widgetType: WidgetType) {
112+
stopWidgetRefresh(widgetType)
113+
114+
val job = when (widgetType) {
115+
WidgetType.NEWS -> repoScope.launch {
116+
while (isActive) {
117+
updateWidget(newsService) { widgetsStore.updateArticles(it) }
118+
delay(newsService.refreshInterval)
119+
}
120+
}
121+
122+
WidgetType.FACTS -> repoScope.launch {
123+
while (isActive) {
124+
updateWidget(factsService) { widgetsStore.updateFacts(it) }
125+
delay(factsService.refreshInterval)
126+
}
127+
}
128+
129+
WidgetType.BLOCK -> repoScope.launch {
130+
while (isActive) {
131+
updateWidget(blocksService) { widgetsStore.updateBlock(it) }
132+
delay(blocksService.refreshInterval)
133+
}
134+
}
135+
136+
WidgetType.WEATHER -> repoScope.launch {
137+
while (isActive) {
138+
updateWidget(weatherService) { widgetsStore.updateWeather(it) }
139+
delay(weatherService.refreshInterval)
140+
}
141+
}
142+
143+
WidgetType.PRICE -> repoScope.launch {
144+
while (isActive) {
145+
updateWidget(priceService) { widgetsStore.updatePrice(it) }
146+
delay(priceService.refreshInterval)
147+
}
148+
}
149+
150+
WidgetType.CALCULATOR -> throw NotImplementedError("Calculator widget doesn't need a service")
151+
}
152+
153+
widgetJobs[widgetType] = job
154+
}
155+
156+
/**
157+
* Stop refresh coroutine for a specific widget type
158+
*/
159+
private fun stopWidgetRefresh(widgetType: WidgetType) {
160+
widgetJobs[widgetType]?.cancel()
161+
widgetJobs.remove(widgetType)
65162
}
66163

67164
suspend fun addWidget(type: WidgetType) = withContext(bgDispatcher) { widgetsStore.addWidget(type) }
@@ -94,55 +191,12 @@ class WidgetsRepo @Inject constructor(
94191

95192
suspend fun fetchAllPeriods() = withContext(bgDispatcher) { priceService.fetchAllPeriods() }
96193

97-
/**
98-
* Start periodic updates for all widgets
99-
*/
100-
private fun startPeriodicUpdates() {
101-
startPeriodicUpdate(newsService) { articles ->
102-
widgetsStore.updateArticles(articles)
103-
}
104-
startPeriodicUpdate(factsService) { facts ->
105-
widgetsStore.updateFacts(facts)
106-
}
107-
startPeriodicUpdate(blocksService) { block ->
108-
widgetsStore.updateBlock(block)
109-
}
110-
startPeriodicUpdate(weatherService) { weather ->
111-
widgetsStore.updateWeather(weather)
112-
}
113-
startPeriodicUpdate(priceService) { price ->
114-
widgetsStore.updatePrice(price)
115-
}
116-
}
117-
118-
/**
119-
* Generic method to start periodic updates for any widget service
120-
*/
121-
private fun <T> startPeriodicUpdate(
122-
service: WidgetService<T>,
123-
updateStore: suspend (T) -> Unit
124-
) {
125-
repoScope.launch {
126-
while (true) {
127-
val isEnabled = widgetsDataFlow.first().widgets.any {
128-
it.type == service.widgetType
129-
}
130-
131-
if (isEnabled) {
132-
updateWidget(service, updateStore)
133-
}
134-
135-
delay(service.refreshInterval)
136-
}
137-
}
138-
}
139-
140194
/**
141195
* Update a specific widget type
142196
*/
143197
private suspend fun <T> updateWidget(
144198
service: WidgetService<T>,
145-
updateStore: suspend (T) -> Unit
199+
updateStore: suspend (T) -> Unit,
146200
) {
147201
val widgetType = service.widgetType
148202
_refreshStates.update { it + (widgetType to true) }

0 commit comments

Comments
 (0)