Add album/track star support

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

View File

@ -2,7 +2,7 @@
<manifest xmlns:a="http://schemas.android.com/apk/res/android"
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"/>

View File

@ -131,158 +131,162 @@ public final class R {
public static final int select_album_play_all_normal=0x7f02004b;
public static final int select_album_play_all_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

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -13,38 +13,49 @@
a:layout_height="wrap_content" a:layout_alignParentTop="true"
a:layout_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"

View File

@ -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" />

View File

@ -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>

View File

@ -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>

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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);
}
}
}
}

View File

@ -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 {

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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();
}
});
}
}

View File

@ -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.

View File

@ -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();
}
}

View File

@ -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) {