fm.krui.kruifm.TrackUpdateHandler.java Source code

Java tutorial

Introduction

Here is the source code for fm.krui.kruifm.TrackUpdateHandler.java

Source

/*
 * fm.krui.kruifm.TrackUpdateHandler - TrackUpdateHandler.java
 *
 * (C) 2013 - Tony Andrys
 * http://www.tonyandrys.com
 *
 * Created: 11/14/2013
 *
 * ---
 *
 * This file is part of KRUI.FM.
 *
 * KRUI.FM is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or(at your option) any later version.
 *
 * KRUI.FM is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with KRUI.FM.  If not, see <http://www.gnu.org/licenses/>.
 */

package fm.krui.kruifm;

import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

public class TrackUpdateHandler extends AsyncTask<Void, Void, Integer> {

    private static final String TAG = TrackUpdateHandler.class.getName();

    final public static String ALBUM_ART_FILENAME = "current_track_album_art";

    /* Constants */
    final private int NO_UPDATE = 0;
    final private int UPDATE_REQUESTED = 1;

    /* Class members */
    TrackUpdateListener listener;

    // Song info storage
    String[] trackInfo;
    String artUrl;
    Bitmap albumArt;
    boolean updateAlbumArt;
    String[] currentTrackInfo;
    Context context;

    public TrackUpdateHandler(Context context, TrackUpdateListener l, String[] currentTrackInfo,
            boolean updateAlbumArt) {
        this.context = context;
        this.listener = l;
        this.currentTrackInfo = currentTrackInfo;
        this.updateAlbumArt = updateAlbumArt;
    }

    @Override
    protected void onPreExecute() {
        Log.v(TAG, "Track Update Handler started!");
    }

    @Override
    protected Integer doInBackground(Void... arg0) {

        // Get song info and album art URL
        try {
            trackInfo = getSongInfo();
            Log.v(TAG, "---");
            Log.v(TAG, "Returned track name = " + trackInfo[0]);
            Log.v(TAG, "Returned track artist = " + trackInfo[1]);
            Log.v(TAG, "Returned track album = " + trackInfo[2]);
            Log.v(TAG, "---");
        } catch (RuntimeException e) {
            Log.e(TAG, "RuntimeException thrown when getting song info!");
            e.printStackTrace();
        }

        // Check if the returned info is a new track or the same as the previous track
        if ((!trackInfo[0].equals(currentTrackInfo[0])) || (!trackInfo[1].equals(currentTrackInfo[1]))
                || (!trackInfo[2].equals(currentTrackInfo[2]))) {
            Log.v(TAG, "Song information has changed. UI will be updated.");

            // UI UPDATE HERE
            listener.broadcastMessage(StreamService.BROADCAST_COMMAND_UPDATE_PENDING);

            // If either the song, artist, or album name is different than the current song playing, we have downloaded a new track and we need to update the UI.
            // If album art update is requested, get the location of the album art and download it.
            if (updateAlbumArt) {
                artUrl = getAlbumArtURL(trackInfo[1], trackInfo[2]);
                Log.v(TAG, "Returned album art = " + artUrl);
                Log.v(TAG, "Grabbing album art from " + artUrl);
                albumArt = downloadAlbumArt(artUrl);
            } else {
                Log.v(TAG, "User has elected to not download album art. Skipping...");
            }

        } else {
            // If the information is the same, don't waste resources updating information with itself.
            Log.v(TAG, "Song information is equivalent to the song currently playing. No UI update necessary.");
            return NO_UPDATE;
        }

        return UPDATE_REQUESTED;
    }

    protected void onPostExecute(Integer result) {

        if (result == UPDATE_REQUESTED) {

            // Write the new track information to SharedPrefs
            SharedPreferences prefs = context.getSharedPreferences(StreamService.PREFS_NAME, 0);
            SharedPreferences.Editor prefEditor = prefs.edit();
            prefEditor.putString(StreamService.PREFKEY_TRACK, trackInfo[0]);
            prefEditor.putString(StreamService.PREFKEY_ARTIST, trackInfo[1]);
            prefEditor.putString(StreamService.PREFKEY_ALBUM, trackInfo[2]);
            prefEditor.commit();

            if (updateAlbumArt) {
                writeAlbumArtToFile(albumArt);
            }

            // Execute callback method of listener.
            listener.onTrackUpdate();
        }

    }

    /**
     * Returns album art location for the passed song using the last.fm api and the artist/album name.
     * @arg artist Artist of the song passed as a string.
     * @arg albumName Name of the album/compilation the song appears on as a string.
     * @return Album Art URL string. 
     */
    protected String getAlbumArtURL(String artist, String albumName) {
        String key = context.getString(R.string.lastfm_api_key);
        Log.v(TAG, "Web encoded artist: " + Utils.webEncodeString(artist));
        Log.v(TAG, "Web encoded album: " + Utils.webEncodeString(albumName));
        String apiQuery = "http://ws.audioscrobbler.com/2.0/?method=album.getinfo&api_key=" + key + "&artist="
                + Utils.webEncodeString(artist) + "&album=" + Utils.webEncodeString(albumName)
                + "&autocorrect=1&format=json";
        JSONObject obj = JSONFunctions.getJSONObjectFromURL(apiQuery);
        try {
            // TODO: Give user ability to stop downloading album art, or download at lower resolution.
            JSONObject album = obj.getJSONObject("album");
            JSONArray imageArray = album.getJSONArray("image");
            // Art is distributed in small, medium, large, extralarge, and mega
            JSONObject megaObj = imageArray.getJSONObject(4);
            String artUrl = megaObj.getString("#text");
            return artUrl;
        } catch (JSONException e) {
            Log.e(TAG, "Failed to retrive album art from JSON (Params: " + artist + ", " + albumName + ": ");
            e.printStackTrace();
            return "*NO_ART*";
        }
    }

    /**
     * Gets the last played song from the staff.krui.fm playlist via its api.
     * @return Artist and album name as a string array.
     * Array format: { track name, artist name, album name }
     */
    protected String[] getSongInfo() {
        String apiQuery = "http://staff.krui.fm/api/playlist/main/items.json?limit=1";
        JSONArray arr = JSONFunctions.getJSONArrayFromURL(apiQuery);
        try {
            JSONArray nestedArr = arr.getJSONArray(0);
            JSONObject obj = nestedArr.getJSONObject(0);
            JSONObject songObj = obj.getJSONObject("song");
            String[] info = { songObj.getString("name"), songObj.getString("artist"), songObj.getString("album") };
            return info;
        } catch (JSONException e) {
            Log.e(TAG, "Failed to get song info from JSON: ");
            e.printStackTrace();
            String[] empty = {};
            return empty;
        }

    }

    /**
     * Downloads album art from passed URL using the last.fm api.
     * @param url Web safe location of image as URL.
     * @return Bitmap of the image requested
     */
    protected Bitmap downloadAlbumArt(String url) {

        Bitmap bitmap;

        // Check if an album art URL was returned. If there's no location, there's no point in wasting resources trying to download nothing.
        if (artUrl.equals("*NO_ART*") == false) {

            // Download the image from URL
            try {
                HttpURLConnection connection;
                connection = (HttpURLConnection) new URL(url).openConnection();
                connection.connect();
                InputStream input = connection.getInputStream();
                bitmap = BitmapFactory.decodeStream(input);
                return bitmap;

            } catch (MalformedURLException e) {
                Log.e(TAG, "Malformed URL detected when downloading album art: ");
                e.printStackTrace();
            } catch (IOException e) {
                Log.e(TAG, "Failed to process album art: ");
                e.printStackTrace();
            }
        }

        // If the download fails or image doesn't exist, display the default KRUI background image.
        bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.krui_background_logo);
        return bitmap;
    }

    /**
     * Writes downloaded album art to file storage
     * @param bitmap Album Art image as Bitmap
     */
    private void writeAlbumArtToFile(Bitmap bitmap) {
        File file = new File(context.getFilesDir(), ALBUM_ART_FILENAME);
        try {
            FileOutputStream out = new FileOutputStream(file);
            bitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
            Log.v(TAG, "Album art successfully written to disk!");
            out.close();
        } catch (FileNotFoundException e) {
            Log.e(TAG, "WARNING: Album art could not be saved to disk. FileNotFoundException.");
            e.printStackTrace();
        } catch (IOException e) {
            Log.e(TAG, "WARNING: Album art could not be saved to disk. IOException.");
            e.printStackTrace();
        }
    }

}