Categories
android architecture blog kotlin

Building a podcast app series: 2. Player state

After the view is set up, it’s very important to have a model that will drive that UI, something that we can observe and render our UI according to its state. It would be nice to react to model changes with some kind of a listener and it would be great if we don’t have to manually subscribe/unsubscribe to this model. Luckily, Android has a ViewModel class that is built for this exact purpose.

ViewModel

View modes are a relatively new invention. Before ViewModels came along, and the whole AAC (android architecture components), representing ViewModels in a clean and safe way was particularly tricky. Android activity or a fragment can be in multiple states and properly connecting/disconnecting a particular activity or a fragment is a dance around onCreate / onDestrory, onResume / onPause, and other lifecycle methods. Since our in-app player view is visible on all screens, wee need a globally accessible state holder for that player. Luckily, a ViewModel is all we need to safely share the player state across different fragments. Since we are using a single activity pattern, ViewModels are automatically tied to that activity and can be shared across fragments! This means that we can easily tell other views what is the state of our in-app player by just sharing a ViewModel across multiple fragments! Oh, by the way, ViewModels survive configuration change, so that is also being taken care of.

So, how do we create a new view model? We can simply write in any fragment

private val playerViewModel by sharedViewModel<PlayerViewModel>()

The basic idea here is that ViewModels do not care who is using its LiveData, it can be a single fragment or bunch of them, it does not matter. ViewModels should not reference any Fragment or Activity, they simply expose data for Views to observe!

If you are curious about the code for the PlayerViewModel check out this link.

LiveData

Once we have a shared state holder for our player, we need a safe way to update our views. That is where LiveData comes in, it is a safe container for any kind of data. And the best part about LiveData is that it automatically knows if our fragment or activity is active and can receive updates, so we don’t have to handle that logic ourselves! This means no more null pointer exceptions of illegal state exceptions. We are free to post new data to LiveData and be sure that nothing will crash as a result of that.

How do we create and update live data?

private val _currentlyPlaying = MutableLiveData<Episode>()
currentlyPlaying.value = currentEpisode

Repository pattern

The last piece of the state puzzle is to identify where does our data come from? In a simple word: from a Repository of Podcasts! Repository pattern is such a simple pattern and very useful. We can make our Repository do a bunch of interesting things. We can cache data to the local DB, we can mock our data, we can fetch something from the internet, everything related to data and its retrieval is hidden behind our Repository class.

How does a simple repository look like?

class PodcastRepository {
    fun getPodcasts(): List<Podcast> =
        listOf(Podcast("The Joe Rogan experience"))
}

Basic setup for every screen

In conclusion, every screen in our app will reference ViewModels and will subscribe to LiveDatas. ViewModels will fetch data from Repositories and update LiveDatas and we have a reactive setup for each screen in our app. Error handling is done in the same manner, we just have a LiveData of error messages where we can push errors and observe them inside Fragments.

Browse the full code on this link. Stay tuned for the next article in this series where we explore exoPlyaer, brains behind any podcast app.

Categories
android blog kotlin

Building a podcast app series: 1. mini in-app player

I am currently working on a project to rewrite an existing podcast app that was originally built with xamarin into a fully native Android app. The most important feature of any podcast app is the player, and specifically, the in-app player that has to:

  1. be visible on all screens
  2. have collapsed and expanded view

Let’s clarify the second point, the in-app player should have two modes, collapsed mode where the player’s view is small and should sit below the main content. The second mode is expanded mode, where the player’s view should occupy the whole screen. Ideally, there should be an animation when the player is transitioning between those two states. Let’s talk about solving problem number 1.

Navigation library

If we use the navigation library and single activity pattern from AAC (android architecture components), we can easily make our in-app player visible on all screens by restricting the navigation host fragment to be above the in-app player’s view. Since the navigation framework loads / unloads all views inside the navigation host fragment, we can make all views in our app span just up to the in-app player. If the player is hidden, all views automatically span over it, otherwise, they will span to just above the player.

A nice side effect of using the single activity pattern is the player itself will be configured from one place, main activity, and in combination with a view model, we can implement the player without violating the DRY principle. Now let’s talk about implementing the second feature.

Layout behavior

Ever since the coordinator layout came out, Android became a lot more flexible in terms of animations and interaction between views. In this specific use case all I had to do was to write a normal constraint layout and add one line to it:

<androidx.constraintlayout.widget.ConstraintLayout
....
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">

That was enough to have a view that can sit on the bottom of the screen, be expanded and animate those state changes out of the box. Pretty awesome if you ask me. So, how does the final layout look like?

<CoordinatorLayout>
   <MainContent/>
   <InAppPlayer/>
</CoordinatorLayout>

Of course, nothing is this simple, is it? :). But, what if our app has tabs and a BottomNavigationView? Our in-app player should, of course, sit above the BottomNavigationView but below the main content. One solution to this edge case is to position the main content above the bottom tabs’ view using constraint layout.

DRY principle and ViewModel

Since the player exists across multiple screens (fragments), we need a view model to group together all the player features in one place and just reuse the view model across all views. What better way to implement this than to use AAC ViewModel class. We can easily share this view model across fragments and have a player UI in sync at all times. PlayerViewModel can get data from a PodcastRepository class which will make network calls or read from the local database.

Please refer to this GitHub link for the code.

Stay tuned for the next article in this series!

Categories
productivity Uncategorized

2019 in a review and plan for 2020

At the beginning of this year, I started by writing a blog post about my goals for 2019. Looking at it now I realized how ambitious I was and how much time it would require to do all the things from the list. Something else I noticed is how little I changed my daily system to achieve those goals. I can confidently say that setting goals is not an optimal way of achieving something. That is why I am trying a different tactic this year. I will work on setting a new daily system instead of focusing on my goals for 2020. Of course, I will set some goals for 2020, but those goals will be of lesser importance than my daily workflow.

Goals vs Systems

So what is the big deal with systems? As with everything, boiling a thing down to its core elements can reveal quite a bit about the original thing. Setting goals is problematic in two fundamental ways:

  1. Goals are future-oriented and they are missing the crucial fast feedback loop
  2. Goals tell little on how to achieve them

So, to fix those two main issues the focus should be on systems instead of goals. This is one of those things that remains unnoticed until someone points it out. Everyone has a system that they follow in their daily life. The question is, is that the daily system set up in such a way that can help you achieve your goals? Most probably not, because goals require doing new things and that in turn requires acquiring new habits. Allocating time into a daily schedule is a crucial step in implementing the right system.

So, systems provide a solution to two major flows of setting goals outlined above. Firstly, systems are something that can be exercised daily, and the feedback loop is immediate. It’s as simple as asking a question, did I do 30 minutes of deep work on my new project today or not? If not, then I did not make any progress! The actual goal I am trying to achieve is hanging in the air. This feedback loop occurs every day and it’s a simple reminder that the actual goal I am perusing is not getting done any time soon. Another benefit of systems is that when we define a system to follow, we inherently have to come up with a plan on what behaviors will help in achieving that particular goal. For example, let’s say my goal in 2020 is to have a 10k mailing list. The goal itself does not mean anything unless we create a system around it to accomplish that. One such system is to set a time to write for 30 minutes, daily. That, in turn, requires changes to daily workflow. One could allocate that time right after work (and in doing that utilize a technique called habit stacking). Every day that those 30 minutes are skipped will be a reminder that the goal of 10k subscribers was just a wish on a piece of paper.

So, instead of plans for 2020, I will focus on systems and how to create a better system for myself. Currently, my system looks like this

  1. Wake up early, do some reading get inspired for the day
  2. Work 9 – 5
  3. Write a couple of paragraphs for my blog from 5 – 5:30
  4. Go home and rest

That’s it. That is my aim for 2020, a simple system to follow daily!