com.mp3tunes.android.player.content.LockerDb.java Source code

Java tutorial

Introduction

Here is the source code for com.mp3tunes.android.player.content.LockerDb.java

Source

/***************************************************************************
 *   Copyright (C) 2009  Casey Link <unnamedrambler@gmail.com>             *
 *   Copyright (C) 2007-2008 sibyl project http://code.google.com/p/sibyl/ *
 *                                                                         *
 *   This program 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.                                   *
 *                                                                         *
 *   This program 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 this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
 ***************************************************************************/

package com.mp3tunes.android.player.content;

import java.io.IOException;
import java.util.List;

import org.json.JSONException;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDiskIOException;
import android.database.sqlite.SQLiteException;
import android.util.Log;
import com.binaryelysium.mp3tunes.api.Album;
import com.binaryelysium.mp3tunes.api.Artist;
import com.binaryelysium.mp3tunes.api.ConcreteTrack;
import com.binaryelysium.mp3tunes.api.Id;
import com.binaryelysium.mp3tunes.api.InvalidSessionException;
import com.binaryelysium.mp3tunes.api.Locker;
import com.binaryelysium.mp3tunes.api.LockerException;
import com.binaryelysium.mp3tunes.api.LockerId;
import com.binaryelysium.mp3tunes.api.Playlist;
import com.binaryelysium.mp3tunes.api.Track;
import com.binaryelysium.mp3tunes.api.Session.LoginException;
import com.binaryelysium.mp3tunes.api.results.SearchResult;
import com.mp3tunes.android.player.IdParcel;
import com.mp3tunes.android.player.Music;
import com.mp3tunes.android.player.content.LockerCache.Progress;
import com.mp3tunes.android.player.content.Queries.MakeQueryException;

/**
 * This class is essentially a wrapper for storing MP3tunes locker data in an
 * sqlite databse. It acts as a local cache of the metadata in a user's locker.
 * 
 * It is also used to handle the current playlist.
 * 
 */
public class LockerDb {

    Context mContext;
    Locker mLocker;
    SQLiteDatabase mDb;
    Queries mQueries;
    LockerCache mCache;

    public LockerDb(Context context) {
        mLocker = new Locker();
        // Open the database
        mDb = (new LockerDbHelper(context, null)).getWritableDatabase();
        if (mDb == null) {
            throw new SQLiteDiskIOException("Error creating database");
        }
        mDb.setLockingEnabled(true);

        mQueries = new Queries(this);

        mContext = context;
        mCache = LockerCache.loadCache(this);
    }

    public void close() {
        if (mDb != null) {
            mCache.saveCache(this);
            mDb.close();
        }
    }

    public void clearDB() {
        mCache.clearCache();
        mDb.delete(DbTables.TRACK, null, null);
        mDb.delete(DbTables.ALBUM, null, null);
        mDb.delete(DbTables.ARTIST, null, null);
        mDb.delete(DbTables.PLAYLIST, null, null);
        mDb.delete(DbTables.PLAYLIST_TRACKS, null, null);
        mDb.delete(DbTables.TOKEN, null, null);
        mDb.delete(DbTables.CACHE, null, null);
    }

    //Basic Getters returning api data structures

    public Track getTrack(String name) {
        String[] args = new String[] { name };
        String where = DbKeys.TITLE + "=?";
        return cursorToTrack(mDb.query(DbTables.TRACK, Music.TRACK, where, args, null, null, null));
    }

    public Track getTrack(LockerId id) {
        return cursorToTrack(
                mDb.query(DbTables.TRACK, Music.TRACK, "_id=" + id.asString(), null, null, null, null));
    }

    public Artist getArtistById(Id id) {
        return cursorToArtist(
                mDb.query(DbTables.ARTIST, Music.ARTIST, DbKeys.ID + "=" + id.asString(), null, null, null, null));
    }

    public Artist getArtistByName(String artistName) {
        return cursorToArtist(mDb.query(DbTables.ARTIST, Music.ARTIST,
                DbKeys.ARTIST_NAME + "=\"" + artistName + "\"", null, null, null, null));
    }

    public Album getAlbum(LockerId id) {
        return cursorToAlbum(
                mDb.query(DbTables.ALBUM, Music.ALBUM, DbKeys.ID + "=" + id.asInt(), null, null, null, null));
    };

    public Album getAlbum(String name) {
        return cursorToAlbum(mDb.query(DbTables.ALBUM, Music.ALBUM, DbKeys.ALBUM_NAME + "=\"" + name + "\"", null,
                null, null, null));
    }

    //Getters returning cursors

    public Cursor getRadioData(String[] from) throws IOException, LockerException {
        return mDb.query(DbTables.PLAYLIST, from, DbKeys.ID + " like 'PLAYMIX_GENRE_D%'", null, null, null,
                DbKeys.PLAYLIST_ORDER);
    }

    public Cursor getPlaylistData(String[] from) throws IOException, LockerException {
        return mDb.query(DbTables.PLAYLIST, from, DbKeys.ID + " not like 'PLAYMIX_GENRE_D%'", null, null, null,
                DbKeys.PLAYLIST_ORDER);
    }

    public Cursor getArtistData(String[] from, String where) throws IOException, LockerException {
        return mDb.query(DbTables.ARTIST, from, where, null, null, null, DbKeys.ARTIST_NAME);
    }

    public Cursor getAlbumData(String[] from, String where) throws SQLiteException, IOException, LockerException {
        return mDb.query(DbTables.ALBUM, from, where, null, null, null, DbKeys.ALBUM_NAME);
    }

    public Cursor getAlbumDataByArtist(String[] from, LockerId id)
            throws SQLiteException, IOException, LockerException, MakeQueryException {
        refreshAlbumsForArtist(id.asInt());
        return mDb.query(DbTables.ALBUM, from, DbKeys.ARTIST_ID + "=" + id.asString(), null, null, null,
                DbKeys.ALBUM_NAME);
    }

    public Cursor getTrackDataByAlbum(String[] from, LockerId id)
            throws SQLiteException, IOException, LockerException {
        Log.w("MP3tunes", "querying for tracks on album: " + id.asString());
        if (id == null)
            return null;
        return mDb.query(DbTables.TRACK, from, DbKeys.ALBUM_ID + "=" + id.asString(), null, null, null,
                DbKeys.TITLE);
    }

    public Cursor getTrackDataByArtist(String[] from, LockerId mId)
            throws SQLiteException, IOException, LockerException {
        System.out.println("querying for tracks by artist: " + mId.asString());
        if (mId == null)
            return null;
        return mDb.query(DbTables.TRACK, from, DbKeys.ARTIST_ID + "=" + mId.asString(), null, null, null,
                DbKeys.TITLE);
    }

    //TODO: replace big query with view
    public Cursor getTrackDataByPlaylist(String[] from, LockerId mId)
            throws SQLiteException, IOException, LockerException {
        System.out.println("querying for tracks on playlist: " + mId.asString());
        if (mId == null)
            return null;

        StringBuilder query = new StringBuilder("SELECT DISTINCT ").append(DbTables.TRACK).append(".")
                .append(DbKeys.ID).append(" ");
        for (String key : from) {
            query.append(key).append(", ");
        }
        query.append(DbKeys.PLAYLIST_INDEX).append(" ").append("FROM ").append(DbTables.PLAYLIST).append(" JOIN ")
                .append(DbTables.PLAYLIST_TRACKS).append(" ").append("ON ").append(DbTables.PLAYLIST).append(".")
                .append(DbKeys.ID).append(" = ").append(DbTables.PLAYLIST_TRACKS).append(".")
                .append(DbKeys.PLAYLIST_ID).append(" ").append("JOIN ").append(DbTables.TRACK).append(" ")
                .append("ON ").append(DbTables.PLAYLIST_TRACKS).append(".").append(DbKeys.TRACK_ID).append(" = ")
                .append(DbTables.TRACK).append(".").append(DbKeys.ID).append(" ").append("WHERE ")
                .append(DbKeys.PLAYLIST_ID).append("='").append(mId.asString()).append("' ").append("ORDER BY ")
                .append(DbKeys.PLAYLIST_INDEX);

        return mDb.rawQuery(query.toString(), null);
    }

    public Id[] getSimilarTracks(Id id, int count)
            throws SQLiteException, MakeQueryException, IOException, LockerException {
        TrackGetter getter = new TrackGetter(this, mContext.getContentResolver());
        LockerId lockerId = null;
        try {
            lockerId = getter.getLockerId(id);
        } catch (Exception e) {
            e.printStackTrace();
        }

        List<Track> tracks = null;
        if (lockerId == null) {
            tracks = mLocker.getTracksForPlaylist(Playlist.RANDOM_TRACKS, count, 0);
        } else {
            tracks = mLocker.getSimilarTracks(lockerId, count, 0);
        }
        multiInsert(tracks, null);
        return Music.lockerDataToIdArray(tracks.toArray(new Track[0]));
    }

    class DataInserter {

        private <T> void insert(T object, int index) throws IOException, SQLiteException {
            if (Track.class.isInstance(object)) {
                insert((Track) object, index);
            } else if (Artist.class.isInstance(object)) {
                insert((Artist) object, index);
            } else if (Album.class.isInstance(object)) {
                insert((Album) object, index);
            } else if (Playlist.class.isInstance(object)) {
                insert((Playlist) object, index);
            }
        }

        private void insert(Track track, int index) throws IOException, SQLiteException {

            if (track == null) {
                System.out.println("OMG TRACK NULL");
                return;
            }

            try {
                // Insert artist info to the artist table
                if (track.getArtistName().length() > 0) {
                    if (!mQueries.artistExists(track.getArtistId())) {
                        mQueries.insertArtist(track);
                    }
                }

                // Insert album info to the album table
                if (track.getAlbumTitle().length() > 0) {
                    if (!mQueries.albumExists(track.getAlbumId())) {
                        mQueries.insertAlbum(track);
                    }
                }

                // Insert track info
                if (!mQueries.trackExists(track.getId().asInt())) {
                    mQueries.insertTrack(track, index);
                }
            } catch (SQLiteException e) {
                throw e;
            }
        }

        private void insert(Artist artist, int index) throws IOException, SQLiteException {
            if (artist == null) {
                System.out.println("OMG Artist NULL");
                return;
            }
            try {
                if (artist.getName().length() > 0) {
                    if (mQueries.artistExists(artist.getId().asInt())) {
                        mQueries.updateArtist(artist);
                    } else {
                        mQueries.insertArtist(artist);
                    }
                }
            } catch (SQLiteException e) {
                throw e;
            }
        }

        private void insert(Album album, int index) throws IOException, SQLiteException {
            if (album == null) {
                System.out.println("OMG Album NULL");
                return;
            }
            try {
                if (album.getName().length() > 0) {
                    if (mQueries.albumExists(album.getId().asInt())) {
                        mQueries.updateAlbum(album);
                    } else {
                        mQueries.insertAlbum(album);
                    }
                }
            } catch (SQLiteException e) {

                throw e;
            }
        }

        private void insert(final Playlist playlist, final int index) throws IOException, SQLiteException {
            if (playlist == null) {
                System.out.println("OMG Playlist NULL");
                return;
            }
            try {
                if (playlist.getName().length() > 0) {
                    if (mQueries.playlistExists(playlist.getId().asString())) {
                        mQueries.updatePlaylist(playlist, index);
                    } else {
                        mQueries.insertPlaylist(playlist, index);
                    }
                }
            } catch (SQLiteException e) {
                throw e;
            }
        }

    }

    <T> boolean multiInsert(List<T> list, LockerCache.Progress progress)
            throws SQLiteException, IOException, MakeQueryException {
        DataInserter inserter = new DataInserter();
        mDb.beginTransaction();
        try {
            int i = 0;
            if (progress != null)
                i = progress.mCurrentSet * progress.mCount;
            for (T o : list) {
                inserter.insert(o, i);
                i++;
            }
            mDb.setTransactionSuccessful();
        } finally {
            mDb.endTransaction();
        }
        return list.size() > 0;
    }

    private void refreshAlbumsForArtist(final int artist_id)
            throws SQLiteException, IOException, LockerException, MakeQueryException {
        DataInserter inserter = new DataInserter();
        List<Album> albums = mLocker.getAlbumsForArtist(artist_id);

        System.out.println("beginning insertion of " + albums.size() + " albums for artist id " + artist_id);
        mDb.beginTransaction();
        try {
            for (Album a : albums) {
                inserter.insert(a, 0);
            }
            mDb.setTransactionSuccessful();
        } finally {
            mDb.endTransaction();
        }
        System.out.println("insertion complete");
    }

    void deleteOldPlaylistTracks(String playlist_id) {
        String where = "playlist_id=?";
        String[] whereArgs = new String[] { playlist_id };
        mDb.delete(DbTables.PLAYLIST_TRACKS, where, whereArgs);
    }

    boolean insertTracksForPlaylist(List<Track> tracks, String playlist_id, LockerCache.Progress progress)
            throws SQLiteException, IOException, MakeQueryException {
        System.out.println("beginning insertion of " + tracks.size() + " tracks for playlist id " + playlist_id);

        multiInsert(tracks, progress);

        int index = 0 + (progress.mCount * progress.mCurrentSet);
        ContentValues cv = new ContentValues();
        for (Track t : tracks) {
            if (!mQueries.trackInPlaylist(playlist_id, t.getId().asInt())) {
                cv.put(DbKeys.PLAYLIST_ID, playlist_id);
                cv.put(DbKeys.TRACK_ID, t.getId().asInt());
                cv.put(DbKeys.PLAYLIST_INDEX, index);
                mDb.insert(DbTables.PLAYLIST_TRACKS, DbKeys.UNKNOWN_STRING, cv);
                index++;
            }
        }
        return tracks.size() > 0;
    }

    void updateCache(String id, long time, int state, LockerCache.Progress progress) throws MakeQueryException {
        try {
            if (mQueries.cacheExists(id)) {
                mQueries.updateCache(id, time, state, progress);
            } else {
                mQueries.insertCache(id, time, state, progress);
            }
        } catch (SQLiteException e) {
            throw e;
        }
    }

    public Cursor getCache(String[] projection) {
        return mDb.query(DbTables.CACHE, projection, null, null, null, null, null);
    }

    //helpers to turn cursors into api data structures

    private Track cursorToTrack(Cursor c) {
        if (c == null)
            return null;
        try {
            if (c.moveToFirst()) {
                int id = c.getInt(Music.TRACK_MAPPING.ID);
                int artist_id = c.getInt(Music.TRACK_MAPPING.ARTIST_ID);
                int album_id = c.getInt(Music.TRACK_MAPPING.ALBUM_ID);
                String play_url = c.getString(Music.TRACK_MAPPING.PLAY_URL);
                String title = c.getString(Music.TRACK_MAPPING.TITLE);
                String artist_name = c.getString(Music.TRACK_MAPPING.ARTIST_NAME);
                String album_name = c.getString(Music.TRACK_MAPPING.ALBUM_NAME);
                Track t = new ConcreteTrack(new LockerId(id), play_url, title, artist_id, artist_name, album_id,
                        album_name);
                return t;
            }
        } finally {
            c.close();
        }
        return null;
    }

    private Artist cursorToArtist(Cursor c) {
        if (c == null)
            return null;
        try {
            if (c.moveToFirst()) {
                Artist a = new Artist(new LockerId(c.getInt(Music.ARTIST_MAPPING.ID)),
                        c.getString(Music.ARTIST_MAPPING.ARTIST_NAME));
                c.close();
                return a;
            }
        } finally {
            c.close();
        }
        return null;
    }

    private Album cursorToAlbum(Cursor c) {
        if (c.getCount() > 1)
            Log.e("Mp3Tunes", "Got more than one album");
        if (c.moveToFirst()) {
            Album a = new Album(new LockerId(c.getInt(Music.ALBUM_MAPPING.ID)),
                    c.getString(Music.ALBUM_MAPPING.ALBUM_NAME));
            c.close();
            return a;
        }
        c.close();
        return null;
    }

    //Refresh tasks that are not cached

    static public class RefreshAlbumTracksTask extends RefreshTask {
        Id mId;

        public RefreshAlbumTracksTask(LockerDb db, Id id) {
            super(db);
            mId = id;
        }

        @Override
        protected Boolean doInBackground(Void... params) {
            Log.w("Mp3Tunes", "Starting RefreshTracks for album");
            try {
                AlbumGetter getter = new AlbumGetter(mDb, mDb.mContext.getContentResolver());
                LockerId id = getter.getLockerId(mId);
                if (id == null)
                    return true;
                List<Track> tracks = mDb.mLocker.getTracksForAlbumFromJson(id.asInt());
                mDb.multiInsert(tracks, null);
                return true;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return false;
        }

        @Override
        protected boolean dispatch(String cacheId, Progress p, String id)
                throws SQLiteException, IOException, LockerException, MakeQueryException {
            return false;
        }
    }

    static public class RefreshArtistTracksTask extends RefreshTask {
        Id mId;

        public RefreshArtistTracksTask(LockerDb db, Id id) {
            super(db);
            mId = id;
        }

        @Override
        protected Boolean doInBackground(Void... params) {
            Log.w("Mp3Tunes", "Starting RefreshTracks for artist");
            try {
                ArtistGetter getter = new ArtistGetter(mDb, mDb.mContext.getContentResolver());
                LockerId id = getter.getLockerId(mId);
                if (id == null)
                    return true;
                List<Track> tracks = mDb.mLocker.getTracksForArtistFromJson(id.asInt());
                mDb.multiInsert(tracks, null);
                return true;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return false;
        }

        @Override
        protected boolean dispatch(String cacheId, Progress p, String id)
                throws SQLiteException, IOException, LockerException, MakeQueryException {
            return false;
        }
    }

    static public class RefreshSearchTask extends RefreshTask {
        private String[] mTrack;
        private String[] mArtist;
        private DbSearchQuery mQuery;
        protected DbSearchResult mResult;

        public RefreshSearchTask(LockerDb db, DbSearchQuery query, String[] track, String[] artist) {
            super(db);
            mQuery = query;
            mTrack = track;
            mArtist = artist;
        }

        @Override
        protected Boolean doInBackground(Void... params) {
            try {
                refresh();
                System.out.println("insertion complete");

                mResult = new DbSearchResult();
                if (mQuery.mTracks)
                    mResult.mTracks = querySearch(mQuery.mQuery, Music.Meta.TRACK, mTrack);
                if (mQuery.mArtists)
                    mResult.mArtists = querySearch(mQuery.mQuery, Music.Meta.ARTIST, mArtist);

                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }

        @Override
        protected boolean dispatch(String cacheId, Progress p, String id)
                throws SQLiteException, IOException, LockerException, MakeQueryException {
            return false;
        }

        public void refresh() throws LockerException, InvalidSessionException, JSONException, LoginException,
                SQLiteException, IOException {
            SearchResult results = null;
            results = mDb.mLocker.search(mQuery.mQuery);

            DataInserter inserter = mDb.new DataInserter();
            for (Artist a : results.getArtists()) {
                inserter.insert(a, 0);
            }
            for (Album a : results.getAlbums()) {
                inserter.insert(a, 0);
            }
            for (Track t : results.getTracks()) {
                inserter.insert(t, 0);
            }
            System.out.println("insertion complete");
        }

        private Cursor querySearch(String query, Music.Meta type, String[] columns) {
            String table;
            String selection;
            String[] selectionArgs = new String[] { "%" + query + "%" };
            switch (type) {
            case TRACK:
                table = "track";
                selection = "lower(title) LIKE lower(?)";
                break;
            case ARTIST:
                table = "artist";
                columns = Music.ARTIST;
                selection = "lower(artist_name) LIKE lower(?)";
                break;
            case ALBUM:
                table = "album";
                selection = "lower(album_name) LIKE lower(?)";
                break;
            default:
                return null;
            }
            return mDb.mDb.query(table, columns, selection, selectionArgs, null, null, null, null);
        }

        static public class DbSearchResult {
            public Cursor mArtists = null;
            public Cursor mAlbums = null;
            public Cursor mTracks = null;
        }

        static public class DbSearchQuery {
            public DbSearchQuery(String query, boolean artists, boolean albums, boolean tracks) {
                mQuery = query;
                mArtists = artists;
                mAlbums = albums;
                mTracks = tracks;
            }

            public String mQuery;
            public boolean mArtists;
            public boolean mAlbums;
            public boolean mTracks;
        }
    }

    //    //The code that manages search
    //    //TODO: There is perhaps no code in the project that needs rewriting more than this
    //    
    //    public DbSearchResult search(DbSearchQuery query, String[] track, String[] artist) throws MakeQueryException
    //    {
    //        try {
    //            // Perform the single http search call
    //            refreshSearch(query.mQuery);
    //            DbSearchResult res = new DbSearchResult();
    //            if (query.mTracks)
    //                res.mTracks = querySearch(query.mQuery, Music.Meta.TRACK, track);
    //            //if (query.mAlbums)
    //            //    res.mAlbums = querySearch(query.mQuery, Music.Meta.ALBUM);
    //            if (query.mArtists)
    //                res.mArtists = querySearch(query.mQuery, Music.Meta.ARTIST, artist);
    //
    //            System.out.println("Got artists: " + res.mArtists.getCount());
    //            System.out.println("Got tracks: " + res.mTracks.getCount());
    //            return res;
    //        } catch (SQLiteException e) {
    //            e.printStackTrace();
    //        } catch (IOException e) {
    //            e.printStackTrace();
    //        } catch (LockerException e) {
    //            e.printStackTrace();
    //        }
    //        return null;
    //    }
    //    
    //    public void refreshSearch(String query) throws SQLiteException, IOException, LockerException, MakeQueryException
    //    {
    //        SearchResult results = null;
    //        try {
    //            results = mLocker.search(query);
    //        } catch (InvalidSessionException e) {
    //            throw new LockerException("Bad Session Data");
    //        } catch (JSONException e) {
    //            throw new LockerException("Sever Sent Corrupt Data");
    //        } catch (LoginException e) {
    //            throw new LockerException("Unable to refresh session");
    //        }
    //        
    //        DataInserter inserter = new DataInserter();
    //        for (Artist a : results.getArtists()) {
    //            inserter.insert(a, 0);
    //        }
    //        for (Album a : results.getAlbums()) {
    //            inserter.insert(a, 0);
    //        }
    //        for (Track t : results.getTracks()) {
    //            inserter.insert(t, 0);
    //        }
    //        System.out.println("insertion complete");
    //    }
    //    private Cursor querySearch(String query, Music.Meta type, String[] columns)
    //    {
    //        String table;
    //        String selection;
    //        String[] selectionArgs = new String[] {"%" + query + "%"};
    //        switch (type) {
    //            case TRACK:
    //                table = "track";
    //                selection = "lower(title) LIKE lower(?)";
    //                break;
    //            case ARTIST:
    //                table = "artist";
    //                columns = Music.ARTIST;
    //                selection = "lower(artist_name) LIKE lower(?)";
    //                break;
    //            case ALBUM:
    //                table = "album";
    //                selection = "lower(album_name) LIKE lower(?)";
    //                break;
    //            default:
    //                return null;
    //        }
    //        return mDb.query(table, columns, selection, selectionArgs, null, null, null,
    //                null);
    //    }
    //    public class DbSearchResult
    //    {
    //        public Cursor mArtists = null;
    //        public Cursor mAlbums  = null;
    //        public Cursor mTracks  = null;
    //    }
    //    public class DbSearchQuery
    //    {
    //        public DbSearchQuery(String query, boolean artists, boolean albums,
    //                boolean tracks)
    //        {
    //            mQuery = query;
    //            mArtists = artists;
    //            mAlbums = albums;
    //            mTracks = tracks;
    //        }
    //
    //        public String  mQuery;
    //        public boolean mArtists;
    //        public boolean mAlbums;
    //        public boolean mTracks;
    //    }

}