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

@ -60,10 +60,10 @@ import com.github.damontecres.wholphin.ui.theme.WholphinTheme
import com.github.damontecres.wholphin.ui.util.ProvideLocalClock
import com.github.damontecres.wholphin.util.DebugLogTree
import com.github.damontecres.wholphin.util.ExceptionHandler
import com.github.damontecres.wholphin.util.WholphinDispatchers
import dagger.hilt.android.AndroidEntryPoint
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.first
@ -170,7 +170,7 @@ class MainActivity : AppCompatActivity() {
viewModel.serverRepository.currentUserFlow
.onEach { user ->
withContext(Dispatchers.Main) {
withContext(WholphinDispatchers.Main) {
if (user?.hasPin == true) {
window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
} else {
@ -184,7 +184,7 @@ class MainActivity : AppCompatActivity() {
screensaverService.keepScreenOn
.onEach { keepScreenOn ->
Timber.v("keepScreenOn: %s", keepScreenOn)
withContext(Dispatchers.Main) {
withContext(WholphinDispatchers.Main) {
if (keepScreenOn) {
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
} else {
@ -383,7 +383,7 @@ class MainActivity : AppCompatActivity() {
}
fun changeDisplayMode(modeId: Int) {
lifecycleScope.launch(Dispatchers.Main + ExceptionHandler(autoToast = true)) {
lifecycleScope.launch(WholphinDispatchers.Main + ExceptionHandler(autoToast = true)) {
val attrs = window.attributes
if (attrs.preferredDisplayModeId != modeId) {
Timber.d("Switch preferredDisplayModeId to %s", modeId)

View file

@ -7,7 +7,7 @@ import com.github.damontecres.wholphin.data.model.PlaybackLanguageChoice
import com.github.damontecres.wholphin.data.model.TrackIndex
import com.github.damontecres.wholphin.preferences.UserPreferences
import com.github.damontecres.wholphin.services.StreamChoiceService
import kotlinx.coroutines.Dispatchers
import com.github.damontecres.wholphin.util.WholphinDispatchers
import kotlinx.coroutines.withContext
import org.jellyfin.sdk.model.api.MediaSourceInfo
import org.jellyfin.sdk.model.api.MediaStream
@ -108,7 +108,7 @@ class ItemPlaybackRepository
itemId: UUID,
sourceId: UUID,
): ItemPlayback? =
withContext(Dispatchers.IO) {
withContext(WholphinDispatchers.IO) {
serverRepository.currentUser?.let { user ->
val itemPlayback =
ItemPlayback(

View file

@ -9,9 +9,9 @@ import com.github.damontecres.wholphin.data.model.JellyfinUser
import com.github.damontecres.wholphin.preferences.AppPreferences
import com.github.damontecres.wholphin.services.hilt.IoDispatcher
import com.github.damontecres.wholphin.ui.toServerString
import com.github.damontecres.wholphin.util.WholphinDispatchers
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@ -109,7 +109,7 @@ class ServerRepository
}.build()
}
val currentUser = CurrentUser(updatedServer, updatedUser)
withContext(Dispatchers.Main) {
withContext(WholphinDispatchers.Main) {
_current.value = currentUser
_currentUserDto.value = userDto
}
@ -222,7 +222,7 @@ class ServerRepository
suspend fun removeUser(user: JellyfinUser) {
if (current.value?.user?.id == user.id) {
withContext(Dispatchers.Main) {
withContext(WholphinDispatchers.Main) {
_current.value = null
}
userPreferencesDataStore.updateData {
@ -241,7 +241,7 @@ class ServerRepository
suspend fun removeServer(server: JellyfinServer) {
if (current.value?.server?.id == server.id) {
withContext(Dispatchers.Main) {
withContext(WholphinDispatchers.Main) {
_current.value = null
}
userPreferencesDataStore.updateData {

View file

@ -18,8 +18,8 @@ import com.github.damontecres.wholphin.data.model.BaseItem
import com.github.damontecres.wholphin.data.model.DiscoverItem
import com.github.damontecres.wholphin.preferences.AppPreferences
import com.github.damontecres.wholphin.preferences.BackdropStyle
import com.github.damontecres.wholphin.util.WholphinDispatchers
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
@ -55,7 +55,7 @@ class BackdropService
* Update the backdrop to use the specified item
*/
suspend fun submit(item: BaseItem) =
withContext(Dispatchers.IO) {
withContext(WholphinDispatchers.IO) {
val imageUrl =
if (item.type == BaseItemKind.GENRE) {
item.imageUrlOverride
@ -76,7 +76,7 @@ class BackdropService
suspend fun submit(
itemId: String,
imageUrl: String?,
) = withContext(Dispatchers.IO) {
) = withContext(WholphinDispatchers.IO) {
if (backdropFlow.firstOrNull()?.imageUrl != imageUrl) {
_backdropFlow.update {
it.copy(
@ -132,7 +132,7 @@ class BackdropService
}
suspend fun extractColorsFromBackdrop(imageUrl: String?): ExtractedColors =
withContext(Dispatchers.IO) {
withContext(WholphinDispatchers.IO) {
if (imageUrl.isNullOrBlank()) {
return@withContext ExtractedColors.DEFAULT
}

View file

@ -9,12 +9,12 @@ import com.github.damontecres.wholphin.preferences.AppPreference
import com.github.damontecres.wholphin.services.hilt.IoCoroutineScope
import com.github.damontecres.wholphin.ui.collectLatestIn
import com.github.damontecres.wholphin.util.GetEpisodesRequestHandler
import com.github.damontecres.wholphin.util.WholphinDispatchers
import com.google.common.cache.CacheBuilder
import dagger.hilt.android.qualifiers.ActivityContext
import dagger.hilt.android.scopes.ActivityScoped
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.withContext
import org.jellyfin.sdk.api.client.ApiClient
@ -56,7 +56,7 @@ class DatePlayedService
adjacentTo = item.id,
limit = 1,
)
return scope.async(Dispatchers.IO) {
return scope.async(WholphinDispatchers.IO) {
val premiereDate = item.data.premiereDate
try {
val result =
@ -92,7 +92,7 @@ class DatePlayedService
* the previous episode's last played timestamp, and the episode's premiere date
*/
suspend fun getLastPlayed(item: BaseItem): LocalDateTime? =
withContext(Dispatchers.IO) {
withContext(WholphinDispatchers.IO) {
val seriesId = item.data.seriesId
return@withContext if (seriesId != null) {
datePlayedCache

View file

@ -3,10 +3,10 @@ package com.github.damontecres.wholphin.services
import android.content.Context
import com.github.damontecres.wholphin.preferences.AssPlaybackMode
import com.github.damontecres.wholphin.preferences.PlaybackPreferences
import com.github.damontecres.wholphin.util.WholphinDispatchers
import com.github.damontecres.wholphin.util.profile.MediaCodecCapabilitiesTest
import com.github.damontecres.wholphin.util.profile.createDeviceProfile
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
@ -37,7 +37,7 @@ class DeviceProfileService
prefs: PlaybackPreferences,
serverVersion: ServerVersion?,
): DeviceProfile =
withContext(Dispatchers.Default) {
withContext(WholphinDispatchers.Default) {
mutex.withLock {
val newConfig =
DeviceProfileConfiguration(

View file

@ -8,8 +8,8 @@ package com.github.damontecres.wholphin.services
import com.github.damontecres.wholphin.data.model.BaseItem
import com.github.damontecres.wholphin.ui.SlimItemFields
import com.github.damontecres.wholphin.util.LocalDateTimeSerializer
import com.github.damontecres.wholphin.util.WholphinDispatchers
import com.github.damontecres.wholphin.util.supportItemKinds
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.sync.Semaphore
@ -143,14 +143,14 @@ class LatestNextUpService
resume: List<BaseItem>,
nextUp: List<BaseItem>,
): List<BaseItem> =
withContext(Dispatchers.IO) {
withContext(WholphinDispatchers.IO) {
val start = System.currentTimeMillis()
val semaphore = Semaphore(3)
val deferred =
nextUp
.filter { it.data.seriesId != null }
.map { item ->
async(Dispatchers.IO) {
async(WholphinDispatchers.IO) {
try {
semaphore.withPermit {
datePlayedService.getLastPlayed(item)

View file

@ -16,12 +16,12 @@ import androidx.work.workDataOf
import com.github.damontecres.wholphin.data.ServerRepository
import com.github.damontecres.wholphin.ui.collectLatestIn
import com.github.damontecres.wholphin.util.ExceptionHandler
import com.github.damontecres.wholphin.util.WholphinDispatchers
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import dagger.hilt.android.qualifiers.ActivityContext
import dagger.hilt.android.scopes.ActivityScoped
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.jellyfin.sdk.api.client.ApiClient
import org.jellyfin.sdk.model.serializer.toUUIDOrNull
@ -92,7 +92,7 @@ class LatestNextUpSchedulerService
)
// Exposed for testing
internal var dispatcher: CoroutineDispatcher = Dispatchers.IO
internal var dispatcher: CoroutineDispatcher = WholphinDispatchers.IO
init {
serverRepository.current.collectLatestIn(activity.lifecycleScope) { user ->

View file

@ -24,10 +24,10 @@ import com.github.damontecres.wholphin.util.BlockingList
import com.github.damontecres.wholphin.util.LoadingState
import com.github.damontecres.wholphin.util.PlaybackItemState
import com.github.damontecres.wholphin.util.TrackActivityPlaybackListener
import com.github.damontecres.wholphin.util.WholphinDispatchers
import com.github.damontecres.wholphin.util.profile.supportedAudioCodecs
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@ -177,9 +177,9 @@ class MusicService
items: BlockingList<BaseItem?>,
startIndex: Int,
shuffled: Boolean,
) = withContext(Dispatchers.IO) {
) = withContext(WholphinDispatchers.IO) {
Timber.d("setQueue: %s items, startIndex=%s, shuffled=%s", items.size, startIndex, shuffled)
withContext(Dispatchers.Main) {
withContext(WholphinDispatchers.Main) {
player.setMediaItems(emptyList())
player.shuffleModeEnabled = shuffled
}
@ -199,7 +199,7 @@ class MusicService
items
.filter { it.type == BaseItemKind.AUDIO }
.map(::convert)
withContext(Dispatchers.Main) {
withContext(WholphinDispatchers.Main) {
player.setMediaItems(mediaItems)
player.shuffleModeEnabled = shuffled
updateQueueSize()
@ -216,7 +216,7 @@ class MusicService
) {
if (item.type == BaseItemKind.AUDIO) {
val mediaItem = convert(item)
withContext(Dispatchers.Main) {
withContext(WholphinDispatchers.Main) {
if (index != null) {
player.addMediaItem(index, mediaItem)
} else {
@ -293,7 +293,7 @@ class MusicService
*/
private suspend fun updateQueueSize() {
// val ids =
// withContext(Dispatchers.Default) {
// withContext(WholphinDispatchers.Default) {
// (0..<player.mediaItemCount).map { player.getMediaItemAt(it).mediaId.toUUID() }
// }
val timeline = onMain { player.currentTimeline }
@ -304,7 +304,7 @@ class MusicService
timeline.getWindow(it, window)
window.mediaItem.mediaId.toUUID()
}.toSet()
withContext(Dispatchers.Main) {
withContext(WholphinDispatchers.Main) {
_state.update {
it.copy(
queueVersion = it.queueVersion + 1,
@ -321,7 +321,7 @@ class MusicService
suspend fun moveQueue(
index: Int,
direction: MoveDirection,
) = withContext(Dispatchers.Main) {
) = withContext(WholphinDispatchers.Main) {
player.moveMediaItem(index, if (direction == MoveDirection.UP) index - 1 else index + 1)
updateQueueSize()
}
@ -332,7 +332,7 @@ class MusicService
suspend fun moveQueue(
index: Int,
newIndex: Int,
) = withContext(Dispatchers.Main) {
) = withContext(WholphinDispatchers.Main) {
player.moveMediaItem(index, newIndex)
updateQueueSize()
}
@ -385,7 +385,7 @@ class MusicService
.subscribe<PlaystateMessage>()
.onEach { message ->
message.data?.let {
withContext(Dispatchers.Main) {
withContext(WholphinDispatchers.Main) {
when (it.command) {
PlaystateCommand.STOP -> {
stop()

View file

@ -13,11 +13,11 @@ import com.github.damontecres.wholphin.ui.nav.Destination
import com.github.damontecres.wholphin.ui.nav.NavDrawerItem
import com.github.damontecres.wholphin.ui.nav.ServerNavDrawerItem
import com.github.damontecres.wholphin.ui.showToast
import com.github.damontecres.wholphin.util.WholphinDispatchers
import com.github.damontecres.wholphin.util.supportedCollectionTypes
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@ -209,7 +209,7 @@ class NavDrawerService
val allItems = builtins + libraries
val navDrawerPins =
withContext(Dispatchers.IO) {
withContext(WholphinDispatchers.IO) {
serverPreferencesDao.getNavDrawerPinnedItems(user).associateBy { it.itemId }
}

View file

@ -29,6 +29,7 @@ import com.github.damontecres.wholphin.preferences.MediaExtensionStatus
import com.github.damontecres.wholphin.preferences.PlaybackPreferences
import com.github.damontecres.wholphin.preferences.PlayerBackend
import com.github.damontecres.wholphin.services.hilt.AuthOkHttpClient
import com.github.damontecres.wholphin.util.WholphinDispatchers
import com.github.damontecres.wholphin.util.mpv.MpvPlayer
import dagger.hilt.android.qualifiers.ApplicationContext
import io.github.peerless2012.ass.media.AssHandler
@ -36,7 +37,6 @@ import io.github.peerless2012.ass.media.factory.AssRenderersFactory
import io.github.peerless2012.ass.media.kt.withAssMkvSupport
import io.github.peerless2012.ass.media.parser.AssSubtitleParserFactory
import io.github.peerless2012.ass.media.type.AssRenderType
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient
import timber.log.Timber
@ -62,7 +62,7 @@ class PlayerFactory
backend: PlayerBackend,
prefs: PlaybackPreferences,
): PlayerCreation {
withContext(Dispatchers.Main) {
withContext(WholphinDispatchers.Main) {
if (currentPlayer?.isReleased == false) {
Timber.w("Player was not released before trying to create a new one!")
currentPlayer?.release()
@ -138,7 +138,7 @@ class PlayerFactory
.build()
.apply {
assHandler?.init(this)
withContext(Dispatchers.Main) {
withContext(WholphinDispatchers.Main) {
setAudioAttributes(
AudioAttributes
.Builder()

View file

@ -8,9 +8,9 @@ import android.os.Looper
import android.view.Display
import com.github.damontecres.wholphin.MainActivity
import com.github.damontecres.wholphin.ui.showToast
import com.github.damontecres.wholphin.util.WholphinDispatchers
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeoutOrNull
@ -38,7 +38,7 @@ class RefreshRateService
stream: MediaStream,
switchRefreshRate: Boolean,
switchResolution: Boolean,
) = withContext(Dispatchers.IO) {
) = withContext(WholphinDispatchers.IO) {
if (!switchRefreshRate && !switchResolution) {
Timber.v("Not switching either refresh rate nor resolution")
return@withContext

View file

@ -3,7 +3,7 @@ package com.github.damontecres.wholphin.services
import com.github.damontecres.wholphin.data.RememberedTabDao
import com.github.damontecres.wholphin.data.ServerRepository
import com.github.damontecres.wholphin.data.model.RememberedTab
import kotlinx.coroutines.Dispatchers
import com.github.damontecres.wholphin.util.WholphinDispatchers
import kotlinx.coroutines.withContext
import javax.inject.Inject
import javax.inject.Singleton
@ -16,7 +16,7 @@ class RememberedTabService
private val rememberedTabDao: RememberedTabDao,
) {
suspend fun getRememberedTab(itemId: String): Int? =
withContext(Dispatchers.IO) {
withContext(WholphinDispatchers.IO) {
serverRepository.currentUser?.rowId?.let { userId ->
rememberedTabDao.getRememberedTab(userId, itemId)?.index
}
@ -26,7 +26,7 @@ class RememberedTabService
itemId: String,
tabIndex: Int,
): Unit =
withContext(Dispatchers.IO) {
withContext(WholphinDispatchers.IO) {
serverRepository.currentUser?.rowId?.let { userId ->
rememberedTabDao.save(RememberedTab(userId, itemId, tabIndex))
}

View file

@ -10,10 +10,10 @@ import com.github.damontecres.wholphin.ui.launchDefault
import com.github.damontecres.wholphin.util.ApiRequestPager
import com.github.damontecres.wholphin.util.ExceptionHandler
import com.github.damontecres.wholphin.util.GetItemsRequestHandler
import com.github.damontecres.wholphin.util.WholphinDispatchers
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
@ -224,7 +224,7 @@ class ScreensaverService
if (index > pager.lastIndex) index = 0
}
}
}.flowOn(Dispatchers.Default).cancellable()
}.flowOn(WholphinDispatchers.Default).cancellable()
private suspend fun createPager(): ApiRequestPager<GetItemsRequest> {
val prefs =

View file

@ -4,11 +4,11 @@ package com.github.damontecres.wholphin.services
import android.content.Context
import com.github.damontecres.wholphin.ui.toServerString
import com.github.damontecres.wholphin.util.WholphinDispatchers
import com.mayakapps.kache.InMemoryKache
import com.mayakapps.kache.ObjectKache
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.serialization.ExperimentalSerializationApi
@ -43,7 +43,7 @@ class SuggestionsCache
private val memoryCache =
InMemoryKache<String, CachedSuggestions>(maxSize = 8) {
creationScope = CoroutineScope(Dispatchers.IO)
creationScope = CoroutineScope(WholphinDispatchers.IO)
}
private fun cacheKey(

View file

@ -9,9 +9,9 @@ import com.github.damontecres.wholphin.data.ServerRepository
import com.github.damontecres.wholphin.preferences.AppPreference
import com.github.damontecres.wholphin.preferences.AppPreferences
import com.github.damontecres.wholphin.util.GetItemsRequestHandler
import com.github.damontecres.wholphin.util.WholphinDispatchers
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
@ -81,7 +81,7 @@ class SuggestionsWorker
.takeIf { it > 0 }
?: AppPreference.HomePageItems.defaultValue.toInt()
val context = Dispatchers.IO.limitedParallelism(2, "fetchSuggestions")
val context = WholphinDispatchers.IO.limitedParallelism(2, "fetchSuggestions")
if (requestedParentId != null && requestedItemKind != null) {
Timber.d(
"Fetching on-demand suggestions for parent=%s kind=%s",
@ -128,7 +128,7 @@ class SuggestionsWorker
supervisorScope {
supportedViews
.map { view ->
async(Dispatchers.IO) {
async(WholphinDispatchers.IO) {
runCatching {
Timber.v("Fetching suggestions for parent=%s kind=%s", view.id, view.itemKind)
fetchAndCacheSuggestions(
@ -293,7 +293,7 @@ class SuggestionsWorker
limit = freshLimit,
)
}
withContext(Dispatchers.Default) {
withContext(WholphinDispatchers.Default) {
val contextual = contextualDeferred.await()
val random = randomDeferred.await()
val fresh = freshDeferred.await()

View file

@ -14,10 +14,10 @@ import com.github.damontecres.wholphin.preferences.ThemeSongVolume
import com.github.damontecres.wholphin.services.hilt.AuthOkHttpClient
import com.github.damontecres.wholphin.services.hilt.IoCoroutineScope
import com.github.damontecres.wholphin.ui.launchIO
import com.github.damontecres.wholphin.util.WholphinDispatchers
import com.github.damontecres.wholphin.util.profile.Codec
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.flow.MutableStateFlow
@ -102,7 +102,7 @@ class ThemeSongPlayer
Codec.Audio.FLAC,
),
)
withContext(Dispatchers.Main) {
withContext(WholphinDispatchers.Main) {
player.apply {
stop()
volume = volumeLevel
@ -127,7 +127,7 @@ class ThemeSongPlayer
scope.launch {
mutex.withLock {
state.value.job?.cancelAndJoin()
withContext(Dispatchers.Main) {
withContext(WholphinDispatchers.Main) {
Timber.v("Stopping theme song")
player.stop()
}

View file

@ -21,8 +21,8 @@ import com.github.damontecres.wholphin.services.hilt.StandardOkHttpClient
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
import com.github.damontecres.wholphin.ui.showToast
import com.github.damontecres.wholphin.util.Version
import com.github.damontecres.wholphin.util.WholphinDispatchers
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
@ -131,7 +131,7 @@ class UpdateChecker
suspend fun getRelease(version: Version): Release? {
val url =
"https://api.github.com/repos/damontecres/Wholphin/releases/tags/v${version.major}.${version.minor}.${version.patch}"
return withContext(Dispatchers.IO) {
return withContext(WholphinDispatchers.IO) {
val request =
Request
.Builder()
@ -146,7 +146,7 @@ class UpdateChecker
* Get the latest released version
*/
suspend fun getLatestRelease(updateUrl: String): Release? =
withContext(Dispatchers.IO) {
withContext(WholphinDispatchers.IO) {
val request =
Request
.Builder()
@ -199,7 +199,7 @@ class UpdateChecker
release: Release,
callback: DownloadCallback,
) {
withContext(Dispatchers.IO) {
withContext(WholphinDispatchers.IO) {
cleanup()
val request =
Request
@ -210,7 +210,7 @@ class UpdateChecker
okHttpClient.newCall(request).execute().use {
if (it.isSuccessful && it.body != null) {
Timber.v("Request successful for ${release.downloadUrl}")
withContext(Dispatchers.Main) {
withContext(WholphinDispatchers.Main) {
callback.contentLength(it.body.contentLength())
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
@ -396,7 +396,7 @@ suspend fun copyTo(
while (bytes >= 0) {
out.write(buffer, 0, bytes)
bytesCopied += bytes
withContext(Dispatchers.Main) {
withContext(WholphinDispatchers.Main) {
callback.bytesDownloaded(bytesCopied)
}
bytes = input.read(buffer)

View file

@ -12,9 +12,9 @@ import com.github.damontecres.wholphin.data.model.JellyfinUser
import com.github.damontecres.wholphin.data.model.SeerrAuthMethod
import com.github.damontecres.wholphin.ui.launchDefault
import com.github.damontecres.wholphin.ui.launchIO
import com.github.damontecres.wholphin.util.WholphinDispatchers
import dagger.hilt.android.qualifiers.ActivityContext
import dagger.hilt.android.scopes.ActivityScoped
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.supervisorScope
import kotlinx.coroutines.withContext
@ -56,7 +56,7 @@ class UserSwitchListener
user.uiLanguage?.let { LocaleListCompat.forLanguageTags(it) }
?: LocaleListCompat.getEmptyLocaleList()
Timber.i("Switching locale to %s", localeList)
withContext(Dispatchers.Main) {
withContext(WholphinDispatchers.Main) {
AppCompatDelegate.setApplicationLocales(localeList)
}

View file

@ -7,6 +7,7 @@ import com.github.damontecres.wholphin.R
import com.github.damontecres.wholphin.data.ServerRepository
import com.github.damontecres.wholphin.services.SeerrApi
import com.github.damontecres.wholphin.util.CoroutineContextApiClientFactory
import com.github.damontecres.wholphin.util.WholphinDispatchers
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@ -14,7 +15,6 @@ import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import okhttp3.OkHttpClient
import org.jellyfin.sdk.Jellyfin
@ -42,21 +42,21 @@ annotation class AuthOkHttpClient
annotation class StandardOkHttpClient
/**
* A [CoroutineScope] with [Dispatchers.IO]
* A [CoroutineScope] with [WholphinDispatchers.IO]
*/
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class IoCoroutineScope
/**
* A [CoroutineScope] with [Dispatchers.Default]
* A [CoroutineScope] with [WholphinDispatchers.Default]
*/
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class DefaultCoroutineScope
/**
* [Dispatchers.IO]
* [WholphinDispatchers.IO]
*
* @see IoCoroutineScope
*/
@ -65,7 +65,7 @@ annotation class DefaultCoroutineScope
annotation class IoDispatcher
/**
* [Dispatchers.Default]
* [WholphinDispatchers.Default]
*
* @see DefaultCoroutineScope
*/
@ -156,7 +156,7 @@ object AppModule {
@Provides
@Singleton
@IoDispatcher
fun ioDispatcher(): CoroutineDispatcher = Dispatchers.IO
fun ioDispatcher(): CoroutineDispatcher = WholphinDispatchers.IO
@Provides
@Singleton
@ -168,7 +168,7 @@ object AppModule {
@Provides
@Singleton
@DefaultDispatcher
fun defaultDispatcher(): CoroutineDispatcher = Dispatchers.Default
fun defaultDispatcher(): CoroutineDispatcher = WholphinDispatchers.Default
@Provides
@Singleton

View file

@ -44,7 +44,7 @@ object DatabaseModule {
).addMigrations(Migrations.Migrate2to3)
// .setQueryCallback({ sqlQuery, args ->
// Timber.v("sqlQuery=$sqlQuery, args=$args")
// }, Dispatchers.IO.asExecutor())
// }, WholphinDispatchers.IO.asExecutor())
.build()
@Provides

View file

@ -33,9 +33,9 @@ import com.github.damontecres.wholphin.data.model.BaseItem
import com.github.damontecres.wholphin.ui.data.RowColumn
import com.github.damontecres.wholphin.ui.data.RowColumnSaver
import com.github.damontecres.wholphin.util.ExceptionHandler
import com.github.damontecres.wholphin.util.WholphinDispatchers
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collectLatest
@ -359,34 +359,34 @@ suspend fun showToast(
context: Context,
text: CharSequence,
duration: Int,
) = withContext(Dispatchers.Main) {
) = withContext(WholphinDispatchers.Main) {
Toast.makeText(context, text, duration).show()
}
suspend fun showToast(
context: Context,
text: CharSequence,
) = withContext(Dispatchers.Main) {
) = withContext(WholphinDispatchers.Main) {
Toast.makeText(context, text, Toast.LENGTH_LONG).show()
}
/**
* Launches a coroutine with [Dispatchers.IO] plus the provided [CoroutineContext] defaulting to using [ExceptionHandler]
* Launches a coroutine with [WholphinDispatchers.IO] plus the provided [CoroutineContext] defaulting to using [ExceptionHandler]
*/
fun CoroutineScope.launchIO(
context: CoroutineContext = ExceptionHandler(),
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit,
): Job = launch(context = Dispatchers.IO + context, start = start, block = block)
): Job = launch(context = WholphinDispatchers.IO + context, start = start, block = block)
/**
* Launches a coroutine with [Dispatchers.Default] plus the provided [CoroutineContext] defaulting to using [ExceptionHandler]
* Launches a coroutine with [WholphinDispatchers.Default] plus the provided [CoroutineContext] defaulting to using [ExceptionHandler]
*/
fun CoroutineScope.launchDefault(
context: CoroutineContext = ExceptionHandler(),
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit,
): Job = launch(context = Dispatchers.Default + context, start = start, block = block)
): Job = launch(context = WholphinDispatchers.Default + context, start = start, block = block)
/**
* Converts a UUID to the format used server-side (ie without hyphens).
@ -410,7 +410,7 @@ fun logTab(
ACRA.errorReporter.putCustomData("tabInfo", info)
}
suspend fun <T> onMain(block: suspend CoroutineScope.() -> T) = withContext(Dispatchers.Main, block)
suspend fun <T> onMain(block: suspend CoroutineScope.() -> T) = withContext(WholphinDispatchers.Main, block)
fun Modifier.dimAndBlur(enabled: Boolean) =
this.ifElse(

View file

@ -30,7 +30,6 @@ import coil3.request.crossfade
import com.github.damontecres.wholphin.ui.AspectRatios
import com.github.damontecres.wholphin.ui.PreviewTvSpec
import com.github.damontecres.wholphin.ui.components.Genre
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
import com.github.damontecres.wholphin.ui.setup.rememberIdColor
import com.github.damontecres.wholphin.ui.theme.WholphinTheme
import java.util.UUID

View file

@ -32,9 +32,7 @@ import coil3.request.ImageRequest
import coil3.request.crossfade
import com.github.damontecres.wholphin.ui.AspectRatios
import com.github.damontecres.wholphin.ui.PreviewTvSpec
import com.github.damontecres.wholphin.ui.components.Genre
import com.github.damontecres.wholphin.ui.components.Studio
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
import com.github.damontecres.wholphin.ui.setup.rememberIdColor
import com.github.damontecres.wholphin.ui.theme.WholphinTheme
import java.util.UUID

View file

@ -70,13 +70,13 @@ import com.github.damontecres.wholphin.util.ExceptionHandler
import com.github.damontecres.wholphin.util.GetItemsRequestHandler
import com.github.damontecres.wholphin.util.GetPersonsHandler
import com.github.damontecres.wholphin.util.LoadingState
import com.github.damontecres.wholphin.util.WholphinDispatchers
import com.github.damontecres.wholphin.util.successValue
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.catch
@ -251,7 +251,7 @@ class CollectionFolderViewModel
fun saveViewOptions(viewOptions: ViewOptions) {
_state.update { it.copy(viewOptions = viewOptions) }
viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
viewModelScope.launch(ExceptionHandler() + WholphinDispatchers.IO) {
saveLibraryDisplayInfo(viewOptions = viewOptions)
if (!viewOptions.showBackdrop) {
backdropService.clearBackdrop()
@ -295,7 +295,7 @@ class CollectionFolderViewModel
recursive: Boolean,
filter: GetItemsFilter,
useSeriesForPrimary: Boolean,
) = viewModelScope.launch(Dispatchers.IO) {
) = viewModelScope.launch(WholphinDispatchers.IO) {
_state.update {
it.copy(
items = DataLoadingState.Loading,
@ -437,7 +437,7 @@ class CollectionFolderViewModel
)
suspend fun positionOfLetter(letter: Char): Int? =
withContext(Dispatchers.IO) {
withContext(WholphinDispatchers.IO) {
val sort = state.value.sortAndDirection
val filter = state.value.filter
val request =
@ -461,7 +461,7 @@ class CollectionFolderViewModel
itemId: UUID,
played: Boolean,
) {
viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
viewModelScope.launch(ExceptionHandler() + WholphinDispatchers.IO) {
favoriteWatchManager.setWatched(itemId, played)
(state.value.items as? DataLoadingState.Success)?.let {
(it.data as? ApiRequestPager<*>)?.refreshItem(position, itemId)
@ -474,7 +474,7 @@ class CollectionFolderViewModel
itemId: UUID,
favorite: Boolean,
) {
viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
viewModelScope.launch(ExceptionHandler() + WholphinDispatchers.IO) {
favoriteWatchManager.setFavorite(itemId, favorite)
(state.value.items as? DataLoadingState.Success)?.let {
(it.data as? ApiRequestPager<*>)?.refreshItem(position, itemId)

View file

@ -33,13 +33,13 @@ import com.github.damontecres.wholphin.ui.tryRequestFocus
import com.github.damontecres.wholphin.util.DataLoadingState
import com.github.damontecres.wholphin.util.GetGenresRequestHandler
import com.github.damontecres.wholphin.util.GetItemsRequestHandler
import com.github.damontecres.wholphin.util.WholphinDispatchers
import com.mayakapps.kache.InMemoryKache
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.flow.MutableStateFlow
@ -142,7 +142,7 @@ class GenreViewModel
}
suspend fun positionOfLetter(letter: Char): Int =
withContext(Dispatchers.IO) {
withContext(WholphinDispatchers.IO) {
val request =
GetGenresRequest(
parentId = itemId,
@ -197,7 +197,7 @@ suspend fun getGenreImageMap(
val semaphore = Semaphore(4)
genres
.map { genreId ->
scope.async(Dispatchers.IO) {
scope.async(WholphinDispatchers.IO) {
semaphore.withPermit {
val item =
GetItemsRequestHandler

View file

@ -38,13 +38,13 @@ import com.github.damontecres.wholphin.util.ApiRequestPager
import com.github.damontecres.wholphin.util.DataLoadingState
import com.github.damontecres.wholphin.util.ExceptionHandler
import com.github.damontecres.wholphin.util.RequestHandler
import com.github.damontecres.wholphin.util.WholphinDispatchers
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
@ -150,7 +150,7 @@ class ItemGridViewModel
itemId: UUID,
played: Boolean,
) {
viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
viewModelScope.launch(ExceptionHandler() + WholphinDispatchers.IO) {
favoriteWatchManager.setWatched(itemId, played)
(state.value.items as? DataLoadingState.Success)?.let {
(it.data as? ApiRequestPager<*>)?.refreshItem(position, itemId)
@ -163,7 +163,7 @@ class ItemGridViewModel
itemId: UUID,
favorite: Boolean,
) {
viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
viewModelScope.launch(ExceptionHandler() + WholphinDispatchers.IO) {
favoriteWatchManager.setFavorite(itemId, favorite)
(state.value.items as? DataLoadingState.Success)?.let {
(it.data as? ApiRequestPager<*>)?.refreshItem(position, itemId)

View file

@ -48,13 +48,13 @@ import com.github.damontecres.wholphin.util.GetItemsRequestHandler
import com.github.damontecres.wholphin.util.HomeRowLoadingState
import com.github.damontecres.wholphin.util.LoadingState
import com.github.damontecres.wholphin.util.RequestHandler
import com.github.damontecres.wholphin.util.WholphinDispatchers
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.first
@ -173,7 +173,7 @@ class RecommendedViewModel
}
private fun fetchSuggestions() {
viewModelScope.launch(Dispatchers.IO) {
viewModelScope.launch(WholphinDispatchers.IO) {
val limit =
userPreferencesService
.getCurrent()

View file

@ -32,11 +32,11 @@ import com.github.damontecres.wholphin.ui.launchIO
import com.github.damontecres.wholphin.ui.tryRequestFocus
import com.github.damontecres.wholphin.util.DataLoadingState
import com.github.damontecres.wholphin.util.GetStudiosRequestHandler
import com.github.damontecres.wholphin.util.WholphinDispatchers
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
@ -113,7 +113,7 @@ class StudioViewModel
}
suspend fun positionOfLetter(letter: Char): Int =
withContext(Dispatchers.IO) {
withContext(WholphinDispatchers.IO) {
val request =
GetStudiosRequest(
userId = serverRepository.currentUser?.id,

View file

@ -17,10 +17,10 @@ import android.speech.RecognizerIntent
import android.speech.SpeechRecognizer
import androidx.core.content.ContextCompat
import com.github.damontecres.wholphin.R
import com.github.damontecres.wholphin.util.WholphinDispatchers
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.android.asCoroutineDispatcher
@ -175,7 +175,7 @@ class VoiceInputManager
private fun provideMainDispatcher(): CoroutineDispatcher =
try {
Dispatchers.Main.immediate
WholphinDispatchers.Main.immediate
} catch (_: IllegalStateException) {
// Fallback for unit tests where Main dispatcher is not installed
handler.asCoroutineDispatcher()

View file

@ -73,7 +73,7 @@ import com.github.damontecres.wholphin.ui.playback.isForwardButton
import com.github.damontecres.wholphin.ui.playback.isPlayKeyUp
import com.github.damontecres.wholphin.ui.tryRequestFocus
import com.github.damontecres.wholphin.util.ExceptionHandler
import kotlinx.coroutines.Dispatchers
import com.github.damontecres.wholphin.util.WholphinDispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import timber.log.Timber
@ -217,7 +217,7 @@ fun <T : CardGridItem> CardGrid(
{ letter: Char ->
scope.launch(ExceptionHandler()) {
val jumpPosition =
withContext(Dispatchers.IO) {
withContext(WholphinDispatchers.IO) {
letterPosition.invoke(letter)
}
Timber.d("Alphabet jump to $jumpPosition")

View file

@ -53,12 +53,12 @@ import com.github.damontecres.wholphin.ui.util.StringStringProvider
import com.github.damontecres.wholphin.util.ApiRequestPager
import com.github.damontecres.wholphin.util.ExceptionHandler
import com.github.damontecres.wholphin.util.HomeRowLoadingState
import com.github.damontecres.wholphin.util.WholphinDispatchers
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
@ -166,7 +166,7 @@ class HomeRowGridViewModel
itemId: UUID,
played: Boolean,
) {
viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
viewModelScope.launch(ExceptionHandler() + WholphinDispatchers.IO) {
favoriteWatchManager.setWatched(itemId, played)
(state.value.loading as? HomeRowLoadingState.Success)?.let {
(it.items as? ApiRequestPager<*>)?.refreshItem(position, itemId)
@ -179,7 +179,7 @@ class HomeRowGridViewModel
itemId: UUID,
favorite: Boolean,
) {
viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
viewModelScope.launch(ExceptionHandler() + WholphinDispatchers.IO) {
favoriteWatchManager.setFavorite(itemId, favorite)
(state.value.loading as? HomeRowLoadingState.Success)?.let {
(it.items as? ApiRequestPager<*>)?.refreshItem(position, itemId)

View file

@ -107,12 +107,12 @@ import com.github.damontecres.wholphin.util.ApiRequestPager
import com.github.damontecres.wholphin.util.ExceptionHandler
import com.github.damontecres.wholphin.util.GetItemsRequestHandler
import com.github.damontecres.wholphin.util.LoadingState
import com.github.damontecres.wholphin.util.WholphinDispatchers
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
@ -316,14 +316,14 @@ class PlaylistViewModel
fun setWatched(
itemId: UUID,
played: Boolean,
) = viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
) = viewModelScope.launch(ExceptionHandler() + WholphinDispatchers.IO) {
favoriteWatchManager.setWatched(itemId, played)
}
fun setFavorite(
itemId: UUID,
favorite: Boolean,
) = viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
) = viewModelScope.launch(ExceptionHandler() + WholphinDispatchers.IO) {
favoriteWatchManager.setFavorite(itemId, favorite)
}

View file

@ -40,12 +40,12 @@ import com.github.damontecres.wholphin.util.ExceptionHandler
import com.github.damontecres.wholphin.util.GetItemsRequestHandler
import com.github.damontecres.wholphin.util.HomeRowLoadingState
import com.github.damontecres.wholphin.util.LoadingState
import com.github.damontecres.wholphin.util.WholphinDispatchers
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.MutableStateFlow
@ -210,7 +210,7 @@ class CollectionViewModel
supervisorScope {
val jobs =
typesInCollection.map { type ->
async(Dispatchers.IO) {
async(WholphinDispatchers.IO) {
val title = ResStringProvider(formatTypeName(type))
val result =
try {
@ -359,7 +359,7 @@ class CollectionViewModel
)
suspend fun letterPosition(letter: Char): Int =
withContext(Dispatchers.IO) {
withContext(WholphinDispatchers.IO) {
val sort = state.value.sortAndDirection
val filter = state.value.itemFilter
val request =
@ -387,7 +387,7 @@ class CollectionViewModel
itemId: UUID,
played: Boolean,
position: RowColumn?,
) = viewModelScope.launch(Dispatchers.IO + ExceptionHandler()) {
) = viewModelScope.launch(WholphinDispatchers.IO + ExceptionHandler()) {
favoriteWatchManager.setWatched(itemId, played)
if (itemId == state.value.collection?.id) {
refreshCollection()
@ -400,7 +400,7 @@ class CollectionViewModel
itemId: UUID,
favorite: Boolean,
position: RowColumn?,
) = viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
) = viewModelScope.launch(ExceptionHandler() + WholphinDispatchers.IO) {
favoriteWatchManager.setFavorite(itemId, favorite)
if (itemId == state.value.collection?.id) {
refreshCollection()

View file

@ -24,6 +24,7 @@ import com.github.damontecres.wholphin.ui.launchIO
import com.github.damontecres.wholphin.ui.nav.Destination
import com.github.damontecres.wholphin.ui.showToast
import com.github.damontecres.wholphin.util.DataLoadingState
import com.github.damontecres.wholphin.util.WholphinDispatchers
import com.github.damontecres.wholphin.util.successValue
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
@ -31,7 +32,6 @@ import dagger.assisted.AssistedInject
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.MutableStateFlow
@ -73,7 +73,7 @@ class DiscoverMovieViewModel
}
private fun fetchAndSetItem(): Deferred<MovieDetails?> =
viewModelScope.async(Dispatchers.IO) {
viewModelScope.async(WholphinDispatchers.IO) {
try {
val movie = seerrService.api.moviesApi.movieMovieIdGet(movieId = item.id)
_state.update { it.copy(movie = DataLoadingState.Success(movie)) }

View file

@ -22,6 +22,7 @@ import com.github.damontecres.wholphin.ui.launchIO
import com.github.damontecres.wholphin.ui.nav.Destination
import com.github.damontecres.wholphin.ui.showToast
import com.github.damontecres.wholphin.util.DataLoadingState
import com.github.damontecres.wholphin.util.WholphinDispatchers
import com.github.damontecres.wholphin.util.successValue
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
@ -30,7 +31,6 @@ import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.MutableStateFlow
@ -71,7 +71,7 @@ class DiscoverSeriesViewModel
}
private fun fetchAndSetItem(): Deferred<TvDetails?> =
viewModelScope.async(Dispatchers.IO) {
viewModelScope.async(WholphinDispatchers.IO) {
try {
val tv = seerrService.api.tvApi.tvTvIdGet(tvId = item.id)
_state.update { it.copy(tvSeries = DataLoadingState.Success(tv)) }

View file

@ -23,13 +23,13 @@ import com.github.damontecres.wholphin.ui.nav.Destination
import com.github.damontecres.wholphin.ui.showToast
import com.github.damontecres.wholphin.util.DataLoadingState
import com.github.damontecres.wholphin.util.ExceptionHandler
import com.github.damontecres.wholphin.util.WholphinDispatchers
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@ -126,7 +126,7 @@ class EpisodeViewModel
fun setWatched(
itemId: UUID,
played: Boolean,
) = viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
) = viewModelScope.launch(ExceptionHandler() + WholphinDispatchers.IO) {
favoriteWatchManager.setWatched(itemId, played)
fetchAndSetItem()
}
@ -134,7 +134,7 @@ class EpisodeViewModel
fun setFavorite(
itemId: UUID,
favorite: Boolean,
) = viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
) = viewModelScope.launch(ExceptionHandler() + WholphinDispatchers.IO) {
favoriteWatchManager.setFavorite(itemId, favorite)
fetchAndSetItem()
}

View file

@ -59,11 +59,11 @@ import com.github.damontecres.wholphin.ui.rememberInt
import com.github.damontecres.wholphin.ui.rememberPosition
import com.github.damontecres.wholphin.ui.tryRequestFocus
import com.github.damontecres.wholphin.util.LoadingState
import com.github.damontecres.wholphin.util.WholphinDispatchers
import eu.wewox.programguide.ProgramGuide
import eu.wewox.programguide.ProgramGuideDimensions
import eu.wewox.programguide.ProgramGuideItem
import eu.wewox.programguide.rememberSaveableProgramGuideState
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import timber.log.Timber
@ -319,7 +319,7 @@ fun TvGuideGridContent(
var focusedProgramIndex by rememberInt(0)
LaunchedEffect(onFocus, focusedProgramIndex) {
withContext(Dispatchers.Default) {
withContext(WholphinDispatchers.Default) {
val program = programs.programs.getOrNull(focusedProgramIndex)
if (program != null) {
val channelIndex = channels.indexOfFirst { it.id == program.channelId }

View file

@ -86,12 +86,12 @@ import com.github.damontecres.wholphin.util.ApiRequestPager
import com.github.damontecres.wholphin.util.ExceptionHandler
import com.github.damontecres.wholphin.util.GetItemsRequestHandler
import com.github.damontecres.wholphin.util.LoadingState
import com.github.damontecres.wholphin.util.WholphinDispatchers
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@ -242,7 +242,7 @@ class AlbumViewModel
fun setFavorite(
itemId: UUID,
favorite: Boolean,
) = viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
) = viewModelScope.launch(ExceptionHandler() + WholphinDispatchers.IO) {
favoriteWatchManager.setFavorite(itemId, favorite)
val album =
api.userLibraryApi

View file

@ -81,12 +81,12 @@ import com.github.damontecres.wholphin.util.ApiRequestPager
import com.github.damontecres.wholphin.util.ExceptionHandler
import com.github.damontecres.wholphin.util.GetItemsRequestHandler
import com.github.damontecres.wholphin.util.LoadingState
import com.github.damontecres.wholphin.util.WholphinDispatchers
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@ -254,7 +254,7 @@ class ArtistViewModel
fun setFavorite(
itemId: UUID,
favorite: Boolean,
) = viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
) = viewModelScope.launch(ExceptionHandler() + WholphinDispatchers.IO) {
favoriteWatchManager.setFavorite(itemId, favorite)
val artist =
api.userLibraryApi

View file

@ -44,7 +44,6 @@ import com.github.damontecres.wholphin.ui.playback.ControllerViewState
import com.github.damontecres.wholphin.ui.playback.overlay.PlaybackAction
import com.github.damontecres.wholphin.ui.playback.overlay.PlaybackButton
import com.github.damontecres.wholphin.ui.playback.overlay.PlaybackButtons
import com.github.damontecres.wholphin.ui.playback.overlay.PlaybackFaButton
import com.github.damontecres.wholphin.ui.playback.overlay.buttonSpacing
import com.github.damontecres.wholphin.ui.theme.PreviewInteractionSource
import com.github.damontecres.wholphin.ui.theme.WholphinTheme

View file

@ -27,13 +27,13 @@ import com.github.damontecres.wholphin.ui.launchDefault
import com.github.damontecres.wholphin.ui.main.settings.MoveDirection
import com.github.damontecres.wholphin.ui.onMain
import com.github.damontecres.wholphin.ui.playback.ControllerViewState
import com.github.damontecres.wholphin.util.WholphinDispatchers
import com.mayakapps.kache.InMemoryKache
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
@ -95,7 +95,7 @@ class NowPlayingViewModel
private val lyricCache =
InMemoryKache<UUID, LyricDto>(20) {
creationScope = CoroutineScope(Dispatchers.IO)
creationScope = CoroutineScope(WholphinDispatchers.IO)
}
init {

View file

@ -24,7 +24,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.tv.material3.DenseListItem
import androidx.tv.material3.Icon
import androidx.tv.material3.ListItem
import androidx.tv.material3.ListItemDefaults
import androidx.tv.material3.LocalContentColor
import androidx.tv.material3.Text

View file

@ -41,6 +41,7 @@ import com.github.damontecres.wholphin.util.DataLoadingState
import com.github.damontecres.wholphin.util.ExceptionHandler
import com.github.damontecres.wholphin.util.GetEpisodesRequestHandler
import com.github.damontecres.wholphin.util.GetItemsRequestHandler
import com.github.damontecres.wholphin.util.WholphinDispatchers
import com.github.damontecres.wholphin.util.successValue
import com.google.common.cache.CacheBuilder
import dagger.assisted.Assisted
@ -50,7 +51,6 @@ import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
@ -137,7 +137,7 @@ class SeriesViewModel
val episodeListDeferred =
if (seriesPageType == SeriesPageType.OVERVIEW) {
viewModelScope.async(Dispatchers.IO) {
viewModelScope.async(WholphinDispatchers.IO) {
if (seasonEpisodeIds != null) {
loadEpisodesInternal(
seasonEpisodeIds.seasonId,
@ -292,7 +292,7 @@ class SeriesViewModel
series: BaseItem,
seasonNum: Int?,
): Deferred<List<BaseItem?>> =
viewModelScope.async(Dispatchers.IO) {
viewModelScope.async(WholphinDispatchers.IO) {
Timber.v("getSeasons for %s", series.id)
val request =
GetItemsRequest(
@ -405,7 +405,7 @@ class SeriesViewModel
itemId: UUID,
played: Boolean,
listIndex: Int?,
) = viewModelScope.launch(Dispatchers.IO + ExceptionHandler()) {
) = viewModelScope.launch(WholphinDispatchers.IO + ExceptionHandler()) {
favoriteWatchManager.setWatched(itemId, played)
listIndex?.let {
refreshEpisode(itemId, listIndex)
@ -440,7 +440,7 @@ class SeriesViewModel
itemId: UUID,
favorite: Boolean,
listIndex: Int?,
) = viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
) = viewModelScope.launch(ExceptionHandler() + WholphinDispatchers.IO) {
favoriteWatchManager.setFavorite(itemId, favorite)
if (listIndex != null) {
refreshEpisode(itemId, listIndex)
@ -452,13 +452,13 @@ class SeriesViewModel
fun setSeasonWatched(
seasonId: UUID,
played: Boolean,
) = viewModelScope.launch(Dispatchers.IO + ExceptionHandler()) {
) = viewModelScope.launch(WholphinDispatchers.IO + ExceptionHandler()) {
setWatched(seasonId, played, null)
updateSeries()
}
fun setWatchedSeries(played: Boolean) =
viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
viewModelScope.launch(ExceptionHandler() + WholphinDispatchers.IO) {
favoriteWatchManager.setWatched(seriesId, played)
updateSeries()
}
@ -466,7 +466,7 @@ class SeriesViewModel
fun refreshEpisode(
itemId: UUID,
listIndex: Int,
) = viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
) = viewModelScope.launch(ExceptionHandler() + WholphinDispatchers.IO) {
val eps = state.value.episodes
if (eps is EpisodeList.Success) {
eps.episodes.refreshItem(listIndex, itemId)
@ -481,7 +481,7 @@ class SeriesViewModel
* Play whichever episode is next up for series or else the first episode
*/
fun playNextUp() {
viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
viewModelScope.launch(ExceptionHandler() + WholphinDispatchers.IO) {
val result by api.tvShowsApi.getNextUp(seriesId = seriesId)
val nextUp =
result.items.firstOrNull() ?: api.tvShowsApi
@ -491,7 +491,7 @@ class SeriesViewModel
).content.items
.firstOrNull()
if (nextUp != null) {
withContext(Dispatchers.Main) {
withContext(WholphinDispatchers.Main) {
navigateTo(Destination.Playback(BaseItem(nextUp)))
}
} else {
@ -582,7 +582,7 @@ class SeriesViewModel
val result =
peopleInEpisodeCache
.get(item.id) {
viewModelScope.async(Dispatchers.IO) {
viewModelScope.async(WholphinDispatchers.IO) {
val list =
api.userLibraryApi
.getItem(item.id)

View file

@ -35,8 +35,8 @@ import com.github.damontecres.wholphin.ui.launchIO
import com.github.damontecres.wholphin.ui.nav.Destination
import com.github.damontecres.wholphin.ui.tryRequestFocus
import com.github.damontecres.wholphin.util.DataLoadingState
import com.github.damontecres.wholphin.util.WholphinDispatchers
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.flow.MutableStateFlow
@ -77,7 +77,7 @@ class SeerrRequestsViewModel
val requests =
mediaRequests.mapNotNull { request ->
if (request.media?.tmdbId != null) {
viewModelScope.async(Dispatchers.IO) {
viewModelScope.async(WholphinDispatchers.IO) {
semaphore.withPermit {
val type = SeerrItemType.fromString(request.type)
when (type) {

View file

@ -28,9 +28,9 @@ import com.github.damontecres.wholphin.ui.util.EmptyStringProvider
import com.github.damontecres.wholphin.util.ExceptionHandler
import com.github.damontecres.wholphin.util.HomeRowLoadingState
import com.github.damontecres.wholphin.util.LoadingState
import com.github.damontecres.wholphin.util.WholphinDispatchers
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.flow.MutableStateFlow
@ -117,7 +117,7 @@ class HomeViewModel
val deferred =
settings.rows
.map { row ->
viewModelScope.async(Dispatchers.IO) {
viewModelScope.async(WholphinDispatchers.IO) {
semaphore.withPermit {
Timber.v("Fetching row: %s", row)
try {
@ -220,9 +220,9 @@ class HomeViewModel
fun setWatched(
itemId: UUID,
played: Boolean,
) = viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
) = viewModelScope.launch(ExceptionHandler() + WholphinDispatchers.IO) {
favoriteWatchManager.setWatched(itemId, played)
withContext(Dispatchers.Main) {
withContext(WholphinDispatchers.Main) {
init()
}
}
@ -230,9 +230,9 @@ class HomeViewModel
fun setFavorite(
itemId: UUID,
favorite: Boolean,
) = viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
) = viewModelScope.launch(ExceptionHandler() + WholphinDispatchers.IO) {
favoriteWatchManager.setFavorite(itemId, favorite)
withContext(Dispatchers.Main) {
withContext(WholphinDispatchers.Main) {
init()
}
}

View file

@ -103,9 +103,9 @@ import com.github.damontecres.wholphin.ui.rememberPosition
import com.github.damontecres.wholphin.ui.tryRequestFocus
import com.github.damontecres.wholphin.util.ExceptionHandler
import com.github.damontecres.wholphin.util.SearchRelevance
import com.github.damontecres.wholphin.util.WholphinDispatchers
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@ -253,7 +253,7 @@ class SearchViewModel
}
private fun searchCombined(query: String) {
viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
viewModelScope.launch(ExceptionHandler() + WholphinDispatchers.IO) {
try {
val request =
GetItemsRequest(
@ -477,7 +477,7 @@ fun SearchPage(
) {
if (!searchClicked) return@LaunchedEffect
withContext(Dispatchers.IO) {
withContext(WholphinDispatchers.IO) {
// Want to focus on the first successful row after all of the ones before it are finished searching
val results =
if (isLibraryTab) {

View file

@ -43,10 +43,10 @@ import com.github.damontecres.wholphin.ui.util.ResStringProvider
import com.github.damontecres.wholphin.ui.util.StringStringProvider
import com.github.damontecres.wholphin.util.HomeRowLoadingState
import com.github.damontecres.wholphin.util.LoadingState
import com.github.damontecres.wholphin.util.WholphinDispatchers
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
@ -129,7 +129,7 @@ class HomeSettingsViewModel
.let { state ->
state.rows
.map { row ->
viewModelScope.async(Dispatchers.IO) {
viewModelScope.async(WholphinDispatchers.IO) {
semaphore.withPermit {
try {
homeSettingsService.fetchDataForRow(

View file

@ -67,6 +67,7 @@ import com.github.damontecres.wholphin.util.ExceptionHandler
import com.github.damontecres.wholphin.util.LoadingState
import com.github.damontecres.wholphin.util.PlaybackItemState
import com.github.damontecres.wholphin.util.TrackActivityPlaybackListener
import com.github.damontecres.wholphin.util.WholphinDispatchers
import com.github.damontecres.wholphin.util.checkForSupport
import com.github.damontecres.wholphin.util.mpv.MpvPlayer
import com.github.damontecres.wholphin.util.mpv.mpvDeviceProfile
@ -79,7 +80,6 @@ import dagger.assisted.AssistedInject
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
@ -242,7 +242,7 @@ class PlaybackViewModel
Timber.d("Selected backend: %s", playerBackend)
if (currentPlayer.value?.backend != playerBackend) {
Timber.i("Switching player backend to %s", playerBackend)
withContext(Dispatchers.Main) {
withContext(WholphinDispatchers.Main) {
disconnectPlayer()
}
@ -421,7 +421,7 @@ class PlaybackViewModel
positionMs: Long,
forceTranscoding: Boolean = this.forceTranscoding,
): Boolean =
withContext(Dispatchers.IO) {
withContext(WholphinDispatchers.IO) {
val item =
when (playlistItem) {
is PlaylistItem.Intro -> playlistItem.item
@ -581,7 +581,7 @@ class PlaybackViewModel
trickPlayInfo = trickPlayInfo,
)
}
withContext(Dispatchers.Main) {
withContext(WholphinDispatchers.Main) {
changeStreams(
item,
itemPlaybackToUse,
@ -612,7 +612,7 @@ class PlaybackViewModel
userInitiated: Boolean,
enableDirectPlay: Boolean = !this.forceTranscoding,
enableDirectStream: Boolean = !this.forceTranscoding,
) = withContext(Dispatchers.IO) {
) = withContext(WholphinDispatchers.IO) {
val itemId = item.id
val currentPlayback = state.value.currentPlayback
@ -779,7 +779,7 @@ class PlaybackViewModel
)
}
}
withContext(Dispatchers.Main) {
withContext(WholphinDispatchers.Main) {
// TODO, don't need to release & recreate when switching streams
this@PlaybackViewModel.activityListener?.let {
it.release()
@ -850,7 +850,7 @@ class PlaybackViewModel
subtitleIndex: Int?,
userInitiated: Boolean,
): Boolean =
withContext(Dispatchers.IO) {
withContext(WholphinDispatchers.IO) {
// TODO there's probably no reason why we can't add external subtitles?
Timber.v("changeStreams direct play")
@ -859,7 +859,7 @@ class PlaybackViewModel
if (externalSubtitle == null) {
val result =
withContext(Dispatchers.Main) {
withContext(WholphinDispatchers.Main) {
TrackSelectionUtils.createTrackSelections(
onMain { player.trackSelectionParameters },
onMain { player.currentTracks },
@ -1124,7 +1124,7 @@ class PlaybackViewModel
MediaSegmentType.INTRO -> prefs.skipIntros
MediaSegmentType.UNKNOWN -> SkipSegmentBehavior.IGNORE
}
withContext(Dispatchers.Main) {
withContext(WholphinDispatchers.Main) {
val newSegment =
when (behavior) {
SkipSegmentBehavior.AUTO_SKIP -> {
@ -1318,7 +1318,7 @@ class PlaybackViewModel
override fun onPlayerError(error: PlaybackException) {
Timber.e(error, "Playback error")
viewModelScope.launch(Dispatchers.Main + ExceptionHandler()) {
viewModelScope.launch(WholphinDispatchers.Main + ExceptionHandler()) {
state.value.currentPlayback?.let {
when (it.playMethod) {
PlayMethod.TRANSCODE -> {
@ -1346,7 +1346,7 @@ class PlaybackViewModel
enableDirectPlay = false,
enableDirectStream = false,
)
withContext(Dispatchers.Main) {
withContext(WholphinDispatchers.Main) {
player.prepare()
player.play()
}
@ -1367,7 +1367,7 @@ class PlaybackViewModel
.subscribe<PlaystateMessage>()
.onEach { message ->
message.data?.let {
withContext(Dispatchers.Main) {
withContext(WholphinDispatchers.Main) {
when (it.command) {
PlaystateCommand.STOP -> {
release()
@ -1422,7 +1422,7 @@ class PlaybackViewModel
* Atomically update [currentMediaInfo]
*/
internal suspend fun updateCurrentMedia(block: (CurrentMediaInfo) -> CurrentMediaInfo) =
withContext(Dispatchers.IO) {
withContext(WholphinDispatchers.IO) {
_state.update {
it.copy(currentMediaInfo = block.invoke(it.currentMediaInfo))
}

View file

@ -10,7 +10,7 @@ import com.github.damontecres.wholphin.data.model.TrackIndex
import com.github.damontecres.wholphin.ui.launchIO
import com.github.damontecres.wholphin.ui.onMain
import com.github.damontecres.wholphin.ui.showToast
import kotlinx.coroutines.Dispatchers
import com.github.damontecres.wholphin.util.WholphinDispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.withContext
@ -183,7 +183,7 @@ fun PlaybackViewModel.downloadAndSwitchSubtitles(
}
}
subtitleSearchState.update { it.copy(status = SubtitleSearchStatus.Inactive) }
withContext(Dispatchers.Main) {
withContext(WholphinDispatchers.Main) {
if (wasPlaying) {
player.play()
}

View file

@ -36,7 +36,7 @@ import com.github.damontecres.wholphin.ui.theme.WholphinTheme
import com.github.damontecres.wholphin.util.TrackSupport
import com.github.damontecres.wholphin.util.TrackSupportReason
import com.github.damontecres.wholphin.util.TrackType
import kotlinx.coroutines.Dispatchers
import com.github.damontecres.wholphin.util.WholphinDispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.withContext
@ -90,7 +90,7 @@ fun PlaybackDebugOverlay(
)
val memoryUsed by produceState("") {
withContext(Dispatchers.Default) {
withContext(WholphinDispatchers.Default) {
while (isActive) {
val runtime = Runtime.getRuntime()
val total = runtime.totalMemory()

View file

@ -16,7 +16,6 @@ import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asFlow
import androidx.lifecycle.viewModelScope
import androidx.tv.material3.Text
import com.github.damontecres.wholphin.R

View file

@ -57,9 +57,9 @@ import com.github.damontecres.wholphin.ui.main.settings.MoveDirection
import com.github.damontecres.wholphin.ui.nav.NavDrawerItem
import com.github.damontecres.wholphin.ui.theme.WholphinTheme
import com.github.damontecres.wholphin.util.ExceptionHandler
import com.github.damontecres.wholphin.util.WholphinDispatchers
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.update
@ -317,7 +317,7 @@ class NavDrawerPreferencesViewModel
return@launchDefault
}
val navDrawerPins =
withContext(Dispatchers.IO) {
withContext(WholphinDispatchers.IO) {
serverPreferencesDao
.getNavDrawerPinnedItems(user)
.associateBy { it.itemId }

View file

@ -34,7 +34,6 @@ import com.github.damontecres.wholphin.R
import com.github.damontecres.wholphin.ui.FontAwesome
import com.github.damontecres.wholphin.ui.PreviewTvSpec
import com.github.damontecres.wholphin.ui.components.BasicDialog
import com.github.damontecres.wholphin.ui.components.Button
import com.github.damontecres.wholphin.ui.components.TextButton
import com.github.damontecres.wholphin.ui.playback.isEnterKey
import com.github.damontecres.wholphin.ui.theme.WholphinTheme

View file

@ -13,13 +13,13 @@ import com.github.damontecres.wholphin.services.SetupNavigationManager
import com.github.damontecres.wholphin.ui.launchDefault
import com.github.damontecres.wholphin.ui.launchIO
import com.github.damontecres.wholphin.util.LoadingState
import com.github.damontecres.wholphin.util.WholphinDispatchers
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
@ -119,7 +119,7 @@ class SwitchUserViewModel
}
fun trySwitchUser(user: JellyfinUser): Deferred<String?> =
viewModelScope.async(Dispatchers.IO) {
viewModelScope.async(WholphinDispatchers.IO) {
try {
val current = serverRepository.changeUser(server, user)
setupNavigationManager.navigateTo(SetupDestination.AppContent(current))
@ -234,7 +234,7 @@ class SwitchUserViewModel
}
private suspend fun getUsers(): List<JellyfinUserAndImage> =
withContext(Dispatchers.IO) {
withContext(WholphinDispatchers.IO) {
val api = jellyfin.createApi(server.url)
val knownUsers =
serverDao

View file

@ -27,13 +27,13 @@ import com.github.damontecres.wholphin.util.ApiRequestPager
import com.github.damontecres.wholphin.util.ExceptionHandler
import com.github.damontecres.wholphin.util.GetItemsRequestHandler
import com.github.damontecres.wholphin.util.LoadingState
import com.github.damontecres.wholphin.util.WholphinDispatchers
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
@ -377,7 +377,7 @@ class SlideshowViewModel
),
)
Timber.d("Saved VideoFilter for image %s", it.image.id)
withContext(Dispatchers.Main) {
withContext(WholphinDispatchers.Main) {
showToast(
context,
"Saved",
@ -406,7 +406,7 @@ class SlideshowViewModel
),
)
Timber.d("Saved VideoFilter for album %s", slideshowSettings.parentId)
withContext(Dispatchers.Main) {
withContext(WholphinDispatchers.Main) {
showToast(
context,
"Saved",

View file

@ -8,7 +8,7 @@ import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import com.github.damontecres.wholphin.ui.getTimeFormatter
import kotlinx.coroutines.Dispatchers
import com.github.damontecres.wholphin.util.WholphinDispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.withContext
@ -34,7 +34,7 @@ data class Clock(
fun ProvideLocalClock(content: @Composable () -> Unit) {
val clock = remember { Clock() }
LaunchedEffect(Unit) {
withContext(Dispatchers.Default) {
withContext(WholphinDispatchers.Default) {
while (isActive) {
val now = LocalDateTime.now()
val time = getTimeFormatter().format(now)

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
}