From 50cb5987b87683ced62f0c48bf5e1d4966797091 Mon Sep 17 00:00:00 2001 From: Subhajit Mallick <153619690+subhajitxyz@users.noreply.github.com> Date: Fri, 27 Sep 2024 08:04:00 +0530 Subject: [PATCH 1/6] Add networkCapabilities --- .../networking/NetworkConnectionTestUtil.kt | 58 ++++++++++++++++--- .../NetworkConnectionUtilProdImpl.kt | 48 ++++++++++++--- 2 files changed, 91 insertions(+), 15 deletions(-) diff --git a/testing/src/main/java/org/oppia/android/testing/networking/NetworkConnectionTestUtil.kt b/testing/src/main/java/org/oppia/android/testing/networking/NetworkConnectionTestUtil.kt index e430bfe4bce..f821b01667f 100644 --- a/testing/src/main/java/org/oppia/android/testing/networking/NetworkConnectionTestUtil.kt +++ b/testing/src/main/java/org/oppia/android/testing/networking/NetworkConnectionTestUtil.kt @@ -4,10 +4,15 @@ package org.oppia.android.testing.networking import android.content.Context import android.net.ConnectivityManager +import android.net.Network +import android.net.NetworkCapabilities import android.net.NetworkInfo +import android.os.Build +import javax.inject.Inject +import org.mockito.Mockito.`when` +import org.mockito.Mockito.mock import org.robolectric.Shadows.shadowOf import org.robolectric.shadows.ShadowNetworkInfo -import javax.inject.Inject /** Test utility to modify [ShadowNetworkInfo] in tests. */ class NetworkConnectionTestUtil @Inject constructor(private val context: Context) { @@ -23,14 +28,51 @@ class NetworkConnectionTestUtil @Inject constructor(private val context: Context * @param networkState state of the network connection */ fun setNetworkInfo(status: Int, networkState: NetworkInfo.State) { - shadowOf(connectivityManager).setActiveNetworkInfo( - ShadowNetworkInfo.newInstance( - /* detailedState= */ null, - /* type= */ status, - /* subType= */ 0, - /* isAvailable= */ true, - /* state= */ networkState + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + // Use ShadowNetworkInfo for older devices (API < 23) + shadowOf(connectivityManager).setActiveNetworkInfo( + ShadowNetworkInfo.newInstance( + /* detailedState= */ null, + /* type= */ status, + /* subType= */ 0, + /* isAvailable= */ true, + /* state= */ networkState + ) ) + } else { + // For newer devices, use NetworkCapabilities + val transportType = getTransportTypeFromStatus(status) + setNetworkCapabilities(networkState == NetworkInfo.State.CONNECTED, transportType) + } + } + + /** + * Sets the [ShadowNetworkCapabilities] during the test for newer devices. + * + * @param isConnected whether the network is connected + * @param transportType the type of transport being used (e.g., WiFi, Cellular) + */ + private fun setNetworkCapabilities(isConnected: Boolean, transportType: Int) { + val networkCapabilities = mock(NetworkCapabilities::class.java) + + `when`(networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) + .thenReturn(isConnected) + + `when`(networkCapabilities.hasTransport(transportType)).thenReturn(isConnected) + + shadowOf(connectivityManager).setNetworkCapabilities( + mock(Network::class.java), + networkCapabilities ) } + + private fun getTransportTypeFromStatus(status: Int): Int { + return when (status) { + ConnectivityManager.TYPE_WIFI -> NetworkCapabilities.TRANSPORT_WIFI + ConnectivityManager.TYPE_MOBILE -> NetworkCapabilities.TRANSPORT_CELLULAR + ConnectivityManager.TYPE_ETHERNET -> NetworkCapabilities.TRANSPORT_ETHERNET + ConnectivityManager.TYPE_WIMAX -> NetworkCapabilities.TRANSPORT_WIFI_AWARE + else -> throw IllegalArgumentException("Unsupported network type: $status") + } + } } diff --git a/utility/src/main/java/org/oppia/android/util/networking/NetworkConnectionUtilProdImpl.kt b/utility/src/main/java/org/oppia/android/util/networking/NetworkConnectionUtilProdImpl.kt index 04807565f0d..0632de6f203 100644 --- a/utility/src/main/java/org/oppia/android/util/networking/NetworkConnectionUtilProdImpl.kt +++ b/utility/src/main/java/org/oppia/android/util/networking/NetworkConnectionUtilProdImpl.kt @@ -2,6 +2,9 @@ package org.oppia.android.util.networking import android.content.Context import android.net.ConnectivityManager +import android.net.NetworkCapabilities +import android.os.Build +import androidx.annotation.RequiresApi import org.oppia.android.util.networking.NetworkConnectionUtil.ProdConnectionStatus.CELLULAR import org.oppia.android.util.networking.NetworkConnectionUtil.ProdConnectionStatus.LOCAL import org.oppia.android.util.networking.NetworkConnectionUtil.ProdConnectionStatus.NONE @@ -21,16 +24,47 @@ class NetworkConnectionUtilProdImpl @Inject constructor( // TODO(#3616): Update to use correct methods according to the SDK version. We can use the // older (current) method for SDK versions < 28 and the newer method for SDK versions >= 29 and // use an if-statement to choose. - @Suppress("DEPRECATION") // The code is correct for targeted versions of Android. - return connectivityManager.activeNetworkInfo?.let { activeNetwork -> + + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + getConnectionStatusForNewerDevices(connectivityManager) + } else { + getConnectionStatusForOlderDevices(connectivityManager) + } + } + + /** Uses NetworkCapabilities for SDK versions >= 23. */ + @RequiresApi(Build.VERSION_CODES.M) + private fun getConnectionStatusForNewerDevices( + connectivityManager: ConnectivityManager + ): ConnectionStatus { + + val network = connectivityManager.activeNetwork + val capabilities = connectivityManager.getNetworkCapabilities(network) + return capabilities?.let { + when { + it.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) || + it.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> LOCAL + it.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) || + it.hasTransport(NetworkCapabilities.TRANSPORT_WIFI_AWARE) -> CELLULAR + else -> NONE + } + } ?: NONE + } + + /** Uses the older `activeNetworkInfo` for SDK versions < 23. */ + @Suppress("DEPRECATION") + private fun getConnectionStatusForOlderDevices( + connectivityManager: ConnectivityManager + ): ConnectionStatus { + + val activeNetworkInfo = connectivityManager.activeNetworkInfo + return activeNetworkInfo?.let { activeNetwork -> val isConnected = activeNetwork.isConnected - val isLocal = activeNetwork.type == - ConnectivityManager.TYPE_WIFI || + val isLocal = activeNetwork.type == ConnectivityManager.TYPE_WIFI || activeNetwork.type == ConnectivityManager.TYPE_ETHERNET - val isCellular = activeNetwork.type == - ConnectivityManager.TYPE_MOBILE || + val isCellular = activeNetwork.type == ConnectivityManager.TYPE_MOBILE || activeNetwork.type == ConnectivityManager.TYPE_WIMAX - return@let when { + when { isConnected && isLocal -> LOCAL isConnected && isCellular -> CELLULAR else -> NONE From 5ef3886274840f7dc77e43a64ace210cb8b3a977 Mon Sep 17 00:00:00 2001 From: Subhajit Mallick <153619690+subhajitxyz@users.noreply.github.com> Date: Fri, 27 Sep 2024 08:14:28 +0530 Subject: [PATCH 2/6] commit --- .../android/testing/networking/NetworkConnectionTestUtil.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/testing/src/main/java/org/oppia/android/testing/networking/NetworkConnectionTestUtil.kt b/testing/src/main/java/org/oppia/android/testing/networking/NetworkConnectionTestUtil.kt index f821b01667f..38f6d5bb760 100644 --- a/testing/src/main/java/org/oppia/android/testing/networking/NetworkConnectionTestUtil.kt +++ b/testing/src/main/java/org/oppia/android/testing/networking/NetworkConnectionTestUtil.kt @@ -8,7 +8,9 @@ import android.net.Network import android.net.NetworkCapabilities import android.net.NetworkInfo import android.os.Build + import javax.inject.Inject + import org.mockito.Mockito.`when` import org.mockito.Mockito.mock import org.robolectric.Shadows.shadowOf From 1e71e36f5fe6fa9a73677b62cd9ccf421e9e13ba Mon Sep 17 00:00:00 2001 From: Subhajit Mallick <153619690+subhajitxyz@users.noreply.github.com> Date: Fri, 27 Sep 2024 08:15:36 +0530 Subject: [PATCH 3/6] Fix order --- .../android/testing/networking/NetworkConnectionTestUtil.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/testing/src/main/java/org/oppia/android/testing/networking/NetworkConnectionTestUtil.kt b/testing/src/main/java/org/oppia/android/testing/networking/NetworkConnectionTestUtil.kt index 38f6d5bb760..f821b01667f 100644 --- a/testing/src/main/java/org/oppia/android/testing/networking/NetworkConnectionTestUtil.kt +++ b/testing/src/main/java/org/oppia/android/testing/networking/NetworkConnectionTestUtil.kt @@ -8,9 +8,7 @@ import android.net.Network import android.net.NetworkCapabilities import android.net.NetworkInfo import android.os.Build - import javax.inject.Inject - import org.mockito.Mockito.`when` import org.mockito.Mockito.mock import org.robolectric.Shadows.shadowOf From bc209d917e9b469b119eddeb119882680a1ff0c8 Mon Sep 17 00:00:00 2001 From: Subhajit Mallick <153619690+subhajitxyz@users.noreply.github.com> Date: Fri, 27 Sep 2024 19:48:19 +0530 Subject: [PATCH 4/6] Solve Import order --- .../android/testing/networking/NetworkConnectionTestUtil.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/src/main/java/org/oppia/android/testing/networking/NetworkConnectionTestUtil.kt b/testing/src/main/java/org/oppia/android/testing/networking/NetworkConnectionTestUtil.kt index f821b01667f..22001d0d5aa 100644 --- a/testing/src/main/java/org/oppia/android/testing/networking/NetworkConnectionTestUtil.kt +++ b/testing/src/main/java/org/oppia/android/testing/networking/NetworkConnectionTestUtil.kt @@ -8,11 +8,11 @@ import android.net.Network import android.net.NetworkCapabilities import android.net.NetworkInfo import android.os.Build -import javax.inject.Inject import org.mockito.Mockito.`when` import org.mockito.Mockito.mock import org.robolectric.Shadows.shadowOf import org.robolectric.shadows.ShadowNetworkInfo +import javax.inject.Inject /** Test utility to modify [ShadowNetworkInfo] in tests. */ class NetworkConnectionTestUtil @Inject constructor(private val context: Context) { From e94ae90d678e73886d4776f168918d0888e1efb3 Mon Sep 17 00:00:00 2001 From: Subhajit Mallick <153619690+subhajitxyz@users.noreply.github.com> Date: Fri, 4 Oct 2024 11:42:13 +0530 Subject: [PATCH 5/6] Changes in logic --- .../networking/NetworkConnectionTestUtil.kt | 20 +++++++++---------- utility/src/main/AndroidManifest.xml | 1 + .../NetworkConnectionUtilProdImpl.kt | 15 ++++++-------- 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/testing/src/main/java/org/oppia/android/testing/networking/NetworkConnectionTestUtil.kt b/testing/src/main/java/org/oppia/android/testing/networking/NetworkConnectionTestUtil.kt index 22001d0d5aa..f473b0a9cd9 100644 --- a/testing/src/main/java/org/oppia/android/testing/networking/NetworkConnectionTestUtil.kt +++ b/testing/src/main/java/org/oppia/android/testing/networking/NetworkConnectionTestUtil.kt @@ -13,6 +13,7 @@ import org.mockito.Mockito.mock import org.robolectric.Shadows.shadowOf import org.robolectric.shadows.ShadowNetworkInfo import javax.inject.Inject +import org.robolectric.shadows.ShadowNetworkCapabilities /** Test utility to modify [ShadowNetworkInfo] in tests. */ class NetworkConnectionTestUtil @Inject constructor(private val context: Context) { @@ -20,6 +21,8 @@ class NetworkConnectionTestUtil @Inject constructor(private val context: Context private val connectivityManager by lazy { context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager } + private val networkCapabilities = ShadowNetworkCapabilities.newInstance() + /** * Sets the [ShadowNetworkInfo] during the test. @@ -53,17 +56,12 @@ class NetworkConnectionTestUtil @Inject constructor(private val context: Context * @param transportType the type of transport being used (e.g., WiFi, Cellular) */ private fun setNetworkCapabilities(isConnected: Boolean, transportType: Int) { - val networkCapabilities = mock(NetworkCapabilities::class.java) - - `when`(networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) - .thenReturn(isConnected) - - `when`(networkCapabilities.hasTransport(transportType)).thenReturn(isConnected) - - shadowOf(connectivityManager).setNetworkCapabilities( - mock(Network::class.java), - networkCapabilities - ) + shadowOf(networkCapabilities).addCapability(transportType) + if (isConnected) { + shadowOf(networkCapabilities).addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + } else { + shadowOf(networkCapabilities).removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + } } private fun getTransportTypeFromStatus(status: Int): Int { diff --git a/utility/src/main/AndroidManifest.xml b/utility/src/main/AndroidManifest.xml index d06626d50ac..fc02d055219 100644 --- a/utility/src/main/AndroidManifest.xml +++ b/utility/src/main/AndroidManifest.xml @@ -1,4 +1,5 @@ + diff --git a/utility/src/main/java/org/oppia/android/util/networking/NetworkConnectionUtilProdImpl.kt b/utility/src/main/java/org/oppia/android/util/networking/NetworkConnectionUtilProdImpl.kt index 0632de6f203..db0cd666d8b 100644 --- a/utility/src/main/java/org/oppia/android/util/networking/NetworkConnectionUtilProdImpl.kt +++ b/utility/src/main/java/org/oppia/android/util/networking/NetworkConnectionUtilProdImpl.kt @@ -4,7 +4,6 @@ import android.content.Context import android.net.ConnectivityManager import android.net.NetworkCapabilities import android.os.Build -import androidx.annotation.RequiresApi import org.oppia.android.util.networking.NetworkConnectionUtil.ProdConnectionStatus.CELLULAR import org.oppia.android.util.networking.NetworkConnectionUtil.ProdConnectionStatus.LOCAL import org.oppia.android.util.networking.NetworkConnectionUtil.ProdConnectionStatus.NONE @@ -33,11 +32,9 @@ class NetworkConnectionUtilProdImpl @Inject constructor( } /** Uses NetworkCapabilities for SDK versions >= 23. */ - @RequiresApi(Build.VERSION_CODES.M) private fun getConnectionStatusForNewerDevices( connectivityManager: ConnectivityManager ): ConnectionStatus { - val network = connectivityManager.activeNetwork val capabilities = connectivityManager.getNetworkCapabilities(network) return capabilities?.let { @@ -56,15 +53,15 @@ class NetworkConnectionUtilProdImpl @Inject constructor( private fun getConnectionStatusForOlderDevices( connectivityManager: ConnectivityManager ): ConnectionStatus { - - val activeNetworkInfo = connectivityManager.activeNetworkInfo - return activeNetworkInfo?.let { activeNetwork -> + return connectivityManager.activeNetworkInfo?.let { activeNetwork -> val isConnected = activeNetwork.isConnected - val isLocal = activeNetwork.type == ConnectivityManager.TYPE_WIFI || + val isLocal = activeNetwork.type == + ConnectivityManager.TYPE_WIFI || activeNetwork.type == ConnectivityManager.TYPE_ETHERNET - val isCellular = activeNetwork.type == ConnectivityManager.TYPE_MOBILE || + val isCellular = activeNetwork.type == + ConnectivityManager.TYPE_MOBILE || activeNetwork.type == ConnectivityManager.TYPE_WIMAX - when { + return@let when { isConnected && isLocal -> LOCAL isConnected && isCellular -> CELLULAR else -> NONE From 6b58bb8b556add932f3341121f0a98662de81554 Mon Sep 17 00:00:00 2001 From: Subhajit Mallick <153619690+subhajitxyz@users.noreply.github.com> Date: Fri, 4 Oct 2024 11:47:24 +0530 Subject: [PATCH 6/6] Solve formatting --- .../android/testing/networking/NetworkConnectionTestUtil.kt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/testing/src/main/java/org/oppia/android/testing/networking/NetworkConnectionTestUtil.kt b/testing/src/main/java/org/oppia/android/testing/networking/NetworkConnectionTestUtil.kt index f473b0a9cd9..21550bf7280 100644 --- a/testing/src/main/java/org/oppia/android/testing/networking/NetworkConnectionTestUtil.kt +++ b/testing/src/main/java/org/oppia/android/testing/networking/NetworkConnectionTestUtil.kt @@ -4,16 +4,13 @@ package org.oppia.android.testing.networking import android.content.Context import android.net.ConnectivityManager -import android.net.Network import android.net.NetworkCapabilities import android.net.NetworkInfo import android.os.Build -import org.mockito.Mockito.`when` -import org.mockito.Mockito.mock import org.robolectric.Shadows.shadowOf import org.robolectric.shadows.ShadowNetworkInfo -import javax.inject.Inject import org.robolectric.shadows.ShadowNetworkCapabilities +import javax.inject.Inject /** Test utility to modify [ShadowNetworkInfo] in tests. */ class NetworkConnectionTestUtil @Inject constructor(private val context: Context) {