diff options
Diffstat (limited to 'subsonic-android/patches/jukebox-patch.txt')
-rw-r--r-- | subsonic-android/patches/jukebox-patch.txt | 1234 |
1 files changed, 1234 insertions, 0 deletions
diff --git a/subsonic-android/patches/jukebox-patch.txt b/subsonic-android/patches/jukebox-patch.txt new file mode 100644 index 00000000..5bc92000 --- /dev/null +++ b/subsonic-android/patches/jukebox-patch.txt @@ -0,0 +1,1234 @@ +Index: AndroidManifest.xml +=================================================================== +--- AndroidManifest.xml (revision 2441) ++++ AndroidManifest.xml (working copy) +@@ -114,7 +114,8 @@ + a:authorities="net.sourceforge.subsonic.androidapp.provider.SearchSuggestionProvider"/> + + <meta-data a:name="android.app.default_searchable" +- a:value="net.sourceforge.subsonic.androidapp.activity.QueryReceiverActivity"/> ++ a:value="net.sourceforge.subsonic.androidapp.activity.QueryReceiverActivity"/> ++ <activity a:name="net.sourceforge.subsonic.androidapp.activity.JukeboxActivity" a:launchMode="singleTask" a:configChanges="keyboardHidden"></activity> + + </application> + +Index: res/drawable/menu_jukebox.png +=================================================================== +Cannot display: file marked as a binary type. +svn:mime-type = application/octet-stream + +Property changes on: res/drawable/menu_jukebox.png +___________________________________________________________________ +Added: svn:mime-type + + application/octet-stream + +Index: res/drawable-hdpi-v4/menu_jukebox.png +=================================================================== +Cannot display: file marked as a binary type. +svn:mime-type = application/octet-stream + +Property changes on: res/drawable-hdpi-v4/menu_jukebox.png +___________________________________________________________________ +Added: svn:mime-type + + application/octet-stream + +Index: res/layout/button_bar.xml +=================================================================== +--- res/layout/button_bar.xml (revision 2441) ++++ res/layout/button_bar.xml (working copy) +@@ -47,6 +47,14 @@ + a:layout_weight="1" + a:layout_width="0dp" + a:layout_height="wrap_content"/> ++ ++ <ImageButton a:id="@+id/button_bar_jukebox" ++ a:src="@drawable/menu_jukebox" ++ a:contentDescription="@string/button_bar.jukebox" ++ a:background="@drawable/menubar_button" ++ a:layout_weight="1" ++ a:layout_width="0dp" ++ a:layout_height="wrap_content"/> + + <ImageButton a:id="@+id/button_bar_now_playing" + a:src="@drawable/menu_now_playing" +Index: res/layout/jukebox.xml +=================================================================== +--- res/layout/jukebox.xml (revision 0) ++++ res/layout/jukebox.xml (revision 0) +@@ -0,0 +1,72 @@ ++<?xml version="1.0" encoding="utf-8"?> ++<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" ++ a:layout_width="fill_parent" ++ a:layout_height="fill_parent" ++ a:orientation="vertical" > ++ ++ <include layout="@layout/tab_progress"/> ++ ++ <ListView ++ a:id="@+id/jukebox_list" ++ a:layout_width="match_parent" ++ a:layout_height="0dp" ++ a:layout_weight="1" > ++ </ListView> ++ ++ <LinearLayout ++ a:layout_width="fill_parent" ++ a:layout_height="wrap_content" ++ a:layout_marginTop="0dip" ++ a:background="@color/mediaControlBackground" ++ a:gravity="center" ++ a:orientation="horizontal" ++ a:paddingBottom="0dip" ++ a:paddingTop="0dip" > ++ ++ <ImageButton ++ a:id="@+id/jukebox_shuffle" ++ a:layout_width="wrap_content" ++ a:layout_height="fill_parent" ++ a:background="@android:color/transparent" ++ a:src="@drawable/media_shuffle" /> ++ ++ <ImageButton ++ a:id="@+id/jukebox_previous" ++ a:layout_width="wrap_content" ++ a:layout_height="wrap_content" ++ a:background="@android:color/transparent" ++ a:padding="0dip" ++ a:src="@drawable/media_backward" /> ++ ++ <ImageButton ++ a:id="@+id/jukebox_stop" ++ a:layout_width="60dip" ++ a:layout_height="60dip" ++ a:padding="0dip" ++ a:background="@drawable/media_stop" /> ++ ++ <ImageButton ++ a:id="@+id/jukebox_start" ++ a:layout_width="60dip" ++ a:layout_height="60dip" ++ a:padding="0dip" ++ a:background="@drawable/media_start" /> ++ ++ <ImageButton ++ a:id="@+id/jukebox_next" ++ a:layout_width="wrap_content" ++ a:layout_height="wrap_content" ++ a:background="@android:color/transparent" ++ a:padding="0dip" ++ a:src="@drawable/media_forward" /> ++ </LinearLayout> ++ ++ <SeekBar ++ a:id="@+id/jukebox_seek" ++ a:background="@color/mediaControlBackground" ++ a:layout_width="match_parent" ++ a:layout_height="wrap_content" /> ++ ++ <include layout="@layout/button_bar" /> ++ ++</LinearLayout> +\ No newline at end of file +Index: res/menu/jukebox_context.xml +=================================================================== +--- res/menu/jukebox_context.xml (revision 0) ++++ res/menu/jukebox_context.xml (revision 0) +@@ -0,0 +1,8 @@ ++<?xml version="1.0" encoding="utf-8"?> ++<menu xmlns:android="http://schemas.android.com/apk/res/android" > ++ <item android:id="@+id/jukebox_clear" android:title="@string/jukebox_clear"></item> ++ <item android:id="@+id/jukebox_remove" android:title="@string/jukebox_remove"></item> ++ ++ ++ ++</menu> +\ No newline at end of file +Index: res/menu/select_album_context.xml +=================================================================== +--- res/menu/select_album_context.xml (revision 2441) ++++ res/menu/select_album_context.xml (working copy) +@@ -15,5 +15,6 @@ + a:id="@+id/album_menu_pin" + a:title="@string/common.pin" + /> ++ <item a:id="@+id/album_menu_jukebox_add" a:title="@string/jukebox_add"></item> + + </menu> +Index: res/menu/select_artist_context.xml +=================================================================== +--- res/menu/select_artist_context.xml (revision 2441) ++++ res/menu/select_artist_context.xml (working copy) +@@ -15,5 +15,6 @@ + a:id="@+id/artist_menu_pin" + a:title="@string/common.pin" + /> ++ <item a:id="@+id/artist_menu_jukebox_add" a:title="@string/jukebox_add"></item> + + </menu> +Index: res/menu/select_song_context.xml +=================================================================== +--- res/menu/select_song_context.xml (revision 2441) ++++ res/menu/select_song_context.xml (working copy) +@@ -15,5 +15,10 @@ + a:id="@+id/song_menu_play_last" + a:title="@string/common.play_last" + /> ++ ++ <item ++ a:id="@+id/song_menu_jukebox_add" ++ a:title="@string/jukebox_add" ++ /> + + </menu> +Index: res/values/strings.xml +=================================================================== +--- res/values/strings.xml (revision 2441) ++++ res/values/strings.xml (working copy) +@@ -226,6 +226,14 @@ + <plurals name="select_album_donate_dialog_n_trial_days_left"> + <item quantity="one">One day left of trial period</item> + <item quantity="other">%d days left of trial period</item> +- </plurals> ++ </plurals> ++ <string name="button_bar.jukebox">Jukebox</string> ++ <string name="jukebox_start">Start</string> ++ <string name="jukebox_stop">Stop</string> ++ <string name="jukebox_next">Next</string> ++ <string name="jukebox_previous">Previous</string> ++ <string name="jukebox_clear">Clear</string> ++ <string name="jukebox_remove">Remove</string> ++ <string name="jukebox_add">Add to Jukebox</string> + + </resources> +Index: src/net/sourceforge/subsonic/androidapp/activity/JukeboxActivity.java +=================================================================== +--- src/net/sourceforge/subsonic/androidapp/activity/JukeboxActivity.java (revision 0) ++++ src/net/sourceforge/subsonic/androidapp/activity/JukeboxActivity.java (revision 0) +@@ -0,0 +1,235 @@ ++/* ++ This file is part of Subsonic. ++ ++ Subsonic 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. ++ ++ Subsonic 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 Subsonic. If not, see <http://www.gnu.org/licenses/>. ++ ++ Copyright 2009 (C) Sindre Mehus ++ */ ++ ++package net.sourceforge.subsonic.androidapp.activity; ++ ++import java.util.List; ++ ++import net.sourceforge.subsonic.androidapp.R; ++import net.sourceforge.subsonic.androidapp.domain.Jukebox; ++import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; ++import net.sourceforge.subsonic.androidapp.domain.MusicDirectory.Entry; ++import net.sourceforge.subsonic.androidapp.service.MusicService; ++import net.sourceforge.subsonic.androidapp.service.MusicServiceFactory; ++import net.sourceforge.subsonic.androidapp.util.BackgroundTask; ++import net.sourceforge.subsonic.androidapp.util.JukeboxSongView; ++import net.sourceforge.subsonic.androidapp.util.ProgressListener; ++import net.sourceforge.subsonic.androidapp.util.TabActivityBackgroundTask; ++import net.sourceforge.subsonic.androidapp.util.Util; ++import android.os.Bundle; ++import android.view.ContextMenu; ++import android.view.MenuInflater; ++import android.view.MenuItem; ++import android.view.View; ++import android.view.View.OnClickListener; ++import android.view.ViewGroup; ++import android.widget.AdapterView; ++import android.widget.AdapterView.OnItemClickListener; ++import android.widget.ArrayAdapter; ++import android.widget.ImageButton; ++import android.widget.ListView; ++import android.widget.SeekBar; ++import android.widget.SeekBar.OnSeekBarChangeListener; ++ ++/** ++ * @author meld0 ++ * ++ */ ++public class JukeboxActivity extends SubsonicTabActivity implements ProgressListener, OnClickListener, OnSeekBarChangeListener, OnItemClickListener{ ++ ++ private ImageButton play, stop, next, prev, shuffle; ++ private ListView entryList; ++ private SeekBar gain; ++ private Jukebox jukebox; ++ ++ @Override ++ public void onCreate(Bundle savedInstanceState){ ++ super.onCreate(savedInstanceState); ++ setContentView(R.layout.jukebox); ++ ++ jukebox = new Jukebox(); ++ ++ play = (ImageButton) findViewById(R.id.jukebox_start); ++ stop = (ImageButton) findViewById(R.id.jukebox_stop); ++ prev = (ImageButton) findViewById(R.id.jukebox_previous); ++ next = (ImageButton) findViewById(R.id.jukebox_next); ++ shuffle = (ImageButton) findViewById(R.id.jukebox_shuffle); ++ entryList = (ListView) findViewById(R.id.jukebox_list); ++ ++ gain = (SeekBar) findViewById(R.id.jukebox_seek); ++ gain.setMax(100); ++ ++ play.setOnClickListener(this); ++ stop.setOnClickListener(this); ++ next.setOnClickListener(this); ++ prev.setOnClickListener(this); ++ shuffle.setOnClickListener(this); ++ gain.setOnSeekBarChangeListener(this); ++ entryList.setOnItemClickListener(this); ++ ++ registerForContextMenu(entryList); ++ load(); ++ } ++ ++ @Override ++ public void updateProgress(int messageId){ ++ ++ } ++ ++ private void load(){ ++ load(Jukebox.GET); ++ } ++ ++ private void load(String action){ ++ load(action, ""); ++ } ++ ++ private void load(final String action, final String additional){ ++ BackgroundTask<Boolean> task = new TabActivityBackgroundTask<Boolean>(this){ ++ ++ protected Boolean doInBackground() throws Throwable{ ++ boolean update = false; ++ if (!Util.isOffline(JukeboxActivity.this)) { ++ MusicService musicService = MusicServiceFactory.getMusicService(JukeboxActivity.this); ++ jukebox.lastAction = action; ++ jukebox = musicService.getJukebox(jukebox, JukeboxActivity.this, this, additional); ++ update = true; ++ } ++ return update; ++ } ++ ++ protected void done(Boolean result){ ++ if (jukebox.isSuccess()) { ++ if (jukebox.lastAction.equals(Jukebox.SET_GAIN)) { ++ jukebox.setGain((int) (Double.valueOf(additional.split("=")[1]) * 100)); ++ gain.setProgress(jukebox.getGain()); ++ } else if (jukebox.lastAction.equals(Jukebox.REMOVE_ITEM)) ((SongListAdapter) entryList.getAdapter()).remove((Entry) entryList.getAdapter().getItem( ++ Integer.valueOf(additional.split("=")[1]))); ++ else if (jukebox.lastAction.equals(Jukebox.CLEAR_PLAYLIST)) ((SongListAdapter) entryList.getAdapter()).clear(); ++ else if (jukebox.lastAction.equals(Jukebox.SKIP_TO_INDEX)) jukebox.setCurrentIndex(Integer.valueOf(additional.split("=")[1])); ++ ++ if (jukebox.isNewPlaylist()) entryList.setAdapter(new SongListAdapter(jukebox.getChildren())); ++ } ++ } ++ }; ++ task.execute(); ++ } ++ ++ @Override ++ public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo){ ++ super.onCreateContextMenu(menu, view, menuInfo); ++ MenuInflater inflater = getMenuInflater(); ++ inflater.inflate(R.menu.jukebox_context, menu); ++ } ++ ++ @Override ++ public boolean onContextItemSelected(MenuItem menuItem){ ++ AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo(); ++ switch (menuItem.getItemId()) { ++ case R.id.jukebox_remove: ++ load(Jukebox.REMOVE_ITEM, "index=" + info.position); ++ break; ++ case R.id.jukebox_clear: ++ load(Jukebox.CLEAR_PLAYLIST); ++ break; ++ default: ++ return super.onContextItemSelected(menuItem); ++ } ++ return true; ++ } ++ ++ @Override ++ protected void onResume(){ ++ super.onResume(); ++ load(); ++ } ++ ++ @Override ++ public void onClick(View arg0){ ++ if (arg0.equals(this.play)) { ++ load(Jukebox.START_PLAYBACK); ++ } else if (arg0.equals(this.stop)) { ++ load(Jukebox.STOP_PLAYBACK); ++ } else if (arg0.equals(this.prev)) { ++ loadW(); ++ load(Jukebox.SKIP_TO_INDEX, "index=" + (jukebox.getCurrentIndex() - 1)); // TODO // MAYBE // BETTER // ? ++ } else if (arg0.equals(this.next)) { ++ loadW(); ++ load(Jukebox.SKIP_TO_INDEX, "index=" + (jukebox.getCurrentIndex() + 1)); ++ } else if (arg0.equals(this.shuffle)) { ++ load(Jukebox.SHUFFLE_PLAYLIST); ++ load(); ++ } ++ } ++ ++ @Override ++ public void onProgressChanged(SeekBar arg0, int arg1, boolean arg2){ ++ } ++ ++ @Override ++ public void onStartTrackingTouch(SeekBar arg0){ ++ } ++ ++ @Override ++ public void onStopTrackingTouch(SeekBar arg0){ ++ load(Jukebox.SET_GAIN, "gain=" + ((double) arg0.getProgress() / 100)); ++ } ++ ++ private class SongListAdapter extends ArrayAdapter<MusicDirectory.Entry>{ ++ ++ public SongListAdapter(List<MusicDirectory.Entry> entries){ ++ super(JukeboxActivity.this, android.R.layout.simple_list_item_1, entries); ++ } ++ ++ @Override ++ public View getView(int position, View convertView, ViewGroup parent){ ++ JukeboxSongView view; ++ if (convertView != null && convertView instanceof JukeboxSongView) { ++ view = (JukeboxSongView) convertView; ++ } else { ++ view = new JukeboxSongView(JukeboxActivity.this); ++ } ++ MusicDirectory.Entry entry = getItem(position); ++ ++ if (position == jukebox.getCurrentIndex()) { ++ view.setSong(entry, false, true); ++ } else view.setSong(entry, false, false); ++ return view; ++ } ++ } ++ ++ @Override ++ public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3){ ++ load(Jukebox.SKIP_TO_INDEX, "index=" + arg3); ++ } ++ ++ public void loadW(){ ++ if (!Util.isOffline(JukeboxActivity.this)) { ++ MusicService musicService = MusicServiceFactory.getMusicService(JukeboxActivity.this); ++ jukebox.lastAction = Jukebox.GET; ++ try { ++ jukebox = musicService.getJukebox(jukebox, JukeboxActivity.this, this, ""); ++ } catch (Exception e) { ++ // TODO Auto-generated catch block ++ e.printStackTrace(); ++ } ++ } ++ } ++ ++} +Index: src/net/sourceforge/subsonic/androidapp/activity/SelectAlbumActivity.java +=================================================================== +--- src/net/sourceforge/subsonic/androidapp/activity/SelectAlbumActivity.java (revision 2441) ++++ src/net/sourceforge/subsonic/androidapp/activity/SelectAlbumActivity.java (working copy) +@@ -252,6 +252,9 @@ + case R.id.album_menu_pin: + downloadRecursively(entry.getId(), true, true, false); + break; ++ case R.id.album_menu_jukebox_add: ++ addToJukebox(entry.getId()); ++ break; + case R.id.song_menu_play_now: + getDownloadService().download(songs, false, true, true); + break; +@@ -261,6 +264,8 @@ + case R.id.song_menu_play_last: + getDownloadService().download(songs, false, false, false); + break; ++ case R.id.song_menu_jukebox_add: ++ addToJukebox(entry); + default: + return super.onContextItemSelected(menuItem); + } +Index: src/net/sourceforge/subsonic/androidapp/activity/SelectArtistActivity.java +=================================================================== +--- src/net/sourceforge/subsonic/androidapp/activity/SelectArtistActivity.java (revision 2441) ++++ src/net/sourceforge/subsonic/androidapp/activity/SelectArtistActivity.java (working copy) +@@ -210,6 +210,9 @@ + case R.id.artist_menu_pin: + downloadRecursively(artist.getId(), true, true, false); + break; ++ case R.id.artist_menu_jukebox_add: ++ addToJukebox(artist.getId()); ++ break; + default: + return super.onContextItemSelected(menuItem); + } +Index: src/net/sourceforge/subsonic/androidapp/activity/SubsonicTabActivity.java +=================================================================== +--- src/net/sourceforge/subsonic/androidapp/activity/SubsonicTabActivity.java (revision 2441) ++++ src/net/sourceforge/subsonic/androidapp/activity/SubsonicTabActivity.java (working copy) +@@ -23,6 +23,16 @@ + import java.util.LinkedList; + import java.util.List; + ++import net.sourceforge.subsonic.androidapp.R; ++import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; ++import net.sourceforge.subsonic.androidapp.service.DownloadService; ++import net.sourceforge.subsonic.androidapp.service.DownloadServiceImpl; ++import net.sourceforge.subsonic.androidapp.service.MusicService; ++import net.sourceforge.subsonic.androidapp.service.MusicServiceFactory; ++import net.sourceforge.subsonic.androidapp.util.Constants; ++import net.sourceforge.subsonic.androidapp.util.ImageLoader; ++import net.sourceforge.subsonic.androidapp.util.ModalBackgroundTask; ++import net.sourceforge.subsonic.androidapp.util.Util; + import android.app.Activity; + import android.content.Context; + import android.content.Intent; +@@ -39,16 +49,6 @@ + import android.view.View; + import android.view.Window; + import android.widget.TextView; +-import net.sourceforge.subsonic.androidapp.R; +-import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; +-import net.sourceforge.subsonic.androidapp.service.DownloadService; +-import net.sourceforge.subsonic.androidapp.service.DownloadServiceImpl; +-import net.sourceforge.subsonic.androidapp.service.MusicService; +-import net.sourceforge.subsonic.androidapp.service.MusicServiceFactory; +-import net.sourceforge.subsonic.androidapp.util.Constants; +-import net.sourceforge.subsonic.androidapp.util.ImageLoader; +-import net.sourceforge.subsonic.androidapp.util.ModalBackgroundTask; +-import net.sourceforge.subsonic.androidapp.util.Util; + + /** + * @author Sindre Mehus +@@ -63,6 +63,7 @@ + private View musicButton; + private View searchButton; + private View playlistButton; ++ private View jukeboxButton; + private View nowPlayingButton; + + @Override +@@ -118,6 +119,14 @@ + Util.startActivityWithoutTransition(SubsonicTabActivity.this, intent); + } + }); ++ ++ jukeboxButton = findViewById(R.id.button_bar_jukebox); ++ jukeboxButton.setOnClickListener(new View.OnClickListener() { ++ @Override ++ public void onClick(View view) { ++ Util.startActivityWithoutTransition(SubsonicTabActivity.this, JukeboxActivity.class); ++ } ++ }); + + nowPlayingButton = findViewById(R.id.button_bar_now_playing); + nowPlayingButton.setOnClickListener(new View.OnClickListener() { +@@ -225,6 +234,22 @@ + int visibility = Util.isOffline(this) ? View.GONE : View.VISIBLE; + searchButton.setVisibility(visibility); + playlistButton.setVisibility(visibility); ++ jukeboxButton.setVisibility(visibility); ++ /* ++ * The following block would check for valid Permissions ++ * but it is called too often -> network traffic/ problems ? ++ * ++ MusicService musicService = MusicServiceFactory.getMusicService(this); ++ try { ++ User user = musicService.getUser(this, null); ++ if(!user.isJukeboxControl()){ ++ jukeboxButton.setVisibility(View.GONE); ++ } ++ } catch (Exception e) { ++ // TODO Auto-generated catch block ++ e.printStackTrace(); ++ } ++ */ + } + + public void setProgressVisible(boolean visible) { +@@ -363,5 +388,67 @@ + } + } + } ++ ++ protected void addToJukebox(String id) { ++ ++ List<MusicDirectory.Entry> entries = new LinkedList<MusicDirectory.Entry>(); ++ MusicService musicService = MusicServiceFactory.getMusicService(this); ++ MusicDirectory root; ++ ++ try { ++ root = musicService.getMusicDirectory(id, false, SubsonicTabActivity.this, null); ++ collectEntries(root, entries); ++ } catch (Exception e) { ++ // TODO Auto-generated catch block ++ e.printStackTrace(); ++ } ++ ++ ++ StringBuilder build = new StringBuilder(); ++ build.append("action=add"); ++ for(MusicDirectory.Entry entry2: entries){ ++ build.append("&id="); ++ build.append(entry2.getId()); ++ } ++ try { ++ musicService.getJukebox(null, this, null, build.toString()); ++ } catch (Exception e) { ++ // TODO Auto-generated catch block ++ e.printStackTrace(); ++ } ++ ++ } ++ ++ protected void addToJukebox(MusicDirectory.Entry entry) { ++ try { ++ MusicService musicService = MusicServiceFactory.getMusicService(this); ++ musicService.getJukebox(null, this, null, "action=add&id="+entry.getId()); ++ } catch (Exception e) { ++ // TODO Auto-generated catch block ++ e.printStackTrace(); ++ } ++ } ++ ++ private List<MusicDirectory.Entry> collectEntries(MusicDirectory parent, List<MusicDirectory.Entry> songs){ ++ for (MusicDirectory.Entry song : parent.getChildren(false, true)) { ++ if (!song.isVideo()) { ++ songs.add(song); ++ } ++ } ++ for (MusicDirectory.Entry dir : parent.getChildren(true, false)) { ++ MusicService musicService = MusicServiceFactory.getMusicService(SubsonicTabActivity.this); ++ try { ++ collectEntries(musicService.getMusicDirectory(dir.getId(), false, SubsonicTabActivity.this, null), songs); ++ } catch (Exception e) { ++ // TODO Auto-generated catch block ++ e.printStackTrace(); ++ } ++ } ++ ++ return songs; ++ } ++ ++ ++ + } + +Index: src/net/sourceforge/subsonic/androidapp/domain/Jukebox.java +=================================================================== +--- src/net/sourceforge/subsonic/androidapp/domain/Jukebox.java (revision 0) ++++ src/net/sourceforge/subsonic/androidapp/domain/Jukebox.java (revision 0) +@@ -0,0 +1,113 @@ ++/* ++ This file is part of Subsonic. ++ ++ Subsonic 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. ++ ++ Subsonic 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 Subsonic. If not, see <http://www.gnu.org/licenses/>. ++ ++ Copyright 2009 (C) Sindre Mehus ++ */ ++ ++package net.sourceforge.subsonic.androidapp.domain; ++ ++import java.io.Serializable; ++import java.util.List; ++ ++/** ++ * @author meld0 ++ * ++ */ ++public class Jukebox extends MusicDirectory implements Serializable{ ++ ++ public String lastAction; ++ ++ //get, start, stop, skip, add, clear, remove, shuffle, setGain ++ public static final String GET = "get"; ++ public static final String START_PLAYBACK = "start"; ++ public static final String STOP_PLAYBACK = "stop"; ++ public static final String SKIP_TO_INDEX = "skip"; ++ public static final String ADD_TO_PLAYLIST = "add"; ++ public static final String CLEAR_PLAYLIST = "clear"; ++ public static final String REMOVE_ITEM = "remove"; ++ public static final String SHUFFLE_PLAYLIST = "shuffle"; ++ public static final String SET_GAIN = "setGain"; ++ ++ public Jukebox() { ++ super(); ++ } ++ ++ public boolean isPlaying() { ++ return playing; ++ } ++ ++ public void setPlaying(boolean playing) { ++ this.playing = playing; ++ } ++ ++ public int getGain() { ++ return gain; ++ } ++ ++ public void setGain(int gain) { ++ this.gain = gain; ++ } ++ ++ public int getCurrentIndex() { ++ return currentIndex; ++ } ++ ++ public void setCurrentIndex(int currentIndex) { ++ this.currentIndex = currentIndex; ++ } ++ ++ public boolean isAvailable() { ++ return available; ++ } ++ ++ public boolean isSuccess() { ++ return success; ++ } ++ ++ public void setAvailable(boolean available) { ++ this.available = available; ++ } ++ ++ public void setSuccess(boolean success) { ++ this.success = success; ++ } ++ ++ public boolean isNewPlaylist() { ++ return newPlaylist; ++ } ++ ++ public void setNewPlaylist(boolean newPlaylist) { ++ this.newPlaylist = newPlaylist; ++ } ++ ++ private boolean playing = false; ++ private int gain = 0; ++ private int currentIndex = 0; ++ private boolean available = false; ++ private boolean success = false; ++ private boolean newPlaylist = false; ++ ++ public void addChildren(List<Entry> children){ ++ super.addChildren(children); ++ } ++ ++ public void reset(){ ++ this.available = false; ++ this.success = false; ++ this.newPlaylist = false; ++ } ++ ++} +Index: src/net/sourceforge/subsonic/androidapp/domain/MusicDirectory.java +=================================================================== +--- src/net/sourceforge/subsonic/androidapp/domain/MusicDirectory.java (revision 2441) ++++ src/net/sourceforge/subsonic/androidapp/domain/MusicDirectory.java (working copy) +@@ -59,6 +59,13 @@ + } + return result; + } ++ ++ protected void addChildren(List<Entry> children){ ++ if(!children.isEmpty()) { ++ this.children.clear(); ++ this.children.addAll(children); ++ } ++ } + + public static class Entry implements Serializable { + private String id; +Index: src/net/sourceforge/subsonic/androidapp/domain/User.java +=================================================================== +--- src/net/sourceforge/subsonic/androidapp/domain/User.java (revision 0) ++++ src/net/sourceforge/subsonic/androidapp/domain/User.java (revision 0) +@@ -0,0 +1,43 @@ ++/* ++ This file is part of Subsonic. ++ ++ Subsonic 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. ++ ++ Subsonic 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 Subsonic. If not, see <http://www.gnu.org/licenses/>. ++ ++ Copyright 2009 (C) Sindre Mehus ++ */ ++ ++package net.sourceforge.subsonic.androidapp.domain; ++ ++import java.io.Serializable; ++ ++/** ++ * @author meld0 ++ * ++ */ ++public class User implements Serializable{ ++ ++ //TODO implement missing user attributes ++ ++ private boolean jukeboxControl; ++ ++ public boolean isJukeboxControl() { ++ return jukeboxControl; ++ } ++ ++ public void setJukeboxControl(boolean jukeboxControl) { ++ this.jukeboxControl = jukeboxControl; ++ } ++ ++ ++} +Index: src/net/sourceforge/subsonic/androidapp/service/CachedMusicService.java +=================================================================== +--- src/net/sourceforge/subsonic/androidapp/service/CachedMusicService.java (revision 2441) ++++ src/net/sourceforge/subsonic/androidapp/service/CachedMusicService.java (working copy) +@@ -21,9 +21,11 @@ + import android.content.Context; + import android.graphics.Bitmap; + import net.sourceforge.subsonic.androidapp.domain.Indexes; ++import net.sourceforge.subsonic.androidapp.domain.Jukebox; + import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; + import net.sourceforge.subsonic.androidapp.domain.MusicFolder; + import net.sourceforge.subsonic.androidapp.domain.Playlist; ++import net.sourceforge.subsonic.androidapp.domain.User; + import net.sourceforge.subsonic.androidapp.domain.Version; + import net.sourceforge.subsonic.androidapp.domain.SearchResult; + import net.sourceforge.subsonic.androidapp.domain.SearchCritera; +@@ -199,4 +201,17 @@ + restUrl = newUrl; + } + } ++ ++ @Override ++ public Jukebox getJukebox(Jukebox jukebox, Context context, ++ ProgressListener progressListener, String action) throws Exception { ++ return musicService.getJukebox(jukebox, context, progressListener, action); ++ } ++ ++ @Override ++ public User getUser(Context context, ProgressListener progressListener) ++ throws Exception { ++ return musicService.getUser(context, progressListener); ++ } ++ + } +Index: src/net/sourceforge/subsonic/androidapp/service/MusicService.java +=================================================================== +--- src/net/sourceforge/subsonic/androidapp/service/MusicService.java (revision 2441) ++++ src/net/sourceforge/subsonic/androidapp/service/MusicService.java (working copy) +@@ -21,9 +21,11 @@ + import android.content.Context; + import android.graphics.Bitmap; + import net.sourceforge.subsonic.androidapp.domain.Indexes; ++import net.sourceforge.subsonic.androidapp.domain.Jukebox; + import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; + import net.sourceforge.subsonic.androidapp.domain.MusicFolder; + import net.sourceforge.subsonic.androidapp.domain.Playlist; ++import net.sourceforge.subsonic.androidapp.domain.User; + import net.sourceforge.subsonic.androidapp.domain.Version; + import net.sourceforge.subsonic.androidapp.domain.SearchResult; + import net.sourceforge.subsonic.androidapp.domain.SearchCritera; +@@ -74,4 +76,8 @@ + Version getLatestVersion(Context context, ProgressListener progressListener) throws Exception; + + String getVideoUrl(Context context, String id); ++ ++ Jukebox getJukebox(Jukebox jukebox, Context context, ProgressListener progressListener , String action) throws Exception; ++ ++ User getUser(Context context, ProgressListener progressListener) throws Exception; + } +\ No newline at end of file +Index: src/net/sourceforge/subsonic/androidapp/service/RESTMusicService.java +=================================================================== +--- src/net/sourceforge/subsonic/androidapp/service/RESTMusicService.java (revision 2441) ++++ src/net/sourceforge/subsonic/androidapp/service/RESTMusicService.java (working copy) +@@ -28,6 +28,7 @@ + import android.util.Log; + import net.sourceforge.subsonic.androidapp.R; + import net.sourceforge.subsonic.androidapp.domain.Indexes; ++import net.sourceforge.subsonic.androidapp.domain.Jukebox; + import net.sourceforge.subsonic.androidapp.domain.Lyrics; + import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; + import net.sourceforge.subsonic.androidapp.domain.MusicFolder; +@@ -35,10 +36,12 @@ + import net.sourceforge.subsonic.androidapp.domain.SearchCritera; + import net.sourceforge.subsonic.androidapp.domain.SearchResult; + import net.sourceforge.subsonic.androidapp.domain.ServerInfo; ++import net.sourceforge.subsonic.androidapp.domain.User; + import net.sourceforge.subsonic.androidapp.domain.Version; + import net.sourceforge.subsonic.androidapp.service.parser.AlbumListParser; + import net.sourceforge.subsonic.androidapp.service.parser.ErrorParser; + import net.sourceforge.subsonic.androidapp.service.parser.IndexesParser; ++import net.sourceforge.subsonic.androidapp.service.parser.JukeboxParser; + import net.sourceforge.subsonic.androidapp.service.parser.LicenseParser; + import net.sourceforge.subsonic.androidapp.service.parser.LyricsParser; + import net.sourceforge.subsonic.androidapp.service.parser.MusicDirectoryParser; +@@ -48,6 +51,7 @@ + import net.sourceforge.subsonic.androidapp.service.parser.RandomSongsParser; + import net.sourceforge.subsonic.androidapp.service.parser.SearchResult2Parser; + import net.sourceforge.subsonic.androidapp.service.parser.SearchResultParser; ++import net.sourceforge.subsonic.androidapp.service.parser.UserParser; + import net.sourceforge.subsonic.androidapp.service.parser.VersionParser; + import net.sourceforge.subsonic.androidapp.service.ssl.SSLSocketFactory; + import net.sourceforge.subsonic.androidapp.service.ssl.TrustSelfSignedStrategy; +@@ -195,6 +199,53 @@ + Util.close(reader); + } + } ++ ++ public Jukebox getJukebox(Jukebox jukebox, Context context, ProgressListener progressListener, String action) throws Exception { ++ ++ List<String> parameterNames = new ArrayList<String>(); ++ List<Object> parameterValues = new ArrayList<Object>(); ++ if(jukebox != null){ ++ parameterNames.add("action"); ++ parameterValues.add(jukebox.lastAction); ++ } ++ if(!action.isEmpty()){ ++ String[] names_values = action.split("&"); ++ for(String name_value: names_values){ ++ String[] pair = name_value.split("="); ++ parameterNames.add(pair[0]); ++ parameterValues.add(pair[1]); ++ } ++ } ++ ++ Reader reader = getReader(context, progressListener, "jukeboxControl", null, parameterNames, parameterValues); ++ ++ try { ++ return new JukeboxParser(context).parse(jukebox, reader, progressListener); ++ } finally { ++ Util.close(reader); ++ } ++ } ++ ++ public User getUser(Context context, ProgressListener progressListener) throws Exception { ++ ++ SharedPreferences prefs = Util.getPreferences(context); ++ int instance = prefs.getInt(Constants.PREFERENCES_KEY_SERVER_INSTANCE, 1); ++ String username = prefs.getString(Constants.PREFERENCES_KEY_USERNAME + instance, null); ++ ++ List<String> parameterNames = new ArrayList<String>(); ++ List<Object> parameterValues = new ArrayList<Object>(); ++ parameterNames.add("username"); ++ parameterValues.add(username); ++ ++ Reader reader = getReader(context, progressListener, "getUser", null, parameterNames, parameterValues); ++ ++ try { ++ return new UserParser(context).parse(reader, progressListener); ++ } finally { ++ Util.close(reader); ++ } ++ } ++ + + @Override + public Indexes getIndexes(String musicFolderId, boolean refresh, Context context, ProgressListener progressListener) throws Exception { +Index: src/net/sourceforge/subsonic/androidapp/service/parser/AbstractParser.java +=================================================================== +--- src/net/sourceforge/subsonic/androidapp/service/parser/AbstractParser.java (revision 2441) ++++ src/net/sourceforge/subsonic/androidapp/service/parser/AbstractParser.java (working copy) +@@ -97,6 +97,11 @@ + String s = get(name); + return s == null ? null : Long.valueOf(s); + } ++ ++ protected Double getDouble(String name) { ++ String s = get(name); ++ return s == null ? null : Double.valueOf(s); ++ } + + protected void init(Reader reader) throws Exception { + parser = Xml.newPullParser(); +Index: src/net/sourceforge/subsonic/androidapp/service/parser/JukeboxParser.java +=================================================================== +--- src/net/sourceforge/subsonic/androidapp/service/parser/JukeboxParser.java (revision 0) ++++ src/net/sourceforge/subsonic/androidapp/service/parser/JukeboxParser.java (revision 0) +@@ -0,0 +1,86 @@ ++/* ++ This file is part of Subsonic. ++ ++ Subsonic 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. ++ ++ Subsonic 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 Subsonic. If not, see <http://www.gnu.org/licenses/>. ++ ++ Copyright 2009 (C) Sindre Mehus ++ */ ++ ++package net.sourceforge.subsonic.androidapp.service.parser; ++ ++import java.io.Reader; ++ ++import net.sourceforge.subsonic.androidapp.R; ++import net.sourceforge.subsonic.androidapp.domain.Jukebox; ++import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; ++import net.sourceforge.subsonic.androidapp.util.ProgressListener; ++ ++import org.xmlpull.v1.XmlPullParser; ++ ++import android.content.Context; ++ ++/** ++ * @author meld0 ++ * ++ */ ++public class JukeboxParser extends MusicDirectoryEntryParser { ++ ++ public JukeboxParser(Context context) { ++ super(context); ++ } ++ ++ public Jukebox parse(Jukebox jukebox, Reader reader, ++ ProgressListener progressListener) throws Exception { ++ updateProgress(progressListener, R.string.parser_reading); ++ ++ if (jukebox == null) ++ jukebox = new Jukebox(); ++ jukebox.reset(); ++ init(reader); ++ jukebox.setAvailable(true); ++ ++ MusicDirectory dir = new MusicDirectory(); ++ ++ int eventType; ++ do { ++ eventType = nextParseEvent(); ++ if (eventType == XmlPullParser.START_TAG) { ++ String name = getElementName(); ++ if ("entry".equals(name)) { ++ dir.addChild(parseEntry()); ++ jukebox.setNewPlaylist(true); ++ } else if ("jukeboxPlaylist".equals(name)) { ++ jukebox.setPlaying(getBoolean("playing")); ++ jukebox.setGain((int) (getDouble("gain") * 100)); ++ jukebox.setCurrentIndex(getInteger("currentIndex")); ++ } else if ("subsonic-response".equals(name)) { ++ if (get("status").equals("ok")) ++ jukebox.setSuccess(true); ++ } else if ("error".equals(name)) { ++ handleError(); ++ } ++ } ++ } while (eventType != XmlPullParser.END_DOCUMENT); ++ ++ if(jukebox.isNewPlaylist()) { ++ jukebox.addChildren(dir.getChildren()); ++ } ++ ++ validate(); ++ updateProgress(progressListener, R.string.parser_reading_done); ++ ++ return jukebox; ++ } ++ ++} +Index: src/net/sourceforge/subsonic/androidapp/service/parser/UserParser.java +=================================================================== +--- src/net/sourceforge/subsonic/androidapp/service/parser/UserParser.java (revision 0) ++++ src/net/sourceforge/subsonic/androidapp/service/parser/UserParser.java (revision 0) +@@ -0,0 +1,67 @@ ++/* ++ This file is part of Subsonic. ++ ++ Subsonic 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. ++ ++ Subsonic 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 Subsonic. If not, see <http://www.gnu.org/licenses/>. ++ ++ Copyright 2009 (C) Sindre Mehus ++ */ ++ ++package net.sourceforge.subsonic.androidapp.service.parser; ++ ++import java.io.Reader; ++ ++import net.sourceforge.subsonic.androidapp.R; ++import net.sourceforge.subsonic.androidapp.domain.User; ++import net.sourceforge.subsonic.androidapp.util.ProgressListener; ++ ++import org.xmlpull.v1.XmlPullParser; ++ ++import android.content.Context; ++ ++/** ++ * @author meld0 ++ * ++ */ ++public class UserParser extends AbstractParser{ ++ ++ public UserParser(Context context) { ++ super(context); ++ } ++ ++ public User parse(Reader reader, ProgressListener progressListener) throws Exception { ++ updateProgress(progressListener, R.string.parser_reading); ++ init(reader); ++ ++ User user = new User(); ++ int eventType; ++ do { ++ eventType = nextParseEvent(); ++ if (eventType == XmlPullParser.START_TAG) { ++ String name = getElementName(); ++ if ("user".equals(name)) { ++ user.setJukeboxControl(getBoolean("jukeboxRole")); ++ } else if ("error".equals(name)) { ++ handleError(); ++ } ++ } ++ } while (eventType != XmlPullParser.END_DOCUMENT); ++ ++ validate(); ++ updateProgress(progressListener, R.string.parser_reading_done); ++ ++ return user; ++ } ++ ++ ++} +Index: src/net/sourceforge/subsonic/androidapp/util/JukeboxSongView.java +=================================================================== +--- src/net/sourceforge/subsonic/androidapp/util/JukeboxSongView.java (revision 0) ++++ src/net/sourceforge/subsonic/androidapp/util/JukeboxSongView.java (revision 0) +@@ -0,0 +1,88 @@ ++/* ++ This file is part of Subsonic. ++ ++ Subsonic 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. ++ ++ Subsonic 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 Subsonic. If not, see <http://www.gnu.org/licenses/>. ++ ++ Copyright 2009 (C) Sindre Mehus ++ */ ++ ++package net.sourceforge.subsonic.androidapp.util; ++ ++import net.sourceforge.subsonic.androidapp.R; ++import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; ++import android.content.Context; ++import android.view.LayoutInflater; ++import android.view.View; ++import android.widget.CheckedTextView; ++import android.widget.LinearLayout; ++import android.widget.TextView; ++ ++/** ++ * @author meld0 ++ * ++ */ ++public class JukeboxSongView extends LinearLayout{ ++ ++ private CheckedTextView checkedTextView; ++ private TextView titleTextView; ++ private TextView artistTextView; ++ private TextView durationTextView; ++ private TextView statusTextView; ++ private MusicDirectory.Entry song; ++ ++ public JukeboxSongView(Context context) { ++ super(context); ++ LayoutInflater.from(context).inflate(R.layout.song_list_item, this, true); ++ ++ checkedTextView = (CheckedTextView) findViewById(R.id.song_check); ++ titleTextView = (TextView) findViewById(R.id.song_title); ++ artistTextView = (TextView) findViewById(R.id.song_artist); ++ durationTextView = (TextView) findViewById(R.id.song_duration); ++ statusTextView = (TextView) findViewById(R.id.song_status); ++ ++ } ++ ++ public void setSong(MusicDirectory.Entry song, boolean checkable, boolean playing) { ++ this.song = song; ++ StringBuilder artist = new StringBuilder(40); ++ ++ String bitRate = null; ++ if (song.getBitRate() != null) { ++ bitRate = String.format(getContext().getString(R.string.song_details_kbps), song.getBitRate()); ++ } ++ ++ String fileFormat = null; ++ if (song.getTranscodedSuffix() != null && !song.getTranscodedSuffix().equals(song.getSuffix())) { ++ fileFormat = String.format("%s > %s", song.getSuffix(), song.getTranscodedSuffix()); ++ } else { ++ fileFormat = song.getSuffix(); ++ } ++ ++ artist.append(song.getArtist()).append(" (") ++ .append(String.format(getContext().getString(R.string.song_details_all), bitRate == null ? "" : bitRate, fileFormat)) ++ .append(")"); ++ ++ titleTextView.setText(song.getTitle()); ++ artistTextView.setText(artist); ++ durationTextView.setText(Util.formatDuration(song.getDuration())); ++ checkedTextView.setVisibility(checkable && !song.isVideo() ? View.VISIBLE : View.GONE); ++ ++ if(playing){ ++ titleTextView.setCompoundDrawablesWithIntrinsicBounds(R.drawable.stat_notify_playing, 0, 0, 0); ++ }else{ ++ titleTextView.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); ++ } ++ } ++ ++} |