mirror of
https://github.com/owenlejeune/TVTime.git
synced 2025-11-08 12:42:44 -05:00
add network monitor and error message popups
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
|
|||||||
@@ -2,11 +2,14 @@ package com.owenlejeune.tvtime
|
|||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
import com.owenlejeune.tvtime.extensions.launchActivity
|
import com.owenlejeune.tvtime.extensions.launchActivity
|
||||||
import com.owenlejeune.tvtime.preferences.AppPreferences
|
import com.owenlejeune.tvtime.preferences.AppPreferences
|
||||||
import com.owenlejeune.tvtime.ui.viewmodel.ConfigurationViewModel
|
import com.owenlejeune.tvtime.ui.viewmodel.ConfigurationViewModel
|
||||||
import com.owenlejeune.tvtime.utils.TmdbUtils
|
import com.owenlejeune.tvtime.utils.TmdbUtils
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import org.koin.android.ext.android.inject
|
import org.koin.android.ext.android.inject
|
||||||
import org.koin.androidx.viewmodel.ext.android.getViewModel
|
import org.koin.androidx.viewmodel.ext.android.getViewModel
|
||||||
|
|
||||||
@@ -17,15 +20,17 @@ class AppRoutingActivity: AppCompatActivity() {
|
|||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launch {
|
||||||
val configurationViewModel = getViewModel<ConfigurationViewModel>()
|
lifecycle.repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||||
configurationViewModel.getConfigurations()
|
val configurationViewModel = getViewModel<ConfigurationViewModel>()
|
||||||
TmdbUtils.setup(configurationViewModel)
|
configurationViewModel.getConfigurations()
|
||||||
|
TmdbUtils.setup(configurationViewModel)
|
||||||
|
|
||||||
if (preferences.firstLaunchTesting || preferences.firstLaunch) {
|
if (preferences.firstLaunchTesting || preferences.firstLaunch) {
|
||||||
launchActivity(OnboardingActivity::class.java)
|
launchActivity(OnboardingActivity::class.java)
|
||||||
} else {
|
} else {
|
||||||
launchActivity(MainActivity::class.java)
|
launchActivity(MainActivity::class.java)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,35 @@
|
|||||||
package com.owenlejeune.tvtime
|
package com.owenlejeune.tvtime
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.SnackbarHost
|
||||||
|
import androidx.compose.material3.SnackbarHostState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.DisposableEffect
|
import androidx.compose.runtime.DisposableEffect
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalFocusManager
|
import androidx.compose.ui.platform.LocalFocusManager
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import com.kieronquinn.monetcompat.app.MonetCompatActivity
|
import com.kieronquinn.monetcompat.app.MonetCompatActivity
|
||||||
import com.owenlejeune.tvtime.extensions.rememberWindowSizeClass
|
import com.owenlejeune.tvtime.extensions.rememberWindowSizeClass
|
||||||
import com.owenlejeune.tvtime.ui.navigation.AppNavigationHost
|
import com.owenlejeune.tvtime.ui.navigation.AppNavigationHost
|
||||||
import com.owenlejeune.tvtime.ui.navigation.HomeScreenNavItem
|
import com.owenlejeune.tvtime.ui.navigation.HomeScreenNavItem
|
||||||
import com.owenlejeune.tvtime.ui.theme.TVTimeTheme
|
import com.owenlejeune.tvtime.ui.theme.TVTimeTheme
|
||||||
|
import com.owenlejeune.tvtime.ui.viewmodel.ConfigurationViewModel
|
||||||
import com.owenlejeune.tvtime.utils.KeyboardManager
|
import com.owenlejeune.tvtime.utils.KeyboardManager
|
||||||
|
import com.owenlejeune.tvtime.utils.NetworkStatus
|
||||||
import com.owenlejeune.tvtime.utils.SessionManager
|
import com.owenlejeune.tvtime.utils.SessionManager
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@@ -22,6 +37,7 @@ import kotlinx.coroutines.launch
|
|||||||
|
|
||||||
class MainActivity : MonetCompatActivity() {
|
class MainActivity : MonetCompatActivity() {
|
||||||
|
|
||||||
|
@SuppressLint("AutoboxingStateValueProperty")
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
@@ -31,19 +47,53 @@ class MainActivity : MonetCompatActivity() {
|
|||||||
|
|
||||||
val mainNavStartRoute = HomeScreenNavItem.SortedItems[0].route
|
val mainNavStartRoute = HomeScreenNavItem.SortedItems[0].route
|
||||||
|
|
||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launch {
|
||||||
monet.awaitMonetReady()
|
lifecycle.repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||||
setContent {
|
monet.awaitMonetReady()
|
||||||
AppKeyboardFocusManager()
|
setContent {
|
||||||
TVTimeTheme(monetCompat = monet) {
|
AppKeyboardFocusManager()
|
||||||
val windowSize = rememberWindowSizeClass()
|
TVTimeTheme(monetCompat = monet) {
|
||||||
val appNavController = rememberNavController()
|
val snackbarHostState = SnackbarHostState()
|
||||||
Box {
|
|
||||||
AppNavigationHost(
|
val configViewModel = viewModel<ConfigurationViewModel>()
|
||||||
appNavController = appNavController,
|
val networkStatus = configViewModel.networkStatus.collectAsState()
|
||||||
mainNavStartRoute = mainNavStartRoute,
|
if (networkStatus.value == NetworkStatus.Disconnected) {
|
||||||
windowSize = windowSize
|
LaunchedEffect(networkStatus) {
|
||||||
)
|
snackbarHostState.showSnackbar(getString(R.string.network_disconnected_message))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val lastResponseCode = remember { configViewModel.lastResponseCode }
|
||||||
|
val code = lastResponseCode.value
|
||||||
|
LaunchedEffect(code) {
|
||||||
|
when (code) {
|
||||||
|
429 -> Toast.makeText(
|
||||||
|
this@MainActivity,
|
||||||
|
getString(R.string.network_api_rate_limit_reached),
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
|
||||||
|
in 400..599 -> Toast.makeText(
|
||||||
|
this@MainActivity,
|
||||||
|
getString(R.string.network_error_occurred, code),
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Scaffold(
|
||||||
|
snackbarHost = { SnackbarHost(hostState = snackbarHostState) }
|
||||||
|
) {
|
||||||
|
val windowSize = rememberWindowSizeClass()
|
||||||
|
val appNavController = rememberNavController()
|
||||||
|
Box(modifier = Modifier.padding(it)) {
|
||||||
|
AppNavigationHost(
|
||||||
|
appNavController = appNavController,
|
||||||
|
mainNavStartRoute = mainNavStartRoute,
|
||||||
|
windowSize = windowSize
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,9 @@ import androidx.compose.ui.text.style.TextAlign
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.core.os.BuildCompat
|
import androidx.core.os.BuildCompat
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
import com.google.accompanist.pager.*
|
import com.google.accompanist.pager.*
|
||||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||||
import com.kieronquinn.monetcompat.app.MonetCompatActivity
|
import com.kieronquinn.monetcompat.app.MonetCompatActivity
|
||||||
@@ -58,11 +60,13 @@ class OnboardingActivity: MonetCompatActivity() {
|
|||||||
@SuppressLint("UnsafeOptInUsageError")
|
@SuppressLint("UnsafeOptInUsageError")
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launch {
|
||||||
monet.awaitMonetReady()
|
lifecycle.repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||||
setContent {
|
monet.awaitMonetReady()
|
||||||
TVTimeTheme(monetCompat = monet) {
|
setContent {
|
||||||
OnboardingUi()
|
TVTimeTheme(monetCompat = monet) {
|
||||||
|
OnboardingUi()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import com.owenlejeune.tvtime.di.modules.networkModule
|
|||||||
import com.owenlejeune.tvtime.di.modules.preferencesModule
|
import com.owenlejeune.tvtime.di.modules.preferencesModule
|
||||||
import com.owenlejeune.tvtime.di.modules.viewModelModule
|
import com.owenlejeune.tvtime.di.modules.viewModelModule
|
||||||
import com.owenlejeune.tvtime.preferences.AppPreferences
|
import com.owenlejeune.tvtime.preferences.AppPreferences
|
||||||
import dev.kdrag0n.monet.factory.ColorSchemeFactory
|
|
||||||
import org.koin.android.ext.android.inject
|
import org.koin.android.ext.android.inject
|
||||||
import org.koin.android.ext.koin.androidContext
|
import org.koin.android.ext.koin.androidContext
|
||||||
import org.koin.android.ext.koin.androidLogger
|
import org.koin.android.ext.koin.androidLogger
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package com.owenlejeune.tvtime.api.tmdb
|
package com.owenlejeune.tvtime.api.tmdb
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import androidx.compose.ui.text.intl.Locale
|
import androidx.compose.ui.text.intl.Locale
|
||||||
|
import androidx.lifecycle.viewmodel.viewModelFactory
|
||||||
import com.owenlejeune.tvtime.BuildConfig
|
import com.owenlejeune.tvtime.BuildConfig
|
||||||
import com.owenlejeune.tvtime.api.Client
|
import com.owenlejeune.tvtime.api.Client
|
||||||
import com.owenlejeune.tvtime.api.QueryParam
|
import com.owenlejeune.tvtime.api.QueryParam
|
||||||
@@ -17,6 +19,7 @@ import com.owenlejeune.tvtime.api.tmdb.api.v4.AuthenticationV4Api
|
|||||||
import com.owenlejeune.tvtime.api.tmdb.api.v4.ListV4Api
|
import com.owenlejeune.tvtime.api.tmdb.api.v4.ListV4Api
|
||||||
import com.owenlejeune.tvtime.extensions.addQueryParams
|
import com.owenlejeune.tvtime.extensions.addQueryParams
|
||||||
import com.owenlejeune.tvtime.preferences.AppPreferences
|
import com.owenlejeune.tvtime.preferences.AppPreferences
|
||||||
|
import com.owenlejeune.tvtime.ui.viewmodel.ConfigurationViewModel
|
||||||
import com.owenlejeune.tvtime.utils.SessionManager
|
import com.owenlejeune.tvtime.utils.SessionManager
|
||||||
import okhttp3.Interceptor
|
import okhttp3.Interceptor
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
@@ -33,7 +36,6 @@ class TmdbClient: KoinComponent {
|
|||||||
|
|
||||||
private val client: Client by inject { parametersOf(V_3_BASE_URL) }
|
private val client: Client by inject { parametersOf(V_3_BASE_URL) }
|
||||||
private val clientV4: Client by inject { parametersOf(V_4_BASE_URL) }
|
private val clientV4: Client by inject { parametersOf(V_4_BASE_URL) }
|
||||||
private val preferences: AppPreferences by inject()
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
client.addInterceptor(TmdbInterceptor())
|
client.addInterceptor(TmdbInterceptor())
|
||||||
@@ -84,6 +86,21 @@ class TmdbClient: KoinComponent {
|
|||||||
return clientV4.create(ListV4Api::class.java)
|
return clientV4.create(ListV4Api::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("AutoboxingStateValueProperty")
|
||||||
|
private fun handleResponseCode(response: Response) {
|
||||||
|
when (response.code) {
|
||||||
|
429 -> {
|
||||||
|
ConfigurationViewModel.lastResponseCode.value = 429
|
||||||
|
}
|
||||||
|
in 400..499 -> {
|
||||||
|
ConfigurationViewModel.lastResponseCode.value = response.code
|
||||||
|
}
|
||||||
|
in 500..599 -> {
|
||||||
|
ConfigurationViewModel.lastResponseCode.value = response.code
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private inner class TmdbInterceptor: Interceptor {
|
private inner class TmdbInterceptor: Interceptor {
|
||||||
override fun intercept(chain: Interceptor.Chain): Response {
|
override fun intercept(chain: Interceptor.Chain): Response {
|
||||||
val apiParam = QueryParam("api_key", BuildConfig.TMDB_ApiKey)
|
val apiParam = QueryParam("api_key", BuildConfig.TMDB_ApiKey)
|
||||||
@@ -106,7 +123,11 @@ class TmdbClient: KoinComponent {
|
|||||||
val requestBuilder = chain.request().newBuilder().url(builder.build())
|
val requestBuilder = chain.request().newBuilder().url(builder.build())
|
||||||
|
|
||||||
val request = requestBuilder.build()
|
val request = requestBuilder.build()
|
||||||
return chain.proceed(request)
|
val response = chain.proceed(request)
|
||||||
|
|
||||||
|
handleResponseCode(response)
|
||||||
|
|
||||||
|
return response
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sessionIdParam(urlSegments: List<String>): QueryParam? {
|
private fun sessionIdParam(urlSegments: List<String>): QueryParam? {
|
||||||
@@ -154,7 +175,11 @@ class TmdbClient: KoinComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return chain.proceed(builder.build())
|
val response = chain.proceed(builder.build())
|
||||||
|
|
||||||
|
handleResponseCode(response)
|
||||||
|
|
||||||
|
return response
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun shouldIncludeLanguageParam(urlSegments: List<String>): Boolean {
|
private fun shouldIncludeLanguageParam(urlSegments: List<String>): Boolean {
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ import com.owenlejeune.tvtime.api.tmdb.api.v4.model.ListItem
|
|||||||
import com.owenlejeune.tvtime.preferences.AppPreferences
|
import com.owenlejeune.tvtime.preferences.AppPreferences
|
||||||
import com.owenlejeune.tvtime.ui.viewmodel.ConfigurationViewModel
|
import com.owenlejeune.tvtime.ui.viewmodel.ConfigurationViewModel
|
||||||
import com.owenlejeune.tvtime.ui.viewmodel.SettingsViewModel
|
import com.owenlejeune.tvtime.ui.viewmodel.SettingsViewModel
|
||||||
|
import com.owenlejeune.tvtime.utils.NetworkConnectivityService
|
||||||
|
import com.owenlejeune.tvtime.utils.NetworkConnectivityServiceImpl
|
||||||
import com.owenlejeune.tvtime.utils.ResourceUtils
|
import com.owenlejeune.tvtime.utils.ResourceUtils
|
||||||
import org.koin.androidx.viewmodel.dsl.viewModel
|
import org.koin.androidx.viewmodel.dsl.viewModel
|
||||||
import org.koin.dsl.module
|
import org.koin.dsl.module
|
||||||
@@ -97,9 +99,10 @@ val preferencesModule = module {
|
|||||||
|
|
||||||
val appModule = module {
|
val appModule = module {
|
||||||
factory { ResourceUtils(get()) }
|
factory { ResourceUtils(get()) }
|
||||||
|
|
||||||
|
single<NetworkConnectivityService> { NetworkConnectivityServiceImpl() }
|
||||||
}
|
}
|
||||||
|
|
||||||
val viewModelModule = module {
|
val viewModelModule = module {
|
||||||
viewModel { ConfigurationViewModel() }
|
viewModel { ConfigurationViewModel() }
|
||||||
viewModel { SettingsViewModel() }
|
|
||||||
}
|
}
|
||||||
@@ -20,16 +20,6 @@ fun <T: Any> LazyGridScope.lazyPagingItems(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T: Any> LazyGridScope.listItems(
|
|
||||||
items: List<T>,
|
|
||||||
key: (T?) -> Any,
|
|
||||||
itemContent: @Composable (value: T) -> Unit
|
|
||||||
) {
|
|
||||||
items(items.size) { index ->
|
|
||||||
itemContent(items[index])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun LazyGridScope.header(
|
fun LazyGridScope.header(
|
||||||
content: @Composable LazyGridItemScope.() -> Unit
|
content: @Composable LazyGridItemScope.() -> Unit
|
||||||
) {
|
) {
|
||||||
@@ -42,21 +32,11 @@ fun LazyGridScope.header(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun <T: Any> LazyListScope.listItems(
|
fun <T: Any> LazyListScope.listItems(
|
||||||
items: Collection<T>,
|
items: List<T>,
|
||||||
key: (T?) -> Any,
|
key: ((T) -> Any)? = null,
|
||||||
itemContent: @Composable (value: T) -> Unit
|
itemContent: @Composable (value: T) -> Unit
|
||||||
) {
|
) {
|
||||||
items(items.size) { index ->
|
items(items.size, key = key?.let { { key(items[it]) } }) { index ->
|
||||||
itemContent(items.elementAt(index))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T: Any?> LazyListScope.listItems(
|
|
||||||
items: List<T?>,
|
|
||||||
key: (T?) -> Any,
|
|
||||||
itemContent: @Composable (value: T?) -> Unit
|
|
||||||
) {
|
|
||||||
items(items.size, key = { key(items[it]) }) { index ->
|
|
||||||
itemContent(items[index])
|
itemContent(items[index])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,7 +46,7 @@ fun <T: Any> LazyListScope.lazyPagingItems(
|
|||||||
key: ((index: Int) -> Any)? = null,
|
key: ((index: Int) -> Any)? = null,
|
||||||
itemContent: @Composable LazyItemScope.(value: T?) -> Unit
|
itemContent: @Composable LazyItemScope.(value: T?) -> Unit
|
||||||
) {
|
) {
|
||||||
items(lazyPagingItems.itemCount) { index ->
|
items(lazyPagingItems.itemCount, key = key) { index ->
|
||||||
itemContent(lazyPagingItems[index])
|
itemContent(lazyPagingItems[index])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,25 @@
|
|||||||
package com.owenlejeune.tvtime.ui.viewmodel
|
package com.owenlejeune.tvtime.ui.viewmodel
|
||||||
|
|
||||||
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.ConfigurationService
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.ConfigurationService
|
||||||
|
import com.owenlejeune.tvtime.utils.NetworkConnectivityService
|
||||||
|
import com.owenlejeune.tvtime.utils.NetworkStatus
|
||||||
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
import org.koin.core.component.inject
|
import org.koin.core.component.inject
|
||||||
|
|
||||||
class ConfigurationViewModel: ViewModel(), KoinComponent {
|
class ConfigurationViewModel: ViewModel(), KoinComponent {
|
||||||
|
|
||||||
private val service: ConfigurationService by inject()
|
private val service: ConfigurationService by inject()
|
||||||
|
private val networkConnectivityService: NetworkConnectivityService by inject()
|
||||||
|
|
||||||
|
companion object Storage {
|
||||||
|
val lastResponseCode = mutableIntStateOf(0)
|
||||||
|
}
|
||||||
|
|
||||||
val detailsConfiguration = service.detailsConfiguration
|
val detailsConfiguration = service.detailsConfiguration
|
||||||
val countriesConfiguration = service.countriesConfiguration
|
val countriesConfiguration = service.countriesConfiguration
|
||||||
@@ -16,6 +28,14 @@ class ConfigurationViewModel: ViewModel(), KoinComponent {
|
|||||||
val primaryTranslationsConfiguration = service.primaryTranslationsConfiguration
|
val primaryTranslationsConfiguration = service.primaryTranslationsConfiguration
|
||||||
val timezonesConfiguration = service.timezonesConfiguration
|
val timezonesConfiguration = service.timezonesConfiguration
|
||||||
|
|
||||||
|
val lastResponseCode = Storage.lastResponseCode
|
||||||
|
|
||||||
|
val networkStatus: StateFlow<NetworkStatus> = networkConnectivityService.networkStatus.stateIn(
|
||||||
|
initialValue = NetworkStatus.Unknown,
|
||||||
|
scope = viewModelScope,
|
||||||
|
started = SharingStarted.WhileSubscribed(5000)
|
||||||
|
)
|
||||||
|
|
||||||
suspend fun getConfigurations() {
|
suspend fun getConfigurations() {
|
||||||
service.getDetailsConfiguration()
|
service.getDetailsConfiguration()
|
||||||
service.getCountriesConfiguration()
|
service.getCountriesConfiguration()
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package com.owenlejeune.tvtime.utils
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.net.ConnectivityManager
|
||||||
|
import android.net.ConnectivityManager.NetworkCallback
|
||||||
|
import android.net.Network
|
||||||
|
import android.net.NetworkCapabilities
|
||||||
|
import android.net.NetworkRequest
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.callbackFlow
|
||||||
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
|
import kotlinx.coroutines.flow.flowOn
|
||||||
|
import org.koin.core.component.KoinComponent
|
||||||
|
import org.koin.core.component.inject
|
||||||
|
|
||||||
|
sealed class NetworkStatus {
|
||||||
|
object Unknown: NetworkStatus()
|
||||||
|
object Connected: NetworkStatus()
|
||||||
|
object Disconnected: NetworkStatus()
|
||||||
|
}
|
||||||
|
|
||||||
|
interface NetworkConnectivityService {
|
||||||
|
val networkStatus: Flow<NetworkStatus>
|
||||||
|
}
|
||||||
|
|
||||||
|
class NetworkConnectivityServiceImpl: NetworkConnectivityService, KoinComponent {
|
||||||
|
|
||||||
|
private val context: Context by inject()
|
||||||
|
|
||||||
|
private val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||||
|
|
||||||
|
override val networkStatus: Flow<NetworkStatus> = callbackFlow {
|
||||||
|
val connectivityCallback = object : NetworkCallback() {
|
||||||
|
override fun onAvailable(network: Network) {
|
||||||
|
trySend(NetworkStatus.Connected)
|
||||||
|
}
|
||||||
|
override fun onUnavailable() {
|
||||||
|
trySend(NetworkStatus.Disconnected)
|
||||||
|
}
|
||||||
|
override fun onLost(network: Network) {
|
||||||
|
trySend(NetworkStatus.Disconnected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val request = NetworkRequest.Builder()
|
||||||
|
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
|
||||||
|
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
|
||||||
|
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
connectivityManager.registerNetworkCallback(request, connectivityCallback)
|
||||||
|
|
||||||
|
awaitClose {
|
||||||
|
connectivityManager.unregisterNetworkCallback(connectivityCallback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.distinctUntilChanged()
|
||||||
|
.flowOn(Dispatchers.IO)
|
||||||
|
|
||||||
|
}
|
||||||
@@ -241,4 +241,7 @@
|
|||||||
<string name="latest_season_title">Latest Season</string>
|
<string name="latest_season_title">Latest Season</string>
|
||||||
<string name="season_ep_count">%1$d | %2$d Episodes</string>
|
<string name="season_ep_count">%1$d | %2$d Episodes</string>
|
||||||
<string name="see_all_seasons_text">See all seasons</string>
|
<string name="see_all_seasons_text">See all seasons</string>
|
||||||
|
<string name="network_disconnected_message">Network Disconnected</string>
|
||||||
|
<string name="network_api_rate_limit_reached">API Rate limit reached</string>
|
||||||
|
<string name="network_error_occurred">%1$d: An error occurred</string>
|
||||||
</resources>
|
</resources>
|
||||||
Reference in New Issue
Block a user