Sapan Diwakar

Software developer

Follow me on Twitter Check out my code on GitHub View some of my designs on Dribbble Take a look at my Linked In profile

Creating a global toolbar (mini music player) on Android

Although this post is self-contained, you might be interested in reading the complete series on creating a music player for Android.

First let's start with a description of what we want to achieve with this mini player.

  • It should be simple enough to be embedded in all activities when the music is playing.
  • Auto-hide when the music is stopped
  • Allow swipe to dismiss to stop music
  • Adapt to changes in music states

In order to support our mini-player to be embedded in other activities, let's model this as a Fragment. Fragments are perfect for this use case as opposed to custom views since we want our mini-player to auto hide when the music isn't playing but still listen to events so it can appear when the music starts.

Now that the fragment vs view argument is sorted out, let's figure out the best kind of Layout to use for the player. We could use a FrameLayout/RelativeLayout, but then we need to handle the swiping behavior manually. Coordinator Layout (quoted as super-powered FrameLayout in the docs) comes with prebuilt behaviours that may be used to implement a variety of interactions and additional layout modifications ranging from sliding drawers and panels to swipe-dismissable elements and buttons that stick to other elements as they move and animate.

Here's how we set up Swipe Behavior on the layout and listen to "dismiss" events.

val swipeDismissBehaviour = SwipeDismissBehavior<CardView>()  
swipeDismissBehaviour.setSwipeDirection(SwipeDismissBehavior.SWIPE_DIRECTION_START_TO_END)  
(musicToolbar.layoutParams as CoordinatorLayout.LayoutParams).behavior = swipeDismissBehaviour
RxSwipeDismissBehavior.dismisses(musicToolbar).subscribeOn(AndroidSchedulers.mainThread()).observeOn(AndroidSchedulers.mainThread()).subscribe {  
  val service = mMusicService ?: [email protected]
  service.stop()
  hideFragment()
}

The next task in our list is changing the visibility based on the music player state. If you have been following my previous tutorials, you already know that the music player is set up as a Service. We broadcast state changes from the Service in form of Otto events. There are two events being broadcasted from the service: a state changed event to signal play/pause/stop states, and a track changed event. We can listen to these events in the Fragment like so:

@com.squareup.otto.Subscribe
fun onMusicServiceStateChanged(event: MusicService.MusicServiceStateChangedEvent) {  
  Timber.d("Music Service state changed %s", event.state)
  if (event.state == MusicService.MusicServiceState.STOPPED) {
    hideFragment()
  } else if (mFragmentHidden) {
    showFragment()
  } else {
    playButton.isSelected = mMusicService?.isPlaying ?: true
  }
}

@com.squareup.otto.Subscribe
fun onCurrentTrackChanged(event: MusicService.MusicServiceTrackChangedEvent) {  
  if (event.track == null) {
    hideFragment()
    return
  }
  setupToolbar()
}

In order to control the music using controls from this fragment, we need to bind to the service. This can be done with activity.bindService(Intent(activity, MusicService::class.java), this, Context.BIND_AUTO_CREATE) and implementing the ServiceConnection methods in our class.

override fun onServiceConnected(name: ComponentName?, service: IBinder?) {  
  mMusicService = (service as MusicService.MusicServiceBinder).service
  setupToolbar()
}

override fun onServiceDisconnected(name: ComponentName?) {  
  mMusicService = null
}

This wraps up the complete logic for the mini player. I leave it to you to set up the user interface and bind the values to different views in your app. The final task in this tutorial is to embed this fragment in other activities. Since everything is handled internally in the Fragment, the embedding couldn't be simpler. All we need is to place the fragment in the Activity's layout at the desired location. It will take care of showing/hiding itself based on music player state and switch tracks when the song changes.

<include layout="@layout/layout_music_toolbar"/>  

This is the fifth tutorial in the series Building a Music Player App for Android. Stay tuned for more tutorials in this series!*