Introduction
In this demo, you’ll learn more about the structure of the project. You’ll also learn how to check your network connectivity in the app.
Open the Starter project for Lesson 1 from the student materials repository. Once it syncs, change your project pane view to Project. It might be set to Android by default. The project view shows a more accurate representation of the structure you’re about to explore.
Project Structure
The project structure is pretty simple. The model package contains User
and MovieReview
models that will be
shown in the app. You’ll have all your network-related code in the networking package. For now, that package holds only MovieDiaryApi
, which has some method signatures already prepared for you.
The ui package contains all the UI-related code, further subpackaged by features in the app.
Finally, there are two more classes: App
and MainActivity
. App
creates and provides access to SharedPreferences
, and MainActivity
is the entry point and a container for the whole app.
Adding Permissions to AndroidManifest.xml
Now that you’re familiar with the project structure, it’s time to start adding some code. The first thing you must do is to add networking dependencies. To do that, open AndroidManifest.xml and add the following code above the <application ..>
tag:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
The first permission lets your app access the internet in general. The second one lets you check the network state.
The next thing you need is a way to check the network status. For that, you’ll create a class called ConnectivityChecker
in the networking package. Add the following code to the class:
class ConnectivityChecker(
private val connectivityManager: ConnectivityManager?
)
You need an instance of ConnectivityManager
to get information about the network.
Now, create a function that will read the network’s capabilities. Add this code to the class:
class ConnectivityChecker(private val connectivityManager: ConnectivityManager?) {
fun hasNetworkConnection(): Boolean {
val network = connectivityManager?.activeNetwork ?: return false
val capabilities =
connectivityManager.getNetworkCapabilities(network) ?: return false
return capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
|| capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)
|| capabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)
|| capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
}
}
First, you get the active network from the connectivityManager
and read its capabilities. Then, you check if it’s
connected to Wi-Fi, ethernet, cellular data, or a VPN. If any of those connections exist, you return true
, meaning
that a user can make a network request.
Using ConnectivityChecker
It’s time to try it out! Open LoginScreen.kt and add the connectivity checker as a parameter to the LoginScreen
composable.
@Composable
fun LoginScreen(
movieDiaryApi: MovieDiaryApi,
connectivityChecker: ConnectivityChecker,
onLogin: (String) -> Unit,
onRegisterTapped: () -> Unit,
)
Scroll down to the Button
composable and replace TODO: Add network check
with this code:
if (connectivityChecker.hasNetworkConnection()) {
Also replace TODO: Handle no network state
with the following:
else {
scaffoldState.snackbarHostState.showSnackbar("Check your network connection.")
}
}
This checks for a network connection every time a user presses the Login button. If the app’s not connected, a snackbar shows with an error. Otherwise, the program continues normally.
To finish the implementation, open MainActivity.kt and create an instance of ConnectivityChecker
by adding the following property:
private val connectivityChecker by lazy { ConnectivityChecker(connectivityManager) }
Scroll down and pass the instance to the LoginScreen
composable:
LoginScreen(
movieDiaryApi = movieApi,
connectivityChecker = connectivityChecker,
onLogin = { token ->
App.saveUserToken(token)
userLoggedIn = true
currentScreen = Screens.MOVIES
},
onRegisterTapped = { currentScreen = Screens.REGISTER }
)
Implement the same connectivity check on the registration screen in RegisterScreen.kt
, like this:
@Composable
fun RegisterScreen(
movieDiaryApi: MovieDiaryApi,
connectivityChecker: ConnectivityChecker,
onUserRegistered: () -> Unit = {},
onLoginTapped: () -> Unit,
)
Scroll down to the button and replace it with the following code:
Button(onClick = {
focusManager.clearFocus()
screenScope.launch {
if (username.isNotBlank() && email.isNotBlank() && password.isNotBlank()) {
if (connectivityChecker.hasNetworkConnection()) {
movieDiaryApi.registerUser(
username,
email,
password
) { message, error ->
screenScope.launch {
if (error != null) {
scaffoldState.snackbarHostState.showSnackbar(error.message ?: "")
} else {
onUserRegistered()
}
}
}
} else {
scaffoldState.snackbarHostState.showSnackbar("Check your network connection.")
}
} else {
scaffoldState.snackbarHostState.showSnackbar("Please fill in all the fields.")
}
}
}) {
Text(text = "Register")
}
And in MainActivity.kt, add the connectivityChecker
to RegisterScreen
:
RegisterScreen(
movieDiaryApi = movieApi,
connectivityChecker = connectivityChecker,
onUserRegistered = { currentScreen = Screens.LOGIN },
onLoginTapped = { currentScreen = Screens.LOGIN })
Run the app and turn on airplane mode. Add some input and click Login. You see an error message to check your connection.
Click the Register button and notice that the same happens if you try to register.
Soon, you’ll add code to register a user.