Use Wholphin-specific dispatchers (#1583)

## Description
Switches all usages of `kotlinx.coroutines.Dispatchers` to
`WholphinDispatchers`.

`WholphinDispatchers` provides an easier and more reliable mechanism to
use a test dispatcher in unit tests. For regular app usage,
`WholphinDispatchers` just delegates to `Dispatchers`.

There are no user facing changes

### Related issues
None

### Testing
Basic emulator testing & unit tests

## Screenshots
N/A

## AI or LLM usage
None
This commit is contained in:
Damontecres 2026-06-18 11:17:19 -04:00 • committed by GitHub
parent ddbec0da73
commit e17ae3eb8a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
68 changed files with 252 additions and 226 deletions

View file

@ -1,7 +1,6 @@
package com.github.damontecres.wholphin.util
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.jellyfin.sdk.api.client.ApiClient
import org.jellyfin.sdk.api.client.ApiClientFactory
@ -21,7 +20,7 @@ import kotlin.coroutines.CoroutineContext
*/
class CoroutineContextApiClient(
private val client: ApiClient,
private val coroutineContext: CoroutineContext = Dispatchers.IO,
private val coroutineContext: CoroutineContext = WholphinDispatchers.IO,
) : ApiClient() {
override val baseUrl: String?
get() = client.baseUrl
@ -65,7 +64,7 @@ class CoroutineContextApiClient(
class CoroutineContextApiClientFactory(
private val factory: OkHttpFactory,
private val coroutineContext: CoroutineContext = Dispatchers.IO,
private val coroutineContext: CoroutineContext = WholphinDispatchers.IO,
) : ApiClientFactory,
SocketConnectionFactory {
override fun create(

View file

@ -7,7 +7,6 @@ import androidx.compose.runtime.setValue
import com.github.damontecres.wholphin.ui.DEFAULT_PAGE_SIZE
import com.google.common.cache.CacheBuilder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
@ -85,7 +84,7 @@ abstract class RequestPager<T>(
get() = totalCount
private fun fetchPage(position: Int): Job =
scope.launch(ExceptionHandler() + Dispatchers.IO) {
scope.launch(ExceptionHandler() + WholphinDispatchers.IO) {
fetchPageBlocking(position, false)
}

View file

@ -7,7 +7,6 @@ import com.github.damontecres.wholphin.data.model.ItemPlayback
import com.github.damontecres.wholphin.ui.launchIO
import com.github.damontecres.wholphin.ui.playback.CurrentPlayback
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.jellyfin.sdk.api.client.ApiClient
import org.jellyfin.sdk.api.client.extensions.playStateApi
@ -34,7 +33,7 @@ class TrackActivityPlaybackListener(
private val player: Player,
private val getState: () -> PlaybackItemState?,
) : Player.Listener {
private val coroutineScope = CoroutineScope(Dispatchers.Main)
private val coroutineScope = CoroutineScope(WholphinDispatchers.Main)
private val task: TimerTask =
object : TimerTask() {
override fun run() {
@ -57,7 +56,7 @@ class TrackActivityPlaybackListener(
PlaybackStartInfo(
canSeek = true,
itemId = state.itemId,
isPaused = withContext(Dispatchers.Main) { !player.isPlaying },
isPaused = withContext(WholphinDispatchers.Main) { !player.isPlaying },
playMethod = state.playMethod,
repeatMode = RepeatMode.REPEAT_NONE,
playbackOrder = PlaybackOrder.DEFAULT,
@ -117,11 +116,11 @@ class TrackActivityPlaybackListener(
launch("saveActivity") {
getState.invoke()?.let { state ->
val calcPosition =
withContext(Dispatchers.Main) {
withContext(WholphinDispatchers.Main) {
(if (position >= 0) position else player.currentPosition)
}
if (calcPosition > 0) {
val isPaused = withContext(Dispatchers.Main) { !player.isPlaying }
val isPaused = withContext(WholphinDispatchers.Main) { !player.isPlaying }
Timber.v("saveActivity: itemId=${state.itemId}, pos=$calcPosition")
api.playStateApi.reportPlaybackProgress(
PlaybackProgressInfo(

View file

@ -0,0 +1,20 @@
package com.github.damontecres.wholphin.util
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.MainCoroutineDispatcher
/**
* Analogous to [kotlinx.coroutines.Dispatchers], but easier to swap for testing
*/
object WholphinDispatchers {
@Suppress("ktlint:standard:property-naming")
val Main: MainCoroutineDispatcher get() = kotlinx.coroutines.Dispatchers.Main
@Suppress("ktlint:standard:property-naming")
var IO: CoroutineDispatcher = kotlinx.coroutines.Dispatchers.IO
internal set
@Suppress("ktlint:standard:property-naming")
var Default: CoroutineDispatcher = kotlinx.coroutines.Dispatchers.Default
internal set
}