Build a Basic CRUD App in Android with Kotlin

Karl - Sep 21 '18 - - Dev Community

Kotlin was recently given official Android support status by Google, but it remains difficult to understand for many developers. The best way to start is by creating a complete app yourself, which you’ll do in this tutorial. In this tutorial, you’ll use Spring Boot for the API that powers your Android (+ Kotlin) mobile app. Spring Boot is a great way to create a robust REST API with a minimal amount of code.

I’m going to assume you have some Java experience and have at least played around with creating an Android app. If you don’t have any Android experience you should be able to follow along but you might have to Google a few things here and there.

Here is the complete code if you rather go straight to the end.

Before we start, let’s talk a bit about Kotlin.

Kotlin vs Java

Kotlin looks strange to newcomers. It resembles other languages you may have seen but some things look off, often because it is so concise!

Don’t panic - because it is so extensible there are many ways to write the same code, and many shortcuts that aren’t available in other languages. For example, often you’ll see curly brackets used as function parameters:

dialogBuilder.setPositiveButton("Delete", { dialog, whichButton ->
    deleteMovie(movie)
})

Enter fullscreen mode Exit fullscreen mode

This is actually creating an anonymous function (a lambda) and passing it in. This function takes in two parameters which are here inferred. Take a look at the equivalent (pre-JRE 8) Java code:

dialogBuilder.setPositiveButton("Delete",
    new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int which) {
            deleteMovie(movie);
        }
    }
);

Enter fullscreen mode Exit fullscreen mode

(Of course, now Java 8 has lambdas too).

Here is another example of some code we will use in a bit:

class MovieViewHolder(val view: View) : RecyclerView.ViewHolder(view)

Enter fullscreen mode Exit fullscreen mode

In order to understand this you have to know several things:

Declaring a class with parentheses (i.e. (view: View)) means you are declaring the class’s primary constructor (and yes - there are secondary constructors as well).The colon : is similar to implements or extends but really is about Interfaces.Anything declared in the primary constructor is automatically declared as a property (member variable).

For clarity, this is the equivalent Java:

public static class MovieViewHolder extends RecyclerView.ViewHolder {
    public final View view;
    public MovieViewHolder(View v) {
        super(v);
        view = v;
   }
}

Enter fullscreen mode Exit fullscreen mode

As a last example, look at the following bean:

package demo

data class Movie( val id: Int, val name: String )

Enter fullscreen mode Exit fullscreen mode

That is the complete file. It declares a class with a constructor, two read-only properties (member variables), and assigns those in the constructor. Then data creates getters and setters for all our member variables, as well as equals(), toString() and others (see here if you want to see it in it’s full Java glory).

Now that you’ve got some background, let’s get started!

Create the Spring Boot API for Your Android + Kotlin Project

The official Spring Boot tutorials suggest you use the Initializr Website to create a starting skeleton but I find it easier to build projects from scratch.

To start, initialize an empty directory with Gradle (make sure you’ve installed Gradle and that it’s available on the command line).

C:\Users\Karl\Kotlin-Spring>gradle init

BUILD SUCCESSFUL in 3s
2 actionable tasks: 2 executed
C:\Users\Karl\Kotlin-Spring>

Enter fullscreen mode Exit fullscreen mode

You should have two folders and four files.

.
├── build.gradle
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle

2 directories, 6 files

Enter fullscreen mode Exit fullscreen mode

Now change build.gradle to the following:

buildscript {
    ext.kotlin_version = '1.2.61' // Required for Kotlin integration
    ext.spring_boot_version = '2.0.2.RELEASE'
    repositories {
        jcenter()
    }
    dependencies {
        // Required for Kotlin integration
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        // See https://kotlinlang.org/docs/reference/compiler-plugins.html#kotlin-spring-compiler-plugin         
        classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlin_version"
        classpath("org.jetbrains.kotlin:kotlin-noarg:$kotlin_version")
        classpath "org.springframework.boot:spring-boot-gradle-plugin:$spring_boot_version"
    }
}

// Required for Kotlin integration
apply plugin: 'kotlin'
// See https://kotlinlang.org/docs/reference/compiler-plugins.html#kotlin-spring-compiler-plugin
apply plugin: "kotlin-spring" 
apply plugin: 'kotlin-jpa'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

jar {
    baseName = 'kotlin-demo'
    version = '0.1.0-SNAPSHOT'
}

repositories {
    jcenter()
}

dependencies {
    // Required for Kotlin integration
    compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    compile "org.jetbrains.kotlin:kotlin-reflect" // For reflection
    compile 'org.springframework.boot:spring-boot-starter-data-rest'
    compile 'org.springframework.boot:spring-boot-starter-data-jpa'
    compile 'com.h2database:h2'
}

Enter fullscreen mode Exit fullscreen mode

Here the Kotlin and Spring Boot plugins are imported, external repositories are declared, and dependency libraries are added.

If you haven’t used Spring Boot before you should know that it (or rather the Spring Framework) uses dependency injection at runtime. This means the entire application is wired up automatically based on the libraries you import. For example, at the end of our build.gradle you’ll see the Data REST and Data JPA libraries. Spring Boot will automatically configure your application as a REST server when it sees two these libraries. Furthermore, since you included the H2 database library Spring will use the H2 database engine to persist any of our REST data coming in and out of queries.

All you need to have a complete REST application, is to define a class with the @SpringBootApplication annotation. You don’t even need to specify it’s path - Spring will search for it!

Put the following into src/main/kotlin/demo/Application.kt:

package demo

import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication

@SpringBootApplication
class Application

fun main(args: Array<String>) {
    SpringApplication.run(Application::class.java, *args)
}

Enter fullscreen mode Exit fullscreen mode

Now if you run gradlew bootRun (./gradlew bootRun on *nix) everything should build (and download) and you should see somewhere in the enormous log Started Application. Now run curl in another window to see what is happening.

C:\Users\Karl>curl localhost:8080
{
  "_links" : {
    "profile" : {
      "href" : "http://localhost:8080/profile"
    }
  }
}

Enter fullscreen mode Exit fullscreen mode

Amazingly, you’ve created a fully compliant REST server with Kotlin, all by editing just two files!

Add Objects with Kotlin

To create objects you just need the entity class and a repository.

Next to Application.kt put the following into Model.kt

package demo

import javax.persistence.*

@Entity
data class Movie(@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
                 val Id: Long,
                 val name: String)

Enter fullscreen mode Exit fullscreen mode

Here you’ve used the data idiom to create getters and setters for all the properties, as well as JPA annotations to specify how to generate the ids for your entity.

Note: The Id field must start with a capital I. If it doesn’t, the server won’t return the id field when doing queries. This will give you trouble down the line when hooking up to the client app.

Now put this into Repository.kt:

package demo

import org.springframework.data.repository.CrudRepository

interface ItemRepository : CrudRepository<Movie, Long>

Enter fullscreen mode Exit fullscreen mode

And you’ve done it! Incredibly, we can now perform any CRUD operation on this server and it will work, persisting all changes to the database.

C:\Users\Karl>curl -X POST -H "Content-Type:application/json" -d " {\"name\":\"The 40 Year Old Virgin\"} " localhost:8080/movies
{
  "name" : "The 40 Year Old Virgin",
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/movies/1"
    },
    "item" : {
      "href" : "http://localhost:8080/movies/1"
    }
  }
}


C:\Users\Karl>curl localhost:8080/movies/1
{
  "name" : "The 40 Year Old Virgin",
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/movies/1"
    },
    "item" : {
      "href" : "http://localhost:8080/movies/1"
    }
  }
}

Enter fullscreen mode Exit fullscreen mode

Load Initial Data in your Kotlin App

To finish up, let’s load some data. Again, as with Spring Boot everything can be done simply. Just put the following into src/main/resources/data.sql and it will be run on boot.

INSERT INTO movie (name) VALUES
  ('Skyfall'),
  ('Casino Royale'),
  ('Spectre');

Enter fullscreen mode Exit fullscreen mode

To confirm it works, restart the server and run curl localhost:8080/movies.

And you are done with the back-end. Time to build out the client.

Build Your Android App with Kotlin

This will require a couple of steps: First you’ll create an empty Kotlin app with Android Studio. You’ll then create a list view (with add, edit and delete buttons) using RecyclerView, populating it with hard-coded data. Finally, you’ll use Retrofit to wire the view to the REST back-end you’ve just created.

Create a project in Android Studio. Make sure you’re using at least version Android Studio 3. Use the default values for each window except make sure you include Kotlin support. Name the project whatever you want - I called mine “Kotlin Crud”. At the end, select an Empty Activity.

When you press Play on the top icon bar you should see Hello World when you run it (you can either plug in your phone or run it on an emulator. Check online for how to set this up).

Hello World in Android

If you’ve made an Android app before using Java you’ll notice the only difference is the main activity: it’s called MainActivity.kt , not MainActivity.java, and the code looks a bit different.

package demo

import android.support.v7.app.AppCompatActivity
import android.os.Bundle

class MainActivity : AppCompatActivity() {

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

Enter fullscreen mode Exit fullscreen mode

Here are the differences:

  1. The class is not specified as public (in Kotlin this is the default)
  2. Types are specified with a colon : - the class is of type AppCompatActivity (or rather it implements AppCompatActivity, as you would say in Java) and the savedInstanceState is of type Bundle
  3. Methods are just called fun instead of function
  4. override is not an annotation
  5. The question mark means a parameter is optional (which is not possible in Java)

The last point is one of the most talked about when discussing the importance of Kotlin vs Java: it’s one of the various ways the language ensures null safety.

Import Additional Android Libraries

You need to add extra libraries to your application’s build.gradle file: one for the recycler view (which you’ll use in a second), one for the card view, and another for the floating action button. Place these next to the others in the dependencies section.

implementation 'com.android.support:design:27.1.1'
implementation 'com.android.support:cardview-v7:27.1.1'
implementation 'com.android.support:recyclerview-v7:27.1.1'

Enter fullscreen mode Exit fullscreen mode

Android Studio should ask you to Sync Now. Click that and see that everything builds without error.

Note: Make sure the version is the same as the other support libraries (e.g. appcompat-v7:27.1.1). Also, because you’ll be using built-in icons (which you should avoid doing in the future) you need to put the following into the defaultConfig section of your build.gradle as well.

vectorDrawables.useSupportLibrary = true

Enter fullscreen mode Exit fullscreen mode

Add Icons in Kotlin

You’ll need some icons for buttons - one for add and another for refresh. Go to the Material Icons site and select the one you like. I’m choosing the add button half the way down. When you click on it a grey and blue download section should appear on the button left. Click the grey box Selected Icons control to open the download options. Now there should be a drop-down where you can select Android as the type.

Change drop down to Android

Change the color to white and download the PNG option. Extract the contents of the ZIP file to app/src/main (you should see the ZIP file has a res folder in it).

Now you can use the new icons in your layouts. They’re called things like baseline_add_white_36.

Finally do the same thing for the loop icon, also white.

Create the View XML

You need an XML view for each list item. Place the following into src/main/res/layout/list_item.xml.

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"

    android:id="@+id/card_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginBottom="3dp"
    android:layout_marginLeft="5dp"
    android:layout_marginRight="5dp"
    android:layout_marginTop="5dp"
    android:padding="3dp"
    card_view:cardElevation="2dp"
    card_view:cardMaxElevation="2dp">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="5dp">

        <TextView
            android:id="@+id/name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="5dp"
            android:text="lastname"
            android:textSize="16dp" />

        <TextView
            android:id="@+id/btnDelete"
            android:layout_width="wrap_content"
            android:layout_height="35dp"
            android:layout_alignParentRight="true"
            android:drawableLeft="@android:drawable/ic_delete"
            android:padding="5dp" />

        <TextView
            android:id="@+id/btnEdit"
            android:layout_width="wrap_content"
            android:layout_height="35dp"
            android:layout_marginRight="2dp"
            android:layout_toLeftOf="@+id/btnDelete"
            android:drawableLeft="@android:drawable/ic_menu_edit"
            android:padding="5dp" />
    </RelativeLayout>

</android.support.v7.widget.CardView>

Enter fullscreen mode Exit fullscreen mode

Here you’re using a Card View which is the popular way of creating lists in Android. Almost all of the XML is layout settings to ensure proper alignment. Note the android:id values which you use to connect these to our Kotlin files. Also, I’ve used some built in Android icons for our edit and delete buttons.

Note: this is not the recommended way of doing it since those icons can change between Android Studio versions - rather download the icons as we did previously !

Now for the main activity XML. Here is what src/main/res/layout/activity_main.xml should look like.

<?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="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/rv_item_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/baseline_add_white_36"
        android:layout_gravity="bottom|end"
        app:elevation="6dp"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentEnd="true"
        android:layout_margin="20dp"/>

</RelativeLayout>

Enter fullscreen mode Exit fullscreen mode

It’s pretty straight forward. You’ve now got a recycle view and a floating action button inside of a relative layout and have assigned baseline_add_white_36 as the source for the button. Note that the id of the recycler view is rv_list_item (you’ll be using this soon).

Add Refresh to the Action Bar

To fill things out lets put a refresh button on the action bar. This requires a new piece of XML in res/menu/buttons.xml:

<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
        android:id="@+id/refresh"
        android:icon="@drawable/baseline_loop_white_48"
        android:title="@string/refresh"
        app:showAsAction="ifRoom"/>

</menu>

Enter fullscreen mode Exit fullscreen mode

Note it has an id called refresh. Also, I’ve used the loop icon from the Android Icons site (the white variant) - you’ll have to download this as before. Also, I’m using a string from the resources so you’ll have to change res/values/strings.xml:

<resources>
    <string name="app_name">Kotlin Crud</string>
    <string name="refresh">Refresh</string>
</resources>

Enter fullscreen mode Exit fullscreen mode

Display Lists in Kotlin

Now to display a list of item using our views. The canonical way of doing this is the relatively new RecyclerView which supplanted the original ListView. The basic idea of a RecyclerView is to create only enough views to show on screen - if the screen can fit five items then only five are created. As you scroll through the list these views are re-used (recycled), replacing their contents with the appropriate (new) values.

How do you get started with this ? The first thing you need is a bean. Let’s call it Movie.kt.

package demo

data class Movie( val id: Int, val name: String )

Enter fullscreen mode Exit fullscreen mode

Note: for all the following classes make sure the package matches that of MainActivity.kt.

Was that not easy? Next, you need an Adapter. This is a class with three methods: one to return how many items in total are being displayed (getItemCount()), one that creates an Android View control for a particular item (onCreateViewHolder()), and one that populates an existing view with an instance of your data (onBindViewHolder()).

Put this into MovieAdapter.kt.

class MovieAdapter : RecyclerView.Adapter<MovieAdapter.MovieViewHolder>() {

    var movies: ArrayList<Movie> = ArrayList()

    init { refreshMovies() }

    class MovieViewHolder(val view: View) : RecyclerView.ViewHolder(view)

    override fun onCreateViewHolder(parent: ViewGroup,
                                    viewType: Int): MovieAdapter.MovieViewHolder {

        val view = LayoutInflater.from(parent.context)
                .inflate(R.layout.list_item, parent, false)

        return MovieViewHolder(view)
    }

    override fun onBindViewHolder(holder: MovieViewHolder, position: Int) {
        holder.view.name.text = movies[position].name
    }

    override fun getItemCount() = movies.size

    fun refreshMovies() {
        movies.clear()

        movies.add(Movie(0, "Guardians of the Galaxy"))
        movies.add(Movie(1, "Avengers: Infinity War"))
        movies.add(Movie(2,"Thor: Ragnorok"))

        notifyDataSetChanged()
    }
}

Enter fullscreen mode Exit fullscreen mode

When you paste this into Android Studio it will highlight certain things as red. You need to ALT-ENTER (Option + Enter on Mac) several times to pull in the imports you need. Eventually this is the list of imports you should have:

import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import kotlinx.android.synthetic.main.list_item.view.*

Enter fullscreen mode Exit fullscreen mode

A lot is going on in MovieAdapter.kt. Besides just implementing the three methods needed to implement RecylcerView.Adapter, you created a property called movies, a list, and initialise it in the init{} constructor. Also, you declared an inner class called MovieViewHolder. That is what gets instantiated for each view needed to be displayed (in the example discussed, five views). As you can see, onCreateViewHolder actually returns an object of this type. The class is quite simple - it takes into its constructor a View (which is now also a property), and returns a Holder type object. This object is what you then use when you fill in data using onBindViewHolder - in our case, setting the text of our display.

This does seem complicated at first. It good way to look at all this is the following: how does this connect to your main code class (i.e. MainActivity.kt), and how does it connect to the views you’ve defined in XML ?

For the first part, this is what main activity should now look like:

class MainActivity : AppCompatActivity() {

    lateinit var adapter:MovieAdapter

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

        adapter = MovieAdapter()    

        rv_item_list.layoutManager = LinearLayoutManager(this)
        rv_item_list.adapter = adapter
    }
}

Enter fullscreen mode Exit fullscreen mode

So here you’ve defined the adapter as a lateinit variable - lateinit is telling Kotlin that you want to initialize this at some stage after creation, not the default in Kotlin classes - normally you have to initialize things immediately.

In the constructor you assign an instance of your adapter to this property (note you don’t need to use new in Kotlin) and assign two things to rv_item_list - a LayoutManager (which is used for positioning), and an Adapter (which we’ve just created).

We should talk about rv_item_list. This is just the id of a control inside of activity_main.xml, specifically the recyclerview. Normally you would need to use findViewById (a pain for Android developers) but with Kotlin you can just specify its name. When Android Studio complains about imports and you ALT-ENTER (or your platform equivalent) it will automatically import kotlinx.android.synthetic.main.activity_main.*, bringing in all the ids into the namespace.

Lastly, add the following two functions to MainActivity:

override fun onCreateOptionsMenu(menu: Menu): Boolean {
    val inflater = menuInflater
    inflater.inflate(R.menu.buttons, menu)
    return true
}

override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
    R.id.refresh -> {
        adapter.refreshMovies()
        Toast.makeText(this.baseContext, "Refreshed", Toast.LENGTH_LONG).show())
        true
    }
    else -> {
        super.onOptionsItemSelected(item)
    }
}

Enter fullscreen mode Exit fullscreen mode

That will inflate the menu xml you defined as well as tying the button to your adapter’s refresh function (and give a convenience toast to say it worked).

That should be it ! Run our code and you should see the following.

Completed Android app with movie list

Wiring Your Android + Kotlin App Up

Next you need to replace the hard-coded values with data coming from your API server, as well as wiring the different buttons to their respective API calls. For that you will be using Square’s Retrofit library.

Start by adding the following to your build.gradle dependencies:

implementation 'com.squareup.retrofit2:retrofit:2.3.0'
implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
implementation 'com.squareup.okhttp3:logging-interceptor:3.9.1'

Enter fullscreen mode Exit fullscreen mode

Now take a look at what happens when you call your server for a list of movies:

C:\Users\Karl>curl http://localhost:8080/movies
{
  "_embedded" : {
    "movies" : [ {
      "name" : "Skyfall",
      "id" : 1,
      "_links" : {
        "self" : {
          "href" : "http://localhost:8080/movies/1"
        },
        "movie" : {
          "href" : "http://localhost:8080/movies/1"
        }
      }
    }

Enter fullscreen mode Exit fullscreen mode

I’ve only shown one since it’s quite long (Spring follows something called HATEOAS which adds links to json responses). As you can see, the response is wrapped in an _embedded object, and your movies come as a list in movies. You need to represent this in your Kotlin model so Retrofit can know what to expect. Change Movie.kt to this:

import com.google.gson.annotations.SerializedName

data class Movie( val id: Int, val name: String )
data class MovieList (
    @SerializedName("movies" )
    val movies: List<Movie>
)
data class MovieEmbedded (
    @SerializedName("_embedded" )
    val list: MovieList
)

Enter fullscreen mode Exit fullscreen mode

Now you need to create a new class to setup Retrofit. Let’s call it MovieApiClient.kt:

import io.reactivex.Completable
import io.reactivex.Observable
import retrofit2.Retrofit
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.http.*

interface MovieApiClient {

    @GET("movies") fun getMovies(): Observable<MovieEmbedded>
    @POST("movies") fun addMovie(@Body movie: Movie): Completable
    @DELETE("movies/{id}") fun deleteMovie(@Path("id") id: Int) : Completable
    @PUT("movies/{id}") fun updateMovie(@Path("id")id: Int, @Body movie: Movie) : Completable

    companion object {

        fun create(): MovieApiClient {

            val retrofit = Retrofit.Builder()
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                    .addConverterFactory(GsonConverterFactory.create())
                    .baseUrl("http://10.0.2.2:8080/")
                    .build()

            return retrofit.create(MovieApiClient::class.java)
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

Here you define all the endpoints using annotations as well as their expected return types (Completable, part of RxJava, just means nothing is returned). You also declare a companion object (which is like a static class) which instantiates a Retrofit builder with the details of our API. Note the base url uses the IP 10.0.2.2 which allows emulators to connect to localhost.

Now in MovieAdapter change the header to include a context property (so you can attach toasts for our API results) as well as adding a lazy client property initialized with your previous create() method.

class MovieAdapter(val context: Context) : RecyclerView.Adapter<MovieAdapter.MovieViewHolder>() {

    val client by lazy { MovieApiClient.create() }
    var movies: ArrayList<Movie> = ArrayList()

Enter fullscreen mode Exit fullscreen mode

Lazy takes in a function (note the curly brackets) and says “when someone first tries to use this property, run this function and assign it”.

To initialize the context, change the adapter initialize statement to include the main activity context:

adapter = MovieAdapter(this.baseContext)

Enter fullscreen mode Exit fullscreen mode

Now change refreshMovies() in the adapter to the following:

fun refreshMovies() {
    client.getMovies()
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe({ result ->
                movies.clear()
                movies.addAll(result.list.movies)
                notifyDataSetChanged()
        },{ error ->
                Toast.makeText(context, "Refresh error: ${error.message}", Toast.LENGTH_LONG).show()
                Log.e("ERRORS", error.message)
        })
}

Enter fullscreen mode Exit fullscreen mode

So you’re using the client’s getMovies() function which is declared at the top of MovieApiClient.kt. To understand what is going on here is an entire discussion on it’s own. Basically it’s using Reactive Programming which is a new way to wire things that are asynchronous (like calling out to an external API) together.

For the rest of the access methods, put the following after refreshMovies():

fun updateMovie(movie: Movie) {
    client.updateMovie(movie.id, movie)
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe({ refreshMovies() }, { throwable ->
                Toast.makeText(context, "Update error: ${throwable.message}", Toast.LENGTH_LONG).show()
        })
}

fun addMovie(movie: Movie) {
    client.addMovie(movie)
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe({ refreshMovies() }, { throwable ->
                Toast.makeText(context, "Add error: ${throwable.message}", Toast.LENGTH_LONG).show()
        })
}

fun deleteMovie(movie: Movie) {
    client.deleteMovie(movie.id)
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe({ refreshMovies() }, { throwable ->
                Toast.makeText(context, "Delete error: ${throwable.message}", Toast.LENGTH_LONG).show()
        })
    }

Enter fullscreen mode Exit fullscreen mode

Here you use the Retrofit reactive calls as before, but instead just call refreshMovies() when things return successfully (and make a toast of the error if they do not).

Show Dialogs in Kotlin

The last thing you need is to show dialogs for the various input situations: delete, edit, and add. Here you’ll do this manually so you don’t have to create any new XML.

In MainActivity.kt add the following function:

fun showNewDialog() {
    val dialogBuilder = AlertDialog.Builder(this)

    val input = EditText(this@MainActivity)
    val lp = LinearLayout.LayoutParams(
            LinearLayout.LayoutParams.MATCH_PARENT,
            LinearLayout.LayoutParams.MATCH_PARENT)
    input.layoutParams = lp

    dialogBuilder.setView(input)

    dialogBuilder.setTitle("New Movie")
    dialogBuilder.setMessage("Enter Name Below")
    dialogBuilder.setPositiveButton("Save", { dialog, whichButton ->
        adapter.addMovie(Movie(0,input.text.toString()))
    })
    dialogBuilder.setNegativeButton("Cancel", { dialog, whichButton ->
        //pass
    })
    val b = dialogBuilder.create()
    b.show()
}

Enter fullscreen mode Exit fullscreen mode

Here the Dialog Builder is used to show a standard pop-up. You’ve also manually added in an EditText control so the user can enter in a new name. Listeners are made for the Positive and Negative buttons (see the Adding Buttons section of the previous link), and when Positive happens (a dialog confirmation) the adapter’s addMovie function is called.

To make sure this dialog pops up when you click the action button, put the following at the bottom of MainActivity.onCreate() function:

fab.setOnClickListener{ showNewDialog() }

Enter fullscreen mode Exit fullscreen mode

Here is the code for the other dialogs, which we’ll put intoMovieAdapter:

fun showUpdateDialog(holder: MovieViewHolder, movie: Movie) {
    val dialogBuilder = AlertDialog.Builder(holder.view.context)

    val input = EditText(holder.view.context)
    val lp = LinearLayout.LayoutParams(
             LinearLayout.LayoutParams.MATCH_PARENT,
             LinearLayout.LayoutParams.MATCH_PARENT)
    input.layoutParams = lp
    input.setText(movie.name)

    dialogBuilder.setView(input)

    dialogBuilder.setTitle("Update Movie")
    dialogBuilder.setPositiveButton("Update", { dialog, whichButton ->
        updateMovie(Movie(movie.id,input.text.toString()))
    })
    dialogBuilder.setNegativeButton("Cancel", { dialog, whichButton ->
        dialog.cancel()
    })
    val b = dialogBuilder.create()
    b.show()
}

fun showDeleteDialog(holder: MovieViewHolder, movie: Movie) {
    val dialogBuilder = AlertDialog.Builder(holder.view.context)
    dialogBuilder.setTitle("Delete")
    dialogBuilder.setMessage("Confirm delete?")
    dialogBuilder.setPositiveButton("Delete", { dialog, whichButton ->
        deleteMovie(movie)
    })
    dialogBuilder.setNegativeButton("Cancel", { dialog, whichButton ->
        dialog.cancel()
    })
    val b = dialogBuilder.create()
    b.show()
}

Enter fullscreen mode Exit fullscreen mode

To wire them up, add the following to the onBindViewHolder method:

holder.view.btnDelete.setOnClickListener { showDeleteDialog(holder, movies[position]) }
holder.view.btnEdit.setOnClickListener { showUpdateDialog(holder, movies[position]) }

Enter fullscreen mode Exit fullscreen mode

You’re almost done. You just need to give your app permission to access the outside server (the internet). Add the following above the <application> node in your AndroidManifest.xml.

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

Enter fullscreen mode Exit fullscreen mode

Now run your app. You should be able to add, edit and delete, all persisted to the back-end and each action with a dialog to confirm or with details. You’ve just made a complete CRUD client-app solution!

Add a movie via a new dialog

Add Authentication for a Secure Mobile App with Android and Kotlin

Most modern apps require some level of security, so it’s worthwhile to know how to add authentication quickly and easily. For this, you’ll use the OktaAppAuth wrapper library.

Why Okta?

Okta’s goal is to make identity management a lot easier, more secure, and more scalable than what you’re used to. Okta is a cloud service that allows developers to create, edit, and securely store user accounts and user account data, and connect them with one or multiple applications. Our API enables you to:

Register for a forever-free developer account, and when you’re done, create a new Native Application and make note of the client id and redirect URI.

Add an OAuth 2.0 Authorization Server

First you need to turn your server into an OAuth resource (managed by Okta). Add the following to your build.gradle.

compile 'com.okta.spring:okta-spring-boot-starter:0.5.0'
compile 'org.springframework.boot:spring-boot-starter-security'
compile 'org.springframework.security.oauth:spring-security-oauth2:2.2.0.RELEASE'
compile 'org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure:2.0.1.RELEASE'

Enter fullscreen mode Exit fullscreen mode

Now add the following import to your Application.kt.

import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer

Enter fullscreen mode Exit fullscreen mode

Also annotate the class with @EnableResourceServer. Then create a file called src/main/resource/application.properties:

okta.oauth2.issuer=https://{yourOktaDomain}/oauth2/default
okta.oauth2.clientId={clientId}

Enter fullscreen mode Exit fullscreen mode

You can get the client ID value in the Applications tab in the Okta management website when you login.

Now when you restart Spring Boot and try access your API anonymously, you will get an authorization error.

C:\Users\Karl>curl localhost:8080/movies
{"error":"unauthorized","error_description":"Full authentication is required to access this resource"}

Enter fullscreen mode Exit fullscreen mode

Add the Android AppAuth Plugin

To make our app use Okta you need to use the AppAuth plugin for Android. Start by creating a new activity to house the login process.

Go to New -> Activity -> Empty Activity and call it LoginActivity. Inside of the created activity_login.xml place the following:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".LoginActivity">

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="vertical"
        android:gravity="center">

        <TextView
            android:id="@+id/auth_message"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:paddingBottom="8pt"
            android:text="Initializing authorization"
            style="@style/Base.TextAppearance.AppCompat.Medium"/>

        <ProgressBar
            android:id="@+id/progress_bar"
            style="@style/Widget.AppCompat.ProgressBar.Horizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:indeterminate="true"/>

        <Button
            android:id="@+id/auth_button"
            style="@style/Widget.AppCompat.Button.Colored"
            android:text="Login"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:visibility="gone" />

    </LinearLayout>
</android.support.constraint.ConstraintLayout>

Enter fullscreen mode Exit fullscreen mode

Now inside of AndroidManifest.xml swap the names of the login activity and main activity so that it’s the login which is launched on startup.

Hello World in Android

You’ve added a progress bar which will stay until the authorization connection to Okta has been made. Once it is complete you’ll hide that and then show a login button. To do that you need to change LoginActivity.kt. But first, add the following to your build.gradle.

implementation 'com.okta.android:appauth-android:0.1.0'

Enter fullscreen mode Exit fullscreen mode

That will pull in the Okta AppAuth library for Android. You also need to change the support lib version to 25.3.1 so that it’s compatible with this library, so change any references to the other versions (e.g. 27.1.1) to that version. Also, change the minSdkVersion to 16 and the target SDK to 25. Finally, add the following to defaultConfig:

android.defaultConfig.manifestPlaceholders = [
    // match the protocol of your "Login redirect URI"
    "appAuthRedirectScheme": "com.oktapreview.dev-628819"
]

Enter fullscreen mode Exit fullscreen mode

Now everything should build fine.

Next, change the contents of LoginActivity.kt to the following:

class LoginActivity : AppCompatActivity() {

    private var mOktaAuth: OktaAppAuth? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        mOktaAuth = OktaAppAuth.getInstance(this)

        setContentView(R.layout.activity_login)

        mOktaAuth!!.init(
                this,
                object : OktaAppAuth.OktaAuthListener {
                    override fun onSuccess() {
                        auth_button.visibility = View.VISIBLE
                        auth_message.visibility = View.GONE
                        progress_bar.visibility = View.GONE
                    }

                    override fun onTokenFailure(ex: AuthorizationException) {
                        auth_message.text = ex.toString()
                        progress_bar.visibility = View.GONE
                        auth_button.visibility = View.GONE
                    }
                }
        )

        val button = findViewById(R.id.auth_button) as Button
        button.setOnClickListener { v ->
                val completionIntent = Intent(v.context, MainActivity::class.java)
                val cancelIntent = Intent(v.context, LoginActivity::class.java)

                cancelIntent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP

                mOktaAuth!!.login(
                        v.context,
                        PendingIntent.getActivity(v.context, 0, completionIntent, 0),
                        PendingIntent.getActivity(v.context, 0, cancelIntent, 0)
                )
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

This will try initialize Okta authentication and hide both the progress bar and the message when done, showing a login button. Right now you’ll see an error if you try to run it. We need to add in the Okta app details to our project

Configure Your Okta App

Create app/src/main/res/raw/okta_app_auth_config.json and place the following inside:

{
  "client_id": "{clientId}",
  "redirect_uri": "{redirectUriValue}",
  "scopes": ["openid", "profile", "offline_access"],
  "issuer_uri": "https://{yourOktaDomain}/oauth2/default"
}

Enter fullscreen mode Exit fullscreen mode

The clientId and redirectUriValues you should get from your application in the Okta management console.

Now when you restart your app you should see a login button.

Login button added to application

Pressing it will take you to a pre-build Okta login screen. If you login using credentials on your Okta account you’ll be let through to our main activity but the list will be empty - and you should get a 401 error toast popup (unauthorized access).

Add Authorization to Retrofit

You need to get the access token out of the AppAuth library and pass it to Retrofit when API calls are made. Inside of MainActivity.kt add the following function:

fun readAuthState(): AuthState {
    val authPrefs = getSharedPreferences("OktaAppAuthState", Context.MODE_PRIVATE)
    val stateJson = authPrefs.getString("state", "")
    return if (!stateJson!!.isEmpty()) {
        try {
            AuthState.jsonDeserialize(stateJson)
        } catch (exp: org.json.JSONException) {
            Log.e("ERROR",exp.message)
            AuthState()
        }

    } else {
        AuthState()
    }
}

Enter fullscreen mode Exit fullscreen mode

It uses shared preferences to pull out the authorization data stored by Okta.

Now change the header of your adapter so that it accepts an access token as a string.

class MovieAdapter(val context: Context, val token: String?)

Enter fullscreen mode Exit fullscreen mode

Then when you instantiate it in MainActivity pass in the token from the auth state object.

adapter = MovieAdapter(this.baseContext, readAuthState().accessToken)

Enter fullscreen mode Exit fullscreen mode

Now you need to change the get call for movies in MovieApiClient.kt to include an authorization header.

@GET("movies") fun getMovies(@Header("Authorization") token:String): Observable<MovieEmbedded>

Enter fullscreen mode Exit fullscreen mode

Back in the adapter, change refreshMovies() to use this new header - with the new token and a Bearer prefix:

fun refreshMovies() {
    client.getMovies("Bearer $token")
}

Enter fullscreen mode Exit fullscreen mode

You’ll need to do the same changes to the other methods (add, delete, update) in order for those functions to work with your authenticated back-end.

That should be it. Redeploy the app and you should get through to a list as before - this time it is authenticated!

Congratulations! You’ve just made a complete client-server solution, with a robust and compliant REST back-end, and an Android application on the front using the latest technologies, all with central, industry-standard authentication. You can find the example created in this tutorial on GitHub.

Learn More about Android, Java, and Secure Authentication

We’ve written some other cool Spring Boot and Android tutorials, check them out if you’re interested.

Finally, if you want to learn more about Kotlin a great place to look once you’ve worked with it for a bit is the Kotlin idioms page.

If you have any questions, please don’t hesitate to leave a comment below, or ask us on our Okta Developer Forums. Follow us on Twitter @oktadev if you want to see more tutorials like this one!

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Terabox Video Player