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:
parent
19839cfd0d
commit
72111240e7
|
@ -2,7 +2,7 @@
|
|||
<manifest xmlns:a="http://schemas.android.com/apk/res/android"
|
||||
package="net.sourceforge.subsonic.androidapp"
|
||||
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.READ_PHONE_STATE"/>
|
||||
|
|
|
@ -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;
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 1.3 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
|
@ -13,38 +13,49 @@
|
|||
a:layout_height="wrap_content" a:layout_alignParentTop="true"
|
||||
a:layout_alignParentLeft="true">
|
||||
|
||||
<ImageButton a:id="@+id/download_shuffle" a:src="@drawable/media_shuffle_normal"
|
||||
a:background="@android:color/transparent" a:padding="0dip"
|
||||
a:layout_width="wrap_content" a:layout_height="wrap_content"
|
||||
a:layout_gravity="center_vertical" />
|
||||
<ImageButton a:id="@+id/download_repeat" a:src="@drawable/media_repeat_off"
|
||||
a:background="@android:color/transparent" a:padding="0dip"
|
||||
a:layout_width="wrap_content" a:layout_height="wrap_content"
|
||||
a:layout_gravity="center_vertical" />
|
||||
<ImageButton a:id="@+id/download_previous" a:src="@drawable/media_backward_normal"
|
||||
a:background="@android:color/transparent" a:padding="0dip"
|
||||
a:layout_width="wrap_content" a:layout_height="wrap_content"
|
||||
a:layout_gravity="center_vertical" />
|
||||
<ImageButton a:id="@+id/download_pause" a:src="@drawable/media_pause_normal"
|
||||
a:background="@android:color/transparent" a:padding="0dip"
|
||||
a:layout_width="wrap_content" a:layout_height="wrap_content"
|
||||
a:layout_gravity="center_vertical" />
|
||||
<ImageButton a:id="@+id/download_stop" a:src="@drawable/media_stop_normal"
|
||||
a:background="@android:color/transparent" a:padding="0dip"
|
||||
a:layout_width="wrap_content" a:layout_height="wrap_content"
|
||||
a:layout_gravity="center_vertical" />
|
||||
<ImageButton a:id="@+id/download_start" a:src="@drawable/media_start_normal"
|
||||
a:background="@android:color/transparent" a:padding="0dip"
|
||||
a:layout_width="wrap_content" a:layout_height="wrap_content"
|
||||
a:layout_gravity="center_vertical" />
|
||||
<ImageButton a:id="@+id/download_next" a:src="@drawable/media_forward_normal"
|
||||
a:background="@android:color/transparent" a:padding="0dip"
|
||||
a:layout_width="wrap_content" a:layout_height="wrap_content"
|
||||
a:layout_gravity="center_vertical" />
|
||||
<ImageButton a:id="@+id/download_toggle_list" a:src="@drawable/media_toggle_list_normal"
|
||||
a:background="@android:color/transparent" a:padding="0dip"
|
||||
a:layout_width="wrap_content" a:layout_height="wrap_content"
|
||||
a:layout_gravity="center_vertical" />
|
||||
<ImageButton a:id="@+id/download_shuffle"
|
||||
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:paddingLeft="4dip"
|
||||
a:src="@drawable/media_shuffle_normal" />
|
||||
|
||||
<ImageButton a:id="@+id/download_previous"
|
||||
a:layout_width="0dp" a:layout_height="match_parent"
|
||||
a:layout_gravity="center_vertical" a:layout_weight="1"
|
||||
a:background="@drawable/list_selector_holo_dark" a:padding="0dip"
|
||||
a:src="@drawable/media_backward_normal" />
|
||||
|
||||
<ImageButton a:id="@+id/download_pause" a:layout_width="0dp"
|
||||
a:layout_height="match_parent" a:layout_gravity="center_vertical"
|
||||
a:layout_weight="1" a:background="@drawable/list_selector_holo_dark"
|
||||
a:padding="0dip" a:src="@drawable/media_pause_normal" />
|
||||
|
||||
<ImageButton a:id="@+id/download_stop" a:layout_width="0dp"
|
||||
a:layout_height="match_parent" a:layout_gravity="center_vertical"
|
||||
a:layout_weight="1" a:background="@drawable/list_selector_holo_dark"
|
||||
a:padding="0dip" a:src="@drawable/media_stop_normal" />
|
||||
|
||||
<ImageButton a:id="@+id/download_start" a:layout_width="0dp"
|
||||
a:layout_height="match_parent" a:layout_gravity="center_vertical"
|
||||
a:layout_weight="1" a:background="@drawable/list_selector_holo_dark"
|
||||
a:padding="0dip" a:src="@drawable/media_start_normal" />
|
||||
|
||||
<ImageButton a:id="@+id/download_next" a:layout_width="0dp"
|
||||
a:layout_height="match_parent" a:layout_gravity="center_vertical"
|
||||
a:layout_weight="1" a:background="@drawable/list_selector_holo_dark"
|
||||
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"
|
||||
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>
|
||||
|
||||
<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"
|
||||
a:layout_height="wrap_content" a:layout_gravity="center_horizontal"
|
||||
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:ellipsize="end" a:gravity="center_horizontal" a:textAppearance="?android:attr/textAppearanceMedium"
|
||||
a:layout_marginLeft="12dip" a:layout_marginRight="12dip"
|
||||
a:layout_marginTop="12dip" a:maxLines="2" a:ellipsize="end"
|
||||
a:gravity="center_horizontal" a:textAppearance="?android:attr/textAppearanceMedium"
|
||||
a:textColor="@color/mediaControlForeground" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
@ -67,13 +79,14 @@
|
|||
a:layout_height="fill_parent" a:layout_alignParentLeft="true"
|
||||
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"
|
||||
a:background="@color/black">
|
||||
|
||||
<FrameLayout a:id="@+id/download_album_art_layout"
|
||||
a:layout_width="fill_parent" a:layout_height="fill_parent"
|
||||
a:background="@color/black">
|
||||
|
||||
<ImageView a:id="@+id/download_album_art_image"
|
||||
a:layout_width="wrap_content" a:layout_height="wrap_content"
|
||||
a:layout_gravity="center" a:layout_marginTop="0dip" a:scaleType="fitCenter" />
|
||||
|
||||
|
||||
<LinearLayout a:id="@+id/download_visualizer_view_layout"
|
||||
a:layout_width="fill_parent" a:layout_height="60dip"
|
||||
a:layout_marginLeft="60dip" a:layout_marginRight="60dip"
|
||||
|
|
|
@ -6,15 +6,10 @@
|
|||
a:id="@+id/download_playlist_flipper" a:layout_width="fill_parent"
|
||||
a:layout_height="0dip" a:layout_weight="1">
|
||||
|
||||
<RelativeLayout
|
||||
a:id="@+id/download_album_art_layout"
|
||||
a:layout_width="fill_parent"
|
||||
a:layout_height="fill_parent"
|
||||
a:layout_weight="1"
|
||||
a:gravity="left"
|
||||
a:orientation="vertical"
|
||||
a:background="@color/black"
|
||||
>
|
||||
<RelativeLayout a:id="@+id/download_album_art_layout"
|
||||
a:layout_width="fill_parent" a:layout_height="fill_parent"
|
||||
a:layout_weight="1" a:gravity="left" a:orientation="vertical"
|
||||
a:background="@color/black">
|
||||
|
||||
<ImageView a:id="@+id/download_album_art_image"
|
||||
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"
|
||||
a:layout_height="wrap_content" a:layout_gravity="center_horizontal"
|
||||
a:layout_above="@+id/download_status" a:layout_centerHorizontal="true"
|
||||
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_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" />
|
||||
|
||||
<LinearLayout a:id="@+id/download_visualizer_view_layout"
|
||||
a:layout_width="fill_parent" a:layout_height="60dip"
|
||||
|
@ -48,16 +44,11 @@
|
|||
a:layout_height="wrap_content" a:layout_marginTop="0dip"
|
||||
a:background="@color/mediaControlBackground" a:orientation="horizontal"
|
||||
a:paddingBottom="0dip" a:paddingTop="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: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"
|
||||
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_weight="1" a:background="@drawable/list_selector_holo_dark"
|
||||
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"
|
||||
a:layout_width="0dip" a:layout_height="fill_parent" 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" />
|
||||
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>
|
||||
|
||||
<include layout="@layout/download_slider" />
|
||||
|
|
|
@ -38,10 +38,11 @@
|
|||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
a:layout_width="wrap_content"
|
||||
a:layout_height="wrap_content"
|
||||
a:layout_gravity="right|center_vertical"
|
||||
a:paddingRight="6dip"/>
|
||||
<!-- a:src="@drawable/list_item_more"-->
|
||||
|
||||
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" />
|
||||
|
||||
</LinearLayout>
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
a:gravity="center_vertical"
|
||||
a:checkMark="@drawable/btn_check_custom"
|
||||
a:paddingLeft="3dip"/>
|
||||
|
||||
|
||||
<LinearLayout a:orientation="vertical"
|
||||
a:layout_width="0dip"
|
||||
a:layout_height="wrap_content"
|
||||
|
@ -73,4 +73,12 @@
|
|||
|
||||
</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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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<String, TimeLimitedCache<MusicDirectory>> cachedMusicDirectories;
|
||||
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<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 String restUrl;
|
||||
|
||||
public CachedMusicService(MusicService musicService) {
|
||||
this.musicService = musicService;
|
||||
cachedMusicDirectories = new LRUCache<String, TimeLimitedCache<MusicDirectory>>(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<MusicFolder> getMusicFolders(Context context, ProgressListener progressListener) throws Exception {
|
||||
checkSettingsChanged(context);
|
||||
List<MusicFolder> 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<MusicDirectory> 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<MusicDirectory>(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<Playlist> getPlaylists(boolean refresh, Context context, ProgressListener progressListener) throws Exception {
|
||||
checkSettingsChanged(context);
|
||||
List<Playlist> 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<MusicDirectory.Entry> 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<String> 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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<String, TimeLimitedCache<MusicDirectory>> cachedMusicDirectories;
|
||||
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<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 String restUrl;
|
||||
|
||||
public CachedMusicService(MusicService musicService) {
|
||||
this.musicService = musicService;
|
||||
cachedMusicDirectories = new LRUCache<String, TimeLimitedCache<MusicDirectory>>(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<MusicFolder> getMusicFolders(Context context, ProgressListener progressListener) throws Exception {
|
||||
checkSettingsChanged(context);
|
||||
List<MusicFolder> 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<MusicDirectory> 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<MusicDirectory>(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<Playlist> getPlaylists(boolean refresh, Context context, ProgressListener progressListener) throws Exception {
|
||||
checkSettingsChanged(context);
|
||||
List<Playlist> 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<MusicDirectory.Entry> 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<String> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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<MusicFolder> 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<Playlist> getPlaylists(boolean refresh, Context context, ProgressListener progressListener) throws Exception;
|
||||
|
||||
void createPlaylist(String id, String name, List<MusicDirectory.Entry> 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<String> 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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<MusicFolder> 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<Playlist> getPlaylists(boolean refresh, Context context, ProgressListener progressListener) throws Exception;
|
||||
|
||||
void createPlaylist(String id, String name, List<MusicDirectory.Entry> 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<String> 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;
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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<Artist> artists = new ArrayList<Artist>();
|
||||
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.<Artist>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<String> names = new HashSet<String>();
|
||||
|
||||
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<MusicFolder> 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<Playlist> 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<MusicDirectory.Entry> 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<String> 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<File> children = new LinkedList<File>();
|
||||
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<File> 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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<Artist> artists = new ArrayList<Artist>();
|
||||
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.<Artist>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<String> names = new HashSet<String>();
|
||||
|
||||
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<MusicFolder> 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<Playlist> 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<MusicDirectory.Entry> 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<String> 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<File> children = new LinkedList<File>();
|
||||
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<File> children) {
|
||||
for (File file : FileUtil.listMusicFiles(parent)) {
|
||||
if (file.isFile()) {
|
||||
children.add(file);
|
||||
} else {
|
||||
listFilesRecursively(file, children);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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<SongView, ?> INSTANCES = new WeakHashMap<SongView, Object>();
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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<SongView, ?> INSTANCES = new WeakHashMap<SongView, Object>();
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue