Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Mobile / Android

Getting Current Playing Song with BroadcastReceiver

19 May 2015CPOL3 min read 21.8K  
In this post I'll show how to get the lyrics of the current playing song

Introduction

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 using BroadcastReceiver.

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

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.

XML
<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, all 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.

XML
<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

Getting data from API

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 a class called 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:

Java
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;
    }
}

 

Using BroadCastReceiver to get the current playing song

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

In order to get the current playing song, we have to use a BroadCastReceiver.

A BroadCastReceiver is, basically, a component that responds to system events, for example when the screen has turned off, the battery is low or like in this case the play state of a music is changed.

There are a lot of system broadcast announcements and you can go deeper with it by reading the documentation at http://developer.android.com/reference/android/content/BroadcastReceiver.html

In the method onCreate are defined the actions that we want to receive from broadcast; we have to register the broadcast intent passing as parameter the receiver just created and the intent filter with the actions.

Then we can check, using AudioManager, if there is any music playing. If nothing is playing we show to the use that nothing is playing. 

I have created a BroadcastReceiver called mReceiver and in the onReceive method we can get the info sent to us by the system. We can now create the URI to get the data from the API.

The VagalumeAsyncTask do all the magic by sending the request to the API, parsing the response as JSONObject and getting the text of the lyrics.

Java
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();
            }
        }
    }
}

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

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

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

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.

 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)