1
0
mirror of https://github.com/ultrasonic/ultrasonic synced 2025-02-16 11:41:16 +01:00

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
This commit is contained in:
Joshua Bahnsen 2013-04-01 18:16:45 -07:00
parent f3c9221d67
commit 4e4e12496c
20 changed files with 2005 additions and 1849 deletions

View File

@ -1,9 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="lib" path="D:/Data/Android/adt-bundle-windows-x86_64/sdk/tools/support/annotations.jar"/>
<classpathentry kind="output" path="bin/classes"/>
</classpath>
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="output" path="bin/classes"/>
</classpath>

22
.gitattributes vendored Normal file
View File

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

163
.gitignore vendored Normal file
View File

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

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:a="http://schemas.android.com/apk/res/android"
package="net.sourceforge.subsonic.androidapp"
a:versionCode="63"
a:versionName="3.9.9.22" a:installLocation="auto">
a:versionCode="64"
a:versionName="3.9.9.23" a:installLocation="auto">
<uses-permission a:name="android.permission.INTERNET"/>
<uses-permission a:name="android.permission.READ_PHONE_STATE"/>

View File

@ -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 {
/** <string name="settings.screen_lit_title">TODO: Keep screen on</string>
<string name="settings.screen_lit_summary">TODO: Keeping the screen on when downloading may improve download speed</string>
*/
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;
/** <string name="menu.exit">TODO: Exit</string>
@ -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;
/** <string name="settings.scrobble_title">TODO: Scrobble to Last.fm</string>
<string name="settings.scrobble_summary">TODO: Remember to set up your Last.fm user and password on the Subsonic server</string>
*/
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;

3
lint.xml Normal file
View File

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<lint>
</lint>

View File

@ -99,7 +99,6 @@
<string-array name="bufferLengthNames">
<item>@string/settings.buffer_length_1</item>
<item>@string/settings.buffer_length_2</item>
<item>@string/settings.buffer_length_2</item>
<item>@string/settings.buffer_length_5</item>
<item>@string/settings.buffer_length_8</item>
@ -132,96 +131,38 @@
<item>@string/settings.network_timeout_120000</item>
</string-array>
<string-array name="maxAlbumsValues">
<string-array name="searchValues">
<item>1</item>
<item>3</item>
<item>5</item>
<item>10</item>
<item>15</item>
<item>20</item>
<item>25</item>
<item>30</item>
<item>40</item>
<item>50</item>
<item>100</item>
<item>250</item>
<item>500</item>
</string-array>
<string-array name="maxAlbumsNames">
<item>@string/settings.max_albums_5</item>
<item>@string/settings.max_albums_10</item>
<item>@string/settings.max_albums_20</item>
<item>@string/settings.max_albums_30</item>
<item>@string/settings.max_albums_40</item>
<item>@string/settings.max_albums_50</item>
<item>@string/settings.max_albums_100</item>
<item>@string/settings.max_albums_250</item>
<item>@string/settings.max_albums_500</item>
</string-array>
<string-array name="maxSongsValues">
<item>5</item>
<item>10</item>
<item>25</item>
<item>50</item>
<item>75</item>
<item>100</item>
<item>250</item>
<item>500</item>
</string-array>
<string-array name="maxSongsNames">
<item>@string/settings.max_songs_5</item>
<item>@string/settings.max_songs_10</item>
<item>@string/settings.max_songs_25</item>
<item>@string/settings.max_songs_50</item>
<item>@string/settings.max_songs_75</item>
<item>@string/settings.max_songs_100</item>
<item>@string/settings.max_songs_250</item>
<item>@string/settings.max_songs_500</item>
</string-array>
<string-array name="maxArtistsValues">
<item>5</item>
<item>10</item>
<item>25</item>
<item>50</item>
<item>75</item>
<item>100</item>
<item>250</item>
<item>500</item>
</string-array>
<string-array name="maxArtistsNames">
<item>@string/settings.max_artists_5</item>
<item>@string/settings.max_artists_10</item>
<item>@string/settings.max_artists_25</item>
<item>@string/settings.max_artists_50</item>
<item>@string/settings.max_artists_75</item>
<item>@string/settings.max_artists_100</item>
<item>@string/settings.max_artists_250</item>
<item>@string/settings.max_artists_500</item>
</string-array>
<string-array name="defaultSearchValues">
<item>3</item>
<item>5</item>
<item>10</item>
<item>25</item>
<item>50</item>
<item>75</item>
<item>100</item>
<item>250</item>
<item>500</item>
</string-array>
<string-array name="defaultSearchNames">
<item>@string/settings.max_artists_3</item>
<item>@string/settings.max_artists_5</item>
<item>@string/settings.max_artists_10</item>
<item>@string/settings.max_artists_25</item>
<item>@string/settings.max_artists_50</item>
<item>@string/settings.max_artists_75</item>
<item>@string/settings.max_artists_100</item>
<item>@string/settings.max_artists_250</item>
<item>@string/settings.max_artists_500</item>
<string-array name="searchNames">
<item>@string/settings.search_1</item>
<item>@string/settings.search_3</item>
<item>@string/settings.search_5</item>
<item>@string/settings.search_10</item>
<item>@string/settings.search_15</item>
<item>@string/settings.search_20</item>
<item>@string/settings.search_25</item>
<item>@string/settings.search_30</item>
<item>@string/settings.search_40</item>
<item>@string/settings.search_50</item>
<item>@string/settings.search_75</item>
<item>@string/settings.search_100</item>
<item>@string/settings.search_250</item>
<item>@string/settings.search_500</item>
</string-array>
</resources>

View File

@ -191,6 +191,8 @@
<string name="settings.playback_control_title">Playback Control Settings</string>
<string name="settings.scrobble_title">Scrobble To Last.fm</string>
<string name="settings.scrobble_summary">Remember to set up your Last.fm user and password on the Subsonic server</string>
<string name="settings.server_scaling_title">Server-Side Album Art Scaling</string>
<string name="settings.server_scaling_summary">Download scaled images from the server instead of full size (saves bandwidth)</string>
<string name="settings.hide_media_title">Hide From Other</string>
<string name="settings.hide_media_summary">Hide music files from other apps.</string>
<string name="settings.hide_media_toast">Takes effect next time Android scans your phone for music.</string>
@ -226,34 +228,22 @@
<string name="settings.use_stream_proxy">Use Stream Proxy</string>
<string name="settings.use_stream_proxy_summary">Stream media playback through a proxy (may help stutter)</string>
<string name="settings.max_albums">Max Albums</string>
<string name="settings.max_albums_5">5</string>
<string name="settings.max_albums_10">10</string>
<string name="settings.max_albums_20">20</string>
<string name="settings.max_albums_30">30</string>
<string name="settings.max_albums_40">40</string>
<string name="settings.max_albums_50">50</string>
<string name="settings.max_albums_100">100</string>
<string name="settings.max_albums_250">250</string>
<string name="settings.max_albums_500">500</string>
<string name="settings.max_songs">Max Songs</string>
<string name="settings.max_songs_5">5</string>
<string name="settings.max_songs_10">10</string>
<string name="settings.max_songs_25">25</string>
<string name="settings.max_songs_50">50</string>
<string name="settings.max_songs_75">75</string>
<string name="settings.max_songs_100">100</string>
<string name="settings.max_songs_250">250</string>
<string name="settings.max_songs_500">500</string>
<string name="settings.search_1">1</string>
<string name="settings.search_3">3</string>
<string name="settings.search_5">5</string>
<string name="settings.search_10">10</string>
<string name="settings.search_15">15</string>
<string name="settings.search_20">20</string>
<string name="settings.search_25">25</string>
<string name="settings.search_30">30</string>
<string name="settings.search_40">40</string>
<string name="settings.search_50">50</string>
<string name="settings.search_75">75</string>
<string name="settings.search_100">100</string>
<string name="settings.search_250">250</string>
<string name="settings.search_500">500</string>
<string name="settings.max_artists">Max Artists</string>
<string name="settings.max_artists_3">3</string>
<string name="settings.max_artists_5">5</string>
<string name="settings.max_artists_10">10</string>
<string name="settings.max_artists_25">25</string>
<string name="settings.max_artists_50">50</string>
<string name="settings.max_artists_75">75</string>
<string name="settings.max_artists_100">100</string>
<string name="settings.max_artists_250">250</string>
<string name="settings.max_artists_500">500</string>
<string name="settings.max_songs">Max Songs</string>
<string name="settings.default_artists">Default Artists</string>
<string name="settings.default_albums">Default Albums</string>
<string name="settings.default_songs">Default Songs</string>

View File

@ -134,38 +134,38 @@
<PreferenceCategory a:title="@string/settings.search_title" >
<ListPreference
a:defaultValue="3"
a:entries="@array/defaultSearchNames"
a:entryValues="@array/defaultSearchValues"
a:entries="@array/searchNames"
a:entryValues="@array/searchValues"
a:key="defaultArtists"
a:title="@string/settings.default_artists" />
<ListPreference
a:defaultValue="10"
a:entries="@array/maxArtistsNames"
a:entryValues="@array/maxArtistsValues"
a:entries="@array/searchNames"
a:entryValues="@array/searchValues"
a:key="maxArtists"
a:title="@string/settings.max_artists" />
<ListPreference
a:defaultValue="5"
a:entries="@array/defaultSearchNames"
a:entryValues="@array/defaultSearchValues"
a:entries="@array/searchNames"
a:entryValues="@array/searchValues"
a:key="defaultAlbums"
a:title="@string/settings.default_albums" />
<ListPreference
a:defaultValue="20"
a:entries="@array/maxAlbumsNames"
a:entryValues="@array/maxAlbumsValues"
a:entries="@array/searchNames"
a:entryValues="@array/searchValues"
a:key="maxAlbums"
a:title="@string/settings.max_albums" />
<ListPreference
a:defaultValue="10"
a:entries="@array/defaultSearchNames"
a:entryValues="@array/defaultSearchValues"
a:entries="@array/searchNames"
a:entryValues="@array/searchValues"
a:key="defaultSongs"
a:title="@string/settings.default_songs" />
<ListPreference
a:defaultValue="25"
a:entries="@array/maxSongsNames"
a:entryValues="@array/maxSongsValues"
a:entries="@array/searchNames"
a:entryValues="@array/searchValues"
a:key="maxSongs"
a:title="@string/settings.max_songs" />
<Preference
@ -201,6 +201,11 @@
a:title="@string/settings.use_stream_proxy" />
</PreferenceCategory>
<PreferenceCategory a:title="@string/settings.other_title" >
<CheckBoxPreference
a:defaultValue="true"
a:key="serverScaling"
a:summary="@string/settings.server_scaling_summary"
a:title="@string/settings.server_scaling_title" />
<CheckBoxPreference
a:defaultValue="false"
a:key="scrobble"

View File

@ -1,369 +1,368 @@
/*
This file is part of Subsonic.
Subsonic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Subsonic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
Copyright 2009 (C) Sindre Mehus
*/
package net.sourceforge.subsonic.androidapp.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<SearchResult> task = new TabActivityBackgroundTask<SearchResult>(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<Artist> artists = searchResult.getArtists();
if (!artists.isEmpty()) {
mergeAdapter.addView(artistsHeading);
List<Artist> displayedArtists = new ArrayList<Artist>(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<MusicDirectory.Entry> albums = searchResult.getAlbums();
if (!albums.isEmpty()) {
mergeAdapter.addView(albumsHeading);
List<MusicDirectory.Entry> displayedAlbums = new ArrayList<MusicDirectory.Entry>(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<MusicDirectory.Entry> songs = searchResult.getSongs();
if (!songs.isEmpty()) {
mergeAdapter.addView(songsHeading);
List<MusicDirectory.Entry> displayedSongs = new ArrayList<MusicDirectory.Entry>(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 <http://www.gnu.org/licenses/>.
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<SearchResult> task = new TabActivityBackgroundTask<SearchResult>(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<Artist> artists = searchResult.getArtists();
if (!artists.isEmpty()) {
mergeAdapter.addView(artistsHeading);
List<Artist> displayedArtists = new ArrayList<Artist>(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<MusicDirectory.Entry> albums = searchResult.getAlbums();
if (!albums.isEmpty()) {
mergeAdapter.addView(albumsHeading);
List<MusicDirectory.Entry> displayedAlbums = new ArrayList<MusicDirectory.Entry>(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<MusicDirectory.Entry> songs = searchResult.getSongs();
if (!songs.isEmpty()) {
mergeAdapter.addView(songsHeading);
List<MusicDirectory.Entry> displayedSongs = new ArrayList<MusicDirectory.Entry>(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);
}
}
}

View File

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

View File

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

View File

@ -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<String> parameterNames;
List<Object> parameterValues;
if (serverScaling) {
parameterNames = Arrays.asList("id", "size");
parameterValues = Arrays.<Object>asList(entry.getCoverArt(), size);
}
else {
parameterNames = Arrays.asList("id");
parameterValues = Arrays.<Object> 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<String> parameterNames = Arrays.asList("id", "size");
List<Object> parameterValues = Arrays.<Object>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 {

View File

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

View File

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

View File

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

View File

@ -52,7 +52,7 @@ public abstract class TabActivityBackgroundTask<T> extends BackgroundTask<T> {
}
private boolean isCancelled() {
return tabActivity.isDestroyed();
return tabActivity.getIsDestroyed();
}
@Override

View File

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