aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--subsonic-android/AndroidManifest.xml4
-rw-r--r--subsonic-android/res/drawable-hdpi-v4/ic_menu_chat_dark.pngbin0 -> 1401 bytes
-rw-r--r--subsonic-android/res/drawable-hdpi-v4/ic_menu_chat_light.pngbin0 -> 1374 bytes
-rw-r--r--subsonic-android/res/drawable-hdpi-v4/ic_menu_chat_send_dark.pngbin0 -> 1668 bytes
-rw-r--r--subsonic-android/res/drawable-hdpi-v4/ic_menu_chat_send_light.pngbin0 -> 1691 bytes
-rw-r--r--subsonic-android/res/layout/chat.xml46
-rw-r--r--subsonic-android/res/layout/chat_item.xml47
-rw-r--r--subsonic-android/res/layout/chat_item_reverse.xml50
-rw-r--r--subsonic-android/res/layout/main.xml6
-rw-r--r--subsonic-android/res/layout/main_buttons.xml12
-rw-r--r--subsonic-android/res/layout/select_genres.xml30
-rw-r--r--subsonic-android/res/layout/shuffle_dialog.xml10
-rw-r--r--subsonic-android/res/menu/chat.xml18
-rw-r--r--subsonic-android/res/menu/select_genres.xml18
-rw-r--r--subsonic-android/res/raw/changelog.xml33
-rw-r--r--subsonic-android/res/values-ru/strings.xml17
-rw-r--r--subsonic-android/res/values/arrays.xml4
-rw-r--r--subsonic-android/res/values/attrs.xml2
-rw-r--r--subsonic-android/res/values/strings.xml23
-rw-r--r--subsonic-android/res/values/themes.xml14
-rw-r--r--subsonic-android/res/xml/settings.xml125
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/activity/EqualizerActivity.java12
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/activity/MainActivity.java54
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/activity/SettingsActivity.java221
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/activity/SubsonicActivity.java60
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/domain/ChatMessage.java51
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/domain/Genre.java29
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/domain/MusicDirectory.java95
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/domain/Share.java140
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/fragments/ChatFragment.java223
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/fragments/DownloadFragment.java21
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/fragments/MainFragment.java71
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/fragments/SelectArtistFragment.java6
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java17
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/fragments/SelectGenreFragment.java141
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/fragments/SelectPlaylistFragment.java6
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/fragments/SubsonicFragment.java65
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/service/CachedMusicService.java38
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/service/DownloadServiceImpl.java21
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/service/MusicService.java13
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/service/OfflineMusicService.java54
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/service/RESTMusicService.java97
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/service/parser/ChatMessageParser.java67
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/service/parser/GenreParser.java122
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/service/parser/IndexesParser.java14
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/service/parser/MusicDirectoryEntryParser.java14
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/service/parser/MusicDirectoryParser.java18
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/service/parser/ShareParser.java77
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/service/parser/StarredListParser.java2
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/updates/Updater.java2
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/updates/Updater403.java (renamed from subsonic-android/src/github/daneren2005/dsub/updates/Updater373.java)12
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/util/CacheCleaner.java2
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/util/Constants.java20
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/util/ErrorDialog.java12
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/util/FileUtil.java4
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/util/ImageLoader.java17
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/util/Util.java60
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/view/AlbumListAdapter.java11
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/view/ArtistEntryView.java83
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/view/ChatAdapter.java100
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/view/EntryAdapter.java15
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/view/GenreAdapter.java59
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/view/GenreView.java53
-rw-r--r--subsonic-android/src/github/daneren2005/dsub/view/SongView.java17
64 files changed, 2395 insertions, 280 deletions
diff --git a/subsonic-android/AndroidManifest.xml b/subsonic-android/AndroidManifest.xml
index 43cff05f..76677dbe 100644
--- a/subsonic-android/AndroidManifest.xml
+++ b/subsonic-android/AndroidManifest.xml
@@ -2,8 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="github.daneren2005.dsub"
android:installLocation="internalOnly"
- android:versionCode="48"
- android:versionName="4.0.0">
+ android:versionCode="52"
+ android:versionName="4.0.3">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
diff --git a/subsonic-android/res/drawable-hdpi-v4/ic_menu_chat_dark.png b/subsonic-android/res/drawable-hdpi-v4/ic_menu_chat_dark.png
new file mode 100644
index 00000000..be04b06e
--- /dev/null
+++ b/subsonic-android/res/drawable-hdpi-v4/ic_menu_chat_dark.png
Binary files differ
diff --git a/subsonic-android/res/drawable-hdpi-v4/ic_menu_chat_light.png b/subsonic-android/res/drawable-hdpi-v4/ic_menu_chat_light.png
new file mode 100644
index 00000000..3f58695c
--- /dev/null
+++ b/subsonic-android/res/drawable-hdpi-v4/ic_menu_chat_light.png
Binary files differ
diff --git a/subsonic-android/res/drawable-hdpi-v4/ic_menu_chat_send_dark.png b/subsonic-android/res/drawable-hdpi-v4/ic_menu_chat_send_dark.png
new file mode 100644
index 00000000..bd37dc59
--- /dev/null
+++ b/subsonic-android/res/drawable-hdpi-v4/ic_menu_chat_send_dark.png
Binary files differ
diff --git a/subsonic-android/res/drawable-hdpi-v4/ic_menu_chat_send_light.png b/subsonic-android/res/drawable-hdpi-v4/ic_menu_chat_send_light.png
new file mode 100644
index 00000000..0c870d2c
--- /dev/null
+++ b/subsonic-android/res/drawable-hdpi-v4/ic_menu_chat_send_light.png
Binary files differ
diff --git a/subsonic-android/res/layout/chat.xml b/subsonic-android/res/layout/chat.xml
new file mode 100644
index 00000000..fdeb5b36
--- /dev/null
+++ b/subsonic-android/res/layout/chat.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical" >
+
+ <include layout="@layout/tab_progress" />
+
+ <ListView
+ android:id="@+id/chat_entries"
+ android:layout_width="fill_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1.0"
+ android:textFilterEnabled="true" />
+
+ <LinearLayout
+ android:layout_height="4dip"
+ android:layout_width="fill_parent"
+ android:layout_marginTop="4dip"/>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:gravity="bottom" >
+
+ <EditText
+ android:id="@+id/chat_edittext"
+ android:layout_width="0dip"
+ android:layout_height="40dip"
+ android:layout_weight="1"
+ android:autoLink="all"
+ android:hint="@string/chat.send_a_message"
+ android:inputType="textEmailAddress|textMultiLine"
+ android:linksClickable="true"
+ android:paddingBottom="10dip"
+ android:paddingTop="10dip" />
+
+ <ImageButton
+ android:id="@+id/chat_send"
+ android:layout_width="60dip"
+ android:layout_height="40dip"
+ android:src="?attr/chat_send" />
+
+ </LinearLayout>
+</LinearLayout> \ No newline at end of file
diff --git a/subsonic-android/res/layout/chat_item.xml b/subsonic-android/res/layout/chat_item.xml
new file mode 100644
index 00000000..b44631d1
--- /dev/null
+++ b/subsonic-android/res/layout/chat_item.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+
+ <TextView
+ android:id="@+id/chat_username"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="6dip"
+ android:layout_marginRight="6dip"
+ android:ellipsize="marquee"
+ android:singleLine="true"
+ android:text="User"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?android:textColorSecondary"/>
+
+ <LinearLayout
+ android:id="@+id/chat_message_layout"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="2dip"
+ android:orientation="horizontal" >
+
+ <TextView
+ android:id="@+id/chat_time"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="6dip"
+ android:singleLine="true"
+ android:text="00:00"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <TextView
+ android:id="@+id/chat_message"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="6dip"
+ android:layout_marginRight="6dip"
+ android:autoLink="all"
+ android:linksClickable="true"
+ android:singleLine="false"
+ android:text="Message Text Goes Here"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+ </LinearLayout>
+</LinearLayout> \ No newline at end of file
diff --git a/subsonic-android/res/layout/chat_item_reverse.xml b/subsonic-android/res/layout/chat_item_reverse.xml
new file mode 100644
index 00000000..62695521
--- /dev/null
+++ b/subsonic-android/res/layout/chat_item_reverse.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+
+ <TextView
+ android:id="@+id/chat_username"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="6dip"
+ android:gravity="right"
+ android:layout_gravity="right"
+ android:ellipsize="marquee"
+ android:singleLine="true"
+ android:text="User"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?android:textColorSecondary"/>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="2dip"
+ android:orientation="horizontal"
+ android:layout_gravity="right" >
+
+ <TextView
+ android:id="@+id/chat_time"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="6dip"
+ android:singleLine="true"
+ android:gravity="right"
+ android:text="00:00"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <TextView
+ android:id="@+id/chat_message"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="6dip"
+ android:layout_marginRight="6dip"
+ android:autoLink="all"
+ android:linksClickable="true"
+ android:singleLine="false"
+ android:gravity="right"
+ android:text="Chat message"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+ </LinearLayout>
+</LinearLayout> \ No newline at end of file
diff --git a/subsonic-android/res/layout/main.xml b/subsonic-android/res/layout/main.xml
index fe79716c..a2a46a1e 100644
--- a/subsonic-android/res/layout/main.xml
+++ b/subsonic-android/res/layout/main.xml
@@ -25,10 +25,10 @@
<ImageView
android:id="@+id/album_art"
- android:layout_width="wrap_content"
- android:layout_height="fill_parent"
+ android:layout_width="50dip"
+ android:layout_height="50dip"
android:layout_gravity="left|center"
- android:scaleType="fitXY"
+ android:scaleType="fitStart"
android:src="@drawable/unknown_album"/>
<LinearLayout
diff --git a/subsonic-android/res/layout/main_buttons.xml b/subsonic-android/res/layout/main_buttons.xml
index e8be57f0..1e60838d 100644
--- a/subsonic-android/res/layout/main_buttons.xml
+++ b/subsonic-android/res/layout/main_buttons.xml
@@ -128,6 +128,18 @@
android:paddingLeft="6dip"
android:paddingRight="6dip"
android:minHeight="50dip"/>
+ <TextView
+ android:id="@+id/main_albums_genres"
+ android:text="@string/main.albums_genres"
+ android:drawableRight="@drawable/list_item_more"
+ android:drawablePadding="6dip"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:gravity="center_vertical"
+ android:paddingLeft="6dip"
+ android:paddingRight="6dip"
+ android:minHeight="50dip"/>
<TextView
android:id="@+id/main_albums_random"
android:text="@string/main.albums_random"
diff --git a/subsonic-android/res/layout/select_genres.xml b/subsonic-android/res/layout/select_genres.xml
new file mode 100644
index 00000000..95f9d415
--- /dev/null
+++ b/subsonic-android/res/layout/select_genres.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/select_genre_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical" >
+
+ <include layout="@layout/tab_progress" />
+
+ <TextView
+ android:id="@+id/select_genre_empty"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:padding="10dip"
+ android:text="@string/select_genre.empty"
+ android:visibility="gone" />
+
+ <ListView
+ android:id="@+id/select_genre_list"
+ android:layout_width="fill_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1.0"
+ android:textFilterEnabled="true"
+ android:fastScrollEnabled="true"/>
+ </LinearLayout>
+</FrameLayout> \ No newline at end of file
diff --git a/subsonic-android/res/layout/shuffle_dialog.xml b/subsonic-android/res/layout/shuffle_dialog.xml
index 2a21dc11..e78aba33 100644
--- a/subsonic-android/res/layout/shuffle_dialog.xml
+++ b/subsonic-android/res/layout/shuffle_dialog.xml
@@ -66,6 +66,14 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginLeft="4dp"
- android:hint="@string/shuffle.genre" />
+ android:hint="@string/shuffle.genre"/>
+
+ <Button
+ android:id="@+id/genre_combo"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginLeft="4dp"
+ android:text="@string/shuffle.genre"/>
</LinearLayout>
</LinearLayout> \ No newline at end of file
diff --git a/subsonic-android/res/menu/chat.xml b/subsonic-android/res/menu/chat.xml
new file mode 100644
index 00000000..e0f9a718
--- /dev/null
+++ b/subsonic-android/res/menu/chat.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:id="@+id/menu_refresh"
+ android:icon="@drawable/action_refresh"
+ android:title="@string/menu.refresh"
+ android:showAsAction="always|withText"/>
+
+ <item
+ android:id="@+id/menu_settings"
+ android:icon="@drawable/action_settings"
+ android:title="@string/menu.settings"/>
+
+ <item
+ android:id="@+id/menu_exit"
+ android:icon="@drawable/action_exit"
+ android:title="@string/menu.exit"/>
+</menu> \ No newline at end of file
diff --git a/subsonic-android/res/menu/select_genres.xml b/subsonic-android/res/menu/select_genres.xml
new file mode 100644
index 00000000..e0f9a718
--- /dev/null
+++ b/subsonic-android/res/menu/select_genres.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:id="@+id/menu_refresh"
+ android:icon="@drawable/action_refresh"
+ android:title="@string/menu.refresh"
+ android:showAsAction="always|withText"/>
+
+ <item
+ android:id="@+id/menu_settings"
+ android:icon="@drawable/action_settings"
+ android:title="@string/menu.settings"/>
+
+ <item
+ android:id="@+id/menu_exit"
+ android:icon="@drawable/action_exit"
+ android:title="@string/menu.exit"/>
+</menu> \ No newline at end of file
diff --git a/subsonic-android/res/raw/changelog.xml b/subsonic-android/res/raw/changelog.xml
index 18636dda..14f59866 100644
--- a/subsonic-android/res/raw/changelog.xml
+++ b/subsonic-android/res/raw/changelog.xml
@@ -1,7 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<changelog>
+ <release version="4.0.3" versioncode="52" releasedate="5/31/2013">
+ <change>Sort by disc number if specified in tags</change>
+ <change>Show starred artists in starred list</change>
+ <change>Change folder.jpg to albumart.jpg which galleries shouldn't display</change>
+ <change>Fix Show Album</change>
+ <change>Added support for server Ignored Articles (future server version) + defaults to server's defaults</change>
+ <change>On network error return to front of the app instead of exiting all the way</change>
+ <change>Fix occasional crash when going back into app after running for a while</change>
+ <change>Various minor bugfixes</change>
+ </release>
+ <release version="4.0.2" versioncode="51" releasedate="5/24/2013">
+ <change>Fix if you set chat refresh rate to 0, will just not refresh</change>
+ <change>Revert dark theme modification</change>
+ <change>New Theme called black which is the pure black background</change>
+ <change>Option to disable chat menu, need to exit app and reenter for now</change>
+ </release>
+
+ <release version="4.0.1" versioncode="50" releasedate="5/23/2013">
+ <change>New: Chat Tab (Set chat auto refresh rate from settings)</change>
+ <change>New: Dynamic servers, add as many, or remove all but the ones you are using</change>
+ <change>New: Added separate setting for songs to preload for Wifi/Mobile</change>
+ <change>Improvement: The infinite playlist while shuffling is now persistent between startups</change>
+ <change>Theme: White is now more white, got rid of blue text for white theme only</change>
+ <change>Theme: Black is now a flat black due to popular request</change>
+ <change>Theme: Apply the current theme to settings screen</change>
+ <change>Fix: Don't stretch album art on bottom of main tabs</change>
+ <change>Fix: Possible fix for some who were having crash on starting EQ</change>
+ </release>
+
<release version="4.0.0" versioncode="48" releasedate="5/16/2013">
- <change>Converted everything to fragments!</change>
+ <change>Converted everything to fragments!</change>
<change>Swipe to switch tabs</change>
<change>Breadcrumb trail when going down several levels</change>
<change>Require double tapping back to exit app</change>
@@ -13,5 +42,5 @@
<change>Added total time to playlist/album headers</change>
<change>Fixed a lot of the menu items not working when using search</change>
<change>Update to Light/Dark themes</change>
- </release>
+ </release>
</changelog> \ No newline at end of file
diff --git a/subsonic-android/res/values-ru/strings.xml b/subsonic-android/res/values-ru/strings.xml
index a74d7f61..73569d9d 100644
--- a/subsonic-android/res/values-ru/strings.xml
+++ b/subsonic-android/res/values-ru/strings.xml
@@ -307,6 +307,23 @@
<string name="util.bytes_format.kilobyte">0 КБ</string>
<string name="util.bytes_format.byte">0 Б</string>
+ <string name="button_bar.chat">Чат</string>
+ <string name="main.back_confirm">Нажмите "назад" еще раз для выхода</string>
+ <string name="download.playing_out_of">Воспроизведение: %1$d/%2$d</string>
+ <string name="settings.persistent_title">Постоянное уведомление</string>
+ <string name="settings.persistent_summary">Показывать уведомление даже во время паузы. Остановка воспроизведения уберет это уведомление.</string>
+ <string name="settings.gapless_playback">Непрерывное воспроизведение</string>
+ <string name="settings.gapless_playback_summary">Galaxy S3 может зависать или испытывать прочие трудности с момента начала непрерывного воспроизведения. Выключите эту функцию для исправления данной проблемы.</string>
+ <string name="settings.chat_refresh">Частота обновления чата (сек)</string>
+ <string name="settings.chat_enabled">Чат активен</string>
+ <string name="settings.chat_enabled_summary">Показывать или нет вкладку чата</string>
+ <string name="changelog_full_title">Журнал изменений</string>
+ <string name="changelog_title">Что нового</string>
+ <string name="changelog_ok_button">OK</string>
+ <string name="changelog_show_full">Еще…</string>
+ <string name="chat.send_a_message">Отправить сообщение</string>
+
+
<plurals name="select_album_n_songs">
<item quantity="zero">Нет композиций</item>
<item quantity="one">1 композиция</item>
diff --git a/subsonic-android/res/values/arrays.xml b/subsonic-android/res/values/arrays.xml
index 75ad2bea..80cc4fad 100644
--- a/subsonic-android/res/values/arrays.xml
+++ b/subsonic-android/res/values/arrays.xml
@@ -4,18 +4,22 @@
<string-array name="themeValues">
<item>light</item>
<item>dark</item>
+ <item>black</item>
<item>holo</item>
<item>light_fullscreen</item>
<item>dark_fullscreen</item>
+ <item>black_fullscreen</item>
<item>holo_fullscreen</item>
</string-array>
<string-array name="themeNames">
<item>@string/settings.theme_light</item>
<item>@string/settings.theme_dark</item>
+ <item>@string/settings.theme_black</item>
<item>@string/settings.theme_holo</item>
<item>@string/settings.theme_light_fullscreen</item>
<item>@string/settings.theme_dark_fullscreen</item>
+ <item>@string/settings.theme_black_fullscreen</item>
<item>@string/settings.theme_holo_fullscreen</item>
</string-array>
diff --git a/subsonic-android/res/values/attrs.xml b/subsonic-android/res/values/attrs.xml
index e5d8984f..8f669cd2 100644
--- a/subsonic-android/res/values/attrs.xml
+++ b/subsonic-android/res/values/attrs.xml
@@ -7,4 +7,6 @@
<attr name="media_button_repeat_off" format="reference"/>
<attr name="media_button_start" format="reference"/>
<attr name="media_button_stop" format="reference"/>
+ <attr name="chat" format="reference"/>
+ <attr name="chat_send" format="reference" />
</resources>
diff --git a/subsonic-android/res/values/strings.xml b/subsonic-android/res/values/strings.xml
index 7f03003e..b5e8f3e1 100644
--- a/subsonic-android/res/values/strings.xml
+++ b/subsonic-android/res/values/strings.xml
@@ -28,6 +28,7 @@
<string name="button_bar.search">Search</string>
<string name="button_bar.playlists">Playlists</string>
<string name="button_bar.now_playing">Playing</string>
+ <string name="button_bar.chat">Chat</string>
<string name="main.welcome_title">Welcome!</string>
<string name="main.welcome_text">Welcome to DSub! The app is currently configured to use the Subsonic demo server. After you\'ve
@@ -50,6 +51,7 @@
<string name="main.albums_highest">Top rated</string>
<string name="main.albums_starred">Starred</string>
<string name="main.albums_random">Random</string>
+ <string name="main.albums_genres">Genres</string>
<string name="main.back_confirm">Press back again to exit</string>
<string name="menu.search">Search</string>
@@ -113,6 +115,8 @@
<string name="select_album.donate_dialog_later">Later</string>
<string name="select_album.donate_dialog_0_trial_days_left">Trial period is over</string>
+ <string name="select_genre.empty">No genres found</string>
+
<string name="select_playlist.empty">No saved playlists on server</string>
<string name="download.empty">Playlist is empty</string>
@@ -175,15 +179,18 @@
<string name="settings.title">DSub settings</string>
<string name="settings.test_connection_title">Test connection</string>
+ <string name="settings.servers_add">Add Server</string>
+ <string name="settings.servers_remove">Remove Server</string>
<string name="settings.servers_title">Servers</string>
- <string name="settings.server_unused1">Unused 1</string>
- <string name="settings.server_unused2">Unused 2</string>
+ <string name="settings.server_unused">Unused</string>
<string name="settings.server_name">Name</string>
<string name="settings.server_address">Server address</string>
<string name="settings.server_username">Username</string>
<string name="settings.server_password">Password</string>
+ <string name="settings.server_open_browser">Open in browser</string>
<string name="settings.cache_title">Music cache</string>
- <string name="settings.preload">Songs to preload</string>
+ <string name="settings.preload_wifi">Songs to preload (Wifi)</string>
+ <string name="settings.preload_mobile">Songs to preload (Mobile)</string>
<string name="settings.cache_size">Cache size (MB)</string>
<string name="settings.cache_location">Cache location</string>
<string name="settings.cache_location_error">Invalid cache location. Using default.</string>
@@ -197,9 +204,11 @@
<string name="settings.theme_title">Theme</string>
<string name="settings.theme_light">Light</string>
<string name="settings.theme_dark">Dark</string>
+ <string name="settings.theme_black">Black</string>
<string name="settings.theme_holo">Holo</string>
<string name="settings.theme_light_fullscreen">Light Fullscreen</string>
<string name="settings.theme_dark_fullscreen">Dark Fullscreen</string>
+ <string name="settings.theme_black_fullscreen">Black Fullscreen</string>
<string name="settings.theme_holo_fullscreen">Holo Fullscreen</string>
<string name="settings.network_title">Network</string>
<string name="settings.max_bitrate_wifi">Max Audio bitrate - Wi-Fi</string>
@@ -270,10 +279,14 @@
<string name="settings.persistent_summary">Show the notification even after pausing. Press the stop button to clear it away.</string>
<string name="settings.gapless_playback">Gapless Playback</string>
<string name="settings.gapless_playback_summary">The Galaxy S3 seems to be experiencing freezes/other weird issue since the introduction of gapless playback. Turn this off to fix the issue.</string>
-
+ <string name="settings.chat_refresh">Chat Refresh Rate (Secs)</string>
+ <string name="settings.chat_enabled">Chat Enabled</string>
+ <string name="settings.chat_enabled_summary">Whether or not to display the chat tab. Restart app after changing.</string>
+
<string name="shuffle.startYear">Start Year:</string>
<string name="shuffle.endYear">End Year:</string>
<string name="shuffle.genre">Genre:</string>
+ <string name="shuffle.pick_genre">Pick a genre</string>
<string name="music_service.retry">A network error occurred. Retrying %1$d of %2$d.</string>
@@ -320,6 +333,8 @@
<string name="changelog_ok_button">OK</string>
<string name="changelog_show_full">More…</string>
+ <string name="chat.send_a_message">Send a message</string>
+
<string name="changelog_version_format" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">Version <xliff:g id="version_name">%s</xliff:g></string>
<plurals name="select_album_n_songs">
diff --git a/subsonic-android/res/values/themes.xml b/subsonic-android/res/values/themes.xml
index 2389365f..33dd2de7 100644
--- a/subsonic-android/res/values/themes.xml
+++ b/subsonic-android/res/values/themes.xml
@@ -3,8 +3,6 @@
<style name="Theme.DSub.Light" parent="Theme.Sherlock.Light">
<item name="actionBarStyle">@style/Widget.DSub.ActionBarStyle.Light</item>
<item name="android:actionBarStyle">@style/Widget.DSub.ActionBarStyle.Light</item>
- <item name="android:textColorSecondary">@color/cyan</item>
- <item name="android:windowBackground">@color/lightBackground</item>
<item name="offline_icon">@drawable/main_offline_light</item>
<item name="media_button_backward">@drawable/media_backward_light</item>
<item name="media_button_forward">@drawable/media_forward_light</item>
@@ -12,6 +10,8 @@
<item name="media_button_repeat_off">@drawable/media_repeat_off_light</item>
<item name="media_button_start">@drawable/media_start_light</item>
<item name="media_button_stop">@drawable/media_stop_light</item>
+ <item name="chat">@drawable/ic_menu_chat_light</item>
+ <item name="chat_send">@drawable/ic_menu_chat_send_light</item>
</style>
<style name="Theme.DSub.Dark" parent="Theme.Sherlock">
<item name="actionBarStyle">@style/Widget.DSub.ActionBarStyle.Dark</item>
@@ -24,6 +24,11 @@
<item name="media_button_repeat_off">@drawable/media_repeat_off</item>
<item name="media_button_start">@drawable/media_start</item>
<item name="media_button_stop">@drawable/media_stop</item>
+ <item name="chat">@drawable/ic_menu_chat_dark</item>
+ <item name="chat_send">@drawable/ic_menu_chat_send_dark</item>
+ </style>
+ <style name="Theme.DSub.Black" parent="Theme.DSub.Dark">
+ <item name="android:windowBackground">@android:color/black</item>
</style>
<style name="Theme.DSub.Holo" parent="Theme.Sherlock">
<item name="actionBarStyle">@style/Widget.DSub.ActionBarStyle.Holo</item>
@@ -37,6 +42,8 @@
<item name="media_button_repeat_off">@drawable/media_repeat_off</item>
<item name="media_button_start">@drawable/media_start</item>
<item name="media_button_stop">@drawable/media_stop</item>
+ <item name="chat">@drawable/ic_menu_chat_dark</item>
+ <item name="chat_send">@drawable/ic_menu_chat_send_dark</item>
</style>
<style name="Theme.DSub.Light.Fullscreen" parent="Theme.DSub.Light">
@@ -45,6 +52,9 @@
<style name="Theme.DSub.Dark.Fullscreen" parent="Theme.DSub.Dark">
<item name="android:windowFullscreen">true</item>
</style>
+ <style name="Theme.DSub.Black.Fullscreen" parent="Theme.DSub.Black">
+ <item name="android:windowFullscreen">true</item>
+ </style>
<style name="Theme.DSub.Holo.Fullscreen" parent="Theme.DSub.Holo">
<item name="android:windowFullscreen">true</item>
</style>
diff --git a/subsonic-android/res/xml/settings.xml b/subsonic-android/res/xml/settings.xml
index d21f928f..17a51621 100644
--- a/subsonic-android/res/xml/settings.xml
+++ b/subsonic-android/res/xml/settings.xml
@@ -4,102 +4,12 @@
android:title="@string/settings.title">
<PreferenceCategory
+ android:key="server"
android:title="@string/settings.servers_title">
- <PreferenceScreen
- android:key="server1">
-
- <EditTextPreference
- android:key="serverName1"
- android:title="@string/settings.server_name"
- android:defaultValue="Subsonic demo"/>
-
- <EditTextPreference
- android:key="serverUrl1"
- android:title="@string/settings.server_address"
- android:defaultValue="http://demo.subsonic.org"
- android:inputType="textUri"/>
-
- <EditTextPreference
- android:key="username1"
- android:title="@string/settings.server_username"
- android:defaultValue="android-guest"/>
-
- <EditTextPreference
- android:key="password1"
- android:title="@string/settings.server_password"
- android:password="true"
- android:defaultValue="guest"
- android:summary="****"/>
-
- <Preference
- android:key="testConnection1"
- android:title="@string/settings.test_connection_title"
- android:persistent="false"/>
-
- </PreferenceScreen>
-
- <PreferenceScreen
- android:key="server2">
-
- <EditTextPreference
- android:key="serverName2"
- android:title="@string/settings.server_name"
- android:defaultValue="@string/settings.server_unused1"/>
-
- <EditTextPreference
- android:key="serverUrl2"
- android:title="@string/settings.server_address"
- android:defaultValue="http://yourhost"
- android:inputType="textUri"/>
-
- <EditTextPreference
- android:key="username2"
- android:title="@string/settings.server_username"/>
-
- <EditTextPreference
- android:key="password2"
- android:title="@string/settings.server_password"
- android:password="true"
- android:summary="****"/>
-
- <Preference
- android:key="testConnection2"
- android:title="@string/settings.test_connection_title"
- android:persistent="false"/>
-
- </PreferenceScreen>
-
- <PreferenceScreen
- android:key="server3">
-
- <EditTextPreference
- android:key="serverName3"
- android:title="@string/settings.server_name"
- android:defaultValue="@string/settings.server_unused2"/>
-
- <EditTextPreference
- android:key="serverUrl3"
- android:title="@string/settings.server_address"
- android:defaultValue="http://yourhost"
- android:inputType="textUri"/>
-
- <EditTextPreference
- android:key="username3"
- android:title="@string/settings.server_username"/>
-
- <EditTextPreference
- android:key="password3"
- android:title="@string/settings.server_password"
- android:password="true"
- android:summary="****"/>
-
- <Preference
- android:key="testConnection3"
- android:title="@string/settings.test_connection_title"
- android:persistent="false"/>
-
- </PreferenceScreen>
+ <Preference
+ android:key="serverAdd"
+ android:title="@string/settings.servers_add"/>
</PreferenceCategory>
@@ -204,14 +114,37 @@
android:key="cacheLocation"/>
<ListPreference
- android:title="@string/settings.preload"
- android:key="preloadCount"
+ android:title="@string/settings.preload_wifi"
+ android:key="preloadCountWifi"
+ android:defaultValue="3"
+ android:entryValues="@array/preloadCountValues"
+ android:entries="@array/preloadCountNames"/>
+
+ <ListPreference
+ android:title="@string/settings.preload_mobile"
+ android:key="preloadCountMobile"
android:defaultValue="3"
android:entryValues="@array/preloadCountValues"
android:entries="@array/preloadCountNames"/>
</PreferenceCategory>
+ <PreferenceCategory
+ android:title="@string/button_bar.chat">
+
+ <CheckBoxPreference
+ android:title="@string/settings.chat_enabled"
+ android:summary="@string/settings.chat_enabled_summary"
+ android:key="chatEnabled"
+ android:defaultValue="true"/>
+
+ <EditTextPreference
+ android:title="@string/settings.chat_refresh"
+ android:key="chatRefreshRate"
+ android:defaultValue="30"
+ android:digits="0123456789"/>
+ </PreferenceCategory>
+
<PreferenceCategory
android:title="@string/settings.other_title">
diff --git a/subsonic-android/src/github/daneren2005/dsub/activity/EqualizerActivity.java b/subsonic-android/src/github/daneren2005/dsub/activity/EqualizerActivity.java
index e5de3858..d9605fc5 100644
--- a/subsonic-android/src/github/daneren2005/dsub/activity/EqualizerActivity.java
+++ b/subsonic-android/src/github/daneren2005/dsub/activity/EqualizerActivity.java
@@ -142,18 +142,26 @@ public class EqualizerActivity extends Activity {
private void updateBars(boolean changedEnabled) {
boolean isEnabled = equalizer.getEnabled();
short minEQLevel = equalizer.getBandLevelRange()[0];
+ short maxEQLevel = equalizer.getBandLevelRange()[1];
for (Map.Entry<Short, SeekBar> entry : bars.entrySet()) {
short band = entry.getKey();
SeekBar bar = entry.getValue();
bar.setEnabled(isEnabled);
if(band >= (short)0) {
+ short setLevel;
if(changedEnabled) {
- equalizer.setBandLevel(band, (short)(equalizer.getBandLevel(band) - masterLevel));
+ setLevel = (short)(equalizer.getBandLevel(band) - masterLevel);
bar.setProgress(equalizer.getBandLevel(band) - minEQLevel);
} else {
bar.setProgress(equalizer.getBandLevel(band) - minEQLevel);
- equalizer.setBandLevel(band, (short)(equalizer.getBandLevel(band) + masterLevel));
+ setLevel = (short)(equalizer.getBandLevel(band) + masterLevel);
}
+ if(setLevel < minEQLevel) {
+ setLevel = minEQLevel;
+ } else if(setLevel > maxEQLevel) {
+ setLevel = maxEQLevel;
+ }
+ equalizer.setBandLevel(band, setLevel);
} else if(!isEnabled) {
bar.setProgress(-minEQLevel);
}
diff --git a/subsonic-android/src/github/daneren2005/dsub/activity/MainActivity.java b/subsonic-android/src/github/daneren2005/dsub/activity/MainActivity.java
index d0eb86ef..2709db46 100644
--- a/subsonic-android/src/github/daneren2005/dsub/activity/MainActivity.java
+++ b/subsonic-android/src/github/daneren2005/dsub/activity/MainActivity.java
@@ -1,9 +1,6 @@
package github.daneren2005.dsub.activity;
import android.app.AlertDialog;
-import android.app.PendingIntent;
-import android.content.ComponentName;
-import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.TypedArray;
@@ -15,16 +12,18 @@ import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.view.Menu;
import android.support.v4.view.ViewPager;
import android.util.Log;
-import android.view.KeyEvent;
import android.view.View;
import android.widget.ImageButton;
import android.widget.TextView;
import github.daneren2005.dsub.R;
import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.domain.PlayerState;
+import github.daneren2005.dsub.fragments.ChatFragment;
import github.daneren2005.dsub.fragments.MainFragment;
import github.daneren2005.dsub.fragments.SelectArtistFragment;
+import github.daneren2005.dsub.fragments.SelectDirectoryFragment;
import github.daneren2005.dsub.fragments.SelectPlaylistFragment;
+import github.daneren2005.dsub.fragments.SubsonicFragment;
import github.daneren2005.dsub.service.DownloadFile;
import github.daneren2005.dsub.service.DownloadServiceImpl;
import github.daneren2005.dsub.updates.Updater;
@@ -152,9 +151,13 @@ public class MainActivity extends SubsonicActivity {
viewPager.setAdapter(pagerAdapter);
viewPager.setOnPageChangeListener(pagerAdapter);
- addTab("Home", MainFragment.class, null);
- addTab("Library", SelectArtistFragment.class, null);
- addTab("Playlists", SelectPlaylistFragment.class, null);
+ addTab(R.string.button_bar_home, MainFragment.class, null);
+ addTab(R.string.button_bar_browse, SelectArtistFragment.class, null);
+ addTab(R.string.button_bar_playlists, SelectPlaylistFragment.class, null);
+ SharedPreferences prefs = Util.getPreferences(this);
+ if(prefs.getBoolean(Constants.PREFERENCES_KEY_CHAT_ENABLED, true)) {
+ addTab(R.string.button_bar_chat, ChatFragment.class, null);
+ }
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
getSupportActionBar().setHomeButtonEnabled(false);
@@ -190,6 +193,31 @@ public class MainActivity extends SubsonicActivity {
});
}
};
+
+ if(getIntent().hasExtra(Constants.INTENT_EXTRA_VIEW_ALBUM)) {
+ viewPager.setCurrentItem(1);
+
+ int fragmentID = R.id.select_artist_layout;
+ if(getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_PARENT_ID)) {
+ SubsonicFragment fragment = new SelectDirectoryFragment();
+ Bundle args = new Bundle();
+ args.putString(Constants.INTENT_EXTRA_NAME_ID, getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_PARENT_ID));
+ args.putString(Constants.INTENT_EXTRA_NAME_NAME, getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_PARENT_NAME));
+ fragment.setArguments(args);
+
+ pagerAdapter.queueFragment(fragment, R.id.select_artist_layout);
+ fragmentID = R.id.select_album_layout;
+ }
+
+ SubsonicFragment fragment = new SelectDirectoryFragment();
+ Bundle args = new Bundle();
+ args.putString(Constants.INTENT_EXTRA_NAME_ID, getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_ID));
+ args.putString(Constants.INTENT_EXTRA_NAME_NAME, getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_NAME));
+ fragment.setArguments(args);
+
+ pagerAdapter.queueFragment(fragment, fragmentID);
+ getIntent().removeExtra(Constants.INTENT_EXTRA_VIEW_ALBUM);
+ }
executorService = Executors.newSingleThreadScheduledExecutor();
executorService.scheduleWithFixedDelay(runnable, 0L, 1000L, TimeUnit.MILLISECONDS);
@@ -261,9 +289,19 @@ public class MainActivity extends SubsonicActivity {
if (!prefs.contains(Constants.PREFERENCES_KEY_OFFLINE)) {
SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean(Constants.PREFERENCES_KEY_OFFLINE, false);
+
+ editor.putString(Constants.PREFERENCES_KEY_SERVER_NAME + 1, "Demo Server");
+ editor.putString(Constants.PREFERENCES_KEY_SERVER_URL + 1, "http://demo.subsonic.org");
+ editor.putString(Constants.PREFERENCES_KEY_USERNAME + 1, "android-guest");
+ editor.putString(Constants.PREFERENCES_KEY_PASSWORD + 1, "guest");
editor.putInt(Constants.PREFERENCES_KEY_SERVER_INSTANCE, 1);
editor.commit();
- }
+ }
+ if(!prefs.contains(Constants.PREFERENCES_KEY_SERVER_COUNT)) {
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putInt(Constants.PREFERENCES_KEY_SERVER_COUNT, 3);
+ editor.commit();
+ }
}
private void showInfoDialog() {
diff --git a/subsonic-android/src/github/daneren2005/dsub/activity/SettingsActivity.java b/subsonic-android/src/github/daneren2005/dsub/activity/SettingsActivity.java
index 18d7e6b4..88e487f1 100644
--- a/subsonic-android/src/github/daneren2005/dsub/activity/SettingsActivity.java
+++ b/subsonic-android/src/github/daneren2005/dsub/activity/SettingsActivity.java
@@ -18,14 +18,19 @@
*/
package github.daneren2005.dsub.activity;
+import android.content.Intent;
import android.content.SharedPreferences;
+import android.net.Uri;
import android.os.Bundle;
+import android.preference.CheckBoxPreference;
import android.preference.EditTextPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
+import android.preference.PreferenceCategory;
import android.preference.PreferenceScreen;
import android.provider.SearchRecentSuggestions;
+import android.text.InputType;
import android.util.Log;
import github.daneren2005.dsub.R;
import github.daneren2005.dsub.provider.DSubSearchProvider;
@@ -45,7 +50,6 @@ import java.util.LinkedHashMap;
import java.util.Map;
public class SettingsActivity extends PreferenceActivity implements SharedPreferences.OnSharedPreferenceChangeListener {
-
private static final String TAG = SettingsActivity.class.getSimpleName();
private final Map<String, ServerSettings> serverSettings = new LinkedHashMap<String, ServerSettings>();
private boolean testingConnection;
@@ -57,13 +61,21 @@ public class SettingsActivity extends PreferenceActivity implements SharedPrefer
private ListPreference networkTimeout;
private EditTextPreference cacheSize;
private EditTextPreference cacheLocation;
- private ListPreference preloadCount;
+ private ListPreference preloadCountWifi;
+ private ListPreference preloadCountMobile;
private EditTextPreference randomSize;
private ListPreference tempLoss;
private EditTextPreference bufferLength;
+ private Preference addServerPreference;
+ private PreferenceCategory serversCategory;
+ private EditTextPreference chatRefreshRate;
+
+ private int serverCount = 3;
+ private SharedPreferences settings;
@Override
public void onCreate(Bundle savedInstanceState) {
+ applyTheme();
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.settings);
@@ -75,34 +87,17 @@ public class SettingsActivity extends PreferenceActivity implements SharedPrefer
networkTimeout = (ListPreference) findPreference(Constants.PREFERENCES_KEY_NETWORK_TIMEOUT);
cacheSize = (EditTextPreference) findPreference(Constants.PREFERENCES_KEY_CACHE_SIZE);
cacheLocation = (EditTextPreference) findPreference(Constants.PREFERENCES_KEY_CACHE_LOCATION);
- preloadCount = (ListPreference) findPreference(Constants.PREFERENCES_KEY_PRELOAD_COUNT);
+ preloadCountWifi = (ListPreference) findPreference(Constants.PREFERENCES_KEY_PRELOAD_COUNT_WIFI);
+ preloadCountMobile = (ListPreference) findPreference(Constants.PREFERENCES_KEY_PRELOAD_COUNT_MOBILE);
randomSize = (EditTextPreference) findPreference(Constants.PREFERENCES_KEY_RANDOM_SIZE);
tempLoss = (ListPreference) findPreference(Constants.PREFERENCES_KEY_TEMP_LOSS);
bufferLength = (EditTextPreference) findPreference(Constants.PREFERENCES_KEY_BUFFER_LENGTH);
-
- findPreference("testConnection1").setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
- @Override
- public boolean onPreferenceClick(Preference preference) {
- testConnection(1);
- return false;
- }
- });
-
- findPreference("testConnection2").setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
- @Override
- public boolean onPreferenceClick(Preference preference) {
- testConnection(2);
- return false;
- }
- });
-
- findPreference("testConnection3").setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
- @Override
- public boolean onPreferenceClick(Preference preference) {
- testConnection(3);
- return false;
- }
- });
+ addServerPreference = (Preference) findPreference(Constants.PREFERENCES_KEY_SERVER_ADD);
+ serversCategory = (PreferenceCategory) findPreference(Constants.PREFERENCES_KEY_SERVER_KEY);
+ chatRefreshRate = (EditTextPreference) findPreference(Constants.PREFERENCES_KEY_CHAT_REFRESH);
+
+ settings = Util.getPreferences(this);
+ serverCount = settings.getInt(Constants.PREFERENCES_KEY_SERVER_COUNT, 3);
findPreference("clearSearchHistory").setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
@@ -113,19 +108,43 @@ public class SettingsActivity extends PreferenceActivity implements SharedPrefer
return false;
}
});
+
+ addServerPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ serverCount++;
+ String instance = String.valueOf(serverCount);
+
+ Preference addServerPreference = findPreference(Constants.PREFERENCES_KEY_SERVER_ADD);
+ serversCategory.removePreference(addServerPreference);
+ serversCategory.addPreference(addServer(serverCount));
+ serversCategory.addPreference(addServerPreference);
+
+ SharedPreferences.Editor editor = settings.edit();
+ editor.putInt(Constants.PREFERENCES_KEY_SERVER_COUNT, serverCount);
+ editor.commit();
+
+ serverSettings.put(instance, new ServerSettings(instance));
+
+ return true;
+ }
+ });
- for (int i = 1; i <= 3; i++) {
+ serversCategory.removePreference(addServerPreference);
+ for (int i = 1; i <= serverCount; i++) {
String instance = String.valueOf(i);
+ serversCategory.addPreference(addServer(i));
serverSettings.put(instance, new ServerSettings(instance));
}
+ serversCategory.addPreference(addServerPreference);
SharedPreferences prefs = Util.getPreferences(this);
prefs.registerOnSharedPreferenceChangeListener(this);
update();
}
-
- @Override
+
+ @Override
protected void onDestroy() {
super.onDestroy();
@@ -166,14 +185,145 @@ public class SettingsActivity extends PreferenceActivity implements SharedPrefer
networkTimeout.setSummary(networkTimeout.getEntry());
cacheSize.setSummary(cacheSize.getText());
cacheLocation.setSummary(cacheLocation.getText());
- preloadCount.setSummary(preloadCount.getEntry());
+ preloadCountWifi.setSummary(preloadCountWifi.getEntry());
+ preloadCountMobile.setSummary(preloadCountMobile.getEntry());
randomSize.setSummary(randomSize.getText());
tempLoss.setSummary(tempLoss.getEntry());
bufferLength.setSummary(bufferLength.getText() + " seconds");
+ chatRefreshRate.setSummary(chatRefreshRate.getText());
for (ServerSettings ss : serverSettings.values()) {
ss.update();
}
}
+
+ private PreferenceScreen addServer(final int instance) {
+ final PreferenceScreen screen = getPreferenceManager().createPreferenceScreen(this);
+ screen.setTitle(R.string.settings_server_unused);
+ screen.setKey(Constants.PREFERENCES_KEY_SERVER_KEY + instance);
+
+ final EditTextPreference serverNamePreference = new EditTextPreference(this);
+ serverNamePreference.setKey(Constants.PREFERENCES_KEY_SERVER_NAME + instance);
+ serverNamePreference.setDefaultValue(getResources().getString(R.string.settings_server_unused));
+ serverNamePreference.setTitle(R.string.settings_server_name);
+
+ if (serverNamePreference.getText() == null) {
+ serverNamePreference.setText(getResources().getString(R.string.settings_server_unused));
+ }
+
+ serverNamePreference.setSummary(serverNamePreference.getText());
+
+ final EditTextPreference serverUrlPreference = new EditTextPreference(this);
+ serverUrlPreference.setKey(Constants.PREFERENCES_KEY_SERVER_URL + instance);
+ serverUrlPreference.getEditText().setInputType(InputType.TYPE_TEXT_VARIATION_URI);
+ serverUrlPreference.setDefaultValue("http://yourhost");
+ serverUrlPreference.setTitle(R.string.settings_server_address);
+
+ if (serverUrlPreference.getText() == null) {
+ serverUrlPreference.setText("http://yourhost");
+ }
+
+ serverUrlPreference.setSummary(serverUrlPreference.getText());
+
+ screen.setSummary(serverUrlPreference.getText());
+
+ final EditTextPreference serverUsernamePreference = new EditTextPreference(this);
+ serverUsernamePreference.setKey(Constants.PREFERENCES_KEY_USERNAME + instance);
+ serverUsernamePreference.setTitle(R.string.settings_server_username);
+
+ final EditTextPreference serverPasswordPreference = new EditTextPreference(this);
+ serverPasswordPreference.setKey(Constants.PREFERENCES_KEY_PASSWORD + instance);
+ serverPasswordPreference.getEditText().setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
+ serverPasswordPreference.setSummary("***");
+ serverPasswordPreference.setTitle(R.string.settings_server_password);
+
+ final Preference serverOpenBrowser = new Preference(this);
+ serverOpenBrowser.setKey(Constants.PREFERENCES_KEY_OPEN_BROWSER);
+ serverOpenBrowser.setPersistent(false);
+ serverOpenBrowser.setTitle(R.string.settings_server_open_browser);
+ serverOpenBrowser.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ openInBrowser(instance);
+ return true;
+ }
+ });
+
+ Preference serverRemoveServerPreference = new Preference(this);
+ serverRemoveServerPreference.setKey(Constants.PREFERENCES_KEY_SERVER_REMOVE + instance);
+ serverRemoveServerPreference.setPersistent(false);
+ serverRemoveServerPreference.setTitle(R.string.settings_servers_remove);
+
+ serverRemoveServerPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ // Reset values to null so when we ask for them again they are new
+ serverNamePreference.setText(null);
+ serverUrlPreference.setText(null);
+ serverUsernamePreference.setText(null);
+ serverPasswordPreference.setText(null);
+
+ int activeServer = Util.getActiveServer(SettingsActivity.this);
+ for (int i = instance; i <= serverCount; i++) {
+ Util.removeInstanceName(SettingsActivity.this, i, activeServer);
+ }
+
+ serverCount--;
+ SharedPreferences.Editor editor = settings.edit();
+ editor.putInt(Constants.PREFERENCES_KEY_SERVER_COUNT, serverCount);
+ editor.commit();
+
+ serversCategory.removePreference(screen);
+ screen.getDialog().dismiss();
+
+ return true;
+ }
+ });
+
+ Preference serverTestConnectionPreference = new Preference(this);
+ serverTestConnectionPreference.setKey(Constants.PREFERENCES_KEY_TEST_CONNECTION + instance);
+ serverTestConnectionPreference.setPersistent(false);
+ serverTestConnectionPreference.setTitle(R.string.settings_test_connection_title);
+ serverTestConnectionPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ testConnection(instance);
+ return false;
+ }
+ });
+
+ screen.addPreference(serverNamePreference);
+ screen.addPreference(serverUrlPreference);
+ screen.addPreference(serverUsernamePreference);
+ screen.addPreference(serverPasswordPreference);
+ screen.addPreference(serverRemoveServerPreference);
+ screen.addPreference(serverTestConnectionPreference);
+ screen.addPreference(serverOpenBrowser);
+
+ return screen;
+ }
+
+ private void applyTheme() {
+ String activeTheme = Util.getTheme(this);
+ if ("dark".equals(activeTheme)) {
+ setTheme(R.style.Theme_DSub_Dark);
+ } else if ("black".equals(activeTheme)) {
+ setTheme(R.style.Theme_DSub_Black);
+ } else if ("light".equals(activeTheme)) {
+ setTheme(R.style.Theme_DSub_Light);
+ } else if ("dark_fullscreen".equals(activeTheme)) {
+ setTheme(R.style.Theme_DSub_Dark_Fullscreen);
+ } else if ("black_fullscreen".equals(activeTheme)) {
+ setTheme(R.style.Theme_DSub_Black_Fullscreen);
+ } else if ("light_fullscreen".equals(activeTheme)) {
+ setTheme(R.style.Theme_DSub_Light_Fullscreen);
+ } else if("holo".equals(activeTheme)) {
+ setTheme(R.style.Theme_DSub_Holo);
+ } else if("holo_fullscreen".equals(activeTheme)) {
+ setTheme(R.style.Theme_DSub_Holo_Fullscreen);
+ }else {
+ setTheme(R.style.Theme_DSub_Holo);
+ }
+ }
private void setHideMedia(boolean hide) {
File nomediaDir = new File(FileUtil.getSubsonicDirectory(), ".nomedia");
@@ -264,6 +414,15 @@ public class SettingsActivity extends PreferenceActivity implements SharedPrefer
};
task.execute();
}
+
+ private void openInBrowser(final int instance) {
+ SharedPreferences prefs = Util.getPreferences(this);
+ String url = prefs.getString(Constants.PREFERENCES_KEY_SERVER_URL + instance, null);
+ Uri uriServer = Uri.parse(url);
+
+ Intent browserIntent = new Intent(Intent.ACTION_VIEW, uriServer);
+ startActivity(browserIntent);
+ }
private class ServerSettings {
private EditTextPreference serverName;
diff --git a/subsonic-android/src/github/daneren2005/dsub/activity/SubsonicActivity.java b/subsonic-android/src/github/daneren2005/dsub/activity/SubsonicActivity.java
index 15b8421f..d8158f7d 100644
--- a/subsonic-android/src/github/daneren2005/dsub/activity/SubsonicActivity.java
+++ b/subsonic-android/src/github/daneren2005/dsub/activity/SubsonicActivity.java
@@ -277,10 +277,14 @@ public class SubsonicActivity extends SherlockFragmentActivity implements OnItem
theme = Util.getTheme(this);
if ("dark".equals(theme)) {
setTheme(R.style.Theme_DSub_Dark);
+ } else if ("black".equals(theme)) {
+ setTheme(R.style.Theme_DSub_Black);
} else if ("light".equals(theme)) {
setTheme(R.style.Theme_DSub_Light);
} else if ("dark_fullscreen".equals(theme)) {
setTheme(R.style.Theme_DSub_Dark_Fullscreen);
+ } else if ("black_fullscreen".equals(theme)) {
+ setTheme(R.style.Theme_DSub_Black_Fullscreen);
} else if ("light_fullscreen".equals(theme)) {
setTheme(R.style.Theme_DSub_Light_Fullscreen);
} else if("holo".equals(theme)) {
@@ -388,6 +392,7 @@ public class SubsonicActivity extends SherlockFragmentActivity implements OnItem
private SubsonicFragment currentFragment;
private List<TabInfo> tabs = new ArrayList<TabInfo>();
private List<List<SubsonicFragment>> frags = new ArrayList<List<SubsonicFragment>>();
+ private List<QueuedFragment> queue = new ArrayList<QueuedFragment>();
private int currentPosition;
public TabPagerAdapter(SherlockFragmentActivity activity, ViewPager pager) {
@@ -404,8 +409,15 @@ public class SubsonicActivity extends SherlockFragmentActivity implements OnItem
SubsonicFragment frag = (SubsonicFragment) Fragment.instantiate(activity, tabInfo.fragmentClass.getName(), tabInfo.args);
List<SubsonicFragment> fragStack = new ArrayList<SubsonicFragment>();
fragStack.add(frag);
- frags.add(i, fragStack);
- if(currentFragment == null) {
+ while(i > frags.size()) {
+ frags.add(null);
+ }
+ if(i == frags.size()) {
+ frags.add(i, fragStack);
+ } else {
+ frags.set(i, fragStack);
+ }
+ if(currentFragment == null || currentPosition == i) {
currentFragment = frag;
currentFragment.setPrimaryFragment(true);
}
@@ -420,6 +432,13 @@ public class SubsonicActivity extends SherlockFragmentActivity implements OnItem
public void onCreateOptionsMenu(Menu menu, com.actionbarsherlock.view.MenuInflater menuInflater) {
if(currentFragment != null) {
currentFragment.onCreateOptionsMenu(menu, menuInflater);
+
+ for(QueuedFragment addFragment: queue) {
+ replaceFragment(addFragment.fragment, addFragment.id, currentFragment.getSupportTag());
+ currentFragment = addFragment.fragment;
+ }
+ currentFragment.setPrimaryFragment(true);
+ queue.clear();
}
}
public boolean onOptionsItemSelected(com.actionbarsherlock.view.MenuItem item) {
@@ -454,13 +473,15 @@ public class SubsonicActivity extends SherlockFragmentActivity implements OnItem
if(currentFragment != null) {
currentFragment.setPrimaryFragment(false);
}
- List<SubsonicFragment> fragStack = frags.get(position);
- currentFragment = fragStack.get(fragStack.size() - 1);
- if(currentFragment != null) {
- currentFragment.setPrimaryFragment(true);
+ if(position <= frags.size()) {
+ List<SubsonicFragment> fragStack = frags.get(position);
+ currentFragment = fragStack.get(fragStack.size() - 1);
+ if(currentFragment != null) {
+ currentFragment.setPrimaryFragment(true);
+ }
+ activity.invalidateOptionsMenu();
+ recreateSpinner();
}
- activity.invalidateOptionsMenu();
- recreateSpinner();
}
public void addTab(CharSequence title, Class fragmentClass, Bundle args) {
@@ -476,7 +497,12 @@ public class SubsonicActivity extends SherlockFragmentActivity implements OnItem
actionBar.addTab(tab);
notifyDataSetChanged();
}
-
+ public void queueFragment(SubsonicFragment fragment, int id) {
+ QueuedFragment frag = new QueuedFragment();
+ frag.fragment = fragment;
+ frag.id = id;
+ queue.add(frag);
+ }
public void replaceCurrent(SubsonicFragment fragment, int id, int tag) {
if(currentFragment != null) {
currentFragment.setPrimaryFragment(false);
@@ -547,11 +573,19 @@ public class SubsonicActivity extends SherlockFragmentActivity implements OnItem
}
public void invalidate() {
+ FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
for (int i = 0; i < frags.size(); i++) {
- List fragStack = (List)frags.get(i);
- SubsonicFragment frag = (SubsonicFragment)fragStack.get(fragStack.size() - 1);
+ List<SubsonicFragment> fragStack = frags.get(i);
+
+ for(int j = fragStack.size() - 1; j > 0; j--) {
+ SubsonicFragment oldFrag = fragStack.remove(j);
+ trans.remove((Fragment)oldFrag);
+ }
+
+ SubsonicFragment frag = (SubsonicFragment)fragStack.get(0);
frag.invalidate();
}
+ trans.commit();
}
public void onSaveInstanceState(Bundle savedInstanceState) {
@@ -599,5 +633,9 @@ public class SubsonicActivity extends SherlockFragmentActivity implements OnItem
this.args = args;
}
}
+ private class QueuedFragment {
+ public SubsonicFragment fragment;
+ public int id;
+ }
}
}
diff --git a/subsonic-android/src/github/daneren2005/dsub/domain/ChatMessage.java b/subsonic-android/src/github/daneren2005/dsub/domain/ChatMessage.java
new file mode 100644
index 00000000..471594e9
--- /dev/null
+++ b/subsonic-android/src/github/daneren2005/dsub/domain/ChatMessage.java
@@ -0,0 +1,51 @@
+/*
+ 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 github.daneren2005.dsub.domain;
+
+import java.io.Serializable;
+
+public class ChatMessage implements Serializable {
+ private String username;
+ private Long time;
+ private String message;
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public Long getTime() {
+ return time;
+ }
+
+ public void setTime(Long time) {
+ this.time = time;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+}
diff --git a/subsonic-android/src/github/daneren2005/dsub/domain/Genre.java b/subsonic-android/src/github/daneren2005/dsub/domain/Genre.java
new file mode 100644
index 00000000..8c705e31
--- /dev/null
+++ b/subsonic-android/src/github/daneren2005/dsub/domain/Genre.java
@@ -0,0 +1,29 @@
+package github.daneren2005.dsub.domain;
+
+import java.io.Serializable;
+
+public class Genre implements Serializable {
+ private String name;
+ private String index;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getIndex() {
+ return index;
+ }
+
+ public void setIndex(String index) {
+ this.index = index;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+}
diff --git a/subsonic-android/src/github/daneren2005/dsub/domain/MusicDirectory.java b/subsonic-android/src/github/daneren2005/dsub/domain/MusicDirectory.java
index b89417c0..10e9ef22 100644
--- a/subsonic-android/src/github/daneren2005/dsub/domain/MusicDirectory.java
+++ b/subsonic-android/src/github/daneren2005/dsub/domain/MusicDirectory.java
@@ -18,17 +18,23 @@
*/
package github.daneren2005.dsub.domain;
+import android.util.Log;
import java.util.ArrayList;
import java.util.List;
import java.io.Serializable;
+import java.util.Collections;
+import java.util.Comparator;
/**
* @author Sindre Mehus
*/
public class MusicDirectory {
+ private static final String TAG = MusicDirectory.class.getSimpleName();
private String name;
- private final List<Entry> children = new ArrayList<Entry>();
+ private String id;
+ private String parent;
+ private List<Entry> children = new ArrayList<Entry>();
public String getName() {
return name;
@@ -37,6 +43,22 @@ public class MusicDirectory {
public void setName(String name) {
this.name = name;
}
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getParent() {
+ return parent;
+ }
+
+ public void setParent(String parent) {
+ this.parent = parent;
+ }
public void addChild(Entry child) {
children.add(child);
@@ -59,10 +81,15 @@ public class MusicDirectory {
}
return result;
}
+
+ public void sortChildren() {
+ EntryComparator.sort(children);
+ }
public static class Entry implements Serializable {
private String id;
private String parent;
+ private String grandParent;
private boolean directory;
private String title;
private String album;
@@ -80,6 +107,7 @@ public class MusicDirectory {
private Integer bitRate;
private String path;
private boolean video;
+ private Integer discNumber;
private boolean starred;
private int closeness;
@@ -98,6 +126,14 @@ public class MusicDirectory {
public void setParent(String parent) {
this.parent = parent;
}
+
+ public String getGrandParent() {
+ return grandParent;
+ }
+
+ public void setGrandParent(String grandParent) {
+ this.grandParent = grandParent;
+ }
public boolean isDirectory() {
return directory;
@@ -234,6 +270,14 @@ public class MusicDirectory {
public void setVideo(boolean video) {
this.video = video;
}
+
+ public Integer getDiscNumber() {
+ return discNumber;
+ }
+
+ public void setDiscNumber(Integer discNumber) {
+ this.discNumber = discNumber;
+ }
public boolean isStarred() {
return starred;
@@ -274,4 +318,53 @@ public class MusicDirectory {
return title;
}
}
+
+ public static class EntryComparator implements Comparator<Entry> {
+ public int compare(Entry lhs, Entry rhs) {
+ if(lhs.isDirectory() && !rhs.isDirectory()) {
+ return -1;
+ } else if(!lhs.isDirectory() && rhs.isDirectory()) {
+ return 1;
+ }
+
+ Integer lhsDisc = lhs.getDiscNumber();
+ Integer rhsDisc = rhs.getDiscNumber();
+
+ if(lhsDisc != null && rhsDisc != null) {
+ if(lhsDisc < rhsDisc) {
+ return -1;
+ } else if(lhsDisc > rhsDisc) {
+ return 1;
+ }
+ } else if(lhsDisc != null) {
+ return -1;
+ } else if(rhsDisc != null) {
+ return 1;
+ }
+
+ Integer lhsTrack = lhs.getTrack();
+ Integer rhsTrack = rhs.getTrack();
+ if(lhsTrack != null && rhsTrack != null) {
+ if(lhsTrack < rhsTrack) {
+ return -1;
+ } else if(lhsTrack > rhsTrack) {
+ return 1;
+ }
+ } else if(lhsTrack != null) {
+ return -1;
+ } else if(rhsTrack != null) {
+ return 1;
+ }
+
+ return lhs.getTitle().compareToIgnoreCase(rhs.getTitle());
+ }
+
+ public static void sort(List<Entry> entries) {
+ try {
+ Collections.sort(entries, new EntryComparator());
+ } catch (Exception e) {
+ Log.w(TAG, "Failed to sort MusicDirectory");
+ }
+ }
+ }
} \ No newline at end of file
diff --git a/subsonic-android/src/github/daneren2005/dsub/domain/Share.java b/subsonic-android/src/github/daneren2005/dsub/domain/Share.java
new file mode 100644
index 00000000..d19496f9
--- /dev/null
+++ b/subsonic-android/src/github/daneren2005/dsub/domain/Share.java
@@ -0,0 +1,140 @@
+/*
+ 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 github.daneren2005.dsub.domain;
+
+import github.daneren2005.dsub.domain.MusicDirectory.Entry;
+import java.io.Serializable;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+
+public class Share implements Serializable {
+ private String id;
+ private String url;
+ private String description;
+ private String username;
+ private Date created;
+ private Date lastVisited;
+ private Date expires;
+ private Long visitCount;
+ private List<Entry> entries;
+
+ public Share() {
+ entries = new ArrayList<Entry>();
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public Date getCreated() {
+ return created;
+ }
+
+ public void setCreated(String created) {
+ if (created != null) {
+ try {
+ this.created = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH).parse(created);
+ } catch (ParseException e) {
+ this.created = null;
+ }
+ } else {
+ this.created = null;
+ }
+ }
+
+ public Date getLastVisited() {
+ return lastVisited;
+ }
+
+ public void setLastVisited(String lastVisited) {
+ if (lastVisited != null) {
+ try {
+ this.lastVisited = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH).parse(lastVisited);
+ } catch (ParseException e) {
+ this.lastVisited = null;
+ }
+ } else {
+ this.lastVisited = null;
+ }
+ }
+
+ public Date getExpires() {
+ return expires;
+ }
+
+ public void setExpires(String expires) {
+ if (expires != null) {
+ try {
+ this.expires = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH).parse(expires);
+ } catch (ParseException e) {
+ this.expires = null;
+ }
+ } else {
+ this.expires = null;
+ }
+ }
+
+ public Long getVisitCount() {
+ return visitCount;
+ }
+
+ public void setVisitCount(Long visitCount) {
+ this.visitCount = visitCount;
+ }
+
+ public List<Entry> getEntries() {
+ return this.entries;
+ }
+
+ public void addEntry(Entry entry) {
+ entries.add(entry);
+ }
+ }
diff --git a/subsonic-android/src/github/daneren2005/dsub/fragments/ChatFragment.java b/subsonic-android/src/github/daneren2005/dsub/fragments/ChatFragment.java
new file mode 100644
index 00000000..4f373442
--- /dev/null
+++ b/subsonic-android/src/github/daneren2005/dsub/fragments/ChatFragment.java
@@ -0,0 +1,223 @@
+package github.daneren2005.dsub.fragments;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import android.os.Bundle;
+import android.os.Handler;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.ListView;
+import android.widget.TextView;
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.ChatMessage;
+import github.daneren2005.dsub.service.MusicService;
+import github.daneren2005.dsub.service.MusicServiceFactory;
+import github.daneren2005.dsub.util.BackgroundTask;
+import github.daneren2005.dsub.util.TabBackgroundTask;
+import github.daneren2005.dsub.util.Util;
+import github.daneren2005.dsub.view.ChatAdapter;
+import com.actionbarsherlock.view.Menu;
+import github.daneren2005.dsub.util.Constants;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author Joshua Bahnsen
+ */
+public class ChatFragment extends SubsonicFragment {
+ private static final String TAG = ChatFragment.class.getSimpleName();
+ private ListView chatListView;
+ private EditText messageEditText;
+ private ImageButton sendButton;
+ private Long lastChatMessageTime = (long) 0;
+ private ArrayList<ChatMessage> messageList = new ArrayList<ChatMessage>();
+ private ScheduledExecutorService executorService;
+
+ @Override
+ public void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
+ rootView = inflater.inflate(R.layout.chat, container, false);
+
+ messageEditText = (EditText) rootView.findViewById(R.id.chat_edittext);
+ sendButton = (ImageButton) rootView.findViewById(R.id.chat_send);
+
+ sendButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ sendMessage();
+ }
+ });
+
+ chatListView = (ListView) rootView.findViewById(R.id.chat_entries);
+
+ messageEditText.setImeActionLabel("Send", KeyEvent.KEYCODE_ENTER);
+ messageEditText.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable editable) {
+ sendButton.setEnabled(!Util.isNullOrWhiteSpace(editable.toString()));
+ }
+ });
+
+ messageEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
+
+ @Override
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ if (actionId == EditorInfo.IME_ACTION_DONE || (actionId == EditorInfo.IME_NULL && event.getAction() == KeyEvent.ACTION_DOWN)) {
+ sendMessage();
+ return true;
+ }
+
+ return false;
+ }
+ });
+
+ invalidated = true;
+ return rootView;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ final Handler handler = new Handler();
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ if(primaryFragment) {
+ load(false);
+ } else {
+ invalidated = true;
+ }
+ }
+ });
+ }
+ };
+
+ SharedPreferences prefs = Util.getPreferences(context);
+ long refreshRate = Integer.parseInt(prefs.getString(Constants.PREFERENCES_KEY_CHAT_REFRESH, "30"));
+ if(refreshRate > 0) {
+ executorService = Executors.newSingleThreadScheduledExecutor();
+ executorService.scheduleWithFixedDelay(runnable, refreshRate * 1000L, refreshRate * 1000L, TimeUnit.MILLISECONDS);
+ }
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ if(executorService != null) {
+ executorService.shutdown();
+ executorService = null;
+ }
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, com.actionbarsherlock.view.MenuInflater menuInflater) {
+ menuInflater.inflate(R.menu.chat, menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(com.actionbarsherlock.view.MenuItem item) {
+ if(super.onOptionsItemSelected(item)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ protected void refresh(boolean refresh) {
+ load(refresh);
+ }
+
+ private synchronized void load(final boolean refresh) {
+ Log.i(TAG, "Loading: " + refresh);
+ setTitle(R.string.button_bar_chat);
+ BackgroundTask<List<ChatMessage>> task = new TabBackgroundTask<List<ChatMessage>>(this) {
+ @Override
+ protected List<ChatMessage> doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ return musicService.getChatMessages(refresh ? 0L : lastChatMessageTime, context, this);
+ }
+
+ @Override
+ protected void done(List<ChatMessage> result) {
+ if (result != null && !result.isEmpty()) {
+ if(refresh) {
+ messageList.clear();
+ }
+
+ // Reset lastChatMessageTime if we have a newer message
+ for (ChatMessage message : result) {
+ if (message.getTime() > lastChatMessageTime) {
+ lastChatMessageTime = message.getTime();
+ }
+ }
+
+ // Reverse results to show them on the bottom
+ Collections.reverse(result);
+ messageList.addAll(result);
+
+ ChatAdapter chatAdapter = new ChatAdapter(context, messageList);
+ chatListView.setAdapter(chatAdapter);
+ }
+ }
+ };
+
+ task.execute();
+ }
+
+ private void sendMessage() {
+ final String message = messageEditText.getText().toString();
+
+ if (!Util.isNullOrWhiteSpace(message)) {
+ messageEditText.setText("");
+ InputMethodManager mgr = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
+ mgr.hideSoftInputFromWindow(messageEditText.getWindowToken(), 0);
+
+ BackgroundTask<Void> task = new TabBackgroundTask<Void>(this) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ musicService.addChatMessage(message, context, this);
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ load(false);
+ }
+ };
+
+ task.execute();
+ }
+ }
+} \ No newline at end of file
diff --git a/subsonic-android/src/github/daneren2005/dsub/fragments/DownloadFragment.java b/subsonic-android/src/github/daneren2005/dsub/fragments/DownloadFragment.java
index c745e6d2..9113cd74 100644
--- a/subsonic-android/src/github/daneren2005/dsub/fragments/DownloadFragment.java
+++ b/subsonic-android/src/github/daneren2005/dsub/fragments/DownloadFragment.java
@@ -58,6 +58,7 @@ import java.util.ArrayList;
import java.util.concurrent.ScheduledFuture;
import com.mobeta.android.dslv.*;
import github.daneren2005.dsub.activity.EqualizerActivity;
+import github.daneren2005.dsub.activity.MainActivity;
import github.daneren2005.dsub.activity.SubsonicActivity;
public class DownloadFragment extends SubsonicFragment implements OnGestureListener {
@@ -518,10 +519,19 @@ public class DownloadFragment extends SubsonicFragment implements OnGestureListe
private boolean menuItemSelected(int menuItemId, final DownloadFile song) {
switch (menuItemId) {
case R.id.menu_show_album:
- /*Intent intent = new Intent(context, SelectAlbumActivity.class);
- intent.putExtra(Constants.INTENT_EXTRA_NAME_ID, song.getSong().getParent());
- intent.putExtra(Constants.INTENT_EXTRA_NAME_NAME, song.getSong().getAlbum());
- Util.startActivityWithoutTransition(context, intent);*/
+ MusicDirectory.Entry entry = song.getSong();
+
+ Intent intent = new Intent(context, MainActivity.class);
+ intent.putExtra(Constants.INTENT_EXTRA_VIEW_ALBUM, true);
+ intent.putExtra(Constants.INTENT_EXTRA_NAME_ID, entry.getParent());
+ intent.putExtra(Constants.INTENT_EXTRA_NAME_NAME, entry.getAlbum());
+
+ if(entry.getGrandParent() != null) {
+ intent.putExtra(Constants.INTENT_EXTRA_NAME_PARENT_ID, entry.getGrandParent());
+ intent.putExtra(Constants.INTENT_EXTRA_NAME_PARENT_NAME, entry.getArtist());
+ }
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ Util.startActivityWithoutTransition(context, intent);
return true;
case R.id.menu_lyrics:
SubsonicFragment fragment = new LyricsFragment();
@@ -653,9 +663,6 @@ public class DownloadFragment extends SubsonicFragment implements OnGestureListe
playlistFlipper.setDisplayedChild(1);
}
- onDownloadListChanged();
- onCurrentChanged();
- onProgressChanged();
scrollToCurrent();
if (downloadService != null && downloadService.getKeepScreenOn()) {
context.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
diff --git a/subsonic-android/src/github/daneren2005/dsub/fragments/MainFragment.java b/subsonic-android/src/github/daneren2005/dsub/fragments/MainFragment.java
index 3ccd8a0b..bbe3c507 100644
--- a/subsonic-android/src/github/daneren2005/dsub/fragments/MainFragment.java
+++ b/subsonic-android/src/github/daneren2005/dsub/fragments/MainFragment.java
@@ -32,9 +32,7 @@ public class MainFragment extends SubsonicFragment {
private LayoutInflater inflater;
private static final int MENU_GROUP_SERVER = 10;
- private static final int MENU_ITEM_SERVER_1 = 101;
- private static final int MENU_ITEM_SERVER_2 = 102;
- private static final int MENU_ITEM_SERVER_3 = 103;
+ private static final int MENU_ITEM_SERVER_BASE = 100;
@Override
public void onCreate(Bundle bundle) {
@@ -90,24 +88,17 @@ public class MainFragment extends SubsonicFragment {
@Override
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, view, menuInfo);
-
- android.view.MenuItem menuItem1 = menu.add(MENU_GROUP_SERVER, MENU_ITEM_SERVER_1, MENU_ITEM_SERVER_1, Util.getServerName(context, 1));
- android.view.MenuItem menuItem2 = menu.add(MENU_GROUP_SERVER, MENU_ITEM_SERVER_2, MENU_ITEM_SERVER_2, Util.getServerName(context, 2));
- android.view.MenuItem menuItem3 = menu.add(MENU_GROUP_SERVER, MENU_ITEM_SERVER_3, MENU_ITEM_SERVER_3, Util.getServerName(context, 3));
+
+ int serverCount = Util.getServerCount(context);
+ int activeServer = Util.getActiveServer(context);
+ for(int i = 1; i <= serverCount; i++) {
+ android.view.MenuItem menuItem = menu.add(MENU_GROUP_SERVER, MENU_ITEM_SERVER_BASE + i, MENU_ITEM_SERVER_BASE + i, Util.getServerName(context, i));
+ if(i == activeServer) {
+ menuItem.setChecked(true);
+ }
+ }
menu.setGroupCheckable(MENU_GROUP_SERVER, true, true);
menu.setHeaderTitle(R.string.main_select_server);
-
- switch (Util.getActiveServer(context)) {
- case 1:
- menuItem1.setChecked(true);
- break;
- case 2:
- menuItem2.setChecked(true);
- break;
- case 3:
- menuItem3.setChecked(true);
- break;
- }
}
@Override
@@ -116,20 +107,8 @@ public class MainFragment extends SubsonicFragment {
return false;
}
- switch (menuItem.getItemId()) {
- case MENU_ITEM_SERVER_1:
- setActiveServer(1);
- break;
- case MENU_ITEM_SERVER_2:
- setActiveServer(2);
- break;
- case MENU_ITEM_SERVER_3:
- setActiveServer(3);
- break;
- default:
- return super.onContextItemSelected(menuItem);
- }
-
+ int activeServer = menuItem.getItemId() - MENU_ITEM_SERVER_BASE;
+ setActiveServer(activeServer);
context.getPagerAdapter().invalidate();
return true;
}
@@ -154,6 +133,7 @@ public class MainFragment extends SubsonicFragment {
final View albumsRecentButton = buttons.findViewById(R.id.main_albums_recent);
final View albumsFrequentButton = buttons.findViewById(R.id.main_albums_frequent);
final View albumsStarredButton = buttons.findViewById(R.id.main_albums_starred);
+ final View albumsGenresButton = buttons.findViewById(R.id.main_albums_genres);
final View dummyView = rootView.findViewById(R.id.main_dummy);
@@ -170,7 +150,7 @@ public class MainFragment extends SubsonicFragment {
adapter.addView(offlineButton, true);
if (!Util.isOffline(context)) {
adapter.addView(albumsTitle, false);
- adapter.addViews(Arrays.asList(albumsNewestButton, albumsRandomButton, albumsHighestButton, albumsStarredButton, albumsRecentButton, albumsFrequentButton), true);
+ adapter.addViews(Arrays.asList(albumsNewestButton, albumsRandomButton, albumsHighestButton, albumsStarredButton, albumsGenresButton, albumsRecentButton, albumsFrequentButton), true);
}
list.setAdapter(adapter);
registerForContextMenu(dummyView);
@@ -194,6 +174,8 @@ public class MainFragment extends SubsonicFragment {
showAlbumList("frequent");
} else if (view == albumsStarredButton) {
showAlbumList("starred");
+ } else if(view == albumsGenresButton) {
+ showAlbumList("genres");
}
}
});
@@ -215,14 +197,19 @@ public class MainFragment extends SubsonicFragment {
}
private void showAlbumList(String type) {
- SubsonicFragment fragment = new SelectDirectoryFragment();
- Bundle args = new Bundle();
- args.putString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE, type);
- args.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 20);
- args.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0);
- fragment.setArguments(args);
-
- replaceFragment(fragment, R.id.home_layout);
+ if("genres".equals(type)) {
+ SubsonicFragment fragment = new SelectGenreFragment();
+ replaceFragment(fragment, R.id.home_layout);
+ } else {
+ SubsonicFragment fragment = new SelectDirectoryFragment();
+ Bundle args = new Bundle();
+ args.putString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE, type);
+ args.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 20);
+ args.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0);
+ fragment.setArguments(args);
+
+ replaceFragment(fragment, R.id.home_layout);
+ }
}
private void showAboutDialog() {
diff --git a/subsonic-android/src/github/daneren2005/dsub/fragments/SelectArtistFragment.java b/subsonic-android/src/github/daneren2005/dsub/fragments/SelectArtistFragment.java
index 8485f342..0a35233e 100644
--- a/subsonic-android/src/github/daneren2005/dsub/fragments/SelectArtistFragment.java
+++ b/subsonic-android/src/github/daneren2005/dsub/fragments/SelectArtistFragment.java
@@ -58,7 +58,11 @@ public class SelectArtistFragment extends SubsonicFragment implements AdapterVie
folderButton = folderButtonParent.findViewById(R.id.select_artist_folder);
registerForContextMenu(artistList);
- invalidated = true;
+ if(!primaryFragment) {
+ invalidated = true;
+ } else {
+ refresh(false);
+ }
return rootView;
}
diff --git a/subsonic-android/src/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java b/subsonic-android/src/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java
index 084269ee..13f61625 100644
--- a/subsonic-android/src/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java
+++ b/subsonic-android/src/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java
@@ -3,15 +3,11 @@ package github.daneren2005.dsub.fragments;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Bundle;
-import android.support.v4.app.FragmentTransaction;
import android.util.Log;
import android.view.ContextMenu;
import android.view.LayoutInflater;
-import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
@@ -26,19 +22,16 @@ import java.util.List;
import com.mobeta.android.dslv.*;
import github.daneren2005.dsub.activity.DownloadActivity;
import github.daneren2005.dsub.activity.SearchActivity;
-import github.daneren2005.dsub.service.DownloadFile;
import github.daneren2005.dsub.service.MusicService;
import github.daneren2005.dsub.service.MusicServiceFactory;
import github.daneren2005.dsub.service.OfflineException;
import github.daneren2005.dsub.service.ServerTooOldException;
import github.daneren2005.dsub.util.Constants;
-import github.daneren2005.dsub.util.FileUtil;
import github.daneren2005.dsub.util.LoadingTask;
import github.daneren2005.dsub.util.Pair;
import github.daneren2005.dsub.util.TabBackgroundTask;
import github.daneren2005.dsub.util.Util;
import github.daneren2005.dsub.view.AlbumListAdapter;
-import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
@@ -62,6 +55,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
String playlistId;
String playlistName;
String albumListType;
+ String albumListExtra;
int albumListSize;
@Override
@@ -107,6 +101,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
playlistId = args.getString(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID);
playlistName = args.getString(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME);
albumListType = args.getString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE);
+ albumListExtra = args.getString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_EXTRA);
albumListSize = args.getInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 0);
}
if(primaryFragment) {
@@ -320,6 +315,8 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
setTitle(R.string.main_albums_frequent);
} else if ("starred".equals(albumListType)) {
setTitle(R.string.main_albums_starred);
+ } else if("genres".equals(albumListType)) {
+ setTitle(albumListExtra);
}
new LoadTask() {
@@ -328,6 +325,8 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
MusicDirectory result;
if ("starred".equals(albumListType)) {
result = service.getStarredList(context, this);
+ } else if("genres".equals(albumListType)) {
+ result = service.getSongsByGenre(albumListExtra, size, 0, context, this);
} else {
result = service.getAlbumList(albumListType, size, 0, context, this);
}
@@ -380,7 +379,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
if(albumListType == null || "starred".equals(albumListType)) {
entryList.setAdapter(entryAdapter);
} else {
- entryList.setAdapter(new AlbumListAdapter(context, entryAdapter, albumListType, albumListSize));
+ entryList.setAdapter(new AlbumListAdapter(context, entryAdapter, albumListType, albumListExtra, albumListSize));
}
entryList.setVisibility(View.VISIBLE);
licenseValid = result.getSecond();
@@ -646,7 +645,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
}
View coverArtView = header.findViewById(R.id.select_album_art);
- getImageLoader().loadImage(coverArtView, entries.get(random.nextInt(entries.size())), true, true);
+ getImageLoader().loadImage(coverArtView, entries.get(random.nextInt(entries.size())), false, true);
TextView titleView = (TextView) header.findViewById(R.id.select_album_title);
if(playlistName != null) {
diff --git a/subsonic-android/src/github/daneren2005/dsub/fragments/SelectGenreFragment.java b/subsonic-android/src/github/daneren2005/dsub/fragments/SelectGenreFragment.java
new file mode 100644
index 00000000..f4b0f213
--- /dev/null
+++ b/subsonic-android/src/github/daneren2005/dsub/fragments/SelectGenreFragment.java
@@ -0,0 +1,141 @@
+/*
+ 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 2010 (C) Sindre Mehus
+ */
+package github.daneren2005.dsub.fragments;
+
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ListView;
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.Genre;
+import github.daneren2005.dsub.service.MusicService;
+import github.daneren2005.dsub.service.MusicServiceFactory;
+import github.daneren2005.dsub.util.BackgroundTask;
+import github.daneren2005.dsub.util.Constants;
+import github.daneren2005.dsub.util.TabBackgroundTask;
+import github.daneren2005.dsub.view.GenreAdapter;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
+import com.actionbarsherlock.view.MenuInflater;
+import java.util.ArrayList;
+import java.util.List;
+
+public class SelectGenreFragment extends SubsonicFragment implements AdapterView.OnItemClickListener {
+ private static final String TAG = SelectGenreFragment.class.getSimpleName();
+ private ListView genreListView;
+ private View emptyView;
+
+ @Override
+ public void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
+ rootView = inflater.inflate(R.layout.select_genres, container, false);
+
+ genreListView = (ListView)rootView.findViewById(R.id.select_genre_list);
+ genreListView.setOnItemClickListener(this);
+ emptyView = rootView.findViewById(R.id.select_genre_empty);
+ refresh();
+
+ return rootView;
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
+ menuInflater.inflate(R.menu.select_genres, menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if(super.onOptionsItemSelected(item)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public void setPrimaryFragment(boolean primary) {
+ super.setPrimaryFragment(primary);
+ if(rootView != null) {
+ if(primary) {
+ ((ViewGroup)rootView).getChildAt(0).setVisibility(View.VISIBLE);
+ } else {
+ ((ViewGroup)rootView).getChildAt(0).setVisibility(View.GONE);
+ }
+ }
+ }
+
+ @Override
+ protected void refresh(boolean refresh) {
+ load();
+ }
+
+ private void load() {
+ setTitle(R.string.main_albums_genres);
+
+ BackgroundTask<List<Genre>> task = new TabBackgroundTask<List<Genre>>(this) {
+ @Override
+ protected List<Genre> doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+
+ List<Genre> genres = new ArrayList<Genre>();
+
+ try {
+ genres = musicService.getGenres(context, this);
+ } catch (Exception x) {
+ Log.e(TAG, "Failed to load genres", x);
+ }
+
+ return genres;
+ }
+
+ @Override
+ protected void done(List<Genre> result) {
+ emptyView.setVisibility(result == null || result.isEmpty() ? View.VISIBLE : View.GONE);
+
+ if (result != null) {
+ genreListView.setAdapter(new GenreAdapter(context, result));
+ }
+
+ }
+ };
+ task.execute();
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ Genre genre = (Genre) parent.getItemAtPosition(position);
+
+ SubsonicFragment fragment = new SelectDirectoryFragment();
+ Bundle args = new Bundle();
+ args.putString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE, "genres");
+ args.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 20);
+ args.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0);
+ args.putString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_EXTRA, genre.getName());
+ fragment.setArguments(args);
+
+ replaceFragment(fragment, R.id.select_genre_layout);
+ }
+}
diff --git a/subsonic-android/src/github/daneren2005/dsub/fragments/SelectPlaylistFragment.java b/subsonic-android/src/github/daneren2005/dsub/fragments/SelectPlaylistFragment.java
index 1369eef0..aa0ffcf8 100644
--- a/subsonic-android/src/github/daneren2005/dsub/fragments/SelectPlaylistFragment.java
+++ b/subsonic-android/src/github/daneren2005/dsub/fragments/SelectPlaylistFragment.java
@@ -51,7 +51,11 @@ public class SelectPlaylistFragment extends SubsonicFragment implements AdapterV
emptyTextView = rootView.findViewById(R.id.select_playlist_empty);
list.setOnItemClickListener(this);
registerForContextMenu(list);
- invalidated = true;
+ if(!primaryFragment) {
+ invalidated = true;
+ } else {
+ refresh(false);
+ }
return rootView;
}
diff --git a/subsonic-android/src/github/daneren2005/dsub/fragments/SubsonicFragment.java b/subsonic-android/src/github/daneren2005/dsub/fragments/SubsonicFragment.java
index 35a272eb..05b78eab 100644
--- a/subsonic-android/src/github/daneren2005/dsub/fragments/SubsonicFragment.java
+++ b/subsonic-android/src/github/daneren2005/dsub/fragments/SubsonicFragment.java
@@ -35,6 +35,7 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
+import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import com.actionbarsherlock.app.SherlockFragment;
@@ -46,8 +47,10 @@ import github.daneren2005.dsub.activity.SearchActivity;
import github.daneren2005.dsub.activity.SettingsActivity;
import github.daneren2005.dsub.activity.SubsonicActivity;
import github.daneren2005.dsub.domain.Artist;
+import github.daneren2005.dsub.domain.Genre;
import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.domain.Playlist;
+import github.daneren2005.dsub.domain.Version;
import github.daneren2005.dsub.service.DownloadFile;
import github.daneren2005.dsub.service.DownloadService;
import github.daneren2005.dsub.service.DownloadServiceImpl;
@@ -379,15 +382,70 @@ public class SubsonicFragment extends SherlockFragment {
final EditText startYearBox = (EditText)dialogView.findViewById(R.id.start_year);
final EditText endYearBox = (EditText)dialogView.findViewById(R.id.end_year);
final EditText genreBox = (EditText)dialogView.findViewById(R.id.genre);
+ final Button genreCombo = (Button)dialogView.findViewById(R.id.genre_combo);
final SharedPreferences prefs = Util.getPreferences(context);
final String oldStartYear = prefs.getString(Constants.PREFERENCES_KEY_SHUFFLE_START_YEAR, "");
final String oldEndYear = prefs.getString(Constants.PREFERENCES_KEY_SHUFFLE_END_YEAR, "");
final String oldGenre = prefs.getString(Constants.PREFERENCES_KEY_SHUFFLE_GENRE, "");
+
+ Version version = Util.getServerRestVersion(context);
+ Version genreVersion = new Version("1.9.0");
+ boolean _useCombo = false;
+ if(version.compareTo(genreVersion) >= 0) {
+ genreBox.setVisibility(View.GONE);
+ genreCombo.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ new LoadingTask<List<Genre>>(context, true) {
+ @Override
+ protected List<Genre> doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ return musicService.getGenres(context, this);
+ }
+
+ @Override
+ protected void done(final List<Genre> genres) {
+ List<String> names = new ArrayList<String>();
+ for(Genre genre: genres) {
+ names.add(genre.getName());
+ }
+ final List<String> finalNames = names;
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.shuffle_pick_genre)
+ .setItems(names.toArray(new CharSequence[names.size()]), new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ genreCombo.setText(finalNames.get(which));
+ }
+ });
+ AlertDialog dialog = builder.create();
+ dialog.show();
+ }
+
+ @Override
+ protected void error(Throwable error) {
+ String msg;
+ if (error instanceof OfflineException || error instanceof ServerTooOldException) {
+ msg = getErrorMessage(error);
+ } else {
+ msg = context.getResources().getString(R.string.playlist_error) + " " + getErrorMessage(error);
+ }
+
+ Util.toast(context, msg, false);
+ }
+ }.execute();
+ }
+ });
+ _useCombo = true;
+ } else {
+ genreCombo.setVisibility(View.GONE);
+ }
+ final boolean useCombo = _useCombo;
startYearBox.setText(oldStartYear);
endYearBox.setText(oldEndYear);
genreBox.setText(oldGenre);
+ genreCombo.setText(oldGenre);
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle("Shuffle By")
@@ -397,7 +455,12 @@ public class SubsonicFragment extends SherlockFragment {
public void onClick(DialogInterface dialog, int id) {
Intent intent = new Intent(context, DownloadActivity.class);
intent.putExtra(Constants.INTENT_EXTRA_NAME_SHUFFLE, true);
- String genre = genreBox.getText().toString();
+ String genre;
+ if(useCombo) {
+ genre = genreCombo.getText().toString();
+ } else {
+ genre = genreBox.getText().toString();
+ }
String startYear = startYearBox.getText().toString();
String endYear = endYearBox.getText().toString();
diff --git a/subsonic-android/src/github/daneren2005/dsub/service/CachedMusicService.java b/subsonic-android/src/github/daneren2005/dsub/service/CachedMusicService.java
index 32807289..71bbb867 100644
--- a/subsonic-android/src/github/daneren2005/dsub/service/CachedMusicService.java
+++ b/subsonic-android/src/github/daneren2005/dsub/service/CachedMusicService.java
@@ -25,15 +25,17 @@ import org.apache.http.HttpResponse;
import android.content.Context;
import android.graphics.Bitmap;
+import github.daneren2005.dsub.domain.ChatMessage;
+import github.daneren2005.dsub.domain.Genre;
import github.daneren2005.dsub.domain.Indexes;
import github.daneren2005.dsub.domain.JukeboxStatus;
import github.daneren2005.dsub.domain.Lyrics;
import github.daneren2005.dsub.domain.MusicDirectory;
-import github.daneren2005.dsub.domain.MusicDirectory.Entry;
import github.daneren2005.dsub.domain.MusicFolder;
import github.daneren2005.dsub.domain.Playlist;
import github.daneren2005.dsub.domain.SearchCritera;
import github.daneren2005.dsub.domain.SearchResult;
+import github.daneren2005.dsub.domain.Share;
import github.daneren2005.dsub.domain.Version;
import github.daneren2005.dsub.util.CancellableTask;
import github.daneren2005.dsub.util.LRUCache;
@@ -55,6 +57,7 @@ public class CachedMusicService implements MusicService {
private final TimeLimitedCache<Indexes> cachedIndexes = new TimeLimitedCache<Indexes>(60 * 60, TimeUnit.SECONDS);
private final TimeLimitedCache<List<Playlist>> cachedPlaylists = new TimeLimitedCache<List<Playlist>>(3600, TimeUnit.SECONDS);
private final TimeLimitedCache<List<MusicFolder>> cachedMusicFolders = new TimeLimitedCache<List<MusicFolder>>(10 * 3600, TimeUnit.SECONDS);
+ private final TimeLimitedCache<List<Genre>> cachedGenres = new TimeLimitedCache<List<Genre>>(10 * 3600, TimeUnit.SECONDS);
private String restUrl;
public CachedMusicService(MusicService musicService) {
@@ -259,6 +262,39 @@ public class CachedMusicService implements MusicService {
public void setStarred(String id, boolean starred, Context context, ProgressListener progressListener) throws Exception {
musicService.setStarred(id, starred, context, progressListener);
}
+
+ @Override
+ public List<Share> getShares(Context context, ProgressListener progressListener) throws Exception {
+ return musicService.getShares(context, progressListener);
+ }
+
+ @Override
+ public List<ChatMessage> getChatMessages(Long since, Context context, ProgressListener progressListener) throws Exception {
+ return musicService.getChatMessages(since, context, progressListener);
+ }
+
+ @Override
+ public void addChatMessage(String message, Context context, ProgressListener progressListener) throws Exception {
+ musicService.addChatMessage(message, context, progressListener);
+ }
+
+ @Override
+ public List<Genre> getGenres(Context context, ProgressListener progressListener) throws Exception {
+ checkSettingsChanged(context);
+ List<Genre> result = cachedGenres.get();
+
+ if (result == null) {
+ result = musicService.getGenres(context, progressListener);
+ cachedGenres.set(result);
+ }
+
+ return result;
+ }
+
+ @Override
+ public MusicDirectory getSongsByGenre(String genre, int count, int offset, Context context, ProgressListener progressListener) throws Exception {
+ return musicService.getSongsByGenre(genre, count, offset, context, progressListener);
+ }
private void checkSettingsChanged(Context context) {
String newUrl = Util.getRestUrl(context, null);
diff --git a/subsonic-android/src/github/daneren2005/dsub/service/DownloadServiceImpl.java b/subsonic-android/src/github/daneren2005/dsub/service/DownloadServiceImpl.java
index fabeba6f..f659a434 100644
--- a/subsonic-android/src/github/daneren2005/dsub/service/DownloadServiceImpl.java
+++ b/subsonic-android/src/github/daneren2005/dsub/service/DownloadServiceImpl.java
@@ -209,6 +209,8 @@ public class DownloadServiceImpl extends Service implements DownloadService {
@Override
public void onDestroy() {
super.onDestroy();
+ instance = null;
+
if(currentPlaying != null) currentPlaying.setPlaying(false);
if(sleepTimer != null){
sleepTimer.cancel();
@@ -249,8 +251,6 @@ public class DownloadServiceImpl extends Service implements DownloadService {
nextPlayingTask.cancel();
}
Util.hidePlayingNotification(this, this, handler);
-
- instance = null;
}
public static DownloadService getInstance() {
@@ -264,7 +264,7 @@ public class DownloadServiceImpl extends Service implements DownloadService {
@Override
public synchronized void download(List<MusicDirectory.Entry> songs, boolean save, boolean autoplay, boolean playNext, boolean shuffle) {
- shufflePlay = false;
+ setShufflePlayEnabled(false);
int offset = 1;
if (songs.isEmpty()) {
@@ -320,8 +320,20 @@ public class DownloadServiceImpl extends Service implements DownloadService {
}
public void restore(List<MusicDirectory.Entry> songs, int currentPlayingIndex, int currentPlayingPosition) {
+ SharedPreferences prefs = Util.getPreferences(this);
+ boolean startShufflePlay = prefs.getBoolean(Constants.PREFERENCES_KEY_SHUFFLE_MODE, false);
download(songs, false, false, false, false);
+ if(startShufflePlay) {
+ shufflePlay = true;
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putBoolean(Constants.PREFERENCES_KEY_SHUFFLE_MODE, true);
+ editor.commit();
+ }
if (currentPlayingIndex != -1) {
+ while(mediaPlayer == null) {
+ Util.sleepQuietly(50L);
+ }
+
play(currentPlayingIndex, autoPlayStart);
if (currentPlaying != null && currentPlaying.isCompleteFileAvailable()) {
doPlay(currentPlaying, currentPlayingPosition, autoPlayStart);
@@ -337,6 +349,9 @@ public class DownloadServiceImpl extends Service implements DownloadService {
clear();
checkDownloads();
}
+ SharedPreferences.Editor editor = Util.getPreferences(this).edit();
+ editor.putBoolean(Constants.PREFERENCES_KEY_SHUFFLE_MODE, enabled);
+ editor.commit();
}
@Override
diff --git a/subsonic-android/src/github/daneren2005/dsub/service/MusicService.java b/subsonic-android/src/github/daneren2005/dsub/service/MusicService.java
index de3046d7..31e9c23c 100644
--- a/subsonic-android/src/github/daneren2005/dsub/service/MusicService.java
+++ b/subsonic-android/src/github/daneren2005/dsub/service/MusicService.java
@@ -24,6 +24,8 @@ import org.apache.http.HttpResponse;
import android.content.Context;
import android.graphics.Bitmap;
+import github.daneren2005.dsub.domain.ChatMessage;
+import github.daneren2005.dsub.domain.Genre;
import github.daneren2005.dsub.domain.Indexes;
import github.daneren2005.dsub.domain.JukeboxStatus;
import github.daneren2005.dsub.domain.Lyrics;
@@ -32,6 +34,7 @@ import github.daneren2005.dsub.domain.MusicFolder;
import github.daneren2005.dsub.domain.Playlist;
import github.daneren2005.dsub.domain.SearchCritera;
import github.daneren2005.dsub.domain.SearchResult;
+import github.daneren2005.dsub.domain.Share;
import github.daneren2005.dsub.domain.Version;
import github.daneren2005.dsub.util.CancellableTask;
import github.daneren2005.dsub.util.ProgressListener;
@@ -102,4 +105,14 @@ public interface MusicService {
JukeboxStatus setJukeboxGain(float gain, Context context, ProgressListener progressListener) throws Exception;
void setStarred(String id, boolean starred, Context context, ProgressListener progressListener) throws Exception;
+
+ List<Share> getShares(Context context, ProgressListener progressListener) throws Exception;
+
+ List<ChatMessage> getChatMessages(Long since, Context context, ProgressListener progressListener) throws Exception;
+
+ void addChatMessage(String message, Context context, ProgressListener progressListener) throws Exception;
+
+ List<Genre> getGenres(Context context, ProgressListener progressListener) throws Exception;
+
+ public MusicDirectory getSongsByGenre(String genre, int count, int offset, Context context, ProgressListener progressListener) throws Exception;
} \ No newline at end of file
diff --git a/subsonic-android/src/github/daneren2005/dsub/service/OfflineMusicService.java b/subsonic-android/src/github/daneren2005/dsub/service/OfflineMusicService.java
index 5f9aaf96..97fed19f 100644
--- a/subsonic-android/src/github/daneren2005/dsub/service/OfflineMusicService.java
+++ b/subsonic-android/src/github/daneren2005/dsub/service/OfflineMusicService.java
@@ -32,11 +32,13 @@ import java.util.Random;
import java.util.Set;
import android.content.Context;
+import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.MediaMetadataRetriever;
import android.util.Log;
import github.daneren2005.dsub.domain.Artist;
+import github.daneren2005.dsub.domain.Genre;
import github.daneren2005.dsub.domain.Indexes;
import github.daneren2005.dsub.domain.JukeboxStatus;
import github.daneren2005.dsub.domain.Lyrics;
@@ -78,6 +80,10 @@ public class OfflineMusicService extends RESTMusicService {
}
}
+ SharedPreferences prefs = Util.getPreferences(context);
+ String ignoredArticlesString = prefs.getString(Constants.CACHE_KEY_IGNORE, "The El La Los Las Le Les");
+ final String[] ignoredArticles = ignoredArticlesString.split(" ");
+
Collections.sort(artists, new Comparator<Artist>() {
public int compare(Artist lhsArtist, Artist rhsArtist) {
String lhs = lhsArtist.getName().toLowerCase();
@@ -92,13 +98,15 @@ public class OfflineMusicService extends RESTMusicService {
return -1;
}
- int index = lhs.indexOf("The ");
- if(index == 0) {
- lhs = lhs.substring(4, 0);
- }
- index = rhs.indexOf("The ");
- if(index == 0) {
- rhs = rhs.substring(4, 0);
+ for(String article: ignoredArticles) {
+ int index = lhs.indexOf(article.toLowerCase());
+ if(index == 0) {
+ lhs = lhs.substring(article.length() + 1);
+ }
+ index = rhs.indexOf(article.toLowerCase());
+ if(index == 0) {
+ rhs = rhs.substring(article.length() + 1);
+ }
}
return lhs.compareTo(rhs);
@@ -123,6 +131,7 @@ public class OfflineMusicService extends RESTMusicService {
result.addChild(createEntry(context, file, name));
}
}
+ result.sortChildren();
return result;
}
@@ -162,6 +171,27 @@ public class OfflineMusicService extends RESTMusicService {
// Failed parseInt, just means track filled out
}
}
+
+ try {
+ MediaMetadataRetriever metadata = new MediaMetadataRetriever();
+ metadata.setDataSource(file.getAbsolutePath());
+ String discNumber = metadata.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER);
+ if(discNumber == null) {
+ discNumber = "1/1";
+ }
+ int slashIndex = discNumber.indexOf("/");
+ if(slashIndex > 0) {
+ discNumber = discNumber.substring(0, slashIndex);
+ }
+ entry.setDiscNumber(Integer.parseInt(discNumber));
+ String bitrate = metadata.extractMetadata(MediaMetadataRetriever.METADATA_KEY_BITRATE);
+ entry.setBitRate(Integer.parseInt((bitrate != null) ? bitrate : "0") / 1000);
+ String length = metadata.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
+ entry.setDuration(Integer.parseInt(length) / 1000);
+ metadata.release();
+ } catch(Exception e) {
+ Log.i(TAG, "Device doesn't properly support MediaMetadataRetreiver");
+ }
}
entry.setTitle(title);
@@ -451,6 +481,16 @@ public class OfflineMusicService extends RESTMusicService {
public void setStarred(String id, boolean starred, Context context, ProgressListener progressListener) throws Exception {
throw new OfflineException("Starring not available in offline mode");
}
+
+ @Override
+ public List<Genre> getGenres(Context context, ProgressListener progressListener) throws Exception {
+ throw new OfflineException("Getting Genres not available in offline mode");
+ }
+
+ @Override
+ public MusicDirectory getSongsByGenre(String genre, int count, int offset, Context context, ProgressListener progressListener) throws Exception {
+ throw new OfflineException("Getting Songs By Genre not available in offline mode");
+ }
@Override
public MusicDirectory getRandomSongs(int size, String folder, String genre, String startYear, String endYear, Context context, ProgressListener progressListener) throws Exception {
diff --git a/subsonic-android/src/github/daneren2005/dsub/service/RESTMusicService.java b/subsonic-android/src/github/daneren2005/dsub/service/RESTMusicService.java
index 24dcbabd..5fc66190 100644
--- a/subsonic-android/src/github/daneren2005/dsub/service/RESTMusicService.java
+++ b/subsonic-android/src/github/daneren2005/dsub/service/RESTMusicService.java
@@ -73,7 +73,9 @@ import github.daneren2005.dsub.R;
import github.daneren2005.dsub.domain.*;
import github.daneren2005.dsub.domain.MusicDirectory.Entry;
import github.daneren2005.dsub.service.parser.AlbumListParser;
+import github.daneren2005.dsub.service.parser.ChatMessageParser;
import github.daneren2005.dsub.service.parser.ErrorParser;
+import github.daneren2005.dsub.service.parser.GenreParser;
import github.daneren2005.dsub.service.parser.IndexesParser;
import github.daneren2005.dsub.service.parser.JukeboxStatusParser;
import github.daneren2005.dsub.service.parser.LicenseParser;
@@ -85,6 +87,7 @@ import github.daneren2005.dsub.service.parser.PlaylistsParser;
import github.daneren2005.dsub.service.parser.RandomSongsParser;
import github.daneren2005.dsub.service.parser.SearchResult2Parser;
import github.daneren2005.dsub.service.parser.SearchResultParser;
+import github.daneren2005.dsub.service.parser.ShareParser;
import github.daneren2005.dsub.service.parser.StarredListParser;
import github.daneren2005.dsub.service.parser.VersionParser;
import github.daneren2005.dsub.service.ssl.SSLSocketFactory;
@@ -729,6 +732,100 @@ public class RESTMusicService implements MusicService {
Util.close(reader);
}
}
+
+ @Override
+ public List<Share> getShares(Context context, ProgressListener progressListener) throws Exception {
+ checkServerVersion(context, "1.6", "Shares not supported.");
+
+ Reader reader = getReader(context, progressListener, "getShares", null);
+ try {
+ return new ShareParser(context).parse(reader, progressListener);
+ } finally {
+ Util.close(reader);
+ }
+ }
+
+ @Override
+ public List<ChatMessage> getChatMessages(Long since, Context context, ProgressListener progressListener) throws Exception {
+ checkServerVersion(context, "1.2", "Chat not supported.");
+
+ HttpParams params = new BasicHttpParams();
+ HttpConnectionParams.setSoTimeout(params, SOCKET_READ_TIMEOUT_GET_RANDOM_SONGS);
+
+ List<String> parameterNames = new ArrayList<String>();
+ List<Object> parameterValues = new ArrayList<Object>();
+
+ parameterNames.add("since");
+ parameterValues.add(since);
+
+ Reader reader = getReader(context, progressListener, "getChatMessages", params, parameterNames, parameterValues);
+
+ try {
+ return new ChatMessageParser(context).parse(reader, progressListener);
+ } finally {
+ Util.close(reader);
+ }
+ }
+
+ @Override
+ public void addChatMessage(String message, Context context, ProgressListener progressListener) throws Exception {
+ checkServerVersion(context, "1.2", "Chat not supported.");
+
+ HttpParams params = new BasicHttpParams();
+ HttpConnectionParams.setSoTimeout(params, SOCKET_READ_TIMEOUT_GET_RANDOM_SONGS);
+
+ List<String> parameterNames = new ArrayList<String>();
+ List<Object> parameterValues = new ArrayList<Object>();
+
+ parameterNames.add("message");
+ parameterValues.add(message);
+
+ Reader reader = getReader(context, progressListener, "addChatMessage", params, parameterNames, parameterValues);
+
+ try {
+ new ErrorParser(context).parse(reader);
+ } finally {
+ Util.close(reader);
+ }
+ }
+
+ @Override
+ public List<Genre> getGenres(Context context, ProgressListener progressListener) throws Exception {
+ checkServerVersion(context, "1.9", "Genres not supported.");
+
+ Reader reader = getReader(context, progressListener, "getGenres", null);
+ try {
+ return new GenreParser(context).parse(reader, progressListener);
+ } finally {
+ Util.close(reader);
+ }
+ }
+
+ @Override
+ public MusicDirectory getSongsByGenre(String genre, int count, int offset, Context context, ProgressListener progressListener) throws Exception {
+ checkServerVersion(context, "1.9", "Genres not supported.");
+
+ HttpParams params = new BasicHttpParams();
+ HttpConnectionParams.setSoTimeout(params, SOCKET_READ_TIMEOUT_GET_RANDOM_SONGS);
+
+ List<String> parameterNames = new ArrayList<String>();
+ List<Object> parameterValues = new ArrayList<Object>();
+
+ parameterNames.add("genre");
+ parameterValues.add(genre);
+ parameterNames.add("count");
+ parameterValues.add(count);
+ parameterNames.add("offset");
+ parameterValues.add(offset);
+
+ Reader reader = getReader(context, progressListener, "getSongsByGenre", params, parameterNames, parameterValues);
+
+ try {
+ return new RandomSongsParser(context).parse(reader, progressListener);
+ } finally {
+ Util.close(reader);
+ }
+ }
private Reader getReader(Context context, ProgressListener progressListener, String method, HttpParams requestParams) throws Exception {
return getReader(context, progressListener, method, requestParams, Collections.<String>emptyList(), Collections.emptyList());
diff --git a/subsonic-android/src/github/daneren2005/dsub/service/parser/ChatMessageParser.java b/subsonic-android/src/github/daneren2005/dsub/service/parser/ChatMessageParser.java
new file mode 100644
index 00000000..1425a734
--- /dev/null
+++ b/subsonic-android/src/github/daneren2005/dsub/service/parser/ChatMessageParser.java
@@ -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 github.daneren2005.dsub.service.parser;
+
+import android.content.Context;
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.ChatMessage;
+import github.daneren2005.dsub.util.ProgressListener;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Joshua Bahnsen
+ */
+public class ChatMessageParser extends AbstractParser {
+
+ public ChatMessageParser(Context context) {
+ super(context);
+ }
+
+ public List<ChatMessage> parse(Reader reader, ProgressListener progressListener) throws Exception {
+ updateProgress(progressListener, R.string.parser_reading);
+ init(reader);
+ List<ChatMessage> result = new ArrayList<ChatMessage>();
+ int eventType;
+
+ do {
+ eventType = nextParseEvent();
+ if (eventType == XmlPullParser.START_TAG) {
+ String name = getElementName();
+ if ("chatMessage".equals(name)) {
+ ChatMessage chatMessage = new ChatMessage();
+ chatMessage.setUsername(get("username"));
+ chatMessage.setTime(getLong("time"));
+ chatMessage.setMessage(get("message"));
+ result.add(chatMessage);
+ } else if ("error".equals(name)) {
+ handleError();
+ }
+ }
+ } while (eventType != XmlPullParser.END_DOCUMENT);
+
+ validate();
+ updateProgress(progressListener, R.string.parser_reading_done);
+
+ return result;
+ }
+}
diff --git a/subsonic-android/src/github/daneren2005/dsub/service/parser/GenreParser.java b/subsonic-android/src/github/daneren2005/dsub/service/parser/GenreParser.java
new file mode 100644
index 00000000..1062d3af
--- /dev/null
+++ b/subsonic-android/src/github/daneren2005/dsub/service/parser/GenreParser.java
@@ -0,0 +1,122 @@
+/*
+ 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 2010 (C) Sindre Mehus
+ */
+package github.daneren2005.dsub.service.parser;
+
+import android.content.Context;
+import android.util.Log;
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.Genre;
+import github.daneren2005.dsub.util.ProgressListener;
+
+import org.xmlpull.v1.XmlPullParser;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Joshua Bahnsen
+ */
+public class GenreParser extends AbstractParser {
+ private static final String TAG = GenreParser.class.getSimpleName();
+
+ public GenreParser(Context context) {
+ super(context);
+ }
+
+ public List<Genre> parse(Reader reader, ProgressListener progressListener) throws Exception {
+ updateProgress(progressListener, R.string.parser_reading);
+
+ List<Genre> result = new ArrayList<Genre>();
+ StringReader sr = null;
+
+ try {
+ BufferedReader br = new BufferedReader(reader);
+ String xml = null;
+ String line = null;
+
+ while ((line = br.readLine()) != null) {
+ if (xml == null) {
+ xml = line;
+ } else {
+ xml += line;
+ }
+ }
+ br.close();
+
+ // Replace double escaped ampersand (&amp;apos;)
+ xml = xml.replaceAll("(?:&amp;)(amp;|lt;|gt;|#37;|apos;)", "&$1");
+
+ // Replace unescaped ampersand
+ xml = xml.replaceAll("&(?!amp;|lt;|gt;|#37;|apos;)", "&amp;");
+
+ // Replace unescaped percent symbol
+ // No replacements for <> at this time
+ xml = xml.replaceAll("%", "&#37;");
+
+ xml = xml.replaceAll("'", "&apos;");
+
+ sr = new StringReader(xml);
+ } catch (IOException ioe) {
+ Log.e(TAG, "Error parsing Genre XML", ioe);
+ }
+
+ if (sr == null) {
+ Log.w(TAG, "Unable to parse Genre XML, returning empty list");
+ return result;
+ }
+
+ init(sr);
+
+ Genre genre = null;
+
+ int eventType;
+ do {
+ eventType = nextParseEvent();
+ if (eventType == XmlPullParser.START_TAG) {
+ String name = getElementName();
+ if ("genre".equals(name)) {
+ genre = new Genre();
+ } else if ("error".equals(name)) {
+ handleError();
+ } else {
+ genre = null;
+ }
+ } else if (eventType == XmlPullParser.TEXT) {
+ if (genre != null) {
+ String value = getText();
+ if (genre != null) {
+ genre.setName(value);
+ genre.setIndex(value.substring(0, 1));
+ result.add(genre);
+ genre = null;
+ }
+ }
+ }
+ } while (eventType != XmlPullParser.END_DOCUMENT);
+
+ validate();
+ updateProgress(progressListener, R.string.parser_reading_done);
+
+ return result;
+ }
+}
diff --git a/subsonic-android/src/github/daneren2005/dsub/service/parser/IndexesParser.java b/subsonic-android/src/github/daneren2005/dsub/service/parser/IndexesParser.java
index 4a307812..6196411d 100644
--- a/subsonic-android/src/github/daneren2005/dsub/service/parser/IndexesParser.java
+++ b/subsonic-android/src/github/daneren2005/dsub/service/parser/IndexesParser.java
@@ -25,20 +25,26 @@ import java.util.ArrayList;
import org.xmlpull.v1.XmlPullParser;
import android.content.Context;
+import android.content.SharedPreferences;
import github.daneren2005.dsub.R;
import github.daneren2005.dsub.domain.Artist;
import github.daneren2005.dsub.domain.Indexes;
import github.daneren2005.dsub.util.ProgressListener;
import android.util.Log;
+import github.daneren2005.dsub.util.Constants;
+import github.daneren2005.dsub.util.Util;
/**
* @author Sindre Mehus
*/
public class IndexesParser extends AbstractParser {
private static final String TAG = IndexesParser.class.getSimpleName();
+
+ private Context context;
public IndexesParser(Context context) {
super(context);
+ this.context = context;
}
public Indexes parse(Reader reader, ProgressListener progressListener) throws Exception {
@@ -52,6 +58,7 @@ public class IndexesParser extends AbstractParser {
Long lastModified = null;
int eventType;
String index = "#";
+ String ignoredArticles = null;
boolean changed = false;
do {
@@ -61,6 +68,7 @@ public class IndexesParser extends AbstractParser {
if ("indexes".equals(name)) {
changed = true;
lastModified = getLong("lastModified");
+ ignoredArticles = get("ignoredArticles");
} else if ("index".equals(name)) {
index = get("name");
@@ -90,6 +98,12 @@ public class IndexesParser extends AbstractParser {
} while (eventType != XmlPullParser.END_DOCUMENT);
validate();
+
+ if(ignoredArticles != null) {
+ SharedPreferences.Editor prefs = Util.getPreferences(context).edit();
+ prefs.putString(Constants.CACHE_KEY_IGNORE, ignoredArticles);
+ prefs.commit();
+ }
if (!changed) {
return null;
diff --git a/subsonic-android/src/github/daneren2005/dsub/service/parser/MusicDirectoryEntryParser.java b/subsonic-android/src/github/daneren2005/dsub/service/parser/MusicDirectoryEntryParser.java
index 818c8235..b0434aca 100644
--- a/subsonic-android/src/github/daneren2005/dsub/service/parser/MusicDirectoryEntryParser.java
+++ b/subsonic-android/src/github/daneren2005/dsub/service/parser/MusicDirectoryEntryParser.java
@@ -25,7 +25,6 @@ import github.daneren2005.dsub.domain.MusicDirectory;
* @author Sindre Mehus
*/
public class MusicDirectoryEntryParser extends AbstractParser {
-
public MusicDirectoryEntryParser(Context context) {
super(context);
}
@@ -54,9 +53,22 @@ public class MusicDirectoryEntryParser extends AbstractParser {
entry.setBitRate(getInteger("bitRate"));
entry.setPath(get("path"));
entry.setVideo(getBoolean("isVideo"));
+ entry.setDiscNumber(getInteger("discNumber"));
} else if(!"".equals(artist)) {
entry.setPath(artist + "/" + entry.getTitle());
}
return entry;
}
+
+ protected MusicDirectory.Entry parseArtist() {
+ MusicDirectory.Entry entry = new MusicDirectory.Entry();
+
+ entry.setId(get("id"));
+ entry.setTitle(get("name"));
+ entry.setPath(entry.getTitle());
+ entry.setStarred(true);
+ entry.setDirectory(true);
+
+ return entry;
+ }
} \ No newline at end of file
diff --git a/subsonic-android/src/github/daneren2005/dsub/service/parser/MusicDirectoryParser.java b/subsonic-android/src/github/daneren2005/dsub/service/parser/MusicDirectoryParser.java
index 370df305..038518c6 100644
--- a/subsonic-android/src/github/daneren2005/dsub/service/parser/MusicDirectoryParser.java
+++ b/subsonic-android/src/github/daneren2005/dsub/service/parser/MusicDirectoryParser.java
@@ -22,7 +22,9 @@ import android.content.Context;
import android.util.Log;
import github.daneren2005.dsub.R;
import github.daneren2005.dsub.domain.MusicDirectory;
+import github.daneren2005.dsub.domain.Version;
import github.daneren2005.dsub.util.ProgressListener;
+import github.daneren2005.dsub.util.Util;
import org.xmlpull.v1.XmlPullParser;
import java.io.Reader;
@@ -33,13 +35,14 @@ import java.io.Reader;
public class MusicDirectoryParser extends MusicDirectoryEntryParser {
private static final String TAG = MusicDirectoryParser.class.getSimpleName();
+ private Context context;
public MusicDirectoryParser(Context context) {
super(context);
+ this.context = context;
}
public MusicDirectory parse(String artist, Reader reader, ProgressListener progressListener) throws Exception {
-
long t0 = System.currentTimeMillis();
updateProgress(progressListener, R.string.parser_reading);
init(reader);
@@ -51,9 +54,13 @@ public class MusicDirectoryParser extends MusicDirectoryEntryParser {
if (eventType == XmlPullParser.START_TAG) {
String name = getElementName();
if ("child".equals(name)) {
- dir.addChild(parseEntry(artist));
+ MusicDirectory.Entry entry = parseEntry(artist);
+ entry.setGrandParent(dir.getParent());
+ dir.addChild(entry);
} else if ("directory".equals(name)) {
dir.setName(get("name"));
+ dir.setId(get("id"));
+ dir.setParent(get("parent"));
} else if ("error".equals(name)) {
handleError();
}
@@ -62,6 +69,13 @@ public class MusicDirectoryParser extends MusicDirectoryEntryParser {
validate();
updateProgress(progressListener, R.string.parser_reading_done);
+
+ // Only apply sorting on server version 4.7 and greater, where disc is supported
+ Version version = Util.getServerRestVersion(context);
+ Version discVersion = new Version("1.8.0");
+ if(version.compareTo(discVersion) >= 0) {
+ dir.sortChildren();
+ }
long t1 = System.currentTimeMillis();
Log.d(TAG, "Got music directory in " + (t1 - t0) + "ms.");
diff --git a/subsonic-android/src/github/daneren2005/dsub/service/parser/ShareParser.java b/subsonic-android/src/github/daneren2005/dsub/service/parser/ShareParser.java
new file mode 100644
index 00000000..c317e799
--- /dev/null
+++ b/subsonic-android/src/github/daneren2005/dsub/service/parser/ShareParser.java
@@ -0,0 +1,77 @@
+/*
+ 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 github.daneren2005.dsub.service.parser;
+
+import android.content.Context;
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.Share;
+import github.daneren2005.dsub.util.ProgressListener;
+import org.xmlpull.v1.XmlPullParser;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Joshua Bahnsen
+ */
+public class ShareParser extends MusicDirectoryEntryParser {
+
+ public ShareParser(Context context) {
+ super(context);
+ }
+
+ public List<Share> parse(Reader reader, ProgressListener progressListener) throws Exception {
+
+ updateProgress(progressListener, R.string.parser_reading);
+ init(reader);
+
+ List<Share> dir = new ArrayList<Share>();
+ Share share = null;
+ int eventType;
+
+ do {
+ eventType = nextParseEvent();
+
+ if (eventType == XmlPullParser.START_TAG) {
+ String name = getElementName();
+
+ if ("share".equals(name)) {
+ share = new Share();
+ share.setCreated(get("created"));
+ share.setDescription(get("description"));
+ share.setExpires(get("expires"));
+ share.setId(get("id"));
+ share.setLastVisited(get("lastVisited"));
+ share.setUrl(get("url"));
+ share.setUsername(get("username"));
+ share.setVisitCount(getLong("visitCount"));
+ } else if ("entry".equals(name)) {
+ share.addEntry(parseEntry(null));
+ } else if ("error".equals(name)) {
+ handleError();
+ }
+ }
+ } while (eventType != XmlPullParser.END_DOCUMENT);
+
+ validate();
+ updateProgress(progressListener, R.string.parser_reading_done);
+
+ return dir;
+ }
+} \ No newline at end of file
diff --git a/subsonic-android/src/github/daneren2005/dsub/service/parser/StarredListParser.java b/subsonic-android/src/github/daneren2005/dsub/service/parser/StarredListParser.java
index 2cb42f30..fc4cd175 100644
--- a/subsonic-android/src/github/daneren2005/dsub/service/parser/StarredListParser.java
+++ b/subsonic-android/src/github/daneren2005/dsub/service/parser/StarredListParser.java
@@ -49,7 +49,7 @@ public class StarredListParser extends MusicDirectoryEntryParser {
if ("album".equals(name) || "song".equals(name)) {
dir.addChild(parseEntry(""));
} else if("artist".equals(name)) {
-
+ dir.addChild(parseArtist());
} else if ("error".equals(name)) {
handleError();
}
diff --git a/subsonic-android/src/github/daneren2005/dsub/updates/Updater.java b/subsonic-android/src/github/daneren2005/dsub/updates/Updater.java
index c239fa43..60a17b67 100644
--- a/subsonic-android/src/github/daneren2005/dsub/updates/Updater.java
+++ b/subsonic-android/src/github/daneren2005/dsub/updates/Updater.java
@@ -43,7 +43,7 @@ public class Updater {
public void checkUpdates(Context context) {
this.context = context;
List<Updater> updaters = new ArrayList<Updater>();
- updaters.add(new Updater373());
+ updaters.add(new Updater403());
SharedPreferences prefs = Util.getPreferences(context);
int lastVersion = prefs.getInt(Constants.LAST_VERSION, 0);
diff --git a/subsonic-android/src/github/daneren2005/dsub/updates/Updater373.java b/subsonic-android/src/github/daneren2005/dsub/updates/Updater403.java
index b56c2731..17947ce5 100644
--- a/subsonic-android/src/github/daneren2005/dsub/updates/Updater373.java
+++ b/subsonic-android/src/github/daneren2005/dsub/updates/Updater403.java
@@ -29,16 +29,16 @@ import java.io.File;
*
* @author Scott
*/
-public class Updater373 extends Updater {
- public Updater373() {
- super(373);
- TAG = Updater373.class.getSimpleName();
+public class Updater403 extends Updater {
+ public Updater403() {
+ super(403);
+ TAG = Updater403.class.getSimpleName();
}
@Override
public void update(Context context) {
// Rename cover.jpeg to cover.jpg
- Log.i(TAG, "Running Updater373: updating cover.jpeg to cover.jpg");
+ Log.i(TAG, "Running Updater403: updating cover.jpg to albumart.jpg");
File dir = FileUtil.getMusicDirectory(context);
if(dir != null) {
moveArt(dir);
@@ -49,7 +49,7 @@ public class Updater373 extends Updater {
for(File file: dir.listFiles()) {
if(file.isDirectory()) {
moveArt(file);
- } else if("cover.jpeg".equals(file.getName())) {
+ } else if("cover.jpg".equals(file.getName()) || "cover.jpeg".equals(file.getName())) {
File renamed = new File(dir, Constants.ALBUM_ART_FILE);
file.renameTo(renamed);
}
diff --git a/subsonic-android/src/github/daneren2005/dsub/util/CacheCleaner.java b/subsonic-android/src/github/daneren2005/dsub/util/CacheCleaner.java
index c69a595c..44de369f 100644
--- a/subsonic-android/src/github/daneren2005/dsub/util/CacheCleaner.java
+++ b/subsonic-android/src/github/daneren2005/dsub/util/CacheCleaner.java
@@ -54,7 +54,7 @@ public class CacheCleaner {
// No songs left in the folder
if(children.length == 1 && children[0].getPath().equals(FileUtil.getAlbumArtFile(dir).getPath())) {
- Util.delete(FileUtil.getAlbumArtFile(dir));
+ Util.delete(children[0]);
children = dir.listFiles();
}
diff --git a/subsonic-android/src/github/daneren2005/dsub/util/Constants.java b/subsonic-android/src/github/daneren2005/dsub/util/Constants.java
index 302ececf..5de13e73 100644
--- a/subsonic-android/src/github/daneren2005/dsub/util/Constants.java
+++ b/subsonic-android/src/github/daneren2005/dsub/util/Constants.java
@@ -36,6 +36,8 @@ public final class Constants {
// Names for intent extras.
public static final String INTENT_EXTRA_NAME_ID = "subsonic.id";
public static final String INTENT_EXTRA_NAME_NAME = "subsonic.name";
+ public static final String INTENT_EXTRA_NAME_PARENT_ID = "subsonic.parent_id";
+ public static final String INTENT_EXTRA_NAME_PARENT_NAME = "subsonic.parent_name";
public static final String INTENT_EXTRA_NAME_ARTIST = "subsonic.artist";
public static final String INTENT_EXTRA_NAME_TITLE = "subsonic.title";
public static final String INTENT_EXTRA_NAME_AUTOPLAY = "subsonic.playall";
@@ -44,6 +46,7 @@ public final class Constants {
public static final String INTENT_EXTRA_NAME_PLAYLIST_ID = "subsonic.playlist.id";
public static final String INTENT_EXTRA_NAME_PLAYLIST_NAME = "subsonic.playlist.name";
public static final String INTENT_EXTRA_NAME_ALBUM_LIST_TYPE = "subsonic.albumlisttype";
+ public static final String INTENT_EXTRA_NAME_ALBUM_LIST_EXTRA = "subsonic.albumlistextra";
public static final String INTENT_EXTRA_NAME_ALBUM_LIST_SIZE = "subsonic.albumlistsize";
public static final String INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET = "subsonic.albumlistoffset";
public static final String INTENT_EXTRA_NAME_SHUFFLE = "subsonic.shuffle";
@@ -51,15 +54,22 @@ public final class Constants {
public static final String INTENT_EXTRA_REQUEST_SEARCH = "subsonic.requestsearch";
public static final String INTENT_EXTRA_NAME_EXIT = "subsonic.exit" ;
public static final String INTENT_EXTRA_NAME_DOWNLOAD = "subsonic.download";
+ public static final String INTENT_EXTRA_VIEW_ALBUM = "subsonic.view_album";
// Notification IDs.
public static final int NOTIFICATION_ID_PLAYING = 100;
public static final int NOTIFICATION_ID_ERROR = 101;
// Preferences keys.
+ public static final String PREFERENCES_KEY_SERVER_KEY = "server";
+ public static final String PREFERENCES_KEY_SERVER_COUNT = "serverCount";
+ public static final String PREFERENCES_KEY_SERVER_ADD = "serverAdd";
+ public static final String PREFERENCES_KEY_SERVER_REMOVE = "serverRemove";
public static final String PREFERENCES_KEY_SERVER_INSTANCE = "serverInstanceId";
public static final String PREFERENCES_KEY_SERVER_NAME = "serverName";
public static final String PREFERENCES_KEY_SERVER_URL = "serverUrl";
+ public static final String PREFERENCES_KEY_TEST_CONNECTION = "serverTestConnection";
+ public static final String PREFERENCES_KEY_OPEN_BROWSER = "openBrowser";
public static final String PREFERENCES_KEY_MUSIC_FOLDER_ID = "musicFolderId";
public static final String PREFERENCES_KEY_USERNAME = "username";
public static final String PREFERENCES_KEY_PASSWORD = "password";
@@ -72,7 +82,8 @@ public final class Constants {
public static final String PREFERENCES_KEY_NETWORK_TIMEOUT = "networkTimeout";
public static final String PREFERENCES_KEY_CACHE_SIZE = "cacheSize";
public static final String PREFERENCES_KEY_CACHE_LOCATION = "cacheLocation";
- public static final String PREFERENCES_KEY_PRELOAD_COUNT = "preloadCount";
+ public static final String PREFERENCES_KEY_PRELOAD_COUNT_WIFI = "preloadCountWifi";
+ public static final String PREFERENCES_KEY_PRELOAD_COUNT_MOBILE = "preloadCountMobile";
public static final String PREFERENCES_KEY_HIDE_MEDIA = "hideMedia";
public static final String PREFERENCES_KEY_MEDIA_BUTTONS = "mediaButtons";
public static final String PREFERENCES_KEY_SCREEN_LIT_ON_DOWNLOAD = "screenLitOnDownload";
@@ -93,6 +104,11 @@ public final class Constants {
public static final String PREFERENCES_EQUALIZER_SETTINGS = "equalizerSettings";
public static final String PREFERENCES_KEY_PERSISTENT_NOTIFICATION = "persistentNotification";
public static final String PREFERENCES_KEY_GAPLESS_PLAYBACK = "gaplessPlayback";
+ public static final String PREFERENCES_KEY_SHUFFLE_MODE = "shuffleMode";
+ public static final String PREFERENCES_KEY_CHAT_REFRESH = "chatRefreshRate";
+ public static final String PREFERENCES_KEY_CHAT_ENABLED = "chatEnabled";
+
+ public static final String CACHE_KEY_IGNORE = "ignoreArticles";
public static final String MAIN_BACK_STACK = "backStackIds";
public static final String MAIN_BACK_STACK_SIZE = "backStackIdsSize";
@@ -108,7 +124,7 @@ public final class Constants {
// URL for project donations.
public static final String DONATION_URL = "http://subsonic.org/pages/android-donation.jsp";
- public static final String ALBUM_ART_FILE = "cover.jpg";
+ public static final String ALBUM_ART_FILE = "albumart.jpg";
private Constants() {
}
diff --git a/subsonic-android/src/github/daneren2005/dsub/util/ErrorDialog.java b/subsonic-android/src/github/daneren2005/dsub/util/ErrorDialog.java
index 2928e93f..ab9b4fa3 100644
--- a/subsonic-android/src/github/daneren2005/dsub/util/ErrorDialog.java
+++ b/subsonic-android/src/github/daneren2005/dsub/util/ErrorDialog.java
@@ -21,6 +21,8 @@ package github.daneren2005.dsub.util;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
+import android.content.Intent;
+import github.daneren2005.dsub.activity.MainActivity;
import github.daneren2005.dsub.R;
/**
@@ -43,7 +45,7 @@ public class ErrorDialog {
@Override
public void onCancel(DialogInterface dialogInterface) {
if (finishActivityOnClose) {
- activity.finish();
+ restart(activity);
}
}
});
@@ -51,11 +53,17 @@ public class ErrorDialog {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
if (finishActivityOnClose) {
- activity.finish();
+ restart(activity);
}
}
});
builder.create().show();
}
+
+ private void restart(Activity context) {
+ Intent intent = new Intent(context, MainActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ Util.startActivityWithoutTransition(context, intent);
+ }
}
diff --git a/subsonic-android/src/github/daneren2005/dsub/util/FileUtil.java b/subsonic-android/src/github/daneren2005/dsub/util/FileUtil.java
index 65f5a37f..7ead874c 100644
--- a/subsonic-android/src/github/daneren2005/dsub/util/FileUtil.java
+++ b/subsonic-android/src/github/daneren2005/dsub/util/FileUtil.java
@@ -129,6 +129,10 @@ public class FileUtil {
File dir = new File(getMusicDirectory(context).getPath() + "/" + fileSystemSafe(artist.getName()));
return dir;
}
+ public static File getArtistDirectory(Context context, MusicDirectory.Entry artist) {
+ File dir = new File(getMusicDirectory(context).getPath() + "/" + fileSystemSafe(artist.getTitle()));
+ return dir;
+ }
public static File getAlbumDirectory(Context context, MusicDirectory.Entry entry) {
File dir;
diff --git a/subsonic-android/src/github/daneren2005/dsub/util/ImageLoader.java b/subsonic-android/src/github/daneren2005/dsub/util/ImageLoader.java
index 61f20c0a..f84116f7 100644
--- a/subsonic-android/src/github/daneren2005/dsub/util/ImageLoader.java
+++ b/subsonic-android/src/github/daneren2005/dsub/util/ImageLoader.java
@@ -56,6 +56,7 @@ public class ImageLoader implements Runnable {
private Handler mHandler = new Handler();
private Context context;
private LruCache<String, Bitmap> cache;
+ private Bitmap nowPlaying;
private final BlockingQueue<Task> queue;
private final int imageSizeDefault;
private final int imageSizeLarge;
@@ -73,7 +74,7 @@ public class ImageLoader implements Runnable {
@Override
protected void entryRemoved(boolean evicted, String key, Bitmap oldBitmap, Bitmap newBitmap) {
- if(evicted) {
+ if(evicted && oldBitmap != nowPlaying) {
oldBitmap.recycle();
}
}
@@ -110,13 +111,16 @@ public class ImageLoader implements Runnable {
if (bitmap != null) {
final Drawable drawable = Util.createDrawableFromBitmap(this.context, bitmap);
setImage(view, drawable, large);
+ if(large) {
+ nowPlaying = bitmap;
+ }
return;
}
if (!large) {
setUnknownImage(view, large);
}
- queue.offer(new Task(view.getContext(), entry, size, imageSizeLarge, new ViewTaskHandler(view, crossfade)));
+ queue.offer(new Task(view.getContext(), entry, size, imageSizeLarge, large, new ViewTaskHandler(view, crossfade)));
}
public void loadImage(Context context, RemoteControlClient remoteControl, MusicDirectory.Entry entry) {
@@ -133,7 +137,7 @@ public class ImageLoader implements Runnable {
}
setUnknownImage(remoteControl);
- queue.offer(new Task(context, entry, imageSizeLarge, imageSizeLarge, new RemoteControlClientTaskHandler(remoteControl)));
+ queue.offer(new Task(context, entry, imageSizeLarge, imageSizeLarge, false, new RemoteControlClientTaskHandler(remoteControl)));
}
private String getKey(String coverArtId, int size) {
@@ -228,13 +232,15 @@ public class ImageLoader implements Runnable {
private final MusicDirectory.Entry mEntry;
private final int mSize;
private final int mSaveSize;
+ private final boolean mIsNowPlaying;
private ImageLoaderTaskHandler mTaskHandler;
- public Task(Context context, MusicDirectory.Entry entry, int size, int saveSize, ImageLoaderTaskHandler taskHandler) {
+ public Task(Context context, MusicDirectory.Entry entry, int size, int saveSize, boolean isNowPlaying, ImageLoaderTaskHandler taskHandler) {
mContext = context;
mEntry = entry;
mSize = size;
mSaveSize = saveSize;
+ mIsNowPlaying = isNowPlaying;
mTaskHandler = taskHandler;
}
@@ -255,6 +261,9 @@ public class ImageLoader implements Runnable {
cache.put(key, bitmap);
// Make sure key is the most recently "used"
cache.get(key);
+ if(mIsNowPlaying) {
+ nowPlaying = bitmap;
+ }
final Drawable drawable = Util.createDrawableFromBitmap(mContext, bitmap);
mTaskHandler.setDrawable(drawable);
diff --git a/subsonic-android/src/github/daneren2005/dsub/util/Util.java b/subsonic-android/src/github/daneren2005/dsub/util/Util.java
index 48cb7276..a8918923 100644
--- a/subsonic-android/src/github/daneren2005/dsub/util/Util.java
+++ b/subsonic-android/src/github/daneren2005/dsub/util/Util.java
@@ -165,11 +165,58 @@ public final class Util {
SharedPreferences prefs = getPreferences(context);
return prefs.getInt(Constants.PREFERENCES_KEY_SERVER_INSTANCE, 1);
}
+
+ public static int getServerCount(Context context) {
+ SharedPreferences prefs = getPreferences(context);
+ return prefs.getInt(Constants.PREFERENCES_KEY_SERVER_COUNT, 1);
+ }
+
+ public static void removeInstanceName(Context context, int instance, int activeInstance) {
+ SharedPreferences prefs = getPreferences(context);
+ SharedPreferences.Editor editor = prefs.edit();
+
+ int newInstance = instance + 1;
+
+ String server = prefs.getString(Constants.PREFERENCES_KEY_SERVER_KEY + newInstance, null);
+ String serverName = prefs.getString(Constants.PREFERENCES_KEY_SERVER_NAME + newInstance, null);
+ String serverUrl = prefs.getString(Constants.PREFERENCES_KEY_SERVER_URL + newInstance, null);
+ String userName = prefs.getString(Constants.PREFERENCES_KEY_USERNAME + newInstance, null);
+ String password = prefs.getString(Constants.PREFERENCES_KEY_PASSWORD + newInstance, null);
+
+ editor.putString(Constants.PREFERENCES_KEY_SERVER_KEY + instance, server);
+ editor.putString(Constants.PREFERENCES_KEY_SERVER_NAME + instance, serverName);
+ editor.putString(Constants.PREFERENCES_KEY_SERVER_URL + instance, serverUrl);
+ editor.putString(Constants.PREFERENCES_KEY_USERNAME + instance, userName);
+ editor.putString(Constants.PREFERENCES_KEY_PASSWORD + instance, password);
+
+ editor.putString(Constants.PREFERENCES_KEY_SERVER_KEY + newInstance, null);
+ editor.putString(Constants.PREFERENCES_KEY_SERVER_NAME + newInstance, null);
+ editor.putString(Constants.PREFERENCES_KEY_SERVER_URL + newInstance, null);
+ editor.putString(Constants.PREFERENCES_KEY_USERNAME + newInstance, null);
+ editor.putString(Constants.PREFERENCES_KEY_PASSWORD + newInstance, null);
+ editor.commit();
+
+ if (instance == activeInstance) {
+ Util.setActiveServer(context, 0);
+ }
+
+ if (newInstance == activeInstance) {
+ Util.setActiveServer(context, instance);
+ }
+ }
public static String getServerName(Context context, int instance) {
SharedPreferences prefs = getPreferences(context);
return prefs.getString(Constants.PREFERENCES_KEY_SERVER_NAME + instance, null);
}
+
+ public static String getUserName(Context context, int instance) {
+ if (instance == 0) {
+ return context.getResources().getString(R.string.main_offline);
+ }
+ SharedPreferences prefs = getPreferences(context);
+ return prefs.getString(Constants.PREFERENCES_KEY_USERNAME + instance, null);
+ }
public static void setServerRestVersion(Context context, Version version) {
SERVER_REST_VERSIONS.put(getActiveServer(context), version);
@@ -223,8 +270,15 @@ public final class Util {
}
public static int getPreloadCount(Context context) {
+ ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo networkInfo = manager.getActiveNetworkInfo();
+ if (networkInfo == null) {
+ return 3;
+ }
+
SharedPreferences prefs = getPreferences(context);
- int preloadCount = Integer.parseInt(prefs.getString(Constants.PREFERENCES_KEY_PRELOAD_COUNT, "-1"));
+ boolean wifi = networkInfo.getType() == ConnectivityManager.TYPE_WIFI;
+ int preloadCount = Integer.parseInt(prefs.getString(wifi ? Constants.PREFERENCES_KEY_PRELOAD_COUNT_WIFI : Constants.PREFERENCES_KEY_PRELOAD_COUNT_MOBILE, "-1"));
return preloadCount == -1 ? Integer.MAX_VALUE : preloadCount;
}
@@ -588,6 +642,10 @@ public final class Util {
throw new RuntimeException(x.getMessage(), x);
}
}
+
+ public static boolean isNullOrWhiteSpace(String string) {
+ return string == null || string.isEmpty() || string.trim().isEmpty();
+ }
public static boolean isNetworkConnected(Context context) {
ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
diff --git a/subsonic-android/src/github/daneren2005/dsub/view/AlbumListAdapter.java b/subsonic-android/src/github/daneren2005/dsub/view/AlbumListAdapter.java
index 6c8d315a..3ff8350b 100644
--- a/subsonic-android/src/github/daneren2005/dsub/view/AlbumListAdapter.java
+++ b/subsonic-android/src/github/daneren2005/dsub/view/AlbumListAdapter.java
@@ -34,15 +34,17 @@ public class AlbumListAdapter extends EndlessAdapter {
Context context;
ArrayAdapter<MusicDirectory.Entry> adapter;
String type;
+ String extra;
int size;
int offset;
List<MusicDirectory.Entry> entries;
- public AlbumListAdapter(Context context, ArrayAdapter<MusicDirectory.Entry> adapter, String type, int size) {
+ public AlbumListAdapter(Context context, ArrayAdapter<MusicDirectory.Entry> adapter, String type, String extra, int size) {
super(adapter);
this.context = context;
this.adapter = adapter;
this.type = type;
+ this.extra = extra;
this.size = size;
this.offset = size;
}
@@ -50,7 +52,12 @@ public class AlbumListAdapter extends EndlessAdapter {
@Override
protected boolean cacheInBackground() throws Exception {
MusicService service = MusicServiceFactory.getMusicService(context);
- MusicDirectory result = service.getAlbumList(type, size, offset, context, null);
+ MusicDirectory result;
+ if("genres".equals(type)) {
+ result = service.getSongsByGenre(extra, size, offset, context, null);
+ } else {
+ result = service.getAlbumList(type, size, offset, context, null);
+ }
entries = result.getChildren();
if(entries.size() > 0) {
return true;
diff --git a/subsonic-android/src/github/daneren2005/dsub/view/ArtistEntryView.java b/subsonic-android/src/github/daneren2005/dsub/view/ArtistEntryView.java
new file mode 100644
index 00000000..3b6a50e4
--- /dev/null
+++ b/subsonic-android/src/github/daneren2005/dsub/view/ArtistEntryView.java
@@ -0,0 +1,83 @@
+/*
+ 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 github.daneren2005.dsub.view;
+
+import android.content.Context;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.TextView;
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.MusicDirectory;
+import github.daneren2005.dsub.util.FileUtil;
+import github.daneren2005.dsub.util.ImageLoader;
+import github.daneren2005.dsub.util.Util;
+import java.io.File;
+/**
+ * Used to display albums in a {@code ListView}.
+ *
+ * @author Sindre Mehus
+ */
+public class ArtistEntryView extends UpdateView {
+ private static final String TAG = AlbumView.class.getSimpleName();
+
+ private Context context;
+ private MusicDirectory.Entry artist;
+
+ private TextView titleView;
+ private ImageButton starButton;
+ private ImageView moreButton;
+
+ public ArtistEntryView(Context context) {
+ super(context);
+ this.context = context;
+ LayoutInflater.from(context).inflate(R.layout.artist_list_item, this, true);
+
+ titleView = (TextView) findViewById(R.id.artist_name);
+ starButton = (ImageButton) findViewById(R.id.artist_star);
+ moreButton = (ImageView) findViewById(R.id.artist_more);
+ moreButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ v.showContextMenu();
+ }
+ });
+ }
+
+ public void setArtist(MusicDirectory.Entry artist) {
+ this.artist = artist;
+
+ titleView.setText(artist.getTitle());
+ starButton.setVisibility((Util.isOffline(getContext()) || !artist.isStarred()) ? View.GONE : View.VISIBLE);
+ starButton.setFocusable(false);
+ update();
+ }
+
+ @Override
+ protected void update() {
+ starButton.setVisibility((Util.isOffline(getContext()) || !artist.isStarred()) ? View.GONE : View.VISIBLE);
+ File file = FileUtil.getArtistDirectory(context, artist);
+ if(file.exists()) {
+ moreButton.setImageResource(R.drawable.list_item_more_shaded);
+ } else {
+ moreButton.setImageResource(R.drawable.list_item_more);
+ }
+ }
+}
diff --git a/subsonic-android/src/github/daneren2005/dsub/view/ChatAdapter.java b/subsonic-android/src/github/daneren2005/dsub/view/ChatAdapter.java
new file mode 100644
index 00000000..518f81ef
--- /dev/null
+++ b/subsonic-android/src/github/daneren2005/dsub/view/ChatAdapter.java
@@ -0,0 +1,100 @@
+package github.daneren2005.dsub.view;
+
+import android.text.method.LinkMovementMethod;
+import android.text.util.Linkify;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.TextView;
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.activity.SubsonicActivity;
+import github.daneren2005.dsub.domain.ChatMessage;
+import github.daneren2005.dsub.util.Util;
+
+import java.text.DateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.regex.Pattern;
+
+public class ChatAdapter extends ArrayAdapter<ChatMessage> {
+
+ private final SubsonicActivity activity;
+ private ArrayList<ChatMessage> messages;
+
+ private static final String phoneRegex = "1?\\W*([2-9][0-8][0-9])\\W*([2-9][0-9]{2})\\W*([0-9]{4})"; //you can just place your support phone here
+ private static final Pattern phoneMatcher = Pattern.compile(phoneRegex);
+
+ public ChatAdapter(SubsonicActivity activity, ArrayList<ChatMessage> messages) {
+ super(activity, R.layout.chat_item, messages);
+ this.activity = activity;
+ this.messages = messages;
+ }
+
+ @Override
+ public int getCount() {
+ return messages.size();
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ ChatMessage message = this.getItem(position);
+
+ ViewHolder holder;
+ int layout;
+
+ String messageUser = message.getUsername();
+ Date messageTime = new java.util.Date(message.getTime());
+ String messageText = message.getMessage();
+
+ String me = Util.getUserName(activity, Util.getActiveServer(activity));
+
+ if (messageUser.equals(me)) {
+ layout = R.layout.chat_item_reverse;
+ } else {
+ layout = R.layout.chat_item;
+ }
+
+ if (convertView == null)
+ {
+ holder = new ViewHolder();
+
+ convertView = LayoutInflater.from(activity).inflate(layout, parent, false);
+
+ TextView usernameView = (TextView) convertView.findViewById(R.id.chat_username);
+ TextView timeView = (TextView) convertView.findViewById(R.id.chat_time);
+ TextView messageView = (TextView) convertView.findViewById(R.id.chat_message);
+
+ messageView.setMovementMethod(LinkMovementMethod.getInstance());
+ Linkify.addLinks(messageView, Linkify.EMAIL_ADDRESSES);
+ Linkify.addLinks(messageView, Linkify.WEB_URLS);
+ Linkify.addLinks(messageView, phoneMatcher, "tel:");
+
+ holder.message = messageView;
+ holder.username = usernameView;
+ holder.time = timeView;
+
+ convertView.setTag(holder);
+ }
+ else
+ {
+ holder = (ViewHolder) convertView.getTag();
+ }
+
+ DateFormat timeFormat = android.text.format.DateFormat.getTimeFormat(activity);
+ String messageTimeFormatted = String.format("[%s]", timeFormat.format(messageTime));
+
+ holder.username.setText(messageUser);
+ holder.message.setText(messageText);
+ holder.time.setText(messageTimeFormatted);
+
+ return convertView;
+ }
+
+ private static class ViewHolder
+ {
+ TextView message;
+ TextView username;
+ TextView time;
+ }
+}
diff --git a/subsonic-android/src/github/daneren2005/dsub/view/EntryAdapter.java b/subsonic-android/src/github/daneren2005/dsub/view/EntryAdapter.java
index 0d67c4c4..1a4544e0 100644
--- a/subsonic-android/src/github/daneren2005/dsub/view/EntryAdapter.java
+++ b/subsonic-android/src/github/daneren2005/dsub/view/EntryAdapter.java
@@ -54,11 +54,16 @@ public class EntryAdapter extends ArrayAdapter<MusicDirectory.Entry> {
MusicDirectory.Entry entry = getItem(position);
if (entry.isDirectory()) {
- AlbumView view;
- view = new AlbumView(activity);
- view.setAlbum(entry, imageLoader);
- return view;
-
+ if(entry.getParent() != null) {
+ AlbumView view;
+ view = new AlbumView(activity);
+ view.setAlbum(entry, imageLoader);
+ return view;
+ } else {
+ ArtistEntryView view = new ArtistEntryView(activity);
+ view.setArtist(entry);
+ return view;
+ }
} else {
SongView view;
if (convertView != null && convertView instanceof SongView) {
diff --git a/subsonic-android/src/github/daneren2005/dsub/view/GenreAdapter.java b/subsonic-android/src/github/daneren2005/dsub/view/GenreAdapter.java
new file mode 100644
index 00000000..b98efd20
--- /dev/null
+++ b/subsonic-android/src/github/daneren2005/dsub/view/GenreAdapter.java
@@ -0,0 +1,59 @@
+/*
+ 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 2010 (C) Sindre Mehus
+ */
+package github.daneren2005.dsub.view;
+
+import android.widget.ArrayAdapter;
+import android.widget.SectionIndexer;
+import android.content.Context;
+import android.view.View;
+import android.view.ViewGroup;
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.Genre;
+
+import java.util.List;
+import java.util.Set;
+import java.util.LinkedHashSet;
+import java.util.ArrayList;
+
+/**
+ * @author Sindre Mehus
+*/
+public class GenreAdapter extends ArrayAdapter<Genre>{
+ private Context activity;
+ private List<Genre> genres;
+
+ public GenreAdapter(Context context, List<Genre> genres) {
+ super(context, android.R.layout.simple_list_item_1, genres);
+ this.activity = context;
+ this.genres = genres;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ Genre genre = genres.get(position);
+ GenreView view;
+ if (convertView != null && convertView instanceof GenreView) {
+ view = (GenreView) convertView;
+ } else {
+ view = new GenreView(activity);
+ }
+ view.setGenre(genre);
+ return view;
+ }
+}
diff --git a/subsonic-android/src/github/daneren2005/dsub/view/GenreView.java b/subsonic-android/src/github/daneren2005/dsub/view/GenreView.java
new file mode 100644
index 00000000..dbb0248b
--- /dev/null
+++ b/subsonic-android/src/github/daneren2005/dsub/view/GenreView.java
@@ -0,0 +1,53 @@
+/*
+ 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 github.daneren2005.dsub.view;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.TextView;
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.Genre;
+
+public class GenreView extends UpdateView {
+ private static final String TAG = GenreView.class.getSimpleName();
+
+ private TextView titleView;
+ private ImageButton starButton;
+ private ImageView moreButton;
+
+ public GenreView(Context context) {
+ super(context);
+ LayoutInflater.from(context).inflate(R.layout.artist_list_item, this, true);
+
+ titleView = (TextView) findViewById(R.id.artist_name);
+ starButton = (ImageButton) findViewById(R.id.artist_star);
+ moreButton = (ImageView) findViewById(R.id.artist_more);
+ moreButton.setClickable(false);
+ }
+
+ public void setGenre(Genre genre) {
+ titleView.setText(genre.getName());
+
+ starButton.setVisibility(View.GONE);
+ starButton.setFocusable(false);
+ }
+}
diff --git a/subsonic-android/src/github/daneren2005/dsub/view/SongView.java b/subsonic-android/src/github/daneren2005/dsub/view/SongView.java
index 51927304..40bedad6 100644
--- a/subsonic-android/src/github/daneren2005/dsub/view/SongView.java
+++ b/subsonic-android/src/github/daneren2005/dsub/view/SongView.java
@@ -72,23 +72,6 @@ public class SongView extends UpdateView implements Checkable {
public void setSong(MusicDirectory.Entry song, boolean checkable) {
this.song = song;
- if(Util.isOffline(context)) {
- DownloadFile downloadFile = new DownloadFile(context, song, false);
- File file = downloadFile.getCompleteFile();
- if(file.exists()) {
- try {
- MediaMetadataRetriever metadata = new MediaMetadataRetriever();
- metadata.setDataSource(file.getAbsolutePath());
- String bitrate = metadata.extractMetadata(MediaMetadataRetriever.METADATA_KEY_BITRATE);
- song.setBitRate(Integer.parseInt((bitrate != null) ? bitrate : "0") / 1000);
- String length = metadata.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
- song.setDuration(Integer.parseInt(length) / 1000);
- } catch(Exception e) {
- Log.i(TAG, "Device doesn't properly support MediaMetadataRetreiver");
- }
- }
- }
-
StringBuilder artist = new StringBuilder(40);
String bitRate = null;