Building A Shazam Clone Part 5 (Discover Song Feature) — Building Android Apps Series
In the last post, we walked through test-driven development on Android and wrote a UI test that checks if the discover view is visible to the user.
In this post, we will be working on the discover page still using the test-driven process. Unfortunately, there won’t be a tutorial video for this post. To cover up for this, I tried to simplify the code as much as possible and added lots of ( redundant) comments.
To begin, let’s update the layout for the
DiscoverFragment we created in the previous post. We want to create a design that looks like the photo below.
fragment_discover layout and copy the layout code below into it (There are just 6 Views in the layout and I added some explanatory comments above each).
Note: We made use of ConstraintLayout, if you are not familiar with it you can use this ConstraintLayout CodeLab to get started.
There are a few resources that need to be created to get the layout working properly. First is the circular background for the start and stop ImageButtons. Create a new drawable file called
bg_circle_white.xml in the
res/drawable folder of the project and copy the content below into it.
Second is the required string resources used mostly as the content description for the ImageButtons. Android apps should be usable by everyone, including people with disabilities. This post does a great job at explaining why adding a content description for images is important. Update the
strings.xml file in the
res/values folder with the content below.
Lastly, we need to include the icons for the:
- Start & stop ImageButton (
- Donate ImageButton (
- History ImageButton (
We will be using vector drawables rather than the regular PNG files. I created the vector drawables easily using the Vector Asset Studio. If you are not familiar with the tool, we will just manually copy the generated vector content into the respective files here.
First, create a new drawable file called
ic_mic_gray_24dp.xml in the
res/drawable folder and copy the content below into it.
You can ignore all the weird looking numbers and letters, except you are interested in learning more about vectors. Create another drawable file called
ic_favorite_white_24dp.xml and copy the code below into it.
Lastly, create a drawable file called
ic_history_white_24dp.xml and copy the code below into it.
To support vector drawable on devices running platform versions lower than Android 5.0 (API level 21), we will need to configure the app to use vector support libraries. Add the
vectorDrawables element to the
build.gradle file in the app module like this:
After all the work, you should be able to preview your layout in the design tab (of the layout editor) and it should look very similar to the screenshot above.
With the discover layout completed, let’s start implementing the discover feature. As usual, we will start by writing tests first.
We will create a quick UI test for the
DiscoverFragment that tests the following buttons:
- The start button
- The donate button
- The history button
Create a new Kotlin class file called
DiscoverFragmentTest in the
androidTest folder (use the photo below as a guide).
Copy the code below into the
DiscoverFragmentTest file (I have included comments for each line to explain what it does)
DiscoverFragmentTest above, we made use of some View ids we have not defined yet. So lets quickly create them and their corresponding Activities before trying to run our test. First, we will need to create the following new packages for the different activities:
Next, let’s create the needed Activities one at a time.
1. Create a new empty Activity called
SongDetailActivity in the newly created
activity_song_detail layout that was generated when the Activity was created with the content below.
2. Create another new empty activity called
HistoryActivity in the
Also, update its layout file (
activity_history) with the content below.
3. Finally, create a new empty activity called
DonateActivity in the
Update the Activity generated layout file (activity_donate) with the layout code below.
We have added all the required view ids and can now run the
DiscoverFragmentTest by clicking on the green play button at the top when the test file is open.
It should fail because there is no implementation yet or no code to handle the button presses.
We can now start adding some implementation. Like I said in part 2, we will be using the MVP architecture (You might also want to check MVVM). Our implementation of MVP will be similar to the Google architecture blueprint MVP app.
We will start out by creating two base interfaces that every
Presenter will implement.
Create a new Kotlin interface called
BaseView and copy the code below into it (Every view in this project will implement this interface).
We will do the same for presenters, create a
BasePresenter Kotlin interface and copy the code below into it (Every presenter in the project will implement this interface).
Like I said in part 2, we will define all the interactions between the
view and the
presenter in a single contract interface. Create a new interface called
DiscoverContract in the
discover package and copy the code below into it.
With the discover presenter contract defined, we can go ahead to create a presenter for the discover page. Create a new class called
DiscoverPresenter in the
discover package. For now, the method body will be empty as we will have to write a test first before writing the implementation.
Again we will follow the TDD mantra when working with the
DiscoverPresenter. We will write test first, then the implementation later. Unlike the previous tests we have written so far, the test for the presenter will be in
src/test is for pure unit test that do not involve android framework. You can run tests here without running on a real device or on emulator. On recent versions of Android Studio you should be able to see it like this.
You will need to create a
discover package in the
test folder. Then create a
DiscoverPresenterTest inside of the
discover package. I tried to really simplify the test code and added explanatory comments in between codes. Copy the code below into the created
We are referencing a class and interface we haven’t written yet and if you try to run the test above, you will get a compilation error. We will need to create the
Song class and the
- To create the Song class, first create a new package called data.identify. Then create the Song class inside of this package
2. The SongIdentifyService will be responsible for music identification and will call a callback functions when an error occurs or when a song is successfully identified. For now we will define the interface, in the later parts we will create a concrete service that identifies songs.
Create a new package called data.identify. Inside of the package, create a new Kotlin interface called SongIdentifyService. Copy the code below into it.
We can now run the
As usual, the test should fail because we haven’t added the
DiscoverPresenter implementation yet.
In the next part of this series, we will be adding the
DiscoverFragment implementation. Stay tuned.