Simple example with Retrofit Gson and Enum types in Android

 The dependencies for this example:

implementation 'com.google.code.gson:gson:2.8.5'
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.6.0'

We will retrieve a list of clothes, with each cloth containing a string and an enum type. Here are the data models.
Cloth.kt

data class Cloth (
    @SerializedName("size") val clothSize : ClothSize,
    @SerializedName("cloth") val cloth : String
)

ColthSize.kt

enum class ClothSize {
    SMALL,
    MEDIUM,
    LARGE
}

The Retrofit api client for fetching the list of clothes from the internet.
ApiClient.kt

import retrofit2.Call
import retrofit2.http.GET

interface ApiClient {
    // https://api.jsonbin.io/b/5cad463a4b652413968069a2
    // https://api.myjson.com/bins/17n6pk
    @GET("bins/17n6pk")
    fun getClothes(): Call<List<Cloth>>
}

The utility class for creating Retrofit instance. RestUtil.kt

import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory

class RestUtil private constructor() {
    private val API_BASE_URL = "https://api.myjson.com/"
//    private val API_BASE_URL = "https://api.jsonbin.io/"
    val retrofit: Retrofit

    init {
        val interceptor = HttpLoggingInterceptor()
        interceptor.level = HttpLoggingInterceptor.Level.BODY
        val httpClient = OkHttpClient.Builder().addInterceptor(interceptor).build()

        val builder = Retrofit.Builder()
            .baseUrl(API_BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
        retrofit = builder.client(httpClient).build()
    }

    companion object {
        private var self: RestUtil? = null

        val instance: RestUtil
            get() {
                if (self == null) {
                    synchronized(RestUtil::class.java) {
                        if (self == null) {
                            self = RestUtil()
                        }
                    }
                }
                return self!!
            }
    }
}

Finally, the activity class for putting all of the above together and get the list of clothes from an url.

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        fab.setOnClickListener {
            fetchClothes()
        }

    }

    private fun fetchClothes() {
        tv_clothes.text = ""

        val client = RestUtil.instance.retrofit.create(ApiClient::class.java)
        val call = client.getClothes()
        call.enqueue(object : Callback<List<Cloth>> {
            override fun onResponse(call: Call<List<Cloth>>, response: Response<List<Cloth>>) {
                if (response.body() != null) {

                    val clothes = response.body()
                    var stringBuilder = StringBuilder("")
                    clothes?.forEach {
                        stringBuilder.append(it.cloth)
                        stringBuilder.append(" ")
                        stringBuilder.append(it.clothSize)
                        stringBuilder.append("\n")
                    }

                    tv_clothes.text = stringBuilder.toString()
                }
            }
            override fun onFailure(call: Call<List<Cloth>>, t: Throwable) {
                t.printStackTrace()
            }
        })
    }

}

Don’t forget to add the internet permission in the manifest file.

<uses-permission android:name="android.permission.INTERNET"/>

Android recyclerview item focus animation on dpad navigation

 Translation animation, moving in from top, anim/move_in_from_top.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:shareInterpolator="@android:anim/decelerate_interpolator">
    <translate
            android:fromXDelta="0%" android:toXDelta="0%"
            android:fromYDelta="-100%" android:toYDelta="0%" />
</set>

Translation animation, moving in from bottom, anim/move_in_from_bottom.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:shareInterpolator="@android:anim/decelerate_interpolator">
    <translate
            android:fromXDelta="0%" android:toXDelta="0%"
            android:fromYDelta="100%" android:toYDelta="0%" />
</set>

Translation animation, moving out to the bottom, anim/move_out_down.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:shareInterpolator="@android:anim/decelerate_interpolator">
    <translate
            android:fromXDelta="0%" android:toXDelta="0%"
            android:fromYDelta="0%" android:toYDelta="100%" />
</set>

Translation animation, moving out to the top, anim/move_out_top.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:shareInterpolator="@android:anim/decelerate_interpolator">
    <translate
            android:fromXDelta="0%" android:toXDelta="0%"
            android:fromYDelta="0%" android:toYDelta="-100%" />
</set>

The layout file for the list item, list_item.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:tools="http://schemas.android.com/tools"
                android:layout_width="match_parent"
                android:layout_height="60dp"
                android:background="@color/lightblue"
                android:focusable="true">

    <View android:id="@+id/view_bg"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:background="#000000"
          android:visibility="invisible"/>

    <TextView android:id="@+id/topic"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:textColor="#ffffff"
              android:gravity="center"
              tools:text="Education"/>

</RelativeLayout>

The layout file for the main activity with the RecyclerView, activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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.support.v7.widget.RecyclerView
            android:id="@+id/recycler_view"
            android:layout_width="200dp"
            android:layout_height="match_parent"
            android:scrollbars="none"
            android:descendantFocusability="afterDescendants"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"/>

</android.support.constraint.ConstraintLayout>

The RecylerView adapter, this class does all the animation jobs. ItemListAdapter.kt

import android.support.v7.widget.RecyclerView
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.AnimationUtils
import android.widget.TextView
import android.widget.Toast
import kotlinx.android.synthetic.main.list_item.view.*


class ItemListAdapter(private val itemList: List<String>): RecyclerView.Adapter<ItemListAdapter.ViewHolder>() {

    private var previousFocusedViewHolder: ViewHolder? = null
    private var previouslyFocusedPos = 0
    private var currentlyFocusedPos = 0
    private lateinit var recyclerView: RecyclerView

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val layoutView = LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false)
        return ViewHolder(layoutView)
    }

    override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
        super.onAttachedToRecyclerView(recyclerView)
        Log.d("ItemListAdapter", "onAttachedToRecyclerView ")
        this.recyclerView = recyclerView
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        Log.d("ItemListAdapter", "onBindViewHolder position: $position")

        holder.itemView.setOnFocusChangeListener { focusedView, hasFocus ->
            Log.d("ItemListAdapter", "onBindViewHolder OnFocusChangeListener position: $position")
            updateFocusPositions(holder, hasFocus, position)
            startFocusAnimation(holder, hasFocus)
        }

        holder.tvTopic.text = itemList[position]
    }

    override fun getItemCount() = itemList.size

    private fun startFocusAnimation(holder: ViewHolder, hasFocus: Boolean) {
        Log.d("ItemListAdapter", "startFocusAnimation hasFocus: $hasFocus, currentlyFocusedPos: $currentlyFocusedPos, previouslyFocusedPos: $previouslyFocusedPos")

        if (hasFocus) {
            previousFocusedViewHolder?.let {
                val moveOutAnimSet = if (currentlyFocusedPos > previouslyFocusedPos) R.anim.move_out_down else R.anim.move_out_up
                val moveOutAnim = AnimationUtils.loadAnimation(it.itemBg.context, moveOutAnimSet)
                moveOutAnim.fillAfter = true
                moveOutAnim.duration = 250
                it.itemBg.startAnimation(moveOutAnim)
                Log.d("ItemListAdapter", "startFocusAnimation, moving out from previousViewHolder: $it")
            }

            val moveInAnimSet = if (currentlyFocusedPos > previouslyFocusedPos) R.anim.move_in_from_top else R.anim.move_in_from_bottom
            val moveInAnim = AnimationUtils.loadAnimation(holder.itemBg.context, moveInAnimSet)
            moveInAnim.fillAfter = true
            moveInAnim.duration = 400
            holder.itemBg.startAnimation(moveInAnim)
            Log.d("ItemListAdapter", "startFocusAnimation, moving into the  currentViewHolder: $holder")
        }
    }

    private fun updateFocusPositions(viewHolder: ViewHolder, hasFocus: Boolean, position: Int) {
        if (hasFocus) {
            previouslyFocusedPos = currentlyFocusedPos
            currentlyFocusedPos = position
        } else {
            previousFocusedViewHolder = viewHolder
        }
    }

    inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener {
        var itemBg: View
        var tvTopic: TextView

        init {
            itemView.setOnClickListener(this)
            tvTopic = itemView.topic
            itemBg = itemView.view_bg
        }

        override fun onClick(view: View) {
            Toast.makeText(view.context, "Clicked Position = " + adapterPosition, Toast.LENGTH_SHORT).show()
        }
    }
}

The MainActivity.kt

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.LinearLayoutManager
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    private lateinit var linearLayoutManager: LinearLayoutManager
    private lateinit var listAdapter: ItemListAdapter

    val topics = listOf("Education","Finance","Government","Entertainment","Technology","Math","Biology","Physics","Chemistry","Space","Sports","Music","Animal","Countries","Weather","Politics","Traffic","Poverty","Social Media","Internet","Housing")

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        linearLayoutManager = LinearLayoutManager(this)
        listAdapter = ItemListAdapter(topics)

        recycler_view.setHasFixedSize(true)
        recycler_view.layoutManager = linearLayoutManager
        recycler_view.adapter = listAdapter
    }

}

How to extract filename from Uri?

Now, we can extract filename with and without extension :) You will convert your bitmap to uri and get the real path of your file. Now w...