Connectivity Manager - AllNetworks Deprecated - android

Can someone point me in the right direction when it comes to replacing this deprecated code for checking the internet connection on a device?
private val isNetworkAvailable = MutableStateFlow(false)
fun checkNetworkAvailability(context: Context): MutableStateFlow<Boolean> {
val connectivityManager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
connectivityManager.registerDefaultNetworkCallback(this)
var isConnected = false
// allNetworks Deprecated
connectivityManager.allNetworks.forEach { network ->
val networkCapability = connectivityManager.getNetworkCapabilities(network)
networkCapability?.let {
if(it.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
isConnected = true
return#forEach
}
}
}
isNetworkAvailable.value = isConnected
return isNetworkAvailable
}

You can fetch the active network and check it's currently connected or not using NetworkCapabilities
private fun isNetworkAvailable(): Boolean {
val connectivityManager =
getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val network = connectivityManager.activeNetwork // network is currently in a high power state for performing data transmission.
Log.d("Network", "active network $network")
network ?: return false // return false if network is null
val actNetwork = connectivityManager.getNetworkCapabilities(network) ?: return false // return false if Network Capabilities is null
return when {
actNetwork.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> { // check if wifi is connected
Log.d("Network", "wifi connected")
true
}
actNetwork.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> { // check if mobile dats is connected
Log.d("Network", "cellular network connected")
true
}
else -> {
Log.d("Network", "internet not connected")
false
}
}
}
return false
}
Note: connectivityManager.activeNetwork requires permission android.permission.ACCESS_NETWORK_STATE

Related

Android app doesn't work while WiFi is on. (With Firebase and Admob)

My app works perfectly fine with mobile data but it doesn't (Firebase SDKs and Google Admob) when wifi is on.
With FirebaseAuth it doesn't return anything back (no log, no error) after I use fbAuth.signInAnonymously().
With Admob, I get "Network error" message in log when I print loadAdError.message under onAdFailedToLoad() in RewardedAdLoadCallback.
The way I check for internet connection:
private fun hasInternetAccess(): Boolean {
if (isNetworkAvailable()) {
try {
val urlConnection: HttpURLConnection = URL("http://clients3.google.com/generate_204") //call this url to be more efficient instead of google.com
.openConnection() as HttpURLConnection
urlConnection.setRequestProperty("User-Agent", "Android")
urlConnection.setRequestProperty("Connection", "close")
urlConnection.connectTimeout = 1500
urlConnection.connect()
log.error("urlc.responseCode = ${urlConnection.responseCode }")
return urlConnection.responseCode == 204 &&
urlConnection.contentLength == 0
} catch (e: IOException) {
Log.e(TAG, "Error checking internet connection", e)
}
} else {
Log.d(TAG, "No network available!")
}
return false
}
private fun isNetworkAvailable(): Boolean {
var result = false
val connectivityManager = context.getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val networkCapabilities = connectivityManager.activeNetwork ?: return false
val actNw = connectivityManager.getNetworkCapabilities(networkCapabilities) ?: return false
result = when {
actNw.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true
actNw.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> true
actNw.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> true
else -> false
}
} else {
connectivityManager.run {
connectivityManager.activeNetworkInfo?.run {
result = when (type) {
ConnectivityManager.TYPE_WIFI -> true
ConnectivityManager.TYPE_MOBILE -> true
ConnectivityManager.TYPE_ETHERNET -> true
else -> false
}
}
}
}
return result
}
The hasInternetAccess() above returns true whether I use mobile data or wifi and urlc.responseCode is 204. I don't think there's an issue with my wifi as I can browser web and play games, that require internet, just fine.
I have these permissions in manifest:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET"/>
and minSdk is 23, the android device that I test on has version of 6.0.
How can I resolve this issue?
This is a known bug with the Android SDK: https://github.com/firebase/firebase-android-sdk/issues/1258
My Android app has been plagued by this problem for years and it seems to be getting worse in 2022. I hope they fix this soon.

Android check internet connection in kotlin

I need to check if the device have internet connection, I search for some examples but when i copy and paste the code i always get errors or deprecated function. I also not understand where I have to put the method that check the connection, because I need to check the internet connection in the viewModel to make some request, and all the methods that i found have Context in the parameters, and I can't get Context in viewModel.
I try this code but I don't understand where I have to put it and I get
'TYPE_WIFI, TYPE_MOBILE, TYPE_ETHERNET: Int' is deprecated. Deprecated in Java
private fun isInternetAvailable(context: Context): Boolean {
var result = false
val connectivityManager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val networkCapabilities = connectivityManager.activeNetwork ?: return false
val actNw =
connectivityManager.getNetworkCapabilities(networkCapabilities) ?: return false
result = when {
actNw.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true
actNw.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> true
actNw.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> true
else -> false
}
} else {
connectivityManager.run {
connectivityManager.activeNetworkInfo?.run {
result = when (type) {
ConnectivityManager.TYPE_WIFI -> true
ConnectivityManager.TYPE_MOBILE -> true
ConnectivityManager.TYPE_ETHERNET -> true
else -> false
}
}
}
}
return result
}
Someone can explain me how to make this check?
I created a helper class.
Network.kt
object Network {
private const val NETWORK_STATUS_NOT_CONNECTED = 0
private const val NETWORK_STATUS_WIFI = 1
private const val NETWORK_STATUS_MOBILE = 2
private const val TYPE_WIFI = 1
private const val TYPE_MOBILE = 2
private const val TYPE_NOT_CONNECTED = 0
private fun connectivityStatus(context: Context): Int {
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val activeNetwork = connectivityManager.activeNetworkInfo
if (null != activeNetwork) {
if (activeNetwork.type == ConnectivityManager.TYPE_WIFI) return TYPE_WIFI
if (activeNetwork.type == ConnectivityManager.TYPE_MOBILE) return TYPE_MOBILE
}
return TYPE_NOT_CONNECTED
}
private fun connectivityStatusString(context: Context): Int {
val connection = connectivityStatus(context)
var status = -1
if (connection == TYPE_WIFI) status = NETWORK_STATUS_WIFI else if (connection == TYPE_MOBILE) status = NETWORK_STATUS_MOBILE else if (connection == TYPE_NOT_CONNECTED) status = NETWORK_STATUS_NOT_CONNECTED
return status
}
fun checkConnectivity(context : Context):Boolean{
val status = connectivityStatusString(context)
return status == NETWORK_STATUS_WIFI || status == NETWORK_STATUS_MOBILE
}
}
and to access it you need to use it like this
if (Network.checkConnectivity(this#MainActivity))
\\Internet is working
else
\\No internet connectivity
You have to extend AndroidViewModel() class. After that you can reach application context in your viewModel.
class viewModel(app: Application): AndroidViewModel(app) {}

ConnectivityManager only can detect connectivity but not network availability

The code below is used to detect network connectivity. The scenario is the software connected to the phone's hotspot but the phone's cellular network doesn't switch on. The code below return isConnected = true, which means it is only detects connectivity? The question is how I change it to detect network availability?
fun Context.isOnline(): Boolean {
val connectivityManager =
this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val networkInfo = connectivityManager.activeNetworkInfo
if (networkInfo == null) {
log(message = "Network info is **NULL**")
return false
} else {
val isConnected = when(networkInfo.detailedState) {
NetworkInfo.DetailedState.CONNECTING -> true
NetworkInfo.DetailedState.AUTHENTICATING -> true
NetworkInfo.DetailedState.OBTAINING_IPADDR -> true
NetworkInfo.DetailedState.CONNECTED -> true
NetworkInfo.DetailedState.VERIFYING_POOR_LINK -> true
else -> false
}
if (!isConnected) {
log(message = "Network state is **${networkInfo.detailedState?.name ?: "NULL"}**")
}
return isConnected
}
}
You're using a deprecated API with NetworkInfo. You should be using NetworkCapabilities.NET_CAPABILITY_VALIDATED
Indicates that connectivity on this network was successfully validated. For example, for a network with NET_CAPABILITY_INTERNET, it means that Internet connectivity was successfully detected.
SDK >= 23
You could change your code to something like this to see if that's true to confirm internet connectivity:
connectivityManager
.getNetworkCapabilities(connectivityManager.getActiveNetwork())
.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
SDK >= 21
If you are using anything as old as SDK 21, you can use the ConnectivityManager.registerNetworkCallback API.
boolean isConnected = false;
final NetworkRequest request =
new NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.build();
final ConnectivityManager connectivityManager = (ConnectivityManager)
context.getSystemService(Context.CONNECTIVITY_SERVICE);
final NetworkCallback networkCallback = new NetworkCallback() {
#Override
void onAvailable(Network network) {
// Triggers when this network is available.
isConnected = true;
}
#Override
void onLost(Network network) {
// Triggers when this network is lost.
isConnected = false;
}
};
connectivityManager.requestNetwork(request, networkCallback);

App crashes when no internet during Retrofit2 Get request

My application seems to keep crashing giving me an E/AndroidRuntime: FATAL EXCEPTION: main error when i try to make a Get request to a server and there is no internet. I expected the app to run but no data be displayed.
Log.i("getStoreData()" , "Inside the coroutine before getData")
this is the last log that I have put myself gets printed before the app crashes.
private fun getStoreData() {
Log.i("getStoreData()", " inside getStoreData")
val job = coroutineScope.launch {
Log.i("getStoreData()" , "Inside the coroutine before getData")
var data = StoreAPI.retrofitService.getData()
Log.i("getStoreData()" , "Inside the coroutine after getData")
try {
var storeData = data.stores
_status.value = "Success: ${storeData.size} Stores received"
if(storeData.size > 0){
_stores.value = storeData
}
} catch (t: Throwable) {
Log.i("Retrofit catch block", _status.value)
_status.value = "Failure: " + t.message
t.printStackTrace()
}
}
}
StoreAPIService.kt
private const val URL = "http://sandbox.bottlerocketapps.com/BR_Android_CodingExam_2015_Server/"
private val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
private val retrofit = Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create(moshi))
.baseUrl(URL)
.build()
interface StoreAPIService{
//Initially was using Jake Wharton's library for retrofit2 kotlin coroutines support but it has been deprecated since the support
// addition of the suspend keyword in retrofit 2.6.0
//Suspend does all the task of coroutines for us by just adding it before the function declaration
#GET("stores.json")
suspend fun getData():
Data //return Data object because Data has access to the Store JSON Object/Array
}
object StoreAPI{
val retrofitService: StoreAPIService by lazy {
retrofit.create(StoreAPIService::class.java)
}
}
Any idea why?
EDIT:
I cannot use these network connectivity functions because I my fragment is not connected to any activity and the fragment is connected to a viewModel. Therefore this line of code doesn't work as there is no context to bound it to. If you have a work around for this that would be great too.
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NB: before making any Network call or sending any requesting you must ensure that the device is connected to internet. I entice you to write a simple function to check if you're connected, if you're connected then you can send the request or make a network call.
Try using this
Create Class For NetworkConnectionDetection
Manifest
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
NetworkConnection Class
class NetworkConnection(val context: Context) : LiveData<Boolean>() {
var connectionManger: ConnectivityManager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
lateinit var netwrokCallback: ConnectivityManager.NetworkCallback
override fun onActive() {
super.onActive()
updateConnection()
when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.N -> {
connectionManger.registerDefaultNetworkCallback(NetworkConnectioncallback())
}
Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP -> {
lollipopNetworkRequest()
}
else -> {
context.registerReceiver(
networkReciever(),
IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)
)
}
}
}
/*override fun onInactive() {
super.onInactive()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
connectionManger.unregisterNetworkCallback(NetworkConnectioncallback())
} else {
context.unregisterReceiver(networkReciever())
}
}*/
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
fun lollipopNetworkRequest() {
val requestBuilder = NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET)
connectionManger.registerNetworkCallback(
requestBuilder.build(),
NetworkConnectioncallback()
)
}
fun NetworkConnectioncallback(): ConnectivityManager.NetworkCallback {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
netwrokCallback = object : ConnectivityManager.NetworkCallback() {
override fun onLost(network: Network) {
super.onLost(network)
postValue(false)
}
override fun onAvailable(network: Network) {
super.onAvailable(network)
postValue(true)
}
}
return netwrokCallback
} else {
throw IllegalAccessError("Error!")
}
}
fun networkReciever() = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
updateConnection()
}
}
fun updateConnection() {
val activeNetwork: NetworkInfo? = connectionManger.activeNetworkInfo
postValue((activeNetwork?.isConnected == true))
}
}
Now Inside your Activity/Fragment Check the connection either it is connected or not. Here is how it will be achieved
val networkConnection = NetworkConnection(requireContext())
networkConnection.observe(viewLifecycleOwner, { isConnected ->
if (isConnected) {
// Do what ever you want to do
} else {
// Show No internet connection message
}
})
You need to add internet checks before calling your retrofit service because to get some data from server, internet connectivity is mandatory
This method checks whether mobile is connected to internet and returns true
if connected:
private boolean isNetworkConnected() {
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
return cm.getActiveNetworkInfo() != null && cm.getActiveNetworkInfo().isConnected();
}
in manifest,
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
Edit: This method actually checks if device is connected to internet(There is
a possibility it's connected to a network but not to internet).
public boolean isInternetAvailable() {
try {
InetAddress ipAddr = InetAddress.getByName("google.com");
//You can replace it with your name
return !ipAddr.equals("");
}catch (Exception e) {
return false;
}
}
This will tell you if you're connected to a network:
boolean connected = false;
ConnectivityManager connectivityManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
if(connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).getState() == NetworkInfo.State.CONNECTED ||
connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).getState() == NetworkInfo.State.CONNECTED) {
//we are connected to a network
connected = true;
}
else
connected = false;
I also followed that tutorial, but I don't remember it having an offline mode. That is an option that you have to integrate on yourself.
When you create the viewModel, because it has an init block, it makes the call to the API and if you don't have an Internet connection, it crashes.
So you should write the init viewModel some code that checks whether you have an Internet connection or not. Or in the method that makes the API call to get the data.
In the next lesson from that tutorial, "Behind the scenes", they talk about offline mode.

Send request over WiFi (without connection) even if Mobile data is ON (with connection) on Android M

I have to send UDP packets to a WiFi module (provided with own AP) with no internet connection but when I connect the mobile with the AP, Android redirects my packets on the mobile data interface because it has got internet connection.
I've used the code below to do my job but it seems not working on Android M.
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void setWifiInterfaceAsDefault() {
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkRequest.Builder builder = new NetworkRequest.Builder();
NetworkRequest networkRequest= builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.build();
connectivityManager.requestNetwork(networkRequest, new ConnectivityManager.NetworkCallback());
}
I've also added
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
on my AndroidManifest.xml and I ensured myself that Settings.System.canWrite(this) returns true but still nothing.
Thanks in advance.
Stanislav's answer is correct but incomplete because only works in Lollipop.
I've wrote a complete solution for Lollipop and Marshmallow onwards for you to route all network requests through WiFi when connected to a specific network of your choice.
Kotlin
In your Activity,
#RequiresApi(Build.VERSION_CODES.LOLLIPOP)
class RoutingActivity : Activity() {
private var mConnectivityManager: ConnectivityManager? = null
private var mNetworkCallback: ConnectivityManager.NetworkCallback? = null
//...
override fun onCreate(savedInstanceState: Bundle?) {
//...
routeNetworkRequestsThroughWifi("Access-Point-SSID-You-Want-To-Route-Your-Requests")
}
Route future network requests from application through WiFi (even if given WiFi network is without internet and mobile data has internet connection)
/**
* This method sets a network callback that is listening for network changes and once is
* connected to the desired WiFi network with the given SSID it will bind to that network.
*
* Note: requires android.permission.INTERNET and android.permission.CHANGE_NETWORK_STATE in
* the manifest.
*
* #param ssid The name of the WiFi network you want to route your requests
*/
private fun routeNetworkRequestsThroughWifi(ssid: String) {
mConnectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
// ensure prior network callback is invalidated
unregisterNetworkCallback(mNetworkCallback)
// new NetworkRequest with WiFi transport type
val request = NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.build()
// network callback to listen for network changes
mNetworkCallback = object : ConnectivityManager.NetworkCallback() {
// on new network ready to use
override fun onAvailable(network: Network) {
if (getNetworkSsid(this#RoutingActivity).equals(ssid, ignoreCase = false)) {
releaseNetworkRoute()
createNetworkRoute(network)
} else {
releaseNetworkRoute()
}
}
}
mConnectivityManager?.requestNetwork(request, mNetworkCallback)
}
Unregister network callback
private fun unregisterNetworkCallback(networkCallback: ConnectivityManager.NetworkCallback?) {
if (networkCallback != null) {
try {
mConnectivityManager?.unregisterNetworkCallback(networkCallback)
} catch (ignore: Exception) {
} finally {
mNetworkCallback = null
}
}
}
Create network route
private fun createNetworkRoute(network: Network): Boolean? {
var processBoundToNetwork: Boolean? = false
when {
// 23 = Marshmallow
Build.VERSION.SDK_INT >= 23 -> {
processBoundToNetwork = mConnectivityManager?.bindProcessToNetwork(network)
}
// 21..22 = Lollipop
Build.VERSION.SDK_INT in 21..22 -> {
processBoundToNetwork = ConnectivityManager.setProcessDefaultNetwork(network)
}
}
return processBoundToNetwork
}
 Release network route
private fun releaseNetworkRoute(): Boolean? {
var processBoundToNetwork: Boolean? = false
when {
// 23 = Marshmallow
Build.VERSION.SDK_INT >= 23 -> {
processBoundToNetwork = mConnectivityManager?.bindProcessToNetwork(null)
}
// 21..22 = Lollipop
Build.VERSION.SDK_INT in 21..22 -> {
processBoundToNetwork = ConnectivityManager.setProcessDefaultNetwork(null)
}
}
return processBoundToNetwork
}
Helper
private fun getNetworkSsid(context: Context?): String {
// WiFiManager must use application context (not activity context) otherwise a memory leak can occur
val mWifiManager = context?.applicationContext?.getSystemService(Context.WIFI_SERVICE) as WifiManager
val wifiInfo: WifiInfo? = mWifiManager.connectionInfo
if (wifiInfo?.supplicantState == SupplicantState.COMPLETED) {
return wifiInfo.ssid.removeSurrounding("\"")
}
return ""
}
Bind the network using ConnectivityManager.setProcessDefaultNetwork() prevents roaming and allows for full TCP access. Thus, within the onAvailable() callback you could bind the application process to that network rather than opening a connection to a particular URL.
ConnectivityManager connection_manager =
(ConnectivityManager) activity.getApplication().getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkRequest.Builder request = new NetworkRequest.Builder();
request.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
connection_manager.registerNetworkCallback(request.build(), new NetworkCallback() {
#Override
public void onAvailable(Network network) {
ConnectivityManager.setProcessDefaultNetwork(network);
}
}
Original answer

Categories

Resources