Introduction to Android Activities with Kotlin

Learn about one of the most important concepts within Android apps with this introduction to Android activities tutorial, using Kotlin! By Steve Smith.

4.3 (22) · 1 Review

Download materials
Save for later
Share
You are currently viewing page 3 of 4 of this article. Click here to view the first page.

Stopping an Activity

Just as important as starting an activity with all the right methods is properly stopping it.

In TaskDescriptionActivity.kt, add these imports, including one for Kotlin Android Extensions:

import android.app.Activity
import android.content.Intent
import kotlinx.android.synthetic.main.activity_task_description.*

Then add the following to doneClicked(), which is called when the Done button is tapped in this activity:

// 1
val taskDescription = descriptionText.text.toString()

if (!taskDescription.isEmpty()) {
  // 2
  val result = Intent()
  result.putExtra(EXTRA_TASK_DESCRIPTION, taskDescription)
  setResult(Activity.RESULT_OK, result)
} else {
  // 3
  setResult(Activity.RESULT_CANCELED)
}

// 4
finish()

You can see a few things are happening here:

  1. You retrieve the task description from the descriptionText EditText, where Kotlin Android Extensions has again been used to get references to view fields.
  2. You create a result Intent to pass back to MainActivity if the task description retrieved in step one is not empty. Then you bundle the task description with the intent and set the activity result to RESULT_OK, indicating that the user successfully entered a task.
  3. If the user has not entered a task description, you set the activity result to RESULT_CANCELED.
  4. Here you close the activity.

Once you call finish() in step four, the callback onActivityResult() will be called in MainActivity — in turn, you need to add the task to the to-do list.

Add the following method to MainActivity.kt, right after onCreate():

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
  // 1
  if (requestCode == ADD_TASK_REQUEST) {
    // 2
    if (resultCode == Activity.RESULT_OK) {
      // 3
      val task = data?.getStringExtra(TaskDescriptionActivity.EXTRA_TASK_DESCRIPTION)
      task?.let {
        taskList.add(task)
        // 4
        adapter.notifyDataSetChanged()
      }
    }
  }
}

Let’s take this step-by-step:

  1. You check the requestCode to ensure the activity result is indeed for your add task request you started with TaskDescriptionActivity.
  2. You make sure the resultCode is RESULT_OK — the standard activity result for a successful operation.
  3. Here you extract the task description from the result intent and, after a null check with the let function, add it to your list.
  4. Finally, you call notifyDataSetChanged() on your list adapter. In turn, it notifies the ListView about changes in your data model so it can trigger a refresh of its view.

Build and run the project to see it in action. After the app starts, tap ADD A TASK. This will bring up a new screen that lets you enter a task. Now add a description and tap Done. The screen will close and the new task will be on your list:

Registering Broadcast Receivers

Every to-do list needs to have a good grasp on date and time, so a time display should be the next thing you add to your app. Open MainActivity.kt and add the following after the existing property declarations at the top:

private val tickReceiver by lazy { makeBroadcastReceiver() }

Then add a companion object near the top of MainActivity:

companion object {
  private const val LOG_TAG = "MainActivityLog"

  private fun getCurrentTimeStamp(): String {
    val simpleDateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.US)
    val now = Date()
    return simpleDateFormat.format(now)
  }
}

And initialize the tickReceiver by adding the following to the bottom of MainActivity:

private fun makeBroadcastReceiver(): BroadcastReceiver {
  return object : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent?) {
      if (intent?.action == Intent.ACTION_TIME_TICK) {
        dateTimeTextView.text = getCurrentTimeStamp()
      }
    }
  }
}

Here, you create a BroadcastReceiver that sets the date and time on the screen if it receives a time change broadcast from the system. You use getCurrentTimeStamp(), which is a utility method in your activity, to format the current date and time.

Note: If you’re not familiar with BroadcastReceivers, you should refer to the Android Developer documentation.

Note: If you’re not familiar with BroadcastReceivers, you should refer to the Android Developer documentation.

Next add the following methods to MainActivity underneath onCreate()

override fun onResume() {
  // 1
  super.onResume()
  // 2
  dateTimeTextView.text = getCurrentTimeStamp()
  // 3
  registerReceiver(tickReceiver, IntentFilter(Intent.ACTION_TIME_TICK))
}

override fun onPause() {
  // 4
  super.onPause()
  // 5
  try {
    unregisterReceiver(tickReceiver)
  } catch (e: IllegalArgumentException) {
    Log.e(MainActivity.LOG_TAG, "Time tick Receiver not registered", e)
  }
}

Here you do a few things:

  1. You call onResume() on the superclass.
  2. You update the date and time TextView with the current time stamp, because the broadcast receiver is not currently registered.
  3. You then register the broadcast receiver in onResume(). This ensures it will receive the broadcasts for ACTION_TIME_TICK. These are sent every minute after the time changes.
  4. In onPause(), you first call onPause() on the superclass.
  5. You then unregister the broadcast receiver in onPause(), so the activity no longer receives the time change broadcasts while paused. This cuts down unnecessary system overhead.

Build and run the app. Now you should now see the current date and time at the top of the screen. Even if you navigate to the add task screen and come back, the time still gets updated.

Persisting State

Every to-do list is good at remembering what you need to do, except for your friend Forget Me Not. Unfortunately, the app is quite forgetful at the moment. See it for yourself.

Open the app and follow these steps.

  1. Tap ADD A TASK.
  2. Enter “Replace regular with decaf in the breakroom” as the task description and tap Done. You’ll see your new task in the list.
  3. Close the app from the recent apps.
  4. Open the app again.

You can see that it forgot about your evil plans.

Persisting Data Between Launches

Open MainActivity.kt, and add the following properties to the top of the class:

private val PREFS_TASKS = "prefs_tasks"
private val KEY_TASKS_LIST = "tasks_list"

And add the following underneath the rest of your activity lifecycle methods.

override fun onStop() {
  super.onStop()

  // Save all data which you want to persist.
  val savedList = StringBuilder()
  for (task in taskList) {
    savedList.append(task)
    savedList.append(",")
  }

  getSharedPreferences(PREFS_TASKS, Context.MODE_PRIVATE).edit()
      .putString(KEY_TASKS_LIST, savedList.toString()).apply()
}

Here you build a comma separated string with all the task descriptions in your list, and then you save the string to SharedPreferences in the onStop() callback. As mentioned earlier, onStop() is a good place to save data that you want to persist across app uses.

Next add the following to onCreate() below the existing initialization code:

val savedList = getSharedPreferences(PREFS_TASKS, Context.MODE_PRIVATE).getString(KEY_TASKS_LIST, null)
if (savedList != null) {
  val items = savedList.split(",".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
  taskList.addAll(items)
}

Here you read the saved list from the SharedPreferences and initialize taskList by converting the retrieved comma separated string to a typed array.

Note: You used SharedPreferences since you were only saving primitive data types. For more complex data you can use a variety of storage options available on Android.

Note: You used SharedPreferences since you were only saving primitive data types. For more complex data you can use a variety of storage options available on Android.

Now, build and run the app. Add a task, close and reopen the app. Do you see a difference? You are now able to retain tasks in your list!