Making Http Request in android is very common when it comes to create a Restful API Client Application. Because this is a network call so it is very common that this task is kind of blocking call. When the API becomes more complex like when it needs to authenticate via OAuth2.0 or something, then the total Http call became a total mess. Thanks to prebuilt libraries like Volley which makes developers life easier and take all mess out of the code.
Quote Master API based Applicaiton
In today post we are going to create an Application which fetch famous quotes from Rest API and display in a Recycler view. We use Volley and Gson for making Http request and parsing response Json data respectively.
Let’s summarise today’s goals first
- Create a recyclerview
- User volley to make Http Get request
- Pass header parameters
- Parse JSON Response into Model Class of quotes using Gson library
- Put list of quotes into Recyclerview and display
- Two buttons for Sharing Quote and one for Copying Quote text to clipboard
Gradle imports
Once we had decided which libraries we are gonna use lets first add required gradle imports in app level gradle file
implementation 'com.android.volley:volley:1.1.1'
implementation 'com.google.code.gson:gson:2.8.5'
Create API Key
We are going to use Free Quote API from Mashape the biggest market place for free as well as private APIs. Create a free account and create and Generate API Key.
Once you create your account and open up above mentioned api then you will see your dashboard and click on get API Key button then on left panel you will see the example response of API data.
Creating Model Data Class in Kotlin
As you can see this response is very simple which contains just three variables “quotes”, “author” and “category” field. So according to above data we create a Model Data Class under model package folder. Let’s Create as follows
data class Quote(var quote:String,var author:String,var category:String)
Creating Recycler View
Now that we are ready to fetch data from API, but before that we need to create our view where we can display fetched data. For this, We are going to use recyclerview. Let’s add recyclerview in our MainActiviy Layout xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".MainActivity"
android:orientation="vertical"
android:background="#DDDDDD"
>
<android.support.v7.widget.RecyclerView
android:id="@+id/recView1"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="android.support.v7.widget.LinearLayoutManager"
>
</android.support.v7.widget.RecyclerView>
</LinearLayout>
Creating Adapter Class
Next step for Recyclerview is to create it’s single Item resource layout file and an Adapter class. So let’s create Item.xml file in resource layout folder like this
<?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="wrap_content"
android:padding="16dp"
android:background="#FFF"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<TextView
android:id="@+id/quote_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="some random quote , this is dummy text"
android:textAppearance="@style/Base.TextAppearance.AppCompat.Large"
/>
<TextView
android:id="@+id/tvAuthor"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginTop="4dp"
android:text="-Author"
/>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#DEDEDE"
android:layout_marginTop="4dp"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_marginTop="4dp"
>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:id="@+id/shareIntentButton"
android:padding="4dp"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Share"
android:drawableLeft="@drawable/ic_share_black_24dp"
android:textSize="24dp"
android:drawablePadding="2dp"
android:textStyle="bold"
/>
</LinearLayout>
<View
android:layout_width="2dp"
android:layout_height="match_parent"
android:background="#DEDEDE"
/>
<LinearLayout
android:id="@+id/markIntentButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:padding="4dp"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Copy"
android:drawableLeft="@drawable/ic_content_copy_black_24dp"
android:textSize="24dp"
android:drawablePadding="2dp"
android:textStyle="bold"
/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
It will look like this
Now create a new class inherited from Recyclerview.Adapter class
package com.fypsolutions.quotemaster
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.TextView
import com.fypsolutions.quotemaster.interactions.ItemInteractor
import com.fypsolutions.quotemaster.models.Quote
class QuoteAdapter(private val itemList:ArrayList<Quote>, private val interactorIntrface:ItemInteractor?): RecyclerView.Adapter<QuoteAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.list_item, parent, false)
return ViewHolder(view)
}
override fun getItemCount(): Int {
return itemList.count()
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.mTitleTView.text = itemList[position].quote
holder.mAuthor.text = itemList[position].author
holder.mBookMarkButton.setOnClickListener {
interactorIntrface?.onCopyText(itemList[position])
}
holder.mShareButton.setOnClickListener {
interactorIntrface?.onShareButonClick(itemList[position])
}
}
inner class ViewHolder(val mView: View) : RecyclerView.ViewHolder(mView) {
val mTitleTView: TextView = mView.findViewById<View>(R.id.quote_text) as TextView
val mAuthor: TextView = mView.findViewById<View>(R.id.tvAuthor) as TextView
val mBookMarkButton:LinearLayout = mView.findViewById<View>(R.id.markIntentButton) as LinearLayout
val mShareButton:LinearLayout=mView.findViewById<View>(R.id.shareIntentButton) as LinearLayout
override fun toString(): String {
return super.toString() + " '" + mTitleTView.text + "'"+mAuthor.text+";"
}
}
}
Finally come back to your Main Activity class and just setup your recyclerview like this
Next create some global variables for your API Call
private val TAG = this.javaClass.simpleName
private val API_KEY = "Your-API-Key"
private val API_URL = "https://andruxnet-random-famous-quotes.p.mashape.com"
var gson = Gson()
var requestQueue:RequestQueue? = null
var stringRequest: StringRequest? = null
Here you can see that we created one variable for API-KEY and one for API_URL and one gson object and two objects from volley
Make Http Get Request using Volley
Now that we had set up every thing so final step is to make Get Request using volley library and parse response data using gson and then finally parse objects to global quote list and notify adapter about the change in data like this
val totalURL = String.format(API_URL+"?cat=%1\$s&count=%2\$s",
"famous",
10)
// Instantiate the RequestQueue.
val requestQueue = Volley.newRequestQueue(this)
stringRequest = object : StringRequest(Request.Method.GET, totalURL,
Response.Listener { response ->
val precount = quoteList.count()
val quoteJsonArray = JSONArray(response)
for (i in 0 until quoteJsonArray.length()) {
val singleObject = quoteJsonArray.getJSONObject(i)
val gsonparse = gson.fromJson(singleObject.toString(), Quote::class.java)
quoteList.add(gsonparse)
}
if(quoteJsonArray.length()>0){
mAdapter.notifyItemRangeChanged(precount,quoteList.count())
}
tvStatus.text="Length = ${quoteJsonArray.length()}"
},
Response.ErrorListener { error ->
tvStatus.text = error.toString()
Log.d("ERROR", "error => " + error.toString())
}
) {
@Throws(AuthFailureError::class)
override fun getHeaders(): Map<String, String> {
val params = HashMap<String, String>()
params["X-Mashape-Key"] = API_KEY
params["Accept"] = "application/json"
return params
}
}
// Add the request to the RequestQueue.
requestQueue?.add(stringRequest)
One more thing that we can do is to stop requestQueue in onStop method like this
override fun onStop() {
super.onStop()
requestQueue?.cancelAll(TAG)
}
Handling Click Listners
As you may already noticed that we had some errors in our adapter class as we had not defined the interaction listener class to handle our click listeners so let’s just create an Interface first
interface ItemInteractor{
fun onShareButonClick(quote: Quote)
fun onCopyText(quote: Quote)
}
This interface hold just two buttons click capturing functions, one for Share button click and one for Copying text to clipboard. Let’s quickly let implement MainActivity class this interface and provide methods there
class MainActivity : AppCompatActivity(), ItemInteractor {
override fun onCopyText(quote: Quote) {
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText(quote.author, quote.quote)
clipboard.primaryClip = clip
toast("Quote Copied to Clipboard")
}
override fun onShareButonClick(quote: Quote) {
val sendIntent = Intent()
sendIntent.action = Intent.ACTION_SEND
sendIntent.putExtra(Intent.EXTRA_TEXT,
quote.quote)
sendIntent.type = "text/plain"
startActivity(sendIntent)
}
}
Time to view real magic done on emulator or on real device which ever way you like so let’s quickly run the project and see things happening.
That’s it for today. Hope you enjoyed building app with me. Let me know if you face any problem in comments section, I will be happy to help 🙂
Download Full Source code
If you are curious to directly jump into full code? here is the link to github repository for this project files