diff --git a/app/src/main/java/org/kiwix/kiwixmobile/ConnectivityManagerExtensions.kt b/app/src/main/java/org/kiwix/kiwixmobile/ConnectivityManagerExtensions.kt index 090e025c7..cb9928066 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/ConnectivityManagerExtensions.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/ConnectivityManagerExtensions.kt @@ -19,12 +19,13 @@ package org.kiwix.kiwixmobile import android.net.ConnectivityManager +import org.kiwix.kiwixmobile.core.compat.CompatHelper.Companion.isNetworkAvailable import org.kiwix.kiwixmobile.zimManager.NetworkState import org.kiwix.kiwixmobile.zimManager.NetworkState.CONNECTED import org.kiwix.kiwixmobile.zimManager.NetworkState.NOT_CONNECTED val ConnectivityManager.networkState: NetworkState - get() = if (activeNetworkInfo?.isConnected == true) + get() = if (isNetworkAvailable()) CONNECTED else NOT_CONNECTED diff --git a/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/OnlineLibraryFragment.kt b/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/OnlineLibraryFragment.kt index a39088d9d..963b28772 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/OnlineLibraryFragment.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/OnlineLibraryFragment.kt @@ -135,7 +135,7 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions { get() = sharedPreferenceUtil.prefWifiOnly && !NetworkUtils.isWiFi(requireContext()) private val isNotConnected: Boolean - get() = NetworkUtils.isNetworkAvailable(requireActivity()) + get() = !NetworkUtils.isNetworkAvailable(requireActivity()) override fun inject(baseActivity: BaseActivity) { baseActivity.cachedComponent.inject(this) diff --git a/app/src/main/java/org/kiwix/kiwixmobile/zimManager/ZimManageViewModel.kt b/app/src/main/java/org/kiwix/kiwixmobile/zimManager/ZimManageViewModel.kt index b447ee661..d41d7eff1 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/zimManager/ZimManageViewModel.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/zimManager/ZimManageViewModel.kt @@ -34,6 +34,7 @@ import io.reactivex.schedulers.Schedulers import org.kiwix.kiwixmobile.core.R import org.kiwix.kiwixmobile.core.StorageObserver import org.kiwix.kiwixmobile.core.base.SideEffect +import org.kiwix.kiwixmobile.core.compat.CompatHelper.Companion.isWifi import org.kiwix.kiwixmobile.core.dao.FetchDownloadDao import org.kiwix.kiwixmobile.core.dao.NewBookDao import org.kiwix.kiwixmobile.core.dao.NewLanguagesDao @@ -229,7 +230,7 @@ class ZimManageViewModel @Inject constructor( ) ) { _, _ -> } .switchMap { - if (connectivityManager.activeNetworkInfo?.type == ConnectivityManager.TYPE_WIFI) { + if (connectivityManager.isWifi()) { Flowable.just(Unit) } else { sharedPreferenceUtil.prefWifiOnlys diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/compat/Compat.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/compat/Compat.kt index fb9f023ef..39d998e43 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/compat/Compat.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/compat/Compat.kt @@ -22,6 +22,7 @@ import android.content.Intent import android.content.pm.PackageInfo import android.content.pm.PackageManager import android.content.pm.ResolveInfo +import android.net.ConnectivityManager /** * This interface defines a set of functions that are not available on all platforms. @@ -71,4 +72,8 @@ interface Compat { packageManager: PackageManager, flag: Int ): PackageInfo + + fun isNetworkAvailable(connectivity: ConnectivityManager): Boolean + + fun isWifi(connectivity: ConnectivityManager): Boolean } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/compat/CompatHelper.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/compat/CompatHelper.kt index 41f88acfc..fd69c9ed0 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/compat/CompatHelper.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/compat/CompatHelper.kt @@ -22,12 +22,14 @@ import android.content.Intent import android.content.pm.PackageInfo import android.content.pm.PackageManager import android.content.pm.ResolveInfo +import android.net.ConnectivityManager import android.os.Build class CompatHelper private constructor() { // Note: Needs ": Compat" or the type system assumes `Compat21` private val compatValue: Compat = when { sdkVersion >= Build.VERSION_CODES.TIRAMISU -> CompatV33() + sdkVersion >= Build.VERSION_CODES.M -> CompatV23() else -> CompatV21() } @@ -69,7 +71,14 @@ class CompatHelper private constructor() { fun PackageInfo.getVersionCode() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { longVersionCode.toInt() } else { + @Suppress("DEPRECATION") versionCode } + + fun ConnectivityManager.isNetworkAvailable(): Boolean = + compat.isNetworkAvailable(this) + + fun ConnectivityManager.isWifi(): Boolean = + compat.isWifi(this) } } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/compat/CompatV21.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/compat/CompatV21.kt index 167e88615..78f9e8f2c 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/compat/CompatV21.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/compat/CompatV21.kt @@ -22,6 +22,9 @@ import android.content.Intent import android.content.pm.PackageInfo import android.content.pm.PackageManager import android.content.pm.ResolveInfo +import android.net.ConnectivityManager +import android.net.ConnectivityManager.TYPE_WIFI +import android.net.NetworkInfo.State.CONNECTED @Suppress("Deprecation") open class CompatV21 : Compat { @@ -36,4 +39,22 @@ open class CompatV21 : Compat { packageManager: PackageManager, flag: Int ): PackageInfo = packageManager.getPackageInfo(packageName, 0) + + /** + * Checks if the device has a network connection with internet access. + * + * @param connectivity The ConnectivityManager instance. + * @return True if a network connection with internet access is available, false otherwise. + */ + override fun isNetworkAvailable(connectivity: ConnectivityManager): Boolean = + connectivity.allNetworkInfo.any { it.state == CONNECTED } + + /** + * Checks if the device is connected to a Wi-Fi network. + * + * @param connectivity The ConnectivityManager instance. + * @return True if connected to a Wi-Fi network, false otherwise. + */ + override fun isWifi(connectivity: ConnectivityManager): Boolean = + connectivity.getNetworkInfo(TYPE_WIFI)?.isConnected == true } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/compat/CompatV23.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/compat/CompatV23.kt new file mode 100644 index 000000000..d3327dbfe --- /dev/null +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/compat/CompatV23.kt @@ -0,0 +1,70 @@ +/* + * Kiwix Android + * Copyright (c) 2023 Kiwix + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package org.kiwix.kiwixmobile.core.compat + +import android.annotation.TargetApi +import android.content.Intent +import android.content.pm.PackageInfo +import android.content.pm.PackageManager +import android.content.pm.ResolveInfo +import android.net.ConnectivityManager +import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET +import android.net.NetworkCapabilities.TRANSPORT_WIFI + +const val API_23 = 23 + +@TargetApi(API_23) +open class CompatV23 : Compat { + private val compatV21 = CompatV21() + + override fun queryIntentActivities( + packageManager: PackageManager, + intent: Intent, + flags: ResolveInfoFlagsCompat + ): List = + compatV21.queryIntentActivities(packageManager, intent, flags) + + override fun getPackageInformation( + packageName: String, + packageManager: PackageManager, + flag: Int + ): PackageInfo = compatV21.getPackageInformation(packageName, packageManager, flag) + + /** + * Checks if the device has a network connection with internet access. + * + * @param connectivity The ConnectivityManager instance. + * @return True if a network connection with internet access is available, false otherwise. + */ + override fun isNetworkAvailable(connectivity: ConnectivityManager): Boolean { + return connectivity.getNetworkCapabilities(connectivity.activeNetwork) + ?.hasCapability(NET_CAPABILITY_INTERNET) == true + } + + /** + * Checks if the device is connected to a Wi-Fi network. + * + * @param connectivity The ConnectivityManager instance. + * @return True if connected to a Wi-Fi network, false otherwise. + */ + override fun isWifi(connectivity: ConnectivityManager): Boolean { + return connectivity.getNetworkCapabilities(connectivity.activeNetwork) + ?.hasTransport(TRANSPORT_WIFI) == true + } +} diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/compat/CompatV33.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/compat/CompatV33.kt index 107d7334f..1de26339e 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/compat/CompatV33.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/compat/CompatV33.kt @@ -24,11 +24,13 @@ import android.content.pm.PackageInfo import android.content.pm.PackageManager import android.content.pm.PackageManager.PackageInfoFlags import android.content.pm.ResolveInfo +import android.net.ConnectivityManager const val API_33 = 33 @TargetApi(API_33) open class CompatV33 : Compat { + private val compatV23 = CompatV23() override fun queryIntentActivities( packageManager: PackageManager, intent: Intent, @@ -44,4 +46,10 @@ open class CompatV33 : Compat { flag: Int ): PackageInfo = packageManager.getPackageInfo(packageName, PackageInfoFlags.of(flag.toLong())) + + override fun isNetworkAvailable(connectivity: ConnectivityManager): Boolean = + compatV23.isNetworkAvailable(connectivity) + + override fun isWifi(connectivity: ConnectivityManager): Boolean = + compatV23.isWifi(connectivity) } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/ConnectivityReporter.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/ConnectivityReporter.kt index 1ffd16414..9b6861348 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/ConnectivityReporter.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/ConnectivityReporter.kt @@ -19,9 +19,8 @@ package org.kiwix.kiwixmobile.core.utils import android.net.ConnectivityManager -import android.net.NetworkCapabilities import android.net.wifi.WifiManager -import android.os.Build +import org.kiwix.kiwixmobile.core.compat.CompatHelper.Companion.isWifi import java.lang.reflect.InvocationTargetException import java.lang.reflect.Method import javax.inject.Inject @@ -32,13 +31,7 @@ class ConnectivityReporter @Inject constructor( ) { fun checkWifi(): Boolean = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - val capabilities = - connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork) - capabilities != null && capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) - } else { - wifiManager.isWifiEnabled && wifiManager.connectionInfo.networkId != -1 - } + connectivityManager.isWifi() fun checkTethering(): Boolean = try { val method: Method = wifiManager.javaClass.getDeclaredMethod("isWifiApEnabled") diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/NetworkUtils.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/NetworkUtils.kt index f86f87701..3e04de779 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/NetworkUtils.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/NetworkUtils.kt @@ -19,12 +19,10 @@ package org.kiwix.kiwixmobile.core.utils import android.content.Context import android.net.ConnectivityManager -import android.net.NetworkCapabilities -import android.net.NetworkInfo -import android.os.Build import android.util.Log -import androidx.annotation.VisibleForTesting import org.kiwix.kiwixmobile.core.R +import org.kiwix.kiwixmobile.core.compat.CompatHelper.Companion.isNetworkAvailable +import org.kiwix.kiwixmobile.core.compat.CompatHelper.Companion.isWifi import java.util.UUID object NetworkUtils { @@ -36,25 +34,9 @@ object NetworkUtils { fun isNetworkAvailable(context: Context): Boolean { val connectivity: ConnectivityManager = context .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - val network = connectivity.activeNetwork - if (network != null) { - val networkCapabilities = connectivity.getNetworkCapabilities(network) - networkCapabilities?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) == true - } else { - false - } - } else { - connectivity.allNetworkInfo.any(::isNetworkConnectionOK) - } + return connectivity.isNetworkAvailable() } - fun isNetworkConnectionOK(networkInfo: NetworkInfo): Boolean = - networkInfo.state == NetworkInfo.State.CONNECTED - - @VisibleForTesting - internal var sdkVersionForTesting = Build.VERSION.SDK_INT - /** * check if network of type WIFI is connected * @@ -66,14 +48,7 @@ object NetworkUtils { fun isWiFi(context: Context): Boolean { val connectivity = context .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager - return if (sdkVersionForTesting >= Build.VERSION_CODES.M) { - val network = connectivity.activeNetwork ?: return false - val networkCapabilities = connectivity.getNetworkCapabilities(network) - networkCapabilities?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) == true - } else { - val wifi = connectivity.getNetworkInfo(ConnectivityManager.TYPE_WIFI) - wifi != null && wifi.isConnected - } + return connectivity.isWifi() } fun getFileNameFromUrl(url: String?): String { diff --git a/core/src/test/java/org/kiwix/kiwixmobile/core/utils/NetworkUtilsTest.kt b/core/src/test/java/org/kiwix/kiwixmobile/core/utils/NetworkUtilsTest.kt index c5cd30df9..fd3326a93 100644 --- a/core/src/test/java/org/kiwix/kiwixmobile/core/utils/NetworkUtilsTest.kt +++ b/core/src/test/java/org/kiwix/kiwixmobile/core/utils/NetworkUtilsTest.kt @@ -20,101 +20,112 @@ package org.kiwix.kiwixmobile.core.utils import android.content.Context import android.net.ConnectivityManager +import android.net.ConnectivityManager.TYPE_WIFI +import android.net.Network +import android.net.NetworkCapabilities +import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET +import android.net.NetworkCapabilities.TRANSPORT_WIFI import android.net.NetworkInfo import io.mockk.every import io.mockk.mockk -import io.mockk.verify import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.jupiter.api.Test import org.kiwix.kiwixmobile.core.R +import org.kiwix.kiwixmobile.core.compat.CompatV21 +import org.kiwix.kiwixmobile.core.compat.CompatV23 import java.util.regex.Pattern class NetworkUtilsTest { private val context: Context = mockk() private val connectivity: ConnectivityManager = mockk() - private val networkInfo: NetworkInfo = mockk() - private val networkInfo1: NetworkInfo = mockk() - private val networkInfo2: NetworkInfo = mockk() + private val networkCapabilities: NetworkCapabilities = mockk() + @Suppress("Deprecation") @Test - fun testNetworkAvailability() { - val networkInfos = arrayOf(networkInfo1, networkInfo2) + fun testNetworkAvailability_CompatV21() { every { context.getSystemService(Context.CONNECTIVITY_SERVICE) } returns connectivity - every { connectivity.allNetworkInfo } returns networkInfos + val compatV21 = CompatV21() + val networkInfo: NetworkInfo = mockk() - // one network is connected - every { networkInfo1.state } returns NetworkInfo.State.CONNECTED - every { networkInfo2.state } returns NetworkInfo.State.DISCONNECTING - assertEquals(true, NetworkUtils.isNetworkAvailable(context)) + every { connectivity.allNetworkInfo } returns arrayOf(networkInfo) + every { networkInfo.state } returns NetworkInfo.State.CONNECTED - every { networkInfo1.state } returns NetworkInfo.State.DISCONNECTING - every { networkInfo2.state } returns NetworkInfo.State.CONNECTING - assertEquals(false, NetworkUtils.isNetworkAvailable(context)) + assertTrue(compatV21.isNetworkAvailable(connectivity)) - // no network is available - every { networkInfo1.state } returns NetworkInfo.State.DISCONNECTED - every { networkInfo2.state } returns NetworkInfo.State.DISCONNECTED - assertEquals(false, NetworkUtils.isNetworkAvailable(context)) + every { networkInfo.state } returns NetworkInfo.State.DISCONNECTED + assertFalse(compatV21.isNetworkAvailable(connectivity)) } @Test - fun test_isNetworkConnectionOK() { - every { networkInfo2.state } returns NetworkInfo.State.CONNECTING - assertFalse(NetworkUtils.isNetworkConnectionOK(networkInfo2)) + fun testNetworkAvailability_CompatV23() { + every { context.getSystemService(Context.CONNECTIVITY_SERVICE) } returns connectivity + val compatV23 = CompatV23() + val network: Network = mockk() - every { networkInfo2.state } returns NetworkInfo.State.CONNECTED - assertTrue(NetworkUtils.isNetworkConnectionOK(networkInfo2)) + every { connectivity.activeNetwork } returns network + every { connectivity.getNetworkCapabilities(network) } returns networkCapabilities + every { networkCapabilities.hasCapability(NET_CAPABILITY_INTERNET) } returns true + + assertTrue(compatV23.isNetworkAvailable(connectivity)) + every { networkCapabilities.hasCapability(NET_CAPABILITY_INTERNET) } returns false + assertFalse(compatV23.isNetworkAvailable(connectivity)) } + @Suppress("Deprecation") @Test - fun testWifiAvailability() { - every { context.getSystemService(Context.CONNECTIVITY_SERVICE) } returns connectivity - every { connectivity.activeNetworkInfo } returns networkInfo + fun test_isWifi_CompatV21() { + val compatV21 = CompatV21() + val networkInfo: NetworkInfo = mockk() - // SDK >= 23 - NetworkUtils.sdkVersionForTesting = 23 - - // on Mobile Data - every { networkInfo.type } returns ConnectivityManager.TYPE_MOBILE - assertEquals(false, NetworkUtils.isWiFi(context)) - // verify that the correct methods are used according to the build SDK version - verify { - connectivity.activeNetworkInfo - networkInfo.type - } - verify(exactly = 0) { connectivity.getNetworkInfo(ConnectivityManager.TYPE_WIFI) } - - // on WIFI connected + every { connectivity.getNetworkInfo(TYPE_WIFI) } returns networkInfo every { networkInfo.type } returns ConnectivityManager.TYPE_WIFI - every { networkInfo.isConnected } returns java.lang.Boolean.TRUE - assertEquals(true, NetworkUtils.isWiFi(context)) - verify(exactly = 2) { connectivity.activeNetworkInfo } - verify(exactly = 0) { connectivity.getNetworkInfo(ConnectivityManager.TYPE_WIFI) } - - // on WIFI disconnected - every { networkInfo.type } returns ConnectivityManager.TYPE_WIFI - every { networkInfo.isConnected } returns java.lang.Boolean.FALSE - assertEquals(false, NetworkUtils.isWiFi(context)) - verify(exactly = 3) { connectivity.activeNetworkInfo } - verify(exactly = 0) { connectivity.getNetworkInfo(ConnectivityManager.TYPE_WIFI) } - - // SDK < 23 - NetworkUtils.sdkVersionForTesting = 22 - - // WIFI connected - every { connectivity.getNetworkInfo(ConnectivityManager.TYPE_WIFI) } returns networkInfo every { networkInfo.isConnected } returns true - assertEquals(true, NetworkUtils.isWiFi(context)) - verify { connectivity.getNetworkInfo(ConnectivityManager.TYPE_WIFI) } - // WIFI disconnected - every { connectivity.getNetworkInfo(ConnectivityManager.TYPE_WIFI) } returns networkInfo + assertTrue(compatV21.isWifi(connectivity)) + every { networkInfo.isConnected } returns false - assertEquals(false, NetworkUtils.isWiFi(context)) - verify(exactly = 2) { connectivity.getNetworkInfo(ConnectivityManager.TYPE_WIFI) } + assertFalse(compatV21.isWifi(connectivity)) + } + + @Test + fun test_isWifi_CompatV23() { + val compatV23 = CompatV23() + val network: Network = mockk() + + every { connectivity.activeNetwork } returns network + every { connectivity.getNetworkCapabilities(network) } returns networkCapabilities + every { networkCapabilities.hasTransport(TRANSPORT_WIFI) } returns true + + assertTrue(compatV23.isWifi(connectivity)) + every { networkCapabilities.hasTransport(TRANSPORT_WIFI) } returns false + assertFalse(compatV23.isWifi(connectivity)) + } + + @Suppress("Deprecation") + @Test + fun testNetworkAvailability_NoNetwork_CompatV21() { + every { context.getSystemService(Context.CONNECTIVITY_SERVICE) } returns connectivity + val compatV21 = CompatV21() + + every { connectivity.allNetworkInfo } returns arrayOf() + + assertFalse(compatV21.isNetworkAvailable(connectivity)) + } + + @Test + fun testNetworkAvailability_NoNetwork_CompatV23() { + every { context.getSystemService(Context.CONNECTIVITY_SERVICE) } returns connectivity + val compatV23 = CompatV23() + val network: Network = mockk() + + every { connectivity.activeNetwork } returns network + every { connectivity.getNetworkCapabilities(network) } returns networkCapabilities + every { networkCapabilities.hasCapability(NET_CAPABILITY_INTERNET) } returns false + + assertFalse(compatV23.isNetworkAvailable(connectivity)) } @Test