mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 15:50:16 +02:00
Fix some IO related crashes (#1008)
## Description - Catch network exceptions when querying to populate the nav drawer - Perform screensaver work on IO thread - Fix possible crash if screensaver service runs while regular activity is destroyed - Fix genre images & backdrop images not loading This PR also adds an Jellyfin SDK `ApiClient` wrapper to push all network calls onto the IO thread. And tries to use a more strict network-on-main policy for debug builds. ### Related issues Fixes #994 Fixes #1001 Should fix #1003 ### Testing Emulator with simulated exception throws ## Screenshots N/A ## AI or LLM usage None
This commit is contained in:
parent
37d52ef57e
commit
9b995bf6ba
11 changed files with 147 additions and 42 deletions
|
|
@ -79,8 +79,12 @@ import dagger.hilt.android.AndroidEntryPoint
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.flow.catch
|
||||||
import kotlinx.coroutines.flow.firstOrNull
|
import kotlinx.coroutines.flow.firstOrNull
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||||
import org.jellyfin.sdk.model.serializer.toUUIDOrNull
|
import org.jellyfin.sdk.model.serializer.toUUIDOrNull
|
||||||
|
|
@ -165,6 +169,20 @@ class MainActivity : AppCompatActivity() {
|
||||||
window?.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
|
window?.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
screensaverService.keepScreenOn
|
||||||
|
.onEach { keepScreenOn ->
|
||||||
|
Timber.v("keepScreenOn: %s", keepScreenOn)
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
if (keepScreenOn) {
|
||||||
|
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||||
|
} else {
|
||||||
|
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.catch { ex ->
|
||||||
|
Timber.e(ex, "Error with keepScreenOn")
|
||||||
|
}.launchIn(lifecycleScope)
|
||||||
|
|
||||||
viewModel.appStart()
|
viewModel.appStart()
|
||||||
setContent {
|
setContent {
|
||||||
val appPreferences by userPreferencesDataStore.data.collectAsState(null)
|
val appPreferences by userPreferencesDataStore.data.collectAsState(null)
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ package com.github.damontecres.wholphin
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.StrictMode
|
import android.os.StrictMode
|
||||||
import android.os.StrictMode.ThreadPolicy
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.compose.runtime.Composer
|
import androidx.compose.runtime.Composer
|
||||||
import androidx.compose.runtime.ExperimentalComposeRuntimeApi
|
import androidx.compose.runtime.ExperimentalComposeRuntimeApi
|
||||||
|
|
@ -27,16 +26,6 @@ class WholphinApplication :
|
||||||
init {
|
init {
|
||||||
instance = this
|
instance = this
|
||||||
|
|
||||||
if (BuildConfig.DEBUG) {
|
|
||||||
StrictMode.setThreadPolicy(
|
|
||||||
ThreadPolicy
|
|
||||||
.Builder()
|
|
||||||
.detectNetwork()
|
|
||||||
.penaltyLog()
|
|
||||||
.build(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (BuildConfig.DEBUG) {
|
if (BuildConfig.DEBUG) {
|
||||||
Timber.plant(Timber.DebugTree())
|
Timber.plant(Timber.DebugTree())
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -64,6 +53,23 @@ class WholphinApplication :
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
|
if (BuildConfig.DEBUG) {
|
||||||
|
StrictMode.setThreadPolicy(
|
||||||
|
StrictMode.ThreadPolicy
|
||||||
|
.Builder()
|
||||||
|
.detectNetwork()
|
||||||
|
.penaltyLog()
|
||||||
|
.penaltyDeathOnNetwork()
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
|
StrictMode.setVmPolicy(
|
||||||
|
StrictMode.VmPolicy
|
||||||
|
.Builder()
|
||||||
|
.detectAll()
|
||||||
|
.penaltyLog()
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
|
}
|
||||||
OkHttp.initialize(this)
|
OkHttp.initialize(this)
|
||||||
initAcra {
|
initAcra {
|
||||||
buildConfigClass = BuildConfig::class.java
|
buildConfigClass = BuildConfig::class.java
|
||||||
|
|
|
||||||
|
|
@ -42,8 +42,6 @@ class ServerRepository
|
||||||
val apiClient: ApiClient,
|
val apiClient: ApiClient,
|
||||||
val userPreferencesDataStore: DataStore<AppPreferences>,
|
val userPreferencesDataStore: DataStore<AppPreferences>,
|
||||||
) {
|
) {
|
||||||
private val sharedPreferences = getServerSharedPreferences(context)
|
|
||||||
|
|
||||||
private var _current = EqualityMutableLiveData<CurrentUser?>(null)
|
private var _current = EqualityMutableLiveData<CurrentUser?>(null)
|
||||||
val current: LiveData<CurrentUser?> = _current
|
val current: LiveData<CurrentUser?> = _current
|
||||||
|
|
||||||
|
|
@ -107,7 +105,7 @@ class ServerRepository
|
||||||
_current.value = CurrentUser(updatedServer, updatedUser)
|
_current.value = CurrentUser(updatedServer, updatedUser)
|
||||||
_currentUserDto.value = userDto
|
_currentUserDto.value = userDto
|
||||||
}
|
}
|
||||||
sharedPreferences.edit(true) {
|
getServerSharedPreferences(context).edit(true) {
|
||||||
putString(SERVER_URL_KEY, updatedServer.url)
|
putString(SERVER_URL_KEY, updatedServer.url)
|
||||||
putString(ACCESS_TOKEN_KEY, updatedUser.accessToken)
|
putString(ACCESS_TOKEN_KEY, updatedUser.accessToken)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ class BackdropService
|
||||||
if (item.type == BaseItemKind.GENRE) {
|
if (item.type == BaseItemKind.GENRE) {
|
||||||
item.imageUrlOverride
|
item.imageUrlOverride
|
||||||
} else {
|
} else {
|
||||||
imageUrlService.getItemImageUrl(item, ImageType.BACKDROP)!!
|
imageUrlService.getItemImageUrl(item, ImageType.BACKDROP)
|
||||||
}
|
}
|
||||||
submit(item.id.toString(), imageUrl)
|
submit(item.id.toString(), imageUrl)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,11 +28,11 @@ class ImageUrlService
|
||||||
itemType: BaseItemKind,
|
itemType: BaseItemKind,
|
||||||
seriesId: UUID?,
|
seriesId: UUID?,
|
||||||
useSeriesForPrimary: Boolean,
|
useSeriesForPrimary: Boolean,
|
||||||
imageTags: Map<ImageType, String?>,
|
|
||||||
imageType: ImageType,
|
imageType: ImageType,
|
||||||
|
imageTags: Map<ImageType, String?>,
|
||||||
|
backdropTags: List<String>,
|
||||||
parentThumbId: UUID? = null,
|
parentThumbId: UUID? = null,
|
||||||
parentBackdropId: UUID? = null,
|
parentBackdropId: UUID? = null,
|
||||||
backdropTags: List<String> = emptyList(),
|
|
||||||
fillWidth: Int? = null,
|
fillWidth: Int? = null,
|
||||||
fillHeight: Int? = null,
|
fillHeight: Int? = null,
|
||||||
): String? =
|
): String? =
|
||||||
|
|
|
||||||
|
|
@ -11,11 +11,13 @@ import com.github.damontecres.wholphin.ui.main.settings.Library
|
||||||
import com.github.damontecres.wholphin.ui.nav.Destination
|
import com.github.damontecres.wholphin.ui.nav.Destination
|
||||||
import com.github.damontecres.wholphin.ui.nav.NavDrawerItem
|
import com.github.damontecres.wholphin.ui.nav.NavDrawerItem
|
||||||
import com.github.damontecres.wholphin.ui.nav.ServerNavDrawerItem
|
import com.github.damontecres.wholphin.ui.nav.ServerNavDrawerItem
|
||||||
|
import com.github.damontecres.wholphin.ui.showToast
|
||||||
import com.github.damontecres.wholphin.util.supportedCollectionTypes
|
import com.github.damontecres.wholphin.util.supportedCollectionTypes
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.catch
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
|
@ -60,7 +62,11 @@ class NavDrawerService
|
||||||
if (user != null && userDto != null && user.id == userDto.id) {
|
if (user != null && userDto != null && user.id == userDto.id) {
|
||||||
updateNavDrawer(user, userDto)
|
updateNavDrawer(user, userDto)
|
||||||
}
|
}
|
||||||
|
}.catch { ex ->
|
||||||
|
Timber.e(ex, "Error updating nav drawer")
|
||||||
|
showToast(context, "Error fetching user's views")
|
||||||
}.launchIn(coroutineScope)
|
}.launchIn(coroutineScope)
|
||||||
|
|
||||||
seerrServerRepository.active
|
seerrServerRepository.active
|
||||||
.onEach { discoverActive ->
|
.onEach { discoverActive ->
|
||||||
_state.update { it.copy(discoverEnabled = discoverActive) }
|
_state.update { it.copy(discoverEnabled = discoverActive) }
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,12 @@
|
||||||
package com.github.damontecres.wholphin.services
|
package com.github.damontecres.wholphin.services
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.view.WindowManager
|
|
||||||
import coil3.imageLoader
|
import coil3.imageLoader
|
||||||
import coil3.request.ImageRequest
|
import coil3.request.ImageRequest
|
||||||
import com.github.damontecres.wholphin.MainActivity
|
|
||||||
import com.github.damontecres.wholphin.services.hilt.DefaultCoroutineScope
|
import com.github.damontecres.wholphin.services.hilt.DefaultCoroutineScope
|
||||||
import com.github.damontecres.wholphin.ui.components.CurrentItem
|
import com.github.damontecres.wholphin.ui.components.CurrentItem
|
||||||
import com.github.damontecres.wholphin.ui.formatDate
|
import com.github.damontecres.wholphin.ui.formatDate
|
||||||
|
import com.github.damontecres.wholphin.ui.launchDefault
|
||||||
import com.github.damontecres.wholphin.util.ApiRequestPager
|
import com.github.damontecres.wholphin.util.ApiRequestPager
|
||||||
import com.github.damontecres.wholphin.util.ExceptionHandler
|
import com.github.damontecres.wholphin.util.ExceptionHandler
|
||||||
import com.github.damontecres.wholphin.util.GetItemsRequestHandler
|
import com.github.damontecres.wholphin.util.GetItemsRequestHandler
|
||||||
|
|
@ -23,11 +22,11 @@ import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.cancellable
|
import kotlinx.coroutines.flow.cancellable
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.flow
|
import kotlinx.coroutines.flow.flow
|
||||||
|
import kotlinx.coroutines.flow.flowOn
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import org.jellyfin.sdk.api.client.ApiClient
|
import org.jellyfin.sdk.api.client.ApiClient
|
||||||
import org.jellyfin.sdk.api.client.extensions.libraryApi
|
import org.jellyfin.sdk.api.client.extensions.libraryApi
|
||||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||||
|
|
@ -52,6 +51,8 @@ class ScreensaverService
|
||||||
private val _state = MutableStateFlow(ScreensaverState(false, false, false, false))
|
private val _state = MutableStateFlow(ScreensaverState(false, false, false, false))
|
||||||
val state: StateFlow<ScreensaverState> = _state
|
val state: StateFlow<ScreensaverState> = _state
|
||||||
|
|
||||||
|
val keepScreenOn = MutableStateFlow(false)
|
||||||
|
|
||||||
private var waitJob: Job? = null
|
private var waitJob: Job? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
|
@ -114,7 +115,7 @@ class ScreensaverService
|
||||||
}
|
}
|
||||||
|
|
||||||
fun keepScreenOn(keep: Boolean) {
|
fun keepScreenOn(keep: Boolean) {
|
||||||
scope.launch {
|
scope.launchDefault {
|
||||||
val screensaverEnabled = _state.value.enabled
|
val screensaverEnabled = _state.value.enabled
|
||||||
Timber.d("Keep screen on: %s, screensaverEnabled=%s", keep, screensaverEnabled)
|
Timber.d("Keep screen on: %s, screensaverEnabled=%s", keep, screensaverEnabled)
|
||||||
if (screensaverEnabled) {
|
if (screensaverEnabled) {
|
||||||
|
|
@ -131,15 +132,9 @@ class ScreensaverService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun keepScreenOnInternal(keep: Boolean) =
|
private fun keepScreenOnInternal(keep: Boolean) {
|
||||||
withContext(Dispatchers.Main) {
|
keepScreenOn.update { keep }
|
||||||
val window = MainActivity.instance.window
|
}
|
||||||
if (keep) {
|
|
||||||
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
|
||||||
} else {
|
|
||||||
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun createItemFlow(scope: CoroutineScope): Flow<CurrentItem?> =
|
fun createItemFlow(scope: CoroutineScope): Flow<CurrentItem?> =
|
||||||
flow {
|
flow {
|
||||||
|
|
@ -186,7 +181,7 @@ class ScreensaverService
|
||||||
}
|
}
|
||||||
val logoUrl = imageUrlService.getItemImageUrl(item, ImageType.LOGO)
|
val logoUrl = imageUrlService.getItemImageUrl(item, ImageType.LOGO)
|
||||||
if (backdropUrl != null) {
|
if (backdropUrl != null) {
|
||||||
val result =
|
try {
|
||||||
context.imageLoader
|
context.imageLoader
|
||||||
.enqueue(
|
.enqueue(
|
||||||
ImageRequest
|
ImageRequest
|
||||||
|
|
@ -195,7 +190,6 @@ class ScreensaverService
|
||||||
.build(),
|
.build(),
|
||||||
).job
|
).job
|
||||||
.await()
|
.await()
|
||||||
try {
|
|
||||||
emit(CurrentItem(item, backdropUrl, logoUrl, title ?: ""))
|
emit(CurrentItem(item, backdropUrl, logoUrl, title ?: ""))
|
||||||
} catch (_: CancellationException) {
|
} catch (_: CancellationException) {
|
||||||
break
|
break
|
||||||
|
|
@ -211,7 +205,7 @@ class ScreensaverService
|
||||||
index++
|
index++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.cancellable()
|
}.flowOn(Dispatchers.Default).cancellable()
|
||||||
}
|
}
|
||||||
|
|
||||||
data class ScreensaverState(
|
data class ScreensaverState(
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import com.github.damontecres.wholphin.preferences.AppPreferences
|
||||||
import com.github.damontecres.wholphin.preferences.UserPreferences
|
import com.github.damontecres.wholphin.preferences.UserPreferences
|
||||||
import com.github.damontecres.wholphin.preferences.updateInterfacePreferences
|
import com.github.damontecres.wholphin.preferences.updateInterfacePreferences
|
||||||
import com.github.damontecres.wholphin.services.SeerrApi
|
import com.github.damontecres.wholphin.services.SeerrApi
|
||||||
|
import com.github.damontecres.wholphin.util.CoroutineContextApiClientFactory
|
||||||
import com.github.damontecres.wholphin.util.ExceptionHandler
|
import com.github.damontecres.wholphin.util.ExceptionHandler
|
||||||
import com.github.damontecres.wholphin.util.RememberTabManager
|
import com.github.damontecres.wholphin.util.RememberTabManager
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
|
|
@ -111,12 +112,12 @@ object AppModule {
|
||||||
@Singleton
|
@Singleton
|
||||||
fun okHttpFactory(
|
fun okHttpFactory(
|
||||||
@StandardOkHttpClient okHttpClient: OkHttpClient,
|
@StandardOkHttpClient okHttpClient: OkHttpClient,
|
||||||
) = OkHttpFactory(okHttpClient)
|
) = CoroutineContextApiClientFactory(OkHttpFactory(okHttpClient))
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun jellyfin(
|
fun jellyfin(
|
||||||
okHttpFactory: OkHttpFactory,
|
okHttpFactory: CoroutineContextApiClientFactory,
|
||||||
@ApplicationContext context: Context,
|
@ApplicationContext context: Context,
|
||||||
clientInfo: ClientInfo,
|
clientInfo: ClientInfo,
|
||||||
deviceInfo: DeviceInfo,
|
deviceInfo: DeviceInfo,
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,6 @@ import org.jellyfin.sdk.api.client.ApiClient
|
||||||
import org.jellyfin.sdk.api.client.Response
|
import org.jellyfin.sdk.api.client.Response
|
||||||
import org.jellyfin.sdk.model.api.BaseItemDto
|
import org.jellyfin.sdk.model.api.BaseItemDto
|
||||||
import org.jellyfin.sdk.model.api.BaseItemDtoQueryResult
|
import org.jellyfin.sdk.model.api.BaseItemDtoQueryResult
|
||||||
import org.jellyfin.sdk.model.api.ImageType
|
|
||||||
import org.jellyfin.sdk.model.extensions.ticks
|
import org.jellyfin.sdk.model.extensions.ticks
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
@ -429,12 +428,6 @@ fun Response<BaseItemDtoQueryResult>.toBaseItems(
|
||||||
useSeriesForPrimary: Boolean,
|
useSeriesForPrimary: Boolean,
|
||||||
) = this.content.items.map { BaseItem.from(it, api, useSeriesForPrimary) }
|
) = this.content.items.map { BaseItem.from(it, api, useSeriesForPrimary) }
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun rememberBackDropImage(item: BaseItem): String? {
|
|
||||||
val imageUrlService = LocalImageUrlService.current
|
|
||||||
return remember(item) { imageUrlService.getItemImageUrl(item, ImageType.BACKDROP) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if this, coalescing nulls to zero, is greater than that
|
* Check if this, coalescing nulls to zero, is greater than that
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,7 @@ suspend fun getGenreImageMap(
|
||||||
imageType = ImageType.BACKDROP,
|
imageType = ImageType.BACKDROP,
|
||||||
imageTags = item.imageTags.orEmpty(),
|
imageTags = item.imageTags.orEmpty(),
|
||||||
fillWidth = cardWidthPx,
|
fillWidth = cardWidthPx,
|
||||||
|
backdropTags = item.backdropImageTags.orEmpty(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
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
|
||||||
|
import org.jellyfin.sdk.api.client.HttpClientOptions
|
||||||
|
import org.jellyfin.sdk.api.client.HttpMethod
|
||||||
|
import org.jellyfin.sdk.api.client.RawResponse
|
||||||
|
import org.jellyfin.sdk.api.okhttp.OkHttpFactory
|
||||||
|
import org.jellyfin.sdk.api.sockets.SocketApi
|
||||||
|
import org.jellyfin.sdk.api.sockets.SocketConnection
|
||||||
|
import org.jellyfin.sdk.api.sockets.SocketConnectionFactory
|
||||||
|
import org.jellyfin.sdk.model.ClientInfo
|
||||||
|
import org.jellyfin.sdk.model.DeviceInfo
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps [ApiClient.request] with the given [CoroutineContext]
|
||||||
|
*/
|
||||||
|
class CoroutineContextApiClient(
|
||||||
|
private val client: ApiClient,
|
||||||
|
private val coroutineContext: CoroutineContext = Dispatchers.IO,
|
||||||
|
) : ApiClient() {
|
||||||
|
override val baseUrl: String?
|
||||||
|
get() = client.baseUrl
|
||||||
|
override val accessToken: String?
|
||||||
|
get() = client.accessToken
|
||||||
|
override val clientInfo: ClientInfo
|
||||||
|
get() = client.clientInfo
|
||||||
|
override val deviceInfo: DeviceInfo
|
||||||
|
get() = client.deviceInfo
|
||||||
|
override val httpClientOptions: HttpClientOptions
|
||||||
|
get() = client.httpClientOptions
|
||||||
|
override val webSocket: SocketApi
|
||||||
|
get() = client.webSocket
|
||||||
|
|
||||||
|
override fun update(
|
||||||
|
baseUrl: String?,
|
||||||
|
accessToken: String?,
|
||||||
|
clientInfo: ClientInfo,
|
||||||
|
deviceInfo: DeviceInfo,
|
||||||
|
) {
|
||||||
|
client.update(baseUrl, accessToken, clientInfo, deviceInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun request(
|
||||||
|
method: HttpMethod,
|
||||||
|
pathTemplate: String,
|
||||||
|
pathParameters: Map<String, Any?>,
|
||||||
|
queryParameters: Map<String, Any?>,
|
||||||
|
requestBody: Any?,
|
||||||
|
): RawResponse =
|
||||||
|
withContext(coroutineContext) {
|
||||||
|
client.request(
|
||||||
|
method,
|
||||||
|
pathTemplate,
|
||||||
|
pathParameters,
|
||||||
|
queryParameters,
|
||||||
|
requestBody,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CoroutineContextApiClientFactory(
|
||||||
|
private val factory: OkHttpFactory,
|
||||||
|
private val coroutineContext: CoroutineContext = Dispatchers.IO,
|
||||||
|
) : ApiClientFactory,
|
||||||
|
SocketConnectionFactory {
|
||||||
|
override fun create(
|
||||||
|
baseUrl: String?,
|
||||||
|
accessToken: String?,
|
||||||
|
clientInfo: ClientInfo,
|
||||||
|
deviceInfo: DeviceInfo,
|
||||||
|
httpClientOptions: HttpClientOptions,
|
||||||
|
socketConnectionFactory: SocketConnectionFactory,
|
||||||
|
): ApiClient =
|
||||||
|
CoroutineContextApiClient(
|
||||||
|
factory.create(baseUrl, accessToken, clientInfo, deviceInfo, httpClientOptions, socketConnectionFactory),
|
||||||
|
coroutineContext,
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun create(
|
||||||
|
clientOptions: HttpClientOptions,
|
||||||
|
scope: CoroutineScope,
|
||||||
|
): SocketConnection = factory.create(clientOptions, scope)
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue