From 72111240e73256c5124bdfcbd9980f5cc7b5b611 Mon Sep 17 00:00:00 2001 From: Joshua Bahnsen Date: Wed, 23 Jan 2013 00:57:13 -0700 Subject: [PATCH] Add album/track star support Changed lowest REST protocol level to 1.8.0, changed downloaded icon, added discNumber support --- AndroidManifest.xml | 2 +- .../sourceforge/subsonic/androidapp/R.java | 288 +++++----- res/drawable-hdpi/ic_stat_downloaded.png | Bin 3729 -> 1331 bytes res/drawable-hdpi/star.png | Bin 0 -> 1725 bytes res/drawable-hdpi/star_hollow.png | Bin 0 -> 1768 bytes res/layout-land/download.xml | 89 ++-- res/layout-port/download.xml | 43 +- res/layout/album_list_item.xml | 13 +- res/layout/song_list_item.xml | 10 +- .../androidapp/domain/MusicDirectory.java | 18 + .../service/CachedMusicService.java | 479 +++++++++-------- .../androidapp/service/MusicService.java | 184 +++---- .../service/OfflineMusicService.java | 498 +++++++++--------- .../androidapp/service/RESTMusicService.java | 18 + .../service/parser/AbstractParser.java | 5 + .../parser/MusicDirectoryEntryParser.java | 3 + .../subsonic/androidapp/util/AlbumView.java | 148 ++++-- .../subsonic/androidapp/util/Constants.java | 2 +- .../subsonic/androidapp/util/SongView.java | 396 +++++++------- .../subsonic/androidapp/util/Util.java | 5 +- 20 files changed, 1187 insertions(+), 1014 deletions(-) create mode 100644 res/drawable-hdpi/star.png create mode 100644 res/drawable-hdpi/star_hollow.png diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 7bbef65d..3f728b91 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -2,7 +2,7 @@ + a:versionName="3.9.9.9" a:installLocation="auto"> diff --git a/gen/net/sourceforge/subsonic/androidapp/R.java b/gen/net/sourceforge/subsonic/androidapp/R.java index 34ca2c20..4ff303ed 100644 --- a/gen/net/sourceforge/subsonic/androidapp/R.java +++ b/gen/net/sourceforge/subsonic/androidapp/R.java @@ -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_pressed=0x7f02004c; public static final int slider_knob=0x7f02004d; - public static final int status_bg=0x7f02004e; - public static final int status_next=0x7f02004f; - public static final int status_pause=0x7f020050; - public static final int status_play=0x7f020051; - public static final int status_prev=0x7f020052; - public static final int status_stop=0x7f020053; - public static final int title_bar_shadow=0x7f020054; - public static final int unknown_album=0x7f020055; - public static final int unknown_album_large=0x7f020056; + public static final int star=0x7f02004e; + public static final int star_hollow=0x7f02004f; + public static final int status_bg=0x7f020050; + public static final int status_next=0x7f020051; + public static final int status_pause=0x7f020052; + public static final int status_play=0x7f020053; + public static final int status_prev=0x7f020054; + public static final int status_stop=0x7f020055; + 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 int album=0x7f0d0048; + public static final int album=0x7f0d0049; public static final int album_artist=0x7f0d0002; public static final int album_coverart=0x7f0d0000; - public static final int album_menu_pin=0x7f0d0085; - public static final int album_menu_play_last=0x7f0d0084; - public static final int album_menu_play_now=0x7f0d0083; + public static final int album_menu_pin=0x7f0d0087; + public static final int album_menu_play_last=0x7f0d0086; + 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 appwidget_coverart=0x7f0d0003; - public static final int appwidget_top=0x7f0d0005; - public static final int artist=0x7f0d0007; - public static final int artist_menu_pin=0x7f0d0088; - public static final int artist_menu_play_last=0x7f0d0087; - public static final int artist_menu_play_now=0x7f0d0086; - public static final int button_bar=0x7f0d000b; - public static final int button_bar_home=0x7f0d000c; - public static final int button_bar_music=0x7f0d000d; - public static final int button_bar_now_playing=0x7f0d0010; - public static final int button_bar_playlists=0x7f0d000f; - public static final int button_bar_search=0x7f0d000e; - public static final int control_next=0x7f0d000a; - public static final int control_play=0x7f0d0009; - public static final int control_previous=0x7f0d0008; - public static final int control_stop=0x7f0d0049; - public static final int download_album=0x7f0d0024; - public static final int download_album_art_image=0x7f0d001e; - public static final int download_album_art_layout=0x7f0d001d; - public static final int download_artist=0x7f0d0023; - public static final int download_button_bar_flipper=0x7f0d0020; - public static final int download_control_layout=0x7f0d0011; - public static final int download_duration=0x7f0d0022; - public static final int download_empty=0x7f0d0025; - public static final int download_equalizer=0x7f0d0079; - public static final int download_jukebox=0x7f0d007b; - public static final int download_list=0x7f0d0026; + public static final int appwidget_coverart=0x7f0d0004; + public static final int appwidget_top=0x7f0d0006; + public static final int artist=0x7f0d0008; + public static final int artist_menu_pin=0x7f0d008a; + public static final int artist_menu_play_last=0x7f0d0089; + public static final int artist_menu_play_now=0x7f0d0088; + public static final int button_bar=0x7f0d000c; + public static final int button_bar_home=0x7f0d000d; + public static final int button_bar_music=0x7f0d000e; + public static final int button_bar_now_playing=0x7f0d0011; + public static final int button_bar_playlists=0x7f0d0010; + public static final int button_bar_search=0x7f0d000f; + public static final int control_next=0x7f0d000b; + public static final int control_play=0x7f0d000a; + public static final int control_previous=0x7f0d0009; + public static final int control_stop=0x7f0d004a; + public static final int download_album=0x7f0d0025; + public static final int download_album_art_image=0x7f0d001f; + public static final int download_album_art_layout=0x7f0d001e; + public static final int download_artist=0x7f0d0024; + public static final int download_button_bar_flipper=0x7f0d0021; + public static final int download_control_layout=0x7f0d0012; + public static final int download_duration=0x7f0d0023; + public static final int download_empty=0x7f0d0026; + public static final int download_equalizer=0x7f0d007b; + public static final int download_jukebox=0x7f0d007d; + public static final int download_list=0x7f0d0027; public static final int download_next=0x7f0d0018; public static final int download_pause=0x7f0d0015; - public static final int download_playlist_flipper=0x7f0d001c; - public static final int download_position=0x7f0d0021; + public static final int download_playlist_flipper=0x7f0d001d; + public static final int download_position=0x7f0d0022; public static final int download_previous=0x7f0d0014; - public static final int download_progress_bar=0x7f0d0027; - public static final int download_repeat=0x7f0d0013; - public static final int download_shuffle=0x7f0d0012; - public static final int download_song_title=0x7f0d001b; + public static final int download_progress_bar=0x7f0d0028; + public static final int download_repeat=0x7f0d0019; + public static final int download_shuffle=0x7f0d0013; + public static final int download_song_title=0x7f0d001c; 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_toggle_list=0x7f0d0019; - public static final int download_visualizer=0x7f0d007a; - public static final int download_visualizer_view_layout=0x7f0d001f; - public static final int equalizer_bar=0x7f0d002d; - public static final int equalizer_frequency=0x7f0d002b; - public static final int equalizer_level=0x7f0d002c; - public static final int equalizer_enabled=0x7f0d0028; - public static final int equalizer_layout=0x7f0d0029; - public static final int equalizer_preset=0x7f0d002a; - public static final int help_back=0x7f0d002f; - public static final int help_buttons=0x7f0d002e; - public static final int help_close=0x7f0d0030; - public static final int help_contents=0x7f0d0031; - public static final int icon=0x7f0d006e; - public static final int jukebox_volume_progress_bar=0x7f0d0033; - public static final int linearLayout1=0x7f0d0004; - public static final int lyrics_artist=0x7f0d0035; - public static final int lyrics_scrollview=0x7f0d0034; - public static final int lyrics_text=0x7f0d0037; - public static final int lyrics_title=0x7f0d0036; - public static final int main_select_server_1=0x7f0d003b; - public static final int main_select_server_2=0x7f0d003c; - public static final int main_albums=0x7f0d003d; - public static final int main_albums_frequent=0x7f0d0040; - public static final int main_albums_highest=0x7f0d0041; - public static final int main_albums_newest=0x7f0d003e; - public static final int main_albums_random=0x7f0d0043; - public static final int main_albums_recent=0x7f0d003f; - public static final int main_albums_starred=0x7f0d0042; - public static final int main_dummy=0x7f0d0039; - public static final int main_list=0x7f0d0038; - public static final int main_select_server=0x7f0d003a; - public static final int main_shuffle=0x7f0d0078; - public static final int menu_exit=0x7f0d0077; - public static final int menu_help=0x7f0d0076; - public static final int menu_lyrics=0x7f0d0080; - public static final int menu_refresh=0x7f0d0089; - public static final int menu_remove=0x7f0d0081; - public static final int menu_remove_all=0x7f0d007d; - public static final int menu_save_playlist=0x7f0d007c; - public static final int menu_screen_on_off=0x7f0d007e; - public static final int menu_settings=0x7f0d0075; - public static final int menu_show_album=0x7f0d007f; - public static final int menu_shuffle=0x7f0d0082; - public static final int notification_image=0x7f0d0045; - public static final int play_video_contents=0x7f0d004a; - public static final int progress_message=0x7f0d004b; - public static final int save_playlist_name=0x7f0d004d; - public static final int save_playlist_root=0x7f0d004c; - public static final int search_albums=0x7f0d0051; - public static final int search_artists=0x7f0d0050; - public static final int search_list=0x7f0d004e; - public static final int search_more_albums=0x7f0d0054; - public static final int search_more_artists=0x7f0d0053; - public static final int search_more_songs=0x7f0d0055; - public static final int search_search=0x7f0d004f; - public static final int search_songs=0x7f0d0052; - public static final int select_album_cover_art=0x7f0d005f; - public static final int select_album_delete=0x7f0d005d; - public static final int select_album_empty=0x7f0d0056; - public static final int select_album_entries=0x7f0d0057; - public static final int select_album_more=0x7f0d005e; - public static final int select_album_pin=0x7f0d005b; - public static final int select_album_play_all=0x7f0d0062; - public static final int select_album_play_last=0x7f0d005a; - public static final int select_album_play_now=0x7f0d0059; - public static final int select_album_select=0x7f0d0058; - public static final int select_album_text1=0x7f0d0060; - public static final int select_album_text2=0x7f0d0061; - public static final int select_album_unpin=0x7f0d005c; - public static final int select_artist_folder=0x7f0d0064; - public static final int select_artist_folder_1=0x7f0d0065; - public static final int select_artist_folder_2=0x7f0d0066; - public static final int select_artist_list=0x7f0d0063; - public static final int select_playlist_empty=0x7f0d0067; - public static final int select_playlist_list=0x7f0d0068; - public static final int song_artist=0x7f0d006c; - public static final int song_check=0x7f0d0069; - public static final int song_duration=0x7f0d006d; - public static final int song_menu_play_last=0x7f0d008c; - public static final int song_menu_play_next=0x7f0d008b; - public static final int song_menu_play_now=0x7f0d008a; - public static final int song_status=0x7f0d006b; - public static final int song_title=0x7f0d006a; - public static final int status_icon=0x7f0d0046; - public static final int status_media_collapse=0x7f0d0072; - public static final int status_media_next=0x7f0d0071; - public static final int status_media_play=0x7f0d0070; - public static final int status_media_prev=0x7f0d006f; - public static final int statusbar=0x7f0d0044; - public static final int tab_progress=0x7f0d0073; - public static final int tab_progress_message=0x7f0d0074; - public static final int title=0x7f0d0006; - public static final int toast_layout_root=0x7f0d0032; - public static final int trackname=0x7f0d0047; + public static final int download_toggle_list=0x7f0d001a; + public static final int download_visualizer=0x7f0d007c; + public static final int download_visualizer_view_layout=0x7f0d0020; + public static final int equalizer_bar=0x7f0d002e; + public static final int equalizer_frequency=0x7f0d002c; + public static final int equalizer_level=0x7f0d002d; + public static final int equalizer_enabled=0x7f0d0029; + public static final int equalizer_layout=0x7f0d002a; + public static final int equalizer_preset=0x7f0d002b; + public static final int help_back=0x7f0d0030; + public static final int help_buttons=0x7f0d002f; + public static final int help_close=0x7f0d0031; + public static final int help_contents=0x7f0d0032; + public static final int icon=0x7f0d0070; + public static final int jukebox_volume_progress_bar=0x7f0d0034; + public static final int linearLayout1=0x7f0d0005; + public static final int lyrics_artist=0x7f0d0036; + public static final int lyrics_scrollview=0x7f0d0035; + public static final int lyrics_text=0x7f0d0038; + public static final int lyrics_title=0x7f0d0037; + public static final int main_select_server_1=0x7f0d003c; + public static final int main_select_server_2=0x7f0d003d; + public static final int main_albums=0x7f0d003e; + public static final int main_albums_frequent=0x7f0d0041; + public static final int main_albums_highest=0x7f0d0042; + public static final int main_albums_newest=0x7f0d003f; + public static final int main_albums_random=0x7f0d0044; + public static final int main_albums_recent=0x7f0d0040; + public static final int main_albums_starred=0x7f0d0043; + public static final int main_dummy=0x7f0d003a; + public static final int main_list=0x7f0d0039; + public static final int main_select_server=0x7f0d003b; + public static final int main_shuffle=0x7f0d007a; + public static final int menu_exit=0x7f0d0079; + public static final int menu_help=0x7f0d0078; + public static final int menu_lyrics=0x7f0d0082; + public static final int menu_refresh=0x7f0d008b; + public static final int menu_remove=0x7f0d0083; + public static final int menu_remove_all=0x7f0d007f; + public static final int menu_save_playlist=0x7f0d007e; + public static final int menu_screen_on_off=0x7f0d0080; + public static final int menu_settings=0x7f0d0077; + public static final int menu_show_album=0x7f0d0081; + public static final int menu_shuffle=0x7f0d0084; + public static final int notification_image=0x7f0d0046; + public static final int play_video_contents=0x7f0d004b; + public static final int progress_message=0x7f0d004c; + public static final int save_playlist_name=0x7f0d004e; + public static final int save_playlist_root=0x7f0d004d; + public static final int search_albums=0x7f0d0052; + public static final int search_artists=0x7f0d0051; + public static final int search_list=0x7f0d004f; + public static final int search_more_albums=0x7f0d0055; + public static final int search_more_artists=0x7f0d0054; + public static final int search_more_songs=0x7f0d0056; + public static final int search_search=0x7f0d0050; + public static final int search_songs=0x7f0d0053; + public static final int select_album_cover_art=0x7f0d0060; + public static final int select_album_delete=0x7f0d005e; + public static final int select_album_empty=0x7f0d0057; + public static final int select_album_entries=0x7f0d0058; + public static final int select_album_more=0x7f0d005f; + public static final int select_album_pin=0x7f0d005c; + public static final int select_album_play_all=0x7f0d0063; + public static final int select_album_play_last=0x7f0d005b; + public static final int select_album_play_now=0x7f0d005a; + public static final int select_album_select=0x7f0d0059; + public static final int select_album_text1=0x7f0d0061; + public static final int select_album_text2=0x7f0d0062; + public static final int select_album_unpin=0x7f0d005d; + public static final int select_artist_folder=0x7f0d0065; + public static final int select_artist_folder_1=0x7f0d0066; + public static final int select_artist_folder_2=0x7f0d0067; + public static final int select_artist_list=0x7f0d0064; + public static final int select_playlist_empty=0x7f0d0068; + public static final int select_playlist_list=0x7f0d0069; + public static final int song_artist=0x7f0d006d; + public static final int song_check=0x7f0d006a; + public static final int song_duration=0x7f0d006e; + public static final int song_menu_play_last=0x7f0d008e; + public static final int song_menu_play_next=0x7f0d008d; + public static final int song_menu_play_now=0x7f0d008c; + public static final int song_star=0x7f0d006f; + public static final int song_status=0x7f0d006c; + public static final int song_title=0x7f0d006b; + public static final int status_icon=0x7f0d0047; + public static final int status_media_collapse=0x7f0d0074; + public static final int status_media_next=0x7f0d0073; + public static final int status_media_play=0x7f0d0072; + public static final int status_media_prev=0x7f0d0071; + public static final int statusbar=0x7f0d0045; + public static final int tab_progress=0x7f0d0075; + public static final int tab_progress_message=0x7f0d0076; + public static final int title=0x7f0d0007; + public static final int toast_layout_root=0x7f0d0033; + public static final int trackname=0x7f0d0048; } public static final class integer { public static final int config_activityDefaultDur=0x7f080001; diff --git a/res/drawable-hdpi/ic_stat_downloaded.png b/res/drawable-hdpi/ic_stat_downloaded.png index 3c20147a805426a2138444fe333c4bdc2a6e5bcb..d5bfa457ce5bf3d9566ebc45ffcfc646a38d28fa 100644 GIT binary patch literal 1331 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWuD@%qp275hW46K32*3xq68pHF_1f1wh>l3^w)^1&PVosU-?Y zsp*+{wo31J?^jaDOtDo8H}y5}EpSfF$n>ZxN)4{^3rViZPPR-@vbR&PsjvbXkegbP zs8ErclUHn2VXFi-*9yo63F|8hm3bwJ6}oxF$}kgLQj3#|G7CyF^YauyCMG83 zmzLNn0bL65LT&-v*t}wBFaZNhzap_f-%!s00+w{G(#^lGX)r!E=H#2u4blAh6aX)u7;K-hL)}-PR@>Q zriSJwPKGeOF8Rr&xv6<2Fuf@Vz2-Ref)Yb+0nlcb)S}F?)D*X({9FaFr>!z^yTt^j zc~HG6xZPrkQ?EYIG5VmWMT%;e5HS6KnDB%P;W3D!SY)E}(9%BXRA`Y*c;tjBt9UghlyYvhiJ*=T-n zR^W%a*oGvIoaV+|))Omi|Gi~QPq>!$ZF3Yef5J_PgvF8zlLaf7xVJT*FA|h*j=gaA z$=9$0i}NEhWMh_jT-Ycp_D!!h?10ervw?>`>J=R1zJA-PfYmI*LGt&@`cBr>cH9AP z8kNtPd^~inc-J1zg+js0G@Gn1oc#Gu;7$7#Zrf(pX**e0ud7mg`s4F2p8K7j16Y^L z-S*Z^-EqI~=BWo+f@3XnzY1D0EM8~VX(D)~DZW{A_IJj)mlo{g$^Ff7_p_(nKM#dR ldEuKTfPw*xrhn8uz~HQrEZ=c=dmyM7^K|udS?83{1OSYc-sJ!Q literal 3729 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 diff --git a/res/drawable-hdpi/star.png b/res/drawable-hdpi/star.png new file mode 100644 index 0000000000000000000000000000000000000000..11f8641448683e8d59f7ba0064d211ee9dac0116 GIT binary patch literal 1725 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 literal 0 HcmV?d00001 diff --git a/res/layout-land/download.xml b/res/layout-land/download.xml index a2182859..c605bbf2 100644 --- a/res/layout-land/download.xml +++ b/res/layout-land/download.xml @@ -13,38 +13,49 @@ a:layout_height="wrap_content" a:layout_alignParentTop="true" a:layout_alignParentLeft="true"> - - - - - - - - + + + + + + + + + + + + + + + @@ -67,13 +79,14 @@ a:layout_height="fill_parent" a:layout_alignParentLeft="true" a:layout_toLeftOf="@+id/download_control_layout"> - - + + - + - + + a:layout_marginLeft="16dip" a:layout_marginRight="16dip" + a:layout_marginTop="16dip" a:singleLine="true" + a:textColor="@color/mediaControlForeground" a:textStyle="bold" + a:textSize="18sp" a:ellipsize="end" /> - + - - - + + + + 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" /> diff --git a/res/layout/album_list_item.xml b/res/layout/album_list_item.xml index 0abb7891..8dae5e8e 100644 --- a/res/layout/album_list_item.xml +++ b/res/layout/album_list_item.xml @@ -38,10 +38,11 @@ - - + a:id="@+id/album_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" /> + diff --git a/res/layout/song_list_item.xml b/res/layout/song_list_item.xml index 746c6813..2a3ef538 100644 --- a/res/layout/song_list_item.xml +++ b/res/layout/song_list_item.xml @@ -11,7 +11,7 @@ a:gravity="center_vertical" a:checkMark="@drawable/btn_check_custom" a:paddingLeft="3dip"/> - + + + diff --git a/src/net/sourceforge/subsonic/androidapp/domain/MusicDirectory.java b/src/net/sourceforge/subsonic/androidapp/domain/MusicDirectory.java index 5b6c0ae1..1eacbccb 100644 --- a/src/net/sourceforge/subsonic/androidapp/domain/MusicDirectory.java +++ b/src/net/sourceforge/subsonic/androidapp/domain/MusicDirectory.java @@ -84,7 +84,25 @@ public class MusicDirectory { private Integer bitRate; private String path; 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() { return id; } diff --git a/src/net/sourceforge/subsonic/androidapp/service/CachedMusicService.java b/src/net/sourceforge/subsonic/androidapp/service/CachedMusicService.java index e0121c09..9665fc62 100644 --- a/src/net/sourceforge/subsonic/androidapp/service/CachedMusicService.java +++ b/src/net/sourceforge/subsonic/androidapp/service/CachedMusicService.java @@ -1,234 +1,245 @@ -/* - This file is part of Subsonic. - - Subsonic is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Subsonic is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Subsonic. If not, see . - - Copyright 2009 (C) Sindre Mehus - */ -package net.sourceforge.subsonic.androidapp.service; - -import java.util.List; -import java.util.concurrent.TimeUnit; - -import org.apache.http.HttpResponse; - -import android.content.Context; -import android.graphics.Bitmap; -import net.sourceforge.subsonic.androidapp.domain.Indexes; -import net.sourceforge.subsonic.androidapp.domain.JukeboxStatus; -import net.sourceforge.subsonic.androidapp.domain.Lyrics; -import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; -import net.sourceforge.subsonic.androidapp.domain.MusicFolder; -import net.sourceforge.subsonic.androidapp.domain.Playlist; -import net.sourceforge.subsonic.androidapp.domain.SearchCritera; -import net.sourceforge.subsonic.androidapp.domain.SearchResult; -import net.sourceforge.subsonic.androidapp.domain.Version; -import net.sourceforge.subsonic.androidapp.util.CancellableTask; -import net.sourceforge.subsonic.androidapp.util.LRUCache; -import net.sourceforge.subsonic.androidapp.util.ProgressListener; -import net.sourceforge.subsonic.androidapp.util.TimeLimitedCache; -import net.sourceforge.subsonic.androidapp.util.Util; - -/** - * @author Sindre Mehus - */ -public class CachedMusicService implements MusicService { - - private static final int MUSIC_DIR_CACHE_SIZE = 50; - private static final int TTL_MUSIC_DIR = 5 * 60; // Five minutes - - private final MusicService musicService; - private final LRUCache> cachedMusicDirectories; - private final TimeLimitedCache cachedLicenseValid = new TimeLimitedCache(120, TimeUnit.SECONDS); - private final TimeLimitedCache cachedIndexes = new TimeLimitedCache(60 * 60, TimeUnit.SECONDS); - private final TimeLimitedCache> cachedPlaylists = new TimeLimitedCache>(60, TimeUnit.SECONDS); - private final TimeLimitedCache> cachedMusicFolders = new TimeLimitedCache>(10 * 3600, TimeUnit.SECONDS); - private String restUrl; - - public CachedMusicService(MusicService musicService) { - this.musicService = musicService; - cachedMusicDirectories = new LRUCache>(MUSIC_DIR_CACHE_SIZE); - } - - @Override - public void ping(Context context, ProgressListener progressListener) throws Exception { - checkSettingsChanged(context); - musicService.ping(context, progressListener); - } - - @Override - public boolean isLicenseValid(Context context, ProgressListener progressListener) throws Exception { - checkSettingsChanged(context); - Boolean result = cachedLicenseValid.get(); - if (result == null) { - result = musicService.isLicenseValid(context, progressListener); - cachedLicenseValid.set(result, result ? 30L * 60L : 2L * 60L, TimeUnit.SECONDS); - } - return result; - } - - @Override - public List getMusicFolders(Context context, ProgressListener progressListener) throws Exception { - checkSettingsChanged(context); - List result = cachedMusicFolders.get(); - if (result == null) { - result = musicService.getMusicFolders(context, progressListener); - cachedMusicFolders.set(result); - } - return result; - } - - @Override - public Indexes getIndexes(String musicFolderId, boolean refresh, Context context, ProgressListener progressListener) throws Exception { - checkSettingsChanged(context); - if (refresh) { - cachedIndexes.clear(); - cachedMusicFolders.clear(); - cachedMusicDirectories.clear(); - } - Indexes result = cachedIndexes.get(); - if (result == null) { - result = musicService.getIndexes(musicFolderId, refresh, context, progressListener); - cachedIndexes.set(result); - } - return result; - } - - @Override - public MusicDirectory getMusicDirectory(String id, boolean refresh, Context context, ProgressListener progressListener) throws Exception { - checkSettingsChanged(context); - TimeLimitedCache cache = refresh ? null : cachedMusicDirectories.get(id); - MusicDirectory dir = cache == null ? null : cache.get(); - if (dir == null) { - dir = musicService.getMusicDirectory(id, refresh, context, progressListener); - cache = new TimeLimitedCache(TTL_MUSIC_DIR, TimeUnit.SECONDS); - cache.set(dir); - cachedMusicDirectories.put(id, cache); - } - return dir; - } - - @Override - public SearchResult search(SearchCritera criteria, Context context, ProgressListener progressListener) throws Exception { - return musicService.search(criteria, context, progressListener); - } - - @Override - public MusicDirectory getPlaylist(String id, Context context, ProgressListener progressListener) throws Exception { - return musicService.getPlaylist(id, context, progressListener); - } - - @Override - public List getPlaylists(boolean refresh, Context context, ProgressListener progressListener) throws Exception { - checkSettingsChanged(context); - List result = refresh ? null : cachedPlaylists.get(); - if (result == null) { - result = musicService.getPlaylists(refresh, context, progressListener); - cachedPlaylists.set(result); - } - return result; - } - - @Override - public void createPlaylist(String id, String name, List entries, Context context, ProgressListener progressListener) throws Exception { - musicService.createPlaylist(id, name, entries, context, progressListener); - } - - @Override - public Lyrics getLyrics(String artist, String title, Context context, ProgressListener progressListener) throws Exception { - return musicService.getLyrics(artist, title, context, progressListener); - } - - @Override - public void scrobble(String id, boolean submission, Context context, ProgressListener progressListener) throws Exception { - musicService.scrobble(id, submission, context, progressListener); - } - - @Override - public MusicDirectory getAlbumList(String type, int size, int offset, Context context, ProgressListener progressListener) throws Exception { - return musicService.getAlbumList(type, size, offset, context, progressListener); - } - - @Override - public MusicDirectory getRandomSongs(int size, Context context, ProgressListener progressListener) throws Exception { - return musicService.getRandomSongs(size, context, progressListener); - } - - @Override - public Bitmap getCoverArt(Context context, MusicDirectory.Entry entry, int size, boolean saveToFile, ProgressListener progressListener) throws Exception { - return musicService.getCoverArt(context, entry, size, saveToFile, progressListener); - } - - @Override - public HttpResponse getDownloadInputStream(Context context, MusicDirectory.Entry song, long offset, int maxBitrate, CancellableTask task) throws Exception { - return musicService.getDownloadInputStream(context, song, offset, maxBitrate, task); - } - - @Override - public Version getLocalVersion(Context context) throws Exception { - return musicService.getLocalVersion(context); - } - - @Override - public Version getLatestVersion(Context context, ProgressListener progressListener) throws Exception { - return musicService.getLatestVersion(context, progressListener); - } - - @Override - public String getVideoUrl(Context context, String id) { - return musicService.getVideoUrl(context, id); - } - - @Override - public JukeboxStatus updateJukeboxPlaylist(List ids, Context context, ProgressListener progressListener) throws Exception { - return musicService.updateJukeboxPlaylist(ids, context, progressListener); - } - - @Override - public JukeboxStatus skipJukebox(int index, int offsetSeconds, Context context, ProgressListener progressListener) throws Exception { - return musicService.skipJukebox(index, offsetSeconds, context, progressListener); - } - - @Override - public JukeboxStatus stopJukebox(Context context, ProgressListener progressListener) throws Exception { - return musicService.stopJukebox(context, progressListener); - } - - @Override - public JukeboxStatus startJukebox(Context context, ProgressListener progressListener) throws Exception { - return musicService.startJukebox(context, progressListener); - } - - @Override - public JukeboxStatus getJukeboxStatus(Context context, ProgressListener progressListener) throws Exception { - return musicService.getJukeboxStatus(context, progressListener); - } - - @Override - public JukeboxStatus setJukeboxGain(float gain, Context context, ProgressListener progressListener) throws Exception { - return musicService.setJukeboxGain(gain, context, progressListener); - } - - private void checkSettingsChanged(Context context) { - String newUrl = Util.getRestUrl(context, null); - if (!Util.equals(newUrl, restUrl)) { - cachedMusicFolders.clear(); - cachedMusicDirectories.clear(); - cachedLicenseValid.clear(); - cachedIndexes.clear(); - cachedPlaylists.clear(); - restUrl = newUrl; - } - } -} +/* + This file is part of Subsonic. + + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see . + + Copyright 2009 (C) Sindre Mehus + */ +package net.sourceforge.subsonic.androidapp.service; + +import java.util.List; +import java.util.concurrent.TimeUnit; + +import org.apache.http.HttpResponse; + +import android.content.Context; +import android.graphics.Bitmap; +import net.sourceforge.subsonic.androidapp.domain.Indexes; +import net.sourceforge.subsonic.androidapp.domain.JukeboxStatus; +import net.sourceforge.subsonic.androidapp.domain.Lyrics; +import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; +import net.sourceforge.subsonic.androidapp.domain.MusicFolder; +import net.sourceforge.subsonic.androidapp.domain.Playlist; +import net.sourceforge.subsonic.androidapp.domain.SearchCritera; +import net.sourceforge.subsonic.androidapp.domain.SearchResult; +import net.sourceforge.subsonic.androidapp.domain.Version; +import net.sourceforge.subsonic.androidapp.util.CancellableTask; +import net.sourceforge.subsonic.androidapp.util.LRUCache; +import net.sourceforge.subsonic.androidapp.util.ProgressListener; +import net.sourceforge.subsonic.androidapp.util.TimeLimitedCache; +import net.sourceforge.subsonic.androidapp.util.Util; + +/** + * @author Sindre Mehus + */ +public class CachedMusicService implements MusicService { + + private static final int MUSIC_DIR_CACHE_SIZE = 50; + private static final int TTL_MUSIC_DIR = 5 * 60; // Five minutes + + private final MusicService musicService; + private final LRUCache> cachedMusicDirectories; + private final TimeLimitedCache cachedLicenseValid = new TimeLimitedCache(120, TimeUnit.SECONDS); + private final TimeLimitedCache cachedIndexes = new TimeLimitedCache(60 * 60, TimeUnit.SECONDS); + private final TimeLimitedCache> cachedPlaylists = new TimeLimitedCache>(60, TimeUnit.SECONDS); + private final TimeLimitedCache> cachedMusicFolders = new TimeLimitedCache>(10 * 3600, TimeUnit.SECONDS); + private String restUrl; + + public CachedMusicService(MusicService musicService) { + this.musicService = musicService; + cachedMusicDirectories = new LRUCache>(MUSIC_DIR_CACHE_SIZE); + } + + @Override + public void ping(Context context, ProgressListener progressListener) throws Exception { + checkSettingsChanged(context); + musicService.ping(context, progressListener); + } + + @Override + public boolean isLicenseValid(Context context, ProgressListener progressListener) throws Exception { + checkSettingsChanged(context); + Boolean result = cachedLicenseValid.get(); + if (result == null) { + result = musicService.isLicenseValid(context, progressListener); + cachedLicenseValid.set(result, result ? 30L * 60L : 2L * 60L, TimeUnit.SECONDS); + } + return result; + } + + @Override + public List getMusicFolders(Context context, ProgressListener progressListener) throws Exception { + checkSettingsChanged(context); + List result = cachedMusicFolders.get(); + if (result == null) { + result = musicService.getMusicFolders(context, progressListener); + cachedMusicFolders.set(result); + } + return result; + } + + @Override + public Indexes getIndexes(String musicFolderId, boolean refresh, Context context, ProgressListener progressListener) throws Exception { + checkSettingsChanged(context); + if (refresh) { + cachedIndexes.clear(); + cachedMusicFolders.clear(); + cachedMusicDirectories.clear(); + } + Indexes result = cachedIndexes.get(); + if (result == null) { + result = musicService.getIndexes(musicFolderId, refresh, context, progressListener); + cachedIndexes.set(result); + } + return result; + } + + @Override + public MusicDirectory getMusicDirectory(String id, boolean refresh, Context context, ProgressListener progressListener) throws Exception { + checkSettingsChanged(context); + TimeLimitedCache cache = refresh ? null : cachedMusicDirectories.get(id); + MusicDirectory dir = cache == null ? null : cache.get(); + if (dir == null) { + dir = musicService.getMusicDirectory(id, refresh, context, progressListener); + cache = new TimeLimitedCache(TTL_MUSIC_DIR, TimeUnit.SECONDS); + cache.set(dir); + cachedMusicDirectories.put(id, cache); + } + return dir; + } + + @Override + public SearchResult search(SearchCritera criteria, Context context, ProgressListener progressListener) throws Exception { + return musicService.search(criteria, context, progressListener); + } + + @Override + public MusicDirectory getPlaylist(String id, Context context, ProgressListener progressListener) throws Exception { + return musicService.getPlaylist(id, context, progressListener); + } + + @Override + public List getPlaylists(boolean refresh, Context context, ProgressListener progressListener) throws Exception { + checkSettingsChanged(context); + List result = refresh ? null : cachedPlaylists.get(); + if (result == null) { + result = musicService.getPlaylists(refresh, context, progressListener); + cachedPlaylists.set(result); + } + return result; + } + + @Override + public void createPlaylist(String id, String name, List entries, Context context, ProgressListener progressListener) throws Exception { + musicService.createPlaylist(id, name, entries, context, progressListener); + } + + @Override + public Lyrics getLyrics(String artist, String title, Context context, ProgressListener progressListener) throws Exception { + return musicService.getLyrics(artist, title, context, progressListener); + } + + @Override + public void scrobble(String id, boolean submission, Context context, ProgressListener progressListener) throws Exception { + musicService.scrobble(id, submission, context, progressListener); + } + + @Override + public MusicDirectory getAlbumList(String type, int size, int offset, Context context, ProgressListener progressListener) throws Exception { + return musicService.getAlbumList(type, size, offset, context, progressListener); + } + + @Override + public MusicDirectory getRandomSongs(int size, Context context, ProgressListener progressListener) throws Exception { + return musicService.getRandomSongs(size, context, progressListener); + } + + @Override + public Bitmap getCoverArt(Context context, MusicDirectory.Entry entry, int size, boolean saveToFile, ProgressListener progressListener) throws Exception { + return musicService.getCoverArt(context, entry, size, saveToFile, progressListener); + } + + @Override + public HttpResponse getDownloadInputStream(Context context, MusicDirectory.Entry song, long offset, int maxBitrate, CancellableTask task) throws Exception { + return musicService.getDownloadInputStream(context, song, offset, maxBitrate, task); + } + + @Override + public Version getLocalVersion(Context context) throws Exception { + return musicService.getLocalVersion(context); + } + + @Override + public Version getLatestVersion(Context context, ProgressListener progressListener) throws Exception { + return musicService.getLatestVersion(context, progressListener); + } + + @Override + public String getVideoUrl(Context context, String id) { + return musicService.getVideoUrl(context, id); + } + + @Override + public JukeboxStatus updateJukeboxPlaylist(List ids, Context context, ProgressListener progressListener) throws Exception { + return musicService.updateJukeboxPlaylist(ids, context, progressListener); + } + + @Override + public JukeboxStatus skipJukebox(int index, int offsetSeconds, Context context, ProgressListener progressListener) throws Exception { + return musicService.skipJukebox(index, offsetSeconds, context, progressListener); + } + + @Override + public JukeboxStatus stopJukebox(Context context, ProgressListener progressListener) throws Exception { + return musicService.stopJukebox(context, progressListener); + } + + @Override + public JukeboxStatus startJukebox(Context context, ProgressListener progressListener) throws Exception { + return musicService.startJukebox(context, progressListener); + } + + @Override + public JukeboxStatus getJukeboxStatus(Context context, ProgressListener progressListener) throws Exception { + return musicService.getJukeboxStatus(context, progressListener); + } + + @Override + public JukeboxStatus setJukeboxGain(float gain, Context context, ProgressListener progressListener) throws Exception { + return musicService.setJukeboxGain(gain, context, progressListener); + } + + private void checkSettingsChanged(Context context) { + String newUrl = Util.getRestUrl(context, null); + if (!Util.equals(newUrl, restUrl)) { + cachedMusicFolders.clear(); + cachedMusicDirectories.clear(); + cachedLicenseValid.clear(); + cachedIndexes.clear(); + cachedPlaylists.clear(); + 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); + } +} diff --git a/src/net/sourceforge/subsonic/androidapp/service/MusicService.java b/src/net/sourceforge/subsonic/androidapp/service/MusicService.java index eda7d1b3..f1770bdb 100644 --- a/src/net/sourceforge/subsonic/androidapp/service/MusicService.java +++ b/src/net/sourceforge/subsonic/androidapp/service/MusicService.java @@ -1,91 +1,95 @@ -/* - This file is part of Subsonic. - - Subsonic is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Subsonic is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Subsonic. If not, see . - - Copyright 2009 (C) Sindre Mehus - */ -package net.sourceforge.subsonic.androidapp.service; - -import java.util.List; - -import org.apache.http.HttpResponse; - -import android.content.Context; -import android.graphics.Bitmap; -import net.sourceforge.subsonic.androidapp.domain.Indexes; -import net.sourceforge.subsonic.androidapp.domain.JukeboxStatus; -import net.sourceforge.subsonic.androidapp.domain.Lyrics; -import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; -import net.sourceforge.subsonic.androidapp.domain.MusicFolder; -import net.sourceforge.subsonic.androidapp.domain.Playlist; -import net.sourceforge.subsonic.androidapp.domain.SearchCritera; -import net.sourceforge.subsonic.androidapp.domain.SearchResult; -import net.sourceforge.subsonic.androidapp.domain.Version; -import net.sourceforge.subsonic.androidapp.util.CancellableTask; -import net.sourceforge.subsonic.androidapp.util.ProgressListener; - -/** - * @author Sindre Mehus - */ -public interface MusicService { - - void ping(Context context, ProgressListener progressListener) throws Exception; - - boolean isLicenseValid(Context context, ProgressListener progressListener) throws Exception; - - List getMusicFolders(Context context, ProgressListener progressListener) throws Exception; - - Indexes getIndexes(String musicFolderId, boolean refresh, Context context, ProgressListener progressListener) throws Exception; - - MusicDirectory getMusicDirectory(String id, boolean refresh, Context context, ProgressListener progressListener) throws Exception; - - SearchResult search(SearchCritera criteria, Context context, ProgressListener progressListener) throws Exception; - - MusicDirectory getPlaylist(String id, Context context, ProgressListener progressListener) throws Exception; - - List getPlaylists(boolean refresh, Context context, ProgressListener progressListener) throws Exception; - - void createPlaylist(String id, String name, List entries, Context context, ProgressListener progressListener) throws Exception; - - Lyrics getLyrics(String artist, String title, Context context, ProgressListener progressListener) throws Exception; - - void scrobble(String id, boolean submission, Context context, ProgressListener progressListener) throws Exception; - - MusicDirectory getAlbumList(String type, int size, int offset, Context context, ProgressListener progressListener) throws Exception; - - MusicDirectory getRandomSongs(int size, Context context, ProgressListener progressListener) throws Exception; - - Bitmap getCoverArt(Context context, MusicDirectory.Entry entry, int size, boolean saveToFile, ProgressListener progressListener) throws Exception; - - HttpResponse getDownloadInputStream(Context context, MusicDirectory.Entry song, long offset, int maxBitrate, CancellableTask task) throws Exception; - - Version getLocalVersion(Context context) throws Exception; - - Version getLatestVersion(Context context, ProgressListener progressListener) throws Exception; - - String getVideoUrl(Context context, String id); - - JukeboxStatus updateJukeboxPlaylist(List ids, Context context, ProgressListener progressListener) throws Exception; - - JukeboxStatus skipJukebox(int index, int offsetSeconds, Context context, ProgressListener progressListener) throws Exception; - - JukeboxStatus stopJukebox(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; +/* + This file is part of Subsonic. + + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see . + + Copyright 2009 (C) Sindre Mehus + */ +package net.sourceforge.subsonic.androidapp.service; + +import java.util.List; + +import org.apache.http.HttpResponse; + +import android.content.Context; +import android.graphics.Bitmap; +import net.sourceforge.subsonic.androidapp.domain.Indexes; +import net.sourceforge.subsonic.androidapp.domain.JukeboxStatus; +import net.sourceforge.subsonic.androidapp.domain.Lyrics; +import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; +import net.sourceforge.subsonic.androidapp.domain.MusicFolder; +import net.sourceforge.subsonic.androidapp.domain.Playlist; +import net.sourceforge.subsonic.androidapp.domain.SearchCritera; +import net.sourceforge.subsonic.androidapp.domain.SearchResult; +import net.sourceforge.subsonic.androidapp.domain.Version; +import net.sourceforge.subsonic.androidapp.util.CancellableTask; +import net.sourceforge.subsonic.androidapp.util.ProgressListener; + +/** + * @author Sindre Mehus + */ +public interface MusicService { + + void ping(Context context, ProgressListener progressListener) throws Exception; + + boolean isLicenseValid(Context context, ProgressListener progressListener) throws Exception; + + List getMusicFolders(Context context, ProgressListener progressListener) throws Exception; + + void star(String id, Context context, ProgressListener progressListener) throws Exception; + + void unstar(String id, Context context, ProgressListener progressListener) throws Exception; + + Indexes getIndexes(String musicFolderId, boolean refresh, Context context, ProgressListener progressListener) throws Exception; + + MusicDirectory getMusicDirectory(String id, boolean refresh, Context context, ProgressListener progressListener) throws Exception; + + SearchResult search(SearchCritera criteria, Context context, ProgressListener progressListener) throws Exception; + + MusicDirectory getPlaylist(String id, Context context, ProgressListener progressListener) throws Exception; + + List getPlaylists(boolean refresh, Context context, ProgressListener progressListener) throws Exception; + + void createPlaylist(String id, String name, List entries, Context context, ProgressListener progressListener) throws Exception; + + Lyrics getLyrics(String artist, String title, Context context, ProgressListener progressListener) throws Exception; + + void scrobble(String id, boolean submission, Context context, ProgressListener progressListener) throws Exception; + + MusicDirectory getAlbumList(String type, int size, int offset, Context context, ProgressListener progressListener) throws Exception; + + MusicDirectory getRandomSongs(int size, Context context, ProgressListener progressListener) throws Exception; + + Bitmap getCoverArt(Context context, MusicDirectory.Entry entry, int size, boolean saveToFile, ProgressListener progressListener) throws Exception; + + HttpResponse getDownloadInputStream(Context context, MusicDirectory.Entry song, long offset, int maxBitrate, CancellableTask task) throws Exception; + + Version getLocalVersion(Context context) throws Exception; + + Version getLatestVersion(Context context, ProgressListener progressListener) throws Exception; + + String getVideoUrl(Context context, String id); + + JukeboxStatus updateJukeboxPlaylist(List ids, Context context, ProgressListener progressListener) throws Exception; + + JukeboxStatus skipJukebox(int index, int offsetSeconds, Context context, ProgressListener progressListener) throws Exception; + + JukeboxStatus stopJukebox(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; } \ No newline at end of file diff --git a/src/net/sourceforge/subsonic/androidapp/service/OfflineMusicService.java b/src/net/sourceforge/subsonic/androidapp/service/OfflineMusicService.java index 7b9fcc7a..5bc8f00b 100644 --- a/src/net/sourceforge/subsonic/androidapp/service/OfflineMusicService.java +++ b/src/net/sourceforge/subsonic/androidapp/service/OfflineMusicService.java @@ -1,244 +1,254 @@ -/* - This file is part of Subsonic. - - Subsonic is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Subsonic is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Subsonic. If not, see . - - Copyright 2009 (C) Sindre Mehus - */ -package net.sourceforge.subsonic.androidapp.service; - -import java.io.File; -import java.io.FileInputStream; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Random; -import java.util.Set; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import net.sourceforge.subsonic.androidapp.domain.Artist; -import net.sourceforge.subsonic.androidapp.domain.Indexes; -import net.sourceforge.subsonic.androidapp.domain.JukeboxStatus; -import net.sourceforge.subsonic.androidapp.domain.Lyrics; -import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; -import net.sourceforge.subsonic.androidapp.domain.MusicFolder; -import net.sourceforge.subsonic.androidapp.domain.Playlist; -import net.sourceforge.subsonic.androidapp.domain.SearchCritera; -import net.sourceforge.subsonic.androidapp.domain.SearchResult; -import net.sourceforge.subsonic.androidapp.util.Constants; -import net.sourceforge.subsonic.androidapp.util.FileUtil; -import net.sourceforge.subsonic.androidapp.util.ProgressListener; -import net.sourceforge.subsonic.androidapp.util.Util; - -/** - * @author Sindre Mehus - */ -public class OfflineMusicService extends RESTMusicService { - - @Override - public boolean isLicenseValid(Context context, ProgressListener progressListener) throws Exception { - return true; - } - - @Override - public Indexes getIndexes(String musicFolderId, boolean refresh, Context context, ProgressListener progressListener) throws Exception { - List artists = new ArrayList(); - File root = FileUtil.getMusicDirectory(context); - for (File file : FileUtil.listFiles(root)) { - if (file.isDirectory()) { - Artist artist = new Artist(); - artist.setId(file.getPath()); - artist.setIndex(file.getName().substring(0, 1)); - artist.setName(file.getName()); - artists.add(artist); - } - } - return new Indexes(0L, Collections.emptyList(), artists); - } - - @Override - public MusicDirectory getMusicDirectory(String id, boolean refresh, Context context, ProgressListener progressListener) throws Exception { - File dir = new File(id); - MusicDirectory result = new MusicDirectory(); - result.setName(dir.getName()); - - Set names = new HashSet(); - - for (File file : FileUtil.listMusicFiles(dir)) { - String name = getName(file); - if (name != null & !names.contains(name)) { - names.add(name); - result.addChild(createEntry(context, file, name)); - } - } - return result; - } - - private String getName(File file) { - String name = file.getName(); - if (file.isDirectory()) { - return name; - } - - if (name.endsWith(".partial") || name.contains(".partial.") || name.equals(Constants.ALBUM_ART_FILE)) { - return null; - } - - name = name.replace(".complete", ""); - return FileUtil.getBaseName(name); - } - - private MusicDirectory.Entry createEntry(Context context, File file, String name) { - MusicDirectory.Entry entry = new MusicDirectory.Entry(); - entry.setDirectory(file.isDirectory()); - entry.setId(file.getPath()); - entry.setParent(file.getParent()); - entry.setSize(file.length()); - String root = FileUtil.getMusicDirectory(context).getPath(); - entry.setPath(file.getPath().replaceFirst("^" + root + "/" , "")); - if (file.isFile()) { - entry.setArtist(file.getParentFile().getParentFile().getName()); - entry.setAlbum(file.getParentFile().getName()); - } - entry.setTitle(name); - entry.setSuffix(FileUtil.getExtension(file.getName().replace(".complete", ""))); - - File albumArt = FileUtil.getAlbumArtFile(context, entry); - if (albumArt.exists()) { - entry.setCoverArt(albumArt.getPath()); - } - return entry; - } - - @Override - public Bitmap getCoverArt(Context context, MusicDirectory.Entry entry, int size, boolean saveToFile, ProgressListener progressListener) throws Exception { - InputStream in = new FileInputStream(entry.getCoverArt()); - try { - byte[] bytes = Util.toByteArray(in); - Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length); - return Bitmap.createScaledBitmap(bitmap, size, size, true); - } finally { - Util.close(in); - } - } - - @Override - public List getMusicFolders(Context context, ProgressListener progressListener) throws Exception { - throw new OfflineException("Music folders not available in offline mode"); - } - - @Override - public SearchResult search(SearchCritera criteria, Context context, ProgressListener progressListener) throws Exception { - throw new OfflineException("Search not available in offline mode"); - } - - @Override - public List getPlaylists(boolean refresh, Context context, ProgressListener progressListener) throws Exception { - throw new OfflineException("Playlists not available in offline mode"); - } - - @Override - public MusicDirectory getPlaylist(String id, Context context, ProgressListener progressListener) throws Exception { - throw new OfflineException("Playlists not available in offline mode"); - } - - @Override - public void createPlaylist(String id, String name, List entries, Context context, ProgressListener progressListener) throws Exception { - throw new OfflineException("Playlists not available in offline mode"); - } - - @Override - public Lyrics getLyrics(String artist, String title, Context context, ProgressListener progressListener) throws Exception { - throw new OfflineException("Lyrics not available in offline mode"); - } - - @Override - public void scrobble(String id, boolean submission, Context context, ProgressListener progressListener) throws Exception { - throw new OfflineException("Scrobbling not available in offline mode"); - } - - @Override - public MusicDirectory getAlbumList(String type, int size, int offset, Context context, ProgressListener progressListener) throws Exception { - throw new OfflineException("Album lists not available in offline mode"); - } - - @Override - public String getVideoUrl(Context context, String id) { - return null; - } - - @Override - public JukeboxStatus updateJukeboxPlaylist(List ids, Context context, ProgressListener progressListener) throws Exception { - throw new OfflineException("Jukebox not available in offline mode"); - } - - @Override - public JukeboxStatus skipJukebox(int index, int offsetSeconds, Context context, ProgressListener progressListener) throws Exception { - throw new OfflineException("Jukebox not available in offline mode"); - } - - @Override - public JukeboxStatus stopJukebox(Context context, ProgressListener progressListener) throws Exception { - throw new OfflineException("Jukebox not available in offline mode"); - } - - @Override - public JukeboxStatus startJukebox(Context context, ProgressListener progressListener) throws Exception { - throw new OfflineException("Jukebox not available in offline mode"); - } - - @Override - public JukeboxStatus getJukeboxStatus(Context context, ProgressListener progressListener) throws Exception { - throw new OfflineException("Jukebox not available in offline mode"); - } - - @Override - public JukeboxStatus setJukeboxGain(float gain, Context context, ProgressListener progressListener) throws Exception { - throw new OfflineException("Jukebox not available in offline mode"); - } - - @Override - public MusicDirectory getRandomSongs(int size, Context context, ProgressListener progressListener) throws Exception { - File root = FileUtil.getMusicDirectory(context); - List children = new LinkedList(); - listFilesRecursively(root, children); - MusicDirectory result = new MusicDirectory(); - - if (children.isEmpty()) { - return result; - } - Random random = new Random(); - for (int i = 0; i < size; i++) { - File file = children.get(random.nextInt(children.size())); - result.addChild(createEntry(context, file, getName(file))); - } - - return result; - } - - private void listFilesRecursively(File parent, List children) { - for (File file : FileUtil.listMusicFiles(parent)) { - if (file.isFile()) { - children.add(file); - } else { - listFilesRecursively(file, children); - } - } - } -} +/* + This file is part of Subsonic. + + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see . + + Copyright 2009 (C) Sindre Mehus + */ +package net.sourceforge.subsonic.androidapp.service; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Random; +import java.util.Set; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import net.sourceforge.subsonic.androidapp.domain.Artist; +import net.sourceforge.subsonic.androidapp.domain.Indexes; +import net.sourceforge.subsonic.androidapp.domain.JukeboxStatus; +import net.sourceforge.subsonic.androidapp.domain.Lyrics; +import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; +import net.sourceforge.subsonic.androidapp.domain.MusicFolder; +import net.sourceforge.subsonic.androidapp.domain.Playlist; +import net.sourceforge.subsonic.androidapp.domain.SearchCritera; +import net.sourceforge.subsonic.androidapp.domain.SearchResult; +import net.sourceforge.subsonic.androidapp.util.Constants; +import net.sourceforge.subsonic.androidapp.util.FileUtil; +import net.sourceforge.subsonic.androidapp.util.ProgressListener; +import net.sourceforge.subsonic.androidapp.util.Util; + +/** + * @author Sindre Mehus + */ +public class OfflineMusicService extends RESTMusicService { + + @Override + public boolean isLicenseValid(Context context, ProgressListener progressListener) throws Exception { + return true; + } + + @Override + public Indexes getIndexes(String musicFolderId, boolean refresh, Context context, ProgressListener progressListener) throws Exception { + List artists = new ArrayList(); + File root = FileUtil.getMusicDirectory(context); + for (File file : FileUtil.listFiles(root)) { + if (file.isDirectory()) { + Artist artist = new Artist(); + artist.setId(file.getPath()); + artist.setIndex(file.getName().substring(0, 1)); + artist.setName(file.getName()); + artists.add(artist); + } + } + return new Indexes(0L, Collections.emptyList(), artists); + } + + @Override + public MusicDirectory getMusicDirectory(String id, boolean refresh, Context context, ProgressListener progressListener) throws Exception { + File dir = new File(id); + MusicDirectory result = new MusicDirectory(); + result.setName(dir.getName()); + + Set names = new HashSet(); + + for (File file : FileUtil.listMusicFiles(dir)) { + String name = getName(file); + if (name != null & !names.contains(name)) { + names.add(name); + result.addChild(createEntry(context, file, name)); + } + } + return result; + } + + private String getName(File file) { + String name = file.getName(); + if (file.isDirectory()) { + return name; + } + + if (name.endsWith(".partial") || name.contains(".partial.") || name.equals(Constants.ALBUM_ART_FILE)) { + return null; + } + + name = name.replace(".complete", ""); + return FileUtil.getBaseName(name); + } + + private MusicDirectory.Entry createEntry(Context context, File file, String name) { + MusicDirectory.Entry entry = new MusicDirectory.Entry(); + entry.setDirectory(file.isDirectory()); + entry.setId(file.getPath()); + entry.setParent(file.getParent()); + entry.setSize(file.length()); + String root = FileUtil.getMusicDirectory(context).getPath(); + entry.setPath(file.getPath().replaceFirst("^" + root + "/" , "")); + if (file.isFile()) { + entry.setArtist(file.getParentFile().getParentFile().getName()); + entry.setAlbum(file.getParentFile().getName()); + } + entry.setTitle(name); + entry.setSuffix(FileUtil.getExtension(file.getName().replace(".complete", ""))); + + File albumArt = FileUtil.getAlbumArtFile(context, entry); + if (albumArt.exists()) { + entry.setCoverArt(albumArt.getPath()); + } + return entry; + } + + @Override + public Bitmap getCoverArt(Context context, MusicDirectory.Entry entry, int size, boolean saveToFile, ProgressListener progressListener) throws Exception { + InputStream in = new FileInputStream(entry.getCoverArt()); + try { + byte[] bytes = Util.toByteArray(in); + Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length); + return Bitmap.createScaledBitmap(bitmap, size, size, true); + } finally { + Util.close(in); + } + } + + @Override + public void star(String id, Context context, ProgressListener progressListener) throws Exception { + throw new OfflineException("Star not available in offline mode"); + } + + @Override + public void unstar(String id, Context context, ProgressListener progressListener) throws Exception { + throw new OfflineException("UnStar not available in offline mode"); + } + + @Override + public List getMusicFolders(Context context, ProgressListener progressListener) throws Exception { + throw new OfflineException("Music folders not available in offline mode"); + } + + @Override + public SearchResult search(SearchCritera criteria, Context context, ProgressListener progressListener) throws Exception { + throw new OfflineException("Search not available in offline mode"); + } + + @Override + public List getPlaylists(boolean refresh, Context context, ProgressListener progressListener) throws Exception { + throw new OfflineException("Playlists not available in offline mode"); + } + + @Override + public MusicDirectory getPlaylist(String id, Context context, ProgressListener progressListener) throws Exception { + throw new OfflineException("Playlists not available in offline mode"); + } + + @Override + public void createPlaylist(String id, String name, List entries, Context context, ProgressListener progressListener) throws Exception { + throw new OfflineException("Playlists not available in offline mode"); + } + + @Override + public Lyrics getLyrics(String artist, String title, Context context, ProgressListener progressListener) throws Exception { + throw new OfflineException("Lyrics not available in offline mode"); + } + + @Override + public void scrobble(String id, boolean submission, Context context, ProgressListener progressListener) throws Exception { + throw new OfflineException("Scrobbling not available in offline mode"); + } + + @Override + public MusicDirectory getAlbumList(String type, int size, int offset, Context context, ProgressListener progressListener) throws Exception { + throw new OfflineException("Album lists not available in offline mode"); + } + + @Override + public String getVideoUrl(Context context, String id) { + return null; + } + + @Override + public JukeboxStatus updateJukeboxPlaylist(List ids, Context context, ProgressListener progressListener) throws Exception { + throw new OfflineException("Jukebox not available in offline mode"); + } + + @Override + public JukeboxStatus skipJukebox(int index, int offsetSeconds, Context context, ProgressListener progressListener) throws Exception { + throw new OfflineException("Jukebox not available in offline mode"); + } + + @Override + public JukeboxStatus stopJukebox(Context context, ProgressListener progressListener) throws Exception { + throw new OfflineException("Jukebox not available in offline mode"); + } + + @Override + public JukeboxStatus startJukebox(Context context, ProgressListener progressListener) throws Exception { + throw new OfflineException("Jukebox not available in offline mode"); + } + + @Override + public JukeboxStatus getJukeboxStatus(Context context, ProgressListener progressListener) throws Exception { + throw new OfflineException("Jukebox not available in offline mode"); + } + + @Override + public JukeboxStatus setJukeboxGain(float gain, Context context, ProgressListener progressListener) throws Exception { + throw new OfflineException("Jukebox not available in offline mode"); + } + + @Override + public MusicDirectory getRandomSongs(int size, Context context, ProgressListener progressListener) throws Exception { + File root = FileUtil.getMusicDirectory(context); + List children = new LinkedList(); + listFilesRecursively(root, children); + MusicDirectory result = new MusicDirectory(); + + if (children.isEmpty()) { + return result; + } + Random random = new Random(); + for (int i = 0; i < size; i++) { + File file = children.get(random.nextInt(children.size())); + result.addChild(createEntry(context, file, getName(file))); + } + + return result; + } + + private void listFilesRecursively(File parent, List children) { + for (File file : FileUtil.listMusicFiles(parent)) { + if (file.isFile()) { + children.add(file); + } else { + listFilesRecursively(file, children); + } + } + } +} diff --git a/src/net/sourceforge/subsonic/androidapp/service/RESTMusicService.java b/src/net/sourceforge/subsonic/androidapp/service/RESTMusicService.java index 4e739a9b..c8deb9bf 100644 --- a/src/net/sourceforge/subsonic/androidapp/service/RESTMusicService.java +++ b/src/net/sourceforge/subsonic/androidapp/service/RESTMusicService.java @@ -203,6 +203,24 @@ public class RESTMusicService implements MusicService { 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 public Indexes getIndexes(String musicFolderId, boolean refresh, Context context, ProgressListener progressListener) throws Exception { diff --git a/src/net/sourceforge/subsonic/androidapp/service/parser/AbstractParser.java b/src/net/sourceforge/subsonic/androidapp/service/parser/AbstractParser.java index 1f71bdc0..d170ba04 100644 --- a/src/net/sourceforge/subsonic/androidapp/service/parser/AbstractParser.java +++ b/src/net/sourceforge/subsonic/androidapp/service/parser/AbstractParser.java @@ -92,6 +92,11 @@ public abstract class AbstractParser { protected boolean getBoolean(String 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) { String s = get(name); diff --git a/src/net/sourceforge/subsonic/androidapp/service/parser/MusicDirectoryEntryParser.java b/src/net/sourceforge/subsonic/androidapp/service/parser/MusicDirectoryEntryParser.java index d35577b6..72f091c5 100644 --- a/src/net/sourceforge/subsonic/androidapp/service/parser/MusicDirectoryEntryParser.java +++ b/src/net/sourceforge/subsonic/androidapp/service/parser/MusicDirectoryEntryParser.java @@ -38,6 +38,7 @@ public class MusicDirectoryEntryParser extends AbstractParser { entry.setDirectory(getBoolean("isDir")); entry.setCoverArt(get("coverArt")); entry.setArtist(get("artist")); + entry.setStarred(getValueExists("starred")); if (!entry.isDirectory()) { entry.setAlbum(get("album")); @@ -53,7 +54,9 @@ public class MusicDirectoryEntryParser extends AbstractParser { entry.setBitRate(getInteger("bitRate")); entry.setPath(get("path")); entry.setVideo(getBoolean("isVideo")); + entry.setDiscNumber(getInteger("discNumber")); } + return entry; } } \ No newline at end of file diff --git a/src/net/sourceforge/subsonic/androidapp/util/AlbumView.java b/src/net/sourceforge/subsonic/androidapp/util/AlbumView.java index a4dd3acd..b7eaf158 100644 --- a/src/net/sourceforge/subsonic/androidapp/util/AlbumView.java +++ b/src/net/sourceforge/subsonic/androidapp/util/AlbumView.java @@ -1,55 +1,93 @@ -/* - This file is part of Subsonic. - - Subsonic is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Subsonic is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Subsonic. If not, see . - - Copyright 2009 (C) Sindre Mehus - */ -package net.sourceforge.subsonic.androidapp.util; - -import android.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.LinearLayout; -import android.widget.TextView; -import net.sourceforge.subsonic.androidapp.R; -import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; - -/** - * Used to display albums in a {@code ListView}. - * - * @author Sindre Mehus - */ -public class AlbumView extends LinearLayout { - - private TextView titleView; - private TextView artistView; - private View coverArtView; - - public AlbumView(Context context) { - super(context); - LayoutInflater.from(context).inflate(R.layout.album_list_item, this, true); - - titleView = (TextView) findViewById(R.id.album_title); - artistView = (TextView) findViewById(R.id.album_artist); - coverArtView = findViewById(R.id.album_coverart); - } - - public void setAlbum(MusicDirectory.Entry album, ImageLoader imageLoader) { - titleView.setText(album.getTitle()); - artistView.setText(album.getArtist()); - artistView.setVisibility(album.getArtist() == null ? View.GONE : View.VISIBLE); - imageLoader.loadImage(coverArtView, album, false, true); - } -} +/* + This file is part of Subsonic. + + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see . + + Copyright 2009 (C) Sindre Mehus + */ +package net.sourceforge.subsonic.androidapp.util; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; +import net.sourceforge.subsonic.androidapp.R; +import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; +import net.sourceforge.subsonic.androidapp.service.MusicService; +import net.sourceforge.subsonic.androidapp.service.MusicServiceFactory; + +/** + * Used to display albums in a {@code ListView}. + * + * @author Sindre Mehus + */ +public class AlbumView extends LinearLayout { + + private TextView titleView; + private TextView artistView; + private View coverArtView; + private ImageView starImageView; + + public AlbumView(Context context) { + super(context); + LayoutInflater.from(context).inflate(R.layout.album_list_item, this, true); + + titleView = (TextView) findViewById(R.id.album_title); + artistView = (TextView) findViewById(R.id.album_artist); + coverArtView = findViewById(R.id.album_coverart); + starImageView = (ImageView) findViewById(R.id.album_star); + } + + 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(); + } + }); + } +} diff --git a/src/net/sourceforge/subsonic/androidapp/util/Constants.java b/src/net/sourceforge/subsonic/androidapp/util/Constants.java index f8c8964d..2e98d710 100644 --- a/src/net/sourceforge/subsonic/androidapp/util/Constants.java +++ b/src/net/sourceforge/subsonic/androidapp/util/Constants.java @@ -29,7 +29,7 @@ public final class Constants { // REST protocol version and client ID. // 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"; // Names for intent extras. diff --git a/src/net/sourceforge/subsonic/androidapp/util/SongView.java b/src/net/sourceforge/subsonic/androidapp/util/SongView.java index b2ce8129..2b2c5673 100644 --- a/src/net/sourceforge/subsonic/androidapp/util/SongView.java +++ b/src/net/sourceforge/subsonic/androidapp/util/SongView.java @@ -1,178 +1,218 @@ -/* - This file is part of Subsonic. - - Subsonic is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Subsonic is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Subsonic. If not, see . - - Copyright 2009 (C) Sindre Mehus - */ -package net.sourceforge.subsonic.androidapp.util; - -import android.content.Context; -import android.os.Handler; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.Checkable; -import android.widget.CheckedTextView; -import android.widget.LinearLayout; -import android.widget.TextView; -import net.sourceforge.subsonic.androidapp.R; -import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; -import net.sourceforge.subsonic.androidapp.service.DownloadService; -import net.sourceforge.subsonic.androidapp.service.DownloadServiceImpl; -import net.sourceforge.subsonic.androidapp.service.DownloadFile; - -import java.io.File; -import java.util.WeakHashMap; - -/** - * Used to display songs in a {@code ListView}. - * - * @author Sindre Mehus - */ -public class SongView extends LinearLayout implements Checkable { - - private static final String TAG = SongView.class.getSimpleName(); - private static final WeakHashMap INSTANCES = new WeakHashMap(); - private static Handler handler; - - private CheckedTextView checkedTextView; - private TextView titleTextView; - private TextView artistTextView; - private TextView durationTextView; - private TextView statusTextView; - private MusicDirectory.Entry song; - - public SongView(Context context) { - super(context); - LayoutInflater.from(context).inflate(R.layout.song_list_item, this, true); - - checkedTextView = (CheckedTextView) findViewById(R.id.song_check); - titleTextView = (TextView) findViewById(R.id.song_title); - artistTextView = (TextView) findViewById(R.id.song_artist); - durationTextView = (TextView) findViewById(R.id.song_duration); - statusTextView = (TextView) findViewById(R.id.song_status); - - INSTANCES.put(this, null); - int instanceCount = INSTANCES.size(); - if (instanceCount > 50) { - Log.w(TAG, instanceCount + " live SongView instances"); - } - startUpdater(); - } - - public void setSong(MusicDirectory.Entry song, boolean checkable) { - this.song = song; - StringBuilder artist = new StringBuilder(40); - - String bitRate = null; - if (song.getBitRate() != null) { - bitRate = String.format(getContext().getString(R.string.song_details_kbps), song.getBitRate()); - } - - String fileFormat = null; - if (song.getTranscodedSuffix() != null && !song.getTranscodedSuffix().equals(song.getSuffix())) { - fileFormat = String.format("%s > %s", song.getSuffix(), song.getTranscodedSuffix()); - } else { - fileFormat = song.getSuffix(); - } - - artist.append(song.getArtist()).append(" (") - .append(String.format(getContext().getString(R.string.song_details_all), bitRate == null ? "" : bitRate, fileFormat)) - .append(")"); - - titleTextView.setText(song.getTitle()); - artistTextView.setText(artist); - durationTextView.setText(Util.formatDuration(song.getDuration())); - checkedTextView.setVisibility(checkable && !song.isVideo() ? View.VISIBLE : View.GONE); - - update(); - } - - private void update() { - DownloadService downloadService = DownloadServiceImpl.getInstance(); - if (downloadService == null) { - return; - } - - DownloadFile downloadFile = downloadService.forSong(song); - File completeFile = downloadFile.getCompleteFile(); - File partialFile = downloadFile.getPartialFile(); - - int leftImage = 0; - int rightImage = 0; - - if (completeFile.exists()) { - leftImage = downloadFile.isSaved() ? R.drawable.ic_stat_saved : R.drawable.ic_stat_downloaded; - } - - if (downloadFile.isDownloading() && !downloadFile.isDownloadCancelled() && partialFile.exists()) { - statusTextView.setText(Util.formatLocalizedBytes(partialFile.length(), getContext())); - rightImage = R.drawable.ic_stat_downloading; - } else { - statusTextView.setText(null); - } - statusTextView.setCompoundDrawablesWithIntrinsicBounds(leftImage, 0, rightImage, 0); - - boolean playing = downloadService.getCurrentPlaying() == downloadFile; - if (playing) { - titleTextView.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_stat_play, 0, 0, 0); - } else { - titleTextView.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); - } - } - - 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(); - } -} +/* + This file is part of Subsonic. + + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see . + + Copyright 2009 (C) Sindre Mehus + */ +package net.sourceforge.subsonic.androidapp.util; + +import android.content.Context; +import android.os.Handler; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.Checkable; +import android.widget.CheckedTextView; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; +import net.sourceforge.subsonic.androidapp.R; +import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; +import net.sourceforge.subsonic.androidapp.service.DownloadService; +import net.sourceforge.subsonic.androidapp.service.DownloadServiceImpl; +import net.sourceforge.subsonic.androidapp.service.DownloadFile; +import net.sourceforge.subsonic.androidapp.service.MusicService; +import net.sourceforge.subsonic.androidapp.service.MusicServiceFactory; + +import java.io.File; +import java.util.WeakHashMap; + +/** + * Used to display songs in a {@code ListView}. + * + * @author Sindre Mehus + */ +public class SongView extends LinearLayout implements Checkable { + + private static final String TAG = SongView.class.getSimpleName(); + private static final WeakHashMap INSTANCES = new WeakHashMap(); + private static Handler handler; + + private CheckedTextView checkedTextView; + private ImageView starImageView; + private TextView titleTextView; + private TextView artistTextView; + private TextView durationTextView; + private TextView statusTextView; + private MusicDirectory.Entry song; + + public SongView(Context context) { + super(context); + LayoutInflater.from(context).inflate(R.layout.song_list_item, this, true); + + checkedTextView = (CheckedTextView) findViewById(R.id.song_check); + starImageView = (ImageView) findViewById(R.id.song_star); + titleTextView = (TextView) findViewById(R.id.song_title); + artistTextView = (TextView) findViewById(R.id.song_artist); + durationTextView = (TextView) findViewById(R.id.song_duration); + statusTextView = (TextView) findViewById(R.id.song_status); + + INSTANCES.put(this, null); + int instanceCount = INSTANCES.size(); + + if (instanceCount > 50) { + Log.w(TAG, instanceCount + " live SongView instances"); + } + + startUpdater(); + } + + public void setSong(final MusicDirectory.Entry song, boolean checkable) { + this.song = song; + StringBuilder artist = new StringBuilder(40); + + String bitRate = null; + if (song.getBitRate() != null) { + bitRate = String.format(getContext().getString(R.string.song_details_kbps), song.getBitRate()); + } + + String fileFormat = null; + if (song.getTranscodedSuffix() != null && !song.getTranscodedSuffix().equals(song.getSuffix())) { + fileFormat = String.format("%s > %s", song.getSuffix(), song.getTranscodedSuffix()); + } else { + fileFormat = song.getSuffix(); + } + + artist.append(song.getArtist()).append(" (") + .append(String.format(getContext().getString(R.string.song_details_all), bitRate == null ? "" : bitRate, fileFormat)) + .append(")"); + + titleTextView.setText(song.getTitle()); + artistTextView.setText(artist); + durationTextView.setText(Util.formatDuration(song.getDuration())); + starImageView.setImageDrawable(song.getStarred() ? getResources().getDrawable(R.drawable.star) : getResources().getDrawable(R.drawable.star_hollow)); + checkedTextView.setVisibility(checkable && !song.isVideo() ? View.VISIBLE : View.GONE); + + starImageView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + final boolean isStarred = song.getStarred(); + final String id = song.getId(); + + if (!isStarred) { + starImageView.setImageDrawable(getResources().getDrawable(R.drawable.star)); + song.setStarred(true); + } else { + starImageView.setImageDrawable(getResources().getDrawable(R.drawable.star_hollow)); + song.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(); + } + }); + + update(); + } + + private void update() { + DownloadService downloadService = DownloadServiceImpl.getInstance(); + if (downloadService == null) { + return; + } + + DownloadFile downloadFile = downloadService.forSong(song); + File completeFile = downloadFile.getCompleteFile(); + File partialFile = downloadFile.getPartialFile(); + + int leftImage = 0; + int rightImage = 0; + + if (completeFile.exists()) { + leftImage = downloadFile.isSaved() ? R.drawable.ic_stat_saved : R.drawable.ic_stat_downloaded; + } + + if (downloadFile.isDownloading() && !downloadFile.isDownloadCancelled() && partialFile.exists()) { + statusTextView.setText(Util.formatLocalizedBytes(partialFile.length(), getContext())); + rightImage = R.drawable.ic_stat_downloading; + } else { + statusTextView.setText(null); + } + statusTextView.setCompoundDrawablesWithIntrinsicBounds(leftImage, 0, rightImage, 0); + + boolean playing = downloadService.getCurrentPlaying() == downloadFile; + if (playing) { + titleTextView.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_stat_play, 0, 0, 0); + } else { + titleTextView.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); + } + } + + 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(); + } +} diff --git a/src/net/sourceforge/subsonic/androidapp/util/Util.java b/src/net/sourceforge/subsonic/androidapp/util/Util.java index 62773909..3394ceac 100644 --- a/src/net/sourceforge/subsonic/androidapp/util/Util.java +++ b/src/net/sourceforge/subsonic/androidapp/util/Util.java @@ -102,7 +102,10 @@ public class Util extends DownloadActivity { } 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) {