The decalogue of a pro media app
In this short article, I’d like to reveal the 10 ancient commandments that will help you create a pro media application. I’ll present things that are important to implement and those that are nice to have. Let’s just point them out.
1. You shall support AudioFocus.
This is a thing that some media apps do not handle, and when not handled, it may spoil UX of other apps. Basically, audio focus helps to prevent two or more apps from playing audio in the same time. For instance, it gives a developer a possibility to pause playback when a call is coming or to lower volume when a text message arrives. It would be really undesirable for a user to hear the audio from both, your media app and Youtube app when switching from one to another or experience a situation when during a call, music from your app is still playing. Please remember to always handle it. Luckily for you, if you use ExoPlayer library, it handles audio focus out of the box starting from version 2.9.0.
More info here ->
https://developer.android.com/guide/topics/media-apps/audio-focus
https://medium.com/google-exoplayer/easy-audio-focus-with-exoplayer-a2dcbbe4640e
2. Don’t be noisy.
Have you ever made a joke on your friend unplugging his headphones jack from his smartphone while he was listening to music during a history class? Yeah, it made music play loud. It happened because the app didn’t handle “Becoming noisy” action broadcast by system in such situations. Basically, what you should do, is to register a broadcast receiver with the following IntentFilter:
IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY)
.
Once the system notifies you about becoming noisy action, you should just pause the playback.
More info here -> https://developer.android.com/guide/topics/media-apps/volume-and-earphones
3. Honour Android Media Session with all the information you’ve got.
Android system holds information about all media sessions. Each media session has its own session token. Everyone owning it, is able to interact with the session. For instance, your smartwatch using session token can control the music playback in your phone. Media session can hold information about playback state and media metadata. Thanks to that, it is possible to:
- control the playback through so called media buttons, like the ones from the headset
- display artwork on lock screen (more about it later)
- display things like music title, subtitle and duration in your car or smartwatch display
- make your media notification look awesome (more about it later)
- and more.
A class responsible for media session creation is MediaSessionCompat
. The instance of it should be created when you start your media service. This class has such methods as setMetadata(MediaMetadataCompat)
and setPlaybackState(PlaybackStateCompat)
which you should be using to give Android system all information about your media.
In order to receive media buttons events in your app, like pressing a play button from your headset, you have to register a MediaButtonReceiver
. Then, you should pass an Intent from a receiver to your media session like this: MediaButtonReceiver.handleIntent(mediaSession, intent)
.
That way, all the media buttons actions will be reflected in your media session callback (MediaSessionCompat.Callback
). For instance, pressing a play button on your headset will call either onPlay()
or onPause()
method in the callback, depending on playback state you have passed to session using setPlaybackState(PlaybackStateCompat)
method.
4. Don’t be lazy and display Media notification.
If your app is going to be playing media in background, it is super useful for a user to display a media notification. Having it, a user is able to control the playback, know what is currently being played, or get back to the app through notification. If you’ve ever seen Spotify media notification, you could think it looks so cool. Saying cool, I mean things like notification background color created from provided media image dominant color, and text colors adjusted to this color, so they look nice and clear on any background. Surprisingly or not, it is just a native Android notification with media style. This is how to create a basic media style of notification.
NotificationCompat.MediaStyle()
.setMediaSession(mediaSession.sessionToken)
.setShowActionsInCompactView(0, 1, 2)
Just pass it to NotificationCompat.Builder().setStyle(mediaStyle)
method. A very important thing to remember is to provide the media session token and, as mentioned before, ensure that media session has all information about our media and playback.
5. Remember about a lock screen.
As an addition to media notification, it is worth implementing lock screen controls. It will give a user an opportunity to control the playback without unlocking the phone. In order to display a media notification on the lock screen, you just need to set public visibility on it, just like this:
NotificationCompat.Builder().setVisibility(NotificationCompar.VISIBILITY_PUBLIC
.
There is one more fancy thing about a lock screen. We can set a full screen background on it. You just have to pass a bitmap to media session metadata.
metadata.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, bitmap)
Just keep in mind that maximum allowed size of the bitmap is 320dp, thus, if the image is fetched from the network, it may be worth requesting a smaller size image. If each of your songs has a different image and you’d like images to load fast on the lock screen, it may be worth considering loading smaller size blurred image.
6. Don’t let your Service die. Use foreground service.
In order to play media in background, you have to start a Service and embed a player within. However, since Android Oreo, we have to remember to start a Service as a foreground service. Otherwise, the system will kill our Service very soon after we put the app in background. When starting a service as foreground, we have to pass a Notification to startForeground(Notification)
method. That is a requirement, however, it is not a problem, as you’d probably want to display a media notification.
More info here -> https://developer.android.com/about/versions/oreo/background
7. Remember about the WakeLock and WiFiLock.
If your app plays media in background and a user turns off the screen, a device may go to sleep while your service is running. Android system tries to save battery and may shut down CPU, WiFi and other resources. You may want to prevent the system from doing this. To keep CPU awake, you should acquire a PowerManager.WakeLock
. However, if you’d like WiFi to keep running, use the WifiManager.WifiLock
.
8. You shall shine and keep the screen on.
If your app is going to play videos, you don’t want the screen to turn off while the video is playing. It may be very annoying to a user. To keep screen on, you have two options, either add a flag to activity window
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
or add an attribute to your root layout
android:keepScreenOn=”true”
9. Remember about Audio Tracks limitation.
The AudioTrack
class manages and plays a single audio resource for Java applications. There is a strict system limitation that allows for only 32 instances of this class in the whole system. A single player instance (ExoPlayer
or MediaPlayer
) usually holds one instance of active AudioTrack so it shouldn’t be a big deal. However, if you use many players or, just forget to release player resources, you, or any other app in the system, might get into trouble. So please always release your player resources.
10. You shall display your media on a big screen with Chromecast.
Chromecast
is a device from Google that enables users with a mobile device or personal computer to play Internet-streamed multimedia content on a television or home audio system through mobile and web apps that support the Google Cast technology. It is quite a popular device so it may be worth considering support Chromecast in your multimedia app. To implement Chromecast, you can use Cast SDK from Google https://developers.google.com/cast/. If you use ExoPlayer, it has a CastExtension that makes things easier. However, this extension is a little bit buggy so I don’t fully recommend it, at least in the version 2.9.2 of ExoPlayer. As a side note, I’ve heard from a dev working on ExoPlayer, that they are building something completely new that will resolve all known issues.
Summary
That’s it. I hope you’re gonna be a good man and you will obey all the commandments. I’ve tried to be very concise and didn’t want to put a lot of code, however, most of the points mentioned, could be detailed in a separate article. I hope this post will help you create a pro media application or, at least, has left some useful knowledge in the back of your head :)
If you don’t know yet what superpower is hidden behind Kotlin delegates, check out my previous article https://medium.com/@madamczewski/kotlin-delegates-in-android-real-life-examples-part-1-17c0e8f0869