At the end of the previous devlog I used AsyncTasks to implement the very basic “Add Task” functionality. Next I set to work getting the other most important feature working: the “flow mode”. The flow mode is where the Pomodorro technique I discussed in the very first devlog is applied. All it shows is a timer and the task you are supposed to be focusing on.

Now like the esteemed ex-convict Martha Stewart, I had already prepared a lot of the logic for the flow activity before this point.

In short, I used three different CountdownTimers that would start one another when they finished. I was not running any of this in a worker thread, or a background service, since the CountdownTimers create their own threads to separate them from the UI thread. In the end this choice became first a curse and then a blessing.

Issue 1: Zombie Tasks

While the logic was really easy to implement, I had envisioned that tasks that were completed in flow would be “burned” and disappear from the task list in the MainActivity. This was easy enough to achieve by deleting the task from the database (and task list) once the timer for that task was completed.

However, when I entered the flow activity, allowed a task to count down, and then returned to the MainActivity, the task list would not reflect that I had completed my task (and remove it from the list). While I thought about using the same startActivityForResult method that I used for adding tasks, this was a little more complicated since the user could quit the flow activity at any time by pressing the back button or quitting the app entirely. Regardless of what the user did, the task list would need to be updated appropriately.

What I ended up doing was learning more about how to work with the activity lifecycle. In essence, when I started my flow activity, my main activity would be paused and then stopped when the activity was no longer visible. This meant that if I wanted to update the task list after the user returned from the flow activity, I needed to implement either onResume or onRestart. Since my flow activity is a fullscreen activity, either method worked for me, however if you were to have a Fragment come into the foreground (with your activity visible in the background) there would be a difference in when onPause and onStop are called.

The code in my MainActivity ended up looking something like this:

override fun onResume() {
    super.onResume()
    reloadTasks()
}

private fun reloadTasks() {
    taskMutableList.clear()
    taskMutableList.addAll(TaskFetchASync(db).execute().get())
    listViewAdapter.notifyDataSetChanged()
}

This means that every time I returned to the MainActivity after moving to a new activity, the TaskList would be updated with the latest state of the database. A cool side effect of this is that should the database be updated in the background (or synchronised with an online one) the new changes will be immediately reflected whenever the MainActivity is resumed or opened for the first time.

Issue 2: Ghost Tasks

While I had fixed the issue of completed tasks not disappearing from the list once completed. I ran into the opposite problem with my tasks starting to disappear from the list when not even finished. When the FlowActivity was started, and the user returned to the MainActivity prematurely, the idea was that the countdown for the task would reset because the entire idea for the task timer was to be an atomic, indivisible thing. Not completing a task means that the user will need to restart it.

However, that was not the case. These “ghost tasks” would start to disappear from the main list despite the FlowActivity being killed. The problem also stemmed from an incomplete understanding of the activity lifecycle and how that relates to using something such as CountdownTimers.

Courtesy of Giphy

Essentially, the timers will continue regardless of the state of the activity and it is up to you to reset them. To fix this I added an OnStop to the FlowActivity that automatically cancels all of the timers. Once again, properly understanding the activity lifecycle proved helpful.

With these bugs out of the way, the app is currently in a state where the core user story loop works: Adding tasks, doing a flow of tasks, and returning to the main task list (prematurely or otherwise). However, that does not mean I am nearly close to finished. This is essentially in “proof of concept” stage, lacking all pomp and polish as well as a few other core features such as editing the task list or editing tasks themselves.

As a final note, for my own records I will include the commit hash/hashes that fixed the bugs described above.

Until next time, study hard, be evil.