Add album/track star support

Changed lowest REST protocol level to 1.8.0, changed downloaded icon,
added discNumber support
This commit is contained in:
Joshua Bahnsen 2013-01-23 00:57:13 -07:00
parent 19839cfd0d
commit 72111240e7
20 changed files with 1187 additions and 1014 deletions

View File

@ -2,7 +2,7 @@
<manifest xmlns:a="http://schemas.android.com/apk/res/android" <manifest xmlns:a="http://schemas.android.com/apk/res/android"
package="net.sourceforge.subsonic.androidapp" package="net.sourceforge.subsonic.androidapp"
a:versionCode="47" a:versionCode="47"
a:versionName="3.9.9.8" a:installLocation="auto"> a:versionName="3.9.9.9" a:installLocation="auto">
<uses-permission a:name="android.permission.INTERNET"/> <uses-permission a:name="android.permission.INTERNET"/>
<uses-permission a:name="android.permission.READ_PHONE_STATE"/> <uses-permission a:name="android.permission.READ_PHONE_STATE"/>

View File

@ -131,158 +131,162 @@ public final class R {
public static final int select_album_play_all_normal=0x7f02004b; public static final int select_album_play_all_normal=0x7f02004b;
public static final int select_album_play_all_pressed=0x7f02004c; public static final int select_album_play_all_pressed=0x7f02004c;
public static final int slider_knob=0x7f02004d; public static final int slider_knob=0x7f02004d;
public static final int status_bg=0x7f02004e; public static final int star=0x7f02004e;
public static final int status_next=0x7f02004f; public static final int star_hollow=0x7f02004f;
public static final int status_pause=0x7f020050; public static final int status_bg=0x7f020050;
public static final int status_play=0x7f020051; public static final int status_next=0x7f020051;
public static final int status_prev=0x7f020052; public static final int status_pause=0x7f020052;
public static final int status_stop=0x7f020053; public static final int status_play=0x7f020053;
public static final int title_bar_shadow=0x7f020054; public static final int status_prev=0x7f020054;
public static final int unknown_album=0x7f020055; public static final int status_stop=0x7f020055;
public static final int unknown_album_large=0x7f020056; public static final int title_bar_shadow=0x7f020056;
public static final int unknown_album=0x7f020057;
public static final int unknown_album_large=0x7f020058;
} }
public static final class id { public static final class id {
public static final int album=0x7f0d0048; public static final int album=0x7f0d0049;
public static final int album_artist=0x7f0d0002; public static final int album_artist=0x7f0d0002;
public static final int album_coverart=0x7f0d0000; public static final int album_coverart=0x7f0d0000;
public static final int album_menu_pin=0x7f0d0085; public static final int album_menu_pin=0x7f0d0087;
public static final int album_menu_play_last=0x7f0d0084; public static final int album_menu_play_last=0x7f0d0086;
public static final int album_menu_play_now=0x7f0d0083; public static final int album_menu_play_now=0x7f0d0085;
public static final int album_star=0x7f0d0003;
public static final int album_title=0x7f0d0001; public static final int album_title=0x7f0d0001;
public static final int appwidget_coverart=0x7f0d0003; public static final int appwidget_coverart=0x7f0d0004;
public static final int appwidget_top=0x7f0d0005; public static final int appwidget_top=0x7f0d0006;
public static final int artist=0x7f0d0007; public static final int artist=0x7f0d0008;
public static final int artist_menu_pin=0x7f0d0088; public static final int artist_menu_pin=0x7f0d008a;
public static final int artist_menu_play_last=0x7f0d0087; public static final int artist_menu_play_last=0x7f0d0089;
public static final int artist_menu_play_now=0x7f0d0086; public static final int artist_menu_play_now=0x7f0d0088;
public static final int button_bar=0x7f0d000b; public static final int button_bar=0x7f0d000c;
public static final int button_bar_home=0x7f0d000c; public static final int button_bar_home=0x7f0d000d;
public static final int button_bar_music=0x7f0d000d; public static final int button_bar_music=0x7f0d000e;
public static final int button_bar_now_playing=0x7f0d0010; public static final int button_bar_now_playing=0x7f0d0011;
public static final int button_bar_playlists=0x7f0d000f; public static final int button_bar_playlists=0x7f0d0010;
public static final int button_bar_search=0x7f0d000e; public static final int button_bar_search=0x7f0d000f;
public static final int control_next=0x7f0d000a; public static final int control_next=0x7f0d000b;
public static final int control_play=0x7f0d0009; public static final int control_play=0x7f0d000a;
public static final int control_previous=0x7f0d0008; public static final int control_previous=0x7f0d0009;
public static final int control_stop=0x7f0d0049; public static final int control_stop=0x7f0d004a;
public static final int download_album=0x7f0d0024; public static final int download_album=0x7f0d0025;
public static final int download_album_art_image=0x7f0d001e; public static final int download_album_art_image=0x7f0d001f;
public static final int download_album_art_layout=0x7f0d001d; public static final int download_album_art_layout=0x7f0d001e;
public static final int download_artist=0x7f0d0023; public static final int download_artist=0x7f0d0024;
public static final int download_button_bar_flipper=0x7f0d0020; public static final int download_button_bar_flipper=0x7f0d0021;
public static final int download_control_layout=0x7f0d0011; public static final int download_control_layout=0x7f0d0012;
public static final int download_duration=0x7f0d0022; public static final int download_duration=0x7f0d0023;
public static final int download_empty=0x7f0d0025; public static final int download_empty=0x7f0d0026;
public static final int download_equalizer=0x7f0d0079; public static final int download_equalizer=0x7f0d007b;
public static final int download_jukebox=0x7f0d007b; public static final int download_jukebox=0x7f0d007d;
public static final int download_list=0x7f0d0026; public static final int download_list=0x7f0d0027;
public static final int download_next=0x7f0d0018; public static final int download_next=0x7f0d0018;
public static final int download_pause=0x7f0d0015; public static final int download_pause=0x7f0d0015;
public static final int download_playlist_flipper=0x7f0d001c; public static final int download_playlist_flipper=0x7f0d001d;
public static final int download_position=0x7f0d0021; public static final int download_position=0x7f0d0022;
public static final int download_previous=0x7f0d0014; public static final int download_previous=0x7f0d0014;
public static final int download_progress_bar=0x7f0d0027; public static final int download_progress_bar=0x7f0d0028;
public static final int download_repeat=0x7f0d0013; public static final int download_repeat=0x7f0d0019;
public static final int download_shuffle=0x7f0d0012; public static final int download_shuffle=0x7f0d0013;
public static final int download_song_title=0x7f0d001b; public static final int download_song_title=0x7f0d001c;
public static final int download_start=0x7f0d0017; public static final int download_start=0x7f0d0017;
public static final int download_status=0x7f0d001a; public static final int download_status=0x7f0d001b;
public static final int download_stop=0x7f0d0016; public static final int download_stop=0x7f0d0016;
public static final int download_toggle_list=0x7f0d0019; public static final int download_toggle_list=0x7f0d001a;
public static final int download_visualizer=0x7f0d007a; public static final int download_visualizer=0x7f0d007c;
public static final int download_visualizer_view_layout=0x7f0d001f; public static final int download_visualizer_view_layout=0x7f0d0020;
public static final int equalizer_bar=0x7f0d002d; public static final int equalizer_bar=0x7f0d002e;
public static final int equalizer_frequency=0x7f0d002b; public static final int equalizer_frequency=0x7f0d002c;
public static final int equalizer_level=0x7f0d002c; public static final int equalizer_level=0x7f0d002d;
public static final int equalizer_enabled=0x7f0d0028; public static final int equalizer_enabled=0x7f0d0029;
public static final int equalizer_layout=0x7f0d0029; public static final int equalizer_layout=0x7f0d002a;
public static final int equalizer_preset=0x7f0d002a; public static final int equalizer_preset=0x7f0d002b;
public static final int help_back=0x7f0d002f; public static final int help_back=0x7f0d0030;
public static final int help_buttons=0x7f0d002e; public static final int help_buttons=0x7f0d002f;
public static final int help_close=0x7f0d0030; public static final int help_close=0x7f0d0031;
public static final int help_contents=0x7f0d0031; public static final int help_contents=0x7f0d0032;
public static final int icon=0x7f0d006e; public static final int icon=0x7f0d0070;
public static final int jukebox_volume_progress_bar=0x7f0d0033; public static final int jukebox_volume_progress_bar=0x7f0d0034;
public static final int linearLayout1=0x7f0d0004; public static final int linearLayout1=0x7f0d0005;
public static final int lyrics_artist=0x7f0d0035; public static final int lyrics_artist=0x7f0d0036;
public static final int lyrics_scrollview=0x7f0d0034; public static final int lyrics_scrollview=0x7f0d0035;
public static final int lyrics_text=0x7f0d0037; public static final int lyrics_text=0x7f0d0038;
public static final int lyrics_title=0x7f0d0036; public static final int lyrics_title=0x7f0d0037;
public static final int main_select_server_1=0x7f0d003b; public static final int main_select_server_1=0x7f0d003c;
public static final int main_select_server_2=0x7f0d003c; public static final int main_select_server_2=0x7f0d003d;
public static final int main_albums=0x7f0d003d; public static final int main_albums=0x7f0d003e;
public static final int main_albums_frequent=0x7f0d0040; public static final int main_albums_frequent=0x7f0d0041;
public static final int main_albums_highest=0x7f0d0041; public static final int main_albums_highest=0x7f0d0042;
public static final int main_albums_newest=0x7f0d003e; public static final int main_albums_newest=0x7f0d003f;
public static final int main_albums_random=0x7f0d0043; public static final int main_albums_random=0x7f0d0044;
public static final int main_albums_recent=0x7f0d003f; public static final int main_albums_recent=0x7f0d0040;
public static final int main_albums_starred=0x7f0d0042; public static final int main_albums_starred=0x7f0d0043;
public static final int main_dummy=0x7f0d0039; public static final int main_dummy=0x7f0d003a;
public static final int main_list=0x7f0d0038; public static final int main_list=0x7f0d0039;
public static final int main_select_server=0x7f0d003a; public static final int main_select_server=0x7f0d003b;
public static final int main_shuffle=0x7f0d0078; public static final int main_shuffle=0x7f0d007a;
public static final int menu_exit=0x7f0d0077; public static final int menu_exit=0x7f0d0079;
public static final int menu_help=0x7f0d0076; public static final int menu_help=0x7f0d0078;
public static final int menu_lyrics=0x7f0d0080; public static final int menu_lyrics=0x7f0d0082;
public static final int menu_refresh=0x7f0d0089; public static final int menu_refresh=0x7f0d008b;
public static final int menu_remove=0x7f0d0081; public static final int menu_remove=0x7f0d0083;
public static final int menu_remove_all=0x7f0d007d; public static final int menu_remove_all=0x7f0d007f;
public static final int menu_save_playlist=0x7f0d007c; public static final int menu_save_playlist=0x7f0d007e;
public static final int menu_screen_on_off=0x7f0d007e; public static final int menu_screen_on_off=0x7f0d0080;
public static final int menu_settings=0x7f0d0075; public static final int menu_settings=0x7f0d0077;
public static final int menu_show_album=0x7f0d007f; public static final int menu_show_album=0x7f0d0081;
public static final int menu_shuffle=0x7f0d0082; public static final int menu_shuffle=0x7f0d0084;
public static final int notification_image=0x7f0d0045; public static final int notification_image=0x7f0d0046;
public static final int play_video_contents=0x7f0d004a; public static final int play_video_contents=0x7f0d004b;
public static final int progress_message=0x7f0d004b; public static final int progress_message=0x7f0d004c;
public static final int save_playlist_name=0x7f0d004d; public static final int save_playlist_name=0x7f0d004e;
public static final int save_playlist_root=0x7f0d004c; public static final int save_playlist_root=0x7f0d004d;
public static final int search_albums=0x7f0d0051; public static final int search_albums=0x7f0d0052;
public static final int search_artists=0x7f0d0050; public static final int search_artists=0x7f0d0051;
public static final int search_list=0x7f0d004e; public static final int search_list=0x7f0d004f;
public static final int search_more_albums=0x7f0d0054; public static final int search_more_albums=0x7f0d0055;
public static final int search_more_artists=0x7f0d0053; public static final int search_more_artists=0x7f0d0054;
public static final int search_more_songs=0x7f0d0055; public static final int search_more_songs=0x7f0d0056;
public static final int search_search=0x7f0d004f; public static final int search_search=0x7f0d0050;
public static final int search_songs=0x7f0d0052; public static final int search_songs=0x7f0d0053;
public static final int select_album_cover_art=0x7f0d005f; public static final int select_album_cover_art=0x7f0d0060;
public static final int select_album_delete=0x7f0d005d; public static final int select_album_delete=0x7f0d005e;
public static final int select_album_empty=0x7f0d0056; public static final int select_album_empty=0x7f0d0057;
public static final int select_album_entries=0x7f0d0057; public static final int select_album_entries=0x7f0d0058;
public static final int select_album_more=0x7f0d005e; public static final int select_album_more=0x7f0d005f;
public static final int select_album_pin=0x7f0d005b; public static final int select_album_pin=0x7f0d005c;
public static final int select_album_play_all=0x7f0d0062; public static final int select_album_play_all=0x7f0d0063;
public static final int select_album_play_last=0x7f0d005a; public static final int select_album_play_last=0x7f0d005b;
public static final int select_album_play_now=0x7f0d0059; public static final int select_album_play_now=0x7f0d005a;
public static final int select_album_select=0x7f0d0058; public static final int select_album_select=0x7f0d0059;
public static final int select_album_text1=0x7f0d0060; public static final int select_album_text1=0x7f0d0061;
public static final int select_album_text2=0x7f0d0061; public static final int select_album_text2=0x7f0d0062;
public static final int select_album_unpin=0x7f0d005c; public static final int select_album_unpin=0x7f0d005d;
public static final int select_artist_folder=0x7f0d0064; public static final int select_artist_folder=0x7f0d0065;
public static final int select_artist_folder_1=0x7f0d0065; public static final int select_artist_folder_1=0x7f0d0066;
public static final int select_artist_folder_2=0x7f0d0066; public static final int select_artist_folder_2=0x7f0d0067;
public static final int select_artist_list=0x7f0d0063; public static final int select_artist_list=0x7f0d0064;
public static final int select_playlist_empty=0x7f0d0067; public static final int select_playlist_empty=0x7f0d0068;
public static final int select_playlist_list=0x7f0d0068; public static final int select_playlist_list=0x7f0d0069;
public static final int song_artist=0x7f0d006c; public static final int song_artist=0x7f0d006d;
public static final int song_check=0x7f0d0069; public static final int song_check=0x7f0d006a;
public static final int song_duration=0x7f0d006d; public static final int song_duration=0x7f0d006e;
public static final int song_menu_play_last=0x7f0d008c; public static final int song_menu_play_last=0x7f0d008e;
public static final int song_menu_play_next=0x7f0d008b; public static final int song_menu_play_next=0x7f0d008d;
public static final int song_menu_play_now=0x7f0d008a; public static final int song_menu_play_now=0x7f0d008c;
public static final int song_status=0x7f0d006b; public static final int song_star=0x7f0d006f;
public static final int song_title=0x7f0d006a; public static final int song_status=0x7f0d006c;
public static final int status_icon=0x7f0d0046; public static final int song_title=0x7f0d006b;
public static final int status_media_collapse=0x7f0d0072; public static final int status_icon=0x7f0d0047;
public static final int status_media_next=0x7f0d0071; public static final int status_media_collapse=0x7f0d0074;
public static final int status_media_play=0x7f0d0070; public static final int status_media_next=0x7f0d0073;
public static final int status_media_prev=0x7f0d006f; public static final int status_media_play=0x7f0d0072;
public static final int statusbar=0x7f0d0044; public static final int status_media_prev=0x7f0d0071;
public static final int tab_progress=0x7f0d0073; public static final int statusbar=0x7f0d0045;
public static final int tab_progress_message=0x7f0d0074; public static final int tab_progress=0x7f0d0075;
public static final int title=0x7f0d0006; public static final int tab_progress_message=0x7f0d0076;
public static final int toast_layout_root=0x7f0d0032; public static final int title=0x7f0d0007;
public static final int trackname=0x7f0d0047; public static final int toast_layout_root=0x7f0d0033;
public static final int trackname=0x7f0d0048;
} }
public static final class integer { public static final class integer {
public static final int config_activityDefaultDur=0x7f080001; public static final int config_activityDefaultDur=0x7f080001;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
res/drawable-hdpi/star.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -13,38 +13,49 @@
a:layout_height="wrap_content" a:layout_alignParentTop="true" a:layout_height="wrap_content" a:layout_alignParentTop="true"
a:layout_alignParentLeft="true"> a:layout_alignParentLeft="true">
<ImageButton a:id="@+id/download_shuffle" a:src="@drawable/media_shuffle_normal" <ImageButton a:id="@+id/download_shuffle"
a:background="@android:color/transparent" a:padding="0dip" a:layout_width="0dip" a:layout_height="match_parent"
a:layout_width="wrap_content" a:layout_height="wrap_content" a:layout_gravity="center_vertical" a:layout_weight="1"
a:layout_gravity="center_vertical" /> a:background="@drawable/list_selector_holo_dark" a:paddingLeft="4dip"
<ImageButton a:id="@+id/download_repeat" a:src="@drawable/media_repeat_off" a:src="@drawable/media_shuffle_normal" />
a:background="@android:color/transparent" a:padding="0dip"
a:layout_width="wrap_content" a:layout_height="wrap_content" <ImageButton a:id="@+id/download_previous"
a:layout_gravity="center_vertical" /> a:layout_width="0dp" a:layout_height="match_parent"
<ImageButton a:id="@+id/download_previous" a:src="@drawable/media_backward_normal" a:layout_gravity="center_vertical" a:layout_weight="1"
a:background="@android:color/transparent" a:padding="0dip" a:background="@drawable/list_selector_holo_dark" a:padding="0dip"
a:layout_width="wrap_content" a:layout_height="wrap_content" a:src="@drawable/media_backward_normal" />
a:layout_gravity="center_vertical" />
<ImageButton a:id="@+id/download_pause" a:src="@drawable/media_pause_normal" <ImageButton a:id="@+id/download_pause" a:layout_width="0dp"
a:background="@android:color/transparent" a:padding="0dip" a:layout_height="match_parent" a:layout_gravity="center_vertical"
a:layout_width="wrap_content" a:layout_height="wrap_content" a:layout_weight="1" a:background="@drawable/list_selector_holo_dark"
a:layout_gravity="center_vertical" /> a:padding="0dip" a:src="@drawable/media_pause_normal" />
<ImageButton a:id="@+id/download_stop" a:src="@drawable/media_stop_normal"
a:background="@android:color/transparent" a:padding="0dip" <ImageButton a:id="@+id/download_stop" a:layout_width="0dp"
a:layout_width="wrap_content" a:layout_height="wrap_content" a:layout_height="match_parent" a:layout_gravity="center_vertical"
a:layout_gravity="center_vertical" /> a:layout_weight="1" a:background="@drawable/list_selector_holo_dark"
<ImageButton a:id="@+id/download_start" a:src="@drawable/media_start_normal" a:padding="0dip" a:src="@drawable/media_stop_normal" />
a:background="@android:color/transparent" a:padding="0dip"
a:layout_width="wrap_content" a:layout_height="wrap_content" <ImageButton a:id="@+id/download_start" a:layout_width="0dp"
a:layout_gravity="center_vertical" /> a:layout_height="match_parent" a:layout_gravity="center_vertical"
<ImageButton a:id="@+id/download_next" a:src="@drawable/media_forward_normal" a:layout_weight="1" a:background="@drawable/list_selector_holo_dark"
a:background="@android:color/transparent" a:padding="0dip" a:padding="0dip" a:src="@drawable/media_start_normal" />
a:layout_width="wrap_content" a:layout_height="wrap_content"
a:layout_gravity="center_vertical" /> <ImageButton a:id="@+id/download_next" a:layout_width="0dp"
<ImageButton a:id="@+id/download_toggle_list" a:src="@drawable/media_toggle_list_normal" a:layout_height="match_parent" a:layout_gravity="center_vertical"
a:background="@android:color/transparent" a:padding="0dip" a:layout_weight="1" a:background="@drawable/list_selector_holo_dark"
a:layout_width="wrap_content" a:layout_height="wrap_content" a:padding="0dip" a:src="@drawable/media_forward_normal" />
a:layout_gravity="center_vertical" />
<ImageButton a:id="@+id/download_repeat" a:layout_width="0dip"
a:layout_height="match_parent" a:layout_gravity="center_vertical"
a:layout_weight="1" a:background="@drawable/list_selector_holo_dark"
a:src="@drawable/media_repeat_off" />
<ImageButton a:id="@+id/download_toggle_list"
a:layout_width="0dip" a:layout_height="fill_parent"
a:layout_gravity="center_vertical" a:layout_weight="1"
a:background="@drawable/list_selector_holo_dark" a:paddingRight="4dip"
a:src="@drawable/media_toggle_list_normal" a:paddingTop="12dip"
a:paddingBottom="12dip" />
</LinearLayout> </LinearLayout>
<TextView a:id="@+id/download_status" a:layout_width="wrap_content" <TextView a:id="@+id/download_status" a:layout_width="wrap_content"
@ -56,8 +67,9 @@
<TextView a:id="@+id/download_song_title" a:layout_width="150dip" <TextView a:id="@+id/download_song_title" a:layout_width="150dip"
a:layout_height="wrap_content" a:layout_gravity="center_horizontal" a:layout_height="wrap_content" a:layout_gravity="center_horizontal"
a:layout_above="@+id/download_status" a:layout_centerHorizontal="true" a:layout_above="@+id/download_status" a:layout_centerHorizontal="true"
a:layout_marginLeft="12dip" a:layout_marginRight="12dip" a:layout_marginTop="12dip" a:maxLines="2" a:layout_marginLeft="12dip" a:layout_marginRight="12dip"
a:ellipsize="end" a:gravity="center_horizontal" a:textAppearance="?android:attr/textAppearanceMedium" a:layout_marginTop="12dip" a:maxLines="2" a:ellipsize="end"
a:gravity="center_horizontal" a:textAppearance="?android:attr/textAppearanceMedium"
a:textColor="@color/mediaControlForeground" /> a:textColor="@color/mediaControlForeground" />
</RelativeLayout> </RelativeLayout>
@ -67,13 +79,14 @@
a:layout_height="fill_parent" a:layout_alignParentLeft="true" a:layout_height="fill_parent" a:layout_alignParentLeft="true"
a:layout_toLeftOf="@+id/download_control_layout"> a:layout_toLeftOf="@+id/download_control_layout">
<FrameLayout a:id="@+id/download_album_art_layout" a:layout_width="fill_parent" a:layout_height="fill_parent" <FrameLayout a:id="@+id/download_album_art_layout"
a:background="@color/black"> a:layout_width="fill_parent" a:layout_height="fill_parent"
a:background="@color/black">
<ImageView a:id="@+id/download_album_art_image" <ImageView a:id="@+id/download_album_art_image"
a:layout_width="wrap_content" a:layout_height="wrap_content" a:layout_width="wrap_content" a:layout_height="wrap_content"
a:layout_gravity="center" a:layout_marginTop="0dip" a:scaleType="fitCenter" /> a:layout_gravity="center" a:layout_marginTop="0dip" a:scaleType="fitCenter" />
<LinearLayout a:id="@+id/download_visualizer_view_layout" <LinearLayout a:id="@+id/download_visualizer_view_layout"
a:layout_width="fill_parent" a:layout_height="60dip" a:layout_width="fill_parent" a:layout_height="60dip"
a:layout_marginLeft="60dip" a:layout_marginRight="60dip" a:layout_marginLeft="60dip" a:layout_marginRight="60dip"

View File

@ -6,15 +6,10 @@
a:id="@+id/download_playlist_flipper" a:layout_width="fill_parent" a:id="@+id/download_playlist_flipper" a:layout_width="fill_parent"
a:layout_height="0dip" a:layout_weight="1"> a:layout_height="0dip" a:layout_weight="1">
<RelativeLayout <RelativeLayout a:id="@+id/download_album_art_layout"
a:id="@+id/download_album_art_layout" a:layout_width="fill_parent" a:layout_height="fill_parent"
a:layout_width="fill_parent" a:layout_weight="1" a:gravity="left" a:orientation="vertical"
a:layout_height="fill_parent" a:background="@color/black">
a:layout_weight="1"
a:gravity="left"
a:orientation="vertical"
a:background="@color/black"
>
<ImageView a:id="@+id/download_album_art_image" <ImageView a:id="@+id/download_album_art_image"
a:layout_width="wrap_content" a:layout_height="wrap_content" a:layout_width="wrap_content" a:layout_height="wrap_content"
@ -29,9 +24,10 @@
<TextView a:id="@+id/download_song_title" a:layout_width="wrap_content" <TextView a:id="@+id/download_song_title" a:layout_width="wrap_content"
a:layout_height="wrap_content" a:layout_gravity="center_horizontal" a:layout_height="wrap_content" a:layout_gravity="center_horizontal"
a:layout_above="@+id/download_status" a:layout_centerHorizontal="true" a:layout_above="@+id/download_status" a:layout_centerHorizontal="true"
a:layout_marginLeft="16dip" a:layout_marginRight="16dip" a:layout_marginTop="16dip" a:layout_marginLeft="16dip" a:layout_marginRight="16dip"
a:singleLine="true" a:textColor="@color/mediaControlForeground" a:layout_marginTop="16dip" a:singleLine="true"
a:textStyle="bold" a:textSize="18sp" a:ellipsize="end" /> a:textColor="@color/mediaControlForeground" a:textStyle="bold"
a:textSize="18sp" a:ellipsize="end" />
<LinearLayout a:id="@+id/download_visualizer_view_layout" <LinearLayout a:id="@+id/download_visualizer_view_layout"
a:layout_width="fill_parent" a:layout_height="60dip" a:layout_width="fill_parent" a:layout_height="60dip"
@ -48,16 +44,11 @@
a:layout_height="wrap_content" a:layout_marginTop="0dip" a:layout_height="wrap_content" a:layout_marginTop="0dip"
a:background="@color/mediaControlBackground" a:orientation="horizontal" a:background="@color/mediaControlBackground" a:orientation="horizontal"
a:paddingBottom="0dip" a:paddingTop="0dip"> a:paddingBottom="0dip" a:paddingTop="0dip">
<ImageButton a:id="@+id/download_shuffle" a:layout_width="0dip" <ImageButton a:id="@+id/download_shuffle" a:layout_width="0dip"
a:layout_height="fill_parent" a:layout_gravity="center_vertical|left" a:layout_height="match_parent" a:layout_gravity="center_vertical"
a:layout_weight="1" a:background="@drawable/list_selector_holo_dark" a:layout_weight="1" a:background="@drawable/list_selector_holo_dark"
a:paddingLeft="4dip" a:src="@drawable/media_shuffle_normal" /> a:paddingLeft="4dip" a:src="@drawable/media_shuffle_normal" />
<ImageButton a:id="@+id/download_repeat" a:layout_width="0dip"
a:layout_height="fill_parent" a:layout_gravity="center_vertical|left"
a:layout_weight="1" a:background="@drawable/list_selector_holo_dark"
a:paddingLeft="4dip" a:src="@drawable/media_repeat_off" />
<ImageButton a:id="@+id/download_previous" a:layout_width="0dp" <ImageButton a:id="@+id/download_previous" a:layout_width="0dp"
a:layout_height="match_parent" a:layout_gravity="center_vertical" a:layout_height="match_parent" a:layout_gravity="center_vertical"
@ -83,11 +74,17 @@
a:layout_height="match_parent" a:layout_gravity="center_vertical" a:layout_height="match_parent" a:layout_gravity="center_vertical"
a:layout_weight="1" a:background="@drawable/list_selector_holo_dark" a:layout_weight="1" a:background="@drawable/list_selector_holo_dark"
a:padding="0dip" a:src="@drawable/media_forward_normal" /> a:padding="0dip" a:src="@drawable/media_forward_normal" />
<ImageButton a:id="@+id/download_repeat" a:layout_width="0dip"
a:layout_height="match_parent" a:layout_gravity="center_vertical"
a:layout_weight="1" a:background="@drawable/list_selector_holo_dark"
a:src="@drawable/media_repeat_off" />
<ImageButton a:id="@+id/download_toggle_list" <ImageButton a:id="@+id/download_toggle_list"
a:layout_width="0dip" a:layout_height="fill_parent" a:layout_weight="1" a:layout_width="0dip" a:layout_height="fill_parent" a:layout_gravity="center_vertical"
a:background="@drawable/list_selector_holo_dark" a:paddingRight="4dip" a:layout_weight="1" a:background="@drawable/list_selector_holo_dark"
a:src="@drawable/media_toggle_list_normal" a:paddingTop="12dip" a:paddingBottom="12dip" /> a:paddingRight="4dip" a:src="@drawable/media_toggle_list_normal"
a:paddingTop="12dip" a:paddingBottom="12dip" />
</LinearLayout> </LinearLayout>
<include layout="@layout/download_slider" /> <include layout="@layout/download_slider" />

View File

@ -38,10 +38,11 @@
</LinearLayout> </LinearLayout>
<ImageView <ImageView
a:layout_width="wrap_content" a:id="@+id/album_star"
a:layout_height="wrap_content" a:layout_width="wrap_content"
a:layout_gravity="right|center_vertical" a:layout_height="fill_parent"
a:paddingRight="6dip"/> a:gravity="center_vertical"
<!-- a:src="@drawable/list_item_more"--> a:background="@drawable/list_selector_holo_dark"
a:src="@drawable/star_hollow" />
</LinearLayout> </LinearLayout>

View File

@ -11,7 +11,7 @@
a:gravity="center_vertical" a:gravity="center_vertical"
a:checkMark="@drawable/btn_check_custom" a:checkMark="@drawable/btn_check_custom"
a:paddingLeft="3dip"/> a:paddingLeft="3dip"/>
<LinearLayout a:orientation="vertical" <LinearLayout a:orientation="vertical"
a:layout_width="0dip" a:layout_width="0dip"
a:layout_height="wrap_content" a:layout_height="wrap_content"
@ -73,4 +73,12 @@
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
<ImageView
a:id="@+id/song_star"
a:layout_width="wrap_content"
a:layout_height="fill_parent"
a:gravity="center_vertical"
a:background="@drawable/list_selector_holo_dark"
a:src="@drawable/star_hollow" />
</LinearLayout> </LinearLayout>

View File

@ -84,7 +84,25 @@ public class MusicDirectory {
private Integer bitRate; private Integer bitRate;
private String path; private String path;
private boolean video; private boolean video;
private boolean starred;
private Integer discNumber;
public Integer getDiscNumber() {
return discNumber;
}
public void setDiscNumber(Integer discNumber) {
this.discNumber = discNumber;
}
public boolean getStarred() {
return starred;
}
public void setStarred(boolean starred) {
this.starred = starred;
}
public String getId() { public String getId() {
return id; return id;
} }

View File

@ -1,234 +1,245 @@
/* /*
This file is part of Subsonic. This file is part of Subsonic.
Subsonic is free software: you can redistribute it and/or modify Subsonic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
Subsonic is distributed in the hope that it will be useful, Subsonic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with Subsonic. If not, see <http://www.gnu.org/licenses/>. along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
Copyright 2009 (C) Sindre Mehus Copyright 2009 (C) Sindre Mehus
*/ */
package net.sourceforge.subsonic.androidapp.service; package net.sourceforge.subsonic.androidapp.service;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.apache.http.HttpResponse; import org.apache.http.HttpResponse;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import net.sourceforge.subsonic.androidapp.domain.Indexes; import net.sourceforge.subsonic.androidapp.domain.Indexes;
import net.sourceforge.subsonic.androidapp.domain.JukeboxStatus; import net.sourceforge.subsonic.androidapp.domain.JukeboxStatus;
import net.sourceforge.subsonic.androidapp.domain.Lyrics; import net.sourceforge.subsonic.androidapp.domain.Lyrics;
import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; import net.sourceforge.subsonic.androidapp.domain.MusicDirectory;
import net.sourceforge.subsonic.androidapp.domain.MusicFolder; import net.sourceforge.subsonic.androidapp.domain.MusicFolder;
import net.sourceforge.subsonic.androidapp.domain.Playlist; import net.sourceforge.subsonic.androidapp.domain.Playlist;
import net.sourceforge.subsonic.androidapp.domain.SearchCritera; import net.sourceforge.subsonic.androidapp.domain.SearchCritera;
import net.sourceforge.subsonic.androidapp.domain.SearchResult; import net.sourceforge.subsonic.androidapp.domain.SearchResult;
import net.sourceforge.subsonic.androidapp.domain.Version; import net.sourceforge.subsonic.androidapp.domain.Version;
import net.sourceforge.subsonic.androidapp.util.CancellableTask; import net.sourceforge.subsonic.androidapp.util.CancellableTask;
import net.sourceforge.subsonic.androidapp.util.LRUCache; import net.sourceforge.subsonic.androidapp.util.LRUCache;
import net.sourceforge.subsonic.androidapp.util.ProgressListener; import net.sourceforge.subsonic.androidapp.util.ProgressListener;
import net.sourceforge.subsonic.androidapp.util.TimeLimitedCache; import net.sourceforge.subsonic.androidapp.util.TimeLimitedCache;
import net.sourceforge.subsonic.androidapp.util.Util; import net.sourceforge.subsonic.androidapp.util.Util;
/** /**
* @author Sindre Mehus * @author Sindre Mehus
*/ */
public class CachedMusicService implements MusicService { public class CachedMusicService implements MusicService {
private static final int MUSIC_DIR_CACHE_SIZE = 50; private static final int MUSIC_DIR_CACHE_SIZE = 50;
private static final int TTL_MUSIC_DIR = 5 * 60; // Five minutes private static final int TTL_MUSIC_DIR = 5 * 60; // Five minutes
private final MusicService musicService; private final MusicService musicService;
private final LRUCache<String, TimeLimitedCache<MusicDirectory>> cachedMusicDirectories; private final LRUCache<String, TimeLimitedCache<MusicDirectory>> cachedMusicDirectories;
private final TimeLimitedCache<Boolean> cachedLicenseValid = new TimeLimitedCache<Boolean>(120, TimeUnit.SECONDS); private final TimeLimitedCache<Boolean> cachedLicenseValid = new TimeLimitedCache<Boolean>(120, TimeUnit.SECONDS);
private final TimeLimitedCache<Indexes> cachedIndexes = new TimeLimitedCache<Indexes>(60 * 60, TimeUnit.SECONDS); private final TimeLimitedCache<Indexes> cachedIndexes = new TimeLimitedCache<Indexes>(60 * 60, TimeUnit.SECONDS);
private final TimeLimitedCache<List<Playlist>> cachedPlaylists = new TimeLimitedCache<List<Playlist>>(60, TimeUnit.SECONDS); private final TimeLimitedCache<List<Playlist>> cachedPlaylists = new TimeLimitedCache<List<Playlist>>(60, TimeUnit.SECONDS);
private final TimeLimitedCache<List<MusicFolder>> cachedMusicFolders = new TimeLimitedCache<List<MusicFolder>>(10 * 3600, TimeUnit.SECONDS); private final TimeLimitedCache<List<MusicFolder>> cachedMusicFolders = new TimeLimitedCache<List<MusicFolder>>(10 * 3600, TimeUnit.SECONDS);
private String restUrl; private String restUrl;
public CachedMusicService(MusicService musicService) { public CachedMusicService(MusicService musicService) {
this.musicService = musicService; this.musicService = musicService;
cachedMusicDirectories = new LRUCache<String, TimeLimitedCache<MusicDirectory>>(MUSIC_DIR_CACHE_SIZE); cachedMusicDirectories = new LRUCache<String, TimeLimitedCache<MusicDirectory>>(MUSIC_DIR_CACHE_SIZE);
} }
@Override @Override
public void ping(Context context, ProgressListener progressListener) throws Exception { public void ping(Context context, ProgressListener progressListener) throws Exception {
checkSettingsChanged(context); checkSettingsChanged(context);
musicService.ping(context, progressListener); musicService.ping(context, progressListener);
} }
@Override @Override
public boolean isLicenseValid(Context context, ProgressListener progressListener) throws Exception { public boolean isLicenseValid(Context context, ProgressListener progressListener) throws Exception {
checkSettingsChanged(context); checkSettingsChanged(context);
Boolean result = cachedLicenseValid.get(); Boolean result = cachedLicenseValid.get();
if (result == null) { if (result == null) {
result = musicService.isLicenseValid(context, progressListener); result = musicService.isLicenseValid(context, progressListener);
cachedLicenseValid.set(result, result ? 30L * 60L : 2L * 60L, TimeUnit.SECONDS); cachedLicenseValid.set(result, result ? 30L * 60L : 2L * 60L, TimeUnit.SECONDS);
} }
return result; return result;
} }
@Override @Override
public List<MusicFolder> getMusicFolders(Context context, ProgressListener progressListener) throws Exception { public List<MusicFolder> getMusicFolders(Context context, ProgressListener progressListener) throws Exception {
checkSettingsChanged(context); checkSettingsChanged(context);
List<MusicFolder> result = cachedMusicFolders.get(); List<MusicFolder> result = cachedMusicFolders.get();
if (result == null) { if (result == null) {
result = musicService.getMusicFolders(context, progressListener); result = musicService.getMusicFolders(context, progressListener);
cachedMusicFolders.set(result); cachedMusicFolders.set(result);
} }
return result; return result;
} }
@Override @Override
public Indexes getIndexes(String musicFolderId, boolean refresh, Context context, ProgressListener progressListener) throws Exception { public Indexes getIndexes(String musicFolderId, boolean refresh, Context context, ProgressListener progressListener) throws Exception {
checkSettingsChanged(context); checkSettingsChanged(context);
if (refresh) { if (refresh) {
cachedIndexes.clear(); cachedIndexes.clear();
cachedMusicFolders.clear(); cachedMusicFolders.clear();
cachedMusicDirectories.clear(); cachedMusicDirectories.clear();
} }
Indexes result = cachedIndexes.get(); Indexes result = cachedIndexes.get();
if (result == null) { if (result == null) {
result = musicService.getIndexes(musicFolderId, refresh, context, progressListener); result = musicService.getIndexes(musicFolderId, refresh, context, progressListener);
cachedIndexes.set(result); cachedIndexes.set(result);
} }
return result; return result;
} }
@Override @Override
public MusicDirectory getMusicDirectory(String id, boolean refresh, Context context, ProgressListener progressListener) throws Exception { public MusicDirectory getMusicDirectory(String id, boolean refresh, Context context, ProgressListener progressListener) throws Exception {
checkSettingsChanged(context); checkSettingsChanged(context);
TimeLimitedCache<MusicDirectory> cache = refresh ? null : cachedMusicDirectories.get(id); TimeLimitedCache<MusicDirectory> cache = refresh ? null : cachedMusicDirectories.get(id);
MusicDirectory dir = cache == null ? null : cache.get(); MusicDirectory dir = cache == null ? null : cache.get();
if (dir == null) { if (dir == null) {
dir = musicService.getMusicDirectory(id, refresh, context, progressListener); dir = musicService.getMusicDirectory(id, refresh, context, progressListener);
cache = new TimeLimitedCache<MusicDirectory>(TTL_MUSIC_DIR, TimeUnit.SECONDS); cache = new TimeLimitedCache<MusicDirectory>(TTL_MUSIC_DIR, TimeUnit.SECONDS);
cache.set(dir); cache.set(dir);
cachedMusicDirectories.put(id, cache); cachedMusicDirectories.put(id, cache);
} }
return dir; return dir;
} }
@Override @Override
public SearchResult search(SearchCritera criteria, Context context, ProgressListener progressListener) throws Exception { public SearchResult search(SearchCritera criteria, Context context, ProgressListener progressListener) throws Exception {
return musicService.search(criteria, context, progressListener); return musicService.search(criteria, context, progressListener);
} }
@Override @Override
public MusicDirectory getPlaylist(String id, Context context, ProgressListener progressListener) throws Exception { public MusicDirectory getPlaylist(String id, Context context, ProgressListener progressListener) throws Exception {
return musicService.getPlaylist(id, context, progressListener); return musicService.getPlaylist(id, context, progressListener);
} }
@Override @Override
public List<Playlist> getPlaylists(boolean refresh, Context context, ProgressListener progressListener) throws Exception { public List<Playlist> getPlaylists(boolean refresh, Context context, ProgressListener progressListener) throws Exception {
checkSettingsChanged(context); checkSettingsChanged(context);
List<Playlist> result = refresh ? null : cachedPlaylists.get(); List<Playlist> result = refresh ? null : cachedPlaylists.get();
if (result == null) { if (result == null) {
result = musicService.getPlaylists(refresh, context, progressListener); result = musicService.getPlaylists(refresh, context, progressListener);
cachedPlaylists.set(result); cachedPlaylists.set(result);
} }
return result; return result;
} }
@Override @Override
public void createPlaylist(String id, String name, List<MusicDirectory.Entry> entries, Context context, ProgressListener progressListener) throws Exception { public void createPlaylist(String id, String name, List<MusicDirectory.Entry> entries, Context context, ProgressListener progressListener) throws Exception {
musicService.createPlaylist(id, name, entries, context, progressListener); musicService.createPlaylist(id, name, entries, context, progressListener);
} }
@Override @Override
public Lyrics getLyrics(String artist, String title, Context context, ProgressListener progressListener) throws Exception { public Lyrics getLyrics(String artist, String title, Context context, ProgressListener progressListener) throws Exception {
return musicService.getLyrics(artist, title, context, progressListener); return musicService.getLyrics(artist, title, context, progressListener);
} }
@Override @Override
public void scrobble(String id, boolean submission, Context context, ProgressListener progressListener) throws Exception { public void scrobble(String id, boolean submission, Context context, ProgressListener progressListener) throws Exception {
musicService.scrobble(id, submission, context, progressListener); musicService.scrobble(id, submission, context, progressListener);
} }
@Override @Override
public MusicDirectory getAlbumList(String type, int size, int offset, Context context, ProgressListener progressListener) throws Exception { public MusicDirectory getAlbumList(String type, int size, int offset, Context context, ProgressListener progressListener) throws Exception {
return musicService.getAlbumList(type, size, offset, context, progressListener); return musicService.getAlbumList(type, size, offset, context, progressListener);
} }
@Override @Override
public MusicDirectory getRandomSongs(int size, Context context, ProgressListener progressListener) throws Exception { public MusicDirectory getRandomSongs(int size, Context context, ProgressListener progressListener) throws Exception {
return musicService.getRandomSongs(size, context, progressListener); return musicService.getRandomSongs(size, context, progressListener);
} }
@Override @Override
public Bitmap getCoverArt(Context context, MusicDirectory.Entry entry, int size, boolean saveToFile, ProgressListener progressListener) throws Exception { public Bitmap getCoverArt(Context context, MusicDirectory.Entry entry, int size, boolean saveToFile, ProgressListener progressListener) throws Exception {
return musicService.getCoverArt(context, entry, size, saveToFile, progressListener); return musicService.getCoverArt(context, entry, size, saveToFile, progressListener);
} }
@Override @Override
public HttpResponse getDownloadInputStream(Context context, MusicDirectory.Entry song, long offset, int maxBitrate, CancellableTask task) throws Exception { public HttpResponse getDownloadInputStream(Context context, MusicDirectory.Entry song, long offset, int maxBitrate, CancellableTask task) throws Exception {
return musicService.getDownloadInputStream(context, song, offset, maxBitrate, task); return musicService.getDownloadInputStream(context, song, offset, maxBitrate, task);
} }
@Override @Override
public Version getLocalVersion(Context context) throws Exception { public Version getLocalVersion(Context context) throws Exception {
return musicService.getLocalVersion(context); return musicService.getLocalVersion(context);
} }
@Override @Override
public Version getLatestVersion(Context context, ProgressListener progressListener) throws Exception { public Version getLatestVersion(Context context, ProgressListener progressListener) throws Exception {
return musicService.getLatestVersion(context, progressListener); return musicService.getLatestVersion(context, progressListener);
} }
@Override @Override
public String getVideoUrl(Context context, String id) { public String getVideoUrl(Context context, String id) {
return musicService.getVideoUrl(context, id); return musicService.getVideoUrl(context, id);
} }
@Override @Override
public JukeboxStatus updateJukeboxPlaylist(List<String> ids, Context context, ProgressListener progressListener) throws Exception { public JukeboxStatus updateJukeboxPlaylist(List<String> ids, Context context, ProgressListener progressListener) throws Exception {
return musicService.updateJukeboxPlaylist(ids, context, progressListener); return musicService.updateJukeboxPlaylist(ids, context, progressListener);
} }
@Override @Override
public JukeboxStatus skipJukebox(int index, int offsetSeconds, Context context, ProgressListener progressListener) throws Exception { public JukeboxStatus skipJukebox(int index, int offsetSeconds, Context context, ProgressListener progressListener) throws Exception {
return musicService.skipJukebox(index, offsetSeconds, context, progressListener); return musicService.skipJukebox(index, offsetSeconds, context, progressListener);
} }
@Override @Override
public JukeboxStatus stopJukebox(Context context, ProgressListener progressListener) throws Exception { public JukeboxStatus stopJukebox(Context context, ProgressListener progressListener) throws Exception {
return musicService.stopJukebox(context, progressListener); return musicService.stopJukebox(context, progressListener);
} }
@Override @Override
public JukeboxStatus startJukebox(Context context, ProgressListener progressListener) throws Exception { public JukeboxStatus startJukebox(Context context, ProgressListener progressListener) throws Exception {
return musicService.startJukebox(context, progressListener); return musicService.startJukebox(context, progressListener);
} }
@Override @Override
public JukeboxStatus getJukeboxStatus(Context context, ProgressListener progressListener) throws Exception { public JukeboxStatus getJukeboxStatus(Context context, ProgressListener progressListener) throws Exception {
return musicService.getJukeboxStatus(context, progressListener); return musicService.getJukeboxStatus(context, progressListener);
} }
@Override @Override
public JukeboxStatus setJukeboxGain(float gain, Context context, ProgressListener progressListener) throws Exception { public JukeboxStatus setJukeboxGain(float gain, Context context, ProgressListener progressListener) throws Exception {
return musicService.setJukeboxGain(gain, context, progressListener); return musicService.setJukeboxGain(gain, context, progressListener);
} }
private void checkSettingsChanged(Context context) { private void checkSettingsChanged(Context context) {
String newUrl = Util.getRestUrl(context, null); String newUrl = Util.getRestUrl(context, null);
if (!Util.equals(newUrl, restUrl)) { if (!Util.equals(newUrl, restUrl)) {
cachedMusicFolders.clear(); cachedMusicFolders.clear();
cachedMusicDirectories.clear(); cachedMusicDirectories.clear();
cachedLicenseValid.clear(); cachedLicenseValid.clear();
cachedIndexes.clear(); cachedIndexes.clear();
cachedPlaylists.clear(); cachedPlaylists.clear();
restUrl = newUrl; restUrl = newUrl;
} }
} }
}
@Override
public void star(String id, Context context, ProgressListener progressListener) throws Exception {
musicService.star(id, context, progressListener);
}
@Override
public void unstar(String id, Context context, ProgressListener progressListener) throws Exception {
musicService.unstar(id, context, progressListener);
}
}

View File

@ -1,91 +1,95 @@
/* /*
This file is part of Subsonic. This file is part of Subsonic.
Subsonic is free software: you can redistribute it and/or modify Subsonic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
Subsonic is distributed in the hope that it will be useful, Subsonic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with Subsonic. If not, see <http://www.gnu.org/licenses/>. along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
Copyright 2009 (C) Sindre Mehus Copyright 2009 (C) Sindre Mehus
*/ */
package net.sourceforge.subsonic.androidapp.service; package net.sourceforge.subsonic.androidapp.service;
import java.util.List; import java.util.List;
import org.apache.http.HttpResponse; import org.apache.http.HttpResponse;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import net.sourceforge.subsonic.androidapp.domain.Indexes; import net.sourceforge.subsonic.androidapp.domain.Indexes;
import net.sourceforge.subsonic.androidapp.domain.JukeboxStatus; import net.sourceforge.subsonic.androidapp.domain.JukeboxStatus;
import net.sourceforge.subsonic.androidapp.domain.Lyrics; import net.sourceforge.subsonic.androidapp.domain.Lyrics;
import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; import net.sourceforge.subsonic.androidapp.domain.MusicDirectory;
import net.sourceforge.subsonic.androidapp.domain.MusicFolder; import net.sourceforge.subsonic.androidapp.domain.MusicFolder;
import net.sourceforge.subsonic.androidapp.domain.Playlist; import net.sourceforge.subsonic.androidapp.domain.Playlist;
import net.sourceforge.subsonic.androidapp.domain.SearchCritera; import net.sourceforge.subsonic.androidapp.domain.SearchCritera;
import net.sourceforge.subsonic.androidapp.domain.SearchResult; import net.sourceforge.subsonic.androidapp.domain.SearchResult;
import net.sourceforge.subsonic.androidapp.domain.Version; import net.sourceforge.subsonic.androidapp.domain.Version;
import net.sourceforge.subsonic.androidapp.util.CancellableTask; import net.sourceforge.subsonic.androidapp.util.CancellableTask;
import net.sourceforge.subsonic.androidapp.util.ProgressListener; import net.sourceforge.subsonic.androidapp.util.ProgressListener;
/** /**
* @author Sindre Mehus * @author Sindre Mehus
*/ */
public interface MusicService { public interface MusicService {
void ping(Context context, ProgressListener progressListener) throws Exception; void ping(Context context, ProgressListener progressListener) throws Exception;
boolean isLicenseValid(Context context, ProgressListener progressListener) throws Exception; boolean isLicenseValid(Context context, ProgressListener progressListener) throws Exception;
List<MusicFolder> getMusicFolders(Context context, ProgressListener progressListener) throws Exception; List<MusicFolder> getMusicFolders(Context context, ProgressListener progressListener) throws Exception;
Indexes getIndexes(String musicFolderId, boolean refresh, Context context, ProgressListener progressListener) throws Exception; void star(String id, Context context, ProgressListener progressListener) throws Exception;
MusicDirectory getMusicDirectory(String id, boolean refresh, Context context, ProgressListener progressListener) throws Exception; void unstar(String id, Context context, ProgressListener progressListener) throws Exception;
SearchResult search(SearchCritera criteria, Context context, ProgressListener progressListener) throws Exception; Indexes getIndexes(String musicFolderId, boolean refresh, Context context, ProgressListener progressListener) throws Exception;
MusicDirectory getPlaylist(String id, Context context, ProgressListener progressListener) throws Exception; MusicDirectory getMusicDirectory(String id, boolean refresh, Context context, ProgressListener progressListener) throws Exception;
List<Playlist> getPlaylists(boolean refresh, Context context, ProgressListener progressListener) throws Exception; SearchResult search(SearchCritera criteria, Context context, ProgressListener progressListener) throws Exception;
void createPlaylist(String id, String name, List<MusicDirectory.Entry> entries, Context context, ProgressListener progressListener) throws Exception; MusicDirectory getPlaylist(String id, Context context, ProgressListener progressListener) throws Exception;
Lyrics getLyrics(String artist, String title, Context context, ProgressListener progressListener) throws Exception; List<Playlist> getPlaylists(boolean refresh, Context context, ProgressListener progressListener) throws Exception;
void scrobble(String id, boolean submission, Context context, ProgressListener progressListener) throws Exception; void createPlaylist(String id, String name, List<MusicDirectory.Entry> entries, Context context, ProgressListener progressListener) throws Exception;
MusicDirectory getAlbumList(String type, int size, int offset, Context context, ProgressListener progressListener) throws Exception; Lyrics getLyrics(String artist, String title, Context context, ProgressListener progressListener) throws Exception;
MusicDirectory getRandomSongs(int size, Context context, ProgressListener progressListener) throws Exception; void scrobble(String id, boolean submission, Context context, ProgressListener progressListener) throws Exception;
Bitmap getCoverArt(Context context, MusicDirectory.Entry entry, int size, boolean saveToFile, ProgressListener progressListener) throws Exception; MusicDirectory getAlbumList(String type, int size, int offset, Context context, ProgressListener progressListener) throws Exception;
HttpResponse getDownloadInputStream(Context context, MusicDirectory.Entry song, long offset, int maxBitrate, CancellableTask task) throws Exception; MusicDirectory getRandomSongs(int size, Context context, ProgressListener progressListener) throws Exception;
Version getLocalVersion(Context context) throws Exception; Bitmap getCoverArt(Context context, MusicDirectory.Entry entry, int size, boolean saveToFile, ProgressListener progressListener) throws Exception;
Version getLatestVersion(Context context, ProgressListener progressListener) throws Exception; HttpResponse getDownloadInputStream(Context context, MusicDirectory.Entry song, long offset, int maxBitrate, CancellableTask task) throws Exception;
String getVideoUrl(Context context, String id); Version getLocalVersion(Context context) throws Exception;
JukeboxStatus updateJukeboxPlaylist(List<String> ids, Context context, ProgressListener progressListener) throws Exception; Version getLatestVersion(Context context, ProgressListener progressListener) throws Exception;
JukeboxStatus skipJukebox(int index, int offsetSeconds, Context context, ProgressListener progressListener) throws Exception; String getVideoUrl(Context context, String id);
JukeboxStatus stopJukebox(Context context, ProgressListener progressListener) throws Exception; JukeboxStatus updateJukeboxPlaylist(List<String> ids, Context context, ProgressListener progressListener) throws Exception;
JukeboxStatus startJukebox(Context context, ProgressListener progressListener) throws Exception; JukeboxStatus skipJukebox(int index, int offsetSeconds, Context context, ProgressListener progressListener) throws Exception;
JukeboxStatus getJukeboxStatus(Context context, ProgressListener progressListener) throws Exception; JukeboxStatus stopJukebox(Context context, ProgressListener progressListener) throws Exception;
JukeboxStatus setJukeboxGain(float gain, Context context, ProgressListener progressListener) throws Exception; JukeboxStatus startJukebox(Context context, ProgressListener progressListener) throws Exception;
JukeboxStatus getJukeboxStatus(Context context, ProgressListener progressListener) throws Exception;
JukeboxStatus setJukeboxGain(float gain, Context context, ProgressListener progressListener) throws Exception;
} }

View File

@ -1,244 +1,254 @@
/* /*
This file is part of Subsonic. This file is part of Subsonic.
Subsonic is free software: you can redistribute it and/or modify Subsonic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
Subsonic is distributed in the hope that it will be useful, Subsonic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with Subsonic. If not, see <http://www.gnu.org/licenses/>. along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
Copyright 2009 (C) Sindre Mehus Copyright 2009 (C) Sindre Mehus
*/ */
package net.sourceforge.subsonic.androidapp.service; package net.sourceforge.subsonic.androidapp.service;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
import java.util.Set; import java.util.Set;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import net.sourceforge.subsonic.androidapp.domain.Artist; import net.sourceforge.subsonic.androidapp.domain.Artist;
import net.sourceforge.subsonic.androidapp.domain.Indexes; import net.sourceforge.subsonic.androidapp.domain.Indexes;
import net.sourceforge.subsonic.androidapp.domain.JukeboxStatus; import net.sourceforge.subsonic.androidapp.domain.JukeboxStatus;
import net.sourceforge.subsonic.androidapp.domain.Lyrics; import net.sourceforge.subsonic.androidapp.domain.Lyrics;
import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; import net.sourceforge.subsonic.androidapp.domain.MusicDirectory;
import net.sourceforge.subsonic.androidapp.domain.MusicFolder; import net.sourceforge.subsonic.androidapp.domain.MusicFolder;
import net.sourceforge.subsonic.androidapp.domain.Playlist; import net.sourceforge.subsonic.androidapp.domain.Playlist;
import net.sourceforge.subsonic.androidapp.domain.SearchCritera; import net.sourceforge.subsonic.androidapp.domain.SearchCritera;
import net.sourceforge.subsonic.androidapp.domain.SearchResult; import net.sourceforge.subsonic.androidapp.domain.SearchResult;
import net.sourceforge.subsonic.androidapp.util.Constants; import net.sourceforge.subsonic.androidapp.util.Constants;
import net.sourceforge.subsonic.androidapp.util.FileUtil; import net.sourceforge.subsonic.androidapp.util.FileUtil;
import net.sourceforge.subsonic.androidapp.util.ProgressListener; import net.sourceforge.subsonic.androidapp.util.ProgressListener;
import net.sourceforge.subsonic.androidapp.util.Util; import net.sourceforge.subsonic.androidapp.util.Util;
/** /**
* @author Sindre Mehus * @author Sindre Mehus
*/ */
public class OfflineMusicService extends RESTMusicService { public class OfflineMusicService extends RESTMusicService {
@Override @Override
public boolean isLicenseValid(Context context, ProgressListener progressListener) throws Exception { public boolean isLicenseValid(Context context, ProgressListener progressListener) throws Exception {
return true; return true;
} }
@Override @Override
public Indexes getIndexes(String musicFolderId, boolean refresh, Context context, ProgressListener progressListener) throws Exception { public Indexes getIndexes(String musicFolderId, boolean refresh, Context context, ProgressListener progressListener) throws Exception {
List<Artist> artists = new ArrayList<Artist>(); List<Artist> artists = new ArrayList<Artist>();
File root = FileUtil.getMusicDirectory(context); File root = FileUtil.getMusicDirectory(context);
for (File file : FileUtil.listFiles(root)) { for (File file : FileUtil.listFiles(root)) {
if (file.isDirectory()) { if (file.isDirectory()) {
Artist artist = new Artist(); Artist artist = new Artist();
artist.setId(file.getPath()); artist.setId(file.getPath());
artist.setIndex(file.getName().substring(0, 1)); artist.setIndex(file.getName().substring(0, 1));
artist.setName(file.getName()); artist.setName(file.getName());
artists.add(artist); artists.add(artist);
} }
} }
return new Indexes(0L, Collections.<Artist>emptyList(), artists); return new Indexes(0L, Collections.<Artist>emptyList(), artists);
} }
@Override @Override
public MusicDirectory getMusicDirectory(String id, boolean refresh, Context context, ProgressListener progressListener) throws Exception { public MusicDirectory getMusicDirectory(String id, boolean refresh, Context context, ProgressListener progressListener) throws Exception {
File dir = new File(id); File dir = new File(id);
MusicDirectory result = new MusicDirectory(); MusicDirectory result = new MusicDirectory();
result.setName(dir.getName()); result.setName(dir.getName());
Set<String> names = new HashSet<String>(); Set<String> names = new HashSet<String>();
for (File file : FileUtil.listMusicFiles(dir)) { for (File file : FileUtil.listMusicFiles(dir)) {
String name = getName(file); String name = getName(file);
if (name != null & !names.contains(name)) { if (name != null & !names.contains(name)) {
names.add(name); names.add(name);
result.addChild(createEntry(context, file, name)); result.addChild(createEntry(context, file, name));
} }
} }
return result; return result;
} }
private String getName(File file) { private String getName(File file) {
String name = file.getName(); String name = file.getName();
if (file.isDirectory()) { if (file.isDirectory()) {
return name; return name;
} }
if (name.endsWith(".partial") || name.contains(".partial.") || name.equals(Constants.ALBUM_ART_FILE)) { if (name.endsWith(".partial") || name.contains(".partial.") || name.equals(Constants.ALBUM_ART_FILE)) {
return null; return null;
} }
name = name.replace(".complete", ""); name = name.replace(".complete", "");
return FileUtil.getBaseName(name); return FileUtil.getBaseName(name);
} }
private MusicDirectory.Entry createEntry(Context context, File file, String name) { private MusicDirectory.Entry createEntry(Context context, File file, String name) {
MusicDirectory.Entry entry = new MusicDirectory.Entry(); MusicDirectory.Entry entry = new MusicDirectory.Entry();
entry.setDirectory(file.isDirectory()); entry.setDirectory(file.isDirectory());
entry.setId(file.getPath()); entry.setId(file.getPath());
entry.setParent(file.getParent()); entry.setParent(file.getParent());
entry.setSize(file.length()); entry.setSize(file.length());
String root = FileUtil.getMusicDirectory(context).getPath(); String root = FileUtil.getMusicDirectory(context).getPath();
entry.setPath(file.getPath().replaceFirst("^" + root + "/" , "")); entry.setPath(file.getPath().replaceFirst("^" + root + "/" , ""));
if (file.isFile()) { if (file.isFile()) {
entry.setArtist(file.getParentFile().getParentFile().getName()); entry.setArtist(file.getParentFile().getParentFile().getName());
entry.setAlbum(file.getParentFile().getName()); entry.setAlbum(file.getParentFile().getName());
} }
entry.setTitle(name); entry.setTitle(name);
entry.setSuffix(FileUtil.getExtension(file.getName().replace(".complete", ""))); entry.setSuffix(FileUtil.getExtension(file.getName().replace(".complete", "")));
File albumArt = FileUtil.getAlbumArtFile(context, entry); File albumArt = FileUtil.getAlbumArtFile(context, entry);
if (albumArt.exists()) { if (albumArt.exists()) {
entry.setCoverArt(albumArt.getPath()); entry.setCoverArt(albumArt.getPath());
} }
return entry; return entry;
} }
@Override @Override
public Bitmap getCoverArt(Context context, MusicDirectory.Entry entry, int size, boolean saveToFile, ProgressListener progressListener) throws Exception { public Bitmap getCoverArt(Context context, MusicDirectory.Entry entry, int size, boolean saveToFile, ProgressListener progressListener) throws Exception {
InputStream in = new FileInputStream(entry.getCoverArt()); InputStream in = new FileInputStream(entry.getCoverArt());
try { try {
byte[] bytes = Util.toByteArray(in); byte[] bytes = Util.toByteArray(in);
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length); Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
return Bitmap.createScaledBitmap(bitmap, size, size, true); return Bitmap.createScaledBitmap(bitmap, size, size, true);
} finally { } finally {
Util.close(in); Util.close(in);
} }
} }
@Override @Override
public List<MusicFolder> getMusicFolders(Context context, ProgressListener progressListener) throws Exception { public void star(String id, Context context, ProgressListener progressListener) throws Exception {
throw new OfflineException("Music folders not available in offline mode"); throw new OfflineException("Star not available in offline mode");
} }
@Override @Override
public SearchResult search(SearchCritera criteria, Context context, ProgressListener progressListener) throws Exception { public void unstar(String id, Context context, ProgressListener progressListener) throws Exception {
throw new OfflineException("Search not available in offline mode"); throw new OfflineException("UnStar not available in offline mode");
} }
@Override @Override
public List<Playlist> getPlaylists(boolean refresh, Context context, ProgressListener progressListener) throws Exception { public List<MusicFolder> getMusicFolders(Context context, ProgressListener progressListener) throws Exception {
throw new OfflineException("Playlists not available in offline mode"); throw new OfflineException("Music folders not available in offline mode");
} }
@Override @Override
public MusicDirectory getPlaylist(String id, Context context, ProgressListener progressListener) throws Exception { public SearchResult search(SearchCritera criteria, Context context, ProgressListener progressListener) throws Exception {
throw new OfflineException("Playlists not available in offline mode"); throw new OfflineException("Search not available in offline mode");
} }
@Override @Override
public void createPlaylist(String id, String name, List<MusicDirectory.Entry> entries, Context context, ProgressListener progressListener) throws Exception { public List<Playlist> getPlaylists(boolean refresh, Context context, ProgressListener progressListener) throws Exception {
throw new OfflineException("Playlists not available in offline mode"); throw new OfflineException("Playlists not available in offline mode");
} }
@Override @Override
public Lyrics getLyrics(String artist, String title, Context context, ProgressListener progressListener) throws Exception { public MusicDirectory getPlaylist(String id, Context context, ProgressListener progressListener) throws Exception {
throw new OfflineException("Lyrics not available in offline mode"); throw new OfflineException("Playlists not available in offline mode");
} }
@Override @Override
public void scrobble(String id, boolean submission, Context context, ProgressListener progressListener) throws Exception { public void createPlaylist(String id, String name, List<MusicDirectory.Entry> entries, Context context, ProgressListener progressListener) throws Exception {
throw new OfflineException("Scrobbling not available in offline mode"); throw new OfflineException("Playlists not available in offline mode");
} }
@Override @Override
public MusicDirectory getAlbumList(String type, int size, int offset, Context context, ProgressListener progressListener) throws Exception { public Lyrics getLyrics(String artist, String title, Context context, ProgressListener progressListener) throws Exception {
throw new OfflineException("Album lists not available in offline mode"); throw new OfflineException("Lyrics not available in offline mode");
} }
@Override @Override
public String getVideoUrl(Context context, String id) { public void scrobble(String id, boolean submission, Context context, ProgressListener progressListener) throws Exception {
return null; throw new OfflineException("Scrobbling not available in offline mode");
} }
@Override @Override
public JukeboxStatus updateJukeboxPlaylist(List<String> ids, Context context, ProgressListener progressListener) throws Exception { public MusicDirectory getAlbumList(String type, int size, int offset, Context context, ProgressListener progressListener) throws Exception {
throw new OfflineException("Jukebox not available in offline mode"); throw new OfflineException("Album lists not available in offline mode");
} }
@Override @Override
public JukeboxStatus skipJukebox(int index, int offsetSeconds, Context context, ProgressListener progressListener) throws Exception { public String getVideoUrl(Context context, String id) {
throw new OfflineException("Jukebox not available in offline mode"); return null;
} }
@Override @Override
public JukeboxStatus stopJukebox(Context context, ProgressListener progressListener) throws Exception { public JukeboxStatus updateJukeboxPlaylist(List<String> ids, Context context, ProgressListener progressListener) throws Exception {
throw new OfflineException("Jukebox not available in offline mode"); throw new OfflineException("Jukebox not available in offline mode");
} }
@Override @Override
public JukeboxStatus startJukebox(Context context, ProgressListener progressListener) throws Exception { public JukeboxStatus skipJukebox(int index, int offsetSeconds, Context context, ProgressListener progressListener) throws Exception {
throw new OfflineException("Jukebox not available in offline mode"); throw new OfflineException("Jukebox not available in offline mode");
} }
@Override @Override
public JukeboxStatus getJukeboxStatus(Context context, ProgressListener progressListener) throws Exception { public JukeboxStatus stopJukebox(Context context, ProgressListener progressListener) throws Exception {
throw new OfflineException("Jukebox not available in offline mode"); throw new OfflineException("Jukebox not available in offline mode");
} }
@Override @Override
public JukeboxStatus setJukeboxGain(float gain, Context context, ProgressListener progressListener) throws Exception { public JukeboxStatus startJukebox(Context context, ProgressListener progressListener) throws Exception {
throw new OfflineException("Jukebox not available in offline mode"); throw new OfflineException("Jukebox not available in offline mode");
} }
@Override @Override
public MusicDirectory getRandomSongs(int size, Context context, ProgressListener progressListener) throws Exception { public JukeboxStatus getJukeboxStatus(Context context, ProgressListener progressListener) throws Exception {
File root = FileUtil.getMusicDirectory(context); throw new OfflineException("Jukebox not available in offline mode");
List<File> children = new LinkedList<File>(); }
listFilesRecursively(root, children);
MusicDirectory result = new MusicDirectory(); @Override
public JukeboxStatus setJukeboxGain(float gain, Context context, ProgressListener progressListener) throws Exception {
if (children.isEmpty()) { throw new OfflineException("Jukebox not available in offline mode");
return result; }
}
Random random = new Random(); @Override
for (int i = 0; i < size; i++) { public MusicDirectory getRandomSongs(int size, Context context, ProgressListener progressListener) throws Exception {
File file = children.get(random.nextInt(children.size())); File root = FileUtil.getMusicDirectory(context);
result.addChild(createEntry(context, file, getName(file))); List<File> children = new LinkedList<File>();
} listFilesRecursively(root, children);
MusicDirectory result = new MusicDirectory();
return result;
} if (children.isEmpty()) {
return result;
private void listFilesRecursively(File parent, List<File> children) { }
for (File file : FileUtil.listMusicFiles(parent)) { Random random = new Random();
if (file.isFile()) { for (int i = 0; i < size; i++) {
children.add(file); File file = children.get(random.nextInt(children.size()));
} else { result.addChild(createEntry(context, file, getName(file)));
listFilesRecursively(file, children); }
}
} return result;
} }
}
private void listFilesRecursively(File parent, List<File> children) {
for (File file : FileUtil.listMusicFiles(parent)) {
if (file.isFile()) {
children.add(file);
} else {
listFilesRecursively(file, children);
}
}
}
}

View File

@ -203,6 +203,24 @@ public class RESTMusicService implements MusicService {
Util.close(reader); Util.close(reader);
} }
} }
public void star(String id, Context context, ProgressListener progressListener) throws Exception {
Reader reader = getReader(context, progressListener, "star", null, "id", id);
try {
new ErrorParser(context).parse(reader);
} finally {
Util.close(reader);
}
}
public void unstar(String id, Context context, ProgressListener progressListener) throws Exception {
Reader reader = getReader(context, progressListener, "unstar", null, "id", id);
try {
new ErrorParser(context).parse(reader);
} finally {
Util.close(reader);
}
}
@Override @Override
public Indexes getIndexes(String musicFolderId, boolean refresh, Context context, ProgressListener progressListener) throws Exception { public Indexes getIndexes(String musicFolderId, boolean refresh, Context context, ProgressListener progressListener) throws Exception {

View File

@ -92,6 +92,11 @@ public abstract class AbstractParser {
protected boolean getBoolean(String name) { protected boolean getBoolean(String name) {
return "true".equals(get(name)); return "true".equals(get(name));
} }
protected boolean getValueExists(String name) {
String value = get(name);
return value != null && !value.isEmpty();
}
protected Integer getInteger(String name) { protected Integer getInteger(String name) {
String s = get(name); String s = get(name);

View File

@ -38,6 +38,7 @@ public class MusicDirectoryEntryParser extends AbstractParser {
entry.setDirectory(getBoolean("isDir")); entry.setDirectory(getBoolean("isDir"));
entry.setCoverArt(get("coverArt")); entry.setCoverArt(get("coverArt"));
entry.setArtist(get("artist")); entry.setArtist(get("artist"));
entry.setStarred(getValueExists("starred"));
if (!entry.isDirectory()) { if (!entry.isDirectory()) {
entry.setAlbum(get("album")); entry.setAlbum(get("album"));
@ -53,7 +54,9 @@ public class MusicDirectoryEntryParser extends AbstractParser {
entry.setBitRate(getInteger("bitRate")); entry.setBitRate(getInteger("bitRate"));
entry.setPath(get("path")); entry.setPath(get("path"));
entry.setVideo(getBoolean("isVideo")); entry.setVideo(getBoolean("isVideo"));
entry.setDiscNumber(getInteger("discNumber"));
} }
return entry; return entry;
} }
} }

View File

@ -1,55 +1,93 @@
/* /*
This file is part of Subsonic. This file is part of Subsonic.
Subsonic is free software: you can redistribute it and/or modify Subsonic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
Subsonic is distributed in the hope that it will be useful, Subsonic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with Subsonic. If not, see <http://www.gnu.org/licenses/>. along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
Copyright 2009 (C) Sindre Mehus Copyright 2009 (C) Sindre Mehus
*/ */
package net.sourceforge.subsonic.androidapp.util; package net.sourceforge.subsonic.androidapp.util;
import android.content.Context; import android.content.Context;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.widget.LinearLayout; import android.widget.ImageView;
import android.widget.TextView; import android.widget.LinearLayout;
import net.sourceforge.subsonic.androidapp.R; import android.widget.TextView;
import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; import net.sourceforge.subsonic.androidapp.R;
import net.sourceforge.subsonic.androidapp.domain.MusicDirectory;
/** import net.sourceforge.subsonic.androidapp.service.MusicService;
* Used to display albums in a {@code ListView}. import net.sourceforge.subsonic.androidapp.service.MusicServiceFactory;
*
* @author Sindre Mehus /**
*/ * Used to display albums in a {@code ListView}.
public class AlbumView extends LinearLayout { *
* @author Sindre Mehus
private TextView titleView; */
private TextView artistView; public class AlbumView extends LinearLayout {
private View coverArtView;
private TextView titleView;
public AlbumView(Context context) { private TextView artistView;
super(context); private View coverArtView;
LayoutInflater.from(context).inflate(R.layout.album_list_item, this, true); private ImageView starImageView;
titleView = (TextView) findViewById(R.id.album_title); public AlbumView(Context context) {
artistView = (TextView) findViewById(R.id.album_artist); super(context);
coverArtView = findViewById(R.id.album_coverart); LayoutInflater.from(context).inflate(R.layout.album_list_item, this, true);
}
titleView = (TextView) findViewById(R.id.album_title);
public void setAlbum(MusicDirectory.Entry album, ImageLoader imageLoader) { artistView = (TextView) findViewById(R.id.album_artist);
titleView.setText(album.getTitle()); coverArtView = findViewById(R.id.album_coverart);
artistView.setText(album.getArtist()); starImageView = (ImageView) findViewById(R.id.album_star);
artistView.setVisibility(album.getArtist() == null ? View.GONE : View.VISIBLE); }
imageLoader.loadImage(coverArtView, album, false, true);
} public void setAlbum(final MusicDirectory.Entry album, ImageLoader imageLoader) {
} titleView.setText(album.getTitle());
artistView.setText(album.getArtist());
artistView.setVisibility(album.getArtist() == null ? View.GONE : View.VISIBLE);
starImageView.setImageDrawable(album.getStarred() ? getResources().getDrawable(R.drawable.star) : getResources().getDrawable(R.drawable.star_hollow));
imageLoader.loadImage(coverArtView, album, false, true);
starImageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
final boolean isStarred = album.getStarred();
final String id = album.getId();
if (!isStarred) {
starImageView.setImageDrawable(getResources().getDrawable(R.drawable.star));
album.setStarred(true);
} else {
starImageView.setImageDrawable(getResources().getDrawable(R.drawable.star_hollow));
album.setStarred(false);
}
new Thread(new Runnable() {
public void run() {
MusicService musicService = MusicServiceFactory.getMusicService(null);
try {
if (!isStarred) {
musicService.star(id, getContext(), null);
} else {
musicService.unstar(id, getContext(), null);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
});
}
}

View File

@ -29,7 +29,7 @@ public final class Constants {
// REST protocol version and client ID. // REST protocol version and client ID.
// Note: Keep it as low as possible to maintain compatibility with older servers. // Note: Keep it as low as possible to maintain compatibility with older servers.
public static final String REST_PROTOCOL_VERSION = "1.2.0"; public static final String REST_PROTOCOL_VERSION = "1.8.0";
public static final String REST_CLIENT_ID = "android"; public static final String REST_CLIENT_ID = "android";
// Names for intent extras. // Names for intent extras.

View File

@ -1,178 +1,218 @@
/* /*
This file is part of Subsonic. This file is part of Subsonic.
Subsonic is free software: you can redistribute it and/or modify Subsonic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
Subsonic is distributed in the hope that it will be useful, Subsonic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with Subsonic. If not, see <http://www.gnu.org/licenses/>. along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
Copyright 2009 (C) Sindre Mehus Copyright 2009 (C) Sindre Mehus
*/ */
package net.sourceforge.subsonic.androidapp.util; package net.sourceforge.subsonic.androidapp.util;
import android.content.Context; import android.content.Context;
import android.os.Handler; import android.os.Handler;
import android.util.Log; import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.widget.Checkable; import android.widget.Checkable;
import android.widget.CheckedTextView; import android.widget.CheckedTextView;
import android.widget.LinearLayout; import android.widget.ImageView;
import android.widget.TextView; import android.widget.LinearLayout;
import net.sourceforge.subsonic.androidapp.R; import android.widget.TextView;
import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; import net.sourceforge.subsonic.androidapp.R;
import net.sourceforge.subsonic.androidapp.service.DownloadService; import net.sourceforge.subsonic.androidapp.domain.MusicDirectory;
import net.sourceforge.subsonic.androidapp.service.DownloadServiceImpl; import net.sourceforge.subsonic.androidapp.service.DownloadService;
import net.sourceforge.subsonic.androidapp.service.DownloadFile; import net.sourceforge.subsonic.androidapp.service.DownloadServiceImpl;
import net.sourceforge.subsonic.androidapp.service.DownloadFile;
import java.io.File; import net.sourceforge.subsonic.androidapp.service.MusicService;
import java.util.WeakHashMap; import net.sourceforge.subsonic.androidapp.service.MusicServiceFactory;
/** import java.io.File;
* Used to display songs in a {@code ListView}. import java.util.WeakHashMap;
*
* @author Sindre Mehus /**
*/ * Used to display songs in a {@code ListView}.
public class SongView extends LinearLayout implements Checkable { *
* @author Sindre Mehus
private static final String TAG = SongView.class.getSimpleName(); */
private static final WeakHashMap<SongView, ?> INSTANCES = new WeakHashMap<SongView, Object>(); public class SongView extends LinearLayout implements Checkable {
private static Handler handler;
private static final String TAG = SongView.class.getSimpleName();
private CheckedTextView checkedTextView; private static final WeakHashMap<SongView, ?> INSTANCES = new WeakHashMap<SongView, Object>();
private TextView titleTextView; private static Handler handler;
private TextView artistTextView;
private TextView durationTextView; private CheckedTextView checkedTextView;
private TextView statusTextView; private ImageView starImageView;
private MusicDirectory.Entry song; private TextView titleTextView;
private TextView artistTextView;
public SongView(Context context) { private TextView durationTextView;
super(context); private TextView statusTextView;
LayoutInflater.from(context).inflate(R.layout.song_list_item, this, true); private MusicDirectory.Entry song;
checkedTextView = (CheckedTextView) findViewById(R.id.song_check); public SongView(Context context) {
titleTextView = (TextView) findViewById(R.id.song_title); super(context);
artistTextView = (TextView) findViewById(R.id.song_artist); LayoutInflater.from(context).inflate(R.layout.song_list_item, this, true);
durationTextView = (TextView) findViewById(R.id.song_duration);
statusTextView = (TextView) findViewById(R.id.song_status); checkedTextView = (CheckedTextView) findViewById(R.id.song_check);
starImageView = (ImageView) findViewById(R.id.song_star);
INSTANCES.put(this, null); titleTextView = (TextView) findViewById(R.id.song_title);
int instanceCount = INSTANCES.size(); artistTextView = (TextView) findViewById(R.id.song_artist);
if (instanceCount > 50) { durationTextView = (TextView) findViewById(R.id.song_duration);
Log.w(TAG, instanceCount + " live SongView instances"); statusTextView = (TextView) findViewById(R.id.song_status);
}
startUpdater(); INSTANCES.put(this, null);
} int instanceCount = INSTANCES.size();
public void setSong(MusicDirectory.Entry song, boolean checkable) { if (instanceCount > 50) {
this.song = song; Log.w(TAG, instanceCount + " live SongView instances");
StringBuilder artist = new StringBuilder(40); }
String bitRate = null; startUpdater();
if (song.getBitRate() != null) { }
bitRate = String.format(getContext().getString(R.string.song_details_kbps), song.getBitRate());
} public void setSong(final MusicDirectory.Entry song, boolean checkable) {
this.song = song;
String fileFormat = null; StringBuilder artist = new StringBuilder(40);
if (song.getTranscodedSuffix() != null && !song.getTranscodedSuffix().equals(song.getSuffix())) {
fileFormat = String.format("%s > %s", song.getSuffix(), song.getTranscodedSuffix()); String bitRate = null;
} else { if (song.getBitRate() != null) {
fileFormat = song.getSuffix(); bitRate = String.format(getContext().getString(R.string.song_details_kbps), song.getBitRate());
} }
artist.append(song.getArtist()).append(" (") String fileFormat = null;
.append(String.format(getContext().getString(R.string.song_details_all), bitRate == null ? "" : bitRate, fileFormat)) if (song.getTranscodedSuffix() != null && !song.getTranscodedSuffix().equals(song.getSuffix())) {
.append(")"); fileFormat = String.format("%s > %s", song.getSuffix(), song.getTranscodedSuffix());
} else {
titleTextView.setText(song.getTitle()); fileFormat = song.getSuffix();
artistTextView.setText(artist); }
durationTextView.setText(Util.formatDuration(song.getDuration()));
checkedTextView.setVisibility(checkable && !song.isVideo() ? View.VISIBLE : View.GONE); artist.append(song.getArtist()).append(" (")
.append(String.format(getContext().getString(R.string.song_details_all), bitRate == null ? "" : bitRate, fileFormat))
update(); .append(")");
}
titleTextView.setText(song.getTitle());
private void update() { artistTextView.setText(artist);
DownloadService downloadService = DownloadServiceImpl.getInstance(); durationTextView.setText(Util.formatDuration(song.getDuration()));
if (downloadService == null) { starImageView.setImageDrawable(song.getStarred() ? getResources().getDrawable(R.drawable.star) : getResources().getDrawable(R.drawable.star_hollow));
return; checkedTextView.setVisibility(checkable && !song.isVideo() ? View.VISIBLE : View.GONE);
}
starImageView.setOnClickListener(new View.OnClickListener() {
DownloadFile downloadFile = downloadService.forSong(song); @Override
File completeFile = downloadFile.getCompleteFile(); public void onClick(View view) {
File partialFile = downloadFile.getPartialFile(); final boolean isStarred = song.getStarred();
final String id = song.getId();
int leftImage = 0;
int rightImage = 0; if (!isStarred) {
starImageView.setImageDrawable(getResources().getDrawable(R.drawable.star));
if (completeFile.exists()) { song.setStarred(true);
leftImage = downloadFile.isSaved() ? R.drawable.ic_stat_saved : R.drawable.ic_stat_downloaded; } else {
} starImageView.setImageDrawable(getResources().getDrawable(R.drawable.star_hollow));
song.setStarred(false);
if (downloadFile.isDownloading() && !downloadFile.isDownloadCancelled() && partialFile.exists()) { }
statusTextView.setText(Util.formatLocalizedBytes(partialFile.length(), getContext()));
rightImage = R.drawable.ic_stat_downloading; new Thread(new Runnable() {
} else { public void run() {
statusTextView.setText(null); MusicService musicService = MusicServiceFactory.getMusicService(null);
}
statusTextView.setCompoundDrawablesWithIntrinsicBounds(leftImage, 0, rightImage, 0); try {
if (!isStarred) {
boolean playing = downloadService.getCurrentPlaying() == downloadFile; musicService.star(id, getContext(), null);
if (playing) { } else {
titleTextView.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_stat_play, 0, 0, 0); musicService.unstar(id, getContext(), null);
} else { }
titleTextView.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); } catch (Exception e) {
} e.printStackTrace();
} }
}
private static synchronized void startUpdater() { }).start();
if (handler != null) { }
return; });
}
update();
handler = new Handler(); }
Runnable runnable = new Runnable() {
@Override private void update() {
public void run() { DownloadService downloadService = DownloadServiceImpl.getInstance();
updateAll(); if (downloadService == null) {
handler.postDelayed(this, 1000L); return;
} }
};
handler.postDelayed(runnable, 1000L); DownloadFile downloadFile = downloadService.forSong(song);
} File completeFile = downloadFile.getCompleteFile();
File partialFile = downloadFile.getPartialFile();
private static void updateAll() {
try { int leftImage = 0;
for (SongView view : INSTANCES.keySet()) { int rightImage = 0;
if (view.isShown()) {
view.update(); if (completeFile.exists()) {
} leftImage = downloadFile.isSaved() ? R.drawable.ic_stat_saved : R.drawable.ic_stat_downloaded;
} }
} catch (Throwable x) {
Log.w(TAG, "Error when updating song views.", x); if (downloadFile.isDownloading() && !downloadFile.isDownloadCancelled() && partialFile.exists()) {
} statusTextView.setText(Util.formatLocalizedBytes(partialFile.length(), getContext()));
} rightImage = R.drawable.ic_stat_downloading;
} else {
@Override statusTextView.setText(null);
public void setChecked(boolean b) { }
checkedTextView.setChecked(b); statusTextView.setCompoundDrawablesWithIntrinsicBounds(leftImage, 0, rightImage, 0);
}
boolean playing = downloadService.getCurrentPlaying() == downloadFile;
@Override if (playing) {
public boolean isChecked() { titleTextView.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_stat_play, 0, 0, 0);
return checkedTextView.isChecked(); } else {
} titleTextView.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
}
@Override }
public void toggle() {
checkedTextView.toggle(); private static synchronized void startUpdater() {
} if (handler != null) {
} return;
}
handler = new Handler();
Runnable runnable = new Runnable() {
@Override
public void run() {
updateAll();
handler.postDelayed(this, 1000L);
}
};
handler.postDelayed(runnable, 1000L);
}
private static void updateAll() {
try {
for (SongView view : INSTANCES.keySet()) {
if (view.isShown()) {
view.update();
}
}
} catch (Throwable x) {
Log.w(TAG, "Error when updating song views.", x);
}
}
@Override
public void setChecked(boolean b) {
checkedTextView.setChecked(b);
}
@Override
public boolean isChecked() {
return checkedTextView.isChecked();
}
@Override
public void toggle() {
checkedTextView.toggle();
}
}

View File

@ -102,7 +102,10 @@ public class Util extends DownloadActivity {
} }
public static boolean isOffline(Context context) { public static boolean isOffline(Context context) {
return getActiveServer(context) == 0; if (context == null)
return false;
else
return getActiveServer(context) == 0;
} }
public static boolean isScreenLitOnDownload(Context context) { public static boolean isScreenLitOnDownload(Context context) {