Reading Time: 7 minutes
If you have an app which gets the data from network, then it is important to handle network state changes. For example the users try to save the data but they can’t do it, because they are offline, or the Acitivty / Fragment should show the data to the users, but what they can see is only an empty page.
After this tutorial you will have an overview how to handle and implement the network state changes using LiveData and a simple notification line at the top of the screen. As an extra you gonna make a cool animation as well. 😎
The sample app
As you can see, our sample application going to be very simple. It will have only a LinearLayout with a TextView. Below the LinearLayout we gonna add one more TextView to see how do the views are moving under the notification line during the animation.
Step 1 – Create new project
First thing first, we gonna create a whole new project. For this, launch Android Studio. If you see the “Welcome page”, then click on the “Start a new Android Studio project”. If you have an open project, then from the “File” menu select “New”, then “New Project”. After thet, this window will popup.
Here select the “Empty Activity” option. In the next window, we should provide some information about the newly created app.
This tutorial will contain very few code, but still we have to select the programming language. As the other tutorials on this website, this will be written also in Kotlin. So, select from the dropdown list the Kotlin language.
From the next list of the “Minimum SDK” select API 21. In our case API 21 gonna be enough.
If you are done, click on the “Finish” button. The build can take few minutes. Be patient!
When the build has been finished, then you should see the open MainActivity::class and next to this the activity_main.xml files.
Step 2 – Add a dependency
We are going to use a LiveData and an Observer to get the network state if it changes. Because of this we have to add to our project the dependency of the LiveData package, which is part of the Android Jetpack libraries.
Next, paste the below implementation into the dependencies {} section.
implementation ‘androidx.lifecycle:lifecycle-livedata-ktx:2.2.0’
Enable Data Binding
To find the views from the layout file we gonna use Data Binding.
Data Binding
Still in the App Build.gradle file paste the below lines to the end of the android {} section.
buildFeatures{
dataBinding = true
}
Enable Data Binding
Thenafter click on the Sync now button, what you can find in Android Studio at the top left corner.
Step 3 – Add some colors and strings
As you saw in the video above we are going to have some new texts and colors. We can implement them in the strings.xml and colors.xml files.
The strings
To add the needed texts to our app, we have to open up the strings.xml file from the res->values folders.
Paste into this xml file the below lines.
<string name="text_no_connectivity">No Connection</string>
<string name="text_connectivity">Back Online</string>
<string name="lorem_ipsum">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum orci vitae ligula fermentum placerat. Nullam at tristique mi, ut gravida mi. Quisque egestas cursus sodales. Vestibulum placerat ex velit, et ullamcorper elit rhoncus at.</string>
strings.xml
The first 2 strings will be shown for the user on the notification line.
The 3. string will be only a placeholder text. We gonna use it to see how does the views are moving down and up when the notification line appears and disappears
The colors
For the colors open up the colors.xml file from the res->values folders.
Paste into this xml file the below lines.
<color name="colorStatusConnected">#43A047</color>
<color name="colorStatusNotConnected">#D32F2F</color>
colors.xml
Step 4 – The user interface
The next step is the implementation of the user interface. So, if you have closed it, open the activity_main.xml file from the res->layout folders.
The layout will be a Data Binding layout, as we have talked about earlier. But there is a more important thing.
For the ConstraintLayout we gonna add the animateLayoutChanges attribute with true value. Using this attribute the layout fulfills the fade-down and fade up animations.
So, copy and paste the below xml code into the activity_main.xml file.
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
tools:context=".MainActivity">
<LinearLayout
android:id="@+id/networkStatusLayout"
android:visibility="gone"
android:padding="8dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@color/colorStatusNotConnected"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/textViewNetworkStatus"
android:layout_gravity="center"
android:textColor="@android:color/white"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="@string/text_no_connectivity" />
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="0dp"
android:textSize="24sp"
android:layout_marginTop="4dp"
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
android:text="@string/lorem_ipsum"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/networkStatusLayout" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
activity_main.xml
Step 5 – Extension functions
Kotlin has a realy cool feature, called extension functions.
Source: Extensions
We are going to use this feature and we gonna add 2 more files to our project.
First of all, we gonna create for them a new package, called “utils”. So, click with the right mouse button on the main source set, then from the popup menu select New and Package options. Name it as we mentioned it before: “utils”.
View Utils
After the creation of the utils package, we gonna add into it a new Kotlin file as well. So, click again with the right mouse button on the utils package, select New and then the Kotlin file/class option. In the popup window name the new file as “ViewUtils”.
Thenafter copy and paste into the new file the below lines, which gonna help to show and hide the notification line.
fun View.show()
{
visibility = View.VISIBLE
}
fun View.hide()
{
visibility = View.GONE
}
ViewUtils.kt
Activity Utils
Then the second file goes also into the utils folder with the name of “ActivityUtils”.
Paste into it the below code which gonna help us to set the color of the notificaiton line.
/**
* Returns Color from resource.
* @param id Color Resource ID
*/
fun Activity.getColorRes(@ColorRes id: Int) = ContextCompat.getColor(applicationContext, id)
AcitivtyUtils.kt
Step 6 – The network utils
The most important part of this tutorial is to handle the network change. For this we gonna create again a new file into the utils folder and we gonna name it as “NetworkUtils”.
Create it once, and use it everywhere you need it. It means, thet later on you can just copy and paste this only file to observe the network state changes.
Paste the below lines into the newly created Kotlin file and after that we gonna go into the details.
import android.content.Context
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import android.os.Build
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
/**
* Network Utility to detect availability or unavailability of Internet connection
*/
// 1
object NetworkUtils : ConnectivityManager.NetworkCallback()
{
// 2
private val networkLiveData: MutableLiveData = MutableLiveData()
/**
* Returns instance of [LiveData] which can be observed for network changes.
*/
// 3
fun getNetworkLiveData(context: Context): LiveData
{
// 4
val connectivityManager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
// 5
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
{
connectivityManager.registerDefaultNetworkCallback(this)
}
else
{
val builder = NetworkRequest.Builder()
connectivityManager.registerNetworkCallback(builder.build(), this)
}
var isConnected = false
// 6
// Retrieve current status of connectivity
connectivityManager.allNetworks.forEach { network ->
val networkCapability = connectivityManager.getNetworkCapabilities(network)
networkCapability?.let {
if (it.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET))
{
isConnected = true
return@forEach
}
}
}
networkLiveData.postValue(isConnected)
return networkLiveData
}
// 7
override fun onAvailable(network: Network)
{
networkLiveData.postValue(true)
}
// 8
override fun onLost(network: Network)
{
networkLiveData.postValue(false)
}
}
NetworkUtils.kt
-
-
- We gonna create this class as an object. In Kotlin it means, thet it is a singleton class.
Then we gonna inherit it from the NetworkCallback() static class. - This MutableLiveData gonna be get back by the getNetworkLiveData() method.
- The getNetworkLiveData() method needs the context, because we can get the connectivity service only with it.
- Create an instance about the connectivity service.
- Using the if statement we have to check the version of the Android system. If it is under Android N, then we have to use a different method to register to the Network Callback.
- Retrieve current status of connectivity
- Post true, when the network is available
- Post false, when the network is available
- We gonna create this class as an object. In Kotlin it means, thet it is a singleton class.
-
Step 7 – The ACCESS_NETWORK_STATE permission
I think you have recognized already the error lines. They are there, because we need the ACCESS_NETWORK_STATE permission if we would like the get the current state of the network.
We can add the needed permission in the AndroidManifest.xml file, what you can find in the manifests folder.
So, copy and paste the below line before the <application> tag.
<uses-permission android:name=“android.permission.ACCESS_NETWORK_STATE” />
Now, the error should be disappeared from the NetworkUtils.kt file.
Step 8 – Add Data Binding to the MainActivity
Next step is to convert the MainActivity::class to a Data Binding class.
First, add the below line before the onCreate() method.
private lateinit var binding: ActivityMainBinding
Then, add the next line into the onCreate() method, after the super call, but before the setContentView() method call.
binding = ActivityMainBinding.inflate(layoutInflater)
Finally modify the setContentView() method call.
setContentView(binding.root)
Step 9 – The network observer
In the final step we gonna add the Observer to the ActivityMain::class. For this we gonna define a new method after the onCreate() method and we gonna call it as “handleNetworkChanges”.
handleNetworkChanges()
So, copy and paste the below code snippet into the handleNetworkChanges() method.
private fun handleNetworkChanges()
{
// 1
NetworkUtils.getNetworkLiveData(applicationContext).observe(this, Observer { isConncted ->
if (!isConncted)
{
// 2
binding.textViewNetworkStatus.text = getString(R.string.text_no_connectivity)
binding.networkStatusLayout.apply {
show()
setBackgroundColor(getColorRes(R.color.colorStatusNotConnected))
}
}
else
{
// 3
binding.textViewNetworkStatus.text = getString(R.string.text_connectivity)
binding.networkStatusLayout.setBackgroundColor(getColorRes(R.color.colorStatusConnected))
doAnimation(binding.networkStatusLayout)
}
})
}
handleNetworkChanges()
-
-
- The Observer, what we can call on the singleton NetworkUtils.getNetworkLiveData() method.
- If the device is offline, then no connectivity line should be visible with red background.
- If the device comes back online, then the line will be green.
Here we have one more method, what will be responsible to do the animation.
-
Add the call of this method to the onCreate() method:
handleNetworkChanges()
doAnimation()
Before we can define this method together with the animation, first we have to define a constans variable, which gonna store the duration of the animation. So, paste the variable to the top of the MainActivity::class.
private val ANIMATION_DURATION = 1000.toLong()
Below is the animation for the LinearLayout. Paste it below the handleNetworkChanges() method.
private fun doAnimation(linearLayout: LinearLayout)
{
linearLayout.apply {
animate()
.alpha(1f)
.setStartDelay(ANIMATION_DURATION)
.setDuration(ANIMATION_DURATION)
.setListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator?)
{
hide()
}
})
}
}
doAnimation()
Run the app
Finally, it is time to run the app.
Turn off all network connections on your device and the notification should be visible at the top of the screen. Then turn on the network, and the red goes green and the notification line disappears.
Congratulations!
You just implemented a handler for the network state.
GitHub
The source code is available on GitHub, check it out and download it using the below link.
Questions
I hope the description was understandable and clear. But, if you have still questions, then leave me comments below! 😉
Have a nice a day! 🙂