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..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,9 +4,12 @@ package org.oppia.android.testing.networking import android.content.Context import android.net.ConnectivityManager +import android.net.NetworkCapabilities import android.net.NetworkInfo +import android.os.Build import org.robolectric.Shadows.shadowOf import org.robolectric.shadows.ShadowNetworkInfo +import org.robolectric.shadows.ShadowNetworkCapabilities import javax.inject.Inject /** Test utility to modify [ShadowNetworkInfo] in tests. */ @@ -15,6 +18,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. @@ -23,14 +28,46 @@ 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) { + 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 { + 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/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 04807565f0d..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 @@ -2,6 +2,8 @@ package org.oppia.android.util.networking import android.content.Context import android.net.ConnectivityManager +import android.net.NetworkCapabilities +import android.os.Build 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,7 +23,36 @@ 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 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + getConnectionStatusForNewerDevices(connectivityManager) + } else { + getConnectionStatusForOlderDevices(connectivityManager) + } + } + + /** Uses NetworkCapabilities for SDK versions >= 23. */ + 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 { return connectivityManager.activeNetworkInfo?.let { activeNetwork -> val isConnected = activeNetwork.isConnected val isLocal = activeNetwork.type ==