Enable Server Configuration with Okhttp and Retrofit in Open Event Attendee Application
The open event attendee is an android app which allows users to discover events happening around the world using the Open Event Platform. It consumes the APIs of the open event server to get a list of available events and can get detailed information about them.
We are using default API for eventyay app. Server configuration is something when we replace backend API with a new one and perform the same applications with the different server. As it is a fully open-source project on F-droid, so we have enabled the server configuration field for the F-droid build variant.
- Retrofit and okhttp for network calls
- Create a feasible UI and set the link to preferences
- Create interceptor for changing API URL
- Add interceptor in okhttp client builder
- Conclusion
- Resources
Let’s analyze every step in detail.
Retrofit and Okhttp for Network Call
Using Retrofit for your Android app’s networking can make your life so much easier. However, Retrofit’s design requires a single Retrofit instance for each API with a different base URL. Consequently, if your app is talking to two or more APIs (under different URLs), you’ll need to deal with at least two Retrofit instances.
Retrofit is a type-safe REST client for Android, Java, and Kotlin developed by Square. The library provides a powerful framework for authenticating and interacting with APIs and sending network requests with OkHttp.
OkHttp communicating with the server-
Design UI and set the link to preferences with MVVM
Create a simple dialog with a checkbox with default URL and a EditText:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<CheckBox
android:id="@+id/urlCheckBox"
android:layout_margin="@dimen/layout_margin_large"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<com.google.android.material.textfield.TextInputLayout style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.Dense"
android:id="@+id/urlTextInputLayout"
android:layout_margin="@dimen/layout_margin_large"
android:hint="@string/other_url"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/urlEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
Handle visibility if the dialog and display for only F-droid build:
preferenceScreen.findPreference<PreferenceCategory>(getString(R.string.key_server_configuration))?.isVisible = BuildConfig.FLAVOR == FDROID_BUILD_FLAVOR
Set current API to preference screen:
preferenceScreen.findPreference<Preference>(getString(R.string.key_api_url))?.title =
settingsViewModel.getApiUrl()
Get API from View model:
fun getApiUrl(): String {
return preference.getString(API_URL) ?: BuildConfig.DEFAULT_BASE_URL
}
Setup alert dialog:
if (preference?.key == getString(R.string.key_api_url)) {
showChangeApiDialog()
}
private fun showChangeApiDialog() {
val layout = layoutInflater.inflate(R.layout.dialog_api_configuration, null)
layout.urlCheckBox.text = BuildConfig.DEFAULT_BASE_URL
val dialog = AlertDialog.Builder(requireContext())
.setView(layout)
.setPositiveButton(getString(R.string.change)) { _, _ ->
val url = if (layout.urlCheckBox.isChecked) BuildConfig.DEFAULT_BASE_URL
else layout.urlEditText.text.toString()
if (url === settingsViewModel.getApiUrl()) return@setPositiveButton
settingsViewModel.changeApiUrl(url)
view?.snackbar("API URL changed to $url")
findNavController().popBackStack(R.id.eventsFragment, false)
}
.setNegativeButton(getString(R.string.cancel)) { dialog, _ -> dialog.cancel() }
.setCancelable(false)
.show()
dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = false
layout.urlCheckBox.setOnCheckedChangeListener { _, isChecked ->
layout.urlTextInputLayout.isVisible = !isChecked
dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = isChecked
}
Set URL to preferences in the view model and end current session:
fun changeApiUrl(url: String) {
preference.putString(API_URL, url)
logout()
}
Create Interceptor to Handle New API URL
Here default API URL is set to the retrofit already:
Retrofit.Builder()
.client(get())
.baseUrl(baseUrl)
.build()
As we discussed earlier OkHttp handles every network call for the application. So here we track the URL host from the okhttp interceptor. If the URL host is equaled to the default API URL host, then we can say that it is an API call and then we can replace same with the host getting from preferences if it is not null and set the interceptor to okhttp client builder.
Create host selection interceptor class to return interceptor with the API URL:
class HostSelectionInterceptor(private val preference: Preference) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
var original = chain.request()
val httpUrl = preference.getString(API_URL)?.toHttpUrlOrNull()
if (original.url.host == BuildConfig.DEFAULT_BASE_URL.toHttpUrlOrNull()?.host && httpUrl != null) {
val newUrl =
original.url.newBuilder()
.scheme(httpUrl.scheme)
.host(httpUrl.host)
.port(httpUrl.port)
.build()
original = original.newBuilder()
.url(newUrl)
.build()
}
return chain.proceed(original)
}
}
Set the interceptor to okhttp client builder:
val builder = OkHttpClient().newBuilder()
.addInterceptor(HostSelectionInterceptor(get()))
GIF
In a Nutshell
Server configuration provides better user experience for open-source platform and developer, as they can mention their own server and test it.
Resources
OkHttp client with retrofit: https://futurestud.io/tutorials/retrofit-2-share-okhttp-client-and-converters-between-retrofit-instances
Tags
Eventyay, open-event, OkHttp, Retrofit, FOSSASIA, GSoC, Android, Kotlin
You must be logged in to post a comment.