From 4e4e12496c63c96e11d375f7cf9ef49222c4d1ef Mon Sep 17 00:00:00 2001 From: Joshua Bahnsen Date: Mon, 1 Apr 2013 18:16:45 -0700 Subject: [PATCH] Fix album art retrieval on main thread, search settings consistency, disable server side scaling randomize album art when viewing tracks, keep aspect ratio when scaling album art --- .classpath | 17 +- .gitattributes | 22 + .gitignore | 163 +++ AndroidManifest.xml | 4 +- .../sourceforge/subsonic/androidapp/R.java | 190 ++- lint.xml | 3 + res/values/arrays.xml | 99 +- res/values/strings.xml | 44 +- res/xml/settings.xml | 29 +- .../androidapp/activity/SearchActivity.java | 735 +++++----- .../activity/SelectAlbumActivity.java | 1285 +++++++++-------- .../activity/SubsonicTabActivity.java | 1102 +++++++------- .../service/DownloadServiceImpl.java | 13 +- .../service/OfflineMusicService.java | 2 +- .../androidapp/service/RESTMusicService.java | 94 +- .../subsonic/androidapp/util/Constants.java | 1 + .../subsonic/androidapp/util/FileUtil.java | 7 +- .../subsonic/androidapp/util/ImageLoader.java | 23 +- .../util/TabActivityBackgroundTask.java | 2 +- .../subsonic/androidapp/util/Util.java | 19 + 20 files changed, 2005 insertions(+), 1849 deletions(-) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 lint.xml diff --git a/.classpath b/.classpath index 2fbb5480..d3bb07b4 100644 --- a/.classpath +++ b/.classpath @@ -1,9 +1,8 @@ - - - - - - - - - + + + + + + + + diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..ab8ed3f3 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,22 @@ +# Auto detect text files and perform LF normalization +#* text=auto + +# Custom for Visual Studio +*.cs diff=csharp +*.sln merge=union +*.csproj merge=union +*.vbproj merge=union +*.fsproj merge=union +*.dbproj merge=union + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..5ebd21a1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,163 @@ +################# +## Eclipse +################# + +*.pydevproject +.project +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.classpath +.settings/ +.loadpath + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# PDT-specific +.buildpath + + +################# +## Visual Studio +################# + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results +[Dd]ebug/ +[Rr]elease/ +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.vspscc +.builds +*.dotCover + +## TODO: If you have NuGet Package Restore enabled, uncomment this +#packages/ + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf + +# Visual Studio profiler +*.psess +*.vsp + +# ReSharper is a .NET coding add-in +_ReSharper* + +# Installshield output folder +[Ee]xpress + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish + +# Others +[Bb]in +[Oo]bj +sql +TestResults +*.Cache +ClientBin +stylecop.* +~$* +*.dbmdl +Generated_Code #added for RIA/Silverlight projects + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML + + + +############ +## Windows +############ + +# Windows image file caches +Thumbs.db + +# Folder config file +Desktop.ini + + +############# +## Python +############# + +*.py[co] + +# Packages +*.egg +*.egg-info +dist +build +eggs +parts +bin +var +sdist +develop-eggs +.installed.cfg + +# Installer logs +pip-log.txt + +# Unit test / coverage reports +.coverage +.tox + +#Translations +*.mo + +#Mr Developer +.mr.developer.cfg + +# Mac crap +.DS_Store diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 2f8c905e..95293178 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -1,8 +1,8 @@ + a:versionCode="64" + a:versionName="3.9.9.23" a:installLocation="auto"> diff --git a/gen/net/sourceforge/subsonic/androidapp/R.java b/gen/net/sourceforge/subsonic/androidapp/R.java index e94bcc9c..9c9afb47 100644 --- a/gen/net/sourceforge/subsonic/androidapp/R.java +++ b/gen/net/sourceforge/subsonic/androidapp/R.java @@ -19,20 +19,14 @@ public final class R { public static final int bufferLengthValues=0x7f060008; public static final int cacheSizeNames=0x7f060005; public static final int cacheSizeValues=0x7f060004; - public static final int defaultSearchNames=0x7f060013; - public static final int defaultSearchValues=0x7f060012; - public static final int maxAlbumsNames=0x7f06000d; - public static final int maxAlbumsValues=0x7f06000c; - public static final int maxArtistsNames=0x7f060011; - public static final int maxArtistsValues=0x7f060010; public static final int maxBitrateNames=0x7f060007; public static final int maxBitrateValues=0x7f060006; - public static final int maxSongsNames=0x7f06000f; - public static final int maxSongsValues=0x7f06000e; public static final int networkTimeoutNames=0x7f06000b; public static final int networkTimeoutValues=0x7f06000a; public static final int preloadCountNames=0x7f060003; public static final int preloadCountValues=0x7f060002; + public static final int searchNames=0x7f06000d; + public static final int searchValues=0x7f06000c; public static final int themeNames=0x7f060001; public static final int themeValues=0x7f060000; } @@ -349,12 +343,12 @@ public final class R { public static final int select_album_n_songs_downloading=0x7f0a0001; } public static final class string { - public static final int background_task_loading=0x7f0900f0; - public static final int background_task_network_error=0x7f0900f2; - public static final int background_task_no_network=0x7f0900f1; - public static final int background_task_not_found=0x7f0900f3; - public static final int background_task_parse_error=0x7f0900f4; - public static final int background_task_wait=0x7f0900ef; + public static final int background_task_loading=0x7f0900e6; + public static final int background_task_network_error=0x7f0900e8; + public static final int background_task_no_network=0x7f0900e7; + public static final int background_task_not_found=0x7f0900e9; + public static final int background_task_parse_error=0x7f0900ea; + public static final int background_task_wait=0x7f0900e5; public static final int button_bar_browse=0x7f09000b; public static final int button_bar_home=0x7f09000a; public static final int button_bar_now_playing=0x7f09000e; @@ -402,9 +396,9 @@ public final class R { public static final int download_repeat_single=0x7f09005f; public static final int download_visualizer_off=0x7f090061; public static final int download_visualizer_on=0x7f090060; - public static final int equalizer_enabled=0x7f090101; - public static final int equalizer_label=0x7f090100; - public static final int equalizer_preset=0x7f090102; + public static final int equalizer_enabled=0x7f0900f7; + public static final int equalizer_label=0x7f0900f6; + public static final int equalizer_preset=0x7f0900f8; public static final int error_label=0x7f09006b; public static final int help_back=0x7f090027; public static final int help_close=0x7f090028; @@ -439,14 +433,14 @@ public final class R { /** TODO: Keep screen on TODO: Keeping the screen on when downloading may improve download speed */ - public static final int music_service_retry=0x7f0900ee; - public static final int parser_artist_count=0x7f0900fc; - public static final int parser_not_authenticated=0x7f0900fa; - public static final int parser_not_authorized=0x7f0900fb; - public static final int parser_reading=0x7f0900f6; - public static final int parser_reading_done=0x7f0900f7; - public static final int parser_upgrade_client=0x7f0900f8; - public static final int parser_upgrade_server=0x7f0900f9; + public static final int music_service_retry=0x7f0900e4; + public static final int parser_artist_count=0x7f0900f2; + public static final int parser_not_authenticated=0x7f0900f0; + public static final int parser_not_authorized=0x7f0900f1; + public static final int parser_reading=0x7f0900ec; + public static final int parser_reading_done=0x7f0900ed; + public static final int parser_upgrade_client=0x7f0900ee; + public static final int parser_upgrade_server=0x7f0900ef; public static final int play_video_loading=0x7f09002b; public static final int play_video_noplugin=0x7f09002c; /** TODO: Exit @@ -481,22 +475,22 @@ public final class R { public static final int select_album_play_all=0x7f09003f; public static final int select_album_searching=0x7f09003e; public static final int select_album_select=0x7f090039; - public static final int select_artist_all_folders=0x7f0900ff; - public static final int select_artist_folder=0x7f0900fe; - public static final int select_artist_refresh=0x7f0900fd; + public static final int select_artist_all_folders=0x7f0900f5; + public static final int select_artist_folder=0x7f0900f4; + public static final int select_artist_refresh=0x7f0900f3; public static final int select_playlist_empty=0x7f090047; - public static final int service_connecting=0x7f0900f5; + public static final int service_connecting=0x7f0900eb; public static final int settings_appearance_title=0x7f090080; - public static final int settings_buffer_length=0x7f0900b3; - public static final int settings_buffer_length_1=0x7f0900b5; - public static final int settings_buffer_length_10=0x7f0900b9; - public static final int settings_buffer_length_12=0x7f0900ba; - public static final int settings_buffer_length_15=0x7f0900bb; - public static final int settings_buffer_length_2=0x7f0900b6; - public static final int settings_buffer_length_20=0x7f0900bc; - public static final int settings_buffer_length_30=0x7f0900bd; - public static final int settings_buffer_length_5=0x7f0900b7; - public static final int settings_buffer_length_8=0x7f0900b8; + public static final int settings_buffer_length=0x7f0900b5; + public static final int settings_buffer_length_1=0x7f0900b7; + public static final int settings_buffer_length_10=0x7f0900bb; + public static final int settings_buffer_length_12=0x7f0900bc; + public static final int settings_buffer_length_15=0x7f0900bd; + public static final int settings_buffer_length_2=0x7f0900b8; + public static final int settings_buffer_length_20=0x7f0900be; + public static final int settings_buffer_length_30=0x7f0900bf; + public static final int settings_buffer_length_5=0x7f0900b9; + public static final int settings_buffer_length_8=0x7f0900ba; public static final int settings_cache_location=0x7f090078; public static final int settings_cache_location_error=0x7f090079; public static final int settings_cache_size=0x7f090077; @@ -512,37 +506,19 @@ public final class R { public static final int settings_cache_title=0x7f090075; public static final int settings_clear_search_history=0x7f0900a5; public static final int settings_connection_failure=0x7f09007d; - public static final int settings_default_albums=0x7f0900ec; - public static final int settings_default_artists=0x7f0900eb; - public static final int settings_default_songs=0x7f0900ed; - public static final int settings_hide_media_summary=0x7f0900ad; + public static final int settings_default_albums=0x7f0900e2; + public static final int settings_default_artists=0x7f0900e1; + public static final int settings_default_songs=0x7f0900e3; + public static final int settings_hide_media_summary=0x7f0900af; /** TODO: Scrobble to Last.fm TODO: Remember to set up your Last.fm user and password on the Subsonic server */ - public static final int settings_hide_media_title=0x7f0900ac; - public static final int settings_hide_media_toast=0x7f0900ae; + public static final int settings_hide_media_title=0x7f0900ae; + public static final int settings_hide_media_toast=0x7f0900b0; public static final int settings_invalid_url=0x7f09007e; public static final int settings_invalid_username=0x7f09007f; - public static final int settings_max_albums=0x7f0900ce; - public static final int settings_max_albums_10=0x7f0900d0; - public static final int settings_max_albums_100=0x7f0900d5; - public static final int settings_max_albums_20=0x7f0900d1; - public static final int settings_max_albums_250=0x7f0900d6; - public static final int settings_max_albums_30=0x7f0900d2; - public static final int settings_max_albums_40=0x7f0900d3; - public static final int settings_max_albums_5=0x7f0900cf; - public static final int settings_max_albums_50=0x7f0900d4; - public static final int settings_max_albums_500=0x7f0900d7; - public static final int settings_max_artists=0x7f0900e1; - public static final int settings_max_artists_10=0x7f0900e4; - public static final int settings_max_artists_100=0x7f0900e8; - public static final int settings_max_artists_25=0x7f0900e5; - public static final int settings_max_artists_250=0x7f0900e9; - public static final int settings_max_artists_3=0x7f0900e2; - public static final int settings_max_artists_5=0x7f0900e3; - public static final int settings_max_artists_50=0x7f0900e6; - public static final int settings_max_artists_500=0x7f0900ea; - public static final int settings_max_artists_75=0x7f0900e7; + public static final int settings_max_albums=0x7f0900d0; + public static final int settings_max_artists=0x7f0900df; public static final int settings_max_bitrate_112=0x7f09008d; public static final int settings_max_bitrate_128=0x7f09008e; public static final int settings_max_bitrate_160=0x7f09008f; @@ -556,26 +532,18 @@ public final class R { public static final int settings_max_bitrate_mobile=0x7f090088; public static final int settings_max_bitrate_unlimited=0x7f090093; public static final int settings_max_bitrate_wifi=0x7f090087; - public static final int settings_max_songs=0x7f0900d8; - public static final int settings_max_songs_10=0x7f0900da; - public static final int settings_max_songs_100=0x7f0900de; - public static final int settings_max_songs_25=0x7f0900db; - public static final int settings_max_songs_250=0x7f0900df; - public static final int settings_max_songs_5=0x7f0900d9; - public static final int settings_max_songs_50=0x7f0900dc; - public static final int settings_max_songs_500=0x7f0900e0; - public static final int settings_max_songs_75=0x7f0900dd; - public static final int settings_media_button_summary=0x7f0900b0; - public static final int settings_media_button_title=0x7f0900af; - public static final int settings_network_timeout=0x7f0900b4; - public static final int settings_network_timeout_105000=0x7f0900c4; - public static final int settings_network_timeout_120000=0x7f0900c5; - public static final int settings_network_timeout_15000=0x7f0900be; - public static final int settings_network_timeout_30000=0x7f0900bf; - public static final int settings_network_timeout_45000=0x7f0900c0; - public static final int settings_network_timeout_60000=0x7f0900c1; - public static final int settings_network_timeout_75000=0x7f0900c2; - public static final int settings_network_timeout_90000=0x7f0900c3; + public static final int settings_max_songs=0x7f0900e0; + public static final int settings_media_button_summary=0x7f0900b2; + public static final int settings_media_button_title=0x7f0900b1; + public static final int settings_network_timeout=0x7f0900b6; + public static final int settings_network_timeout_105000=0x7f0900c6; + public static final int settings_network_timeout_120000=0x7f0900c7; + public static final int settings_network_timeout_15000=0x7f0900c0; + public static final int settings_network_timeout_30000=0x7f0900c1; + public static final int settings_network_timeout_45000=0x7f0900c2; + public static final int settings_network_timeout_60000=0x7f0900c3; + public static final int settings_network_timeout_75000=0x7f0900c4; + public static final int settings_network_timeout_90000=0x7f0900c5; public static final int settings_network_title=0x7f090086; public static final int settings_other_title=0x7f0900a7; public static final int settings_playback_control_title=0x7f0900a9; @@ -586,25 +554,41 @@ public final class R { public static final int settings_preload_3=0x7f090098; public static final int settings_preload_5=0x7f090099; public static final int settings_preload_unlimited=0x7f09009b; - public static final int settings_screen_lit_summary=0x7f0900b2; - public static final int settings_screen_lit_title=0x7f0900b1; + public static final int settings_screen_lit_summary=0x7f0900b4; + public static final int settings_screen_lit_title=0x7f0900b3; public static final int settings_scrobble_summary=0x7f0900ab; public static final int settings_scrobble_title=0x7f0900aa; + public static final int settings_search_1=0x7f0900d1; + public static final int settings_search_10=0x7f0900d4; + public static final int settings_search_100=0x7f0900dc; + public static final int settings_search_15=0x7f0900d5; + public static final int settings_search_20=0x7f0900d6; + public static final int settings_search_25=0x7f0900d7; + public static final int settings_search_250=0x7f0900dd; + public static final int settings_search_3=0x7f0900d2; + public static final int settings_search_30=0x7f0900d8; + public static final int settings_search_40=0x7f0900d9; + public static final int settings_search_5=0x7f0900d3; + public static final int settings_search_50=0x7f0900da; + public static final int settings_search_500=0x7f0900de; + public static final int settings_search_75=0x7f0900db; public static final int settings_search_history_cleared=0x7f0900a6; public static final int settings_search_title=0x7f0900a8; public static final int settings_server_address=0x7f090072; public static final int settings_server_name=0x7f090071; public static final int settings_server_password=0x7f090074; + public static final int settings_server_scaling_summary=0x7f0900ad; + public static final int settings_server_scaling_title=0x7f0900ac; public static final int settings_server_unused1=0x7f09006f; public static final int settings_server_unused2=0x7f090070; public static final int settings_server_username=0x7f090073; public static final int settings_servers_title=0x7f09006e; - public static final int settings_show_lockscreen_controls=0x7f0900ca; - public static final int settings_show_lockscreen_controls_summary=0x7f0900cb; - public static final int settings_show_notification=0x7f0900c6; - public static final int settings_show_notification_always=0x7f0900c8; - public static final int settings_show_notification_always_summary=0x7f0900c9; - public static final int settings_show_notification_summary=0x7f0900c7; + public static final int settings_show_lockscreen_controls=0x7f0900cc; + public static final int settings_show_lockscreen_controls_summary=0x7f0900cd; + public static final int settings_show_notification=0x7f0900c8; + public static final int settings_show_notification_always=0x7f0900ca; + public static final int settings_show_notification_always_summary=0x7f0900cb; + public static final int settings_show_notification_summary=0x7f0900c9; public static final int settings_test_connection_title=0x7f09006d; public static final int settings_testing_connection=0x7f09007a; public static final int settings_testing_ok=0x7f09007b; @@ -614,21 +598,21 @@ public final class R { public static final int settings_theme_fullscreenlight=0x7f090083; public static final int settings_theme_light=0x7f090084; public static final int settings_theme_title=0x7f090081; - public static final int settings_theme_wheat=0x7f09010a; + public static final int settings_theme_wheat=0x7f090100; public static final int settings_title=0x7f09006c; - public static final int settings_use_stream_proxy=0x7f0900cc; - public static final int settings_use_stream_proxy_summary=0x7f0900cd; + public static final int settings_use_stream_proxy=0x7f0900ce; + public static final int settings_use_stream_proxy_summary=0x7f0900cf; public static final int settings_wifi_required_summary=0x7f090095; public static final int settings_wifi_required_title=0x7f090094; public static final int song_details_all=0x7f090068; public static final int song_details_kbps=0x7f090069; - public static final int util_bytes_format_byte=0x7f090109; - public static final int util_bytes_format_gigabyte=0x7f090106; - public static final int util_bytes_format_kilobyte=0x7f090108; - public static final int util_bytes_format_megabyte=0x7f090107; - public static final int widget_initial_text=0x7f090103; - public static final int widget_sdcard_busy=0x7f090104; - public static final int widget_sdcard_missing=0x7f090105; + public static final int util_bytes_format_byte=0x7f0900ff; + public static final int util_bytes_format_gigabyte=0x7f0900fc; + public static final int util_bytes_format_kilobyte=0x7f0900fe; + public static final int util_bytes_format_megabyte=0x7f0900fd; + public static final int widget_initial_text=0x7f0900f9; + public static final int widget_sdcard_busy=0x7f0900fa; + public static final int widget_sdcard_missing=0x7f0900fb; } public static final class style { public static final int Dark=0x7f0b0000; diff --git a/lint.xml b/lint.xml new file mode 100644 index 00000000..ee0eead5 --- /dev/null +++ b/lint.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/res/values/arrays.xml b/res/values/arrays.xml index fb178a96..f0a2beb8 100644 --- a/res/values/arrays.xml +++ b/res/values/arrays.xml @@ -99,7 +99,6 @@ @string/settings.buffer_length_1 - @string/settings.buffer_length_2 @string/settings.buffer_length_2 @string/settings.buffer_length_5 @string/settings.buffer_length_8 @@ -132,96 +131,38 @@ @string/settings.network_timeout_120000 - + + 1 + 3 5 10 + 15 20 + 25 30 40 50 - 100 - 250 - 500 - - - - @string/settings.max_albums_5 - @string/settings.max_albums_10 - @string/settings.max_albums_20 - @string/settings.max_albums_30 - @string/settings.max_albums_40 - @string/settings.max_albums_50 - @string/settings.max_albums_100 - @string/settings.max_albums_250 - @string/settings.max_albums_500 - - - - 5 - 10 - 25 - 50 75 100 250 500 - - @string/settings.max_songs_5 - @string/settings.max_songs_10 - @string/settings.max_songs_25 - @string/settings.max_songs_50 - @string/settings.max_songs_75 - @string/settings.max_songs_100 - @string/settings.max_songs_250 - @string/settings.max_songs_500 - - - - 5 - 10 - 25 - 50 - 75 - 100 - 250 - 500 - - - - @string/settings.max_artists_5 - @string/settings.max_artists_10 - @string/settings.max_artists_25 - @string/settings.max_artists_50 - @string/settings.max_artists_75 - @string/settings.max_artists_100 - @string/settings.max_artists_250 - @string/settings.max_artists_500 - - - - 3 - 5 - 10 - 25 - 50 - 75 - 100 - 250 - 500 - - - - @string/settings.max_artists_3 - @string/settings.max_artists_5 - @string/settings.max_artists_10 - @string/settings.max_artists_25 - @string/settings.max_artists_50 - @string/settings.max_artists_75 - @string/settings.max_artists_100 - @string/settings.max_artists_250 - @string/settings.max_artists_500 + + @string/settings.search_1 + @string/settings.search_3 + @string/settings.search_5 + @string/settings.search_10 + @string/settings.search_15 + @string/settings.search_20 + @string/settings.search_25 + @string/settings.search_30 + @string/settings.search_40 + @string/settings.search_50 + @string/settings.search_75 + @string/settings.search_100 + @string/settings.search_250 + @string/settings.search_500 diff --git a/res/values/strings.xml b/res/values/strings.xml index e552beaa..6d760870 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -191,6 +191,8 @@ Playback Control Settings Scrobble To Last.fm Remember to set up your Last.fm user and password on the Subsonic server + Server-Side Album Art Scaling + Download scaled images from the server instead of full size (saves bandwidth) Hide From Other Hide music files from other apps. Takes effect next time Android scans your phone for music. @@ -226,34 +228,22 @@ Use Stream Proxy Stream media playback through a proxy (may help stutter) Max Albums - 5 - 10 - 20 - 30 - 40 - 50 - 100 - 250 - 500 - Max Songs - 5 - 10 - 25 - 50 - 75 - 100 - 250 - 500 + 1 + 3 + 5 + 10 + 15 + 20 + 25 + 30 + 40 + 50 + 75 + 100 + 250 + 500 Max Artists - 3 - 5 - 10 - 25 - 50 - 75 - 100 - 250 - 500 + Max Songs Default Artists Default Albums Default Songs diff --git a/res/xml/settings.xml b/res/xml/settings.xml index 0b620ed5..d3f2422d 100644 --- a/res/xml/settings.xml +++ b/res/xml/settings.xml @@ -134,38 +134,38 @@ + . - - Copyright 2009 (C) Sindre Mehus - */ - -package net.sourceforge.subsonic.androidapp.activity; - -import java.util.ArrayList; -import java.util.List; -import java.util.Arrays; - -import android.content.Intent; -import android.os.Bundle; -import android.view.ContextMenu; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.View; -import android.view.MenuItem; -import android.widget.AdapterView; -import android.widget.ImageButton; -import android.widget.ListAdapter; -import android.widget.ListView; -import android.widget.TextView; -import android.net.Uri; -import net.sourceforge.subsonic.androidapp.R; -import net.sourceforge.subsonic.androidapp.domain.Artist; -import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; -import net.sourceforge.subsonic.androidapp.domain.SearchCritera; -import net.sourceforge.subsonic.androidapp.domain.SearchResult; -import net.sourceforge.subsonic.androidapp.service.MusicService; -import net.sourceforge.subsonic.androidapp.service.MusicServiceFactory; -import net.sourceforge.subsonic.androidapp.service.DownloadService; -import net.sourceforge.subsonic.androidapp.util.ArtistAdapter; -import net.sourceforge.subsonic.androidapp.util.BackgroundTask; -import net.sourceforge.subsonic.androidapp.util.Constants; -import net.sourceforge.subsonic.androidapp.util.EntryAdapter; -import net.sourceforge.subsonic.androidapp.util.MergeAdapter; -import net.sourceforge.subsonic.androidapp.util.TabActivityBackgroundTask; -import net.sourceforge.subsonic.androidapp.util.Util; - -/** - * Performs searches and displays the matching artists, albums and songs. - * - * @author Sindre Mehus - */ -public class SearchActivity extends SubsonicTabActivity { - - private static int DEFAULT_ARTISTS; - private static int DEFAULT_ALBUMS; - private static int DEFAULT_SONGS; - - private ListView list; - - private View artistsHeading; - private View albumsHeading; - private View songsHeading; - private TextView searchButton; - private View moreArtistsButton; - private View moreAlbumsButton; - private View moreSongsButton; - private SearchResult searchResult; - private MergeAdapter mergeAdapter; - private ArtistAdapter artistAdapter; - private ListAdapter moreArtistsAdapter; - private EntryAdapter albumAdapter; - private ListAdapter moreAlbumsAdapter; - private ListAdapter moreSongsAdapter; - private EntryAdapter songAdapter; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.search); - - setTitle(R.string.search_title); - - DEFAULT_ARTISTS = Util.getDefaultArtists(this); - DEFAULT_ALBUMS = Util.getDefaultAlbums(this); - DEFAULT_SONGS = Util.getDefaultSongs(this); - - View buttons = LayoutInflater.from(this).inflate(R.layout.search_buttons, null); - - artistsHeading = buttons.findViewById(R.id.search_artists); - albumsHeading = buttons.findViewById(R.id.search_albums); - songsHeading = buttons.findViewById(R.id.search_songs); - - searchButton = (TextView) buttons.findViewById(R.id.search_search); - moreArtistsButton = buttons.findViewById(R.id.search_more_artists); - moreAlbumsButton = buttons.findViewById(R.id.search_more_albums); - moreSongsButton = buttons.findViewById(R.id.search_more_songs); - - list = (ListView) findViewById(R.id.search_list); - - list.setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - if (view == searchButton) { - onSearchRequested(); - } else if (view == moreArtistsButton) { - expandArtists(); - } else if (view == moreAlbumsButton) { - expandAlbums(); - } else if (view == moreSongsButton) { - expandSongs(); - } else { - Object item = parent.getItemAtPosition(position); - if (item instanceof Artist) { - onArtistSelected((Artist) item); - } else if (item instanceof MusicDirectory.Entry) { - MusicDirectory.Entry entry = (MusicDirectory.Entry) item; - if (entry.isDirectory()) { - onAlbumSelected(entry, false); - } else if (entry.isVideo()) { - onVideoSelected(entry); - } else { - onSongSelected(entry, false, true, true, false); - } - - } - } - } - }); - list.setOnTouchListener(gestureListener); - - registerForContextMenu(list); - - onNewIntent(getIntent()); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - super.onCreateOptionsMenu(menu); - - return true; - } - - @Override - protected void onNewIntent(Intent intent) { - super.onNewIntent(intent); - String query = intent.getStringExtra(Constants.INTENT_EXTRA_NAME_QUERY); - boolean autoplay = intent.getBooleanExtra(Constants.INTENT_EXTRA_NAME_AUTOPLAY, false); - boolean requestsearch = intent.getBooleanExtra(Constants.INTENT_EXTRA_REQUEST_SEARCH, false); - - if (query != null) { - mergeAdapter = new MergeAdapter(); - list.setAdapter(mergeAdapter); - search(query, autoplay); - } else { - populateList(); - if (requestsearch) - onSearchRequested(); - } - } - - @Override - public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) { - super.onCreateContextMenu(menu, view, menuInfo); - AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; - Object selectedItem = list.getItemAtPosition(info.position); - - boolean isArtist = selectedItem instanceof Artist; - boolean isAlbum = selectedItem instanceof MusicDirectory.Entry && ((MusicDirectory.Entry) selectedItem).isDirectory(); - boolean isSong = selectedItem instanceof MusicDirectory.Entry && (!((MusicDirectory.Entry) selectedItem).isDirectory()) - && (!((MusicDirectory.Entry) selectedItem).isVideo()); - - if (isArtist || isAlbum) { - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.select_album_context, menu); - } else if (isSong) { - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.select_song_context, menu); - } - } - - @Override - public boolean onContextItemSelected(MenuItem menuItem) { - AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo(); - Object selectedItem = list.getItemAtPosition(info.position); - - Artist artist = selectedItem instanceof Artist ? (Artist) selectedItem : null; - MusicDirectory.Entry entry = selectedItem instanceof MusicDirectory.Entry ? (MusicDirectory.Entry) selectedItem : null; - String id = artist != null ? artist.getId() : entry.getId(); - - switch (menuItem.getItemId()) { - case R.id.album_menu_play_now: - downloadRecursively(id, false, false, true); - break; - case R.id.album_menu_play_last: - downloadRecursively(id, false, true, false); - break; - case R.id.album_menu_pin: - downloadRecursively(id, true, true, false); - break; - case R.id.song_menu_play_now: - onSongSelected(entry, false, false, true, false); - break; - case R.id.song_menu_play_next: - onSongSelected(entry, false, true, false, true); - break; - case R.id.song_menu_play_last: - onSongSelected(entry, false, true, false, false); - break; - default: - return super.onContextItemSelected(menuItem); - } - - return true; - } - - private void search(final String query, final boolean autoplay) { - final int maxArtists = Util.getMaxArtists(this); - final int maxAlbums = Util.getMaxAlbums(this); - final int maxSongs = Util.getMaxSongs(this); - - BackgroundTask task = new TabActivityBackgroundTask(this) { - @Override - protected SearchResult doInBackground() throws Throwable { - SearchCritera criteria = new SearchCritera(query, maxArtists, maxAlbums, maxSongs); - MusicService service = MusicServiceFactory.getMusicService(SearchActivity.this); - return service.search(criteria, SearchActivity.this, this); - } - - @Override - protected void done(SearchResult result) { - searchResult = result; - populateList(); - if (autoplay) { - autoplay(); - } - - } - }; - task.execute(); - } - - private void populateList() { - mergeAdapter = new MergeAdapter(); - mergeAdapter.addView(searchButton, true); - - if (searchResult != null) { - List artists = searchResult.getArtists(); - if (!artists.isEmpty()) { - mergeAdapter.addView(artistsHeading); - List displayedArtists = new ArrayList(artists.subList(0, Math.min(DEFAULT_ARTISTS, artists.size()))); - artistAdapter = new ArtistAdapter(this, displayedArtists); - mergeAdapter.addAdapter(artistAdapter); - if (artists.size() > DEFAULT_ARTISTS) { - moreArtistsAdapter = mergeAdapter.addView(moreArtistsButton, true); - } - } - - List albums = searchResult.getAlbums(); - if (!albums.isEmpty()) { - mergeAdapter.addView(albumsHeading); - List displayedAlbums = new ArrayList(albums.subList(0, Math.min(DEFAULT_ALBUMS, albums.size()))); - albumAdapter = new EntryAdapter(this, getImageLoader(), displayedAlbums, false); - mergeAdapter.addAdapter(albumAdapter); - if (albums.size() > DEFAULT_ALBUMS) { - moreAlbumsAdapter = mergeAdapter.addView(moreAlbumsButton, true); - } - } - - List songs = searchResult.getSongs(); - if (!songs.isEmpty()) { - mergeAdapter.addView(songsHeading); - List displayedSongs = new ArrayList(songs.subList(0, Math.min(DEFAULT_SONGS, songs.size()))); - songAdapter = new EntryAdapter(this, getImageLoader(), displayedSongs, false); - mergeAdapter.addAdapter(songAdapter); - if (songs.size() > DEFAULT_SONGS) { - moreSongsAdapter = mergeAdapter.addView(moreSongsButton, true); - } - } - - boolean empty = searchResult.getArtists().isEmpty() && searchResult.getAlbums().isEmpty() && searchResult.getSongs().isEmpty(); - searchButton.setText(empty ? R.string.search_no_match : R.string.search_search); - } - - list.setAdapter(mergeAdapter); - } - - private void expandArtists() { - artistAdapter.clear(); - for (Artist artist : searchResult.getArtists()) { - artistAdapter.add(artist); - } - artistAdapter.notifyDataSetChanged(); - mergeAdapter.removeAdapter(moreArtistsAdapter); - mergeAdapter.notifyDataSetChanged(); - } - - private void expandAlbums() { - albumAdapter.clear(); - for (MusicDirectory.Entry album : searchResult.getAlbums()) { - albumAdapter.add(album); - } - albumAdapter.notifyDataSetChanged(); - mergeAdapter.removeAdapter(moreAlbumsAdapter); - mergeAdapter.notifyDataSetChanged(); - } - - private void expandSongs() { - songAdapter.clear(); - for (MusicDirectory.Entry song : searchResult.getSongs()) { - songAdapter.add(song); - } - songAdapter.notifyDataSetChanged(); - mergeAdapter.removeAdapter(moreSongsAdapter); - mergeAdapter.notifyDataSetChanged(); - } - - private void onArtistSelected(Artist artist) { - Intent intent = new Intent(this, SelectAlbumActivity.class); - intent.putExtra(Constants.INTENT_EXTRA_NAME_ID, artist.getId()); - intent.putExtra(Constants.INTENT_EXTRA_NAME_NAME, artist.getName()); - Util.startActivityWithoutTransition(this, intent); - } - - private void onAlbumSelected(MusicDirectory.Entry album, boolean autoplay) { - Intent intent = new Intent(SearchActivity.this, SelectAlbumActivity.class); - intent.putExtra(Constants.INTENT_EXTRA_NAME_ID, album.getId()); - intent.putExtra(Constants.INTENT_EXTRA_NAME_NAME, album.getTitle()); - intent.putExtra(Constants.INTENT_EXTRA_NAME_AUTOPLAY, autoplay); - Util.startActivityWithoutTransition(SearchActivity.this, intent); - } - - private void onSongSelected(MusicDirectory.Entry song, boolean save, boolean append, boolean autoplay, boolean playNext) { - DownloadService downloadService = getDownloadService(); - if (downloadService != null) { - if (!append) { - downloadService.clear(); - } - downloadService.download(Arrays.asList(song), save, false, playNext); - if (autoplay) { - downloadService.play(downloadService.size() - 1); - } - - Util.toast(SearchActivity.this, getResources().getQuantityString(R.plurals.select_album_n_songs_added, 1, 1)); - } - } - - private void onVideoSelected(MusicDirectory.Entry entry) { - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setData(Uri.parse(MusicServiceFactory.getMusicService(this).getVideoUrl(this, entry.getId()))); - startActivity(intent); - } - - private void autoplay() { - if (!searchResult.getSongs().isEmpty()) { - onSongSelected(searchResult.getSongs().get(0), false, false, true, false); - } else if (!searchResult.getAlbums().isEmpty()) { - onAlbumSelected(searchResult.getAlbums().get(0), true); - } - } +/* + This file is part of Subsonic. + + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see . + + Copyright 2009 (C) Sindre Mehus + */ + +package net.sourceforge.subsonic.androidapp.activity; + +import java.util.ArrayList; +import java.util.List; +import java.util.Arrays; + +import android.content.Intent; +import android.os.Bundle; +import android.view.ContextMenu; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.View; +import android.view.MenuItem; +import android.widget.AdapterView; +import android.widget.ListAdapter; +import android.widget.ListView; +import android.widget.TextView; +import android.net.Uri; +import net.sourceforge.subsonic.androidapp.R; +import net.sourceforge.subsonic.androidapp.domain.Artist; +import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; +import net.sourceforge.subsonic.androidapp.domain.SearchCritera; +import net.sourceforge.subsonic.androidapp.domain.SearchResult; +import net.sourceforge.subsonic.androidapp.service.MusicService; +import net.sourceforge.subsonic.androidapp.service.MusicServiceFactory; +import net.sourceforge.subsonic.androidapp.service.DownloadService; +import net.sourceforge.subsonic.androidapp.util.ArtistAdapter; +import net.sourceforge.subsonic.androidapp.util.BackgroundTask; +import net.sourceforge.subsonic.androidapp.util.Constants; +import net.sourceforge.subsonic.androidapp.util.EntryAdapter; +import net.sourceforge.subsonic.androidapp.util.MergeAdapter; +import net.sourceforge.subsonic.androidapp.util.TabActivityBackgroundTask; +import net.sourceforge.subsonic.androidapp.util.Util; + +/** + * Performs searches and displays the matching artists, albums and songs. + * + * @author Sindre Mehus + */ +public class SearchActivity extends SubsonicTabActivity { + + private static int DEFAULT_ARTISTS; + private static int DEFAULT_ALBUMS; + private static int DEFAULT_SONGS; + + private ListView list; + + private View artistsHeading; + private View albumsHeading; + private View songsHeading; + private TextView searchButton; + private View moreArtistsButton; + private View moreAlbumsButton; + private View moreSongsButton; + private SearchResult searchResult; + private MergeAdapter mergeAdapter; + private ArtistAdapter artistAdapter; + private ListAdapter moreArtistsAdapter; + private EntryAdapter albumAdapter; + private ListAdapter moreAlbumsAdapter; + private ListAdapter moreSongsAdapter; + private EntryAdapter songAdapter; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.search); + + setTitle(R.string.search_title); + + DEFAULT_ARTISTS = Util.getDefaultArtists(this); + DEFAULT_ALBUMS = Util.getDefaultAlbums(this); + DEFAULT_SONGS = Util.getDefaultSongs(this); + + View buttons = LayoutInflater.from(this).inflate(R.layout.search_buttons, null); + + artistsHeading = buttons.findViewById(R.id.search_artists); + albumsHeading = buttons.findViewById(R.id.search_albums); + songsHeading = buttons.findViewById(R.id.search_songs); + + searchButton = (TextView) buttons.findViewById(R.id.search_search); + moreArtistsButton = buttons.findViewById(R.id.search_more_artists); + moreAlbumsButton = buttons.findViewById(R.id.search_more_albums); + moreSongsButton = buttons.findViewById(R.id.search_more_songs); + + list = (ListView) findViewById(R.id.search_list); + + list.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + if (view == searchButton) { + onSearchRequested(); + } else if (view == moreArtistsButton) { + expandArtists(); + } else if (view == moreAlbumsButton) { + expandAlbums(); + } else if (view == moreSongsButton) { + expandSongs(); + } else { + Object item = parent.getItemAtPosition(position); + if (item instanceof Artist) { + onArtistSelected((Artist) item); + } else if (item instanceof MusicDirectory.Entry) { + MusicDirectory.Entry entry = (MusicDirectory.Entry) item; + if (entry.isDirectory()) { + onAlbumSelected(entry, false); + } else if (entry.isVideo()) { + onVideoSelected(entry); + } else { + onSongSelected(entry, false, true, true, false); + } + + } + } + } + }); + list.setOnTouchListener(gestureListener); + + registerForContextMenu(list); + + onNewIntent(getIntent()); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + + return true; + } + + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + String query = intent.getStringExtra(Constants.INTENT_EXTRA_NAME_QUERY); + boolean autoplay = intent.getBooleanExtra(Constants.INTENT_EXTRA_NAME_AUTOPLAY, false); + boolean requestsearch = intent.getBooleanExtra(Constants.INTENT_EXTRA_REQUEST_SEARCH, false); + + if (query != null) { + mergeAdapter = new MergeAdapter(); + list.setAdapter(mergeAdapter); + search(query, autoplay); + } else { + populateList(); + if (requestsearch) + onSearchRequested(); + } + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) { + super.onCreateContextMenu(menu, view, menuInfo); + AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; + Object selectedItem = list.getItemAtPosition(info.position); + + boolean isArtist = selectedItem instanceof Artist; + boolean isAlbum = selectedItem instanceof MusicDirectory.Entry && ((MusicDirectory.Entry) selectedItem).isDirectory(); + boolean isSong = selectedItem instanceof MusicDirectory.Entry && (!((MusicDirectory.Entry) selectedItem).isDirectory()) + && (!((MusicDirectory.Entry) selectedItem).isVideo()); + + if (isArtist || isAlbum) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.select_album_context, menu); + } else if (isSong) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.select_song_context, menu); + } + } + + @Override + public boolean onContextItemSelected(MenuItem menuItem) { + AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo(); + Object selectedItem = list.getItemAtPosition(info.position); + + Artist artist = selectedItem instanceof Artist ? (Artist) selectedItem : null; + MusicDirectory.Entry entry = selectedItem instanceof MusicDirectory.Entry ? (MusicDirectory.Entry) selectedItem : null; + String id = artist != null ? artist.getId() : entry.getId(); + + switch (menuItem.getItemId()) { + case R.id.album_menu_play_now: + downloadRecursively(id, false, false, true); + break; + case R.id.album_menu_play_last: + downloadRecursively(id, false, true, false); + break; + case R.id.album_menu_pin: + downloadRecursively(id, true, true, false); + break; + case R.id.song_menu_play_now: + onSongSelected(entry, false, false, true, false); + break; + case R.id.song_menu_play_next: + onSongSelected(entry, false, true, false, true); + break; + case R.id.song_menu_play_last: + onSongSelected(entry, false, true, false, false); + break; + default: + return super.onContextItemSelected(menuItem); + } + + return true; + } + + private void search(final String query, final boolean autoplay) { + final int maxArtists = Util.getMaxArtists(this); + final int maxAlbums = Util.getMaxAlbums(this); + final int maxSongs = Util.getMaxSongs(this); + + BackgroundTask task = new TabActivityBackgroundTask(this) { + @Override + protected SearchResult doInBackground() throws Throwable { + SearchCritera criteria = new SearchCritera(query, maxArtists, maxAlbums, maxSongs); + MusicService service = MusicServiceFactory.getMusicService(SearchActivity.this); + return service.search(criteria, SearchActivity.this, this); + } + + @Override + protected void done(SearchResult result) { + searchResult = result; + populateList(); + if (autoplay) { + autoplay(); + } + + } + }; + task.execute(); + } + + private void populateList() { + mergeAdapter = new MergeAdapter(); + mergeAdapter.addView(searchButton, true); + + if (searchResult != null) { + List artists = searchResult.getArtists(); + if (!artists.isEmpty()) { + mergeAdapter.addView(artistsHeading); + List displayedArtists = new ArrayList(artists.subList(0, Math.min(DEFAULT_ARTISTS, artists.size()))); + artistAdapter = new ArtistAdapter(this, displayedArtists); + mergeAdapter.addAdapter(artistAdapter); + if (artists.size() > DEFAULT_ARTISTS) { + moreArtistsAdapter = mergeAdapter.addView(moreArtistsButton, true); + } + } + + List albums = searchResult.getAlbums(); + if (!albums.isEmpty()) { + mergeAdapter.addView(albumsHeading); + List displayedAlbums = new ArrayList(albums.subList(0, Math.min(DEFAULT_ALBUMS, albums.size()))); + albumAdapter = new EntryAdapter(this, getImageLoader(), displayedAlbums, false); + mergeAdapter.addAdapter(albumAdapter); + if (albums.size() > DEFAULT_ALBUMS) { + moreAlbumsAdapter = mergeAdapter.addView(moreAlbumsButton, true); + } + } + + List songs = searchResult.getSongs(); + if (!songs.isEmpty()) { + mergeAdapter.addView(songsHeading); + List displayedSongs = new ArrayList(songs.subList(0, Math.min(DEFAULT_SONGS, songs.size()))); + songAdapter = new EntryAdapter(this, getImageLoader(), displayedSongs, false); + mergeAdapter.addAdapter(songAdapter); + if (songs.size() > DEFAULT_SONGS) { + moreSongsAdapter = mergeAdapter.addView(moreSongsButton, true); + } + } + + boolean empty = searchResult.getArtists().isEmpty() && searchResult.getAlbums().isEmpty() && searchResult.getSongs().isEmpty(); + searchButton.setText(empty ? R.string.search_no_match : R.string.search_search); + } + + list.setAdapter(mergeAdapter); + } + + private void expandArtists() { + artistAdapter.clear(); + for (Artist artist : searchResult.getArtists()) { + artistAdapter.add(artist); + } + artistAdapter.notifyDataSetChanged(); + mergeAdapter.removeAdapter(moreArtistsAdapter); + mergeAdapter.notifyDataSetChanged(); + } + + private void expandAlbums() { + albumAdapter.clear(); + for (MusicDirectory.Entry album : searchResult.getAlbums()) { + albumAdapter.add(album); + } + albumAdapter.notifyDataSetChanged(); + mergeAdapter.removeAdapter(moreAlbumsAdapter); + mergeAdapter.notifyDataSetChanged(); + } + + private void expandSongs() { + songAdapter.clear(); + for (MusicDirectory.Entry song : searchResult.getSongs()) { + songAdapter.add(song); + } + songAdapter.notifyDataSetChanged(); + mergeAdapter.removeAdapter(moreSongsAdapter); + mergeAdapter.notifyDataSetChanged(); + } + + private void onArtistSelected(Artist artist) { + Intent intent = new Intent(this, SelectAlbumActivity.class); + intent.putExtra(Constants.INTENT_EXTRA_NAME_ID, artist.getId()); + intent.putExtra(Constants.INTENT_EXTRA_NAME_NAME, artist.getName()); + Util.startActivityWithoutTransition(this, intent); + } + + private void onAlbumSelected(MusicDirectory.Entry album, boolean autoplay) { + Intent intent = new Intent(SearchActivity.this, SelectAlbumActivity.class); + intent.putExtra(Constants.INTENT_EXTRA_NAME_ID, album.getId()); + intent.putExtra(Constants.INTENT_EXTRA_NAME_NAME, album.getTitle()); + intent.putExtra(Constants.INTENT_EXTRA_NAME_AUTOPLAY, autoplay); + Util.startActivityWithoutTransition(SearchActivity.this, intent); + } + + private void onSongSelected(MusicDirectory.Entry song, boolean save, boolean append, boolean autoplay, boolean playNext) { + DownloadService downloadService = getDownloadService(); + if (downloadService != null) { + if (!append) { + downloadService.clear(); + } + downloadService.download(Arrays.asList(song), save, false, playNext); + if (autoplay) { + downloadService.play(downloadService.size() - 1); + } + + Util.toast(SearchActivity.this, getResources().getQuantityString(R.plurals.select_album_n_songs_added, 1, 1)); + } + } + + private void onVideoSelected(MusicDirectory.Entry entry) { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse(MusicServiceFactory.getMusicService(this).getVideoUrl(this, entry.getId()))); + startActivity(intent); + } + + private void autoplay() { + if (!searchResult.getSongs().isEmpty()) { + onSongSelected(searchResult.getSongs().get(0), false, false, true, false); + } else if (!searchResult.getAlbums().isEmpty()) { + onAlbumSelected(searchResult.getAlbums().get(0), true); + } + } } \ No newline at end of file diff --git a/src/net/sourceforge/subsonic/androidapp/activity/SelectAlbumActivity.java b/src/net/sourceforge/subsonic/androidapp/activity/SelectAlbumActivity.java index 41c6b55b..9af9184b 100644 --- a/src/net/sourceforge/subsonic/androidapp/activity/SelectAlbumActivity.java +++ b/src/net/sourceforge/subsonic/androidapp/activity/SelectAlbumActivity.java @@ -1,641 +1,644 @@ -/* - This file is part of Subsonic. - - Subsonic is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Subsonic is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Subsonic. If not, see . - - Copyright 2009 (C) Sindre Mehus - */ -package net.sourceforge.subsonic.androidapp.activity; - -import android.app.ActionBar; -import android.app.AlertDialog; -import android.content.DialogInterface; -import android.content.Intent; -import android.graphics.drawable.Drawable; -import android.net.Uri; -import android.os.Bundle; -import android.util.Log; -import android.view.ContextMenu; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.widget.AdapterView; -import android.widget.Button; -import android.widget.ListView; -import net.sourceforge.subsonic.androidapp.R; -import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; -import net.sourceforge.subsonic.androidapp.service.DownloadFile; -import net.sourceforge.subsonic.androidapp.service.MusicService; -import net.sourceforge.subsonic.androidapp.service.MusicServiceFactory; -import net.sourceforge.subsonic.androidapp.util.Constants; -import net.sourceforge.subsonic.androidapp.util.EntryAdapter; -import net.sourceforge.subsonic.androidapp.util.Pair; -import net.sourceforge.subsonic.androidapp.util.TabActivityBackgroundTask; -import net.sourceforge.subsonic.androidapp.util.Util; - -import java.util.ArrayList; -import java.util.List; - -public class SelectAlbumActivity extends SubsonicTabActivity { - - private static final String TAG = SelectAlbumActivity.class.getSimpleName(); - - private ListView entryList; - private View footer; - private View emptyView; - private Button selectButton; - private Button playNowButton; - private Button playLastButton; - private Button pinButton; - private Button unpinButton; - private Button deleteButton; - private Button moreButton; - private boolean licenseValid; - private boolean playAllButtonVisible; - private MenuItem playAllButton; - - /** - * Called when the activity is first created. - */ - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.select_album); - - entryList = (ListView) findViewById(R.id.select_album_entries); - - footer = LayoutInflater.from(this).inflate(R.layout.select_album_footer, entryList, false); - entryList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); - entryList.setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - if (position >= 0) { - MusicDirectory.Entry entry = (MusicDirectory.Entry) parent.getItemAtPosition(position); - if (entry.isDirectory()) { - Intent intent = new Intent(SelectAlbumActivity.this, SelectAlbumActivity.class); - intent.putExtra(Constants.INTENT_EXTRA_NAME_ID, entry.getId()); - intent.putExtra(Constants.INTENT_EXTRA_NAME_NAME, entry.getTitle()); - Util.startActivityWithoutTransition(SelectAlbumActivity.this, intent); - } else if (entry.isVideo()) { - playVideo(entry); - } else { - enableButtons(); - } - } - } - }); - entryList.setOnTouchListener(gestureListener); - - selectButton = (Button) findViewById(R.id.select_album_select); - playNowButton = (Button) findViewById(R.id.select_album_play_now); - playLastButton = (Button) findViewById(R.id.select_album_play_last); - pinButton = (Button) footer.findViewById(R.id.select_album_pin); - unpinButton = (Button) footer.findViewById(R.id.select_album_unpin); - deleteButton = (Button) footer.findViewById(R.id.select_album_delete); - moreButton = (Button) footer.findViewById(R.id.select_album_more); - emptyView = findViewById(R.id.select_album_empty); - - selectButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - selectAllOrNone(); - } - }); - playNowButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - download(false, false, true, false); - selectAll(false, false); - } - }); - playLastButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - download(true, false, false, false); - selectAll(false, false); - } - }); - pinButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - download(true, true, false, false); - selectAll(false, false); - } - }); - unpinButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - unpin(); - selectAll(false, false); - } - }); - deleteButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - delete(); - selectAll(false, false); - } - }); - - registerForContextMenu(entryList); - - enableButtons(); - - String id = getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_ID); - String name = getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_NAME); - String playlistId = getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID); - String playlistName = getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME); - String albumListType = getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE); - int getStarredTracks = getIntent().getIntExtra(Constants.INTENT_EXTRA_NAME_STARRED, 0); - int albumListSize = getIntent().getIntExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 0); - int albumListOffset = getIntent().getIntExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0); - - if (playlistId != null) { - getPlaylist(playlistId, playlistName); - } else if (albumListType != null) { - getAlbumList(albumListType, albumListSize, albumListOffset); - } else if (getStarredTracks != 0) { - getStarred(); - } else { - getMusicDirectory(id, name); - } - } - - @Override - public boolean onPrepareOptionsMenu(Menu menu) { - super.onPrepareOptionsMenu(menu); - playAllButton = menu.findItem(R.id.select_album_play_all); - playAllButton.setVisible(playAllButtonVisible); - - return true; - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.select_album, menu); - inflater.inflate(R.menu.select_common, menu); - super.onCreateOptionsMenu(menu); - - return true; - } - - private void playAll() { - boolean hasSubFolders = false; - for (int i = 0; i < entryList.getCount(); i++) { - MusicDirectory.Entry entry = (MusicDirectory.Entry) entryList.getItemAtPosition(i); - if (entry != null && entry.isDirectory()) { - hasSubFolders = true; - break; - } - } - - String id = getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_ID); - if (hasSubFolders && id != null) { - downloadRecursively(id, false, false, true); - } else { - selectAll(true, false); - download(false, false, true, false); - selectAll(false, false); - } - } - - private void refresh() { - finish(); - Intent intent = getIntent(); - intent.putExtra(Constants.INTENT_EXTRA_NAME_REFRESH, true); - Util.startActivityWithoutTransition(this, intent); - } - - @Override - public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) { - super.onCreateContextMenu(menu, view, menuInfo); - AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; - - MusicDirectory.Entry entry = (MusicDirectory.Entry) entryList.getItemAtPosition(info.position); - - if (entry.isDirectory()) { - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.select_album_context, menu); - } else { - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.select_song_context, menu); - } - } - - @Override - public boolean onContextItemSelected(MenuItem menuItem) { - AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo(); - MusicDirectory.Entry entry = (MusicDirectory.Entry) entryList.getItemAtPosition(info.position); - List songs = new ArrayList(10); - songs.add((MusicDirectory.Entry) entryList.getItemAtPosition(info.position)); - switch (menuItem.getItemId()) { - case R.id.album_menu_play_now: - downloadRecursively(entry.getId(), false, false, true); - break; - case R.id.album_menu_play_last: - downloadRecursively(entry.getId(), false, true, false); - break; - case R.id.album_menu_pin: - downloadRecursively(entry.getId(), true, true, false); - break; - case R.id.song_menu_play_now: - getDownloadService().download(songs, false, true, true); - break; - case R.id.song_menu_play_next: - getDownloadService().download(songs, false, false, true); - break; - case R.id.song_menu_play_last: - getDownloadService().download(songs, false, false, false); - break; - case R.id.select_album_play_all: - playAll(); - break; - default: - return super.onContextItemSelected(menuItem); - } - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - - case R.id.main_shuffle: - Intent intent1 = new Intent(this, DownloadActivity.class); - intent1.putExtra(Constants.INTENT_EXTRA_NAME_SHUFFLE, true); - Util.startActivityWithoutTransition(this, intent1); - return true; - - case R.id.menu_refresh: - refresh(); - return true; - - case R.id.select_album_play_all: - playAll(); - return true; - - case R.id.menu_exit: - Intent intent = new Intent(this, MainActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - intent.putExtra(Constants.INTENT_EXTRA_NAME_EXIT, true); - Util.startActivityWithoutTransition(this, intent); - return true; - - case R.id.menu_settings: - startActivity(new Intent(this, SettingsActivity.class)); - return true; - - case R.id.menu_help: - startActivity(new Intent(this, HelpActivity.class)); - return true; - } - - return false; - } - - private void getMusicDirectory(final String id, String name) { - setTitle(name); - - new LoadTask() { - @Override - protected MusicDirectory load(MusicService service) throws Exception { - boolean refresh = getIntent().getBooleanExtra(Constants.INTENT_EXTRA_NAME_REFRESH, false); - return service.getMusicDirectory(id, refresh, SelectAlbumActivity.this, this); - } - }.execute(); - } - - private void getStarred() { - setTitle(R.string.main_songs_starred); - - new LoadTask() { - @Override - protected MusicDirectory load(MusicService service) throws Exception { - return Util.getSongsFromSearchResult(service.getStarred(SelectAlbumActivity.this, this)); - } - }.execute(); - } - - private void getPlaylist(final String playlistId, String playlistName) { - setTitle(playlistName); - - new LoadTask() { - @Override - protected MusicDirectory load(MusicService service) throws Exception { - return service.getPlaylist(playlistId, SelectAlbumActivity.this, this); - } - }.execute(); - } - - private void getAlbumList(final String albumListType, final int size, final int offset) { - - if ("newest".equals(albumListType)) { - setTitle(R.string.main_albums_newest); - } else if ("random".equals(albumListType)) { - setTitle(R.string.main_albums_random); - } else if ("highest".equals(albumListType)) { - setTitle(R.string.main_albums_highest); - } else if ("recent".equals(albumListType)) { - setTitle(R.string.main_albums_recent); - } else if ("frequent".equals(albumListType)) { - setTitle(R.string.main_albums_frequent); - } else if ("starred".equals(albumListType)) { - setTitle(R.string.main_albums_starred); - } else if ("alphabeticalByName".equals(albumListType)) { - setTitle(R.string.main_albums_alphaByName); - } else if ("alphabeticalByArtist".equals(albumListType)) { - setTitle(R.string.main_albums_alphaByArtist); - } - - new LoadTask() { - @Override - protected MusicDirectory load(MusicService service) throws Exception { - return service.getAlbumList(albumListType, size, offset, SelectAlbumActivity.this, this); - } - - @Override - protected void done(Pair result) { - if (!result.getFirst().getChildren().isEmpty()) { - pinButton.setVisibility(View.GONE); - unpinButton.setVisibility(View.GONE); - deleteButton.setVisibility(View.GONE); - - // Hide more button when results are less than album list size - if (result.getFirst().getChildren().size() < getIntent().getIntExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 0)) { - moreButton.setVisibility(View.GONE); - } else { - entryList.addFooterView(footer); - moreButton.setVisibility(View.VISIBLE); - } - - moreButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - Intent intent = new Intent(SelectAlbumActivity.this, SelectAlbumActivity.class); - String type = getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE); - int size = getIntent().getIntExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 0); - int offset = getIntent().getIntExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0) + size; - - intent.putExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE, type); - intent.putExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, size); - intent.putExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, offset); - Util.startActivityWithoutTransition(SelectAlbumActivity.this, intent); - } - }); - } - super.done(result); - } - }.execute(); - } - - private void selectAllOrNone() { - boolean someUnselected = false; - int count = entryList.getCount(); - for (int i = 0; i < count; i++) { - if (!entryList.isItemChecked(i) && entryList.getItemAtPosition(i) instanceof MusicDirectory.Entry) { - someUnselected = true; - break; - } - } - selectAll(someUnselected, true); - } - - private void selectAll(boolean selected, boolean toast) { - int count = entryList.getCount(); - int selectedCount = 0; - for (int i = 0; i < count; i++) { - MusicDirectory.Entry entry = (MusicDirectory.Entry) entryList.getItemAtPosition(i); - if (entry != null && !entry.isDirectory() && !entry.isVideo()) { - entryList.setItemChecked(i, selected); - selectedCount++; - } - } - - // Display toast: N tracks selected / N tracks unselected - if (toast) { - int toastResId = selected ? R.string.select_album_n_selected - : R.string.select_album_n_unselected; - Util.toast(this, getString(toastResId, selectedCount)); - } - - enableButtons(); - } - - private void enableButtons() { - if (getDownloadService() == null) { - return; - } - - List selection = getSelectedSongs(); - boolean enabled = !selection.isEmpty(); - boolean unpinEnabled = false; - boolean deleteEnabled = false; - - for (MusicDirectory.Entry song : selection) { - DownloadFile downloadFile = getDownloadService().forSong(song); - if (downloadFile.isCompleteFileAvailable()) { - deleteEnabled = true; - } - if (downloadFile.isSaved()) { - unpinEnabled = true; - } - } - - playNowButton.setEnabled(enabled); - playLastButton.setEnabled(enabled); - pinButton.setEnabled(enabled && !Util.isOffline(this)); - unpinButton.setEnabled(unpinEnabled); - deleteButton.setEnabled(deleteEnabled); - } - - private List getSelectedSongs() { - List songs = new ArrayList(10); - int count = entryList.getCount(); - for (int i = 0; i < count; i++) { - if (entryList.isItemChecked(i)) { - songs.add((MusicDirectory.Entry) entryList.getItemAtPosition(i)); - } - } - return songs; - } - - private void download(final boolean append, final boolean save, final boolean autoplay, final boolean playNext) { - if (getDownloadService() == null) { - return; - } - - final List songs = getSelectedSongs(); - Runnable onValid = new Runnable() { - @Override - public void run() { - if (!append) { - getDownloadService().clear(); - } - - warnIfNetworkOrStorageUnavailable(); - getDownloadService().download(songs, save, autoplay, playNext); - String playlistName = getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME); - if (playlistName != null) { - getDownloadService().setSuggestedPlaylistName(playlistName); - } - if (autoplay) { - Util.startActivityWithoutTransition(SelectAlbumActivity.this, DownloadActivity.class); - } else if (save) { - Util.toast(SelectAlbumActivity.this, - getResources().getQuantityString(R.plurals.select_album_n_songs_downloading, songs.size(), songs.size())); - } else if (append) { - Util.toast(SelectAlbumActivity.this, - getResources().getQuantityString(R.plurals.select_album_n_songs_added, songs.size(), songs.size())); - } - } - }; - - checkLicenseAndTrialPeriod(onValid); - } - - private void delete() { - if (getDownloadService() != null) { - getDownloadService().delete(getSelectedSongs()); - } - } - - private void unpin() { - if (getDownloadService() != null) { - getDownloadService().unpin(getSelectedSongs()); - } - } - - private void playVideo(MusicDirectory.Entry entry) { - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setData(Uri.parse(MusicServiceFactory.getMusicService(this).getVideoUrl(this, entry.getId()))); - - startActivity(intent); - } - - private void checkLicenseAndTrialPeriod(Runnable onValid) { - if (licenseValid) { - onValid.run(); - return; - } - - int trialDaysLeft = Util.getRemainingTrialDays(this); - Log.i(TAG, trialDaysLeft + " trial days left."); - - if (trialDaysLeft == 0) { - showDonationDialog(trialDaysLeft, null); - } else if (trialDaysLeft < Constants.FREE_TRIAL_DAYS / 2) { - showDonationDialog(trialDaysLeft, onValid); - } else { - Util.toast(this, getResources().getString(R.string.select_album_not_licensed, trialDaysLeft)); - onValid.run(); - } - } - - private void showDonationDialog(int trialDaysLeft, final Runnable onValid) { - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setIcon(android.R.drawable.ic_dialog_info); - - if (trialDaysLeft == 0) { - builder.setTitle(R.string.select_album_donate_dialog_0_trial_days_left); - } else { - builder.setTitle(getResources().getQuantityString(R.plurals.select_album_donate_dialog_n_trial_days_left, - trialDaysLeft, trialDaysLeft)); - } - - builder.setMessage(R.string.select_album_donate_dialog_message); - - builder.setPositiveButton(R.string.select_album_donate_dialog_now, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(Constants.DONATION_URL))); - } - }); - - builder.setNegativeButton(R.string.select_album_donate_dialog_later, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - dialogInterface.dismiss(); - if (onValid != null) { - onValid.run(); - } - } - }); - - builder.create().show(); - } - - private abstract class LoadTask extends TabActivityBackgroundTask> { - - public LoadTask() { - super(SelectAlbumActivity.this); - } - - protected abstract MusicDirectory load(MusicService service) throws Exception; - - @Override - protected Pair doInBackground() throws Throwable { - MusicService musicService = MusicServiceFactory.getMusicService(SelectAlbumActivity.this); - MusicDirectory dir = load(musicService); - boolean valid = musicService.isLicenseValid(SelectAlbumActivity.this, this); - return new Pair(dir, valid); - } - - @Override - protected void done(Pair result) { - List entries = result.getFirst().getChildren(); - - int songCount = 0; - for (MusicDirectory.Entry entry : entries) { - if (!entry.isDirectory()) { - songCount++; - } - } - - if (songCount > 0) { - ActionBar actionBar = getActionBar(); - - if (actionBar != null) { - getImageLoader().setActionBarArtwork(selectButton, entries.get(0), actionBar); - } - - entryList.addFooterView(footer); - selectButton.setVisibility(View.VISIBLE); - playNowButton.setVisibility(View.VISIBLE); - playLastButton.setVisibility(View.VISIBLE); - } - - boolean isAlbumList = getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE); - playAllButtonVisible = !(isAlbumList || entries.isEmpty()); - - emptyView.setVisibility(entries.isEmpty() ? View.VISIBLE : View.GONE); - - if (playAllButton != null) { - playAllButton.setVisible(playAllButtonVisible); - } - - entryList.setAdapter(new EntryAdapter(SelectAlbumActivity.this, getImageLoader(), entries, true)); - licenseValid = result.getSecond(); - - boolean playAll = getIntent().getBooleanExtra(Constants.INTENT_EXTRA_NAME_AUTOPLAY, false); - if (playAll && songCount > 0) { - playAll(); - } - } - } -} +/* + This file is part of Subsonic. + + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see . + + Copyright 2009 (C) Sindre Mehus + */ +package net.sourceforge.subsonic.androidapp.activity; + +import android.app.ActionBar; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.util.Log; +import android.view.ContextMenu; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.widget.AdapterView; +import android.widget.Button; +import android.widget.ListView; +import net.sourceforge.subsonic.androidapp.R; +import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; +import net.sourceforge.subsonic.androidapp.service.DownloadFile; +import net.sourceforge.subsonic.androidapp.service.MusicService; +import net.sourceforge.subsonic.androidapp.service.MusicServiceFactory; +import net.sourceforge.subsonic.androidapp.util.Constants; +import net.sourceforge.subsonic.androidapp.util.EntryAdapter; +import net.sourceforge.subsonic.androidapp.util.Pair; +import net.sourceforge.subsonic.androidapp.util.TabActivityBackgroundTask; +import net.sourceforge.subsonic.androidapp.util.Util; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +public class SelectAlbumActivity extends SubsonicTabActivity { + + private static final String TAG = SelectAlbumActivity.class.getSimpleName(); + + private ListView entryList; + private View footer; + private View emptyView; + private Button selectButton; + private Button playNowButton; + private Button playLastButton; + private Button pinButton; + private Button unpinButton; + private Button deleteButton; + private Button moreButton; + private boolean licenseValid; + private boolean playAllButtonVisible; + private MenuItem playAllButton; + private Random random = new Random(); + + /** + * Called when the activity is first created. + */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.select_album); + + entryList = (ListView) findViewById(R.id.select_album_entries); + + footer = LayoutInflater.from(this).inflate(R.layout.select_album_footer, entryList, false); + entryList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); + entryList.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + if (position >= 0) { + MusicDirectory.Entry entry = (MusicDirectory.Entry) parent.getItemAtPosition(position); + if (entry.isDirectory()) { + Intent intent = new Intent(SelectAlbumActivity.this, SelectAlbumActivity.class); + intent.putExtra(Constants.INTENT_EXTRA_NAME_ID, entry.getId()); + intent.putExtra(Constants.INTENT_EXTRA_NAME_NAME, entry.getTitle()); + Util.startActivityWithoutTransition(SelectAlbumActivity.this, intent); + } else if (entry.isVideo()) { + playVideo(entry); + } else { + enableButtons(); + } + } + } + }); + entryList.setOnTouchListener(gestureListener); + + selectButton = (Button) findViewById(R.id.select_album_select); + playNowButton = (Button) findViewById(R.id.select_album_play_now); + playLastButton = (Button) findViewById(R.id.select_album_play_last); + pinButton = (Button) footer.findViewById(R.id.select_album_pin); + unpinButton = (Button) footer.findViewById(R.id.select_album_unpin); + deleteButton = (Button) footer.findViewById(R.id.select_album_delete); + moreButton = (Button) footer.findViewById(R.id.select_album_more); + emptyView = findViewById(R.id.select_album_empty); + + selectButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + selectAllOrNone(); + } + }); + playNowButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + download(false, false, true, false); + selectAll(false, false); + } + }); + playLastButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + download(true, false, false, false); + selectAll(false, false); + } + }); + pinButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + download(true, true, false, false); + selectAll(false, false); + } + }); + unpinButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + unpin(); + selectAll(false, false); + } + }); + deleteButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + delete(); + selectAll(false, false); + } + }); + + registerForContextMenu(entryList); + + enableButtons(); + + String id = getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_ID); + String name = getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_NAME); + String playlistId = getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID); + String playlistName = getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME); + String albumListType = getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE); + int getStarredTracks = getIntent().getIntExtra(Constants.INTENT_EXTRA_NAME_STARRED, 0); + int albumListSize = getIntent().getIntExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 0); + int albumListOffset = getIntent().getIntExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0); + + if (playlistId != null) { + getPlaylist(playlistId, playlistName); + } else if (albumListType != null) { + getAlbumList(albumListType, albumListSize, albumListOffset); + } else if (getStarredTracks != 0) { + getStarred(); + } else { + getMusicDirectory(id, name); + } + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + super.onPrepareOptionsMenu(menu); + playAllButton = menu.findItem(R.id.select_album_play_all); + playAllButton.setVisible(playAllButtonVisible); + + return true; + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.select_album, menu); + inflater.inflate(R.menu.select_common, menu); + super.onCreateOptionsMenu(menu); + + return true; + } + + private void playAll() { + boolean hasSubFolders = false; + for (int i = 0; i < entryList.getCount(); i++) { + MusicDirectory.Entry entry = (MusicDirectory.Entry) entryList.getItemAtPosition(i); + if (entry != null && entry.isDirectory()) { + hasSubFolders = true; + break; + } + } + + String id = getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_ID); + if (hasSubFolders && id != null) { + downloadRecursively(id, false, false, true); + } else { + selectAll(true, false); + download(false, false, true, false); + selectAll(false, false); + } + } + + private void refresh() { + finish(); + Intent intent = getIntent(); + intent.putExtra(Constants.INTENT_EXTRA_NAME_REFRESH, true); + Util.startActivityWithoutTransition(this, intent); + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) { + super.onCreateContextMenu(menu, view, menuInfo); + AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; + + MusicDirectory.Entry entry = (MusicDirectory.Entry) entryList.getItemAtPosition(info.position); + + if (entry.isDirectory()) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.select_album_context, menu); + } else { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.select_song_context, menu); + } + } + + @Override + public boolean onContextItemSelected(MenuItem menuItem) { + AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo(); + MusicDirectory.Entry entry = (MusicDirectory.Entry) entryList.getItemAtPosition(info.position); + List songs = new ArrayList(10); + songs.add((MusicDirectory.Entry) entryList.getItemAtPosition(info.position)); + switch (menuItem.getItemId()) { + case R.id.album_menu_play_now: + downloadRecursively(entry.getId(), false, false, true); + break; + case R.id.album_menu_play_last: + downloadRecursively(entry.getId(), false, true, false); + break; + case R.id.album_menu_pin: + downloadRecursively(entry.getId(), true, true, false); + break; + case R.id.song_menu_play_now: + getDownloadService().download(songs, false, true, true); + break; + case R.id.song_menu_play_next: + getDownloadService().download(songs, false, false, true); + break; + case R.id.song_menu_play_last: + getDownloadService().download(songs, false, false, false); + break; + case R.id.select_album_play_all: + playAll(); + break; + default: + return super.onContextItemSelected(menuItem); + } + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + + case R.id.main_shuffle: + Intent intent1 = new Intent(this, DownloadActivity.class); + intent1.putExtra(Constants.INTENT_EXTRA_NAME_SHUFFLE, true); + Util.startActivityWithoutTransition(this, intent1); + return true; + + case R.id.menu_refresh: + refresh(); + return true; + + case R.id.select_album_play_all: + playAll(); + return true; + + case R.id.menu_exit: + Intent intent = new Intent(this, MainActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + intent.putExtra(Constants.INTENT_EXTRA_NAME_EXIT, true); + Util.startActivityWithoutTransition(this, intent); + return true; + + case R.id.menu_settings: + startActivity(new Intent(this, SettingsActivity.class)); + return true; + + case R.id.menu_help: + startActivity(new Intent(this, HelpActivity.class)); + return true; + } + + return false; + } + + private void getMusicDirectory(final String id, String name) { + setTitle(name); + + new LoadTask() { + @Override + protected MusicDirectory load(MusicService service) throws Exception { + boolean refresh = getIntent().getBooleanExtra(Constants.INTENT_EXTRA_NAME_REFRESH, false); + return service.getMusicDirectory(id, refresh, SelectAlbumActivity.this, this); + } + }.execute(); + } + + private void getStarred() { + setTitle(R.string.main_songs_starred); + + new LoadTask() { + @Override + protected MusicDirectory load(MusicService service) throws Exception { + return Util.getSongsFromSearchResult(service.getStarred(SelectAlbumActivity.this, this)); + } + }.execute(); + } + + private void getPlaylist(final String playlistId, String playlistName) { + setTitle(playlistName); + + new LoadTask() { + @Override + protected MusicDirectory load(MusicService service) throws Exception { + return service.getPlaylist(playlistId, SelectAlbumActivity.this, this); + } + }.execute(); + } + + private void getAlbumList(final String albumListType, final int size, final int offset) { + + if ("newest".equals(albumListType)) { + setTitle(R.string.main_albums_newest); + } else if ("random".equals(albumListType)) { + setTitle(R.string.main_albums_random); + } else if ("highest".equals(albumListType)) { + setTitle(R.string.main_albums_highest); + } else if ("recent".equals(albumListType)) { + setTitle(R.string.main_albums_recent); + } else if ("frequent".equals(albumListType)) { + setTitle(R.string.main_albums_frequent); + } else if ("starred".equals(albumListType)) { + setTitle(R.string.main_albums_starred); + } else if ("alphabeticalByName".equals(albumListType)) { + setTitle(R.string.main_albums_alphaByName); + } else if ("alphabeticalByArtist".equals(albumListType)) { + setTitle(R.string.main_albums_alphaByArtist); + } + + new LoadTask() { + @Override + protected MusicDirectory load(MusicService service) throws Exception { + return service.getAlbumList(albumListType, size, offset, SelectAlbumActivity.this, this); + } + + @Override + protected void done(Pair result) { + if (!result.getFirst().getChildren().isEmpty()) { + pinButton.setVisibility(View.GONE); + unpinButton.setVisibility(View.GONE); + deleteButton.setVisibility(View.GONE); + + // Hide more button when results are less than album list size + if (result.getFirst().getChildren().size() < getIntent().getIntExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 0)) { + moreButton.setVisibility(View.GONE); + } else { + entryList.addFooterView(footer); + moreButton.setVisibility(View.VISIBLE); + } + + moreButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Intent intent = new Intent(SelectAlbumActivity.this, SelectAlbumActivity.class); + String type = getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE); + int size = getIntent().getIntExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 0); + int offset = getIntent().getIntExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0) + size; + + intent.putExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE, type); + intent.putExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, size); + intent.putExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, offset); + Util.startActivityWithoutTransition(SelectAlbumActivity.this, intent); + } + }); + } + super.done(result); + } + }.execute(); + } + + private void selectAllOrNone() { + boolean someUnselected = false; + int count = entryList.getCount(); + for (int i = 0; i < count; i++) { + if (!entryList.isItemChecked(i) && entryList.getItemAtPosition(i) instanceof MusicDirectory.Entry) { + someUnselected = true; + break; + } + } + selectAll(someUnselected, true); + } + + private void selectAll(boolean selected, boolean toast) { + int count = entryList.getCount(); + int selectedCount = 0; + for (int i = 0; i < count; i++) { + MusicDirectory.Entry entry = (MusicDirectory.Entry) entryList.getItemAtPosition(i); + if (entry != null && !entry.isDirectory() && !entry.isVideo()) { + entryList.setItemChecked(i, selected); + selectedCount++; + } + } + + // Display toast: N tracks selected / N tracks unselected + if (toast) { + int toastResId = selected ? R.string.select_album_n_selected + : R.string.select_album_n_unselected; + Util.toast(this, getString(toastResId, selectedCount)); + } + + enableButtons(); + } + + private void enableButtons() { + if (getDownloadService() == null) { + return; + } + + List selection = getSelectedSongs(); + boolean enabled = !selection.isEmpty(); + boolean unpinEnabled = false; + boolean deleteEnabled = false; + + for (MusicDirectory.Entry song : selection) { + DownloadFile downloadFile = getDownloadService().forSong(song); + if (downloadFile.isCompleteFileAvailable()) { + deleteEnabled = true; + } + if (downloadFile.isSaved()) { + unpinEnabled = true; + } + } + + playNowButton.setEnabled(enabled); + playLastButton.setEnabled(enabled); + pinButton.setEnabled(enabled && !Util.isOffline(this)); + unpinButton.setEnabled(unpinEnabled); + deleteButton.setEnabled(deleteEnabled); + } + + private List getSelectedSongs() { + List songs = new ArrayList(10); + int count = entryList.getCount(); + for (int i = 0; i < count; i++) { + if (entryList.isItemChecked(i)) { + songs.add((MusicDirectory.Entry) entryList.getItemAtPosition(i)); + } + } + return songs; + } + + private void download(final boolean append, final boolean save, final boolean autoplay, final boolean playNext) { + if (getDownloadService() == null) { + return; + } + + final List songs = getSelectedSongs(); + Runnable onValid = new Runnable() { + @Override + public void run() { + if (!append) { + getDownloadService().clear(); + } + + warnIfNetworkOrStorageUnavailable(); + getDownloadService().download(songs, save, autoplay, playNext); + String playlistName = getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME); + if (playlistName != null) { + getDownloadService().setSuggestedPlaylistName(playlistName); + } + if (autoplay) { + Util.startActivityWithoutTransition(SelectAlbumActivity.this, DownloadActivity.class); + } else if (save) { + Util.toast(SelectAlbumActivity.this, + getResources().getQuantityString(R.plurals.select_album_n_songs_downloading, songs.size(), songs.size())); + } else if (append) { + Util.toast(SelectAlbumActivity.this, + getResources().getQuantityString(R.plurals.select_album_n_songs_added, songs.size(), songs.size())); + } + } + }; + + checkLicenseAndTrialPeriod(onValid); + } + + private void delete() { + if (getDownloadService() != null) { + getDownloadService().delete(getSelectedSongs()); + } + } + + private void unpin() { + if (getDownloadService() != null) { + getDownloadService().unpin(getSelectedSongs()); + } + } + + private void playVideo(MusicDirectory.Entry entry) { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse(MusicServiceFactory.getMusicService(this).getVideoUrl(this, entry.getId()))); + + startActivity(intent); + } + + private void checkLicenseAndTrialPeriod(Runnable onValid) { + if (licenseValid) { + onValid.run(); + return; + } + + int trialDaysLeft = Util.getRemainingTrialDays(this); + Log.i(TAG, trialDaysLeft + " trial days left."); + + if (trialDaysLeft == 0) { + showDonationDialog(trialDaysLeft, null); + } else if (trialDaysLeft < Constants.FREE_TRIAL_DAYS / 2) { + showDonationDialog(trialDaysLeft, onValid); + } else { + Util.toast(this, getResources().getString(R.string.select_album_not_licensed, trialDaysLeft)); + onValid.run(); + } + } + + private void showDonationDialog(int trialDaysLeft, final Runnable onValid) { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setIcon(android.R.drawable.ic_dialog_info); + + if (trialDaysLeft == 0) { + builder.setTitle(R.string.select_album_donate_dialog_0_trial_days_left); + } else { + builder.setTitle(getResources().getQuantityString(R.plurals.select_album_donate_dialog_n_trial_days_left, + trialDaysLeft, trialDaysLeft)); + } + + builder.setMessage(R.string.select_album_donate_dialog_message); + + builder.setPositiveButton(R.string.select_album_donate_dialog_now, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(Constants.DONATION_URL))); + } + }); + + builder.setNegativeButton(R.string.select_album_donate_dialog_later, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + dialogInterface.dismiss(); + if (onValid != null) { + onValid.run(); + } + } + }); + + builder.create().show(); + } + + private abstract class LoadTask extends TabActivityBackgroundTask> { + + public LoadTask() { + super(SelectAlbumActivity.this); + } + + protected abstract MusicDirectory load(MusicService service) throws Exception; + + @Override + protected Pair doInBackground() throws Throwable { + MusicService musicService = MusicServiceFactory.getMusicService(SelectAlbumActivity.this); + MusicDirectory dir = load(musicService); + boolean valid = musicService.isLicenseValid(SelectAlbumActivity.this, this); + return new Pair(dir, valid); + } + + @Override + protected void done(Pair result) { + List entries = result.getFirst().getChildren(); + + int songCount = 0; + for (MusicDirectory.Entry entry : entries) { + if (!entry.isDirectory()) { + songCount++; + } + } + + if (songCount > 0) { + ActionBar actionBar = getActionBar(); + + if (actionBar != null) { + // Use random entry selection for artwork in list of tracks + int artworkSelection = random.nextInt(entries.size()); + getImageLoader().setActionBarArtwork(selectButton, entries.get(artworkSelection), actionBar); + } + + entryList.addFooterView(footer); + selectButton.setVisibility(View.VISIBLE); + playNowButton.setVisibility(View.VISIBLE); + playLastButton.setVisibility(View.VISIBLE); + } + + boolean isAlbumList = getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE); + playAllButtonVisible = !(isAlbumList || entries.isEmpty()); + + emptyView.setVisibility(entries.isEmpty() ? View.VISIBLE : View.GONE); + + if (playAllButton != null) { + playAllButton.setVisible(playAllButtonVisible); + } + + entryList.setAdapter(new EntryAdapter(SelectAlbumActivity.this, getImageLoader(), entries, true)); + licenseValid = result.getSecond(); + + boolean playAll = getIntent().getBooleanExtra(Constants.INTENT_EXTRA_NAME_AUTOPLAY, false); + if (playAll && songCount > 0) { + playAll(); + } + } + } +} diff --git a/src/net/sourceforge/subsonic/androidapp/activity/SubsonicTabActivity.java b/src/net/sourceforge/subsonic/androidapp/activity/SubsonicTabActivity.java index b5c86d91..b778e89c 100644 --- a/src/net/sourceforge/subsonic/androidapp/activity/SubsonicTabActivity.java +++ b/src/net/sourceforge/subsonic/androidapp/activity/SubsonicTabActivity.java @@ -1,551 +1,551 @@ -/* - This file is part of Subsonic. - - Subsonic is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Subsonic is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Subsonic. If not, see . - - Copyright 2009 (C) Sindre Mehus - */ -package net.sourceforge.subsonic.androidapp.activity; - -import java.io.File; -import java.io.PrintWriter; -import java.util.LinkedList; -import java.util.List; - -import android.app.Activity; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageInfo; -import android.media.AudioManager; -import android.os.Build; -import android.os.Bundle; -import android.os.Environment; -import android.util.Log; -import android.view.GestureDetector; -import android.view.GestureDetector.SimpleOnGestureListener; -import android.view.KeyEvent; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.MotionEvent; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.Window; -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.MusicService; -import net.sourceforge.subsonic.androidapp.service.MusicServiceFactory; -import net.sourceforge.subsonic.androidapp.util.Constants; -import net.sourceforge.subsonic.androidapp.util.ImageLoader; -import net.sourceforge.subsonic.androidapp.util.ModalBackgroundTask; -import net.sourceforge.subsonic.androidapp.util.Util; - -/** - * @author Sindre Mehus - */ -public class SubsonicTabActivity extends Activity implements OnClickListener{ - private static final String TAG = SubsonicTabActivity.class.getSimpleName(); - private static ImageLoader IMAGE_LOADER; - private static final int SWIPE_MIN_DISTANCE = 120; - private static final int SWIPE_MAX_OFF_PATH = 250; - private static final int SWIPE_THRESHOLD_VELOCITY = 200; - - private boolean destroyed; - private View homeButton; - private View musicButton; - private View searchButton; - private View playlistButton; - private View nowPlayingButton; - - //private boolean shortPress = false; - - private GestureDetector gestureDetector; - View.OnTouchListener gestureListener; - - enum SwipeDirection - { - Left, - Right - }; - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.common, menu); - - return true; - } - - @Override - protected void onCreate(Bundle bundle) { - setUncaughtExceptionHandler(); - applyTheme(); - super.onCreate(bundle); - - startService(new Intent(this, DownloadServiceImpl.class)); - setVolumeControlStream(AudioManager.STREAM_MUSIC); - - gestureDetector = new GestureDetector(new GestureActivity()); - gestureListener = new View.OnTouchListener() { - @Override - public boolean onTouch(View v, MotionEvent event) { - return gestureDetector.onTouchEvent(event); - } - }; - } - - @Override - protected void onPostCreate(Bundle bundle) { - super.onPostCreate(bundle); - - homeButton = findViewById(R.id.button_bar_home); - homeButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - Intent intent = new Intent(SubsonicTabActivity.this, MainActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - Util.startActivityWithoutTransition(SubsonicTabActivity.this, intent); - } - }); - - musicButton = findViewById(R.id.button_bar_music); - musicButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - Intent intent = new Intent(SubsonicTabActivity.this, SelectArtistActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - Util.startActivityWithoutTransition(SubsonicTabActivity.this, intent); - } - }); - - searchButton = findViewById(R.id.button_bar_search); - searchButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - Intent intent = new Intent(SubsonicTabActivity.this, SearchActivity.class); - intent.putExtra(Constants.INTENT_EXTRA_REQUEST_SEARCH, true); - Util.startActivityWithoutTransition(SubsonicTabActivity.this, intent); - } - }); - - playlistButton = findViewById(R.id.button_bar_playlists); - playlistButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - Intent intent = new Intent(SubsonicTabActivity.this, SelectPlaylistActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - Util.startActivityWithoutTransition(SubsonicTabActivity.this, intent); - } - }); - - nowPlayingButton = findViewById(R.id.button_bar_now_playing); - nowPlayingButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - Util.startActivityWithoutTransition(SubsonicTabActivity.this, DownloadActivity.class); - } - }); - - if (this instanceof MainActivity) { - homeButton.setEnabled(false); - } else if (this instanceof SelectAlbumActivity || this instanceof SelectArtistActivity) { - musicButton.setEnabled(false); - } else if (this instanceof SearchActivity) { - searchButton.setEnabled(false); - } else if (this instanceof SelectPlaylistActivity) { - playlistButton.setEnabled(false); - } else if (this instanceof DownloadActivity || this instanceof LyricsActivity) { - nowPlayingButton.setEnabled(false); - } - - updateButtonVisibility(); - } - - @Override - protected void onResume() { - super.onResume(); - Util.registerMediaButtonEventReceiver(this); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - - case R.id.menu_exit: - Intent intent = new Intent(this, MainActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - intent.putExtra(Constants.INTENT_EXTRA_NAME_EXIT, true); - Util.startActivityWithoutTransition(this, intent); - return true; - - case R.id.menu_settings: - startActivity(new Intent(this, SettingsActivity.class)); - return true; - - case R.id.menu_help: - startActivity(new Intent(this, HelpActivity.class)); - return true; - } - - return false; - } - - @Override - protected void onDestroy() { - Util.unregisterMediaButtonEventReceiver(this); - super.onDestroy(); - destroyed = true; - getImageLoader().clear(); - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - boolean isVolumeDown = keyCode == KeyEvent.KEYCODE_VOLUME_DOWN; - boolean isVolumeUp = keyCode == KeyEvent.KEYCODE_VOLUME_UP; - boolean isVolumeAdjust = isVolumeDown || isVolumeUp; - boolean isJukebox = getDownloadService() != null && getDownloadService().isJukeboxEnabled(); - - if (isVolumeAdjust && isJukebox) { - getDownloadService().adjustJukeboxVolume(isVolumeUp); - return true; - } - -// if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP) { -// if(event.getAction() == KeyEvent.ACTION_DOWN){ -// event.startTracking(); -// -// if(event.getRepeatCount() == 0){ -// shortPress = true; -// } -// -// return true; -// } -// } - - return super.onKeyDown(keyCode, event); - } - - @Override - public void finish() { - super.finish(); - Util.disablePendingTransition(this); - } - - private void applyTheme() { - String theme = Util.getTheme(this); - if ("dark".equalsIgnoreCase(theme)) { - setTheme(R.style.Dark); - } else if ("light".equalsIgnoreCase(theme)) { - setTheme(R.style.Light); - } else if ("fullscreen".equalsIgnoreCase(theme)) { - setTheme(R.style.Fullscreen); - } else if ("fullscreenlight".equalsIgnoreCase(theme)) { - setTheme(R.style.Fullscreenlight); - } - } - - public boolean isDestroyed() { - return destroyed; - } - - private void updateButtonVisibility() { - int visibility = Util.isOffline(this) ? View.GONE : View.VISIBLE; - searchButton.setVisibility(visibility); - playlistButton.setVisibility(visibility); - } - - public void setProgressVisible(boolean visible) { - View view = findViewById(R.id.tab_progress); - if (view != null) { - view.setVisibility(visible ? View.VISIBLE : View.GONE); - } - } - - public void updateProgress(String message) { - TextView view = (TextView) findViewById(R.id.tab_progress_message); - if (view != null) { - view.setText(message); - } - } - - public DownloadService getDownloadService() { - // If service is not available, request it to start and wait for it. - for (int i = 0; i < 5; i++) { - DownloadService downloadService = DownloadServiceImpl.getInstance(); - if (downloadService != null) { - return downloadService; - } - Log.w(TAG, "DownloadService not running. Attempting to start it."); - startService(new Intent(this, DownloadServiceImpl.class)); - Util.sleepQuietly(50L); - } - return DownloadServiceImpl.getInstance(); - } - - protected void warnIfNetworkOrStorageUnavailable() { - if (!Util.isExternalStoragePresent()) { - Util.toast(this, R.string.select_album_no_sdcard); - } else if (!Util.isOffline(this) && !Util.isNetworkConnected(this)) { - Util.toast(this, R.string.select_album_no_network); - } - } - - protected synchronized ImageLoader getImageLoader() { - if (IMAGE_LOADER == null) { - IMAGE_LOADER = new ImageLoader(this); - } - return IMAGE_LOADER; - } - - protected void downloadRecursively(final String id, final boolean save, final boolean append, final boolean autoplay) { - ModalBackgroundTask> task = new ModalBackgroundTask>(this, false) { - - private static final int MAX_SONGS = 500; - - @Override - protected List doInBackground() throws Throwable { - MusicService musicService = MusicServiceFactory.getMusicService(SubsonicTabActivity.this); - MusicDirectory root = musicService.getMusicDirectory(id, false, SubsonicTabActivity.this, this); - List songs = new LinkedList(); - getSongsRecursively(root, songs); - return songs; - } - - private void getSongsRecursively(MusicDirectory parent, List songs) throws Exception { - if (songs.size() > MAX_SONGS) { - return; - } - - for (MusicDirectory.Entry song : parent.getChildren(false, true)) { - if (!song.isVideo()) { - songs.add(song); - } - } - for (MusicDirectory.Entry dir : parent.getChildren(true, false)) { - MusicService musicService = MusicServiceFactory.getMusicService(SubsonicTabActivity.this); - getSongsRecursively(musicService.getMusicDirectory(dir.getId(), false, SubsonicTabActivity.this, this), songs); - } - } - - @Override - protected void done(List songs) { - DownloadService downloadService = getDownloadService(); - if (!songs.isEmpty() && downloadService != null) { - if (!append) { - downloadService.clear(); - } - warnIfNetworkOrStorageUnavailable(); - downloadService.download(songs, save, autoplay, false); - Util.startActivityWithoutTransition(SubsonicTabActivity.this, DownloadActivity.class); - } - } - }; - - task.execute(); - } - - private void setUncaughtExceptionHandler() { - Thread.UncaughtExceptionHandler handler = Thread.getDefaultUncaughtExceptionHandler(); - if (!(handler instanceof SubsonicUncaughtExceptionHandler)) { - Thread.setDefaultUncaughtExceptionHandler(new SubsonicUncaughtExceptionHandler(this)); - } - } - - /** - * Logs the stack trace of uncaught exceptions to a file on the SD card. - */ - private static class SubsonicUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler { - - private final Thread.UncaughtExceptionHandler defaultHandler; - private final Context context; - - private SubsonicUncaughtExceptionHandler(Context context) { - this.context = context; - defaultHandler = Thread.getDefaultUncaughtExceptionHandler(); - } - - @Override - public void uncaughtException(Thread thread, Throwable throwable) { - File file = null; - PrintWriter printWriter = null; - try { - - PackageInfo packageInfo = context.getPackageManager().getPackageInfo("net.sourceforge.subsonic.androidapp", 0); - file = new File(Environment.getExternalStorageDirectory(), "subsonic-stacktrace.txt"); - printWriter = new PrintWriter(file); - printWriter.println("Android API level: " + Build.VERSION.SDK_INT); - printWriter.println("Subsonic version name: " + packageInfo.versionName); - printWriter.println("Subsonic version code: " + packageInfo.versionCode); - printWriter.println(); - throwable.printStackTrace(printWriter); - Log.i(TAG, "Stack trace written to " + file); - } catch (Throwable x) { - Log.e(TAG, "Failed to write stack trace to " + file, x); - } finally { - Util.close(printWriter); - if (defaultHandler != null) { - defaultHandler.uncaughtException(thread, throwable); - } - - } - } - } - - @Override - public void onClick(View arg0) { - // TODO Auto-generated method stub - - } - - class GestureActivity extends SimpleOnGestureListener { - @Override - public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - try { - if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH) - return false; - - SwipeDirection swipe = null; - - // right to left swipe - if(e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) { - swipe = SwipeDirection.Left; - } else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) { - swipe = SwipeDirection.Right; - } - - String name = SubsonicTabActivity.this.getClass().getSimpleName(); - - switch (swipe) - { - case Right: - if (name.equalsIgnoreCase("MainActivity")) - { - Intent intent = new Intent(SubsonicTabActivity.this, DownloadActivity.class); - SubsonicTabActivity.this.startActivity(intent); - } - else if (name.equalsIgnoreCase("SelectArtistActivity") || name.equalsIgnoreCase("SelectAlbumActivity")) - { - Intent intent = new Intent(SubsonicTabActivity.this, MainActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - SubsonicTabActivity.this.startActivity(intent); - } - else if (name.equalsIgnoreCase("SearchActivity")) - { - Intent intent = new Intent(SubsonicTabActivity.this, SelectArtistActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - SubsonicTabActivity.this.startActivity(intent); - } - else if (name.equalsIgnoreCase("SelectPlaylistActivity")) - { - Intent intent = new Intent(SubsonicTabActivity.this, SearchActivity.class); - intent.putExtra(Constants.INTENT_EXTRA_REQUEST_SEARCH, true); - SubsonicTabActivity.this.startActivity(intent); - } - else if (name.equalsIgnoreCase("DownloadActivity") || name.equalsIgnoreCase("LyricsActivity")) - { - Intent intent = new Intent(SubsonicTabActivity.this, SelectPlaylistActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - SubsonicTabActivity.this.startActivity(intent); - } - - break; - case Left: - if (name.equalsIgnoreCase("MainActivity")) - { - Intent intent = new Intent(SubsonicTabActivity.this, SelectArtistActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - SubsonicTabActivity.this.startActivity(intent); - } - if (name.equalsIgnoreCase("SelectArtistActivity") || name.equalsIgnoreCase("SelectAlbumActivity")) - { - Intent intent = new Intent(SubsonicTabActivity.this, SearchActivity.class); - intent.putExtra(Constants.INTENT_EXTRA_REQUEST_SEARCH, true); - SubsonicTabActivity.this.startActivity(intent); - } - else if (name.equalsIgnoreCase("SearchActivity")) - { - Intent intent = new Intent(SubsonicTabActivity.this, SelectPlaylistActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - SubsonicTabActivity.this.startActivity(intent); - } - else if (name.equalsIgnoreCase("SelectPlaylistActivity")) - { - Intent intent = new Intent(SubsonicTabActivity.this, DownloadActivity.class); - SubsonicTabActivity.this.startActivity(intent); - } - else if (name.equalsIgnoreCase("DownloadActivity") || name.equalsIgnoreCase("LyricsActivity")) - { - Intent intent = new Intent(SubsonicTabActivity.this, MainActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - SubsonicTabActivity.this.startActivity(intent); - } - - break; - } - } catch (Exception e) { - // nothing - } - - return false; - } - } - -// @Override -// public boolean onKeyLongPress(int keyCode, KeyEvent event) { -// DownloadService service = getDownloadService(); -// int current = service.getCurrentPlayingIndex(); -// -// switch(keyCode){ -// case KeyEvent.KEYCODE_VOLUME_UP: -// shortPress = false; -// -// if (current == -1) { -// service.play(0); -// } else { -// current++; -// service.play(current); -// } -// return true; -// case KeyEvent.KEYCODE_VOLUME_DOWN: -// shortPress = false; -// -// if (current == -1 || current == 0) { -// service.play(0); -// } else { -// current--; -// service.play(current); -// } -// return true; -// } -// -// return super.onKeyLongPress(keyCode, event); -// } -// -// @Override -// public boolean onKeyUp(int keyCode, KeyEvent event) { -// if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) { -// shortPress = false; -// return true; -// } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP){ -// shortPress = false; -// return true; -// } -// -// return super.onKeyUp(keyCode, event); -// } -} - +/* + This file is part of Subsonic. + + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see . + + Copyright 2009 (C) Sindre Mehus + */ +package net.sourceforge.subsonic.androidapp.activity; + +import java.io.File; +import java.io.PrintWriter; +import java.util.LinkedList; +import java.util.List; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.media.AudioManager; +import android.os.Build; +import android.os.Bundle; +import android.os.Environment; +import android.util.Log; +import android.view.GestureDetector; +import android.view.GestureDetector.SimpleOnGestureListener; +import android.view.KeyEvent; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.Window; +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.MusicService; +import net.sourceforge.subsonic.androidapp.service.MusicServiceFactory; +import net.sourceforge.subsonic.androidapp.util.Constants; +import net.sourceforge.subsonic.androidapp.util.ImageLoader; +import net.sourceforge.subsonic.androidapp.util.ModalBackgroundTask; +import net.sourceforge.subsonic.androidapp.util.Util; + +/** + * @author Sindre Mehus + */ +public class SubsonicTabActivity extends Activity implements OnClickListener{ + private static final String TAG = SubsonicTabActivity.class.getSimpleName(); + private static ImageLoader IMAGE_LOADER; + private static final int SWIPE_MIN_DISTANCE = 120; + private static final int SWIPE_MAX_OFF_PATH = 250; + private static final int SWIPE_THRESHOLD_VELOCITY = 200; + + private boolean destroyed; + private View homeButton; + private View musicButton; + private View searchButton; + private View playlistButton; + private View nowPlayingButton; + + //private boolean shortPress = false; + + private GestureDetector gestureDetector; + View.OnTouchListener gestureListener; + + enum SwipeDirection + { + Left, + Right + }; + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.common, menu); + + return true; + } + + @Override + protected void onCreate(Bundle bundle) { + setUncaughtExceptionHandler(); + applyTheme(); + super.onCreate(bundle); + + startService(new Intent(this, DownloadServiceImpl.class)); + setVolumeControlStream(AudioManager.STREAM_MUSIC); + + gestureDetector = new GestureDetector(new GestureActivity()); + gestureListener = new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + return gestureDetector.onTouchEvent(event); + } + }; + } + + @Override + protected void onPostCreate(Bundle bundle) { + super.onPostCreate(bundle); + + homeButton = findViewById(R.id.button_bar_home); + homeButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Intent intent = new Intent(SubsonicTabActivity.this, MainActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + Util.startActivityWithoutTransition(SubsonicTabActivity.this, intent); + } + }); + + musicButton = findViewById(R.id.button_bar_music); + musicButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Intent intent = new Intent(SubsonicTabActivity.this, SelectArtistActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + Util.startActivityWithoutTransition(SubsonicTabActivity.this, intent); + } + }); + + searchButton = findViewById(R.id.button_bar_search); + searchButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Intent intent = new Intent(SubsonicTabActivity.this, SearchActivity.class); + intent.putExtra(Constants.INTENT_EXTRA_REQUEST_SEARCH, true); + Util.startActivityWithoutTransition(SubsonicTabActivity.this, intent); + } + }); + + playlistButton = findViewById(R.id.button_bar_playlists); + playlistButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Intent intent = new Intent(SubsonicTabActivity.this, SelectPlaylistActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + Util.startActivityWithoutTransition(SubsonicTabActivity.this, intent); + } + }); + + nowPlayingButton = findViewById(R.id.button_bar_now_playing); + nowPlayingButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Util.startActivityWithoutTransition(SubsonicTabActivity.this, DownloadActivity.class); + } + }); + + if (this instanceof MainActivity) { + homeButton.setEnabled(false); + } else if (this instanceof SelectAlbumActivity || this instanceof SelectArtistActivity) { + musicButton.setEnabled(false); + } else if (this instanceof SearchActivity) { + searchButton.setEnabled(false); + } else if (this instanceof SelectPlaylistActivity) { + playlistButton.setEnabled(false); + } else if (this instanceof DownloadActivity || this instanceof LyricsActivity) { + nowPlayingButton.setEnabled(false); + } + + updateButtonVisibility(); + } + + @Override + protected void onResume() { + super.onResume(); + Util.registerMediaButtonEventReceiver(this); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + + case R.id.menu_exit: + Intent intent = new Intent(this, MainActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + intent.putExtra(Constants.INTENT_EXTRA_NAME_EXIT, true); + Util.startActivityWithoutTransition(this, intent); + return true; + + case R.id.menu_settings: + startActivity(new Intent(this, SettingsActivity.class)); + return true; + + case R.id.menu_help: + startActivity(new Intent(this, HelpActivity.class)); + return true; + } + + return false; + } + + @Override + protected void onDestroy() { + Util.unregisterMediaButtonEventReceiver(this); + super.onDestroy(); + destroyed = true; + getImageLoader().clear(); + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + boolean isVolumeDown = keyCode == KeyEvent.KEYCODE_VOLUME_DOWN; + boolean isVolumeUp = keyCode == KeyEvent.KEYCODE_VOLUME_UP; + boolean isVolumeAdjust = isVolumeDown || isVolumeUp; + boolean isJukebox = getDownloadService() != null && getDownloadService().isJukeboxEnabled(); + + if (isVolumeAdjust && isJukebox) { + getDownloadService().adjustJukeboxVolume(isVolumeUp); + return true; + } + +// if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP) { +// if(event.getAction() == KeyEvent.ACTION_DOWN){ +// event.startTracking(); +// +// if(event.getRepeatCount() == 0){ +// shortPress = true; +// } +// +// return true; +// } +// } + + return super.onKeyDown(keyCode, event); + } + + @Override + public void finish() { + super.finish(); + Util.disablePendingTransition(this); + } + + private void applyTheme() { + String theme = Util.getTheme(this); + if ("dark".equalsIgnoreCase(theme)) { + setTheme(R.style.Dark); + } else if ("light".equalsIgnoreCase(theme)) { + setTheme(R.style.Light); + } else if ("fullscreen".equalsIgnoreCase(theme)) { + setTheme(R.style.Fullscreen); + } else if ("fullscreenlight".equalsIgnoreCase(theme)) { + setTheme(R.style.Fullscreenlight); + } + } + + public boolean getIsDestroyed() { + return destroyed; + } + + private void updateButtonVisibility() { + int visibility = Util.isOffline(this) ? View.GONE : View.VISIBLE; + searchButton.setVisibility(visibility); + playlistButton.setVisibility(visibility); + } + + public void setProgressVisible(boolean visible) { + View view = findViewById(R.id.tab_progress); + if (view != null) { + view.setVisibility(visible ? View.VISIBLE : View.GONE); + } + } + + public void updateProgress(String message) { + TextView view = (TextView) findViewById(R.id.tab_progress_message); + if (view != null) { + view.setText(message); + } + } + + public DownloadService getDownloadService() { + // If service is not available, request it to start and wait for it. + for (int i = 0; i < 5; i++) { + DownloadService downloadService = DownloadServiceImpl.getInstance(); + if (downloadService != null) { + return downloadService; + } + Log.w(TAG, "DownloadService not running. Attempting to start it."); + startService(new Intent(this, DownloadServiceImpl.class)); + Util.sleepQuietly(50L); + } + return DownloadServiceImpl.getInstance(); + } + + protected void warnIfNetworkOrStorageUnavailable() { + if (!Util.isExternalStoragePresent()) { + Util.toast(this, R.string.select_album_no_sdcard); + } else if (!Util.isOffline(this) && !Util.isNetworkConnected(this)) { + Util.toast(this, R.string.select_album_no_network); + } + } + + protected synchronized ImageLoader getImageLoader() { + if (IMAGE_LOADER == null) { + IMAGE_LOADER = new ImageLoader(this); + } + return IMAGE_LOADER; + } + + protected void downloadRecursively(final String id, final boolean save, final boolean append, final boolean autoplay) { + ModalBackgroundTask> task = new ModalBackgroundTask>(this, false) { + + private static final int MAX_SONGS = 500; + + @Override + protected List doInBackground() throws Throwable { + MusicService musicService = MusicServiceFactory.getMusicService(SubsonicTabActivity.this); + MusicDirectory root = musicService.getMusicDirectory(id, false, SubsonicTabActivity.this, this); + List songs = new LinkedList(); + getSongsRecursively(root, songs); + return songs; + } + + private void getSongsRecursively(MusicDirectory parent, List songs) throws Exception { + if (songs.size() > MAX_SONGS) { + return; + } + + for (MusicDirectory.Entry song : parent.getChildren(false, true)) { + if (!song.isVideo()) { + songs.add(song); + } + } + for (MusicDirectory.Entry dir : parent.getChildren(true, false)) { + MusicService musicService = MusicServiceFactory.getMusicService(SubsonicTabActivity.this); + getSongsRecursively(musicService.getMusicDirectory(dir.getId(), false, SubsonicTabActivity.this, this), songs); + } + } + + @Override + protected void done(List songs) { + DownloadService downloadService = getDownloadService(); + if (!songs.isEmpty() && downloadService != null) { + if (!append) { + downloadService.clear(); + } + warnIfNetworkOrStorageUnavailable(); + downloadService.download(songs, save, autoplay, false); + Util.startActivityWithoutTransition(SubsonicTabActivity.this, DownloadActivity.class); + } + } + }; + + task.execute(); + } + + private void setUncaughtExceptionHandler() { + Thread.UncaughtExceptionHandler handler = Thread.getDefaultUncaughtExceptionHandler(); + if (!(handler instanceof SubsonicUncaughtExceptionHandler)) { + Thread.setDefaultUncaughtExceptionHandler(new SubsonicUncaughtExceptionHandler(this)); + } + } + + /** + * Logs the stack trace of uncaught exceptions to a file on the SD card. + */ + private static class SubsonicUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler { + + private final Thread.UncaughtExceptionHandler defaultHandler; + private final Context context; + + private SubsonicUncaughtExceptionHandler(Context context) { + this.context = context; + defaultHandler = Thread.getDefaultUncaughtExceptionHandler(); + } + + @Override + public void uncaughtException(Thread thread, Throwable throwable) { + File file = null; + PrintWriter printWriter = null; + try { + + PackageInfo packageInfo = context.getPackageManager().getPackageInfo("net.sourceforge.subsonic.androidapp", 0); + file = new File(Environment.getExternalStorageDirectory(), "subsonic-stacktrace.txt"); + printWriter = new PrintWriter(file); + printWriter.println("Android API level: " + Build.VERSION.SDK_INT); + printWriter.println("Subsonic version name: " + packageInfo.versionName); + printWriter.println("Subsonic version code: " + packageInfo.versionCode); + printWriter.println(); + throwable.printStackTrace(printWriter); + Log.i(TAG, "Stack trace written to " + file); + } catch (Throwable x) { + Log.e(TAG, "Failed to write stack trace to " + file, x); + } finally { + Util.close(printWriter); + if (defaultHandler != null) { + defaultHandler.uncaughtException(thread, throwable); + } + + } + } + } + + @Override + public void onClick(View arg0) { + // TODO Auto-generated method stub + + } + + class GestureActivity extends SimpleOnGestureListener { + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + try { + if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH) + return false; + + SwipeDirection swipe = null; + + // right to left swipe + if(e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) { + swipe = SwipeDirection.Left; + } else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) { + swipe = SwipeDirection.Right; + } + + String name = SubsonicTabActivity.this.getClass().getSimpleName(); + + switch (swipe) + { + case Right: + if (name.equalsIgnoreCase("MainActivity")) + { + Intent intent = new Intent(SubsonicTabActivity.this, DownloadActivity.class); + SubsonicTabActivity.this.startActivity(intent); + } + else if (name.equalsIgnoreCase("SelectArtistActivity") || name.equalsIgnoreCase("SelectAlbumActivity")) + { + Intent intent = new Intent(SubsonicTabActivity.this, MainActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + SubsonicTabActivity.this.startActivity(intent); + } + else if (name.equalsIgnoreCase("SearchActivity")) + { + Intent intent = new Intent(SubsonicTabActivity.this, SelectArtistActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + SubsonicTabActivity.this.startActivity(intent); + } + else if (name.equalsIgnoreCase("SelectPlaylistActivity")) + { + Intent intent = new Intent(SubsonicTabActivity.this, SearchActivity.class); + intent.putExtra(Constants.INTENT_EXTRA_REQUEST_SEARCH, true); + SubsonicTabActivity.this.startActivity(intent); + } + else if (name.equalsIgnoreCase("DownloadActivity") || name.equalsIgnoreCase("LyricsActivity")) + { + Intent intent = new Intent(SubsonicTabActivity.this, SelectPlaylistActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + SubsonicTabActivity.this.startActivity(intent); + } + + break; + case Left: + if (name.equalsIgnoreCase("MainActivity")) + { + Intent intent = new Intent(SubsonicTabActivity.this, SelectArtistActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + SubsonicTabActivity.this.startActivity(intent); + } + if (name.equalsIgnoreCase("SelectArtistActivity") || name.equalsIgnoreCase("SelectAlbumActivity")) + { + Intent intent = new Intent(SubsonicTabActivity.this, SearchActivity.class); + intent.putExtra(Constants.INTENT_EXTRA_REQUEST_SEARCH, true); + SubsonicTabActivity.this.startActivity(intent); + } + else if (name.equalsIgnoreCase("SearchActivity")) + { + Intent intent = new Intent(SubsonicTabActivity.this, SelectPlaylistActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + SubsonicTabActivity.this.startActivity(intent); + } + else if (name.equalsIgnoreCase("SelectPlaylistActivity")) + { + Intent intent = new Intent(SubsonicTabActivity.this, DownloadActivity.class); + SubsonicTabActivity.this.startActivity(intent); + } + else if (name.equalsIgnoreCase("DownloadActivity") || name.equalsIgnoreCase("LyricsActivity")) + { + Intent intent = new Intent(SubsonicTabActivity.this, MainActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + SubsonicTabActivity.this.startActivity(intent); + } + + break; + } + } catch (Exception e) { + // nothing + } + + return false; + } + } + +// @Override +// public boolean onKeyLongPress(int keyCode, KeyEvent event) { +// DownloadService service = getDownloadService(); +// int current = service.getCurrentPlayingIndex(); +// +// switch(keyCode){ +// case KeyEvent.KEYCODE_VOLUME_UP: +// shortPress = false; +// +// if (current == -1) { +// service.play(0); +// } else { +// current++; +// service.play(current); +// } +// return true; +// case KeyEvent.KEYCODE_VOLUME_DOWN: +// shortPress = false; +// +// if (current == -1 || current == 0) { +// service.play(0); +// } else { +// current--; +// service.play(current); +// } +// return true; +// } +// +// return super.onKeyLongPress(keyCode, event); +// } +// +// @Override +// public boolean onKeyUp(int keyCode, KeyEvent event) { +// if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) { +// shortPress = false; +// return true; +// } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP){ +// shortPress = false; +// return true; +// } +// +// return super.onKeyUp(keyCode, event); +// } +} + diff --git a/src/net/sourceforge/subsonic/androidapp/service/DownloadServiceImpl.java b/src/net/sourceforge/subsonic/androidapp/service/DownloadServiceImpl.java index 195de7fc..34dd2a7c 100644 --- a/src/net/sourceforge/subsonic/androidapp/service/DownloadServiceImpl.java +++ b/src/net/sourceforge/subsonic/androidapp/service/DownloadServiceImpl.java @@ -47,6 +47,7 @@ import net.sourceforge.subsonic.androidapp.domain.RepeatMode; import net.sourceforge.subsonic.androidapp.provider.SubsonicAppWidgetProvider4x1; import net.sourceforge.subsonic.androidapp.receiver.MediaButtonIntentReceiver; import net.sourceforge.subsonic.androidapp.util.CancellableTask; +import net.sourceforge.subsonic.androidapp.util.FileUtil; import net.sourceforge.subsonic.androidapp.util.LRUCache; import net.sourceforge.subsonic.androidapp.util.ShufflePlayBuffer; import net.sourceforge.subsonic.androidapp.util.SimpleServiceBinder; @@ -796,10 +797,10 @@ public class DownloadServiceImpl extends Service implements DownloadService { String title = artist + " - " + currentSong.getTitle(); Integer duration = currentSong.getDuration(); - MusicService musicService = MusicServiceFactory.getMusicService(this); DisplayMetrics metrics = this.getResources().getDisplayMetrics(); int size = Math.min(metrics.widthPixels, metrics.heightPixels); - Bitmap bitmap = musicService.getCoverArt(this, currentSong, size, true, null); + // Always get the album art from disk + Bitmap bitmap = FileUtil.getAlbumArtBitmap(this, currentSong, size); // Update the remote controls remoteControlClient @@ -808,8 +809,14 @@ public class DownloadServiceImpl extends Service implements DownloadService { .putString(MediaMetadataRetriever.METADATA_KEY_ARTIST, artist) .putString(MediaMetadataRetriever.METADATA_KEY_ALBUM, album) .putLong(MediaMetadataRetriever.METADATA_KEY_DURATION, duration) - .putBitmap(RemoteControlClient.MetadataEditor.BITMAP_KEY_ARTWORK, bitmap) .apply(); + + if (bitmap != null) { + remoteControlClient + .editMetadata(false) + .putBitmap(RemoteControlClient.MetadataEditor.BITMAP_KEY_ARTWORK, bitmap) + .apply(); + } } } catch (Exception e) { diff --git a/src/net/sourceforge/subsonic/androidapp/service/OfflineMusicService.java b/src/net/sourceforge/subsonic/androidapp/service/OfflineMusicService.java index 11893d5a..11c8e11f 100644 --- a/src/net/sourceforge/subsonic/androidapp/service/OfflineMusicService.java +++ b/src/net/sourceforge/subsonic/androidapp/service/OfflineMusicService.java @@ -134,7 +134,7 @@ public class OfflineMusicService extends RESTMusicService { byte[] bytes = Util.toByteArray(in); Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length); Log.i("getCoverArt", "getCoverArt"); - return Bitmap.createScaledBitmap(bitmap, size, size, true); + return Util.scaleBitmap(bitmap, size); } finally { Util.close(in); } diff --git a/src/net/sourceforge/subsonic/androidapp/service/RESTMusicService.java b/src/net/sourceforge/subsonic/androidapp/service/RESTMusicService.java index ed69924a..eeabf1cd 100644 --- a/src/net/sourceforge/subsonic/androidapp/service/RESTMusicService.java +++ b/src/net/sourceforge/subsonic/androidapp/service/RESTMusicService.java @@ -448,52 +448,64 @@ public class RESTMusicService implements MusicService { } @Override - public Bitmap getCoverArt(Context context, MusicDirectory.Entry entry, int size, boolean saveToFile, ProgressListener progressListener) throws Exception { + public Bitmap getCoverArt(Context context, MusicDirectory.Entry entry, int size, boolean saveToFile, ProgressListener progressListener) throws Exception { + // Synchronize on the entry so that we don't download concurrently for + // the same song. + synchronized (entry) { + // Use cached file, if existing. + Bitmap bitmap = FileUtil.getAlbumArtBitmap(context, entry, size); + boolean serverScaling = Util.isServerScalingEnabled(context); + + if (bitmap == null) { + String url = Util.getRestUrl(context, "getCoverArt"); - // Synchronize on the entry so that we don't download concurrently for the same song. - synchronized (entry) { + InputStream in = null; + try { + List parameterNames; + List parameterValues; + + if (serverScaling) { + parameterNames = Arrays.asList("id", "size"); + parameterValues = Arrays.asList(entry.getCoverArt(), size); + } + else { + parameterNames = Arrays.asList("id"); + parameterValues = Arrays. asList(entry.getCoverArt()); + } + + HttpEntity entity = getEntityForURL(context, url, null, parameterNames, parameterValues, progressListener); + in = entity.getContent(); - // Use cached file, if existing. - Bitmap bitmap = FileUtil.getAlbumArtBitmap(context, entry, size); - if (bitmap != null) { - return bitmap; - } + // If content type is XML, an error occurred. Get it. + String contentType = Util.getContentType(entity); + if (contentType != null && contentType.startsWith("text/xml")) { + new ErrorParser(context).parse(new InputStreamReader(in, Constants.UTF_8)); + return null; // Never reached. + } - String url = Util.getRestUrl(context, "getCoverArt"); + byte[] bytes = Util.toByteArray(in); - InputStream in = null; - try { - List parameterNames = Arrays.asList("id", "size"); - List parameterValues = Arrays.asList(entry.getCoverArt(), size); - HttpEntity entity = getEntityForURL(context, url, null, parameterNames, parameterValues, progressListener); - in = entity.getContent(); + // If we aren't allowing server-side scaling, always save the file to disk because it will be unmodified + if (!serverScaling || saveToFile) { + OutputStream out = null; + try { + out = new FileOutputStream(FileUtil.getAlbumArtFile(context, entry)); + out.write(bytes); + } finally { + Util.close(out); + } + } - // If content type is XML, an error occured. Get it. - String contentType = Util.getContentType(entity); - if (contentType != null && contentType.startsWith("text/xml")) { - new ErrorParser(context).parse(new InputStreamReader(in, Constants.UTF_8)); - return null; // Never reached. - } - - byte[] bytes = Util.toByteArray(in); - - if (saveToFile) { - OutputStream out = null; - try { - out = new FileOutputStream(FileUtil.getAlbumArtFile(context, entry)); - out.write(bytes); - } finally { - Util.close(out); - } - } - - return BitmapFactory.decodeByteArray(bytes, 0, bytes.length); - - } finally { - Util.close(in); - } - } - } + bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length); + } finally { + Util.close(in); + } + } + + // Return scaled bitmap + return Util.scaleBitmap(bitmap, size); + } + } @Override public HttpResponse getDownloadInputStream(Context context, MusicDirectory.Entry song, long offset, int maxBitrate, CancellableTask task) throws Exception { diff --git a/src/net/sourceforge/subsonic/androidapp/util/Constants.java b/src/net/sourceforge/subsonic/androidapp/util/Constants.java index e1adb80c..532824b9 100644 --- a/src/net/sourceforge/subsonic/androidapp/util/Constants.java +++ b/src/net/sourceforge/subsonic/androidapp/util/Constants.java @@ -73,6 +73,7 @@ public final class Constants { public static final String PREFERENCES_KEY_MEDIA_BUTTONS = "mediaButtons"; public static final String PREFERENCES_KEY_SCREEN_LIT_ON_DOWNLOAD = "screenLitOnDownload"; public static final String PREFERENCES_KEY_SCROBBLE = "scrobble"; + public static final String PREFERENCES_KEY_SERVER_SCALING = "serverScaling"; public static final String PREFERENCES_KEY_REPEAT_MODE = "repeatMode"; public static final String PREFERENCES_KEY_WIFI_REQUIRED_FOR_DOWNLOAD = "wifiRequiredForDownload"; public static final String PREFERENCES_KEY_BUFFER_LENGTH = "bufferLength"; diff --git a/src/net/sourceforge/subsonic/androidapp/util/FileUtil.java b/src/net/sourceforge/subsonic/androidapp/util/FileUtil.java index 5934d3d9..5567fba7 100644 --- a/src/net/sourceforge/subsonic/androidapp/util/FileUtil.java +++ b/src/net/sourceforge/subsonic/androidapp/util/FileUtil.java @@ -84,11 +84,13 @@ public class FileUtil { public static Bitmap getAlbumArtBitmap(Context context, MusicDirectory.Entry entry, int size) { File albumArtFile = getAlbumArtFile(context, entry); + if (albumArtFile.exists()) { Bitmap bitmap = BitmapFactory.decodeFile(albumArtFile.getPath()); Log.i("getAlbumArtBitmap", String.valueOf(size)); - return bitmap == null ? null : Bitmap.createScaledBitmap(bitmap, size, size, true); + return bitmap == null ? null : Util.scaleBitmap(bitmap, size); } + return null; } @@ -107,6 +109,9 @@ public class FileUtil { } else { String artist = fileSystemSafe(entry.getArtist()); String album = fileSystemSafe(entry.getAlbum()); + if (album == "unnamed") { + album = fileSystemSafe(entry.getTitle()); + } dir = new File(getMusicDirectory(context).getPath() + "/" + artist + "/" + album); } return dir; diff --git a/src/net/sourceforge/subsonic/androidapp/util/ImageLoader.java b/src/net/sourceforge/subsonic/androidapp/util/ImageLoader.java index 9af71a2d..039b03cb 100644 --- a/src/net/sourceforge/subsonic/androidapp/util/ImageLoader.java +++ b/src/net/sourceforge/subsonic/androidapp/util/ImageLoader.java @@ -21,11 +21,6 @@ package net.sourceforge.subsonic.androidapp.util; import android.app.ActionBar; import android.content.Context; import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.LinearGradient; -import android.graphics.Matrix; -import android.graphics.Paint; -import android.graphics.Shader; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.TransitionDrawable; @@ -37,7 +32,6 @@ import android.view.View; import android.widget.ImageView; import android.widget.TextView; import net.sourceforge.subsonic.androidapp.R; -import net.sourceforge.subsonic.androidapp.activity.DownloadActivity; import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; import net.sourceforge.subsonic.androidapp.service.MusicService; import net.sourceforge.subsonic.androidapp.service.MusicServiceFactory; @@ -82,20 +76,28 @@ public class ImageLoader implements Runnable { private void createLargeUnknownImage(Context context) { BitmapDrawable drawable = (BitmapDrawable) context.getResources().getDrawable(R.drawable.unknown_album_large); Log.i(TAG, "createLargeUnknownImage"); - Bitmap bitmap = Bitmap.createScaledBitmap(drawable.getBitmap(), imageSizeLarge, imageSizeLarge, true); + Bitmap bitmap = Util.scaleBitmap(drawable.getBitmap(), imageSizeLarge); //bitmap = createReflection(bitmap); largeUnknownImage = Util.createDrawableFromBitmap(context, bitmap); } public void loadImage(View view, MusicDirectory.Entry entry, boolean large, boolean crossfade) { - if (entry == null || entry.getCoverArt() == null) { + if (entry == null) { + setUnknownImage(view, large); + return; + } + + String coverArt = entry.getCoverArt(); + + if (coverArt == null) { setUnknownImage(view, large); return; } - + int size = large ? imageSizeLarge : imageSizeDefault; - Drawable drawable = cache.get(getKey(entry.getCoverArt(), size)); + Drawable drawable = cache.get(getKey(coverArt, size)); + if (drawable != null) { setImage(view, drawable, large); return; @@ -104,6 +106,7 @@ public class ImageLoader implements Runnable { if (!large) { setUnknownImage(view, large); } + queue.offer(new Task(view, entry, size, large, large, crossfade)); } diff --git a/src/net/sourceforge/subsonic/androidapp/util/TabActivityBackgroundTask.java b/src/net/sourceforge/subsonic/androidapp/util/TabActivityBackgroundTask.java index 033a51ad..91b67f3b 100644 --- a/src/net/sourceforge/subsonic/androidapp/util/TabActivityBackgroundTask.java +++ b/src/net/sourceforge/subsonic/androidapp/util/TabActivityBackgroundTask.java @@ -52,7 +52,7 @@ public abstract class TabActivityBackgroundTask extends BackgroundTask { } private boolean isCancelled() { - return tabActivity.isDestroyed(); + return tabActivity.getIsDestroyed(); } @Override diff --git a/src/net/sourceforge/subsonic/androidapp/util/Util.java b/src/net/sourceforge/subsonic/androidapp/util/Util.java index 759f51cf..78ce38c9 100644 --- a/src/net/sourceforge/subsonic/androidapp/util/Util.java +++ b/src/net/sourceforge/subsonic/androidapp/util/Util.java @@ -137,6 +137,14 @@ public class Util extends DownloadActivity { return prefs.getBoolean(Constants.PREFERENCES_KEY_SCROBBLE, false); } + public static boolean isServerScalingEnabled(Context context) { + if (isOffline(context)) { + return false; + } + SharedPreferences prefs = getPreferences(context); + return prefs.getBoolean(Constants.PREFERENCES_KEY_SERVER_SCALING, false); + } + public static boolean isNotificationEnabled(Context context) { SharedPreferences prefs = getPreferences(context); return prefs.getBoolean(Constants.PREFERENCES_KEY_SHOW_NOTIFICATION, false); @@ -693,6 +701,17 @@ public class Util extends DownloadActivity { return new BitmapDrawable(bitmap); } } + + public static Bitmap scaleBitmap(Bitmap bitmap, int size) { + // Try to keep correct aspect ratio of the original image, do not force a square + double aspectRatio = (double)bitmap.getHeight() / (double)bitmap.getWidth(); + + // Assume the size given refers to the width of the image, so calculate the new height using + // the previously determined aspect ratio + int newHeight = (int) Math.round(size * aspectRatio); + + return Bitmap.createScaledBitmap(bitmap, size, newHeight, true); + } public static void registerMediaButtonEventReceiver(Context context) {