diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt index 4522299310..be2387002a 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt @@ -279,6 +279,16 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), SafeStoreSubscriber + if (isOnline) { + (activity as? MainActivity)?.dismissSnackbar() + } else { + isNetworkConnectionError.value = true + binding.srlWallet.isRefreshing = false + (activity as? MainActivity)?.showSnackbar( + text = R.string.wallet_notification_no_internet, + buttonTitle = R.string.common_retry, + ) + } if (isOnline && isNetworkConnectionError.value) { refreshWalletData() } diff --git a/core/datasource/build.gradle.kts b/core/datasource/build.gradle.kts index 36c8fda7a2..3350c7930f 100644 --- a/core/datasource/build.gradle.kts +++ b/core/datasource/build.gradle.kts @@ -22,6 +22,7 @@ dependencies { /** Coroutines */ implementation(deps.kotlin.coroutines) + implementation(deps.kotlin.coroutines.reactive) /** Logging */ implementation(deps.timber) @@ -34,6 +35,7 @@ dependencies { implementation(deps.okHttp.logging) implementation(deps.retrofit) implementation(deps.retrofit.moshi) + implementation(deps.reactive.network) /** Time */ implementation(deps.jodatime) diff --git a/core/datasource/src/main/AndroidManifest.xml b/core/datasource/src/main/AndroidManifest.xml index b7e7790e14..a87322003a 100644 --- a/core/datasource/src/main/AndroidManifest.xml +++ b/core/datasource/src/main/AndroidManifest.xml @@ -3,4 +3,5 @@ xmlns:android="http://schemas.android.com/apk/res/android"> - \ No newline at end of file + + diff --git a/core/datasource/src/main/java/com/tangem/datasource/connection/AndroidNetworkConnectionManager.kt b/core/datasource/src/main/java/com/tangem/datasource/connection/AndroidNetworkConnectionManager.kt deleted file mode 100644 index 6581fbb384..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/connection/AndroidNetworkConnectionManager.kt +++ /dev/null @@ -1,110 +0,0 @@ -package com.tangem.datasource.connection - -import android.app.Activity -import android.app.Application -import android.app.Application.ActivityLifecycleCallbacks -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import android.content.IntentFilter -import android.net.NetworkCapabilities -import android.os.Build -import android.os.Bundle -import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import dagger.hilt.android.qualifiers.ApplicationContext -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.launch -import timber.log.Timber -import javax.inject.Inject -import javax.inject.Singleton -import android.net.ConnectivityManager as AndroidConnectivityManager - -/** - * Network connection manager implementation using Android connectivity service - * - * @property context application context for registration lifecycle callbacks - * @property dispatchers coroutine dispatcher provider - * -[REDACTED_AUTHOR] - */ -@Singleton -internal class AndroidNetworkConnectionManager @Inject constructor( - @ApplicationContext private val context: Context, - private val dispatchers: CoroutineDispatcherProvider, -) : NetworkConnectionManager { - - private val _isOnline = MutableStateFlow(value = false) - private val callbacks = NetworkConnectionManagerCallbacks() - private val receiver = NetworkConnectionBroadcastReceiver() - - override val isOnline: Boolean get() = _isOnline.value - override val isOnlineFlow: StateFlow = _isOnline - - init { - (context as? Application)?.registerActivityLifecycleCallbacks(callbacks) - } - - private inner class NetworkConnectionManagerCallbacks : ActivityLifecycleCallbacks { - - override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { - val intentFilter = IntentFilter().apply { - addAction(AndroidConnectivityManager.CONNECTIVITY_ACTION) - } - - activity.registerReceiver(receiver, intentFilter) - } - - override fun onActivityStarted(activity: Activity) = Unit - override fun onActivityResumed(activity: Activity) = Unit - override fun onActivityPaused(activity: Activity) = Unit - override fun onActivityStopped(activity: Activity) = Unit - override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) = Unit - - override fun onActivityDestroyed(activity: Activity) { - activity.unregisterReceiver(receiver) - (context as? Application)?.unregisterActivityLifecycleCallbacks(callbacks) - } - } - - private inner class NetworkConnectionBroadcastReceiver : BroadcastReceiver() { - - private val connectivityManager: android.net.ConnectivityManager? = - context.getSystemService(Context.CONNECTIVITY_SERVICE) as? android.net.ConnectivityManager - - override fun onReceive(c: Context?, intent: Intent?) { - val pendingResult = goAsync() - - CoroutineScope(SupervisorJob()).launch(dispatchers.io) { - try { - val status = isConnected() - - _isOnline.emit(value = status) - Timber.i("Connection status is ${if (status) "ONLINE" else "OFFLINE"}") - } finally { - pendingResult.finish() - } - } - } - - private fun isConnected(): Boolean { - connectivityManager ?: return false - - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - val capabilities = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork) - capabilities != null && capabilities.hasNetworkTransport() - } else { - val networkInfo = connectivityManager.activeNetworkInfo - networkInfo != null && networkInfo.isConnectedOrConnecting - } - } - - private fun NetworkCapabilities.hasNetworkTransport(): Boolean { - return hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) || - hasTransport(NetworkCapabilities.TRANSPORT_WIFI) || - hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) - } - } -} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/connection/RealInternetConnectionManager.kt b/core/datasource/src/main/java/com/tangem/datasource/connection/RealInternetConnectionManager.kt new file mode 100644 index 0000000000..669dc8ed20 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/connection/RealInternetConnectionManager.kt @@ -0,0 +1,29 @@ +package com.tangem.datasource.connection + +import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork +import com.tangem.utils.coroutines.FeatureCoroutineExceptionHandler +import io.reactivex.BackpressureStrategy +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.reactive.asFlow + +internal class RealInternetConnectionManager : NetworkConnectionManager { + + private val scope = + CoroutineScope( + SupervisorJob() + Dispatchers.IO + FeatureCoroutineExceptionHandler.create("RealInternetConnectionManager"), + ) + + override val isOnlineFlow: StateFlow = ReactiveNetwork + .observeInternetConnectivity() + .toFlowable(BackpressureStrategy.LATEST) + .asFlow() + .stateIn(scope, SharingStarted.Eagerly, initialValue = false) + + override val isOnline: Boolean + get() = isOnlineFlow.value +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/NetworkConnectionModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/NetworkConnectionModule.kt index d112c7ccab..b3eca89707 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/di/NetworkConnectionModule.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/di/NetworkConnectionModule.kt @@ -1,20 +1,20 @@ package com.tangem.datasource.di -import com.tangem.datasource.connection.AndroidNetworkConnectionManager import com.tangem.datasource.connection.NetworkConnectionManager -import dagger.Binds +import com.tangem.datasource.connection.RealInternetConnectionManager import dagger.Module +import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) -internal interface NetworkConnectionModule { +internal class NetworkConnectionModule { - @Binds + @Provides @Singleton - fun bindNetworkConnectionManager( - androidNetworkConnectionManager: AndroidNetworkConnectionManager, - ): NetworkConnectionManager + fun provideNetworkConnectionManager(): NetworkConnectionManager { + return RealInternetConnectionManager() + } } \ No newline at end of file diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 860b2162f4..452d9dcf83 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -69,6 +69,7 @@ zxingQrCode = "3.5.1" mviCore = "1.3.1" kotlinSerialization = "1.4.1" arrow = "1.2.0-RC" +reactiveNetwork = "3.0.8" # endregion Other libraries # region Tangem @@ -174,6 +175,7 @@ appsflyer = { module = "com.appsflyer:af-android-sdk", version.ref = "appsflyer" armadillo = { module = "at.favre.lib:armadillo", version.ref = "armadillo" } coil = { module = "io.coil-kt:coil", version.ref = "coil" } kotlin-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutine" } +kotlin-coroutines-reactive = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-reactive", version.ref = "coroutine" } kotlin-immutable-collections = { module = "org.jetbrains.kotlinx:kotlinx-collections-immutable", version.ref = "kotlin-immutable-collections" } desugar = { module = "com.android.tools:desugar_jdk_libs", version.ref = "desugarJdkLibs" } googlePlay-core = { module = "com.google.android.play:core", version.ref = "googlePlayCore" } @@ -209,4 +211,5 @@ mviCore-watcher = { module = "com.github.badoo.mvicore:mvicore-diff", version.re kotlin-serialization = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinSerialization" } arrow-core = { module = "io.arrow-kt:arrow-core", version.ref = "arrow" } arrow-fx = { module = "io.arrow-kt:arrow-fx-coroutines", version.ref = "arrow" } +reactive-network = { module = "com.github.pwittchen:reactivenetwork-rx2", version.ref = "reactiveNetwork" } # endregion Other