From 4e4e12496c63c96e11d375f7cf9ef49222c4d1ef Mon Sep 17 00:00:00 2001
From: Joshua Bahnsen <archrival@gmail.com>
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 @@
-<?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>
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 @@
 <?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"/>
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 {
         /** <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;
diff --git a/lint.xml b/lint.xml
new file mode 100644
index 00000000..ee0eead5
--- /dev/null
+++ b/lint.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<lint>
+</lint>
\ 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-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>
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 @@
     <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>
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 @@
     <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"
diff --git a/src/net/sourceforge/subsonic/androidapp/activity/SearchActivity.java b/src/net/sourceforge/subsonic/androidapp/activity/SearchActivity.java
index e21d9db1..8ea0eeab 100644
--- a/src/net/sourceforge/subsonic/androidapp/activity/SearchActivity.java
+++ b/src/net/sourceforge/subsonic/androidapp/activity/SearchActivity.java
@@ -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);
+        }
+    }
 }
\ 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 <http://www.gnu.org/licenses/>.
-
- 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<MusicDirectory.Entry> songs = new ArrayList<MusicDirectory.Entry>(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<MusicDirectory, Boolean> 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<MusicDirectory.Entry> 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<MusicDirectory.Entry> getSelectedSongs() {
-        List<MusicDirectory.Entry> songs = new ArrayList<MusicDirectory.Entry>(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<MusicDirectory.Entry> 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<Pair<MusicDirectory, Boolean>> {
-
-        public LoadTask() {
-            super(SelectAlbumActivity.this);
-        }
-
-        protected abstract MusicDirectory load(MusicService service) throws Exception;
-
-        @Override
-        protected Pair<MusicDirectory, Boolean> doInBackground() throws Throwable {
-            MusicService musicService = MusicServiceFactory.getMusicService(SelectAlbumActivity.this);
-            MusicDirectory dir = load(musicService);
-            boolean valid = musicService.isLicenseValid(SelectAlbumActivity.this, this);
-            return new Pair<MusicDirectory, Boolean>(dir, valid);
-        }
-
-        @Override
-        protected void done(Pair<MusicDirectory, Boolean> result) {
-            List<MusicDirectory.Entry> 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 <http://www.gnu.org/licenses/>.
+
+ 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<MusicDirectory.Entry> songs = new ArrayList<MusicDirectory.Entry>(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<MusicDirectory, Boolean> 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<MusicDirectory.Entry> 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<MusicDirectory.Entry> getSelectedSongs() {
+        List<MusicDirectory.Entry> songs = new ArrayList<MusicDirectory.Entry>(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<MusicDirectory.Entry> 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<Pair<MusicDirectory, Boolean>> {
+
+        public LoadTask() {
+            super(SelectAlbumActivity.this);
+        }
+
+        protected abstract MusicDirectory load(MusicService service) throws Exception;
+
+        @Override
+        protected Pair<MusicDirectory, Boolean> doInBackground() throws Throwable {
+            MusicService musicService = MusicServiceFactory.getMusicService(SelectAlbumActivity.this);
+            MusicDirectory dir = load(musicService);
+            boolean valid = musicService.isLicenseValid(SelectAlbumActivity.this, this);
+            return new Pair<MusicDirectory, Boolean>(dir, valid);
+        }
+
+        @Override
+        protected void done(Pair<MusicDirectory, Boolean> result) {
+            List<MusicDirectory.Entry> 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 <http://www.gnu.org/licenses/>.
-
- 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<List<MusicDirectory.Entry>> task = new ModalBackgroundTask<List<MusicDirectory.Entry>>(this, false) {
-
-            private static final int MAX_SONGS = 500;
-
-            @Override
-            protected List<MusicDirectory.Entry> doInBackground() throws Throwable {
-                MusicService musicService = MusicServiceFactory.getMusicService(SubsonicTabActivity.this);
-                MusicDirectory root = musicService.getMusicDirectory(id, false, SubsonicTabActivity.this, this);
-                List<MusicDirectory.Entry> songs = new LinkedList<MusicDirectory.Entry>();
-                getSongsRecursively(root, songs);
-                return songs;
-            }
-
-            private void getSongsRecursively(MusicDirectory parent, List<MusicDirectory.Entry> 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<MusicDirectory.Entry> 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 <http://www.gnu.org/licenses/>.
+
+ 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<List<MusicDirectory.Entry>> task = new ModalBackgroundTask<List<MusicDirectory.Entry>>(this, false) {
+
+            private static final int MAX_SONGS = 500;
+
+            @Override
+            protected List<MusicDirectory.Entry> doInBackground() throws Throwable {
+                MusicService musicService = MusicServiceFactory.getMusicService(SubsonicTabActivity.this);
+                MusicDirectory root = musicService.getMusicDirectory(id, false, SubsonicTabActivity.this, this);
+                List<MusicDirectory.Entry> songs = new LinkedList<MusicDirectory.Entry>();
+                getSongsRecursively(root, songs);
+                return songs;
+            }
+
+            private void getSongsRecursively(MusicDirectory parent, List<MusicDirectory.Entry> 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<MusicDirectory.Entry> 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<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 {
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<T> extends BackgroundTask<T> {
     }
 
     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) {