ultrasonic-app-subsonic-and.../patches/jukebox-patch.txt

1235 lines
44 KiB
Plaintext

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:a="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);
+ }
+ }
+
+}