/* This file is part of the Android Clementine Remote. * Copyright (C) 2013, Andreas Muttscheller <asfa194@gmail.com> * * 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, see <http://www.gnu.org/licenses/>. */ package de.qspool.clementineremote.backend.pb; import com.google.protobuf.InvalidProtocolBufferException; import android.util.Log; import java.util.LinkedList; import java.util.List; import de.qspool.clementineremote.App; import de.qspool.clementineremote.backend.Clementine; import de.qspool.clementineremote.backend.Clementine.RepeatMode; import de.qspool.clementineremote.backend.Clementine.ShuffleMode; import de.qspool.clementineremote.backend.pb.ClementineMessage.ErrorMessage; import de.qspool.clementineremote.backend.pb.ClementineRemoteProtocolBuffer.EngineState; import de.qspool.clementineremote.backend.pb.ClementineRemoteProtocolBuffer.Lyric; import de.qspool.clementineremote.backend.pb.ClementineRemoteProtocolBuffer.Message; import de.qspool.clementineremote.backend.pb.ClementineRemoteProtocolBuffer.Playlist; import de.qspool.clementineremote.backend.pb.ClementineRemoteProtocolBuffer.Repeat; import de.qspool.clementineremote.backend.pb.ClementineRemoteProtocolBuffer.ResponseActiveChanged; import de.qspool.clementineremote.backend.pb.ClementineRemoteProtocolBuffer.ResponseClementineInfo; import de.qspool.clementineremote.backend.pb.ClementineRemoteProtocolBuffer.ResponseCurrentMetadata; import de.qspool.clementineremote.backend.pb.ClementineRemoteProtocolBuffer.ResponseLyrics; import de.qspool.clementineremote.backend.pb.ClementineRemoteProtocolBuffer.ResponsePlaylistSongs; import de.qspool.clementineremote.backend.pb.ClementineRemoteProtocolBuffer.ResponsePlaylists; import de.qspool.clementineremote.backend.pb.ClementineRemoteProtocolBuffer.ResponseUpdateTrackPosition; import de.qspool.clementineremote.backend.pb.ClementineRemoteProtocolBuffer.Shuffle; import de.qspool.clementineremote.backend.pb.ClementineRemoteProtocolBuffer.SongMetadata; import de.qspool.clementineremote.backend.player.LyricsProvider; import de.qspool.clementineremote.backend.player.MyPlaylist; import de.qspool.clementineremote.backend.player.MySong; import de.qspool.clementineremote.backend.player.PlaylistManager; public class ClementinePbParser { private PlaylistManager mPlaylistManager; public ClementinePbParser() { mPlaylistManager = App.Clementine.getPlaylistManager(); } /** * Create a protocol buffer object from the binary data * * @param bs The binary representation of the protocol buffer * @return The parsed Element */ public ClementineMessage parse(byte[] bs) { ClementineMessage parsedElement = null; try { Message msg = Message.parseFrom(bs); // First check the proto version if (!msg.hasVersion() || msg.getVersion() < Message.getDefaultInstance().getVersion()) { parsedElement = new ClementineMessage(ErrorMessage.OLD_PROTO); } else { parsedElement = parseMsg(msg); } } catch (InvalidProtocolBufferException e) { Log.d("Parser", "InvalidProtocolBufferException"); parsedElement = new ClementineMessage(ErrorMessage.INVALID_DATA); } return parsedElement; } /** * Parse the message itself * * @param msg The created message * @return The parsed data */ private ClementineMessage parseMsg(Message msg) { ClementineMessage clementineMessage = new ClementineMessage(msg); switch (msg.getType()) { case INFO: parseInfos(msg.getResponseClementineInfo()); break; case CURRENT_METAINFO: MySong s = parseSong(msg.getResponseCurrentMetadata()); App.Clementine.setCurrentSong(s); App.Clementine.setSongPosition(0); break; case UPDATE_TRACK_POSITION: parseUpdateTrackPosition(msg.getResponseUpdateTrackPosition()); break; case KEEP_ALIVE: App.ClementineConnection.setLastKeepAlive(System.currentTimeMillis()); break; case SET_VOLUME: App.Clementine.setVolume(msg.getRequestSetVolume().getVolume()); break; case PLAY: App.Clementine.setState(Clementine.State.PLAY); break; case PAUSE: App.Clementine.setState(Clementine.State.PAUSE); break; case STOP: App.Clementine.setState(Clementine.State.STOP); break; case DISCONNECT: break; case PLAYLISTS: parsePlaylists(msg.getResponsePlaylists()); break; case PLAYLIST_SONGS: parsePlaylistSongs(msg.getResponsePlaylistSongs()); break; case ACTIVE_PLAYLIST_CHANGED: parseActivePlaylistChanged(msg.getResponseActiveChanged()); break; case REPEAT: parseRepeat(msg.getRepeat()); break; case SHUFFLE: parseShuffle(msg.getShuffle()); break; case LYRICS: parseLyrics(msg.getResponseLyrics()); break; case SONG_FILE_CHUNK: break; case DOWNLOAD_QUEUE_EMPTY: break; default: break; } return clementineMessage; } /** * Parse the lyrics and save them into the song object * * @param responseLyrics The protocolbuffer message with the lyrics */ private void parseLyrics(ResponseLyrics responseLyrics) { // Read all lyric providers for (Lyric lyric : responseLyrics.getLyricsList()) { // Save them into the structure LyricsProvider provider = new LyricsProvider(); provider.setId(lyric.getId()); provider.setTitle(lyric.getTitle()); provider.setContent(lyric.getContent()); // And save them into the song App.Clementine.getCurrentSong().getLyricsProvider().add(provider); } } /** * Update the currently active playlist id * * @param responseActiveChanged The response element * @return A new Reload element */ private void parseActivePlaylistChanged( ResponseActiveChanged responseActiveChanged) { mPlaylistManager.setActivePlaylist(responseActiveChanged.getId()); } /** * Parse a song message * * @param responseCurrentMetadata The song message * @return The Song in the representation of this app */ private MySong parseSong(ResponseCurrentMetadata responseCurrentMetadata) { if (!responseCurrentMetadata.hasSongMetadata()) { return null; } // Get the metadata from protocolbuffer and set the song SongMetadata songMetadata = responseCurrentMetadata.getSongMetadata(); if (!songMetadata.hasId()) { return null; } return MySong.fromProtocolBuffer(songMetadata); } /** * Parse the info message * * @param responseClementineInfo The info message */ private void parseInfos(ResponseClementineInfo responseClementineInfo) { // Get the version number of clementine App.Clementine.setVersion(responseClementineInfo.getVersion()); // Get the current state of the player EngineState state = responseClementineInfo.getState(); switch (state.getNumber()) { case EngineState.Playing_VALUE: App.Clementine.setState(Clementine.State.PLAY); break; case EngineState.Paused_VALUE: App.Clementine.setState(Clementine.State.PAUSE); break; default: App.Clementine.setState(Clementine.State.STOP); } } /** * Sets the current position of the track * * @param responseUpdateTrackPosition The message */ private void parseUpdateTrackPosition(ResponseUpdateTrackPosition responseUpdateTrackPosition) { App.Clementine.setSongPosition(responseUpdateTrackPosition.getPosition()); } /** * Parse the playlists * * @param responsePlaylists The Playlist Elements */ private void parsePlaylists(ResponsePlaylists responsePlaylists) { mPlaylistManager.removeAll(); List<Playlist> playlists = responsePlaylists.getPlaylistList(); for (Playlist playlist : playlists) { // Create the playlist and add the information MyPlaylist myPlaylist = new MyPlaylist(); myPlaylist.setId(playlist.getId()); myPlaylist.setName(playlist.getName()); myPlaylist.setActive(playlist.getActive()); myPlaylist.setItemCount(playlist.getItemCount()); myPlaylist.setClosed(playlist.getClosed()); mPlaylistManager.addPlaylist(myPlaylist); } mPlaylistManager.allPlaylistsReceived(); } /** * Parse the songs in a playlist and add them to our structure * * @param response The message with the songs */ private void parsePlaylistSongs(ResponsePlaylistSongs response) { Playlist playlist = response.getRequestedPlaylist(); List<SongMetadata> songs = response.getSongsList(); List<MySong> mySongs = new LinkedList<MySong>(); for (SongMetadata s : songs) { mySongs.add(MySong.fromProtocolBuffer(s)); } mPlaylistManager.playlistSongsDownloaded(playlist.getId(), mySongs); } /** * Get the Repeat Mode * * @param repeat The Element */ private void parseRepeat(Repeat repeat) { switch (repeat.getRepeatMode()) { case Repeat_Off: App.Clementine.setRepeatMode(RepeatMode.OFF); break; case Repeat_Track: App.Clementine.setRepeatMode(RepeatMode.TRACK); break; case Repeat_Album: App.Clementine.setRepeatMode(RepeatMode.ALBUM); break; case Repeat_Playlist: App.Clementine.setRepeatMode(RepeatMode.PLAYLIST); break; default: break; } } /** * Get the shuffle Mode * * @param shuffle The Element */ private void parseShuffle(Shuffle shuffle) { switch (shuffle.getShuffleMode()) { case Shuffle_Off: App.Clementine.setShuffleMode(ShuffleMode.OFF); break; case Shuffle_All: App.Clementine.setShuffleMode(ShuffleMode.ALL); break; case Shuffle_InsideAlbum: App.Clementine.setShuffleMode(ShuffleMode.INSIDE_ALBUM); break; case Shuffle_Albums: App.Clementine.setShuffleMode(ShuffleMode.ALBUMS); break; default: break; } } }