Android: Lyrics by vagalume API

Another API I found searching on internet that surprised me with good documentation and usability is http://api.vagalume.com.br

This API offers info about songs and artists; we can search song by name or phrase.

In this post I’ll show how to get the lyrics of the current playing song

You can get the source code at https://github.com/rpresb/android-letradamusica.

Android Studio Setup

Dowload the package with the installation files at https://developer.android.com/sdk/index.html

Follow the installation steps:

Captura de Tela 2015-04-05 às 11.27.52

Choose Standard at the Install Type step.

Captura de Tela 2015-04-05 às 11.28.01

The next step is very slow, click accept for all License Agreements; you should have a coffee until it finishes.

Captura de Tela 2015-04-05 às 11.28.16

When the installation finishes, start the Android Studio and you will see the following screen:

Captura de Tela 2015-04-05 às 19.29.40

Create a New Project

Choose the option Start a new Android Studio project, in this screen you will type the application name, company domain and the location that your project will be saved.

Captura de Tela 2015-04-06 às 18.50.33

In the next screen you must choose the minimum SDK.

Lower API levels is better so you target more devices. I chose the API 15, which is for android 4.0.3 and higher

Captura de Tela 2015-04-05 às 19.31.33

The next screen is for choose the template for the main activity.

I choose Blank Activity because it is a very basic app.

Captura de Tela 2015-04-05 às 19.31.59

For the last step before the coding we have to choose the name of the main Activity also the name for its layout, a title and the name for the Menu Resource.

I only changed the Title.

Captura de Tela 2015-04-05 às 19.34.04

The project is ready to start.

Preparing the Interface

I don’t want to create a very complex interface, because I need only one area to show the lyrics of the song.

In the /res/layout/activity_main.xml file, we have to create a ScrollView in order to enable scroll for long lyrics.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:paddingBottom="@dimen/activity_vertical_margin"
 android:paddingLeft="@dimen/activity_horizontal_margin"
 android:paddingRight="@dimen/activity_horizontal_margin"
 android:paddingTop="@dimen/activity_vertical_margin"
 android:scrollbars="vertical"
 tools:context=".MainActivity">

 <ScrollView
 android:layout_width="fill_parent"
 android:layout_height="fill_parent">

  <TextView
   android:id="@+id/status"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:text="@string/carregando" />
 </ScrollView>

</RelativeLayout>

There is, in the /res/values/strings.xml file, the strings used in the app. This file exists to easily create a translated version of the app without changing any code, but I’ll not cover this scenario in this post.

<resources>
 <string name="app_name">Letra da Música</string>

 <string name="carregando">Carregando...</string>
 <string name="action_settings">Settings</string>
</resources>

The interface is very simple and there is nothing more to do.

An important thing to pay attention is that we have a TextView and there is the attribute android:id=”@+id/status” to allow it be accessed in the code.

Coding the App

Because this app access an API over the internet, we have to send a get to the URL and the result is a JSON that we have to manipulate to use in the app.

I have created the class JSONParser, with some code that I found on the internet, to get the JSON from an URL and return one object of JSONObject type.

Below is the JSONParser.java file:

package presba.com.br.letradamusica;

import android.util.Log;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.security.KeyStore;

public class JSONParser {
    static InputStream is = null;
    static JSONObject jObj = null;
    static String json = "";
    HttpClient httpClient = createHttpClient();

    public JSONParser() {

    }

    public static HttpClient createHttpClient() {
        try {
            KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
            trustStore.load(null, null);

            HttpParams params = new BasicHttpParams();
            HttpConnectionParams.setConnectionTimeout(params, 15000);
            HttpConnectionParams.setSoTimeout(params, 5000);

            SchemeRegistry registry = new SchemeRegistry();
            registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));

            ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);

            return new DefaultHttpClient(ccm, params);
        } catch (Exception e) {
            return new DefaultHttpClient();
        }
    }

    public JSONObject getJSONFromUrl(String url) {
        try {
            HttpGet httpGet = new HttpGet(url);
            HttpResponse httpResponse = httpClient.execute(httpGet);
            HttpEntity httpEntity = httpResponse.getEntity();
            is = httpEntity.getContent();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(
                    is, "iso-8859-1"), 8);
            StringBuilder sb = new StringBuilder();
            String line = null;
            while ((line = reader.readLine()) != null) {
                sb.append(line + "n");
            }
            is.close();
            json = sb.toString();
        } catch (Exception e) {
            Log.e("Buffer Error", "Error converting result " + e.toString());
        }

        try {
            jObj = new JSONObject(json);
        } catch (JSONException e) {
            Log.e("JSON Parser", "Error parsing data " + e.toString());
        }

        return jObj;
    }
}

In the MainActivity.java file, which is responsible for controlling the app life cycle, it is where our main code is.

package presba.com.br.letradamusica;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;

import org.json.JSONException;
import org.json.JSONObject;

public class MainActivity extends ActionBarActivity {
    TextView status = null;
    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            String cmd = intent.getStringExtra("command");

            Log.d("Music", cmd + " : " + action);

            String artist = intent.getStringExtra("artist");
            String album = intent.getStringExtra("album");
            String track = intent.getStringExtra("track");
            boolean playing = intent.getBooleanExtra("playing", false);

            Log.d("Music", artist + " : " + album + " : " + track);

            if (!playing) {
                status.setText("Nenhuma música tocando");
            } else {
                status.setText(artist + "\n" + album + "\n" + track);

                String uri = Uri.parse("http://api.vagalume.com.br/search.php")
                        .buildUpon()
                        .appendQueryParameter("mus", track)
                        .appendQueryParameter("art", artist)
                        .build().toString();

                if (uri != null) {
                    new VagalumeAsyncTask().execute(uri);
                }
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        IntentFilter iF = new IntentFilter();
        iF.addAction("com.android.music.musicservicecommand");
        iF.addAction("com.android.music.metachanged");
        iF.addAction("com.android.music.playstatechanged");
        iF.addAction("com.android.music.updateprogress");

        this.status = (TextView) this.findViewById(R.id.status);

        registerReceiver(mReceiver, iF);

        AudioManager manager = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
        if (!manager.isMusicActive()) {
            status.setText("Nenhuma música tocando");
        }

    }

    @Override
    protected void onPause() {
        super.onPause();
        unregisterReceiver(mReceiver);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    private class VagalumeAsyncTask extends AsyncTask<String, Void, JSONObject> {
        @Override
        protected JSONObject doInBackground(String... params) {
            JSONParser jParser = new JSONParser();
            return jParser.getJSONFromUrl(params[0]);
        }

        @Override
        protected void onPostExecute(JSONObject json) {
            super.onPostExecute(json);

            try {
                JSONObject mus = (JSONObject) json.getJSONArray("mus").get(0);
                status.setText(mus.getString("text"));
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
    }
}

Running the App

When you click on Execute (Run > Run…) in the Android Studio, you will see the below screen asking to choose a device to run the application.

Captura de Tela 2015-04-06 às 20.43.49

In the case you have an android device you have to connect to your computer and enable the developer mode on device.

When it’s done, your device will appear in the list and you can choose it to run the app.

In the case you don’t have an android device, you can choose the Launch emulator option and test your code on the android emulator.

Captura de Tela 2015-04-06 às 20.47.00

It is recommended to perform tests on real devices, although the emulator help when we don’t have a real device.

After the app is installed at the device or emulator, you will see the screen below:

tela1

Now it is up to you, play your favorite song and enjoy the lyrics.

tela2

When you return to the app the lyrics is there for you to sing aloud.

tela3

 

Conclusion

In this post I have made a very small app and I used the BroadcastReceiver to get the data of the current playing song and with this info I received the lyrics from the API.

The app calls the API asynchronously with the class that extends AsyncTask and does it all behind the scenes, without blocking the app.

If you have any issue, comment or suggestion, leave your message below.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Blog at WordPress.com.

Up ↑

%d bloggers like this: