aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorScott Jackson <daneren2005@gmail.com>2015-04-25 17:03:02 -0700
committerScott Jackson <daneren2005@gmail.com>2015-04-25 17:03:05 -0700
commitcfd014d38cba03ba05f571597b361ab253bff578 (patch)
tree4256723561dec7ef3ed3507382eb7020724ec570
parent8a332a20ec272d59fe74520825b18017a8f0cac3 (diff)
downloaddsub-cfd014d38cba03ba05f571597b361ab253bff578.tar.gz
dsub-cfd014d38cba03ba05f571597b361ab253bff578.tar.bz2
dsub-cfd014d38cba03ba05f571597b361ab253bff578.zip
Update to gradle
-rw-r--r--.gitignore2
-rw-r--r--DSub.iml19
m---------DragSortListView0
m---------ServerProxy0
-rw-r--r--Subsonic.iml33
-rw-r--r--ant.properties20
-rw-r--r--app/.gitignore1
-rw-r--r--app/app.iml108
-rw-r--r--app/build.gradle32
-rw-r--r--app/libs/CWAC-AdapterWrapper.jar (renamed from libs/CWAC-AdapterWrapper.jar)bin4841 -> 4841 bytes
-rw-r--r--app/libs/CWAC-EndlessAdapter.jar (renamed from libs/CWAC-EndlessAdapter.jar)bin5317 -> 5317 bytes
-rw-r--r--app/libs/cling-core-2.0.1.jar (renamed from libs/cling-core-2.0.1.jar)bin686501 -> 686501 bytes
-rw-r--r--app/libs/cling-support-2.0.1.jar (renamed from libs/cling-support-2.0.1.jar)bin490043 -> 490043 bytes
-rw-r--r--app/libs/javax.servlet-3.0.0.v201112011016.jar (renamed from libs/javax.servlet-3.0.0.v201112011016.jar)bin200387 -> 200387 bytes
-rw-r--r--app/libs/jetty-all-8.1.16.v20140903.jar (renamed from libs/jetty-all-8.1.16.v20140903.jar)bin1880786 -> 1880786 bytes
-rw-r--r--app/libs/kryo-2.21-all.jar (renamed from libs/kryo-2.21-all.jar)bin236628 -> 236628 bytes
-rw-r--r--app/libs/seamless-http-1.1.0.jar (renamed from libs/seamless-http-1.1.0.jar)bin21646 -> 21646 bytes
-rw-r--r--app/libs/seamless-util-1.1.0.jar (renamed from libs/seamless-util-1.1.0.jar)bin94456 -> 94456 bytes
-rw-r--r--app/libs/seamless-xml-1.1.0.jar (renamed from libs/seamless-xml-1.1.0.jar)bin63142 -> 63142 bytes
-rw-r--r--app/src/androidTest/java/github/daneren2005/dsub/ApplicationTest.java13
-rw-r--r--app/src/androidTest/java/github/daneren2005/dsub/activity/DownloadActivityTest.java (renamed from test/github/daneren2005/dsub/activity/DownloadActivityTest.java)0
-rw-r--r--app/src/androidTest/java/github/daneren2005/dsub/activity/SubsonicFragmentActivityTest.java (renamed from test/github/daneren2005/dsub/activity/SubsonicFragmentActivityTest.java)0
-rw-r--r--app/src/androidTest/java/github/daneren2005/dsub/domain/BookmarkTest.java (renamed from test/github/daneren2005/dsub/domain/BookmarkTest.java)0
-rw-r--r--app/src/androidTest/java/github/daneren2005/dsub/domain/GenreComparatorTest.java (renamed from test/github/daneren2005/dsub/domain/GenreComparatorTest.java)0
-rw-r--r--app/src/androidTest/java/github/daneren2005/dsub/service/DownloadServiceTest.java (renamed from test/github/daneren2005/dsub/service/DownloadServiceTest.java)0
-rw-r--r--app/src/main/AndroidManifest.xml (renamed from AndroidManifest.xml)500
-rw-r--r--app/src/main/java/github/daneren2005/dsub/activity/DownloadActivity.java (renamed from src/github/daneren2005/dsub/activity/DownloadActivity.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/activity/EditPlayActionActivity.java (renamed from src/github/daneren2005/dsub/activity/EditPlayActionActivity.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/activity/QueryReceiverActivity.java (renamed from src/github/daneren2005/dsub/activity/QueryReceiverActivity.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/activity/SettingsActivity.java (renamed from src/github/daneren2005/dsub/activity/SettingsActivity.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/activity/SubsonicActivity.java (renamed from src/github/daneren2005/dsub/activity/SubsonicActivity.java)1720
-rw-r--r--app/src/main/java/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java (renamed from src/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java)1372
-rw-r--r--app/src/main/java/github/daneren2005/dsub/activity/VoiceQueryReceiverActivity.java (renamed from src/github/daneren2005/dsub/activity/VoiceQueryReceiverActivity.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/AlbumGridAdapter.java (renamed from src/github/daneren2005/dsub/adapter/AlbumGridAdapter.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/AlbumListAdapter.java (renamed from src/github/daneren2005/dsub/adapter/AlbumListAdapter.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/ArtistAdapter.java (renamed from src/github/daneren2005/dsub/adapter/ArtistAdapter.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/BookmarkAdapter.java (renamed from src/github/daneren2005/dsub/adapter/BookmarkAdapter.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/ChatAdapter.java (renamed from src/github/daneren2005/dsub/adapter/ChatAdapter.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/DownloadFileAdapter.java (renamed from src/github/daneren2005/dsub/adapter/DownloadFileAdapter.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/DrawerAdapter.java (renamed from src/github/daneren2005/dsub/adapter/DrawerAdapter.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/EntryAdapter.java (renamed from src/github/daneren2005/dsub/adapter/EntryAdapter.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/GenreAdapter.java (renamed from src/github/daneren2005/dsub/adapter/GenreAdapter.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/MergeAdapter.java (renamed from src/github/daneren2005/dsub/adapter/MergeAdapter.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/PlaylistAdapter.java (renamed from src/github/daneren2005/dsub/adapter/PlaylistAdapter.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/PodcastChannelAdapter.java (renamed from src/github/daneren2005/dsub/adapter/PodcastChannelAdapter.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/SackOfViewsAdapter.java (renamed from src/github/daneren2005/dsub/adapter/SackOfViewsAdapter.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/SettingsAdapter.java (renamed from src/github/daneren2005/dsub/adapter/SettingsAdapter.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/ShareAdapter.java (renamed from src/github/daneren2005/dsub/adapter/ShareAdapter.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/UserAdapter.java (renamed from src/github/daneren2005/dsub/adapter/UserAdapter.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/audiofx/AudioEffectsController.java (renamed from src/github/daneren2005/dsub/audiofx/AudioEffectsController.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/audiofx/EqualizerController.java (renamed from src/github/daneren2005/dsub/audiofx/EqualizerController.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/audiofx/LoudnessEnhancerController.java (renamed from src/github/daneren2005/dsub/audiofx/LoudnessEnhancerController.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/domain/Artist.java (renamed from src/github/daneren2005/dsub/domain/Artist.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/domain/ArtistInfo.java (renamed from src/github/daneren2005/dsub/domain/ArtistInfo.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/domain/Bookmark.java (renamed from src/github/daneren2005/dsub/domain/Bookmark.java)210
-rw-r--r--app/src/main/java/github/daneren2005/dsub/domain/ChatMessage.java (renamed from src/github/daneren2005/dsub/domain/ChatMessage.java)102
-rw-r--r--app/src/main/java/github/daneren2005/dsub/domain/DLNADevice.java (renamed from src/github/daneren2005/dsub/domain/DLNADevice.java)156
-rw-r--r--app/src/main/java/github/daneren2005/dsub/domain/Genre.java (renamed from src/github/daneren2005/dsub/domain/Genre.java)138
-rw-r--r--app/src/main/java/github/daneren2005/dsub/domain/Indexes.java (renamed from src/github/daneren2005/dsub/domain/Indexes.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/domain/Lyrics.java (renamed from src/github/daneren2005/dsub/domain/Lyrics.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/domain/MusicDirectory.java (renamed from src/github/daneren2005/dsub/domain/MusicDirectory.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/domain/MusicFolder.java (renamed from src/github/daneren2005/dsub/domain/MusicFolder.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/domain/PlayerQueue.java (renamed from src/github/daneren2005/dsub/domain/PlayerQueue.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/domain/PlayerState.java (renamed from src/github/daneren2005/dsub/domain/PlayerState.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/domain/Playlist.java (renamed from src/github/daneren2005/dsub/domain/Playlist.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/domain/PodcastChannel.java (renamed from src/github/daneren2005/dsub/domain/PodcastChannel.java)290
-rw-r--r--app/src/main/java/github/daneren2005/dsub/domain/PodcastEpisode.java (renamed from src/github/daneren2005/dsub/domain/PodcastEpisode.java)108
-rw-r--r--app/src/main/java/github/daneren2005/dsub/domain/RemoteControlState.java (renamed from src/github/daneren2005/dsub/domain/RemoteControlState.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/domain/RemoteStatus.java (renamed from src/github/daneren2005/dsub/domain/RemoteStatus.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/domain/RepeatMode.java (renamed from src/github/daneren2005/dsub/domain/RepeatMode.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/domain/SearchCritera.java (renamed from src/github/daneren2005/dsub/domain/SearchCritera.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/domain/SearchResult.java (renamed from src/github/daneren2005/dsub/domain/SearchResult.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/domain/ServerInfo.java (renamed from src/github/daneren2005/dsub/domain/ServerInfo.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/domain/Share.java (renamed from src/github/daneren2005/dsub/domain/Share.java)330
-rw-r--r--app/src/main/java/github/daneren2005/dsub/domain/User.java (renamed from src/github/daneren2005/dsub/domain/User.java)234
-rw-r--r--app/src/main/java/github/daneren2005/dsub/domain/Version.java (renamed from src/github/daneren2005/dsub/domain/Version.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/AdminFragment.java (renamed from src/github/daneren2005/dsub/fragments/AdminFragment.java)294
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/ChatFragment.java (renamed from src/github/daneren2005/dsub/fragments/ChatFragment.java)498
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/DownloadFragment.java (renamed from src/github/daneren2005/dsub/fragments/DownloadFragment.java)378
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/EqualizerFragment.java (renamed from src/github/daneren2005/dsub/fragments/EqualizerFragment.java)882
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/LyricsFragment.java (renamed from src/github/daneren2005/dsub/fragments/LyricsFragment.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/MainFragment.java (renamed from src/github/daneren2005/dsub/fragments/MainFragment.java)1172
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/NowPlayingFragment.java (renamed from src/github/daneren2005/dsub/fragments/NowPlayingFragment.java)3136
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/PreferenceCompatFragment.java (renamed from src/github/daneren2005/dsub/fragments/PreferenceCompatFragment.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SearchFragment.java (renamed from src/github/daneren2005/dsub/fragments/SearchFragment.java)736
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectArtistFragment.java (renamed from src/github/daneren2005/dsub/fragments/SelectArtistFragment.java)666
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectBookmarkFragment.java (renamed from src/github/daneren2005/dsub/fragments/SelectBookmarkFragment.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java (renamed from src/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java)3194
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectGenreFragment.java (renamed from src/github/daneren2005/dsub/fragments/SelectGenreFragment.java)142
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectListFragment.java (renamed from src/github/daneren2005/dsub/fragments/SelectListFragment.java)326
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectPlaylistFragment.java (renamed from src/github/daneren2005/dsub/fragments/SelectPlaylistFragment.java)606
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectPodcastsFragment.java (renamed from src/github/daneren2005/dsub/fragments/SelectPodcastsFragment.java)616
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectShareFragment.java (renamed from src/github/daneren2005/dsub/fragments/SelectShareFragment.java)432
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectVideoFragment.java (renamed from src/github/daneren2005/dsub/fragments/SelectVideoFragment.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectYearFragment.java (renamed from src/github/daneren2005/dsub/fragments/SelectYearFragment.java)156
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SettingsFragment.java (renamed from src/github/daneren2005/dsub/fragments/SettingsFragment.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SimilarArtistFragment.java (renamed from src/github/daneren2005/dsub/fragments/SimilarArtistFragment.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java (renamed from src/github/daneren2005/dsub/fragments/SubsonicFragment.java)3634
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/UserFragment.java (renamed from src/github/daneren2005/dsub/fragments/UserFragment.java)250
-rw-r--r--app/src/main/java/github/daneren2005/dsub/provider/DLNARouteProvider.java (renamed from src/github/daneren2005/dsub/provider/DLNARouteProvider.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/provider/DSubSearchProvider.java (renamed from src/github/daneren2005/dsub/provider/DSubSearchProvider.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/provider/DSubWidget4x1.java (renamed from src/github/daneren2005/dsub/provider/DSubWidget4x1.java)56
-rw-r--r--app/src/main/java/github/daneren2005/dsub/provider/DSubWidget4x2.java (renamed from src/github/daneren2005/dsub/provider/DSubWidget4x2.java)56
-rw-r--r--app/src/main/java/github/daneren2005/dsub/provider/DSubWidget4x3.java (renamed from src/github/daneren2005/dsub/provider/DSubWidget4x3.java)56
-rw-r--r--app/src/main/java/github/daneren2005/dsub/provider/DSubWidget4x4.java (renamed from src/github/daneren2005/dsub/provider/DSubWidget4x4.java)56
-rw-r--r--app/src/main/java/github/daneren2005/dsub/provider/DSubWidgetProvider.java (renamed from src/github/daneren2005/dsub/provider/DSubWidgetProvider.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/provider/JukeboxRouteProvider.java (renamed from src/github/daneren2005/dsub/provider/JukeboxRouteProvider.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/provider/MostRecentStubProvider.java (renamed from src/github/daneren2005/dsub/provider/MostRecentStubProvider.java)122
-rw-r--r--app/src/main/java/github/daneren2005/dsub/provider/PlaylistStubProvider.java (renamed from src/github/daneren2005/dsub/provider/PlaylistStubProvider.java)122
-rw-r--r--app/src/main/java/github/daneren2005/dsub/provider/PodcastStubProvider.java (renamed from src/github/daneren2005/dsub/provider/PodcastStubProvider.java)122
-rw-r--r--app/src/main/java/github/daneren2005/dsub/provider/StarredStubProvider.java (renamed from src/github/daneren2005/dsub/provider/StarredStubProvider.java)122
-rw-r--r--app/src/main/java/github/daneren2005/dsub/receiver/A2dpIntentReceiver.java (renamed from src/github/daneren2005/dsub/receiver/A2dpIntentReceiver.java)92
-rw-r--r--app/src/main/java/github/daneren2005/dsub/receiver/AudioNoisyReceiver.java (renamed from src/github/daneren2005/dsub/receiver/AudioNoisyReceiver.java)102
-rw-r--r--app/src/main/java/github/daneren2005/dsub/receiver/BootReceiver.java (renamed from src/github/daneren2005/dsub/receiver/BootReceiver.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/receiver/HeadphonePlugReceiver.java (renamed from src/github/daneren2005/dsub/receiver/HeadphonePlugReceiver.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/receiver/MediaButtonIntentReceiver.java (renamed from src/github/daneren2005/dsub/receiver/MediaButtonIntentReceiver.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/receiver/PlayActionReceiver.java (renamed from src/github/daneren2005/dsub/receiver/PlayActionReceiver.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/CachedMusicService.java (renamed from src/github/daneren2005/dsub/service/CachedMusicService.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/ChromeCastController.java (renamed from src/github/daneren2005/dsub/service/ChromeCastController.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/DLNAController.java (renamed from src/github/daneren2005/dsub/service/DLNAController.java)1374
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/DownloadFile.java (renamed from src/github/daneren2005/dsub/service/DownloadFile.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/DownloadService.java (renamed from src/github/daneren2005/dsub/service/DownloadService.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/DownloadServiceLifecycleSupport.java (renamed from src/github/daneren2005/dsub/service/DownloadServiceLifecycleSupport.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/HeadphoneListenerService.java (renamed from src/github/daneren2005/dsub/service/HeadphoneListenerService.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/JukeboxController.java (renamed from src/github/daneren2005/dsub/service/JukeboxController.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/MediaStoreService.java (renamed from src/github/daneren2005/dsub/service/MediaStoreService.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/MusicService.java (renamed from src/github/daneren2005/dsub/service/MusicService.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/MusicServiceFactory.java (renamed from src/github/daneren2005/dsub/service/MusicServiceFactory.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/OfflineException.java (renamed from src/github/daneren2005/dsub/service/OfflineException.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/OfflineMusicService.java (renamed from src/github/daneren2005/dsub/service/OfflineMusicService.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java (renamed from src/github/daneren2005/dsub/service/RESTMusicService.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/RemoteController.java (renamed from src/github/daneren2005/dsub/service/RemoteController.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/Scrobbler.java (renamed from src/github/daneren2005/dsub/service/Scrobbler.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/ServerTooOldException.java (renamed from src/github/daneren2005/dsub/service/ServerTooOldException.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/parser/AbstractParser.java (renamed from src/github/daneren2005/dsub/service/parser/AbstractParser.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/parser/AlbumListParser.java (renamed from src/github/daneren2005/dsub/service/parser/AlbumListParser.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/parser/ArtistInfoParser.java (renamed from src/github/daneren2005/dsub/service/parser/ArtistInfoParser.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/parser/BookmarkParser.java (renamed from src/github/daneren2005/dsub/service/parser/BookmarkParser.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/parser/ChatMessageParser.java (renamed from src/github/daneren2005/dsub/service/parser/ChatMessageParser.java)130
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/parser/ErrorParser.java (renamed from src/github/daneren2005/dsub/service/parser/ErrorParser.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/parser/GenreParser.java (renamed from src/github/daneren2005/dsub/service/parser/GenreParser.java)244
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/parser/IndexesParser.java (renamed from src/github/daneren2005/dsub/service/parser/IndexesParser.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/parser/JukeboxStatusParser.java (renamed from src/github/daneren2005/dsub/service/parser/JukeboxStatusParser.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/parser/LicenseParser.java (renamed from src/github/daneren2005/dsub/service/parser/LicenseParser.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/parser/LyricsParser.java (renamed from src/github/daneren2005/dsub/service/parser/LyricsParser.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/parser/MusicDirectoryEntryParser.java (renamed from src/github/daneren2005/dsub/service/parser/MusicDirectoryEntryParser.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/parser/MusicDirectoryParser.java (renamed from src/github/daneren2005/dsub/service/parser/MusicDirectoryParser.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/parser/MusicFoldersParser.java (renamed from src/github/daneren2005/dsub/service/parser/MusicFoldersParser.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/parser/PlayQueueParser.java (renamed from src/github/daneren2005/dsub/service/parser/PlayQueueParser.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/parser/PlaylistParser.java (renamed from src/github/daneren2005/dsub/service/parser/PlaylistParser.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/parser/PlaylistsParser.java (renamed from src/github/daneren2005/dsub/service/parser/PlaylistsParser.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/parser/PodcastChannelParser.java (renamed from src/github/daneren2005/dsub/service/parser/PodcastChannelParser.java)132
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/parser/PodcastEntryParser.java (renamed from src/github/daneren2005/dsub/service/parser/PodcastEntryParser.java)224
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/parser/RandomSongsParser.java (renamed from src/github/daneren2005/dsub/service/parser/RandomSongsParser.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/parser/ScanStatusParser.java (renamed from src/github/daneren2005/dsub/service/parser/ScanStatusParser.java)110
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/parser/SearchResult2Parser.java (renamed from src/github/daneren2005/dsub/service/parser/SearchResult2Parser.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/parser/SearchResultParser.java (renamed from src/github/daneren2005/dsub/service/parser/SearchResultParser.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/parser/ShareParser.java (renamed from src/github/daneren2005/dsub/service/parser/ShareParser.java)250
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/parser/StarredListParser.java (renamed from src/github/daneren2005/dsub/service/parser/StarredListParser.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/parser/SubsonicRESTException.java (renamed from src/github/daneren2005/dsub/service/parser/SubsonicRESTException.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/parser/UserParser.java (renamed from src/github/daneren2005/dsub/service/parser/UserParser.java)146
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/parser/VideosParser.java (renamed from src/github/daneren2005/dsub/service/parser/VideosParser.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/ssl/SSLSocketFactory.java (renamed from src/github/daneren2005/dsub/service/ssl/SSLSocketFactory.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/ssl/TrustManagerDecorator.java (renamed from src/github/daneren2005/dsub/service/ssl/TrustManagerDecorator.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/ssl/TrustSelfSignedStrategy.java (renamed from src/github/daneren2005/dsub/service/ssl/TrustSelfSignedStrategy.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/ssl/TrustStrategy.java (renamed from src/github/daneren2005/dsub/service/ssl/TrustStrategy.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/sync/AuthenticatorService.java (renamed from src/github/daneren2005/dsub/service/sync/AuthenticatorService.java)180
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/sync/MostRecentSyncAdapter.java (renamed from src/github/daneren2005/dsub/service/sync/MostRecentSyncAdapter.java)210
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/sync/MostRecentSyncService.java (renamed from src/github/daneren2005/dsub/service/sync/MostRecentSyncService.java)96
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/sync/PlaylistSyncAdapter.java (renamed from src/github/daneren2005/dsub/service/sync/PlaylistSyncAdapter.java)306
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/sync/PlaylistSyncService.java (renamed from src/github/daneren2005/dsub/service/sync/PlaylistSyncService.java)96
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/sync/PodcastSyncAdapter.java (renamed from src/github/daneren2005/dsub/service/sync/PodcastSyncAdapter.java)226
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/sync/PodcastSyncService.java (renamed from src/github/daneren2005/dsub/service/sync/PodcastSyncService.java)96
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/sync/StarredSyncAdapter.java (renamed from src/github/daneren2005/dsub/service/sync/StarredSyncAdapter.java)160
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/sync/StarredSyncService.java (renamed from src/github/daneren2005/dsub/service/sync/StarredSyncService.java)96
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/sync/SubsonicSyncAdapter.java (renamed from src/github/daneren2005/dsub/service/sync/SubsonicSyncAdapter.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/updates/Updater.java (renamed from src/github/daneren2005/dsub/updates/Updater.java)196
-rw-r--r--app/src/main/java/github/daneren2005/dsub/updates/Updater403.java (renamed from src/github/daneren2005/dsub/updates/Updater403.java)116
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/ArtistRadioBuffer.java (renamed from src/github/daneren2005/dsub/util/ArtistRadioBuffer.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/BackgroundTask.java (renamed from src/github/daneren2005/dsub/util/BackgroundTask.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/CacheCleaner.java (renamed from src/github/daneren2005/dsub/util/CacheCleaner.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/Constants.java (renamed from src/github/daneren2005/dsub/util/Constants.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/FileUtil.java (renamed from src/github/daneren2005/dsub/util/FileUtil.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/ImageLoader.java (renamed from src/github/daneren2005/dsub/util/ImageLoader.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/LoadingTask.java (renamed from src/github/daneren2005/dsub/util/LoadingTask.java)146
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/MediaRouteManager.java (renamed from src/github/daneren2005/dsub/util/MediaRouteManager.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/Notifications.java (renamed from src/github/daneren2005/dsub/util/Notifications.java)696
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/Pair.java (renamed from src/github/daneren2005/dsub/util/Pair.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/ProgressListener.java (renamed from src/github/daneren2005/dsub/util/ProgressListener.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/SettingsBackupAgent.java (renamed from src/github/daneren2005/dsub/util/SettingsBackupAgent.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/ShufflePlayBuffer.java (renamed from src/github/daneren2005/dsub/util/ShufflePlayBuffer.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/SilentBackgroundTask.java (renamed from src/github/daneren2005/dsub/util/SilentBackgroundTask.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/SimpleServiceBinder.java (renamed from src/github/daneren2005/dsub/util/SimpleServiceBinder.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/SyncUtil.java (renamed from src/github/daneren2005/dsub/util/SyncUtil.java)444
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/TabBackgroundTask.java (renamed from src/github/daneren2005/dsub/util/TabBackgroundTask.java)102
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/TimeLimitedCache.java (renamed from src/github/daneren2005/dsub/util/TimeLimitedCache.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/UserUtil.java (renamed from src/github/daneren2005/dsub/util/UserUtil.java)904
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/Util.java (renamed from src/github/daneren2005/dsub/util/Util.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/compat/CastCompat.java (renamed from src/github/daneren2005/dsub/util/compat/CastCompat.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientBase.java (renamed from src/github/daneren2005/dsub/util/compat/RemoteControlClientBase.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientHelper.java (renamed from src/github/daneren2005/dsub/util/compat/RemoteControlClientHelper.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientICS.java (renamed from src/github/daneren2005/dsub/util/compat/RemoteControlClientICS.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientJB.java (renamed from src/github/daneren2005/dsub/util/compat/RemoteControlClientJB.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/tags/Bastp.java (renamed from src/github/daneren2005/dsub/util/tags/Bastp.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/tags/BastpUtil.java (renamed from src/github/daneren2005/dsub/util/tags/BastpUtil.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/tags/Common.java (renamed from src/github/daneren2005/dsub/util/tags/Common.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/tags/FlacFile.java (renamed from src/github/daneren2005/dsub/util/tags/FlacFile.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/tags/ID3v2File.java (renamed from src/github/daneren2005/dsub/util/tags/ID3v2File.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/tags/LameHeader.java (renamed from src/github/daneren2005/dsub/util/tags/LameHeader.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/tags/OggFile.java (renamed from src/github/daneren2005/dsub/util/tags/OggFile.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/AlbumCell.java (renamed from src/github/daneren2005/dsub/view/AlbumCell.java)216
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/AlbumView.java (renamed from src/github/daneren2005/dsub/view/AlbumView.java)214
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/ArtistEntryView.java (renamed from src/github/daneren2005/dsub/view/ArtistEntryView.java)158
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/ArtistView.java (renamed from src/github/daneren2005/dsub/view/ArtistView.java)156
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/AutoRepeatButton.java (renamed from src/github/daneren2005/dsub/view/AutoRepeatButton.java)172
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/ChangeLog.java (renamed from src/github/daneren2005/dsub/view/ChangeLog.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/ErrorDialog.java (renamed from src/github/daneren2005/dsub/view/ErrorDialog.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/FadeOutAnimation.java (renamed from src/github/daneren2005/dsub/view/FadeOutAnimation.java)154
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/GenreView.java (renamed from src/github/daneren2005/dsub/view/GenreView.java)116
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/HeaderGridView.java (renamed from src/github/daneren2005/dsub/view/HeaderGridView.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/MyLeadingMarginSpan2.java (renamed from src/github/daneren2005/dsub/view/MyLeadingMarginSpan2.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/MyViewFlipper.java (renamed from src/github/daneren2005/dsub/view/MyViewFlipper.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/PlaylistSongView.java (renamed from src/github/daneren2005/dsub/view/PlaylistSongView.java)204
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/PlaylistView.java (renamed from src/github/daneren2005/dsub/view/PlaylistView.java)138
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/PodcastChannelView.java (renamed from src/github/daneren2005/dsub/view/PodcastChannelView.java)174
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/RecyclingImageView.java (renamed from src/github/daneren2005/dsub/view/RecyclingImageView.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/SeekBarPreference.java (renamed from src/github/daneren2005/dsub/view/SeekBarPreference.java)312
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/SettingView.java (renamed from src/github/daneren2005/dsub/view/SettingView.java)204
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/ShareView.java (renamed from src/github/daneren2005/dsub/view/ShareView.java)130
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/SongView.java (renamed from src/github/daneren2005/dsub/view/SongView.java)0
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/SquareImageView.java (renamed from src/github/daneren2005/dsub/view/SquareImageView.java)64
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/UnscrollableGridView.java (renamed from src/github/daneren2005/dsub/view/UnscrollableGridView.java)256
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/UpdateView.java (renamed from src/github/daneren2005/dsub/view/UpdateView.java)572
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/UserView.java (renamed from src/github/daneren2005/dsub/view/UserView.java)108
-rw-r--r--app/src/main/res/anim/enter_from_left.xml (renamed from res/anim/enter_from_left.xml)22
-rw-r--r--app/src/main/res/anim/enter_from_right.xml (renamed from res/anim/enter_from_right.xml)22
-rw-r--r--app/src/main/res/anim/exit_to_left.xml (renamed from res/anim/exit_to_left.xml)22
-rw-r--r--app/src/main/res/anim/exit_to_right.xml (renamed from res/anim/exit_to_right.xml)22
-rw-r--r--app/src/main/res/anim/fade_in.xml (renamed from res/anim/fade_in.xml)0
-rw-r--r--app/src/main/res/anim/fade_out.xml (renamed from res/anim/fade_out.xml)0
-rw-r--r--app/src/main/res/anim/push_down_in.xml (renamed from res/anim/push_down_in.xml)0
-rw-r--r--app/src/main/res/anim/push_down_out.xml (renamed from res/anim/push_down_out.xml)0
-rw-r--r--app/src/main/res/anim/push_up_in.xml (renamed from res/anim/push_up_in.xml)0
-rw-r--r--app/src/main/res/anim/push_up_out.xml (renamed from res/anim/push_up_out.xml)0
-rw-r--r--app/src/main/res/drawable-hdpi-v11/notification_close.png (renamed from res/drawable-hdpi-v11/notification_close.png)bin384 -> 384 bytes
-rw-r--r--app/src/main/res/drawable-hdpi-v11/notification_next.png (renamed from res/drawable-hdpi-v11/notification_next.png)bin525 -> 525 bytes
-rw-r--r--app/src/main/res/drawable-hdpi-v11/notification_pause.png (renamed from res/drawable-hdpi-v11/notification_pause.png)bin210 -> 210 bytes
-rw-r--r--app/src/main/res/drawable-hdpi-v11/notification_play.png (renamed from res/drawable-hdpi-v11/notification_play.png)bin385 -> 385 bytes
-rw-r--r--app/src/main/res/drawable-hdpi-v11/notification_previous.png (renamed from res/drawable-hdpi-v11/notification_previous.png)bin541 -> 541 bytes
-rw-r--r--app/src/main/res/drawable-hdpi-v11/stat_notify_download.png (renamed from res/drawable-hdpi-v11/stat_notify_download.png)bin300 -> 300 bytes
-rw-r--r--app/src/main/res/drawable-hdpi-v11/stat_notify_playing.png (renamed from res/drawable-hdpi-v11/stat_notify_playing.png)bin385 -> 385 bytes
-rw-r--r--app/src/main/res/drawable-hdpi-v11/stat_notify_sync.png (renamed from res/drawable-hdpi-v11/stat_notify_sync.png)bin819 -> 819 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/action_toggle_list_dark.png (renamed from res/drawable-hdpi/action_toggle_list_dark.png)bin290 -> 290 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/action_toggle_list_light.png (renamed from res/drawable-hdpi/action_toggle_list_light.png)bin309 -> 309 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/actionbar_button_normal.9.png (renamed from res/drawable-hdpi/actionbar_button_normal.9.png)bin208 -> 208 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/appwidget_art_default.png (renamed from res/drawable-hdpi/appwidget_art_default.png)bin3711 -> 3711 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/appwidget_art_unknown.png (renamed from res/drawable-hdpi/appwidget_art_unknown.png)bin3711 -> 3711 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/appwidget_bg.9.png (renamed from res/drawable-hdpi/appwidget_bg.9.png)bin489 -> 489 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/background.png (renamed from res/drawable-hdpi/background.png)bin1701 -> 1701 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/download_cached.png (renamed from res/drawable-hdpi/download_cached.png)bin982 -> 982 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/download_none_dark.png (renamed from res/drawable-hdpi/download_none_dark.png)bin342 -> 342 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/download_none_light.png (renamed from res/drawable-hdpi/download_none_light.png)bin374 -> 374 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/download_pinned.png (renamed from res/drawable-hdpi/download_pinned.png)bin992 -> 992 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/downloading_dark.png (renamed from res/drawable-hdpi/downloading_dark.png)bin618 -> 618 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/downloading_light.png (renamed from res/drawable-hdpi/downloading_light.png)bin743 -> 743 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_action_add_dark.png (renamed from res/drawable-hdpi/ic_action_add_dark.png)bin289 -> 289 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_action_add_light.png (renamed from res/drawable-hdpi/ic_action_add_light.png)bin308 -> 308 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_action_album.png (renamed from res/drawable-hdpi/ic_action_album.png)bin716 -> 716 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_action_artist.png (renamed from res/drawable-hdpi/ic_action_artist.png)bin685 -> 685 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_action_rating_bad_dark.png (renamed from res/drawable-hdpi/ic_action_rating_bad_dark.png)bin754 -> 754 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_action_rating_bad_light.png (renamed from res/drawable-hdpi/ic_action_rating_bad_light.png)bin892 -> 892 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_action_rating_bad_selected.png (renamed from res/drawable-hdpi/ic_action_rating_bad_selected.png)bin965 -> 965 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_action_rating_good_dark.png (renamed from res/drawable-hdpi/ic_action_rating_good_dark.png)bin744 -> 744 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_action_rating_good_light.png (renamed from res/drawable-hdpi/ic_action_rating_good_light.png)bin873 -> 873 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_action_rating_good_selected.png (renamed from res/drawable-hdpi/ic_action_rating_good_selected.png)bin921 -> 921 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_action_song.png (renamed from res/drawable-hdpi/ic_action_song.png)bin568 -> 568 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_action_volume_dark.png (renamed from res/drawable-hdpi/ic_action_volume_dark.png)bin1365 -> 1365 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_action_volume_light.png (renamed from res/drawable-hdpi/ic_action_volume_light.png)bin1550 -> 1550 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_appwidget_music_next.png (renamed from res/drawable-hdpi/ic_appwidget_music_next.png)bin489 -> 489 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_appwidget_music_pause.png (renamed from res/drawable-hdpi/ic_appwidget_music_pause.png)bin232 -> 232 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_appwidget_music_play.png (renamed from res/drawable-hdpi/ic_appwidget_music_play.png)bin344 -> 344 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_appwidget_music_previous.png (renamed from res/drawable-hdpi/ic_appwidget_music_previous.png)bin666 -> 666 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_menu_add_person_dark.png (renamed from res/drawable-hdpi/ic_menu_add_person_dark.png)bin990 -> 990 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_menu_add_person_light.png (renamed from res/drawable-hdpi/ic_menu_add_person_light.png)bin1191 -> 1191 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_menu_admin_dark.png (renamed from res/drawable-hdpi/ic_menu_admin_dark.png)bin1263 -> 1263 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_menu_admin_light.png (renamed from res/drawable-hdpi/ic_menu_admin_light.png)bin1524 -> 1524 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_menu_bookmark_dark.png (renamed from res/drawable-hdpi/ic_menu_bookmark_dark.png)bin1087 -> 1087 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_menu_bookmark_light.png (renamed from res/drawable-hdpi/ic_menu_bookmark_light.png)bin1292 -> 1292 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_menu_bookmark_selected.png (renamed from res/drawable-hdpi/ic_menu_bookmark_selected.png)bin1374 -> 1374 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_menu_chat_dark.png (renamed from res/drawable-hdpi/ic_menu_chat_dark.png)bin421 -> 421 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_menu_chat_light.png (renamed from res/drawable-hdpi/ic_menu_chat_light.png)bin453 -> 453 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_menu_chat_send_dark.png (renamed from res/drawable-hdpi/ic_menu_chat_send_dark.png)bin602 -> 602 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_menu_chat_send_light.png (renamed from res/drawable-hdpi/ic_menu_chat_send_light.png)bin677 -> 677 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_menu_download_dark.png (renamed from res/drawable-hdpi/ic_menu_download_dark.png)bin540 -> 540 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_menu_download_light.png (renamed from res/drawable-hdpi/ic_menu_download_light.png)bin615 -> 615 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_menu_library_dark.png (renamed from res/drawable-hdpi/ic_menu_library_dark.png)bin617 -> 617 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_menu_library_light.png (renamed from res/drawable-hdpi/ic_menu_library_light.png)bin696 -> 696 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_menu_password_dark.png (renamed from res/drawable-hdpi/ic_menu_password_dark.png)bin843 -> 843 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_menu_password_light.png (renamed from res/drawable-hdpi/ic_menu_password_light.png)bin958 -> 958 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_menu_playlist_dark.png (renamed from res/drawable-hdpi/ic_menu_playlist_dark.png)bin457 -> 457 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_menu_playlist_light.png (renamed from res/drawable-hdpi/ic_menu_playlist_light.png)bin496 -> 496 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_menu_podcast_dark.png (renamed from res/drawable-hdpi/ic_menu_podcast_dark.png)bin1167 -> 1167 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_menu_podcast_light.png (renamed from res/drawable-hdpi/ic_menu_podcast_light.png)bin1410 -> 1410 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_menu_radio_dark.png (renamed from res/drawable-hdpi/ic_menu_radio_dark.png)bin768 -> 768 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_menu_radio_light.png (renamed from res/drawable-hdpi/ic_menu_radio_light.png)bin878 -> 878 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_menu_refresh_dark.png (renamed from res/drawable-hdpi/ic_menu_refresh_dark.png)bin1139 -> 1139 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_menu_refresh_light.png (renamed from res/drawable-hdpi/ic_menu_refresh_light.png)bin1351 -> 1351 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_menu_remove_dark.png (renamed from res/drawable-hdpi/ic_menu_remove_dark.png)bin898 -> 898 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_menu_remove_light.png (renamed from res/drawable-hdpi/ic_menu_remove_light.png)bin1090 -> 1090 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_menu_save_dark.png (renamed from res/drawable-hdpi/ic_menu_save_dark.png)bin553 -> 553 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_menu_save_light.png (renamed from res/drawable-hdpi/ic_menu_save_light.png)bin631 -> 631 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_menu_search_dark.png (renamed from res/drawable-hdpi/ic_menu_search_dark.png)bin1071 -> 1071 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_menu_search_light.png (renamed from res/drawable-hdpi/ic_menu_search_light.png)bin1271 -> 1271 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_menu_settings_dark.png (renamed from res/drawable-hdpi/ic_menu_settings_dark.png)bin557 -> 557 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_menu_settings_light.png (renamed from res/drawable-hdpi/ic_menu_settings_light.png)bin586 -> 586 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_menu_share_dark.png (renamed from res/drawable-hdpi/ic_menu_share_dark.png)bin737 -> 737 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_menu_share_light.png (renamed from res/drawable-hdpi/ic_menu_share_light.png)bin825 -> 825 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_menu_shuffle_dark.png (renamed from res/drawable-hdpi/ic_menu_shuffle_dark.png)bin985 -> 985 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_menu_shuffle_light.png (renamed from res/drawable-hdpi/ic_menu_shuffle_light.png)bin1132 -> 1132 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_number_border.png (renamed from res/drawable-hdpi/ic_number_border.png)bin2058 -> 2058 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_social_person.png (renamed from res/drawable-hdpi/ic_social_person.png)bin4518 -> 4518 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_stat_star.png (renamed from res/drawable-hdpi/ic_stat_star.png)bin826 -> 826 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/launch.png (renamed from res/drawable-hdpi/launch.png)bin7496 -> 7496 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/main_offline_dark.png (renamed from res/drawable-hdpi/main_offline_dark.png)bin631 -> 631 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/main_offline_light.png (renamed from res/drawable-hdpi/main_offline_light.png)bin746 -> 746 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/main_select_server_dark.png (renamed from res/drawable-hdpi/main_select_server_dark.png)bin720 -> 720 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/main_select_server_light.png (renamed from res/drawable-hdpi/main_select_server_light.png)bin799 -> 799 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/media_backward_dark.png (renamed from res/drawable-hdpi/media_backward_dark.png)bin579 -> 579 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/media_backward_light.png (renamed from res/drawable-hdpi/media_backward_light.png)bin627 -> 627 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/media_forward_dark.png (renamed from res/drawable-hdpi/media_forward_dark.png)bin559 -> 559 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/media_forward_light.png (renamed from res/drawable-hdpi/media_forward_light.png)bin631 -> 631 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/media_pause_dark.png (renamed from res/drawable-hdpi/media_pause_dark.png)bin276 -> 276 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/media_pause_light.png (renamed from res/drawable-hdpi/media_pause_light.png)bin301 -> 301 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/media_repeat_all.png (renamed from res/drawable-hdpi/media_repeat_all.png)bin5090 -> 5090 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/media_repeat_off.png (renamed from res/drawable-hdpi/media_repeat_off.png)bin1079 -> 1079 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/media_repeat_off_light.png (renamed from res/drawable-hdpi/media_repeat_off_light.png)bin1512 -> 1512 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/media_repeat_single.png (renamed from res/drawable-hdpi/media_repeat_single.png)bin5564 -> 5564 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/media_start_dark.png (renamed from res/drawable-hdpi/media_start_dark.png)bin449 -> 449 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/media_start_light.png (renamed from res/drawable-hdpi/media_start_light.png)bin511 -> 511 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/media_stop_dark.png (renamed from res/drawable-hdpi/media_stop_dark.png)bin265 -> 265 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/media_stop_light.png (renamed from res/drawable-hdpi/media_stop_light.png)bin274 -> 274 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/notification_close.png (renamed from res/drawable-hdpi/notification_close.png)bin501 -> 501 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/notification_next.png (renamed from res/drawable-hdpi/notification_next.png)bin651 -> 651 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/notification_pause.png (renamed from res/drawable-hdpi/notification_pause.png)bin459 -> 459 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/notification_play.png (renamed from res/drawable-hdpi/notification_play.png)bin599 -> 599 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/notification_previous.png (renamed from res/drawable-hdpi/notification_previous.png)bin633 -> 633 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/now_playing.png (renamed from res/drawable-hdpi/now_playing.png)bin599 -> 599 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/stat_notify_download.png (renamed from res/drawable-hdpi/stat_notify_download.png)bin350 -> 350 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/stat_notify_playing.png (renamed from res/drawable-hdpi/stat_notify_playing.png)bin599 -> 599 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/stat_notify_sync.png (renamed from res/drawable-hdpi/stat_notify_sync.png)bin894 -> 894 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/toast_frame.9.png (renamed from res/drawable-hdpi/toast_frame.9.png)bin2461 -> 2461 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/unknown_album.png (renamed from res/drawable-hdpi/unknown_album.png)bin7127 -> 7127 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/unknown_album_large.png (renamed from res/drawable-hdpi/unknown_album_large.png)bin41908 -> 41908 bytes
-rw-r--r--app/src/main/res/drawable-large/unknown_album.png (renamed from res/drawable-large/unknown_album.png)bin14939 -> 14939 bytes
-rw-r--r--app/src/main/res/drawable-mdpi-v11/notification_close.png (renamed from res/drawable-mdpi-v11/notification_close.png)bin241 -> 241 bytes
-rw-r--r--app/src/main/res/drawable-mdpi-v11/notification_next.png (renamed from res/drawable-mdpi-v11/notification_next.png)bin341 -> 341 bytes
-rw-r--r--app/src/main/res/drawable-mdpi-v11/notification_pause.png (renamed from res/drawable-mdpi-v11/notification_pause.png)bin156 -> 156 bytes
-rw-r--r--app/src/main/res/drawable-mdpi-v11/notification_play.png (renamed from res/drawable-mdpi-v11/notification_play.png)bin280 -> 280 bytes
-rw-r--r--app/src/main/res/drawable-mdpi-v11/notification_previous.png (renamed from res/drawable-mdpi-v11/notification_previous.png)bin355 -> 355 bytes
-rw-r--r--app/src/main/res/drawable-mdpi-v11/stat_notify_download.png (renamed from res/drawable-mdpi-v11/stat_notify_download.png)bin234 -> 234 bytes
-rw-r--r--app/src/main/res/drawable-mdpi-v11/stat_notify_playing.png (renamed from res/drawable-mdpi-v11/stat_notify_playing.png)bin280 -> 280 bytes
-rw-r--r--app/src/main/res/drawable-mdpi-v11/stat_notify_sync.png (renamed from res/drawable-mdpi-v11/stat_notify_sync.png)bin623 -> 623 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/action_toggle_list_dark.png (renamed from res/drawable-mdpi/action_toggle_list_dark.png)bin204 -> 204 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/action_toggle_list_light.png (renamed from res/drawable-mdpi/action_toggle_list_light.png)bin225 -> 225 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/download_cached.png (renamed from res/drawable-mdpi/download_cached.png)bin704 -> 704 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/download_none_dark.png (renamed from res/drawable-mdpi/download_none_dark.png)bin216 -> 216 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/download_none_light.png (renamed from res/drawable-mdpi/download_none_light.png)bin239 -> 239 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/download_pinned.png (renamed from res/drawable-mdpi/download_pinned.png)bin673 -> 673 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/downloading_dark.png (renamed from res/drawable-mdpi/downloading_dark.png)bin447 -> 447 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/downloading_light.png (renamed from res/drawable-mdpi/downloading_light.png)bin527 -> 527 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_action_add_dark.png (renamed from res/drawable-mdpi/ic_action_add_dark.png)bin171 -> 171 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_action_add_light.png (renamed from res/drawable-mdpi/ic_action_add_light.png)bin183 -> 183 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_action_album.png (renamed from res/drawable-mdpi/ic_action_album.png)bin474 -> 474 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_action_artist.png (renamed from res/drawable-mdpi/ic_action_artist.png)bin505 -> 505 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_action_rating_bad_dark.png (renamed from res/drawable-mdpi/ic_action_rating_bad_dark.png)bin460 -> 460 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_action_rating_bad_light.png (renamed from res/drawable-mdpi/ic_action_rating_bad_light.png)bin543 -> 543 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_action_rating_bad_selected.png (renamed from res/drawable-mdpi/ic_action_rating_bad_selected.png)bin584 -> 584 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_action_rating_good_dark.png (renamed from res/drawable-mdpi/ic_action_rating_good_dark.png)bin456 -> 456 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_action_rating_good_light.png (renamed from res/drawable-mdpi/ic_action_rating_good_light.png)bin541 -> 541 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_action_rating_good_selected.png (renamed from res/drawable-mdpi/ic_action_rating_good_selected.png)bin581 -> 581 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_action_song.png (renamed from res/drawable-mdpi/ic_action_song.png)bin431 -> 431 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_action_volume_dark.png (renamed from res/drawable-mdpi/ic_action_volume_dark.png)bin820 -> 820 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_action_volume_light.png (renamed from res/drawable-mdpi/ic_action_volume_light.png)bin974 -> 974 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_menu_add_person_dark.png (renamed from res/drawable-mdpi/ic_menu_add_person_dark.png)bin652 -> 652 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_menu_add_person_light.png (renamed from res/drawable-mdpi/ic_menu_add_person_light.png)bin811 -> 811 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_menu_admin_dark.png (renamed from res/drawable-mdpi/ic_menu_admin_dark.png)bin781 -> 781 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_menu_admin_light.png (renamed from res/drawable-mdpi/ic_menu_admin_light.png)bin966 -> 966 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_menu_bookmark_dark.png (renamed from res/drawable-mdpi/ic_menu_bookmark_dark.png)bin658 -> 658 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_menu_bookmark_light.png (renamed from res/drawable-mdpi/ic_menu_bookmark_light.png)bin782 -> 782 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_menu_bookmark_selected.png (renamed from res/drawable-mdpi/ic_menu_bookmark_selected.png)bin849 -> 849 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_menu_chat_dark.png (renamed from res/drawable-mdpi/ic_menu_chat_dark.png)bin277 -> 277 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_menu_chat_light.png (renamed from res/drawable-mdpi/ic_menu_chat_light.png)bin311 -> 311 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_menu_chat_send_dark.png (renamed from res/drawable-mdpi/ic_menu_chat_send_dark.png)bin366 -> 366 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_menu_chat_send_light.png (renamed from res/drawable-mdpi/ic_menu_chat_send_light.png)bin394 -> 394 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_menu_download_dark.png (renamed from res/drawable-mdpi/ic_menu_download_dark.png)bin379 -> 379 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_menu_download_light.png (renamed from res/drawable-mdpi/ic_menu_download_light.png)bin444 -> 444 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_menu_library_dark.png (renamed from res/drawable-mdpi/ic_menu_library_dark.png)bin420 -> 420 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_menu_library_light.png (renamed from res/drawable-mdpi/ic_menu_library_light.png)bin492 -> 492 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_menu_password_dark.png (renamed from res/drawable-mdpi/ic_menu_password_dark.png)bin554 -> 554 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_menu_password_light.png (renamed from res/drawable-mdpi/ic_menu_password_light.png)bin676 -> 676 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_menu_playlist_dark.png (renamed from res/drawable-mdpi/ic_menu_playlist_dark.png)bin315 -> 315 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_menu_playlist_light.png (renamed from res/drawable-mdpi/ic_menu_playlist_light.png)bin364 -> 364 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_menu_podcast_dark.png (renamed from res/drawable-mdpi/ic_menu_podcast_dark.png)bin750 -> 750 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_menu_podcast_light.png (renamed from res/drawable-mdpi/ic_menu_podcast_light.png)bin862 -> 862 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_menu_radio_dark.png (renamed from res/drawable-mdpi/ic_menu_radio_dark.png)bin578 -> 578 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_menu_radio_light.png (renamed from res/drawable-mdpi/ic_menu_radio_light.png)bin675 -> 675 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_menu_refresh_dark.png (renamed from res/drawable-mdpi/ic_menu_refresh_dark.png)bin748 -> 748 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_menu_refresh_light.png (renamed from res/drawable-mdpi/ic_menu_refresh_light.png)bin914 -> 914 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_menu_remove_dark.png (renamed from res/drawable-mdpi/ic_menu_remove_dark.png)bin576 -> 576 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_menu_remove_light.png (renamed from res/drawable-mdpi/ic_menu_remove_light.png)bin689 -> 689 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_menu_save_dark.png (renamed from res/drawable-mdpi/ic_menu_save_dark.png)bin406 -> 406 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_menu_save_light.png (renamed from res/drawable-mdpi/ic_menu_save_light.png)bin481 -> 481 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_menu_search_dark.png (renamed from res/drawable-mdpi/ic_menu_search_dark.png)bin655 -> 655 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_menu_search_light.png (renamed from res/drawable-mdpi/ic_menu_search_light.png)bin794 -> 794 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_menu_settings_dark.png (renamed from res/drawable-mdpi/ic_menu_settings_dark.png)bin365 -> 365 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_menu_settings_light.png (renamed from res/drawable-mdpi/ic_menu_settings_light.png)bin365 -> 365 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_menu_share_dark.png (renamed from res/drawable-mdpi/ic_menu_share_dark.png)bin455 -> 455 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_menu_share_light.png (renamed from res/drawable-mdpi/ic_menu_share_light.png)bin534 -> 534 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_menu_shuffle_dark.png (renamed from res/drawable-mdpi/ic_menu_shuffle_dark.png)bin653 -> 653 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_menu_shuffle_light.png (renamed from res/drawable-mdpi/ic_menu_shuffle_light.png)bin725 -> 725 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_number_border.png (renamed from res/drawable-mdpi/ic_number_border.png)bin1206 -> 1206 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_social_person.png (renamed from res/drawable-mdpi/ic_social_person.png)bin2834 -> 2834 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/launch.png (renamed from res/drawable-mdpi/launch.png)bin4077 -> 4077 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/main_offline_dark.png (renamed from res/drawable-mdpi/main_offline_dark.png)bin408 -> 408 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/main_offline_light.png (renamed from res/drawable-mdpi/main_offline_light.png)bin456 -> 456 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/main_select_server_dark.png (renamed from res/drawable-mdpi/main_select_server_dark.png)bin434 -> 434 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/main_select_server_light.png (renamed from res/drawable-mdpi/main_select_server_light.png)bin502 -> 502 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/media_backward_dark.png (renamed from res/drawable-mdpi/media_backward_dark.png)bin378 -> 378 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/media_backward_light.png (renamed from res/drawable-mdpi/media_backward_light.png)bin412 -> 412 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/media_forward_dark.png (renamed from res/drawable-mdpi/media_forward_dark.png)bin372 -> 372 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/media_forward_light.png (renamed from res/drawable-mdpi/media_forward_light.png)bin417 -> 417 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/media_pause_dark.png (renamed from res/drawable-mdpi/media_pause_dark.png)bin169 -> 169 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/media_pause_light.png (renamed from res/drawable-mdpi/media_pause_light.png)bin192 -> 192 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/media_start_dark.png (renamed from res/drawable-mdpi/media_start_dark.png)bin301 -> 301 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/media_start_light.png (renamed from res/drawable-mdpi/media_start_light.png)bin335 -> 335 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/media_stop_dark.png (renamed from res/drawable-mdpi/media_stop_dark.png)bin154 -> 154 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/media_stop_light.png (renamed from res/drawable-mdpi/media_stop_light.png)bin162 -> 162 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/notification_close.png (renamed from res/drawable-mdpi/notification_close.png)bin337 -> 337 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/notification_next.png (renamed from res/drawable-mdpi/notification_next.png)bin460 -> 460 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/notification_pause.png (renamed from res/drawable-mdpi/notification_pause.png)bin361 -> 361 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/notification_play.png (renamed from res/drawable-mdpi/notification_play.png)bin417 -> 417 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/notification_previous.png (renamed from res/drawable-mdpi/notification_previous.png)bin476 -> 476 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/now_playing.png (renamed from res/drawable-mdpi/now_playing.png)bin417 -> 417 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/stat_notify_download.png (renamed from res/drawable-mdpi/stat_notify_download.png)bin272 -> 272 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/stat_notify_playing.png (renamed from res/drawable-mdpi/stat_notify_playing.png)bin417 -> 417 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/stat_notify_sync.png (renamed from res/drawable-mdpi/stat_notify_sync.png)bin575 -> 575 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi-v11/notification_close.png (renamed from res/drawable-xhdpi-v11/notification_close.png)bin491 -> 491 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi-v11/notification_next.png (renamed from res/drawable-xhdpi-v11/notification_next.png)bin731 -> 731 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi-v11/notification_pause.png (renamed from res/drawable-xhdpi-v11/notification_pause.png)bin257 -> 257 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi-v11/notification_play.png (renamed from res/drawable-xhdpi-v11/notification_play.png)bin493 -> 493 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi-v11/notification_previous.png (renamed from res/drawable-xhdpi-v11/notification_previous.png)bin750 -> 750 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi-v11/stat_notify_download.png (renamed from res/drawable-xhdpi-v11/stat_notify_download.png)bin379 -> 379 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi-v11/stat_notify_playing.png (renamed from res/drawable-xhdpi-v11/stat_notify_playing.png)bin493 -> 493 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi-v11/stat_notify_sync.png (renamed from res/drawable-xhdpi-v11/stat_notify_sync.png)bin1205 -> 1205 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/action_toggle_list_dark.png (renamed from res/drawable-xhdpi/action_toggle_list_dark.png)bin312 -> 312 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/action_toggle_list_light.png (renamed from res/drawable-xhdpi/action_toggle_list_light.png)bin320 -> 320 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/download_cached.png (renamed from res/drawable-xhdpi/download_cached.png)bin1300 -> 1300 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/download_none_dark.png (renamed from res/drawable-xhdpi/download_none_dark.png)bin355 -> 355 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/download_none_light.png (renamed from res/drawable-xhdpi/download_none_light.png)bin375 -> 375 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/download_pinned.png (renamed from res/drawable-xhdpi/download_pinned.png)bin1278 -> 1278 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/downloading_dark.png (renamed from res/drawable-xhdpi/downloading_dark.png)bin869 -> 869 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/downloading_light.png (renamed from res/drawable-xhdpi/downloading_light.png)bin1017 -> 1017 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_action_add_dark.png (renamed from res/drawable-xhdpi/ic_action_add_dark.png)bin336 -> 336 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_action_add_light.png (renamed from res/drawable-xhdpi/ic_action_add_light.png)bin349 -> 349 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_action_album.png (renamed from res/drawable-xhdpi/ic_action_album.png)bin1023 -> 1023 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_action_artist.png (renamed from res/drawable-xhdpi/ic_action_artist.png)bin820 -> 820 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_action_rating_bad_dark.png (renamed from res/drawable-xhdpi/ic_action_rating_bad_dark.png)bin961 -> 961 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_action_rating_bad_light.png (renamed from res/drawable-xhdpi/ic_action_rating_bad_light.png)bin1141 -> 1141 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_action_rating_bad_selected.png (renamed from res/drawable-xhdpi/ic_action_rating_bad_selected.png)bin1197 -> 1197 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_action_rating_good_dark.png (renamed from res/drawable-xhdpi/ic_action_rating_good_dark.png)bin946 -> 946 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_action_rating_good_light.png (renamed from res/drawable-xhdpi/ic_action_rating_good_light.png)bin1129 -> 1129 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_action_rating_good_selected.png (renamed from res/drawable-xhdpi/ic_action_rating_good_selected.png)bin1176 -> 1176 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_action_song.png (renamed from res/drawable-xhdpi/ic_action_song.png)bin705 -> 705 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_action_volume_dark.png (renamed from res/drawable-xhdpi/ic_action_volume_dark.png)bin1916 -> 1916 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_action_volume_light.png (renamed from res/drawable-xhdpi/ic_action_volume_light.png)bin2180 -> 2180 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_menu_add_person_dark.png (renamed from res/drawable-xhdpi/ic_menu_add_person_dark.png)bin1284 -> 1284 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_menu_add_person_light.png (renamed from res/drawable-xhdpi/ic_menu_add_person_light.png)bin1534 -> 1534 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_menu_admin_dark.png (renamed from res/drawable-xhdpi/ic_menu_admin_dark.png)bin1807 -> 1807 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_menu_admin_light.png (renamed from res/drawable-xhdpi/ic_menu_admin_light.png)bin2119 -> 2119 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_menu_bookmark_dark.png (renamed from res/drawable-xhdpi/ic_menu_bookmark_dark.png)bin1442 -> 1442 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_menu_bookmark_light.png (renamed from res/drawable-xhdpi/ic_menu_bookmark_light.png)bin1665 -> 1665 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_menu_bookmark_selected.png (renamed from res/drawable-xhdpi/ic_menu_bookmark_selected.png)bin1780 -> 1780 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_menu_chat_dark.png (renamed from res/drawable-xhdpi/ic_menu_chat_dark.png)bin472 -> 472 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_menu_chat_light.png (renamed from res/drawable-xhdpi/ic_menu_chat_light.png)bin517 -> 517 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_menu_chat_send_dark.png (renamed from res/drawable-xhdpi/ic_menu_chat_send_dark.png)bin743 -> 743 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_menu_chat_send_light.png (renamed from res/drawable-xhdpi/ic_menu_chat_send_light.png)bin799 -> 799 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_menu_download_dark.png (renamed from res/drawable-xhdpi/ic_menu_download_dark.png)bin695 -> 695 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_menu_download_light.png (renamed from res/drawable-xhdpi/ic_menu_download_light.png)bin797 -> 797 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_menu_library_dark.png (renamed from res/drawable-xhdpi/ic_menu_library_dark.png)bin820 -> 820 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_menu_library_light.png (renamed from res/drawable-xhdpi/ic_menu_library_light.png)bin980 -> 980 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_menu_password_dark.png (renamed from res/drawable-xhdpi/ic_menu_password_dark.png)bin1067 -> 1067 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_menu_password_light.png (renamed from res/drawable-xhdpi/ic_menu_password_light.png)bin1234 -> 1234 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_menu_playlist_dark.png (renamed from res/drawable-xhdpi/ic_menu_playlist_dark.png)bin508 -> 508 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_menu_playlist_light.png (renamed from res/drawable-xhdpi/ic_menu_playlist_light.png)bin555 -> 555 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_menu_podcast_dark.png (renamed from res/drawable-xhdpi/ic_menu_podcast_dark.png)bin1553 -> 1553 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_menu_podcast_light.png (renamed from res/drawable-xhdpi/ic_menu_podcast_light.png)bin1787 -> 1787 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_menu_radio_dark.png (renamed from res/drawable-xhdpi/ic_menu_radio_dark.png)bin1131 -> 1131 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_menu_radio_light.png (renamed from res/drawable-xhdpi/ic_menu_radio_light.png)bin1376 -> 1376 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_menu_refresh_dark.png (renamed from res/drawable-xhdpi/ic_menu_refresh_dark.png)bin1520 -> 1520 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_menu_refresh_light.png (renamed from res/drawable-xhdpi/ic_menu_refresh_light.png)bin1802 -> 1802 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_menu_remove_dark.png (renamed from res/drawable-xhdpi/ic_menu_remove_dark.png)bin1146 -> 1146 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_menu_remove_light.png (renamed from res/drawable-xhdpi/ic_menu_remove_light.png)bin1394 -> 1394 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_menu_save_dark.png (renamed from res/drawable-xhdpi/ic_menu_save_dark.png)bin644 -> 644 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_menu_save_light.png (renamed from res/drawable-xhdpi/ic_menu_save_light.png)bin735 -> 735 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_menu_search_dark.png (renamed from res/drawable-xhdpi/ic_menu_search_dark.png)bin1445 -> 1445 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_menu_search_light.png (renamed from res/drawable-xhdpi/ic_menu_search_light.png)bin1701 -> 1701 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_menu_settings_dark.png (renamed from res/drawable-xhdpi/ic_menu_settings_dark.png)bin708 -> 708 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_menu_settings_light.png (renamed from res/drawable-xhdpi/ic_menu_settings_light.png)bin748 -> 748 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_menu_share_dark.png (renamed from res/drawable-xhdpi/ic_menu_share_dark.png)bin947 -> 947 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_menu_share_light.png (renamed from res/drawable-xhdpi/ic_menu_share_light.png)bin1101 -> 1101 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_menu_shuffle_dark.png (renamed from res/drawable-xhdpi/ic_menu_shuffle_dark.png)bin1400 -> 1400 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_menu_shuffle_light.png (renamed from res/drawable-xhdpi/ic_menu_shuffle_light.png)bin1637 -> 1637 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_number_border.png (renamed from res/drawable-xhdpi/ic_number_border.png)bin2798 -> 2798 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_social_person.png (renamed from res/drawable-xhdpi/ic_social_person.png)bin5960 -> 5960 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/launch.png (renamed from res/drawable-xhdpi/launch.png)bin10916 -> 10916 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/main_offline_dark.png (renamed from res/drawable-xhdpi/main_offline_dark.png)bin818 -> 818 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/main_offline_light.png (renamed from res/drawable-xhdpi/main_offline_light.png)bin976 -> 976 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/main_select_server_dark.png (renamed from res/drawable-xhdpi/main_select_server_dark.png)bin939 -> 939 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/main_select_server_light.png (renamed from res/drawable-xhdpi/main_select_server_light.png)bin1079 -> 1079 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/media_backward_dark.png (renamed from res/drawable-xhdpi/media_backward_dark.png)bin778 -> 778 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/media_backward_light.png (renamed from res/drawable-xhdpi/media_backward_light.png)bin860 -> 860 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/media_forward_dark.png (renamed from res/drawable-xhdpi/media_forward_dark.png)bin716 -> 716 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/media_forward_light.png (renamed from res/drawable-xhdpi/media_forward_light.png)bin834 -> 834 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/media_pause_dark.png (renamed from res/drawable-xhdpi/media_pause_dark.png)bin314 -> 314 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/media_pause_light.png (renamed from res/drawable-xhdpi/media_pause_light.png)bin333 -> 333 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/media_start_dark.png (renamed from res/drawable-xhdpi/media_start_dark.png)bin580 -> 580 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/media_start_light.png (renamed from res/drawable-xhdpi/media_start_light.png)bin649 -> 649 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/media_stop_dark.png (renamed from res/drawable-xhdpi/media_stop_dark.png)bin298 -> 298 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/media_stop_light.png (renamed from res/drawable-xhdpi/media_stop_light.png)bin307 -> 307 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/notification_close.png (renamed from res/drawable-xhdpi/notification_close.png)bin538 -> 538 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/notification_next.png (renamed from res/drawable-xhdpi/notification_next.png)bin886 -> 886 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/notification_pause.png (renamed from res/drawable-xhdpi/notification_pause.png)bin529 -> 529 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/notification_play.png (renamed from res/drawable-xhdpi/notification_play.png)bin753 -> 753 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/notification_previous.png (renamed from res/drawable-xhdpi/notification_previous.png)bin891 -> 891 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/now_playing.png (renamed from res/drawable-xhdpi/now_playing.png)bin753 -> 753 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/stat_notify_download.png (renamed from res/drawable-xhdpi/stat_notify_download.png)bin404 -> 404 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/stat_notify_playing.png (renamed from res/drawable-xhdpi/stat_notify_playing.png)bin753 -> 753 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/stat_notify_sync.png (renamed from res/drawable-xhdpi/stat_notify_sync.png)bin1058 -> 1058 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi-v11/notification_close.png (renamed from res/drawable-xxhdpi-v11/notification_close.png)bin712 -> 712 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi-v11/notification_next.png (renamed from res/drawable-xxhdpi-v11/notification_next.png)bin1105 -> 1105 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi-v11/notification_pause.png (renamed from res/drawable-xxhdpi-v11/notification_pause.png)bin358 -> 358 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi-v11/notification_play.png (renamed from res/drawable-xxhdpi-v11/notification_play.png)bin781 -> 781 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi-v11/notification_previous.png (renamed from res/drawable-xxhdpi-v11/notification_previous.png)bin1143 -> 1143 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi-v11/stat_notify_download.png (renamed from res/drawable-xxhdpi-v11/stat_notify_download.png)bin531 -> 531 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi-v11/stat_notify_playing.png (renamed from res/drawable-xxhdpi-v11/stat_notify_playing.png)bin781 -> 781 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi-v11/stat_notify_sync.png (renamed from res/drawable-xxhdpi-v11/stat_notify_sync.png)bin2198 -> 2198 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/action_toggle_list_dark.png (renamed from res/drawable-xxhdpi/action_toggle_list_dark.png)bin608 -> 608 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/action_toggle_list_light.png (renamed from res/drawable-xxhdpi/action_toggle_list_light.png)bin630 -> 630 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/download_cached.png (renamed from res/drawable-xxhdpi/download_cached.png)bin1906 -> 1906 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/download_none_dark.png (renamed from res/drawable-xxhdpi/download_none_dark.png)bin617 -> 617 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/download_none_light.png (renamed from res/drawable-xxhdpi/download_none_light.png)bin639 -> 639 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/download_pinned.png (renamed from res/drawable-xxhdpi/download_pinned.png)bin1899 -> 1899 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/downloading_dark.png (renamed from res/drawable-xxhdpi/downloading_dark.png)bin1353 -> 1353 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/downloading_light.png (renamed from res/drawable-xxhdpi/downloading_light.png)bin1542 -> 1542 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_action_add_dark.png (renamed from res/drawable-xxhdpi/ic_action_add_dark.png)bin645 -> 645 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_action_add_light.png (renamed from res/drawable-xxhdpi/ic_action_add_light.png)bin636 -> 636 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_action_rating_bad_dark.png (renamed from res/drawable-xxhdpi/ic_action_rating_bad_dark.png)bin1540 -> 1540 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_action_rating_bad_light.png (renamed from res/drawable-xxhdpi/ic_action_rating_bad_light.png)bin1822 -> 1822 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_action_rating_bad_selected.png (renamed from res/drawable-xxhdpi/ic_action_rating_bad_selected.png)bin1953 -> 1953 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_action_rating_good_dark.png (renamed from res/drawable-xxhdpi/ic_action_rating_good_dark.png)bin1582 -> 1582 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_action_rating_good_light.png (renamed from res/drawable-xxhdpi/ic_action_rating_good_light.png)bin1835 -> 1835 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_action_rating_good_selected.png (renamed from res/drawable-xxhdpi/ic_action_rating_good_selected.png)bin1915 -> 1915 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_action_volume_dark.png (renamed from res/drawable-xxhdpi/ic_action_volume_dark.png)bin3148 -> 3148 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_action_volume_light.png (renamed from res/drawable-xxhdpi/ic_action_volume_light.png)bin3473 -> 3473 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_menu_add_person_dark.png (renamed from res/drawable-xxhdpi/ic_menu_add_person_dark.png)bin2036 -> 2036 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_menu_add_person_light.png (renamed from res/drawable-xxhdpi/ic_menu_add_person_light.png)bin2350 -> 2350 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_menu_admin_dark.png (renamed from res/drawable-xxhdpi/ic_menu_admin_dark.png)bin2992 -> 2992 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_menu_admin_light.png (renamed from res/drawable-xxhdpi/ic_menu_admin_light.png)bin3467 -> 3467 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_menu_bookmark_dark.png (renamed from res/drawable-xxhdpi/ic_menu_bookmark_dark.png)bin2194 -> 2194 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_menu_bookmark_light.png (renamed from res/drawable-xxhdpi/ic_menu_bookmark_light.png)bin2474 -> 2474 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_menu_bookmark_selected.png (renamed from res/drawable-xxhdpi/ic_menu_bookmark_selected.png)bin2635 -> 2635 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_menu_chat_dark.png (renamed from res/drawable-xxhdpi/ic_menu_chat_dark.png)bin723 -> 723 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_menu_chat_light.png (renamed from res/drawable-xxhdpi/ic_menu_chat_light.png)bin771 -> 771 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_menu_chat_send_dark.png (renamed from res/drawable-xxhdpi/ic_menu_chat_send_dark.png)bin1326 -> 1326 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_menu_chat_send_light.png (renamed from res/drawable-xxhdpi/ic_menu_chat_send_light.png)bin1608 -> 1608 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_menu_download_dark.png (renamed from res/drawable-xxhdpi/ic_menu_download_dark.png)bin1072 -> 1072 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_menu_download_light.png (renamed from res/drawable-xxhdpi/ic_menu_download_light.png)bin1230 -> 1230 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_menu_library_dark.png (renamed from res/drawable-xxhdpi/ic_menu_library_dark.png)bin1357 -> 1357 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_menu_library_light.png (renamed from res/drawable-xxhdpi/ic_menu_library_light.png)bin1579 -> 1579 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_menu_password_dark.png (renamed from res/drawable-xxhdpi/ic_menu_password_dark.png)bin1610 -> 1610 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_menu_password_light.png (renamed from res/drawable-xxhdpi/ic_menu_password_light.png)bin1852 -> 1852 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_menu_playlist_dark.png (renamed from res/drawable-xxhdpi/ic_menu_playlist_dark.png)bin783 -> 783 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_menu_playlist_light.png (renamed from res/drawable-xxhdpi/ic_menu_playlist_light.png)bin840 -> 840 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_menu_podcast_dark.png (renamed from res/drawable-xxhdpi/ic_menu_podcast_dark.png)bin2439 -> 2439 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_menu_podcast_light.png (renamed from res/drawable-xxhdpi/ic_menu_podcast_light.png)bin2798 -> 2798 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_menu_radio_dark.png (renamed from res/drawable-xxhdpi/ic_menu_radio_dark.png)bin1992 -> 1992 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_menu_radio_light.png (renamed from res/drawable-xxhdpi/ic_menu_radio_light.png)bin2310 -> 2310 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_menu_refresh_dark.png (renamed from res/drawable-xxhdpi/ic_menu_refresh_dark.png)bin2453 -> 2453 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_menu_refresh_light.png (renamed from res/drawable-xxhdpi/ic_menu_refresh_light.png)bin2952 -> 2952 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_menu_remove_dark.png (renamed from res/drawable-xxhdpi/ic_menu_remove_dark.png)bin1843 -> 1843 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_menu_remove_light.png (renamed from res/drawable-xxhdpi/ic_menu_remove_light.png)bin2164 -> 2164 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_menu_save_dark.png (renamed from res/drawable-xxhdpi/ic_menu_save_dark.png)bin977 -> 977 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_menu_save_light.png (renamed from res/drawable-xxhdpi/ic_menu_save_light.png)bin1076 -> 1076 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_menu_search_dark.png (renamed from res/drawable-xxhdpi/ic_menu_search_dark.png)bin2258 -> 2258 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_menu_search_light.png (renamed from res/drawable-xxhdpi/ic_menu_search_light.png)bin2571 -> 2571 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_menu_settings_dark.png (renamed from res/drawable-xxhdpi/ic_menu_settings_dark.png)bin1221 -> 1221 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_menu_settings_light.png (renamed from res/drawable-xxhdpi/ic_menu_settings_light.png)bin1194 -> 1194 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_menu_share_dark.png (renamed from res/drawable-xxhdpi/ic_menu_share_dark.png)bin1592 -> 1592 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_menu_share_light.png (renamed from res/drawable-xxhdpi/ic_menu_share_light.png)bin1790 -> 1790 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_menu_shuffle_dark.png (renamed from res/drawable-xxhdpi/ic_menu_shuffle_dark.png)bin2268 -> 2268 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_menu_shuffle_light.png (renamed from res/drawable-xxhdpi/ic_menu_shuffle_light.png)bin2529 -> 2529 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_number_border.png (renamed from res/drawable-xxhdpi/ic_number_border.png)bin5066 -> 5066 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_social_person.png (renamed from res/drawable-xxhdpi/ic_social_person.png)bin9169 -> 9169 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/launch.png (renamed from res/drawable-xxhdpi/launch.png)bin20218 -> 20218 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/main_offline_dark.png (renamed from res/drawable-xxhdpi/main_offline_dark.png)bin1265 -> 1265 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/main_offline_light.png (renamed from res/drawable-xxhdpi/main_offline_light.png)bin1466 -> 1466 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/main_select_server_dark.png (renamed from res/drawable-xxhdpi/main_select_server_dark.png)bin1396 -> 1396 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/main_select_server_light.png (renamed from res/drawable-xxhdpi/main_select_server_light.png)bin1622 -> 1622 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/media_backward_dark.png (renamed from res/drawable-xxhdpi/media_backward_dark.png)bin1282 -> 1282 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/media_backward_light.png (renamed from res/drawable-xxhdpi/media_backward_light.png)bin1443 -> 1443 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/media_forward_dark.png (renamed from res/drawable-xxhdpi/media_forward_dark.png)bin1258 -> 1258 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/media_forward_light.png (renamed from res/drawable-xxhdpi/media_forward_light.png)bin1388 -> 1388 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/media_pause_dark.png (renamed from res/drawable-xxhdpi/media_pause_dark.png)bin612 -> 612 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/media_pause_light.png (renamed from res/drawable-xxhdpi/media_pause_light.png)bin631 -> 631 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/media_start_dark.png (renamed from res/drawable-xxhdpi/media_start_dark.png)bin996 -> 996 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/media_start_light.png (renamed from res/drawable-xxhdpi/media_start_light.png)bin1069 -> 1069 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/media_stop_dark.png (renamed from res/drawable-xxhdpi/media_stop_dark.png)bin545 -> 545 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/media_stop_light.png (renamed from res/drawable-xxhdpi/media_stop_light.png)bin554 -> 554 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/notification_close.png (renamed from res/drawable-xxhdpi/notification_close.png)bin1081 -> 1081 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/notification_next.png (renamed from res/drawable-xxhdpi/notification_next.png)bin1292 -> 1292 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/notification_pause.png (renamed from res/drawable-xxhdpi/notification_pause.png)bin724 -> 724 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/notification_play.png (renamed from res/drawable-xxhdpi/notification_play.png)bin1125 -> 1125 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/notification_previous.png (renamed from res/drawable-xxhdpi/notification_previous.png)bin1261 -> 1261 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/now_playing.png (renamed from res/drawable-xxhdpi/now_playing.png)bin1125 -> 1125 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/stat_notify_download.png (renamed from res/drawable-xxhdpi/stat_notify_download.png)bin558 -> 558 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/stat_notify_playing.png (renamed from res/drawable-xxhdpi/stat_notify_playing.png)bin1125 -> 1125 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/stat_notify_sync.png (renamed from res/drawable-xxhdpi/stat_notify_sync.png)bin1932 -> 1932 bytes
-rw-r--r--app/src/main/res/drawable/appwidget4x1_preview.png (renamed from res/drawable/appwidget4x1_preview.png)bin3365 -> 3365 bytes
-rw-r--r--app/src/main/res/drawable/appwidget4x2_preview.png (renamed from res/drawable/appwidget4x2_preview.png)bin5856 -> 5856 bytes
-rw-r--r--app/src/main/res/drawable/appwidget4x3_preview.png (renamed from res/drawable/appwidget4x3_preview.png)bin6216 -> 6216 bytes
-rw-r--r--app/src/main/res/drawable/appwidget4x4_preview.png (renamed from res/drawable/appwidget4x4_preview.png)bin9186 -> 9186 bytes
-rw-r--r--app/src/main/res/layout-land/download.xml (renamed from res/layout-land/download.xml)258
-rw-r--r--app/src/main/res/layout-large-land/abstract_fragment_container.xml (renamed from res/layout-large-land/abstract_fragment_container.xml)40
-rw-r--r--app/src/main/res/layout-large-land/download.xml (renamed from res/layout-large-land/download.xml)260
-rw-r--r--app/src/main/res/layout-port/download.xml (renamed from res/layout-port/download.xml)240
-rw-r--r--app/src/main/res/layout/abstract_activity.xml (renamed from res/layout/abstract_activity.xml)42
-rw-r--r--app/src/main/res/layout/abstract_fragment_activity.xml (renamed from res/layout/abstract_fragment_activity.xml)166
-rw-r--r--app/src/main/res/layout/abstract_fragment_container.xml (renamed from res/layout/abstract_fragment_container.xml)10
-rw-r--r--app/src/main/res/layout/abstract_list_fragment.xml (renamed from res/layout/abstract_list_fragment.xml)52
-rw-r--r--app/src/main/res/layout/actionbar_spinner.xml (renamed from res/layout/actionbar_spinner.xml)0
-rw-r--r--app/src/main/res/layout/album_cell_item.xml (renamed from res/layout/album_cell_item.xml)176
-rw-r--r--app/src/main/res/layout/album_list_item.xml (renamed from res/layout/album_list_item.xml)148
-rw-r--r--app/src/main/res/layout/appwidget4x1.xml (renamed from res/layout/appwidget4x1.xml)0
-rw-r--r--app/src/main/res/layout/appwidget4x2.xml (renamed from res/layout/appwidget4x2.xml)0
-rw-r--r--app/src/main/res/layout/appwidget4x3.xml (renamed from res/layout/appwidget4x3.xml)0
-rw-r--r--app/src/main/res/layout/appwidget4x4.xml (renamed from res/layout/appwidget4x4.xml)0
-rw-r--r--app/src/main/res/layout/basic_count_item.xml (renamed from res/layout/basic_count_item.xml)68
-rw-r--r--app/src/main/res/layout/basic_list_item.xml (renamed from res/layout/basic_list_item.xml)72
-rw-r--r--app/src/main/res/layout/change_email.xml (renamed from res/layout/change_email.xml)54
-rw-r--r--app/src/main/res/layout/change_password.xml (renamed from res/layout/change_password.xml)54
-rw-r--r--app/src/main/res/layout/chat.xml (renamed from res/layout/chat.xml)0
-rw-r--r--app/src/main/res/layout/chat_item.xml (renamed from res/layout/chat_item.xml)0
-rw-r--r--app/src/main/res/layout/chat_item_reverse.xml (renamed from res/layout/chat_item_reverse.xml)0
-rw-r--r--app/src/main/res/layout/complex_list_item.xml (renamed from res/layout/complex_list_item.xml)96
-rw-r--r--app/src/main/res/layout/confirm_password.xml (renamed from res/layout/confirm_password.xml)54
-rw-r--r--app/src/main/res/layout/create_bookmark.xml (renamed from res/layout/create_bookmark.xml)0
-rw-r--r--app/src/main/res/layout/create_podcast.xml (renamed from res/layout/create_podcast.xml)0
-rw-r--r--app/src/main/res/layout/create_user.xml (renamed from res/layout/create_user.xml)152
-rw-r--r--app/src/main/res/layout/download_activity.xml (renamed from res/layout/download_activity.xml)0
-rw-r--r--app/src/main/res/layout/download_media_buttons.xml (renamed from res/layout/download_media_buttons.xml)0
-rw-r--r--app/src/main/res/layout/download_playlist.xml (renamed from res/layout/download_playlist.xml)60
-rw-r--r--app/src/main/res/layout/download_slider.xml (renamed from res/layout/download_slider.xml)84
-rw-r--r--app/src/main/res/layout/drawer_list_item.xml (renamed from res/layout/drawer_list_item.xml)52
-rw-r--r--app/src/main/res/layout/edit_play_action.xml (renamed from res/layout/edit_play_action.xml)0
-rw-r--r--app/src/main/res/layout/equalizer.xml (renamed from res/layout/equalizer.xml)102
-rw-r--r--app/src/main/res/layout/equalizer_bar.xml (renamed from res/layout/equalizer_bar.xml)72
-rw-r--r--app/src/main/res/layout/genre_list_item.xml (renamed from res/layout/genre_list_item.xml)82
-rw-r--r--app/src/main/res/layout/grid_view.xml (renamed from res/layout/grid_view.xml)26
-rw-r--r--app/src/main/res/layout/home.xml (renamed from res/layout/home.xml)46
-rw-r--r--app/src/main/res/layout/jukebox_volume.xml (renamed from res/layout/jukebox_volume.xml)0
-rw-r--r--app/src/main/res/layout/lyrics.xml (renamed from res/layout/lyrics.xml)110
-rw-r--r--app/src/main/res/layout/main_buttons.xml (renamed from res/layout/main_buttons.xml)314
-rw-r--r--app/src/main/res/layout/notification.xml (renamed from res/layout/notification.xml)0
-rw-r--r--app/src/main/res/layout/notification_expanded.xml (renamed from res/layout/notification_expanded.xml)0
-rw-r--r--app/src/main/res/layout/preferences.xml (renamed from res/layout/preferences.xml)0
-rw-r--r--app/src/main/res/layout/progress.xml (renamed from res/layout/progress.xml)38
-rw-r--r--app/src/main/res/layout/rating.xml (renamed from res/layout/rating.xml)28
-rw-r--r--app/src/main/res/layout/save_playlist.xml (renamed from res/layout/save_playlist.xml)52
-rw-r--r--app/src/main/res/layout/search_buttons.xml (renamed from res/layout/search_buttons.xml)146
-rw-r--r--app/src/main/res/layout/seekbar_preference.xml (renamed from res/layout/seekbar_preference.xml)36
-rw-r--r--app/src/main/res/layout/select_album.xml (renamed from res/layout/select_album.xml)54
-rw-r--r--app/src/main/res/layout/select_album_header.xml (renamed from res/layout/select_album_header.xml)0
-rw-r--r--app/src/main/res/layout/select_artist_header.xml (renamed from res/layout/select_artist_header.xml)84
-rw-r--r--app/src/main/res/layout/shuffle_dialog.xml (renamed from res/layout/shuffle_dialog.xml)0
-rw-r--r--app/src/main/res/layout/song_list_item.xml (renamed from res/layout/song_list_item.xml)252
-rw-r--r--app/src/main/res/layout/start_timer.xml (renamed from res/layout/start_timer.xml)0
-rw-r--r--app/src/main/res/layout/static_drawer_activity.xml (renamed from res/layout/static_drawer_activity.xml)0
-rw-r--r--app/src/main/res/layout/sync_dialog.xml (renamed from res/layout/sync_dialog.xml)0
-rw-r--r--app/src/main/res/layout/tab_progress.xml (renamed from res/layout/tab_progress.xml)60
-rw-r--r--app/src/main/res/layout/unscrollable_grid_view.xml (renamed from res/layout/unscrollable_grid_view.xml)20
-rw-r--r--app/src/main/res/layout/update_playlist.xml (renamed from res/layout/update_playlist.xml)0
-rw-r--r--app/src/main/res/layout/update_share.xml (renamed from res/layout/update_share.xml)0
-rw-r--r--app/src/main/res/layout/user_header.xml (renamed from res/layout/user_header.xml)0
-rw-r--r--app/src/main/res/layout/user_list_item.xml (renamed from res/layout/user_list_item.xml)86
-rw-r--r--app/src/main/res/menu/abstract_top_menu.xml (renamed from res/menu/abstract_top_menu.xml)42
-rw-r--r--app/src/main/res/menu/admin.xml (renamed from res/menu/admin.xml)22
-rw-r--r--app/src/main/res/menu/admin_context.xml (renamed from res/menu/admin_context.xml)32
-rw-r--r--app/src/main/res/menu/admin_context_user.xml (renamed from res/menu/admin_context_user.xml)14
-rw-r--r--app/src/main/res/menu/downloading.xml (renamed from res/menu/downloading.xml)0
-rw-r--r--app/src/main/res/menu/drawer_menu.xml (renamed from res/menu/drawer_menu.xml)28
-rw-r--r--app/src/main/res/menu/empty.xml (renamed from res/menu/empty.xml)0
-rw-r--r--app/src/main/res/menu/main.xml (renamed from res/menu/main.xml)0
-rw-r--r--app/src/main/res/menu/nowplaying.xml (renamed from res/menu/nowplaying.xml)0
-rw-r--r--app/src/main/res/menu/nowplaying_context.xml (renamed from res/menu/nowplaying_context.xml)0
-rw-r--r--app/src/main/res/menu/nowplaying_context_offline.xml (renamed from res/menu/nowplaying_context_offline.xml)0
-rw-r--r--app/src/main/res/menu/nowplaying_offline.xml (renamed from res/menu/nowplaying_offline.xml)0
-rw-r--r--app/src/main/res/menu/search.xml (renamed from res/menu/search.xml)0
-rw-r--r--app/src/main/res/menu/select_album.xml (renamed from res/menu/select_album.xml)0
-rw-r--r--app/src/main/res/menu/select_album_context.xml (renamed from res/menu/select_album_context.xml)0
-rw-r--r--app/src/main/res/menu/select_album_context_offline.xml (renamed from res/menu/select_album_context_offline.xml)0
-rw-r--r--app/src/main/res/menu/select_album_list.xml (renamed from res/menu/select_album_list.xml)0
-rw-r--r--app/src/main/res/menu/select_artist.xml (renamed from res/menu/select_artist.xml)0
-rw-r--r--app/src/main/res/menu/select_artist_context.xml (renamed from res/menu/select_artist_context.xml)0
-rw-r--r--app/src/main/res/menu/select_artist_context_offline.xml (renamed from res/menu/select_artist_context_offline.xml)0
-rw-r--r--app/src/main/res/menu/select_bookmark_context.xml (renamed from res/menu/select_bookmark_context.xml)0
-rw-r--r--app/src/main/res/menu/select_playlist_context.xml (renamed from res/menu/select_playlist_context.xml)0
-rw-r--r--app/src/main/res/menu/select_playlist_context_offline.xml (renamed from res/menu/select_playlist_context_offline.xml)0
-rw-r--r--app/src/main/res/menu/select_podcast_episode.xml (renamed from res/menu/select_podcast_episode.xml)0
-rw-r--r--app/src/main/res/menu/select_podcast_episode_context.xml (renamed from res/menu/select_podcast_episode_context.xml)0
-rw-r--r--app/src/main/res/menu/select_podcast_episode_context_offline.xml (renamed from res/menu/select_podcast_episode_context_offline.xml)0
-rw-r--r--app/src/main/res/menu/select_podcast_episode_offline.xml (renamed from res/menu/select_podcast_episode_offline.xml)0
-rw-r--r--app/src/main/res/menu/select_podcasts.xml (renamed from res/menu/select_podcasts.xml)0
-rw-r--r--app/src/main/res/menu/select_podcasts_context.xml (renamed from res/menu/select_podcasts_context.xml)0
-rw-r--r--app/src/main/res/menu/select_podcasts_context_offline.xml (renamed from res/menu/select_podcasts_context_offline.xml)0
-rw-r--r--app/src/main/res/menu/select_share_context.xml (renamed from res/menu/select_share_context.xml)42
-rw-r--r--app/src/main/res/menu/select_song.xml (renamed from res/menu/select_song.xml)0
-rw-r--r--app/src/main/res/menu/select_song_context.xml (renamed from res/menu/select_song_context.xml)0
-rw-r--r--app/src/main/res/menu/select_song_context_offline.xml (renamed from res/menu/select_song_context_offline.xml)0
-rw-r--r--app/src/main/res/menu/select_song_offline.xml (renamed from res/menu/select_song_offline.xml)0
-rw-r--r--app/src/main/res/menu/select_video_context.xml (renamed from res/menu/select_video_context.xml)0
-rw-r--r--app/src/main/res/menu/select_video_context_offline.xml (renamed from res/menu/select_video_context_offline.xml)0
-rw-r--r--app/src/main/res/menu/similar_artists.xml (renamed from res/menu/similar_artists.xml)0
-rw-r--r--app/src/main/res/menu/tasker_configuration.xml (renamed from res/menu/tasker_configuration.xml)32
-rw-r--r--app/src/main/res/menu/unstar.xml (renamed from res/menu/unstar.xml)12
-rw-r--r--app/src/main/res/menu/user.xml (renamed from res/menu/user.xml)62
-rw-r--r--app/src/main/res/menu/user_user.xml (renamed from res/menu/user_user.xml)26
-rw-r--r--app/src/main/res/values-de/strings.xml (renamed from res/values-de/strings.xml)1114
-rw-r--r--app/src/main/res/values-es/strings.xml (renamed from res/values-es/strings.xml)0
-rw-r--r--app/src/main/res/values-fr/strings.xml (renamed from res/values-fr/strings.xml)1140
-rw-r--r--app/src/main/res/values-hu/strings.xml (renamed from res/values-hu/strings.xml)1204
-rw-r--r--app/src/main/res/values-land/integers.xml (renamed from res/values-land/integers.xml)6
-rw-r--r--app/src/main/res/values-large/dimens.xml (renamed from res/values-large/dimens.xml)12
-rw-r--r--app/src/main/res/values-large/integers.xml (renamed from res/values-large/integers.xml)8
-rw-r--r--app/src/main/res/values-ru/strings.xml (renamed from res/values-ru/strings.xml)624
-rw-r--r--app/src/main/res/values-v11/colors.xml (renamed from res/values-v11/colors.xml)0
-rw-r--r--app/src/main/res/values-v11/styles.xml (renamed from res/values-v11/styles.xml)10
-rw-r--r--app/src/main/res/values-v16/themes.xml (renamed from res/values-v16/themes.xml)28
-rw-r--r--app/src/main/res/values/arrays.xml (renamed from res/values/arrays.xml)0
-rw-r--r--app/src/main/res/values/attrs.xml (renamed from res/values/attrs.xml)0
-rw-r--r--app/src/main/res/values/colors.xml (renamed from res/values/colors.xml)0
-rw-r--r--app/src/main/res/values/dimens.xml (renamed from res/values/dimens.xml)12
-rw-r--r--app/src/main/res/values/ids.xml (renamed from res/values/ids.xml)0
-rw-r--r--app/src/main/res/values/integers.xml (renamed from res/values/integers.xml)8
-rw-r--r--app/src/main/res/values/strings.xml (renamed from res/values/strings.xml)1214
-rw-r--r--app/src/main/res/values/styles.xml (renamed from res/values/styles.xml)0
-rw-r--r--app/src/main/res/values/themes.xml (renamed from res/values/themes.xml)0
-rw-r--r--app/src/main/res/xml/appwidget4x1.xml (renamed from res/xml/appwidget4x1.xml)0
-rw-r--r--app/src/main/res/xml/appwidget4x2.xml (renamed from res/xml/appwidget4x2.xml)0
-rw-r--r--app/src/main/res/xml/appwidget4x3.xml (renamed from res/xml/appwidget4x3.xml)0
-rw-r--r--app/src/main/res/xml/appwidget4x4.xml (renamed from res/xml/appwidget4x4.xml)0
-rw-r--r--app/src/main/res/xml/authenticator.xml (renamed from res/xml/authenticator.xml)12
-rw-r--r--app/src/main/res/xml/changelog.xml (renamed from res/xml/changelog.xml)0
-rw-r--r--app/src/main/res/xml/mostrecent_syncadapter.xml (renamed from res/xml/mostrecent_syncadapter.xml)14
-rw-r--r--app/src/main/res/xml/playlists_syncadapter.xml (renamed from res/xml/playlists_syncadapter.xml)14
-rw-r--r--app/src/main/res/xml/podcasts_syncadapter.xml (renamed from res/xml/podcasts_syncadapter.xml)14
-rw-r--r--app/src/main/res/xml/searchable.xml (renamed from res/xml/searchable.xml)0
-rw-r--r--app/src/main/res/xml/settings.xml (renamed from res/xml/settings.xml)0
-rw-r--r--app/src/main/res/xml/starred_syncadapter.xml (renamed from res/xml/starred_syncadapter.xml)14
-rw-r--r--build.gradle19
-rw-r--r--build.properties0
-rw-r--r--build.xml92
-rw-r--r--default.properties0
-rw-r--r--gradle.properties18
-rw-r--r--gradle/wrapper/gradle-wrapper.jarbin0 -> 49896 bytes
-rw-r--r--gradle/wrapper/gradle-wrapper.properties6
-rw-r--r--gradlew164
-rw-r--r--gradlew.bat90
-rw-r--r--local.properties10
-rw-r--r--project.properties16
-rw-r--r--settings.gradle3
780 files changed, 21888 insertions, 21564 deletions
diff --git a/.gitignore b/.gitignore
index ae198bf3..78fa8a1f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,3 +10,5 @@ releases/
proguard_logs/
/gen/
/out/
+.gradle/*
+/build/
diff --git a/DSub.iml b/DSub.iml
new file mode 100644
index 00000000..0bb6048a
--- /dev/null
+++ b/DSub.iml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
+ <component name="FacetManager">
+ <facet type="java-gradle" name="Java-Gradle">
+ <configuration>
+ <option name="BUILD_FOLDER_PATH" value="$MODULE_DIR$/build" />
+ </configuration>
+ </facet>
+ </component>
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
+ <exclude-output />
+ <content url="file://$MODULE_DIR$">
+ <excludeFolder url="file://$MODULE_DIR$/.gradle" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ </component>
+</module>
+
diff --git a/DragSortListView b/DragSortListView
-Subproject c366792f270e6919fa75503347c30ab62919c5f
+Subproject 6110b307573e37e6d248a20b3cf389dac1c96c6
diff --git a/ServerProxy b/ServerProxy
-Subproject f5db1184694e6ce7d159355a7742d75a7b8c32c
+Subproject 6b179383a7fc9e1acebd373f55a527a221806cd
diff --git a/Subsonic.iml b/Subsonic.iml
deleted file mode 100644
index da683d63..00000000
--- a/Subsonic.iml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<module type="JAVA_MODULE" version="4">
- <component name="FacetManager">
- <facet type="android" name="Android">
- <configuration>
- <proGuardCfgFiles>
- <file>file://$MODULE_DIR$/proguard.cfg</file>
- </proGuardCfgFiles>
- <option name="UPDATE_PROPERTY_FILES" value="true" />
- </configuration>
- </facet>
- </component>
- <component name="NewModuleRootManager" inherit-compiler-output="true">
- <exclude-output />
- <content url="file://$MODULE_DIR$">
- <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
- <sourceFolder url="file://$MODULE_DIR$/gen" isTestSource="false" generated="true" />
- </content>
- <orderEntry type="library" name="libs1" level="project" />
- <orderEntry type="inheritedJdk" />
- <orderEntry type="sourceFolder" forTests="false" />
- <orderEntry type="module" module-name="DragSortListView" />
- <orderEntry type="module" module-name="appcompat" />
- <orderEntry type="module" module-name="mediarouter" />
- <orderEntry type="module" module-name="google-play-services_lib" />
- <orderEntry type="library" name="google-player-services" level="project" />
- <orderEntry type="module" module-name="ServerProxy" />
- <orderEntry type="library" name="android-support-v4" level="project" />
- <orderEntry type="library" name="android-support-v7-appcompat" level="project" />
- <orderEntry type="library" name="android-support-v7-mediarouter" level="project" />
- </component>
-</module>
-
diff --git a/ant.properties b/ant.properties
deleted file mode 100644
index de5f19ef..00000000
--- a/ant.properties
+++ /dev/null
@@ -1,20 +0,0 @@
-# This file is used to override default values used by the Ant build system.
-#
-# This file must be checked in Version Control Systems, as it is
-# integral to the build system of your project.
-
-# This file is only used by the Ant script.
-
-# You can use this to override default values such as
-# 'source.dir' for the location of your java source folder and
-# 'out.dir' for the location of your output folder.
-
-# You can also use it define how the release builds are signed by declaring
-# the following properties:
-# 'key.store' for the location of your keystore and
-# 'key.alias' for the name of the key to use.
-# The password will be asked during the build when you use the 'release' target.
-
-key.store=C:/Users/Scott/Documents/Subsonic/subsonic-android/subsonic.keystore
-key.alias=subsonic
-
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 00000000..796b96d1
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/app/app.iml b/app/app.iml
new file mode 100644
index 00000000..e50d2481
--- /dev/null
+++ b/app/app.iml
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="DSub" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
+ <component name="FacetManager">
+ <facet type="android-gradle" name="Android-Gradle">
+ <configuration>
+ <option name="GRADLE_PROJECT_PATH" value=":app" />
+ </configuration>
+ </facet>
+ <facet type="android" name="Android">
+ <configuration>
+ <option name="SELECTED_BUILD_VARIANT" value="debug" />
+ <option name="SELECTED_TEST_ARTIFACT" value="_android_test_" />
+ <option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
+ <option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
+ <option name="ASSEMBLE_TEST_TASK_NAME" value="assembleDebugAndroidTest" />
+ <option name="SOURCE_GEN_TASK_NAME" value="generateDebugSources" />
+ <option name="TEST_SOURCE_GEN_TASK_NAME" value="generateDebugAndroidTestSources" />
+ <option name="ALLOW_USER_CONFIGURATION" value="false" />
+ <option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
+ <option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
+ <option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
+ <option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
+ </configuration>
+ </facet>
+ </component>
+ <component name="NewModuleRootManager" inherit-compiler-output="false">
+ <output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
+ <output-test url="file://$MODULE_DIR$/build/intermediates/classes/androidTest/debug" />
+ <exclude-output />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
+ <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
+ <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
+ <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
+ <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/build/generated/res/generated/debug" type="java-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" />
+ <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
+ <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
+ <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
+ <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/build/generated/res/generated/androidTest/debug" type="java-test-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
+ <sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
+ <sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
+ <sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/coverage-instrumented-classes" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex-cache" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/jacoco" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaResources" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/libs" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/ndk" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/proguard" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
+ <excludeFolder url="file://$MODULE_DIR$/build/outputs" />
+ <excludeFolder url="file://$MODULE_DIR$/build/tmp" />
+ </content>
+ <orderEntry type="jdk" jdkName="Android API 22 Platform" jdkType="Android SDK" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="library" exported="" name="mediarouter-v7-22.1.1" level="project" />
+ <orderEntry type="library" exported="" name="seamless-util-1.1.0" level="project" />
+ <orderEntry type="library" exported="" name="cling-core-2.0.1" level="project" />
+ <orderEntry type="library" exported="" name="javax.servlet-3.0.0.v201112011016" level="project" />
+ <orderEntry type="library" exported="" name="support-annotations-22.1.1" level="project" />
+ <orderEntry type="library" exported="" name="support-v4-22.1.1" level="project" />
+ <orderEntry type="library" exported="" name="play-services-cast-7.0.0" level="project" />
+ <orderEntry type="library" exported="" name="cling-support-2.0.1" level="project" />
+ <orderEntry type="library" exported="" name="seamless-http-1.1.0" level="project" />
+ <orderEntry type="library" exported="" name="appcompat-v7-22.1.1" level="project" />
+ <orderEntry type="library" exported="" name="seamless-xml-1.1.0" level="project" />
+ <orderEntry type="library" exported="" name="CWAC-EndlessAdapter" level="project" />
+ <orderEntry type="library" exported="" name="kryo-2.21-all" level="project" />
+ <orderEntry type="library" exported="" name="play-services-base-7.0.0" level="project" />
+ <orderEntry type="library" exported="" name="jetty-all-8.1.16.v20140903" level="project" />
+ <orderEntry type="library" exported="" name="CWAC-AdapterWrapper" level="project" />
+ <orderEntry type="module" module-name="DragSort ListView" exported="" />
+ <orderEntry type="module" module-name="Server Proxy" exported="" />
+ </component>
+</module>
+
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 00000000..cb3119bd
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,32 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 22
+ buildToolsVersion "22.0.0"
+
+ defaultConfig {
+ applicationId "github.daneren2005.dsub"
+ minSdkVersion 9
+ targetSdkVersion 22
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard.cfg')
+ }
+ }
+
+ packagingOptions {
+ exclude 'META-INF/beans.xml'
+ }
+}
+
+dependencies {
+ compile project(':DragSort ListView')
+ compile project(':Server Proxy')
+ compile fileTree(include: ['*.jar'], dir: 'libs')
+ compile 'com.android.support:support-v4:22.1.1'
+ compile 'com.android.support:appcompat-v7:22.1.1'
+ compile 'com.android.support:mediarouter-v7:22.1.1'
+ compile 'com.google.android.gms:play-services-cast:7.0.0'
+}
diff --git a/libs/CWAC-AdapterWrapper.jar b/app/libs/CWAC-AdapterWrapper.jar
index 692fe4d3..692fe4d3 100644
--- a/libs/CWAC-AdapterWrapper.jar
+++ b/app/libs/CWAC-AdapterWrapper.jar
Binary files differ
diff --git a/libs/CWAC-EndlessAdapter.jar b/app/libs/CWAC-EndlessAdapter.jar
index ec20d936..ec20d936 100644
--- a/libs/CWAC-EndlessAdapter.jar
+++ b/app/libs/CWAC-EndlessAdapter.jar
Binary files differ
diff --git a/libs/cling-core-2.0.1.jar b/app/libs/cling-core-2.0.1.jar
index 632d3038..632d3038 100644
--- a/libs/cling-core-2.0.1.jar
+++ b/app/libs/cling-core-2.0.1.jar
Binary files differ
diff --git a/libs/cling-support-2.0.1.jar b/app/libs/cling-support-2.0.1.jar
index 7fa28604..7fa28604 100644
--- a/libs/cling-support-2.0.1.jar
+++ b/app/libs/cling-support-2.0.1.jar
Binary files differ
diff --git a/libs/javax.servlet-3.0.0.v201112011016.jar b/app/libs/javax.servlet-3.0.0.v201112011016.jar
index b1354096..b1354096 100644
--- a/libs/javax.servlet-3.0.0.v201112011016.jar
+++ b/app/libs/javax.servlet-3.0.0.v201112011016.jar
Binary files differ
diff --git a/libs/jetty-all-8.1.16.v20140903.jar b/app/libs/jetty-all-8.1.16.v20140903.jar
index 25b1d324..25b1d324 100644
--- a/libs/jetty-all-8.1.16.v20140903.jar
+++ b/app/libs/jetty-all-8.1.16.v20140903.jar
Binary files differ
diff --git a/libs/kryo-2.21-all.jar b/app/libs/kryo-2.21-all.jar
index 83f8b0f0..83f8b0f0 100644
--- a/libs/kryo-2.21-all.jar
+++ b/app/libs/kryo-2.21-all.jar
Binary files differ
diff --git a/libs/seamless-http-1.1.0.jar b/app/libs/seamless-http-1.1.0.jar
index 98ec884a..98ec884a 100644
--- a/libs/seamless-http-1.1.0.jar
+++ b/app/libs/seamless-http-1.1.0.jar
Binary files differ
diff --git a/libs/seamless-util-1.1.0.jar b/app/libs/seamless-util-1.1.0.jar
index 12026b7f..12026b7f 100644
--- a/libs/seamless-util-1.1.0.jar
+++ b/app/libs/seamless-util-1.1.0.jar
Binary files differ
diff --git a/libs/seamless-xml-1.1.0.jar b/app/libs/seamless-xml-1.1.0.jar
index 1e740877..1e740877 100644
--- a/libs/seamless-xml-1.1.0.jar
+++ b/app/libs/seamless-xml-1.1.0.jar
Binary files differ
diff --git a/app/src/androidTest/java/github/daneren2005/dsub/ApplicationTest.java b/app/src/androidTest/java/github/daneren2005/dsub/ApplicationTest.java
new file mode 100644
index 00000000..b7d12df1
--- /dev/null
+++ b/app/src/androidTest/java/github/daneren2005/dsub/ApplicationTest.java
@@ -0,0 +1,13 @@
+package github.daneren2005.dsub;
+
+import android.app.Application;
+import android.test.ApplicationTestCase;
+
+/**
+ * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
+ */
+public class ApplicationTest extends ApplicationTestCase<Application> {
+ public ApplicationTest() {
+ super(Application.class);
+ }
+} \ No newline at end of file
diff --git a/test/github/daneren2005/dsub/activity/DownloadActivityTest.java b/app/src/androidTest/java/github/daneren2005/dsub/activity/DownloadActivityTest.java
index ce859181..ce859181 100644
--- a/test/github/daneren2005/dsub/activity/DownloadActivityTest.java
+++ b/app/src/androidTest/java/github/daneren2005/dsub/activity/DownloadActivityTest.java
diff --git a/test/github/daneren2005/dsub/activity/SubsonicFragmentActivityTest.java b/app/src/androidTest/java/github/daneren2005/dsub/activity/SubsonicFragmentActivityTest.java
index 553938c8..553938c8 100644
--- a/test/github/daneren2005/dsub/activity/SubsonicFragmentActivityTest.java
+++ b/app/src/androidTest/java/github/daneren2005/dsub/activity/SubsonicFragmentActivityTest.java
diff --git a/test/github/daneren2005/dsub/domain/BookmarkTest.java b/app/src/androidTest/java/github/daneren2005/dsub/domain/BookmarkTest.java
index 814f658a..814f658a 100644
--- a/test/github/daneren2005/dsub/domain/BookmarkTest.java
+++ b/app/src/androidTest/java/github/daneren2005/dsub/domain/BookmarkTest.java
diff --git a/test/github/daneren2005/dsub/domain/GenreComparatorTest.java b/app/src/androidTest/java/github/daneren2005/dsub/domain/GenreComparatorTest.java
index 9ffa518e..9ffa518e 100644
--- a/test/github/daneren2005/dsub/domain/GenreComparatorTest.java
+++ b/app/src/androidTest/java/github/daneren2005/dsub/domain/GenreComparatorTest.java
diff --git a/test/github/daneren2005/dsub/service/DownloadServiceTest.java b/app/src/androidTest/java/github/daneren2005/dsub/service/DownloadServiceTest.java
index 44b77b84..44b77b84 100644
--- a/test/github/daneren2005/dsub/service/DownloadServiceTest.java
+++ b/app/src/androidTest/java/github/daneren2005/dsub/service/DownloadServiceTest.java
diff --git a/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 191c423f..30a6eb3c 100644
--- a/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,250 +1,250 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="github.daneren2005.dsub"
- android:installLocation="internalOnly"
- android:versionCode="151"
- android:versionName="4.9.6">
-
- <instrumentation android:name="android.test.InstrumentationTestRunner"
- android:targetPackage="github.daneren2005.dsub"
- android:label="Tests" />
-
- <uses-permission android:name="android.permission.INTERNET"/>
- <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
- <uses-permission android:name="android.permission.WAKE_LOCK"/>
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
- <uses-permission android:name="android.permission.RECORD_AUDIO"/>
- <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
- <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
- <uses-permission android:name="android.permission.BLUETOOTH"/>
- <uses-permission android:name="android.permission.READ_LOGS"/>
- <uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/>
- <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"/>
- <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"/>
- <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
- <uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"/>
- <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
-
- <uses-feature android:name="android.hardware.touchscreen" android:required="false" />
- <uses-feature android:name="android.hardware.bluetooth" android:required="false" />
- <uses-feature android:name="android.hardware.microphone" android:required="false" />
- <uses-feature android:name="android.hardware.wifi" android:required="false" />
-
- <uses-sdk android:minSdkVersion="9" android:targetSdkVersion="19"/>
-
- <supports-screens android:anyDensity="true" android:xlargeScreens="true" android:largeScreens="true" android:normalScreens="true" android:smallScreens="true"/>
-
- <application android:label="@string/common.appname"
- android:backupAgent="github.daneren2005.dsub.util.SettingsBackupAgent"
- android:icon="@drawable/launch"
- android:theme="@style/Theme.DSub.Light">
-
- <uses-library android:name="android.test.runner" />
-
- <activity android:name="github.daneren2005.dsub.activity.SubsonicFragmentActivity"
- android:configChanges="orientation|keyboardHidden"
- android:launchMode="standard">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
- <category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
- </intent-filter>
- </activity>
-
- <activity android:name="github.daneren2005.dsub.activity.DownloadActivity"
- android:configChanges="keyboardHidden"
- android:launchMode="singleTask"/>
-
- <activity android:name="github.daneren2005.dsub.activity.SettingsActivity"
- android:configChanges="orientation|keyboardHidden"
- android:launchMode="singleTask"/>
-
- <activity android:name="github.daneren2005.dsub.activity.VoiceQueryReceiverActivity"
- android:launchMode="singleTask">
- <intent-filter>
- <action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
-
- <intent-filter>
- <action android:name="com.google.android.gms.actions.SEARCH_ACTION"/>
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.BROWSABLE" />
- </intent-filter>
- </activity>
-
- <activity android:name="github.daneren2005.dsub.activity.QueryReceiverActivity"
- android:launchMode="singleTask">
- <intent-filter>
- <action android:name="android.intent.action.SEARCH"/>
- </intent-filter>
- <meta-data android:name="android.app.searchable" android:resource="@xml/searchable"/>
- </activity>
-
- <activity
- android:name="github.daneren2005.dsub.activity.EditPlayActionActivity"
- android:label="@string/tasker.start_playing"
- android:icon="@drawable/launch">
-
- <intent-filter>
- <action android:name="com.twofortyfouram.locale.intent.action.EDIT_SETTING" />
- </intent-filter>
- </activity>
-
- <service android:name=".service.DownloadService"
- android:label="DSub Playback Service"/>
- <service android:name="org.fourthline.cling.android.AndroidUpnpServiceImpl"/>
- <service android:name="github.daneren2005.dsub.service.sync.AuthenticatorService">
- <intent-filter>
- <action android:name="android.accounts.AccountAuthenticator"/>
- </intent-filter>
-
- <meta-data android:name="android.accounts.AccountAuthenticator"
- android:resource="@xml/authenticator" />
- </service>
- <service android:name="github.daneren2005.dsub.service.sync.PlaylistSyncService"
- android:exported="true"
- android:process=":sync">
-
- <intent-filter>
- <action android:name="android.content.SyncAdapter"/>
- </intent-filter>
- <meta-data android:name="android.content.SyncAdapter"
- android:resource="@xml/playlists_syncadapter" />
- </service>
- <service android:name="github.daneren2005.dsub.service.sync.PodcastSyncService"
- android:exported="true"
- android:process=":sync">
-
- <intent-filter>
- <action android:name="android.content.SyncAdapter"/>
- </intent-filter>
- <meta-data android:name="android.content.SyncAdapter"
- android:resource="@xml/podcasts_syncadapter" />
- </service>
- <service android:name="github.daneren2005.dsub.service.sync.StarredSyncService"
- android:exported="true"
- android:process=":sync">
-
- <intent-filter>
- <action android:name="android.content.SyncAdapter"/>
- </intent-filter>
- <meta-data android:name="android.content.SyncAdapter"
- android:resource="@xml/starred_syncadapter" />
- </service>
- <service android:name="github.daneren2005.dsub.service.sync.MostRecentSyncService"
- android:exported="true"
- android:process=":sync">
-
- <intent-filter>
- <action android:name="android.content.SyncAdapter"/>
- </intent-filter>
- <meta-data android:name="android.content.SyncAdapter"
- android:resource="@xml/mostrecent_syncadapter" />
- </service>
-
- <service android:name="github.daneren2005.dsub.service.HeadphoneListenerService"
- android:label="DSub Headphone Listener"/>
- <receiver
- android:name="github.daneren2005.dsub.receiver.BootReceiver">
- <intent-filter>
- <action
- android:name="android.intent.action.BOOT_COMPLETED" />
- </intent-filter>
- </receiver>
-
- <receiver android:name="github.daneren2005.dsub.receiver.MediaButtonIntentReceiver">
- <intent-filter>
- <action android:name="android.intent.action.MEDIA_BUTTON" />
- </intent-filter>
- </receiver>
-
- <receiver android:name="github.daneren2005.dsub.receiver.AudioNoisyReceiver">
- <intent-filter android:priority="999">
- <action android:name="android.media.AUDIO_BECOMING_NOISY" />
- </intent-filter>
- </receiver>
-
- <receiver android:name="github.daneren2005.dsub.receiver.A2dpIntentReceiver">
- <intent-filter>
- <action android:name="android.music.playstatusrequest"/>
- </intent-filter>
- </receiver>
-
- <receiver
- android:name="github.daneren2005.dsub.provider.DSubWidget4x1"
- android:label="@string/widget.4x1">
- <intent-filter>
- <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
- </intent-filter>
- <meta-data android:name="android.appwidget.provider" android:resource="@xml/appwidget4x1"/>
- </receiver>
- <receiver
- android:name="github.daneren2005.dsub.provider.DSubWidget4x2"
- android:label="@string/widget.4x2">
- <intent-filter>
- <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
- </intent-filter>
- <meta-data android:name="android.appwidget.provider" android:resource="@xml/appwidget4x2"/>
- </receiver>
- <receiver
- android:name="github.daneren2005.dsub.provider.DSubWidget4x3"
- android:label="@string/widget.4x3">
- <intent-filter>
- <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
- </intent-filter>
- <meta-data android:name="android.appwidget.provider" android:resource="@xml/appwidget4x3"/>
- </receiver>
- <receiver
- android:name="github.daneren2005.dsub.provider.DSubWidget4x4"
- android:label="@string/widget.4x4">
- <intent-filter>
- <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
- </intent-filter>
- <meta-data android:name="android.appwidget.provider" android:resource="@xml/appwidget4x4"/>
- </receiver>
-
- <receiver
- android:name="github.daneren2005.dsub.receiver.PlayActionReceiver">
-
- <intent-filter>
- <action android:name="com.twofortyfouram.locale.intent.action.FIRE_SETTING" />
- </intent-filter>
- </receiver>
-
- <provider android:name="github.daneren2005.dsub.provider.DSubSearchProvider"
- android:authorities="github.daneren2005.dsub.provider.DSubSearchProvider"/>
- <provider android:name="github.daneren2005.dsub.provider.PlaylistStubProvider"
- android:authorities="github.daneren2005.dsub.playlists.provider"
- android:label="@string/button_bar.playlists"
- android:exported="false"
- android:syncable="true"/>
- <provider android:name="github.daneren2005.dsub.provider.PodcastStubProvider"
- android:authorities="github.daneren2005.dsub.podcasts.provider"
- android:label="@string/button_bar.podcasts"
- android:exported="false"
- android:syncable="true"/>
- <provider android:name="github.daneren2005.dsub.provider.StarredStubProvider"
- android:authorities="github.daneren2005.dsub.starred.provider"
- android:label="@string/main.albums_starred"
- android:exported="false"
- android:syncable="true"/>
- <provider android:name="github.daneren2005.dsub.provider.MostRecentStubProvider"
- android:authorities="github.daneren2005.dsub.mostrecent.provider"
- android:label="@string/main.albums_newest"
- android:exported="false"
- android:syncable="true"/>
-
- <meta-data android:name="android.app.default_searchable"
- android:value="github.daneren2005.dsub.activity.QueryReceiverActivity"/>
-
- <meta-data android:name="com.google.android.backup.api_key"
- android:value="AEdPqrEAAAAIUhOMtwa_eG-f0oYUHnetl_Cz7cO9zae8ZXOK5w"/>
-
- <meta-data
- android:name="com.google.android.gms.version"
- android:value="@integer/google_play_services_version" />
- </application>
-
-</manifest>
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="github.daneren2005.dsub"
+ android:installLocation="internalOnly"
+ android:versionCode="151"
+ android:versionName="4.9.6">
+
+ <instrumentation android:name="android.test.InstrumentationTestRunner"
+ android:targetPackage="github.daneren2005.dsub"
+ android:label="Tests" />
+
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+ <uses-permission android:name="android.permission.WAKE_LOCK"/>
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+ <uses-permission android:name="android.permission.RECORD_AUDIO"/>
+ <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
+ <uses-permission android:name="android.permission.BLUETOOTH"/>
+ <uses-permission android:name="android.permission.READ_LOGS"/>
+ <uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/>
+ <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"/>
+ <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"/>
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
+ <uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"/>
+ <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
+
+ <uses-feature android:name="android.hardware.touchscreen" android:required="false" />
+ <uses-feature android:name="android.hardware.bluetooth" android:required="false" />
+ <uses-feature android:name="android.hardware.microphone" android:required="false" />
+ <uses-feature android:name="android.hardware.wifi" android:required="false" />
+
+ <uses-sdk android:minSdkVersion="9" android:targetSdkVersion="19"/>
+
+ <supports-screens android:anyDensity="true" android:xlargeScreens="true" android:largeScreens="true" android:normalScreens="true" android:smallScreens="true"/>
+
+ <application android:label="@string/common.appname"
+ android:backupAgent="github.daneren2005.dsub.util.SettingsBackupAgent"
+ android:icon="@drawable/launch"
+ android:theme="@style/Theme.DSub.Light">
+
+ <uses-library android:name="android.test.runner" />
+
+ <activity android:name="github.daneren2005.dsub.activity.SubsonicFragmentActivity"
+ android:configChanges="orientation|keyboardHidden"
+ android:launchMode="standard">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ <category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
+ </intent-filter>
+ </activity>
+
+ <activity android:name="github.daneren2005.dsub.activity.DownloadActivity"
+ android:configChanges="keyboardHidden"
+ android:launchMode="singleTask"/>
+
+ <activity android:name="github.daneren2005.dsub.activity.SettingsActivity"
+ android:configChanges="orientation|keyboardHidden"
+ android:launchMode="singleTask"/>
+
+ <activity android:name="github.daneren2005.dsub.activity.VoiceQueryReceiverActivity"
+ android:launchMode="singleTask">
+ <intent-filter>
+ <action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+
+ <intent-filter>
+ <action android:name="com.google.android.gms.actions.SEARCH_ACTION"/>
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ </intent-filter>
+ </activity>
+
+ <activity android:name="github.daneren2005.dsub.activity.QueryReceiverActivity"
+ android:launchMode="singleTask">
+ <intent-filter>
+ <action android:name="android.intent.action.SEARCH"/>
+ </intent-filter>
+ <meta-data android:name="android.app.searchable" android:resource="@xml/searchable"/>
+ </activity>
+
+ <activity
+ android:name="github.daneren2005.dsub.activity.EditPlayActionActivity"
+ android:label="@string/tasker.start_playing"
+ android:icon="@drawable/launch">
+
+ <intent-filter>
+ <action android:name="com.twofortyfouram.locale.intent.action.EDIT_SETTING" />
+ </intent-filter>
+ </activity>
+
+ <service android:name=".service.DownloadService"
+ android:label="DSub Playback Service"/>
+ <service android:name="org.fourthline.cling.android.AndroidUpnpServiceImpl"/>
+ <service android:name="github.daneren2005.dsub.service.sync.AuthenticatorService">
+ <intent-filter>
+ <action android:name="android.accounts.AccountAuthenticator"/>
+ </intent-filter>
+
+ <meta-data android:name="android.accounts.AccountAuthenticator"
+ android:resource="@xml/authenticator" />
+ </service>
+ <service android:name="github.daneren2005.dsub.service.sync.PlaylistSyncService"
+ android:exported="true"
+ android:process=":sync">
+
+ <intent-filter>
+ <action android:name="android.content.SyncAdapter"/>
+ </intent-filter>
+ <meta-data android:name="android.content.SyncAdapter"
+ android:resource="@xml/playlists_syncadapter" />
+ </service>
+ <service android:name="github.daneren2005.dsub.service.sync.PodcastSyncService"
+ android:exported="true"
+ android:process=":sync">
+
+ <intent-filter>
+ <action android:name="android.content.SyncAdapter"/>
+ </intent-filter>
+ <meta-data android:name="android.content.SyncAdapter"
+ android:resource="@xml/podcasts_syncadapter" />
+ </service>
+ <service android:name="github.daneren2005.dsub.service.sync.StarredSyncService"
+ android:exported="true"
+ android:process=":sync">
+
+ <intent-filter>
+ <action android:name="android.content.SyncAdapter"/>
+ </intent-filter>
+ <meta-data android:name="android.content.SyncAdapter"
+ android:resource="@xml/starred_syncadapter" />
+ </service>
+ <service android:name="github.daneren2005.dsub.service.sync.MostRecentSyncService"
+ android:exported="true"
+ android:process=":sync">
+
+ <intent-filter>
+ <action android:name="android.content.SyncAdapter"/>
+ </intent-filter>
+ <meta-data android:name="android.content.SyncAdapter"
+ android:resource="@xml/mostrecent_syncadapter" />
+ </service>
+
+ <service android:name="github.daneren2005.dsub.service.HeadphoneListenerService"
+ android:label="DSub Headphone Listener"/>
+ <receiver
+ android:name="github.daneren2005.dsub.receiver.BootReceiver">
+ <intent-filter>
+ <action
+ android:name="android.intent.action.BOOT_COMPLETED" />
+ </intent-filter>
+ </receiver>
+
+ <receiver android:name="github.daneren2005.dsub.receiver.MediaButtonIntentReceiver">
+ <intent-filter>
+ <action android:name="android.intent.action.MEDIA_BUTTON" />
+ </intent-filter>
+ </receiver>
+
+ <receiver android:name="github.daneren2005.dsub.receiver.AudioNoisyReceiver">
+ <intent-filter android:priority="999">
+ <action android:name="android.media.AUDIO_BECOMING_NOISY" />
+ </intent-filter>
+ </receiver>
+
+ <receiver android:name="github.daneren2005.dsub.receiver.A2dpIntentReceiver">
+ <intent-filter>
+ <action android:name="android.music.playstatusrequest"/>
+ </intent-filter>
+ </receiver>
+
+ <receiver
+ android:name="github.daneren2005.dsub.provider.DSubWidget4x1"
+ android:label="@string/widget.4x1">
+ <intent-filter>
+ <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
+ </intent-filter>
+ <meta-data android:name="android.appwidget.provider" android:resource="@xml/appwidget4x1"/>
+ </receiver>
+ <receiver
+ android:name="github.daneren2005.dsub.provider.DSubWidget4x2"
+ android:label="@string/widget.4x2">
+ <intent-filter>
+ <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
+ </intent-filter>
+ <meta-data android:name="android.appwidget.provider" android:resource="@xml/appwidget4x2"/>
+ </receiver>
+ <receiver
+ android:name="github.daneren2005.dsub.provider.DSubWidget4x3"
+ android:label="@string/widget.4x3">
+ <intent-filter>
+ <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
+ </intent-filter>
+ <meta-data android:name="android.appwidget.provider" android:resource="@xml/appwidget4x3"/>
+ </receiver>
+ <receiver
+ android:name="github.daneren2005.dsub.provider.DSubWidget4x4"
+ android:label="@string/widget.4x4">
+ <intent-filter>
+ <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
+ </intent-filter>
+ <meta-data android:name="android.appwidget.provider" android:resource="@xml/appwidget4x4"/>
+ </receiver>
+
+ <receiver
+ android:name="github.daneren2005.dsub.receiver.PlayActionReceiver">
+
+ <intent-filter>
+ <action android:name="com.twofortyfouram.locale.intent.action.FIRE_SETTING" />
+ </intent-filter>
+ </receiver>
+
+ <provider android:name="github.daneren2005.dsub.provider.DSubSearchProvider"
+ android:authorities="github.daneren2005.dsub.provider.DSubSearchProvider"/>
+ <provider android:name="github.daneren2005.dsub.provider.PlaylistStubProvider"
+ android:authorities="github.daneren2005.dsub.playlists.provider"
+ android:label="@string/button_bar.playlists"
+ android:exported="false"
+ android:syncable="true"/>
+ <provider android:name="github.daneren2005.dsub.provider.PodcastStubProvider"
+ android:authorities="github.daneren2005.dsub.podcasts.provider"
+ android:label="@string/button_bar.podcasts"
+ android:exported="false"
+ android:syncable="true"/>
+ <provider android:name="github.daneren2005.dsub.provider.StarredStubProvider"
+ android:authorities="github.daneren2005.dsub.starred.provider"
+ android:label="@string/main.albums_starred"
+ android:exported="false"
+ android:syncable="true"/>
+ <provider android:name="github.daneren2005.dsub.provider.MostRecentStubProvider"
+ android:authorities="github.daneren2005.dsub.mostrecent.provider"
+ android:label="@string/main.albums_newest"
+ android:exported="false"
+ android:syncable="true"/>
+
+ <meta-data android:name="android.app.default_searchable"
+ android:value="github.daneren2005.dsub.activity.QueryReceiverActivity"/>
+
+ <meta-data android:name="com.google.android.backup.api_key"
+ android:value="AEdPqrEAAAAIUhOMtwa_eG-f0oYUHnetl_Cz7cO9zae8ZXOK5w"/>
+
+ <meta-data
+ android:name="com.google.android.gms.version"
+ android:value="@integer/google_play_services_version" />
+ </application>
+
+</manifest>
diff --git a/src/github/daneren2005/dsub/activity/DownloadActivity.java b/app/src/main/java/github/daneren2005/dsub/activity/DownloadActivity.java
index e13a8b8c..e13a8b8c 100644
--- a/src/github/daneren2005/dsub/activity/DownloadActivity.java
+++ b/app/src/main/java/github/daneren2005/dsub/activity/DownloadActivity.java
diff --git a/src/github/daneren2005/dsub/activity/EditPlayActionActivity.java b/app/src/main/java/github/daneren2005/dsub/activity/EditPlayActionActivity.java
index e1f2cad3..e1f2cad3 100644
--- a/src/github/daneren2005/dsub/activity/EditPlayActionActivity.java
+++ b/app/src/main/java/github/daneren2005/dsub/activity/EditPlayActionActivity.java
diff --git a/src/github/daneren2005/dsub/activity/QueryReceiverActivity.java b/app/src/main/java/github/daneren2005/dsub/activity/QueryReceiverActivity.java
index eefb9c56..eefb9c56 100644
--- a/src/github/daneren2005/dsub/activity/QueryReceiverActivity.java
+++ b/app/src/main/java/github/daneren2005/dsub/activity/QueryReceiverActivity.java
diff --git a/src/github/daneren2005/dsub/activity/SettingsActivity.java b/app/src/main/java/github/daneren2005/dsub/activity/SettingsActivity.java
index d5ac60d3..d5ac60d3 100644
--- a/src/github/daneren2005/dsub/activity/SettingsActivity.java
+++ b/app/src/main/java/github/daneren2005/dsub/activity/SettingsActivity.java
diff --git a/src/github/daneren2005/dsub/activity/SubsonicActivity.java b/app/src/main/java/github/daneren2005/dsub/activity/SubsonicActivity.java
index 97b86e19..4651eb0b 100644
--- a/src/github/daneren2005/dsub/activity/SubsonicActivity.java
+++ b/app/src/main/java/github/daneren2005/dsub/activity/SubsonicActivity.java
@@ -1,860 +1,860 @@
-/*
- 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 github.daneren2005.dsub.activity;
-
-import android.app.UiModeManager;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.res.Configuration;
-import android.content.res.TypedArray;
-import android.media.AudioManager;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Environment;
-import android.support.v7.app.ActionBarDrawerToggle;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentManager;
-import android.support.v4.app.FragmentTransaction;
-import android.support.v4.widget.DrawerLayout;
-import android.support.v7.app.ActionBarActivity;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.view.WindowManager;
-import android.view.animation.AnimationUtils;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemSelectedListener;
-import android.widget.ArrayAdapter;
-import android.widget.ListView;
-import android.widget.Spinner;
-import android.widget.TextView;
-
-import java.io.File;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.domain.ServerInfo;
-import github.daneren2005.dsub.fragments.SubsonicFragment;
-import github.daneren2005.dsub.service.DownloadService;
-import github.daneren2005.dsub.service.HeadphoneListenerService;
-import github.daneren2005.dsub.util.Constants;
-import github.daneren2005.dsub.util.ImageLoader;
-import github.daneren2005.dsub.util.Util;
-import github.daneren2005.dsub.adapter.DrawerAdapter;
-import github.daneren2005.dsub.view.UpdateView;
-import github.daneren2005.dsub.util.UserUtil;
-
-public class SubsonicActivity extends ActionBarActivity implements OnItemSelectedListener {
- private static final String TAG = SubsonicActivity.class.getSimpleName();
- private static ImageLoader IMAGE_LOADER;
- protected static String theme;
- protected static boolean fullScreen;
- private String[] drawerItemsDescriptions;
- private String[] drawerItems;
- private boolean drawerIdle = true;
- private boolean[] enabledItems = {true, true, true, true, true};
- private boolean destroyed = false;
- private boolean finished = false;
- protected List<SubsonicFragment> backStack = new ArrayList<SubsonicFragment>();
- protected SubsonicFragment currentFragment;
- protected View primaryContainer;
- protected View secondaryContainer;
- protected boolean tv = false;
- protected boolean touchscreen = true;
- Spinner actionBarSpinner;
- ArrayAdapter<CharSequence> spinnerAdapter;
- ViewGroup rootView;
- DrawerLayout drawer;
- ActionBarDrawerToggle drawerToggle;
- DrawerAdapter drawerAdapter;
- ListView drawerList;
- TextView lastSelectedView = null;
- int lastSelectedPosition = 0;
- boolean drawerOpen = false;
-
- @Override
- protected void onCreate(Bundle bundle) {
- UiModeManager uiModeManager = (UiModeManager) getSystemService(UI_MODE_SERVICE);
- if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) {
- // tv = true;
- }
- PackageManager pm = getPackageManager();
- if(!pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN)) {
- touchscreen = false;
- }
-
- setUncaughtExceptionHandler();
- applyTheme();
- applyFullscreen();
- super.onCreate(bundle);
- startService(new Intent(this, DownloadService.class));
- setVolumeControlStream(AudioManager.STREAM_MUSIC);
-
- View actionbar = getLayoutInflater().inflate(R.layout.actionbar_spinner, null);
- actionBarSpinner = (Spinner)actionbar.findViewById(R.id.spinner);
- spinnerAdapter = new ArrayAdapter(this, android.R.layout.simple_spinner_item);
- spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- actionBarSpinner.setOnItemSelectedListener(this);
- actionBarSpinner.setAdapter(spinnerAdapter);
-
- getSupportActionBar().setCustomView(actionbar);
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
- getSupportActionBar().setHomeButtonEnabled(true);
-
- if(getIntent().hasExtra(Constants.FRAGMENT_POSITION)) {
- lastSelectedPosition = getIntent().getIntExtra(Constants.FRAGMENT_POSITION, 0);
- }
- }
-
- @Override
- protected void onPostCreate(Bundle savedInstanceState) {
- super.onPostCreate(savedInstanceState);
- // Sync the toggle state after onRestoreInstanceState has occurred.
- if(drawerToggle != null) {
- drawerToggle.syncState();
- }
-
- if(Util.shouldStartOnHeadphones(this)) {
- Intent serviceIntent = new Intent();
- serviceIntent.setClassName(this.getPackageName(), HeadphoneListenerService.class.getName());
- this.startService(serviceIntent);
- }
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- Util.registerMediaButtonEventReceiver(this);
-
- // Make sure to update theme
- if (theme != null && !theme.equals(Util.getTheme(this)) || fullScreen != Util.getPreferences(this).getBoolean(Constants.PREFERENCES_KEY_FULL_SCREEN, false)) {
- restart();
- overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
- }
-
- populateDrawer();
- UpdateView.addActiveActivity();
- }
-
- @Override
- protected void onPause() {
- super.onPause();
-
- UpdateView.removeActiveActivity();
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- destroyed = true;
- }
-
- @Override
- public void finish() {
- super.finish();
- Util.disablePendingTransition(this);
- }
-
- @Override
- public void startActivity(Intent intent) {
- if(intent.getComponent() != null) {
- String name = intent.getComponent().getClassName();
- if(name != null && name.indexOf("DownloadActivity") != -1) {
- intent.putExtra(Constants.FRAGMENT_POSITION, lastSelectedPosition);
- } else if(name != null && name.indexOf("SettingsActivity") != -1) {
- intent.putExtra(Constants.FRAGMENT_POSITION, drawerItems.length - 1);
- }
- }
- super.startActivity(intent);
- }
-
- @Override
- public void setContentView(int viewId) {
- if(isTv()) {
- super.setContentView(R.layout.static_drawer_activity);
- } else {
- super.setContentView(R.layout.abstract_activity);
- }
- rootView = (ViewGroup) findViewById(R.id.content_frame);
-
- if(viewId != 0) {
- LayoutInflater layoutInflater = getLayoutInflater();
- layoutInflater.inflate(viewId, rootView);
- }
-
- drawerList = (ListView) findViewById(R.id.left_drawer);
- drawerList.setOnItemClickListener(new ListView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, final View view, final int position, long id) {
- final int actualPosition = drawerAdapter.getActualPosition(position);
- if("Settings".equals(drawerItemsDescriptions[actualPosition])) {
- startActivity(new Intent(SubsonicActivity.this, SettingsActivity.class));
- drawer.closeDrawers();
- } else if("Admin".equals(drawerItemsDescriptions[actualPosition]) && UserUtil.isCurrentAdmin()) {
- UserUtil.confirmCredentials(SubsonicActivity.this, new Runnable() {
- @Override
- public void run() {
- drawerItemSelected(actualPosition, view);
- }
- });
- } else {
- drawerItemSelected(actualPosition, view);
- }
- }
- });
-
-
-
- if(!isTv()) {
- drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
-
- drawerToggle = new ActionBarDrawerToggle(this, drawer, R.string.common_appname, R.string.common_appname) {
- @Override
- public void onDrawerClosed(View view) {
- setTitle(currentFragment.getTitle());
-
- drawerIdle = true;
- drawerOpen = false;
-
- supportInvalidateOptionsMenu();
- }
-
- @Override
- public void onDrawerOpened(View view) {
- DownloadService downloadService = getDownloadService();
- if (downloadService == null || downloadService.getBackgroundDownloads().isEmpty()) {
- drawerAdapter.setDownloadVisible(false);
- } else {
- drawerAdapter.setDownloadVisible(true);
- }
-
- if (lastSelectedView == null && drawerList.getCount() > lastSelectedPosition) {
- lastSelectedView = (TextView) drawerList.getChildAt(lastSelectedPosition).findViewById(R.id.drawer_name);
- if (lastSelectedView != null) {
- lastSelectedView.setTextAppearance(SubsonicActivity.this, R.style.DSub_TextViewStyle_Bold);
- }
- }
-
- getSupportActionBar().setTitle(R.string.common_appname);
- getSupportActionBar().setDisplayShowCustomEnabled(false);
-
- drawerIdle = true;
- drawerOpen = true;
-
- supportInvalidateOptionsMenu();
- }
-
- @Override
- public void onDrawerSlide(View drawerView, float slideOffset) {
- super.onDrawerSlide(drawerView, slideOffset);
- drawerIdle = false;
- }
- };
- drawer.setDrawerListener(drawerToggle);
- drawerToggle.setDrawerIndicatorEnabled(false);
-
- drawer.setOnTouchListener(new View.OnTouchListener() {
- public boolean onTouch(View v, MotionEvent event) {
- if (drawerIdle && currentFragment != null && currentFragment.getGestureDetector() != null) {
- return currentFragment.getGestureDetector().onTouchEvent(event);
- } else {
- return false;
- }
- }
- });
- }
-
- // Check whether this is a tablet or not
- secondaryContainer = findViewById(R.id.fragment_second_container);
- if(secondaryContainer != null) {
- primaryContainer = findViewById(R.id.fragment_container);
- }
- }
-
- @Override
- public void onSaveInstanceState(Bundle savedInstanceState) {
- super.onSaveInstanceState(savedInstanceState);
- String[] ids = new String[backStack.size() + 1];
- ids[0] = currentFragment.getTag();
- int i = 1;
- for(SubsonicFragment frag: backStack) {
- ids[i] = frag.getTag();
- i++;
- }
- savedInstanceState.putStringArray(Constants.MAIN_BACK_STACK, ids);
- savedInstanceState.putInt(Constants.MAIN_BACK_STACK_SIZE, backStack.size() + 1);
- savedInstanceState.putInt(Constants.FRAGMENT_POSITION, lastSelectedPosition);
- }
- @Override
- public void onRestoreInstanceState(Bundle savedInstanceState) {
- super.onRestoreInstanceState(savedInstanceState);
- int size = savedInstanceState.getInt(Constants.MAIN_BACK_STACK_SIZE);
- String[] ids = savedInstanceState.getStringArray(Constants.MAIN_BACK_STACK);
- FragmentManager fm = getSupportFragmentManager();
- currentFragment = (SubsonicFragment)fm.findFragmentByTag(ids[0]);
- currentFragment.setPrimaryFragment(true);
- currentFragment.setSupportTag(ids[0]);
- supportInvalidateOptionsMenu();
- FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
- for(int i = 1; i < size; i++) {
- SubsonicFragment frag = (SubsonicFragment)fm.findFragmentByTag(ids[i]);
- frag.setSupportTag(ids[i]);
- if(secondaryContainer != null) {
- frag.setPrimaryFragment(false, true);
- }
- trans.hide(frag);
- backStack.add(frag);
- }
- trans.commit();
-
- // Current fragment is hidden in secondaryContainer
- if(secondaryContainer == null && !currentFragment.isVisible()) {
- trans = getSupportFragmentManager().beginTransaction();
- trans.remove(currentFragment);
- trans.commit();
- getSupportFragmentManager().executePendingTransactions();
-
- trans = getSupportFragmentManager().beginTransaction();
- trans.add(R.id.fragment_container, currentFragment, ids[0]);
- trans.commit();
- }
- // Current fragment needs to be moved over to secondaryContainer
- else if(secondaryContainer != null && secondaryContainer.findViewById(currentFragment.getRootId()) == null && backStack.size() > 0) {
- trans = getSupportFragmentManager().beginTransaction();
- trans.remove(currentFragment);
- trans.show(backStack.get(backStack.size() - 1));
- trans.commit();
- getSupportFragmentManager().executePendingTransactions();
-
- trans = getSupportFragmentManager().beginTransaction();
- trans.add(R.id.fragment_second_container, currentFragment, ids[0]);
- trans.commit();
-
- secondaryContainer.setVisibility(View.VISIBLE);
- }
-
- lastSelectedPosition = savedInstanceState.getInt(Constants.FRAGMENT_POSITION);
- recreateSpinner();
- }
-
- @Override
- public void onNewIntent(Intent intent) {
- super.onNewIntent(intent);
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- MenuInflater menuInflater = getMenuInflater();
- if(drawerOpen) {
- menuInflater.inflate(R.menu.drawer_menu, menu);
- } else if(currentFragment != null) {
- try {
- currentFragment.setContext(this);
- currentFragment.onCreateOptionsMenu(menu, menuInflater);
-
- if(isTouchscreen()) {
- menu.setGroupVisible(R.id.not_touchscreen, false);
- }
- } catch(Exception e) {
- Log.w(TAG, "Error on creating options menu", e);
- }
- }
- return true;
- }
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if(drawerToggle != null && drawerToggle.onOptionsItemSelected(item)) {
- return true;
- } else if(item.getItemId() == android.R.id.home) {
- onBackPressed();
- return true;
- }
-
- return currentFragment.onOptionsItemSelected(item);
- }
-
- @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().isRemoteEnabled();
-
- if (isVolumeAdjust && isJukebox) {
- getDownloadService().updateRemoteVolume(isVolumeUp);
- return true;
- }
- return super.onKeyDown(keyCode, event);
- }
-
- @Override
- public void setTitle(CharSequence title) {
- if(title != null && !title.equals(getSupportActionBar().getTitle())) {
- getSupportActionBar().setTitle(title);
- recreateSpinner();
- }
- }
- public void setSubtitle(CharSequence title) {
- getSupportActionBar().setSubtitle(title);
- }
-
- @Override
- public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- int top = spinnerAdapter.getCount() - 1;
- if(position < top) {
- for(int i = top; i > position; i--) {
- removeCurrent();
- }
- }
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
-
- }
-
- private void populateDrawer() {
- SharedPreferences prefs = Util.getPreferences(this);
- boolean podcastsEnabled = prefs.getBoolean(Constants.PREFERENCES_KEY_PODCASTS_ENABLED, true);
- boolean bookmarksEnabled = prefs.getBoolean(Constants.PREFERENCES_KEY_BOOKMARKS_ENABLED, true) && !Util.isOffline(this) && ServerInfo.canBookmark(this);
- boolean sharedEnabled = prefs.getBoolean(Constants.PREFERENCES_KEY_SHARED_ENABLED, true) && !Util.isOffline(this);
- boolean chatEnabled = prefs.getBoolean(Constants.PREFERENCES_KEY_CHAT_ENABLED, true) && !Util.isOffline(this);
- boolean adminEnabled = prefs.getBoolean(Constants.PREFERENCES_KEY_ADMIN_ENABLED, true) && !Util.isOffline(this);
-
- if(drawerItems == null || !enabledItems[0] == podcastsEnabled || !enabledItems[1] == bookmarksEnabled || !enabledItems[2] == sharedEnabled || !enabledItems[3] == chatEnabled || !enabledItems[4] == adminEnabled) {
- drawerItems = getResources().getStringArray(R.array.drawerItems);
- drawerItemsDescriptions = getResources().getStringArray(R.array.drawerItemsDescriptions);
-
- List<String> drawerItemsList = new ArrayList<String>(Arrays.asList(drawerItems));
- List<Integer> drawerItemsIconsList = new ArrayList<Integer>();
- List<Boolean> drawerItemsVisibleList = new ArrayList<Boolean>();
-
- int[] arrayAttr = {R.attr.drawerItemsIcons};
- TypedArray arrayType = obtainStyledAttributes(arrayAttr);
- int arrayId = arrayType.getResourceId(0, 0);
- TypedArray iconType = getResources().obtainTypedArray(arrayId);
- for(int i = 0; i < drawerItemsList.size(); i++) {
- drawerItemsIconsList.add(iconType.getResourceId(i, 0));
- drawerItemsVisibleList.add(true);
- }
- iconType.recycle();
- arrayType.recycle();
-
- // Hide listings user doesn't want to see
- if(!podcastsEnabled) {
- drawerItemsVisibleList.set(3, false);
- }
- if(!bookmarksEnabled) {
- drawerItemsVisibleList.set(4, false);
- }
- if(!sharedEnabled) {
- drawerItemsVisibleList.set(5, false);
- }
- if(!chatEnabled) {
- drawerItemsVisibleList.set(6, false);
- }
- if(!adminEnabled) {
- drawerItemsVisibleList.set(7, false);
- }
- if(!getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD_VIEW)) {
- drawerItemsVisibleList.set(8, false);
- }
-
- drawerList.setAdapter(drawerAdapter = new DrawerAdapter(this, drawerItemsList, drawerItemsIconsList, drawerItemsVisibleList));
- enabledItems[0] = podcastsEnabled;
- enabledItems[1] = bookmarksEnabled;
- enabledItems[2] = sharedEnabled;
- enabledItems[3] = chatEnabled;
- enabledItems[4] = adminEnabled;
-
- String fragmentType = getIntent().getStringExtra(Constants.INTENT_EXTRA_FRAGMENT_TYPE);
- if(fragmentType != null && lastSelectedPosition == 0) {
- for(int i = 0; i < drawerItemsDescriptions.length; i++) {
- if(fragmentType.equals(drawerItemsDescriptions[i])) {
- lastSelectedPosition = drawerAdapter.getAdapterPosition(i);
- break;
- }
- }
- }
-
- if(drawerList.getChildAt(lastSelectedPosition) == null) {
- lastSelectedView = null;
- drawerAdapter.setSelectedPosition(lastSelectedPosition);
- } else {
- lastSelectedView = (TextView) drawerList.getChildAt(lastSelectedPosition).findViewById(R.id.drawer_name);
- if(lastSelectedView != null) {
- lastSelectedView.setTextAppearance(SubsonicActivity.this, R.style.DSub_TextViewStyle_Bold);
- }
- }
- }
- }
-
- private void drawerItemSelected(int position, View view) {
- startFragmentActivity(drawerItemsDescriptions[position]);
-
- if(lastSelectedView != view) {
- if(lastSelectedView != null) {
- lastSelectedView.setTextAppearance(this, R.style.DSub_TextViewStyle);
- }
-
- lastSelectedView = (TextView) view.findViewById(R.id.drawer_name);
- lastSelectedView.setTextAppearance(this, R.style.DSub_TextViewStyle_Bold);
- lastSelectedPosition = position;
- }
- }
-
- public void startFragmentActivity(String fragmentType) {
- Intent intent = new Intent();
- intent.setClass(SubsonicActivity.this, SubsonicFragmentActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- if(!"".equals(fragmentType)) {
- intent.putExtra(Constants.INTENT_EXTRA_FRAGMENT_TYPE, fragmentType);
- }
- startActivity(intent);
- finish();
- }
-
- protected void exit() {
- if(((Object) this).getClass() != SubsonicFragmentActivity.class) {
- Intent intent = new Intent(this, SubsonicFragmentActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- intent.putExtra(Constants.INTENT_EXTRA_NAME_EXIT, true);
- Util.startActivityWithoutTransition(this, intent);
- } else {
- finished = true;
- this.stopService(new Intent(this, DownloadService.class));
- this.finish();
- }
- }
-
- public boolean onBackPressedSupport() {
- if(drawerOpen) {
- drawer.closeDrawers();
- return false;
- } else if(backStack.size() > 0) {
- removeCurrent();
- return false;
- } else {
- return true;
- }
- }
-
- @Override
- public void onBackPressed() {
- if(onBackPressedSupport()) {
- super.onBackPressed();
- }
- }
-
- public void replaceFragment(SubsonicFragment fragment, int tag) {
- replaceFragment(fragment, tag, false);
- }
- public void replaceFragment(SubsonicFragment fragment, int tag, boolean replaceCurrent) {
- SubsonicFragment oldFragment = currentFragment;
- if(currentFragment != null) {
- currentFragment.setPrimaryFragment(false, secondaryContainer != null);
- }
- backStack.add(currentFragment);
-
- currentFragment = fragment;
- currentFragment.setPrimaryFragment(true);
- supportInvalidateOptionsMenu();
-
- if(secondaryContainer == null) {
- FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
- trans.setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left, R.anim.enter_from_left, R.anim.exit_to_right);
- trans.hide(oldFragment);
- trans.add(R.id.fragment_container, fragment, tag + "");
- trans.commit();
- } else {
- // Make sure secondary container is visible now
- secondaryContainer.setVisibility(View.VISIBLE);
-
- FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
-
- // Check to see if you need to put on top of old left or not
- if(backStack.size() > 1) {
- // Move old right to left if there is a backstack already
- SubsonicFragment newLeftFragment = backStack.get(backStack.size() - 1);
- if(replaceCurrent) {
- // trans.setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left, R.anim.enter_from_left, R.anim.exit_to_right);
- }
- trans.remove(newLeftFragment);
-
- // Only move right to left if replaceCurrent is false
- if(!replaceCurrent) {
- SubsonicFragment oldLeftFragment = backStack.get(backStack.size() - 2);
- oldLeftFragment.setSecondaryFragment(false);
- // trans.setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left, R.anim.enter_from_left, R.anim.exit_to_right);
- trans.hide(oldLeftFragment);
-
- // Make sure remove is finished before adding
- trans.commit();
- getSupportFragmentManager().executePendingTransactions();
-
- trans = getSupportFragmentManager().beginTransaction();
- // trans.setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left, R.anim.enter_from_left, R.anim.exit_to_right);
- trans.add(R.id.fragment_container, newLeftFragment, newLeftFragment.getSupportTag() + "");
- } else {
- backStack.remove(backStack.size() - 1);
- }
- }
-
- // Add fragment to the right container
- trans.setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left, R.anim.enter_from_left, R.anim.exit_to_right);
- trans.add(R.id.fragment_second_container, fragment, tag + "");
-
- // Commit it all
- trans.commit();
- }
- recreateSpinner();
- }
- public void removeCurrent() {
- if(currentFragment != null) {
- currentFragment.setPrimaryFragment(false);
- }
- Fragment oldFrag = currentFragment;
-
- currentFragment = backStack.remove(backStack.size() - 1);
- currentFragment.setPrimaryFragment(true, false);
- supportInvalidateOptionsMenu();
-
- if(secondaryContainer == null) {
- FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
- trans.setCustomAnimations(R.anim.enter_from_left, R.anim.exit_to_right, R.anim.enter_from_right, R.anim.exit_to_left);
- trans.remove(oldFrag);
- trans.show(currentFragment);
- trans.commit();
- } else {
- FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
-
- // Remove old right fragment
- trans.setCustomAnimations(R.anim.enter_from_left, R.anim.exit_to_right, R.anim.enter_from_right, R.anim.exit_to_left);
- trans.remove(oldFrag);
-
- // Only switch places if there is a backstack, otherwise primary container is correct
- if(backStack.size() > 0) {
- trans.setCustomAnimations(0, 0, 0, 0);
- // Add current left fragment to right side
- trans.remove(currentFragment);
-
- // Make sure remove is finished before adding
- trans.commit();
- getSupportFragmentManager().executePendingTransactions();
-
- trans = getSupportFragmentManager().beginTransaction();
- // trans.setCustomAnimations(R.anim.enter_from_left, R.anim.exit_to_right, R.anim.enter_from_right, R.anim.exit_to_left);
- trans.add(R.id.fragment_second_container, currentFragment, currentFragment.getSupportTag() + "");
-
- SubsonicFragment newLeftFragment = backStack.get(backStack.size() - 1);
- newLeftFragment.setSecondaryFragment(true);
- trans.show(newLeftFragment);
- } else {
- secondaryContainer.startAnimation(AnimationUtils.loadAnimation(this, R.anim.exit_to_right));
- secondaryContainer.setVisibility(View.GONE);
- }
-
- trans.commit();
- }
- recreateSpinner();
- }
-
- public void invalidate() {
- if(currentFragment != null) {
- while(backStack.size() > 0) {
- removeCurrent();
- }
-
- currentFragment.invalidate();
- populateDrawer();
- }
-
- supportInvalidateOptionsMenu();
- }
-
- protected void recreateSpinner() {
- if(currentFragment == null || currentFragment.getTitle() == null) {
- return;
- }
-
- if(backStack.size() > 0) {
- spinnerAdapter.clear();
- for(int i = 0; i < backStack.size(); i++) {
- CharSequence title = backStack.get(i).getTitle();
- if(title != null) {
- spinnerAdapter.add(title);
- } else {
- spinnerAdapter.add("null");
- }
- }
- if(currentFragment.getTitle() != null) {
- spinnerAdapter.add(currentFragment.getTitle());
- } else {
- spinnerAdapter.add("null");
- }
- spinnerAdapter.notifyDataSetChanged();
- actionBarSpinner.setSelection(spinnerAdapter.getCount() - 1);
- if(!isTv()) {
- getSupportActionBar().setDisplayShowCustomEnabled(true);
- }
- } else if(!isTv()) {
- getSupportActionBar().setDisplayShowCustomEnabled(false);
- }
- }
-
- protected void restart() {
- Intent intent = new Intent(this, ((Object) this).getClass());
- intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- intent.putExtras(getIntent());
- Util.startActivityWithoutTransition(this, intent);
- }
-
- private void applyTheme() {
- theme = Util.getTheme(this);
-
- if(theme != null && theme.indexOf("fullscreen") != -1) {
- theme = theme.substring(0, theme.indexOf("_fullscreen"));
- Util.setTheme(this, theme);
- }
-
- Util.applyTheme(this, theme);
- }
- private void applyFullscreen() {
- fullScreen = Util.getPreferences(this).getBoolean(Constants.PREFERENCES_KEY_FULL_SCREEN, false);
- if(fullScreen || isTv()) {
- // Hide additional elements on higher Android versions
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
- int flags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
- View.SYSTEM_UI_FLAG_FULLSCREEN |
- View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
-
- getWindow().getDecorView().setSystemUiVisibility(flags);
- } else if(Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
- getWindow().requestFeature(Window.FEATURE_NO_TITLE);
- }
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
- }
- }
-
- public boolean isDestroyedCompat() {
- return destroyed;
- }
-
- public synchronized ImageLoader getImageLoader() {
- if (IMAGE_LOADER == null) {
- IMAGE_LOADER = new ImageLoader(this);
- }
- return IMAGE_LOADER;
- }
- public synchronized static ImageLoader getStaticImageLoader(Context context) {
- if (IMAGE_LOADER == null) {
- IMAGE_LOADER = new ImageLoader(context);
- }
- return IMAGE_LOADER;
- }
-
- public DownloadService getDownloadService() {
- if(finished) {
- return null;
- }
-
- // If service is not available, request it to start and wait for it.
- for (int i = 0; i < 5; i++) {
- DownloadService downloadService = DownloadService.getInstance();
- if (downloadService != null) {
- return downloadService;
- }
- Log.w(TAG, "DownloadService not running. Attempting to start it.");
- startService(new Intent(this, DownloadService.class));
- Util.sleepQuietly(50L);
- }
- return DownloadService.getInstance();
- }
-
- public static String getThemeName() {
- return theme;
- }
-
- public boolean isTv() {
- return tv;
- }
- public boolean isTouchscreen() {
- return touchscreen;
- }
-
- private void setUncaughtExceptionHandler() {
- Thread.UncaughtExceptionHandler handler = Thread.getDefaultUncaughtExceptionHandler();
- if (!(handler instanceof SubsonicActivity.SubsonicUncaughtExceptionHandler)) {
- Thread.setDefaultUncaughtExceptionHandler(new SubsonicActivity.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("github.daneren2005.dsub", 0);
- file = new File(Environment.getExternalStorageDirectory(), "dsub-stacktrace.txt");
- printWriter = new PrintWriter(file);
- printWriter.println("Android API level: " + Build.VERSION.SDK);
- 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);
- }
-
- }
- }
- }
-}
+/*
+ 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 github.daneren2005.dsub.activity;
+
+import android.app.UiModeManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.content.res.TypedArray;
+import android.media.AudioManager;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Environment;
+import android.support.v7.app.ActionBarDrawerToggle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentTransaction;
+import android.support.v4.widget.DrawerLayout;
+import android.support.v7.app.ActionBarActivity;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.animation.AnimationUtils;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.ServerInfo;
+import github.daneren2005.dsub.fragments.SubsonicFragment;
+import github.daneren2005.dsub.service.DownloadService;
+import github.daneren2005.dsub.service.HeadphoneListenerService;
+import github.daneren2005.dsub.util.Constants;
+import github.daneren2005.dsub.util.ImageLoader;
+import github.daneren2005.dsub.util.Util;
+import github.daneren2005.dsub.adapter.DrawerAdapter;
+import github.daneren2005.dsub.view.UpdateView;
+import github.daneren2005.dsub.util.UserUtil;
+
+public class SubsonicActivity extends ActionBarActivity implements OnItemSelectedListener {
+ private static final String TAG = SubsonicActivity.class.getSimpleName();
+ private static ImageLoader IMAGE_LOADER;
+ protected static String theme;
+ protected static boolean fullScreen;
+ private String[] drawerItemsDescriptions;
+ private String[] drawerItems;
+ private boolean drawerIdle = true;
+ private boolean[] enabledItems = {true, true, true, true, true};
+ private boolean destroyed = false;
+ private boolean finished = false;
+ protected List<SubsonicFragment> backStack = new ArrayList<SubsonicFragment>();
+ protected SubsonicFragment currentFragment;
+ protected View primaryContainer;
+ protected View secondaryContainer;
+ protected boolean tv = false;
+ protected boolean touchscreen = true;
+ Spinner actionBarSpinner;
+ ArrayAdapter<CharSequence> spinnerAdapter;
+ ViewGroup rootView;
+ DrawerLayout drawer;
+ ActionBarDrawerToggle drawerToggle;
+ DrawerAdapter drawerAdapter;
+ ListView drawerList;
+ TextView lastSelectedView = null;
+ int lastSelectedPosition = 0;
+ boolean drawerOpen = false;
+
+ @Override
+ protected void onCreate(Bundle bundle) {
+ UiModeManager uiModeManager = (UiModeManager) getSystemService(UI_MODE_SERVICE);
+ if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) {
+ // tv = true;
+ }
+ PackageManager pm = getPackageManager();
+ if(!pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN)) {
+ touchscreen = false;
+ }
+
+ setUncaughtExceptionHandler();
+ applyTheme();
+ applyFullscreen();
+ super.onCreate(bundle);
+ startService(new Intent(this, DownloadService.class));
+ setVolumeControlStream(AudioManager.STREAM_MUSIC);
+
+ View actionbar = getLayoutInflater().inflate(R.layout.actionbar_spinner, null);
+ actionBarSpinner = (Spinner)actionbar.findViewById(R.id.spinner);
+ spinnerAdapter = new ArrayAdapter(this, android.R.layout.simple_spinner_item);
+ spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ actionBarSpinner.setOnItemSelectedListener(this);
+ actionBarSpinner.setAdapter(spinnerAdapter);
+
+ getSupportActionBar().setCustomView(actionbar);
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setHomeButtonEnabled(true);
+
+ if(getIntent().hasExtra(Constants.FRAGMENT_POSITION)) {
+ lastSelectedPosition = getIntent().getIntExtra(Constants.FRAGMENT_POSITION, 0);
+ }
+ }
+
+ @Override
+ protected void onPostCreate(Bundle savedInstanceState) {
+ super.onPostCreate(savedInstanceState);
+ // Sync the toggle state after onRestoreInstanceState has occurred.
+ if(drawerToggle != null) {
+ drawerToggle.syncState();
+ }
+
+ if(Util.shouldStartOnHeadphones(this)) {
+ Intent serviceIntent = new Intent();
+ serviceIntent.setClassName(this.getPackageName(), HeadphoneListenerService.class.getName());
+ this.startService(serviceIntent);
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ Util.registerMediaButtonEventReceiver(this);
+
+ // Make sure to update theme
+ if (theme != null && !theme.equals(Util.getTheme(this)) || fullScreen != Util.getPreferences(this).getBoolean(Constants.PREFERENCES_KEY_FULL_SCREEN, false)) {
+ restart();
+ overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
+ }
+
+ populateDrawer();
+ UpdateView.addActiveActivity();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+
+ UpdateView.removeActiveActivity();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ destroyed = true;
+ }
+
+ @Override
+ public void finish() {
+ super.finish();
+ Util.disablePendingTransition(this);
+ }
+
+ @Override
+ public void startActivity(Intent intent) {
+ if(intent.getComponent() != null) {
+ String name = intent.getComponent().getClassName();
+ if(name != null && name.indexOf("DownloadActivity") != -1) {
+ intent.putExtra(Constants.FRAGMENT_POSITION, lastSelectedPosition);
+ } else if(name != null && name.indexOf("SettingsActivity") != -1) {
+ intent.putExtra(Constants.FRAGMENT_POSITION, drawerItems.length - 1);
+ }
+ }
+ super.startActivity(intent);
+ }
+
+ @Override
+ public void setContentView(int viewId) {
+ if(isTv()) {
+ super.setContentView(R.layout.static_drawer_activity);
+ } else {
+ super.setContentView(R.layout.abstract_activity);
+ }
+ rootView = (ViewGroup) findViewById(R.id.content_frame);
+
+ if(viewId != 0) {
+ LayoutInflater layoutInflater = getLayoutInflater();
+ layoutInflater.inflate(viewId, rootView);
+ }
+
+ drawerList = (ListView) findViewById(R.id.left_drawer);
+ drawerList.setOnItemClickListener(new ListView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, final View view, final int position, long id) {
+ final int actualPosition = drawerAdapter.getActualPosition(position);
+ if("Settings".equals(drawerItemsDescriptions[actualPosition])) {
+ startActivity(new Intent(SubsonicActivity.this, SettingsActivity.class));
+ drawer.closeDrawers();
+ } else if("Admin".equals(drawerItemsDescriptions[actualPosition]) && UserUtil.isCurrentAdmin()) {
+ UserUtil.confirmCredentials(SubsonicActivity.this, new Runnable() {
+ @Override
+ public void run() {
+ drawerItemSelected(actualPosition, view);
+ }
+ });
+ } else {
+ drawerItemSelected(actualPosition, view);
+ }
+ }
+ });
+
+
+
+ if(!isTv()) {
+ drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
+
+ drawerToggle = new ActionBarDrawerToggle(this, drawer, R.string.common_appname, R.string.common_appname) {
+ @Override
+ public void onDrawerClosed(View view) {
+ setTitle(currentFragment.getTitle());
+
+ drawerIdle = true;
+ drawerOpen = false;
+
+ supportInvalidateOptionsMenu();
+ }
+
+ @Override
+ public void onDrawerOpened(View view) {
+ DownloadService downloadService = getDownloadService();
+ if (downloadService == null || downloadService.getBackgroundDownloads().isEmpty()) {
+ drawerAdapter.setDownloadVisible(false);
+ } else {
+ drawerAdapter.setDownloadVisible(true);
+ }
+
+ if (lastSelectedView == null && drawerList.getCount() > lastSelectedPosition) {
+ lastSelectedView = (TextView) drawerList.getChildAt(lastSelectedPosition).findViewById(R.id.drawer_name);
+ if (lastSelectedView != null) {
+ lastSelectedView.setTextAppearance(SubsonicActivity.this, R.style.DSub_TextViewStyle_Bold);
+ }
+ }
+
+ getSupportActionBar().setTitle(R.string.common_appname);
+ getSupportActionBar().setDisplayShowCustomEnabled(false);
+
+ drawerIdle = true;
+ drawerOpen = true;
+
+ supportInvalidateOptionsMenu();
+ }
+
+ @Override
+ public void onDrawerSlide(View drawerView, float slideOffset) {
+ super.onDrawerSlide(drawerView, slideOffset);
+ drawerIdle = false;
+ }
+ };
+ drawer.setDrawerListener(drawerToggle);
+ drawerToggle.setDrawerIndicatorEnabled(false);
+
+ drawer.setOnTouchListener(new View.OnTouchListener() {
+ public boolean onTouch(View v, MotionEvent event) {
+ if (drawerIdle && currentFragment != null && currentFragment.getGestureDetector() != null) {
+ return currentFragment.getGestureDetector().onTouchEvent(event);
+ } else {
+ return false;
+ }
+ }
+ });
+ }
+
+ // Check whether this is a tablet or not
+ secondaryContainer = findViewById(R.id.fragment_second_container);
+ if(secondaryContainer != null) {
+ primaryContainer = findViewById(R.id.fragment_container);
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle savedInstanceState) {
+ super.onSaveInstanceState(savedInstanceState);
+ String[] ids = new String[backStack.size() + 1];
+ ids[0] = currentFragment.getTag();
+ int i = 1;
+ for(SubsonicFragment frag: backStack) {
+ ids[i] = frag.getTag();
+ i++;
+ }
+ savedInstanceState.putStringArray(Constants.MAIN_BACK_STACK, ids);
+ savedInstanceState.putInt(Constants.MAIN_BACK_STACK_SIZE, backStack.size() + 1);
+ savedInstanceState.putInt(Constants.FRAGMENT_POSITION, lastSelectedPosition);
+ }
+ @Override
+ public void onRestoreInstanceState(Bundle savedInstanceState) {
+ super.onRestoreInstanceState(savedInstanceState);
+ int size = savedInstanceState.getInt(Constants.MAIN_BACK_STACK_SIZE);
+ String[] ids = savedInstanceState.getStringArray(Constants.MAIN_BACK_STACK);
+ FragmentManager fm = getSupportFragmentManager();
+ currentFragment = (SubsonicFragment)fm.findFragmentByTag(ids[0]);
+ currentFragment.setPrimaryFragment(true);
+ currentFragment.setSupportTag(ids[0]);
+ supportInvalidateOptionsMenu();
+ FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
+ for(int i = 1; i < size; i++) {
+ SubsonicFragment frag = (SubsonicFragment)fm.findFragmentByTag(ids[i]);
+ frag.setSupportTag(ids[i]);
+ if(secondaryContainer != null) {
+ frag.setPrimaryFragment(false, true);
+ }
+ trans.hide(frag);
+ backStack.add(frag);
+ }
+ trans.commit();
+
+ // Current fragment is hidden in secondaryContainer
+ if(secondaryContainer == null && !currentFragment.isVisible()) {
+ trans = getSupportFragmentManager().beginTransaction();
+ trans.remove(currentFragment);
+ trans.commit();
+ getSupportFragmentManager().executePendingTransactions();
+
+ trans = getSupportFragmentManager().beginTransaction();
+ trans.add(R.id.fragment_container, currentFragment, ids[0]);
+ trans.commit();
+ }
+ // Current fragment needs to be moved over to secondaryContainer
+ else if(secondaryContainer != null && secondaryContainer.findViewById(currentFragment.getRootId()) == null && backStack.size() > 0) {
+ trans = getSupportFragmentManager().beginTransaction();
+ trans.remove(currentFragment);
+ trans.show(backStack.get(backStack.size() - 1));
+ trans.commit();
+ getSupportFragmentManager().executePendingTransactions();
+
+ trans = getSupportFragmentManager().beginTransaction();
+ trans.add(R.id.fragment_second_container, currentFragment, ids[0]);
+ trans.commit();
+
+ secondaryContainer.setVisibility(View.VISIBLE);
+ }
+
+ lastSelectedPosition = savedInstanceState.getInt(Constants.FRAGMENT_POSITION);
+ recreateSpinner();
+ }
+
+ @Override
+ public void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater menuInflater = getMenuInflater();
+ if(drawerOpen) {
+ menuInflater.inflate(R.menu.drawer_menu, menu);
+ } else if(currentFragment != null) {
+ try {
+ currentFragment.setContext(this);
+ currentFragment.onCreateOptionsMenu(menu, menuInflater);
+
+ if(isTouchscreen()) {
+ menu.setGroupVisible(R.id.not_touchscreen, false);
+ }
+ } catch(Exception e) {
+ Log.w(TAG, "Error on creating options menu", e);
+ }
+ }
+ return true;
+ }
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if(drawerToggle != null && drawerToggle.onOptionsItemSelected(item)) {
+ return true;
+ } else if(item.getItemId() == android.R.id.home) {
+ onBackPressed();
+ return true;
+ }
+
+ return currentFragment.onOptionsItemSelected(item);
+ }
+
+ @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().isRemoteEnabled();
+
+ if (isVolumeAdjust && isJukebox) {
+ getDownloadService().updateRemoteVolume(isVolumeUp);
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ @Override
+ public void setTitle(CharSequence title) {
+ if(title != null && !title.equals(getSupportActionBar().getTitle())) {
+ getSupportActionBar().setTitle(title);
+ recreateSpinner();
+ }
+ }
+ public void setSubtitle(CharSequence title) {
+ getSupportActionBar().setSubtitle(title);
+ }
+
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ int top = spinnerAdapter.getCount() - 1;
+ if(position < top) {
+ for(int i = top; i > position; i--) {
+ removeCurrent();
+ }
+ }
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+
+ }
+
+ private void populateDrawer() {
+ SharedPreferences prefs = Util.getPreferences(this);
+ boolean podcastsEnabled = prefs.getBoolean(Constants.PREFERENCES_KEY_PODCASTS_ENABLED, true);
+ boolean bookmarksEnabled = prefs.getBoolean(Constants.PREFERENCES_KEY_BOOKMARKS_ENABLED, true) && !Util.isOffline(this) && ServerInfo.canBookmark(this);
+ boolean sharedEnabled = prefs.getBoolean(Constants.PREFERENCES_KEY_SHARED_ENABLED, true) && !Util.isOffline(this);
+ boolean chatEnabled = prefs.getBoolean(Constants.PREFERENCES_KEY_CHAT_ENABLED, true) && !Util.isOffline(this);
+ boolean adminEnabled = prefs.getBoolean(Constants.PREFERENCES_KEY_ADMIN_ENABLED, true) && !Util.isOffline(this);
+
+ if(drawerItems == null || !enabledItems[0] == podcastsEnabled || !enabledItems[1] == bookmarksEnabled || !enabledItems[2] == sharedEnabled || !enabledItems[3] == chatEnabled || !enabledItems[4] == adminEnabled) {
+ drawerItems = getResources().getStringArray(R.array.drawerItems);
+ drawerItemsDescriptions = getResources().getStringArray(R.array.drawerItemsDescriptions);
+
+ List<String> drawerItemsList = new ArrayList<String>(Arrays.asList(drawerItems));
+ List<Integer> drawerItemsIconsList = new ArrayList<Integer>();
+ List<Boolean> drawerItemsVisibleList = new ArrayList<Boolean>();
+
+ int[] arrayAttr = {R.attr.drawerItemsIcons};
+ TypedArray arrayType = obtainStyledAttributes(arrayAttr);
+ int arrayId = arrayType.getResourceId(0, 0);
+ TypedArray iconType = getResources().obtainTypedArray(arrayId);
+ for(int i = 0; i < drawerItemsList.size(); i++) {
+ drawerItemsIconsList.add(iconType.getResourceId(i, 0));
+ drawerItemsVisibleList.add(true);
+ }
+ iconType.recycle();
+ arrayType.recycle();
+
+ // Hide listings user doesn't want to see
+ if(!podcastsEnabled) {
+ drawerItemsVisibleList.set(3, false);
+ }
+ if(!bookmarksEnabled) {
+ drawerItemsVisibleList.set(4, false);
+ }
+ if(!sharedEnabled) {
+ drawerItemsVisibleList.set(5, false);
+ }
+ if(!chatEnabled) {
+ drawerItemsVisibleList.set(6, false);
+ }
+ if(!adminEnabled) {
+ drawerItemsVisibleList.set(7, false);
+ }
+ if(!getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD_VIEW)) {
+ drawerItemsVisibleList.set(8, false);
+ }
+
+ drawerList.setAdapter(drawerAdapter = new DrawerAdapter(this, drawerItemsList, drawerItemsIconsList, drawerItemsVisibleList));
+ enabledItems[0] = podcastsEnabled;
+ enabledItems[1] = bookmarksEnabled;
+ enabledItems[2] = sharedEnabled;
+ enabledItems[3] = chatEnabled;
+ enabledItems[4] = adminEnabled;
+
+ String fragmentType = getIntent().getStringExtra(Constants.INTENT_EXTRA_FRAGMENT_TYPE);
+ if(fragmentType != null && lastSelectedPosition == 0) {
+ for(int i = 0; i < drawerItemsDescriptions.length; i++) {
+ if(fragmentType.equals(drawerItemsDescriptions[i])) {
+ lastSelectedPosition = drawerAdapter.getAdapterPosition(i);
+ break;
+ }
+ }
+ }
+
+ if(drawerList.getChildAt(lastSelectedPosition) == null) {
+ lastSelectedView = null;
+ drawerAdapter.setSelectedPosition(lastSelectedPosition);
+ } else {
+ lastSelectedView = (TextView) drawerList.getChildAt(lastSelectedPosition).findViewById(R.id.drawer_name);
+ if(lastSelectedView != null) {
+ lastSelectedView.setTextAppearance(SubsonicActivity.this, R.style.DSub_TextViewStyle_Bold);
+ }
+ }
+ }
+ }
+
+ private void drawerItemSelected(int position, View view) {
+ startFragmentActivity(drawerItemsDescriptions[position]);
+
+ if(lastSelectedView != view) {
+ if(lastSelectedView != null) {
+ lastSelectedView.setTextAppearance(this, R.style.DSub_TextViewStyle);
+ }
+
+ lastSelectedView = (TextView) view.findViewById(R.id.drawer_name);
+ lastSelectedView.setTextAppearance(this, R.style.DSub_TextViewStyle_Bold);
+ lastSelectedPosition = position;
+ }
+ }
+
+ public void startFragmentActivity(String fragmentType) {
+ Intent intent = new Intent();
+ intent.setClass(SubsonicActivity.this, SubsonicFragmentActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ if(!"".equals(fragmentType)) {
+ intent.putExtra(Constants.INTENT_EXTRA_FRAGMENT_TYPE, fragmentType);
+ }
+ startActivity(intent);
+ finish();
+ }
+
+ protected void exit() {
+ if(((Object) this).getClass() != SubsonicFragmentActivity.class) {
+ Intent intent = new Intent(this, SubsonicFragmentActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ intent.putExtra(Constants.INTENT_EXTRA_NAME_EXIT, true);
+ Util.startActivityWithoutTransition(this, intent);
+ } else {
+ finished = true;
+ this.stopService(new Intent(this, DownloadService.class));
+ this.finish();
+ }
+ }
+
+ public boolean onBackPressedSupport() {
+ if(drawerOpen) {
+ drawer.closeDrawers();
+ return false;
+ } else if(backStack.size() > 0) {
+ removeCurrent();
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ @Override
+ public void onBackPressed() {
+ if(onBackPressedSupport()) {
+ super.onBackPressed();
+ }
+ }
+
+ public void replaceFragment(SubsonicFragment fragment, int tag) {
+ replaceFragment(fragment, tag, false);
+ }
+ public void replaceFragment(SubsonicFragment fragment, int tag, boolean replaceCurrent) {
+ SubsonicFragment oldFragment = currentFragment;
+ if(currentFragment != null) {
+ currentFragment.setPrimaryFragment(false, secondaryContainer != null);
+ }
+ backStack.add(currentFragment);
+
+ currentFragment = fragment;
+ currentFragment.setPrimaryFragment(true);
+ supportInvalidateOptionsMenu();
+
+ if(secondaryContainer == null) {
+ FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
+ trans.setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left, R.anim.enter_from_left, R.anim.exit_to_right);
+ trans.hide(oldFragment);
+ trans.add(R.id.fragment_container, fragment, tag + "");
+ trans.commit();
+ } else {
+ // Make sure secondary container is visible now
+ secondaryContainer.setVisibility(View.VISIBLE);
+
+ FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
+
+ // Check to see if you need to put on top of old left or not
+ if(backStack.size() > 1) {
+ // Move old right to left if there is a backstack already
+ SubsonicFragment newLeftFragment = backStack.get(backStack.size() - 1);
+ if(replaceCurrent) {
+ // trans.setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left, R.anim.enter_from_left, R.anim.exit_to_right);
+ }
+ trans.remove(newLeftFragment);
+
+ // Only move right to left if replaceCurrent is false
+ if(!replaceCurrent) {
+ SubsonicFragment oldLeftFragment = backStack.get(backStack.size() - 2);
+ oldLeftFragment.setSecondaryFragment(false);
+ // trans.setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left, R.anim.enter_from_left, R.anim.exit_to_right);
+ trans.hide(oldLeftFragment);
+
+ // Make sure remove is finished before adding
+ trans.commit();
+ getSupportFragmentManager().executePendingTransactions();
+
+ trans = getSupportFragmentManager().beginTransaction();
+ // trans.setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left, R.anim.enter_from_left, R.anim.exit_to_right);
+ trans.add(R.id.fragment_container, newLeftFragment, newLeftFragment.getSupportTag() + "");
+ } else {
+ backStack.remove(backStack.size() - 1);
+ }
+ }
+
+ // Add fragment to the right container
+ trans.setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left, R.anim.enter_from_left, R.anim.exit_to_right);
+ trans.add(R.id.fragment_second_container, fragment, tag + "");
+
+ // Commit it all
+ trans.commit();
+ }
+ recreateSpinner();
+ }
+ public void removeCurrent() {
+ if(currentFragment != null) {
+ currentFragment.setPrimaryFragment(false);
+ }
+ Fragment oldFrag = currentFragment;
+
+ currentFragment = backStack.remove(backStack.size() - 1);
+ currentFragment.setPrimaryFragment(true, false);
+ supportInvalidateOptionsMenu();
+
+ if(secondaryContainer == null) {
+ FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
+ trans.setCustomAnimations(R.anim.enter_from_left, R.anim.exit_to_right, R.anim.enter_from_right, R.anim.exit_to_left);
+ trans.remove(oldFrag);
+ trans.show(currentFragment);
+ trans.commit();
+ } else {
+ FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
+
+ // Remove old right fragment
+ trans.setCustomAnimations(R.anim.enter_from_left, R.anim.exit_to_right, R.anim.enter_from_right, R.anim.exit_to_left);
+ trans.remove(oldFrag);
+
+ // Only switch places if there is a backstack, otherwise primary container is correct
+ if(backStack.size() > 0) {
+ trans.setCustomAnimations(0, 0, 0, 0);
+ // Add current left fragment to right side
+ trans.remove(currentFragment);
+
+ // Make sure remove is finished before adding
+ trans.commit();
+ getSupportFragmentManager().executePendingTransactions();
+
+ trans = getSupportFragmentManager().beginTransaction();
+ // trans.setCustomAnimations(R.anim.enter_from_left, R.anim.exit_to_right, R.anim.enter_from_right, R.anim.exit_to_left);
+ trans.add(R.id.fragment_second_container, currentFragment, currentFragment.getSupportTag() + "");
+
+ SubsonicFragment newLeftFragment = backStack.get(backStack.size() - 1);
+ newLeftFragment.setSecondaryFragment(true);
+ trans.show(newLeftFragment);
+ } else {
+ secondaryContainer.startAnimation(AnimationUtils.loadAnimation(this, R.anim.exit_to_right));
+ secondaryContainer.setVisibility(View.GONE);
+ }
+
+ trans.commit();
+ }
+ recreateSpinner();
+ }
+
+ public void invalidate() {
+ if(currentFragment != null) {
+ while(backStack.size() > 0) {
+ removeCurrent();
+ }
+
+ currentFragment.invalidate();
+ populateDrawer();
+ }
+
+ supportInvalidateOptionsMenu();
+ }
+
+ protected void recreateSpinner() {
+ if(currentFragment == null || currentFragment.getTitle() == null) {
+ return;
+ }
+
+ if(backStack.size() > 0) {
+ spinnerAdapter.clear();
+ for(int i = 0; i < backStack.size(); i++) {
+ CharSequence title = backStack.get(i).getTitle();
+ if(title != null) {
+ spinnerAdapter.add(title);
+ } else {
+ spinnerAdapter.add("null");
+ }
+ }
+ if(currentFragment.getTitle() != null) {
+ spinnerAdapter.add(currentFragment.getTitle());
+ } else {
+ spinnerAdapter.add("null");
+ }
+ spinnerAdapter.notifyDataSetChanged();
+ actionBarSpinner.setSelection(spinnerAdapter.getCount() - 1);
+ if(!isTv()) {
+ getSupportActionBar().setDisplayShowCustomEnabled(true);
+ }
+ } else if(!isTv()) {
+ getSupportActionBar().setDisplayShowCustomEnabled(false);
+ }
+ }
+
+ protected void restart() {
+ Intent intent = new Intent(this, ((Object) this).getClass());
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ intent.putExtras(getIntent());
+ Util.startActivityWithoutTransition(this, intent);
+ }
+
+ private void applyTheme() {
+ theme = Util.getTheme(this);
+
+ if(theme != null && theme.indexOf("fullscreen") != -1) {
+ theme = theme.substring(0, theme.indexOf("_fullscreen"));
+ Util.setTheme(this, theme);
+ }
+
+ Util.applyTheme(this, theme);
+ }
+ private void applyFullscreen() {
+ fullScreen = Util.getPreferences(this).getBoolean(Constants.PREFERENCES_KEY_FULL_SCREEN, false);
+ if(fullScreen || isTv()) {
+ // Hide additional elements on higher Android versions
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ int flags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
+ View.SYSTEM_UI_FLAG_FULLSCREEN |
+ View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
+
+ getWindow().getDecorView().setSystemUiVisibility(flags);
+ } else if(Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ getWindow().requestFeature(Window.FEATURE_NO_TITLE);
+ }
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ }
+ }
+
+ public boolean isDestroyedCompat() {
+ return destroyed;
+ }
+
+ public synchronized ImageLoader getImageLoader() {
+ if (IMAGE_LOADER == null) {
+ IMAGE_LOADER = new ImageLoader(this);
+ }
+ return IMAGE_LOADER;
+ }
+ public synchronized static ImageLoader getStaticImageLoader(Context context) {
+ if (IMAGE_LOADER == null) {
+ IMAGE_LOADER = new ImageLoader(context);
+ }
+ return IMAGE_LOADER;
+ }
+
+ public DownloadService getDownloadService() {
+ if(finished) {
+ return null;
+ }
+
+ // If service is not available, request it to start and wait for it.
+ for (int i = 0; i < 5; i++) {
+ DownloadService downloadService = DownloadService.getInstance();
+ if (downloadService != null) {
+ return downloadService;
+ }
+ Log.w(TAG, "DownloadService not running. Attempting to start it.");
+ startService(new Intent(this, DownloadService.class));
+ Util.sleepQuietly(50L);
+ }
+ return DownloadService.getInstance();
+ }
+
+ public static String getThemeName() {
+ return theme;
+ }
+
+ public boolean isTv() {
+ return tv;
+ }
+ public boolean isTouchscreen() {
+ return touchscreen;
+ }
+
+ private void setUncaughtExceptionHandler() {
+ Thread.UncaughtExceptionHandler handler = Thread.getDefaultUncaughtExceptionHandler();
+ if (!(handler instanceof SubsonicActivity.SubsonicUncaughtExceptionHandler)) {
+ Thread.setDefaultUncaughtExceptionHandler(new SubsonicActivity.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("github.daneren2005.dsub", 0);
+ file = new File(Environment.getExternalStorageDirectory(), "dsub-stacktrace.txt");
+ printWriter = new PrintWriter(file);
+ printWriter.println("Android API level: " + Build.VERSION.SDK);
+ 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);
+ }
+
+ }
+ }
+ }
+}
diff --git a/src/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java b/app/src/main/java/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java
index ca0ac2f6..6614e09d 100644
--- a/src/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java
+++ b/app/src/main/java/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java
@@ -1,686 +1,686 @@
-/*
- 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 github.daneren2005.dsub.activity;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.app.Dialog;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.res.TypedArray;
-import android.os.Bundle;
-import android.os.Handler;
-import android.preference.PreferenceManager;
-import android.support.v4.app.FragmentTransaction;
-import android.util.Log;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.ImageButton;
-import android.widget.TextView;
-
-import java.io.File;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.domain.MusicDirectory;
-import github.daneren2005.dsub.domain.PlayerQueue;
-import github.daneren2005.dsub.domain.PlayerState;
-import github.daneren2005.dsub.domain.ServerInfo;
-import github.daneren2005.dsub.fragments.AdminFragment;
-import github.daneren2005.dsub.fragments.ChatFragment;
-import github.daneren2005.dsub.fragments.DownloadFragment;
-import github.daneren2005.dsub.fragments.MainFragment;
-import github.daneren2005.dsub.fragments.SearchFragment;
-import github.daneren2005.dsub.fragments.SelectArtistFragment;
-import github.daneren2005.dsub.fragments.SelectBookmarkFragment;
-import github.daneren2005.dsub.fragments.SelectDirectoryFragment;
-import github.daneren2005.dsub.fragments.SelectPlaylistFragment;
-import github.daneren2005.dsub.fragments.SelectPodcastsFragment;
-import github.daneren2005.dsub.fragments.SelectShareFragment;
-import github.daneren2005.dsub.fragments.SubsonicFragment;
-import github.daneren2005.dsub.service.DownloadFile;
-import github.daneren2005.dsub.service.DownloadService;
-import github.daneren2005.dsub.service.MusicService;
-import github.daneren2005.dsub.service.MusicServiceFactory;
-import github.daneren2005.dsub.updates.Updater;
-import github.daneren2005.dsub.util.BackgroundTask;
-import github.daneren2005.dsub.util.Constants;
-import github.daneren2005.dsub.util.FileUtil;
-import github.daneren2005.dsub.util.SilentBackgroundTask;
-import github.daneren2005.dsub.util.UserUtil;
-import github.daneren2005.dsub.util.Util;
-import github.daneren2005.dsub.view.ChangeLog;
-
-/**
- * Created by Scott on 10/14/13.
- */
-public class SubsonicFragmentActivity extends SubsonicActivity {
- private static String TAG = SubsonicFragmentActivity.class.getSimpleName();
- private static boolean infoDialogDisplayed;
- private static boolean sessionInitialized = false;
- private static long ALLOWED_SKEW = 30000L;
-
- private ScheduledExecutorService executorService;
- private View bottomBar;
- private View coverArtView;
- private TextView trackView;
- private TextView artistView;
- private ImageButton startButton;
- private long lastBackPressTime = 0;
- private DownloadFile currentPlaying;
- private PlayerState currentState;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- if (getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_EXIT)) {
- stopService(new Intent(this, DownloadService.class));
- finish();
- getImageLoader().clearCache();
- } else if(getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD_VIEW)) {
- getIntent().putExtra(Constants.INTENT_EXTRA_FRAGMENT_TYPE, "Download");
- if(drawerAdapter != null) {
- drawerAdapter.setDownloadVisible(true);
- }
- } else if(getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD)) {
- DownloadService service = getDownloadService();
- if((service != null && service.getCurrentPlaying() != null)) {
- getIntent().removeExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD);
- Intent intent = new Intent();
- intent.setClass(this, DownloadActivity.class);
- startActivity(intent);
- }
- }
- setContentView(R.layout.abstract_fragment_activity);
-
- UserUtil.seedCurrentUser(this);
- if (findViewById(R.id.fragment_container) != null && savedInstanceState == null) {
- String fragmentType = getIntent().getStringExtra(Constants.INTENT_EXTRA_FRAGMENT_TYPE);
- boolean firstRun = false;
- if(fragmentType == null) {
- fragmentType = Util.openToTab(this);
- if(fragmentType != null) {
- getIntent().putExtra(Constants.INTENT_EXTRA_FRAGMENT_TYPE, fragmentType);
- firstRun = true;
- }
- }
- currentFragment = getNewFragment(fragmentType);
-
- if("".equals(fragmentType) || fragmentType == null || firstRun) {
- // Initial startup stuff
- if(!sessionInitialized) {
- loadSession();
- }
- }
-
- currentFragment.setPrimaryFragment(true);
- getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, currentFragment, currentFragment.getSupportTag() + "").commit();
-
- if(getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_QUERY) != null) {
- SearchFragment fragment = new SearchFragment();
- replaceFragment(fragment, fragment.getSupportTag());
- }
-
- // If a album type is set, switch to that album type view
- String albumType = getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE);
- if(albumType != null) {
- SubsonicFragment fragment = new SelectDirectoryFragment();
-
- Bundle args = new Bundle();
- args.putString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE, albumType);
- args.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 20);
- args.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0);
-
- fragment.setArguments(args);
- replaceFragment(fragment, fragment.getSupportTag());
- }
- }
-
- bottomBar = findViewById(R.id.bottom_bar);
- bottomBar.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- Intent intent = new Intent();
- intent.setClass(v.getContext(), DownloadActivity.class);
- startActivity(intent);
- }
- });
- coverArtView = bottomBar.findViewById(R.id.album_art);
- trackView = (TextView) bottomBar.findViewById(R.id.track_name);
- artistView = (TextView) bottomBar.findViewById(R.id.artist_name);
-
- ImageButton previousButton = (ImageButton) findViewById(R.id.download_previous);
- previousButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- new SilentBackgroundTask<Void>(SubsonicFragmentActivity.this) {
- @Override
- protected Void doInBackground() throws Throwable {
- if(getDownloadService() == null) {
- return null;
- }
-
- getDownloadService().previous();
- return null;
- }
-
- @Override
- protected void done(Void result) {
- update();
- }
- }.execute();
- }
- });
-
- startButton = (ImageButton) findViewById(R.id.download_start);
- startButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- new SilentBackgroundTask<Void>(SubsonicFragmentActivity.this) {
- @Override
- protected Void doInBackground() throws Throwable {
- PlayerState state = getDownloadService().getPlayerState();
- if(state == PlayerState.STARTED) {
- getDownloadService().pause();
- } else {
- getDownloadService().start();
- }
-
- return null;
- }
-
- @Override
- protected void done(Void result) {
- update();
- }
- }.execute();
- }
- });
-
- ImageButton nextButton = (ImageButton) findViewById(R.id.download_next);
- nextButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- new SilentBackgroundTask<Void>(SubsonicFragmentActivity.this) {
- @Override
- protected Void doInBackground() throws Throwable {
- if(getDownloadService() == null) {
- return null;
- }
-
- getDownloadService().next();
- return null;
- }
-
- @Override
- protected void done(Void result) {
- update();
- }
- }.execute();
- }
- });
- }
-
- @Override
- protected void onPostCreate(Bundle bundle) {
- super.onPostCreate(bundle);
-
- showInfoDialog();
- checkUpdates();
-
- ChangeLog changeLog = new ChangeLog(this, Util.getPreferences(this));
- if(changeLog.isFirstRun()) {
- if(changeLog.isFirstRunEver()) {
- changeLog.updateVersionInPreferences();
- } else {
- Dialog log = changeLog.getLogDialog();
- if (log != null) {
- log.show();
- }
- }
- }
- }
-
- @Override
- public void onNewIntent(Intent intent) {
- super.onNewIntent(intent);
-
- if(currentFragment != null && intent.getStringExtra(Constants.INTENT_EXTRA_NAME_QUERY) != null) {
- if(currentFragment instanceof SearchFragment) {
- 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) {
- ((SearchFragment)currentFragment).search(query, autoplay);
- } else {
- ((SearchFragment)currentFragment).populateList();
- if (requestsearch) {
- onSearchRequested();
- }
- }
- getIntent().removeExtra(Constants.INTENT_EXTRA_NAME_QUERY);
- } else {
- setIntent(intent);
-
- SearchFragment fragment = new SearchFragment();
- replaceFragment(fragment, fragment.getSupportTag());
- }
- } else {
- setIntent(intent);
- }
- if(drawer != null) {
- drawer.closeDrawers();
- }
- }
-
- @Override
- public void onResume() {
- super.onResume();
-
- final Handler handler = new Handler();
- Runnable runnable = new Runnable() {
- @Override
- public void run() {
- handler.post(new Runnable() {
- @Override
- public void run() {
- update();
- }
- });
- }
- };
-
- if(getIntent().hasExtra(Constants.INTENT_EXTRA_VIEW_ALBUM)) {
- SubsonicFragment fragment = new SelectDirectoryFragment();
- Bundle args = new Bundle();
- args.putString(Constants.INTENT_EXTRA_NAME_ID, getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_ID));
- args.putString(Constants.INTENT_EXTRA_NAME_NAME, getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_NAME));
- args.putString(Constants.INTENT_EXTRA_SEARCH_SONG, getIntent().getStringExtra(Constants.INTENT_EXTRA_SEARCH_SONG));
- if(getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_ARTIST)) {
- args.putBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, true);
- }
- if(getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_CHILD_ID)) {
- args.putString(Constants.INTENT_EXTRA_NAME_CHILD_ID, getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_CHILD_ID));
- }
- fragment.setArguments(args);
-
- replaceFragment(fragment, fragment.getSupportTag());
- getIntent().removeExtra(Constants.INTENT_EXTRA_VIEW_ALBUM);
- if("Artist".equals(getIntent().getStringExtra(Constants.INTENT_EXTRA_FRAGMENT_TYPE))) {
- lastSelectedPosition = 1;
- }
- }
-
- createAccount();
-
- executorService = Executors.newSingleThreadScheduledExecutor();
- executorService.scheduleWithFixedDelay(runnable, 0L, 1000L, TimeUnit.MILLISECONDS);
- }
-
- @Override
- public void onPause() {
- super.onPause();
- executorService.shutdown();
- }
-
- @Override
- public void onRestoreInstanceState(Bundle savedInstanceState) {
- super.onRestoreInstanceState(savedInstanceState);
- if(drawerToggle != null && backStack.size() > 0) {
- drawerToggle.setDrawerIndicatorEnabled(false);
- }
- }
-
- @Override
- public void setContentView(int viewId) {
- super.setContentView(viewId);
- if(drawerToggle != null){
- drawerToggle.setDrawerIndicatorEnabled(true);
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- return super.onOptionsItemSelected(item);
- }
-
- @Override
- public void onBackPressed() {
- if(onBackPressedSupport()) {
- if(!Util.disableExitPrompt(this) && lastBackPressTime < (System.currentTimeMillis() - 4000)) {
- lastBackPressTime = System.currentTimeMillis();
- Util.toast(this, R.string.main_back_confirm);
- } else {
- finish();
- }
- }
- }
-
- @Override
- public void replaceFragment(SubsonicFragment fragment, int tag, boolean replaceCurrent) {
- super.replaceFragment(fragment, tag, replaceCurrent);
- if(drawerToggle != null) {
- drawerToggle.setDrawerIndicatorEnabled(false);
- }
- }
- @Override
- public void removeCurrent() {
- super.removeCurrent();
- if(drawerToggle != null && backStack.isEmpty()) {
- drawerToggle.setDrawerIndicatorEnabled(true);
- }
- }
-
- @Override
- public void startFragmentActivity(String fragmentType) {
- // Create a transaction that does all of this
- FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
-
- // Clear existing stack
- for(int i = backStack.size() - 1; i >= 0; i--) {
- trans.remove(backStack.get(i));
- }
- trans.remove(currentFragment);
- backStack.clear();
-
- // Create new stack
- currentFragment = getNewFragment(fragmentType);
- currentFragment.setPrimaryFragment(true);
- trans.add(R.id.fragment_container, currentFragment, currentFragment.getSupportTag() + "");
-
- // Done, cleanup
- trans.commit();
- supportInvalidateOptionsMenu();
- recreateSpinner();
- if(drawer != null) {
- drawer.closeDrawers();
- }
-
- if(secondaryContainer != null) {
- secondaryContainer.setVisibility(View.GONE);
- }
- if(drawerToggle != null) {
- drawerToggle.setDrawerIndicatorEnabled(true);
- }
- }
-
- private SubsonicFragment getNewFragment(String fragmentType) {
- if("Artist".equals(fragmentType)) {
- return new SelectArtistFragment();
- } else if("Playlist".equals(fragmentType)) {
- return new SelectPlaylistFragment();
- } else if("Chat".equals(fragmentType)) {
- return new ChatFragment();
- } else if("Podcast".equals(fragmentType)) {
- return new SelectPodcastsFragment();
- } else if("Bookmark".equals(fragmentType)) {
- return new SelectBookmarkFragment();
- } else if("Share".equals(fragmentType)) {
- return new SelectShareFragment();
- } else if("Admin".equals(fragmentType)) {
- return new AdminFragment();
- } else if("Download".equals(fragmentType)) {
- return new DownloadFragment();
- } else {
- return new MainFragment();
- }
- }
-
- private void update() {
- DownloadService downloadService = getDownloadService();
- if (downloadService == null) {
- return;
- }
-
- DownloadFile current = downloadService.getCurrentPlaying();
- PlayerState state = downloadService.getPlayerState();
- if(current == currentPlaying && state == currentState) {
- return;
- } else {
- currentPlaying = current;
- currentState = state;
- }
-
- MusicDirectory.Entry song = null;
- if(current != null) {
- song = current.getSong();
- trackView.setText(song.getTitle());
- artistView.setText(song.getArtist());
- } else {
- trackView.setText("Title");
- artistView.setText("Artist");
- }
-
- getImageLoader().loadImage(coverArtView, song, false, false);
- int[] attrs = new int[] {(state == PlayerState.STARTED) ? R.attr.media_button_pause : R.attr.media_button_start};
- TypedArray typedArray = this.obtainStyledAttributes(attrs);
- startButton.setImageResource(typedArray.getResourceId(0, 0));
- typedArray.recycle();
- }
-
- public void checkUpdates() {
- try {
- String version = getPackageManager().getPackageInfo(getPackageName(), 0).versionName;
- int ver = Integer.parseInt(version.replace(".", ""));
- Updater updater = new Updater(ver);
- updater.checkUpdates(this);
- }
- catch(Exception e) {
-
- }
- }
-
- private void loadSession() {
- loadSettings();
- if(!Util.isOffline(this) && ServerInfo.canBookmark(this)) {
- loadBookmarks();
- }
- // If we are on Subsonic 5.2+, save play queue
- if(ServerInfo.canSavePlayQueue(this) && !Util.isOffline(this)) {
- loadRemotePlayQueue();
- }
-
- sessionInitialized = true;
- }
- private void loadSettings() {
- PreferenceManager.setDefaultValues(this, R.xml.settings, false);
- SharedPreferences prefs = Util.getPreferences(this);
- if (!prefs.contains(Constants.PREFERENCES_KEY_CACHE_LOCATION) || prefs.getString(Constants.PREFERENCES_KEY_CACHE_LOCATION, null) == null) {
- resetCacheLocation(prefs);
- } else {
- String path = prefs.getString(Constants.PREFERENCES_KEY_CACHE_LOCATION, null);
- File cacheLocation = new File(path);
- if(!FileUtil.verifyCanWrite(cacheLocation)) {
- // Only warn user if there is a difference saved
- if(resetCacheLocation(prefs)) {
- Util.info(this, R.string.common_warning, R.string.settings_cache_location_reset);
- }
- }
- }
-
- if (!prefs.contains(Constants.PREFERENCES_KEY_OFFLINE)) {
- SharedPreferences.Editor editor = prefs.edit();
- editor.putBoolean(Constants.PREFERENCES_KEY_OFFLINE, false);
-
- editor.putString(Constants.PREFERENCES_KEY_SERVER_NAME + 1, "Demo Server");
- editor.putString(Constants.PREFERENCES_KEY_SERVER_URL + 1, "http://demo.subsonic.org");
- editor.putString(Constants.PREFERENCES_KEY_USERNAME + 1, "android-guest");
- editor.putString(Constants.PREFERENCES_KEY_PASSWORD + 1, "guest");
- editor.putInt(Constants.PREFERENCES_KEY_SERVER_INSTANCE, 1);
- editor.commit();
- }
- if(!prefs.contains(Constants.PREFERENCES_KEY_SERVER_COUNT)) {
- SharedPreferences.Editor editor = prefs.edit();
- editor.putInt(Constants.PREFERENCES_KEY_SERVER_COUNT, 1);
- editor.commit();
- }
- }
-
- private boolean resetCacheLocation(SharedPreferences prefs) {
- String newDirectory = FileUtil.getDefaultMusicDirectory(this).getPath();
- String oldDirectory = prefs.getString(Constants.PREFERENCES_KEY_CACHE_LOCATION, null);
- if(newDirectory == null || (oldDirectory != null && newDirectory.equals(oldDirectory))) {
- return false;
- } else {
- SharedPreferences.Editor editor = prefs.edit();
- editor.putString(Constants.PREFERENCES_KEY_CACHE_LOCATION, newDirectory);
- editor.commit();
- return true;
- }
- }
-
- private void loadBookmarks() {
- final Context context = this;
- new SilentBackgroundTask<Void>(context) {
- @Override
- public Void doInBackground() throws Throwable {
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- musicService.getBookmarks(true, context, null);
-
- return null;
- }
-
- @Override
- public void error(Throwable error) {
- Log.e(TAG, "Failed to get bookmarks", error);
- }
- }.execute();
- }
- private void loadRemotePlayQueue() {
- final SubsonicActivity context = this;
- new SilentBackgroundTask<Void>(this) {
- private PlayerQueue playerQueue;
-
- @Override
- protected Void doInBackground() throws Throwable {
- try {
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- PlayerQueue remoteState = musicService.getPlayQueue(context, null);
-
- // Make sure we wait until download service is ready
- DownloadService downloadService = getDownloadService();
- while(downloadService == null || !downloadService.isInitialized()) {
- Util.sleepQuietly(100L);
- downloadService = getDownloadService();
- }
-
- // If we had a remote state and it's changed is more recent than our existing state
- if(remoteState != null && remoteState.changed != null) {
- // Check if changed + 30 seconds since some servers have slight skew
- Date remoteChange = new Date(remoteState.changed.getTime() - ALLOWED_SKEW);
- Date localChange = downloadService.getLastStateChanged();
- if(localChange == null || localChange.before(remoteChange)) {
- playerQueue = remoteState;
- }
- }
- } catch (Exception e) {
- Log.e(TAG, "Failed to get playing queue to server", e);
- }
-
- return null;
- }
-
- @Override
- protected void done(Void arg) {
- if(!context.isDestroyedCompat() && playerQueue != null) {
- promptRestoreFromRemoteQueue(playerQueue);
- }
- }
- }.execute();
- }
- private void promptRestoreFromRemoteQueue(final PlayerQueue remoteState) {
- Util.confirmDialog(this, R.string.download_restore_play_queue, Util.formatDate(remoteState.changed), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- new SilentBackgroundTask<Void>(SubsonicFragmentActivity.this) {
- @Override
- protected Void doInBackground() throws Throwable {
- DownloadService downloadService = getDownloadService();
- downloadService.clear();
- downloadService.download(remoteState.songs, false, false, false, false, remoteState.currentPlayingIndex, remoteState.currentPlayingPosition);
- return null;
- }
- }.execute();
- }
- }, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- new SilentBackgroundTask<Void>(SubsonicFragmentActivity.this) {
- @Override
- protected Void doInBackground() throws Throwable {
- DownloadService downloadService = getDownloadService();
- downloadService.serializeQueue(false);
- return null;
- }
- }.execute();
- }
- });
- }
-
- private void createAccount() {
- final Context context = this;
-
- new SilentBackgroundTask<Void>(this) {
- @Override
- protected Void doInBackground() throws Throwable {
- AccountManager accountManager = (AccountManager) context.getSystemService(ACCOUNT_SERVICE);
- Account account = new Account(Constants.SYNC_ACCOUNT_NAME, Constants.SYNC_ACCOUNT_TYPE);
- accountManager.addAccountExplicitly(account, null, null);
-
- SharedPreferences prefs = Util.getPreferences(context);
- boolean syncEnabled = prefs.getBoolean(Constants.PREFERENCES_KEY_SYNC_ENABLED, true);
- int syncInterval = Integer.parseInt(prefs.getString(Constants.PREFERENCES_KEY_SYNC_INTERVAL, "60"));
-
- // Add enabled/frequency to playlist/podcasts syncing
- ContentResolver.setSyncAutomatically(account, Constants.SYNC_ACCOUNT_PLAYLIST_AUTHORITY, syncEnabled);
- ContentResolver.addPeriodicSync(account, Constants.SYNC_ACCOUNT_PLAYLIST_AUTHORITY, new Bundle(), 60L * syncInterval);
- ContentResolver.setSyncAutomatically(account, Constants.SYNC_ACCOUNT_PODCAST_AUTHORITY, syncEnabled);
- ContentResolver.addPeriodicSync(account, Constants.SYNC_ACCOUNT_PODCAST_AUTHORITY, new Bundle(), 60L * syncInterval);
-
- // Add for starred/recently added
- ContentResolver.setSyncAutomatically(account, Constants.SYNC_ACCOUNT_STARRED_AUTHORITY, (syncEnabled && prefs.getBoolean(Constants.PREFERENCES_KEY_SYNC_STARRED, false)));
- ContentResolver.addPeriodicSync(account, Constants.SYNC_ACCOUNT_STARRED_AUTHORITY, new Bundle(), 60L * syncInterval);
- ContentResolver.setSyncAutomatically(account, Constants.SYNC_ACCOUNT_MOST_RECENT_AUTHORITY, (syncEnabled && prefs.getBoolean(Constants.PREFERENCES_KEY_SYNC_MOST_RECENT, false)));
- ContentResolver.addPeriodicSync(account, Constants.SYNC_ACCOUNT_MOST_RECENT_AUTHORITY, new Bundle(), 60L * syncInterval);
- return null;
- }
-
- @Override
- protected void done(Void result) {
-
- }
- }.execute();
- }
-
- private void showInfoDialog() {
- if (!infoDialogDisplayed) {
- infoDialogDisplayed = true;
- if (Util.getRestUrl(this, null).contains("demo.subsonic.org")) {
- Util.info(this, R.string.main_welcome_title, R.string.main_welcome_text);
- }
- }
- }
-}
+/*
+ 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 github.daneren2005.dsub.activity;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.app.Dialog;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.res.TypedArray;
+import android.os.Bundle;
+import android.os.Handler;
+import android.preference.PreferenceManager;
+import android.support.v4.app.FragmentTransaction;
+import android.util.Log;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.TextView;
+
+import java.io.File;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.MusicDirectory;
+import github.daneren2005.dsub.domain.PlayerQueue;
+import github.daneren2005.dsub.domain.PlayerState;
+import github.daneren2005.dsub.domain.ServerInfo;
+import github.daneren2005.dsub.fragments.AdminFragment;
+import github.daneren2005.dsub.fragments.ChatFragment;
+import github.daneren2005.dsub.fragments.DownloadFragment;
+import github.daneren2005.dsub.fragments.MainFragment;
+import github.daneren2005.dsub.fragments.SearchFragment;
+import github.daneren2005.dsub.fragments.SelectArtistFragment;
+import github.daneren2005.dsub.fragments.SelectBookmarkFragment;
+import github.daneren2005.dsub.fragments.SelectDirectoryFragment;
+import github.daneren2005.dsub.fragments.SelectPlaylistFragment;
+import github.daneren2005.dsub.fragments.SelectPodcastsFragment;
+import github.daneren2005.dsub.fragments.SelectShareFragment;
+import github.daneren2005.dsub.fragments.SubsonicFragment;
+import github.daneren2005.dsub.service.DownloadFile;
+import github.daneren2005.dsub.service.DownloadService;
+import github.daneren2005.dsub.service.MusicService;
+import github.daneren2005.dsub.service.MusicServiceFactory;
+import github.daneren2005.dsub.updates.Updater;
+import github.daneren2005.dsub.util.BackgroundTask;
+import github.daneren2005.dsub.util.Constants;
+import github.daneren2005.dsub.util.FileUtil;
+import github.daneren2005.dsub.util.SilentBackgroundTask;
+import github.daneren2005.dsub.util.UserUtil;
+import github.daneren2005.dsub.util.Util;
+import github.daneren2005.dsub.view.ChangeLog;
+
+/**
+ * Created by Scott on 10/14/13.
+ */
+public class SubsonicFragmentActivity extends SubsonicActivity {
+ private static String TAG = SubsonicFragmentActivity.class.getSimpleName();
+ private static boolean infoDialogDisplayed;
+ private static boolean sessionInitialized = false;
+ private static long ALLOWED_SKEW = 30000L;
+
+ private ScheduledExecutorService executorService;
+ private View bottomBar;
+ private View coverArtView;
+ private TextView trackView;
+ private TextView artistView;
+ private ImageButton startButton;
+ private long lastBackPressTime = 0;
+ private DownloadFile currentPlaying;
+ private PlayerState currentState;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_EXIT)) {
+ stopService(new Intent(this, DownloadService.class));
+ finish();
+ getImageLoader().clearCache();
+ } else if(getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD_VIEW)) {
+ getIntent().putExtra(Constants.INTENT_EXTRA_FRAGMENT_TYPE, "Download");
+ if(drawerAdapter != null) {
+ drawerAdapter.setDownloadVisible(true);
+ }
+ } else if(getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD)) {
+ DownloadService service = getDownloadService();
+ if((service != null && service.getCurrentPlaying() != null)) {
+ getIntent().removeExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD);
+ Intent intent = new Intent();
+ intent.setClass(this, DownloadActivity.class);
+ startActivity(intent);
+ }
+ }
+ setContentView(R.layout.abstract_fragment_activity);
+
+ UserUtil.seedCurrentUser(this);
+ if (findViewById(R.id.fragment_container) != null && savedInstanceState == null) {
+ String fragmentType = getIntent().getStringExtra(Constants.INTENT_EXTRA_FRAGMENT_TYPE);
+ boolean firstRun = false;
+ if(fragmentType == null) {
+ fragmentType = Util.openToTab(this);
+ if(fragmentType != null) {
+ getIntent().putExtra(Constants.INTENT_EXTRA_FRAGMENT_TYPE, fragmentType);
+ firstRun = true;
+ }
+ }
+ currentFragment = getNewFragment(fragmentType);
+
+ if("".equals(fragmentType) || fragmentType == null || firstRun) {
+ // Initial startup stuff
+ if(!sessionInitialized) {
+ loadSession();
+ }
+ }
+
+ currentFragment.setPrimaryFragment(true);
+ getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, currentFragment, currentFragment.getSupportTag() + "").commit();
+
+ if(getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_QUERY) != null) {
+ SearchFragment fragment = new SearchFragment();
+ replaceFragment(fragment, fragment.getSupportTag());
+ }
+
+ // If a album type is set, switch to that album type view
+ String albumType = getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE);
+ if(albumType != null) {
+ SubsonicFragment fragment = new SelectDirectoryFragment();
+
+ Bundle args = new Bundle();
+ args.putString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE, albumType);
+ args.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 20);
+ args.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0);
+
+ fragment.setArguments(args);
+ replaceFragment(fragment, fragment.getSupportTag());
+ }
+ }
+
+ bottomBar = findViewById(R.id.bottom_bar);
+ bottomBar.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ Intent intent = new Intent();
+ intent.setClass(v.getContext(), DownloadActivity.class);
+ startActivity(intent);
+ }
+ });
+ coverArtView = bottomBar.findViewById(R.id.album_art);
+ trackView = (TextView) bottomBar.findViewById(R.id.track_name);
+ artistView = (TextView) bottomBar.findViewById(R.id.artist_name);
+
+ ImageButton previousButton = (ImageButton) findViewById(R.id.download_previous);
+ previousButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ new SilentBackgroundTask<Void>(SubsonicFragmentActivity.this) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ if(getDownloadService() == null) {
+ return null;
+ }
+
+ getDownloadService().previous();
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ update();
+ }
+ }.execute();
+ }
+ });
+
+ startButton = (ImageButton) findViewById(R.id.download_start);
+ startButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ new SilentBackgroundTask<Void>(SubsonicFragmentActivity.this) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ PlayerState state = getDownloadService().getPlayerState();
+ if(state == PlayerState.STARTED) {
+ getDownloadService().pause();
+ } else {
+ getDownloadService().start();
+ }
+
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ update();
+ }
+ }.execute();
+ }
+ });
+
+ ImageButton nextButton = (ImageButton) findViewById(R.id.download_next);
+ nextButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ new SilentBackgroundTask<Void>(SubsonicFragmentActivity.this) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ if(getDownloadService() == null) {
+ return null;
+ }
+
+ getDownloadService().next();
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ update();
+ }
+ }.execute();
+ }
+ });
+ }
+
+ @Override
+ protected void onPostCreate(Bundle bundle) {
+ super.onPostCreate(bundle);
+
+ showInfoDialog();
+ checkUpdates();
+
+ ChangeLog changeLog = new ChangeLog(this, Util.getPreferences(this));
+ if(changeLog.isFirstRun()) {
+ if(changeLog.isFirstRunEver()) {
+ changeLog.updateVersionInPreferences();
+ } else {
+ Dialog log = changeLog.getLogDialog();
+ if (log != null) {
+ log.show();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+
+ if(currentFragment != null && intent.getStringExtra(Constants.INTENT_EXTRA_NAME_QUERY) != null) {
+ if(currentFragment instanceof SearchFragment) {
+ 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) {
+ ((SearchFragment)currentFragment).search(query, autoplay);
+ } else {
+ ((SearchFragment)currentFragment).populateList();
+ if (requestsearch) {
+ onSearchRequested();
+ }
+ }
+ getIntent().removeExtra(Constants.INTENT_EXTRA_NAME_QUERY);
+ } else {
+ setIntent(intent);
+
+ SearchFragment fragment = new SearchFragment();
+ replaceFragment(fragment, fragment.getSupportTag());
+ }
+ } else {
+ setIntent(intent);
+ }
+ if(drawer != null) {
+ drawer.closeDrawers();
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ final Handler handler = new Handler();
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ update();
+ }
+ });
+ }
+ };
+
+ if(getIntent().hasExtra(Constants.INTENT_EXTRA_VIEW_ALBUM)) {
+ SubsonicFragment fragment = new SelectDirectoryFragment();
+ Bundle args = new Bundle();
+ args.putString(Constants.INTENT_EXTRA_NAME_ID, getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_ID));
+ args.putString(Constants.INTENT_EXTRA_NAME_NAME, getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_NAME));
+ args.putString(Constants.INTENT_EXTRA_SEARCH_SONG, getIntent().getStringExtra(Constants.INTENT_EXTRA_SEARCH_SONG));
+ if(getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_ARTIST)) {
+ args.putBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, true);
+ }
+ if(getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_CHILD_ID)) {
+ args.putString(Constants.INTENT_EXTRA_NAME_CHILD_ID, getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_CHILD_ID));
+ }
+ fragment.setArguments(args);
+
+ replaceFragment(fragment, fragment.getSupportTag());
+ getIntent().removeExtra(Constants.INTENT_EXTRA_VIEW_ALBUM);
+ if("Artist".equals(getIntent().getStringExtra(Constants.INTENT_EXTRA_FRAGMENT_TYPE))) {
+ lastSelectedPosition = 1;
+ }
+ }
+
+ createAccount();
+
+ executorService = Executors.newSingleThreadScheduledExecutor();
+ executorService.scheduleWithFixedDelay(runnable, 0L, 1000L, TimeUnit.MILLISECONDS);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ executorService.shutdown();
+ }
+
+ @Override
+ public void onRestoreInstanceState(Bundle savedInstanceState) {
+ super.onRestoreInstanceState(savedInstanceState);
+ if(drawerToggle != null && backStack.size() > 0) {
+ drawerToggle.setDrawerIndicatorEnabled(false);
+ }
+ }
+
+ @Override
+ public void setContentView(int viewId) {
+ super.setContentView(viewId);
+ if(drawerToggle != null){
+ drawerToggle.setDrawerIndicatorEnabled(true);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public void onBackPressed() {
+ if(onBackPressedSupport()) {
+ if(!Util.disableExitPrompt(this) && lastBackPressTime < (System.currentTimeMillis() - 4000)) {
+ lastBackPressTime = System.currentTimeMillis();
+ Util.toast(this, R.string.main_back_confirm);
+ } else {
+ finish();
+ }
+ }
+ }
+
+ @Override
+ public void replaceFragment(SubsonicFragment fragment, int tag, boolean replaceCurrent) {
+ super.replaceFragment(fragment, tag, replaceCurrent);
+ if(drawerToggle != null) {
+ drawerToggle.setDrawerIndicatorEnabled(false);
+ }
+ }
+ @Override
+ public void removeCurrent() {
+ super.removeCurrent();
+ if(drawerToggle != null && backStack.isEmpty()) {
+ drawerToggle.setDrawerIndicatorEnabled(true);
+ }
+ }
+
+ @Override
+ public void startFragmentActivity(String fragmentType) {
+ // Create a transaction that does all of this
+ FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
+
+ // Clear existing stack
+ for(int i = backStack.size() - 1; i >= 0; i--) {
+ trans.remove(backStack.get(i));
+ }
+ trans.remove(currentFragment);
+ backStack.clear();
+
+ // Create new stack
+ currentFragment = getNewFragment(fragmentType);
+ currentFragment.setPrimaryFragment(true);
+ trans.add(R.id.fragment_container, currentFragment, currentFragment.getSupportTag() + "");
+
+ // Done, cleanup
+ trans.commit();
+ supportInvalidateOptionsMenu();
+ recreateSpinner();
+ if(drawer != null) {
+ drawer.closeDrawers();
+ }
+
+ if(secondaryContainer != null) {
+ secondaryContainer.setVisibility(View.GONE);
+ }
+ if(drawerToggle != null) {
+ drawerToggle.setDrawerIndicatorEnabled(true);
+ }
+ }
+
+ private SubsonicFragment getNewFragment(String fragmentType) {
+ if("Artist".equals(fragmentType)) {
+ return new SelectArtistFragment();
+ } else if("Playlist".equals(fragmentType)) {
+ return new SelectPlaylistFragment();
+ } else if("Chat".equals(fragmentType)) {
+ return new ChatFragment();
+ } else if("Podcast".equals(fragmentType)) {
+ return new SelectPodcastsFragment();
+ } else if("Bookmark".equals(fragmentType)) {
+ return new SelectBookmarkFragment();
+ } else if("Share".equals(fragmentType)) {
+ return new SelectShareFragment();
+ } else if("Admin".equals(fragmentType)) {
+ return new AdminFragment();
+ } else if("Download".equals(fragmentType)) {
+ return new DownloadFragment();
+ } else {
+ return new MainFragment();
+ }
+ }
+
+ private void update() {
+ DownloadService downloadService = getDownloadService();
+ if (downloadService == null) {
+ return;
+ }
+
+ DownloadFile current = downloadService.getCurrentPlaying();
+ PlayerState state = downloadService.getPlayerState();
+ if(current == currentPlaying && state == currentState) {
+ return;
+ } else {
+ currentPlaying = current;
+ currentState = state;
+ }
+
+ MusicDirectory.Entry song = null;
+ if(current != null) {
+ song = current.getSong();
+ trackView.setText(song.getTitle());
+ artistView.setText(song.getArtist());
+ } else {
+ trackView.setText("Title");
+ artistView.setText("Artist");
+ }
+
+ getImageLoader().loadImage(coverArtView, song, false, false);
+ int[] attrs = new int[] {(state == PlayerState.STARTED) ? R.attr.media_button_pause : R.attr.media_button_start};
+ TypedArray typedArray = this.obtainStyledAttributes(attrs);
+ startButton.setImageResource(typedArray.getResourceId(0, 0));
+ typedArray.recycle();
+ }
+
+ public void checkUpdates() {
+ try {
+ String version = getPackageManager().getPackageInfo(getPackageName(), 0).versionName;
+ int ver = Integer.parseInt(version.replace(".", ""));
+ Updater updater = new Updater(ver);
+ updater.checkUpdates(this);
+ }
+ catch(Exception e) {
+
+ }
+ }
+
+ private void loadSession() {
+ loadSettings();
+ if(!Util.isOffline(this) && ServerInfo.canBookmark(this)) {
+ loadBookmarks();
+ }
+ // If we are on Subsonic 5.2+, save play queue
+ if(ServerInfo.canSavePlayQueue(this) && !Util.isOffline(this)) {
+ loadRemotePlayQueue();
+ }
+
+ sessionInitialized = true;
+ }
+ private void loadSettings() {
+ PreferenceManager.setDefaultValues(this, R.xml.settings, false);
+ SharedPreferences prefs = Util.getPreferences(this);
+ if (!prefs.contains(Constants.PREFERENCES_KEY_CACHE_LOCATION) || prefs.getString(Constants.PREFERENCES_KEY_CACHE_LOCATION, null) == null) {
+ resetCacheLocation(prefs);
+ } else {
+ String path = prefs.getString(Constants.PREFERENCES_KEY_CACHE_LOCATION, null);
+ File cacheLocation = new File(path);
+ if(!FileUtil.verifyCanWrite(cacheLocation)) {
+ // Only warn user if there is a difference saved
+ if(resetCacheLocation(prefs)) {
+ Util.info(this, R.string.common_warning, R.string.settings_cache_location_reset);
+ }
+ }
+ }
+
+ if (!prefs.contains(Constants.PREFERENCES_KEY_OFFLINE)) {
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putBoolean(Constants.PREFERENCES_KEY_OFFLINE, false);
+
+ editor.putString(Constants.PREFERENCES_KEY_SERVER_NAME + 1, "Demo Server");
+ editor.putString(Constants.PREFERENCES_KEY_SERVER_URL + 1, "http://demo.subsonic.org");
+ editor.putString(Constants.PREFERENCES_KEY_USERNAME + 1, "android-guest");
+ editor.putString(Constants.PREFERENCES_KEY_PASSWORD + 1, "guest");
+ editor.putInt(Constants.PREFERENCES_KEY_SERVER_INSTANCE, 1);
+ editor.commit();
+ }
+ if(!prefs.contains(Constants.PREFERENCES_KEY_SERVER_COUNT)) {
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putInt(Constants.PREFERENCES_KEY_SERVER_COUNT, 1);
+ editor.commit();
+ }
+ }
+
+ private boolean resetCacheLocation(SharedPreferences prefs) {
+ String newDirectory = FileUtil.getDefaultMusicDirectory(this).getPath();
+ String oldDirectory = prefs.getString(Constants.PREFERENCES_KEY_CACHE_LOCATION, null);
+ if(newDirectory == null || (oldDirectory != null && newDirectory.equals(oldDirectory))) {
+ return false;
+ } else {
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putString(Constants.PREFERENCES_KEY_CACHE_LOCATION, newDirectory);
+ editor.commit();
+ return true;
+ }
+ }
+
+ private void loadBookmarks() {
+ final Context context = this;
+ new SilentBackgroundTask<Void>(context) {
+ @Override
+ public Void doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ musicService.getBookmarks(true, context, null);
+
+ return null;
+ }
+
+ @Override
+ public void error(Throwable error) {
+ Log.e(TAG, "Failed to get bookmarks", error);
+ }
+ }.execute();
+ }
+ private void loadRemotePlayQueue() {
+ final SubsonicActivity context = this;
+ new SilentBackgroundTask<Void>(this) {
+ private PlayerQueue playerQueue;
+
+ @Override
+ protected Void doInBackground() throws Throwable {
+ try {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ PlayerQueue remoteState = musicService.getPlayQueue(context, null);
+
+ // Make sure we wait until download service is ready
+ DownloadService downloadService = getDownloadService();
+ while(downloadService == null || !downloadService.isInitialized()) {
+ Util.sleepQuietly(100L);
+ downloadService = getDownloadService();
+ }
+
+ // If we had a remote state and it's changed is more recent than our existing state
+ if(remoteState != null && remoteState.changed != null) {
+ // Check if changed + 30 seconds since some servers have slight skew
+ Date remoteChange = new Date(remoteState.changed.getTime() - ALLOWED_SKEW);
+ Date localChange = downloadService.getLastStateChanged();
+ if(localChange == null || localChange.before(remoteChange)) {
+ playerQueue = remoteState;
+ }
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to get playing queue to server", e);
+ }
+
+ return null;
+ }
+
+ @Override
+ protected void done(Void arg) {
+ if(!context.isDestroyedCompat() && playerQueue != null) {
+ promptRestoreFromRemoteQueue(playerQueue);
+ }
+ }
+ }.execute();
+ }
+ private void promptRestoreFromRemoteQueue(final PlayerQueue remoteState) {
+ Util.confirmDialog(this, R.string.download_restore_play_queue, Util.formatDate(remoteState.changed), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ new SilentBackgroundTask<Void>(SubsonicFragmentActivity.this) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ DownloadService downloadService = getDownloadService();
+ downloadService.clear();
+ downloadService.download(remoteState.songs, false, false, false, false, remoteState.currentPlayingIndex, remoteState.currentPlayingPosition);
+ return null;
+ }
+ }.execute();
+ }
+ }, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ new SilentBackgroundTask<Void>(SubsonicFragmentActivity.this) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ DownloadService downloadService = getDownloadService();
+ downloadService.serializeQueue(false);
+ return null;
+ }
+ }.execute();
+ }
+ });
+ }
+
+ private void createAccount() {
+ final Context context = this;
+
+ new SilentBackgroundTask<Void>(this) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ AccountManager accountManager = (AccountManager) context.getSystemService(ACCOUNT_SERVICE);
+ Account account = new Account(Constants.SYNC_ACCOUNT_NAME, Constants.SYNC_ACCOUNT_TYPE);
+ accountManager.addAccountExplicitly(account, null, null);
+
+ SharedPreferences prefs = Util.getPreferences(context);
+ boolean syncEnabled = prefs.getBoolean(Constants.PREFERENCES_KEY_SYNC_ENABLED, true);
+ int syncInterval = Integer.parseInt(prefs.getString(Constants.PREFERENCES_KEY_SYNC_INTERVAL, "60"));
+
+ // Add enabled/frequency to playlist/podcasts syncing
+ ContentResolver.setSyncAutomatically(account, Constants.SYNC_ACCOUNT_PLAYLIST_AUTHORITY, syncEnabled);
+ ContentResolver.addPeriodicSync(account, Constants.SYNC_ACCOUNT_PLAYLIST_AUTHORITY, new Bundle(), 60L * syncInterval);
+ ContentResolver.setSyncAutomatically(account, Constants.SYNC_ACCOUNT_PODCAST_AUTHORITY, syncEnabled);
+ ContentResolver.addPeriodicSync(account, Constants.SYNC_ACCOUNT_PODCAST_AUTHORITY, new Bundle(), 60L * syncInterval);
+
+ // Add for starred/recently added
+ ContentResolver.setSyncAutomatically(account, Constants.SYNC_ACCOUNT_STARRED_AUTHORITY, (syncEnabled && prefs.getBoolean(Constants.PREFERENCES_KEY_SYNC_STARRED, false)));
+ ContentResolver.addPeriodicSync(account, Constants.SYNC_ACCOUNT_STARRED_AUTHORITY, new Bundle(), 60L * syncInterval);
+ ContentResolver.setSyncAutomatically(account, Constants.SYNC_ACCOUNT_MOST_RECENT_AUTHORITY, (syncEnabled && prefs.getBoolean(Constants.PREFERENCES_KEY_SYNC_MOST_RECENT, false)));
+ ContentResolver.addPeriodicSync(account, Constants.SYNC_ACCOUNT_MOST_RECENT_AUTHORITY, new Bundle(), 60L * syncInterval);
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+
+ }
+ }.execute();
+ }
+
+ private void showInfoDialog() {
+ if (!infoDialogDisplayed) {
+ infoDialogDisplayed = true;
+ if (Util.getRestUrl(this, null).contains("demo.subsonic.org")) {
+ Util.info(this, R.string.main_welcome_title, R.string.main_welcome_text);
+ }
+ }
+ }
+}
diff --git a/src/github/daneren2005/dsub/activity/VoiceQueryReceiverActivity.java b/app/src/main/java/github/daneren2005/dsub/activity/VoiceQueryReceiverActivity.java
index 7ae0ba77..7ae0ba77 100644
--- a/src/github/daneren2005/dsub/activity/VoiceQueryReceiverActivity.java
+++ b/app/src/main/java/github/daneren2005/dsub/activity/VoiceQueryReceiverActivity.java
diff --git a/src/github/daneren2005/dsub/adapter/AlbumGridAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/AlbumGridAdapter.java
index eb187569..eb187569 100644
--- a/src/github/daneren2005/dsub/adapter/AlbumGridAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/AlbumGridAdapter.java
diff --git a/src/github/daneren2005/dsub/adapter/AlbumListAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/AlbumListAdapter.java
index b2fcded3..b2fcded3 100644
--- a/src/github/daneren2005/dsub/adapter/AlbumListAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/AlbumListAdapter.java
diff --git a/src/github/daneren2005/dsub/adapter/ArtistAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/ArtistAdapter.java
index 4d469faf..4d469faf 100644
--- a/src/github/daneren2005/dsub/adapter/ArtistAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/ArtistAdapter.java
diff --git a/src/github/daneren2005/dsub/adapter/BookmarkAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/BookmarkAdapter.java
index 26d3e16a..26d3e16a 100644
--- a/src/github/daneren2005/dsub/adapter/BookmarkAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/BookmarkAdapter.java
diff --git a/src/github/daneren2005/dsub/adapter/ChatAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/ChatAdapter.java
index 0c116d39..0c116d39 100644
--- a/src/github/daneren2005/dsub/adapter/ChatAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/ChatAdapter.java
diff --git a/src/github/daneren2005/dsub/adapter/DownloadFileAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/DownloadFileAdapter.java
index be9b4cb9..be9b4cb9 100644
--- a/src/github/daneren2005/dsub/adapter/DownloadFileAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/DownloadFileAdapter.java
diff --git a/src/github/daneren2005/dsub/adapter/DrawerAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/DrawerAdapter.java
index b0a4a33d..b0a4a33d 100644
--- a/src/github/daneren2005/dsub/adapter/DrawerAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/DrawerAdapter.java
diff --git a/src/github/daneren2005/dsub/adapter/EntryAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/EntryAdapter.java
index 9e506e5a..9e506e5a 100644
--- a/src/github/daneren2005/dsub/adapter/EntryAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/EntryAdapter.java
diff --git a/src/github/daneren2005/dsub/adapter/GenreAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/GenreAdapter.java
index abb208c9..abb208c9 100644
--- a/src/github/daneren2005/dsub/adapter/GenreAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/GenreAdapter.java
diff --git a/src/github/daneren2005/dsub/adapter/MergeAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/MergeAdapter.java
index a2db4cf0..a2db4cf0 100644
--- a/src/github/daneren2005/dsub/adapter/MergeAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/MergeAdapter.java
diff --git a/src/github/daneren2005/dsub/adapter/PlaylistAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/PlaylistAdapter.java
index d56a6b97..d56a6b97 100644
--- a/src/github/daneren2005/dsub/adapter/PlaylistAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/PlaylistAdapter.java
diff --git a/src/github/daneren2005/dsub/adapter/PodcastChannelAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/PodcastChannelAdapter.java
index 8ee39a10..8ee39a10 100644
--- a/src/github/daneren2005/dsub/adapter/PodcastChannelAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/PodcastChannelAdapter.java
diff --git a/src/github/daneren2005/dsub/adapter/SackOfViewsAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/SackOfViewsAdapter.java
index e4744cc5..e4744cc5 100644
--- a/src/github/daneren2005/dsub/adapter/SackOfViewsAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/SackOfViewsAdapter.java
diff --git a/src/github/daneren2005/dsub/adapter/SettingsAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/SettingsAdapter.java
index 45c3ead1..45c3ead1 100644
--- a/src/github/daneren2005/dsub/adapter/SettingsAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/SettingsAdapter.java
diff --git a/src/github/daneren2005/dsub/adapter/ShareAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/ShareAdapter.java
index 4121a85a..4121a85a 100644
--- a/src/github/daneren2005/dsub/adapter/ShareAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/ShareAdapter.java
diff --git a/src/github/daneren2005/dsub/adapter/UserAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/UserAdapter.java
index f0f78d97..f0f78d97 100644
--- a/src/github/daneren2005/dsub/adapter/UserAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/UserAdapter.java
diff --git a/src/github/daneren2005/dsub/audiofx/AudioEffectsController.java b/app/src/main/java/github/daneren2005/dsub/audiofx/AudioEffectsController.java
index 1933bd64..1933bd64 100644
--- a/src/github/daneren2005/dsub/audiofx/AudioEffectsController.java
+++ b/app/src/main/java/github/daneren2005/dsub/audiofx/AudioEffectsController.java
diff --git a/src/github/daneren2005/dsub/audiofx/EqualizerController.java b/app/src/main/java/github/daneren2005/dsub/audiofx/EqualizerController.java
index f170af0b..f170af0b 100644
--- a/src/github/daneren2005/dsub/audiofx/EqualizerController.java
+++ b/app/src/main/java/github/daneren2005/dsub/audiofx/EqualizerController.java
diff --git a/src/github/daneren2005/dsub/audiofx/LoudnessEnhancerController.java b/app/src/main/java/github/daneren2005/dsub/audiofx/LoudnessEnhancerController.java
index df6fdb1c..df6fdb1c 100644
--- a/src/github/daneren2005/dsub/audiofx/LoudnessEnhancerController.java
+++ b/app/src/main/java/github/daneren2005/dsub/audiofx/LoudnessEnhancerController.java
diff --git a/src/github/daneren2005/dsub/domain/Artist.java b/app/src/main/java/github/daneren2005/dsub/domain/Artist.java
index f30147e6..f30147e6 100644
--- a/src/github/daneren2005/dsub/domain/Artist.java
+++ b/app/src/main/java/github/daneren2005/dsub/domain/Artist.java
diff --git a/src/github/daneren2005/dsub/domain/ArtistInfo.java b/app/src/main/java/github/daneren2005/dsub/domain/ArtistInfo.java
index 2205d561..2205d561 100644
--- a/src/github/daneren2005/dsub/domain/ArtistInfo.java
+++ b/app/src/main/java/github/daneren2005/dsub/domain/ArtistInfo.java
diff --git a/src/github/daneren2005/dsub/domain/Bookmark.java b/app/src/main/java/github/daneren2005/dsub/domain/Bookmark.java
index df3535d0..3c0c5835 100644
--- a/src/github/daneren2005/dsub/domain/Bookmark.java
+++ b/app/src/main/java/github/daneren2005/dsub/domain/Bookmark.java
@@ -1,105 +1,105 @@
-/*
- 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 2013 (C) Scott Jackson
- */
-package github.daneren2005.dsub.domain;
-
-import java.io.Serializable;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Locale;
-
-/**
- * Created by Scott on 11/4/13.
- */
-public class Bookmark implements Serializable {
- private int position;
- private String username;
- private String comment;
- private Date created;
- private Date changed;
-
- public Bookmark() {
-
- }
- public Bookmark(int position) {
- this.position = position;
- }
-
- public int getPosition() {
- return position;
- }
-
- public void setPosition(int position) {
- this.position = position;
- }
-
- public String getUsername() {
- return username;
- }
-
- public void setUsername(String username) {
- this.username = username;
- }
-
- public String getComment() {
- return comment;
- }
-
- public void setComment(String comment) {
- this.comment = comment;
- }
-
- public Date getCreated() {
- return created;
- }
-
- public void setCreated(String created) {
- if (created != null) {
- try {
- this.created = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH).parse(created);
- } catch (ParseException e) {
- this.created = null;
- }
- } else {
- this.created = null;
- }
- }
- public void setCreated(Date created) {
- this.created = created;
- }
-
- public Date getChanged() {
- return changed;
- }
-
- public void setChanged(String changed) {
- if (changed != null) {
- try {
- this.changed = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH).parse(changed);
- } catch (ParseException e) {
- this.changed = null;
- }
- } else {
- this.changed = null;
- }
- }
- public void setChanged(Date changed) {
- this.changed = changed;
- }
-}
+/*
+ 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 2013 (C) Scott Jackson
+ */
+package github.daneren2005.dsub.domain;
+
+import java.io.Serializable;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+/**
+ * Created by Scott on 11/4/13.
+ */
+public class Bookmark implements Serializable {
+ private int position;
+ private String username;
+ private String comment;
+ private Date created;
+ private Date changed;
+
+ public Bookmark() {
+
+ }
+ public Bookmark(int position) {
+ this.position = position;
+ }
+
+ public int getPosition() {
+ return position;
+ }
+
+ public void setPosition(int position) {
+ this.position = position;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getComment() {
+ return comment;
+ }
+
+ public void setComment(String comment) {
+ this.comment = comment;
+ }
+
+ public Date getCreated() {
+ return created;
+ }
+
+ public void setCreated(String created) {
+ if (created != null) {
+ try {
+ this.created = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH).parse(created);
+ } catch (ParseException e) {
+ this.created = null;
+ }
+ } else {
+ this.created = null;
+ }
+ }
+ public void setCreated(Date created) {
+ this.created = created;
+ }
+
+ public Date getChanged() {
+ return changed;
+ }
+
+ public void setChanged(String changed) {
+ if (changed != null) {
+ try {
+ this.changed = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH).parse(changed);
+ } catch (ParseException e) {
+ this.changed = null;
+ }
+ } else {
+ this.changed = null;
+ }
+ }
+ public void setChanged(Date changed) {
+ this.changed = changed;
+ }
+}
diff --git a/src/github/daneren2005/dsub/domain/ChatMessage.java b/app/src/main/java/github/daneren2005/dsub/domain/ChatMessage.java
index 471594e9..04b9effc 100644
--- a/src/github/daneren2005/dsub/domain/ChatMessage.java
+++ b/app/src/main/java/github/daneren2005/dsub/domain/ChatMessage.java
@@ -1,51 +1,51 @@
-/*
- 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 github.daneren2005.dsub.domain;
-
-import java.io.Serializable;
-
-public class ChatMessage implements Serializable {
- private String username;
- private Long time;
- private String message;
-
- public String getUsername() {
- return username;
- }
-
- public void setUsername(String username) {
- this.username = username;
- }
-
- public Long getTime() {
- return time;
- }
-
- public void setTime(Long time) {
- this.time = time;
- }
-
- public String getMessage() {
- return message;
- }
-
- public void setMessage(String message) {
- this.message = message;
- }
-}
+/*
+ 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 github.daneren2005.dsub.domain;
+
+import java.io.Serializable;
+
+public class ChatMessage implements Serializable {
+ private String username;
+ private Long time;
+ private String message;
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public Long getTime() {
+ return time;
+ }
+
+ public void setTime(Long time) {
+ this.time = time;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+}
diff --git a/src/github/daneren2005/dsub/domain/DLNADevice.java b/app/src/main/java/github/daneren2005/dsub/domain/DLNADevice.java
index 2de84013..d50a398a 100644
--- a/src/github/daneren2005/dsub/domain/DLNADevice.java
+++ b/app/src/main/java/github/daneren2005/dsub/domain/DLNADevice.java
@@ -1,78 +1,78 @@
-/*
- 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 2014 (C) Scott Jackson
-*/
-
-package github.daneren2005.dsub.domain;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import org.fourthline.cling.model.meta.Device;
-
-/**
- * Created by Scott on 11/1/2014.
- */
-public class DLNADevice implements Parcelable {
- public Device renderer;
- public String id;
- public String name;
- public String description;
- public int volume;
- public int volumeMax;
-
- public static final Parcelable.Creator<DLNADevice> CREATOR = new Parcelable.Creator<DLNADevice>() {
- public DLNADevice createFromParcel(Parcel in) {
- return new DLNADevice(in);
- }
-
- public DLNADevice[] newArray(int size) {
- return new DLNADevice[size];
- }
- };
-
- private DLNADevice(Parcel in) {
- id = in.readString();
- name = in.readString();
- description = in.readString();
- volume = in.readInt();
- volumeMax = in.readInt();
- }
-
- public DLNADevice(Device renderer, String id, String name, String description, int volume, int volumeMax) {
- this.renderer = renderer;
- this.id = id;
- this.name = name;
- this.description = description;
- this.volume = volume;
- this.volumeMax = volumeMax;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(id);
- dest.writeString(name);
- dest.writeString(description);
- dest.writeInt(volume);
- dest.writeInt(volumeMax);
- }
-}
+/*
+ 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 2014 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.domain;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import org.fourthline.cling.model.meta.Device;
+
+/**
+ * Created by Scott on 11/1/2014.
+ */
+public class DLNADevice implements Parcelable {
+ public Device renderer;
+ public String id;
+ public String name;
+ public String description;
+ public int volume;
+ public int volumeMax;
+
+ public static final Parcelable.Creator<DLNADevice> CREATOR = new Parcelable.Creator<DLNADevice>() {
+ public DLNADevice createFromParcel(Parcel in) {
+ return new DLNADevice(in);
+ }
+
+ public DLNADevice[] newArray(int size) {
+ return new DLNADevice[size];
+ }
+ };
+
+ private DLNADevice(Parcel in) {
+ id = in.readString();
+ name = in.readString();
+ description = in.readString();
+ volume = in.readInt();
+ volumeMax = in.readInt();
+ }
+
+ public DLNADevice(Device renderer, String id, String name, String description, int volume, int volumeMax) {
+ this.renderer = renderer;
+ this.id = id;
+ this.name = name;
+ this.description = description;
+ this.volume = volume;
+ this.volumeMax = volumeMax;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(id);
+ dest.writeString(name);
+ dest.writeString(description);
+ dest.writeInt(volume);
+ dest.writeInt(volumeMax);
+ }
+}
diff --git a/src/github/daneren2005/dsub/domain/Genre.java b/app/src/main/java/github/daneren2005/dsub/domain/Genre.java
index 4ca4c387..4b6ac344 100644
--- a/src/github/daneren2005/dsub/domain/Genre.java
+++ b/app/src/main/java/github/daneren2005/dsub/domain/Genre.java
@@ -1,69 +1,69 @@
-package github.daneren2005.dsub.domain;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-
-import java.io.Serializable;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-import github.daneren2005.dsub.util.Constants;
-import github.daneren2005.dsub.util.Util;
-
-public class Genre implements Serializable {
- private String name;
- private String index;
- private Integer albumCount;
- private Integer songCount;
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public String getIndex() {
- return index;
- }
-
- public void setIndex(String index) {
- this.index = index;
- }
-
- @Override
- public String toString() {
- return name;
- }
-
- public Integer getAlbumCount() {
- return albumCount;
- }
-
- public void setAlbumCount(Integer albumCount) {
- this.albumCount = albumCount;
- }
-
- public Integer getSongCount() {
- return songCount;
- }
-
- public void setSongCount(Integer songCount) {
- this.songCount = songCount;
- }
-
- public static class GenreComparator implements Comparator<Genre> {
- @Override
- public int compare(Genre genre1, Genre genre2) {
- return genre1.getName().compareToIgnoreCase(genre2.getName());
- }
-
- public static List<Genre> sort(List<Genre> genres) {
- Collections.sort(genres, new GenreComparator());
- return genres;
- }
-
- }
-}
+package github.daneren2005.dsub.domain;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import github.daneren2005.dsub.util.Constants;
+import github.daneren2005.dsub.util.Util;
+
+public class Genre implements Serializable {
+ private String name;
+ private String index;
+ private Integer albumCount;
+ private Integer songCount;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getIndex() {
+ return index;
+ }
+
+ public void setIndex(String index) {
+ this.index = index;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+
+ public Integer getAlbumCount() {
+ return albumCount;
+ }
+
+ public void setAlbumCount(Integer albumCount) {
+ this.albumCount = albumCount;
+ }
+
+ public Integer getSongCount() {
+ return songCount;
+ }
+
+ public void setSongCount(Integer songCount) {
+ this.songCount = songCount;
+ }
+
+ public static class GenreComparator implements Comparator<Genre> {
+ @Override
+ public int compare(Genre genre1, Genre genre2) {
+ return genre1.getName().compareToIgnoreCase(genre2.getName());
+ }
+
+ public static List<Genre> sort(List<Genre> genres) {
+ Collections.sort(genres, new GenreComparator());
+ return genres;
+ }
+
+ }
+}
diff --git a/src/github/daneren2005/dsub/domain/Indexes.java b/app/src/main/java/github/daneren2005/dsub/domain/Indexes.java
index e15ccf9f..e15ccf9f 100644
--- a/src/github/daneren2005/dsub/domain/Indexes.java
+++ b/app/src/main/java/github/daneren2005/dsub/domain/Indexes.java
diff --git a/src/github/daneren2005/dsub/domain/Lyrics.java b/app/src/main/java/github/daneren2005/dsub/domain/Lyrics.java
index 5272920d..5272920d 100644
--- a/src/github/daneren2005/dsub/domain/Lyrics.java
+++ b/app/src/main/java/github/daneren2005/dsub/domain/Lyrics.java
diff --git a/src/github/daneren2005/dsub/domain/MusicDirectory.java b/app/src/main/java/github/daneren2005/dsub/domain/MusicDirectory.java
index ad819763..ad819763 100644
--- a/src/github/daneren2005/dsub/domain/MusicDirectory.java
+++ b/app/src/main/java/github/daneren2005/dsub/domain/MusicDirectory.java
diff --git a/src/github/daneren2005/dsub/domain/MusicFolder.java b/app/src/main/java/github/daneren2005/dsub/domain/MusicFolder.java
index 99e86e23..99e86e23 100644
--- a/src/github/daneren2005/dsub/domain/MusicFolder.java
+++ b/app/src/main/java/github/daneren2005/dsub/domain/MusicFolder.java
diff --git a/src/github/daneren2005/dsub/domain/PlayerQueue.java b/app/src/main/java/github/daneren2005/dsub/domain/PlayerQueue.java
index 32f29725..32f29725 100644
--- a/src/github/daneren2005/dsub/domain/PlayerQueue.java
+++ b/app/src/main/java/github/daneren2005/dsub/domain/PlayerQueue.java
diff --git a/src/github/daneren2005/dsub/domain/PlayerState.java b/app/src/main/java/github/daneren2005/dsub/domain/PlayerState.java
index 21f1b1a4..21f1b1a4 100644
--- a/src/github/daneren2005/dsub/domain/PlayerState.java
+++ b/app/src/main/java/github/daneren2005/dsub/domain/PlayerState.java
diff --git a/src/github/daneren2005/dsub/domain/Playlist.java b/app/src/main/java/github/daneren2005/dsub/domain/Playlist.java
index 7cd820c0..7cd820c0 100644
--- a/src/github/daneren2005/dsub/domain/Playlist.java
+++ b/app/src/main/java/github/daneren2005/dsub/domain/Playlist.java
diff --git a/src/github/daneren2005/dsub/domain/PodcastChannel.java b/app/src/main/java/github/daneren2005/dsub/domain/PodcastChannel.java
index a1c1617e..545f76c6 100644
--- a/src/github/daneren2005/dsub/domain/PodcastChannel.java
+++ b/app/src/main/java/github/daneren2005/dsub/domain/PodcastChannel.java
@@ -1,145 +1,145 @@
-/*
- 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 github.daneren2005.dsub.domain;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-
-import java.io.Serializable;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-import github.daneren2005.dsub.util.Constants;
-import github.daneren2005.dsub.util.Util;
-
-/**
- *
- * @author Scott
- */
-public class PodcastChannel implements Serializable {
- private String id;
- private String name;
- private String url;
- private String description;
- private String status;
- private String errorMessage;
-
- public PodcastChannel() {
-
- }
-
- public String getId() {
- return id;
- }
- public void setId(String id) {
- this.id = id;
- }
-
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
-
- public String getUrl() {
- return url;
- }
- public void setUrl(String url) {
- this.url = url;
- }
-
- public String getDescription() {
- return description;
- }
- public void setDescription(String description) {
- this.description = description;
- }
-
- public String getStatus() {
- return status;
- }
- public void setStatus(String status) {
- this.status = status;
- }
-
- public String getErrorMessage() {
- return errorMessage;
- }
- public void setErrorMessage(String errorMessage) {
- this.errorMessage = errorMessage;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
-
- PodcastChannel entry = (PodcastChannel) o;
- return id.equals(entry.id);
- }
-
- public static class PodcastComparator implements Comparator<PodcastChannel> {
- private static String[] ignoredArticles;
-
- @Override
- public int compare(PodcastChannel podcast1, PodcastChannel podcast2) {
- String lhs = podcast1.getName();
- String rhs = podcast2.getName();
- if(lhs == null && rhs == null) {
- return 0;
- } else if(lhs == null) {
- return 1;
- } else if(rhs == null) {
- return -1;
- }
-
- lhs = lhs.toLowerCase();
- rhs = rhs.toLowerCase();
-
- for(String article: ignoredArticles) {
- int index = lhs.indexOf(article.toLowerCase() + " ");
- if(index == 0) {
- lhs = lhs.substring(article.length() + 1);
- }
- index = rhs.indexOf(article.toLowerCase() + " ");
- if(index == 0) {
- rhs = rhs.substring(article.length() + 1);
- }
- }
-
- return lhs.compareToIgnoreCase(rhs);
- }
-
- public static List<PodcastChannel> sort(List<PodcastChannel> podcasts, Context context) {
- SharedPreferences prefs = Util.getPreferences(context);
- String ignoredArticlesString = prefs.getString(Constants.CACHE_KEY_IGNORE, "The El La Los Las Le Les");
- ignoredArticles = ignoredArticlesString.split(" ");
-
- Collections.sort(podcasts, new PodcastComparator());
- return podcasts;
- }
-
- }
-}
+/*
+ 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 github.daneren2005.dsub.domain;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import github.daneren2005.dsub.util.Constants;
+import github.daneren2005.dsub.util.Util;
+
+/**
+ *
+ * @author Scott
+ */
+public class PodcastChannel implements Serializable {
+ private String id;
+ private String name;
+ private String url;
+ private String description;
+ private String status;
+ private String errorMessage;
+
+ public PodcastChannel() {
+
+ }
+
+ public String getId() {
+ return id;
+ }
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public String getErrorMessage() {
+ return errorMessage;
+ }
+ public void setErrorMessage(String errorMessage) {
+ this.errorMessage = errorMessage;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ PodcastChannel entry = (PodcastChannel) o;
+ return id.equals(entry.id);
+ }
+
+ public static class PodcastComparator implements Comparator<PodcastChannel> {
+ private static String[] ignoredArticles;
+
+ @Override
+ public int compare(PodcastChannel podcast1, PodcastChannel podcast2) {
+ String lhs = podcast1.getName();
+ String rhs = podcast2.getName();
+ if(lhs == null && rhs == null) {
+ return 0;
+ } else if(lhs == null) {
+ return 1;
+ } else if(rhs == null) {
+ return -1;
+ }
+
+ lhs = lhs.toLowerCase();
+ rhs = rhs.toLowerCase();
+
+ for(String article: ignoredArticles) {
+ int index = lhs.indexOf(article.toLowerCase() + " ");
+ if(index == 0) {
+ lhs = lhs.substring(article.length() + 1);
+ }
+ index = rhs.indexOf(article.toLowerCase() + " ");
+ if(index == 0) {
+ rhs = rhs.substring(article.length() + 1);
+ }
+ }
+
+ return lhs.compareToIgnoreCase(rhs);
+ }
+
+ public static List<PodcastChannel> sort(List<PodcastChannel> podcasts, Context context) {
+ SharedPreferences prefs = Util.getPreferences(context);
+ String ignoredArticlesString = prefs.getString(Constants.CACHE_KEY_IGNORE, "The El La Los Las Le Les");
+ ignoredArticles = ignoredArticlesString.split(" ");
+
+ Collections.sort(podcasts, new PodcastComparator());
+ return podcasts;
+ }
+
+ }
+}
diff --git a/src/github/daneren2005/dsub/domain/PodcastEpisode.java b/app/src/main/java/github/daneren2005/dsub/domain/PodcastEpisode.java
index 01821072..4181b3d3 100644
--- a/src/github/daneren2005/dsub/domain/PodcastEpisode.java
+++ b/app/src/main/java/github/daneren2005/dsub/domain/PodcastEpisode.java
@@ -1,54 +1,54 @@
-/*
- 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 github.daneren2005.dsub.domain;
-
-/**
- *
- * @author Scott
- */
-public class PodcastEpisode extends MusicDirectory.Entry {
- private String episodeId;
- private String date;
- private String status;
-
- public PodcastEpisode() {
- setDirectory(false);
- }
-
- public String getEpisodeId() {
- return episodeId;
- }
- public void setEpisodeId(String episodeId) {
- this.episodeId = episodeId;
- }
-
- public String getDate() {
- return date;
- }
- public void setDate(String date) {
- this.date = date;
- }
-
- public String getStatus() {
- return status;
- }
- public void setStatus(String status) {
- this.status = status;
- }
-}
+/*
+ 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 github.daneren2005.dsub.domain;
+
+/**
+ *
+ * @author Scott
+ */
+public class PodcastEpisode extends MusicDirectory.Entry {
+ private String episodeId;
+ private String date;
+ private String status;
+
+ public PodcastEpisode() {
+ setDirectory(false);
+ }
+
+ public String getEpisodeId() {
+ return episodeId;
+ }
+ public void setEpisodeId(String episodeId) {
+ this.episodeId = episodeId;
+ }
+
+ public String getDate() {
+ return date;
+ }
+ public void setDate(String date) {
+ this.date = date;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+ public void setStatus(String status) {
+ this.status = status;
+ }
+}
diff --git a/src/github/daneren2005/dsub/domain/RemoteControlState.java b/app/src/main/java/github/daneren2005/dsub/domain/RemoteControlState.java
index 47895984..47895984 100644
--- a/src/github/daneren2005/dsub/domain/RemoteControlState.java
+++ b/app/src/main/java/github/daneren2005/dsub/domain/RemoteControlState.java
diff --git a/src/github/daneren2005/dsub/domain/RemoteStatus.java b/app/src/main/java/github/daneren2005/dsub/domain/RemoteStatus.java
index e9749120..e9749120 100644
--- a/src/github/daneren2005/dsub/domain/RemoteStatus.java
+++ b/app/src/main/java/github/daneren2005/dsub/domain/RemoteStatus.java
diff --git a/src/github/daneren2005/dsub/domain/RepeatMode.java b/app/src/main/java/github/daneren2005/dsub/domain/RepeatMode.java
index 7139029c..7139029c 100644
--- a/src/github/daneren2005/dsub/domain/RepeatMode.java
+++ b/app/src/main/java/github/daneren2005/dsub/domain/RepeatMode.java
diff --git a/src/github/daneren2005/dsub/domain/SearchCritera.java b/app/src/main/java/github/daneren2005/dsub/domain/SearchCritera.java
index 20d46aa0..20d46aa0 100644
--- a/src/github/daneren2005/dsub/domain/SearchCritera.java
+++ b/app/src/main/java/github/daneren2005/dsub/domain/SearchCritera.java
diff --git a/src/github/daneren2005/dsub/domain/SearchResult.java b/app/src/main/java/github/daneren2005/dsub/domain/SearchResult.java
index 3427f2ca..3427f2ca 100644
--- a/src/github/daneren2005/dsub/domain/SearchResult.java
+++ b/app/src/main/java/github/daneren2005/dsub/domain/SearchResult.java
diff --git a/src/github/daneren2005/dsub/domain/ServerInfo.java b/app/src/main/java/github/daneren2005/dsub/domain/ServerInfo.java
index 3ece6af9..3ece6af9 100644
--- a/src/github/daneren2005/dsub/domain/ServerInfo.java
+++ b/app/src/main/java/github/daneren2005/dsub/domain/ServerInfo.java
diff --git a/src/github/daneren2005/dsub/domain/Share.java b/app/src/main/java/github/daneren2005/dsub/domain/Share.java
index aa22956d..380811a7 100644
--- a/src/github/daneren2005/dsub/domain/Share.java
+++ b/app/src/main/java/github/daneren2005/dsub/domain/Share.java
@@ -1,165 +1,165 @@
-/*
- 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 github.daneren2005.dsub.domain;
-
-import github.daneren2005.dsub.domain.MusicDirectory.Entry;
-import java.io.Serializable;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-import java.util.Locale;
-
-public class Share implements Serializable {
- private String id;
- private String url;
- private String description;
- private String username;
- private Date created;
- private Date lastVisited;
- private Date expires;
- private Long visitCount;
- private List<Entry> entries;
-
- public Share() {
- entries = new ArrayList<Entry>();
- }
-
- public String getName() {
- if(description != null && !"".equals(description)) {
- return description;
- } else {
- return url.replaceFirst(".*/([^/?]+).*", "$1");
- }
- }
-
- public String getId() {
- return id;
- }
-
- public void setId(String id) {
- this.id = id;
- }
-
- public String getUrl() {
- return url;
- }
-
- public void setUrl(String url) {
- this.url = url;
- }
-
- public String getDescription() {
- return description;
- }
-
- public void setDescription(String description) {
- this.description = description;
- }
-
- public String getUsername() {
- return username;
- }
-
- public void setUsername(String username) {
- this.username = username;
- }
-
- public Date getCreated() {
- return created;
- }
-
- public void setCreated(String created) {
- if (created != null) {
- try {
- this.created = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH).parse(created);
- } catch (ParseException e) {
- this.created = null;
- }
- } else {
- this.created = null;
- }
- }
- public void setCreated(Date created) {
- this.created = created;
- }
-
- public Date getLastVisited() {
- return lastVisited;
- }
-
- public void setLastVisited(String lastVisited) {
- if (lastVisited != null) {
- try {
- this.lastVisited = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH).parse(lastVisited);
- } catch (ParseException e) {
- this.lastVisited = null;
- }
- } else {
- this.lastVisited = null;
- }
- }
- public void setLastVisited(Date lastVisited) {
- this.lastVisited = lastVisited;
- }
-
- public Date getExpires() {
- return expires;
- }
-
- public void setExpires(String expires) {
- if (expires != null) {
- try {
- this.expires = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH).parse(expires);
- } catch (ParseException e) {
- this.expires = null;
- }
- } else {
- this.expires = null;
- }
- }
- public void setExpires(Date expires) {
- this.expires = expires;
- }
-
- public Long getVisitCount() {
- return visitCount;
- }
-
- public void setVisitCount(Long visitCount) {
- this.visitCount = visitCount;
- }
-
- public MusicDirectory getMusicDirectory() {
- MusicDirectory dir = new MusicDirectory();
- dir.addChildren(entries);
- dir.setId(getId());
- dir.setName(getName());
- return dir;
- }
-
- public List<Entry> getEntries() {
- return this.entries;
- }
-
- public void addEntry(Entry entry) {
- entries.add(entry);
- }
- }
+/*
+ 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 github.daneren2005.dsub.domain;
+
+import github.daneren2005.dsub.domain.MusicDirectory.Entry;
+import java.io.Serializable;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+
+public class Share implements Serializable {
+ private String id;
+ private String url;
+ private String description;
+ private String username;
+ private Date created;
+ private Date lastVisited;
+ private Date expires;
+ private Long visitCount;
+ private List<Entry> entries;
+
+ public Share() {
+ entries = new ArrayList<Entry>();
+ }
+
+ public String getName() {
+ if(description != null && !"".equals(description)) {
+ return description;
+ } else {
+ return url.replaceFirst(".*/([^/?]+).*", "$1");
+ }
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public Date getCreated() {
+ return created;
+ }
+
+ public void setCreated(String created) {
+ if (created != null) {
+ try {
+ this.created = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH).parse(created);
+ } catch (ParseException e) {
+ this.created = null;
+ }
+ } else {
+ this.created = null;
+ }
+ }
+ public void setCreated(Date created) {
+ this.created = created;
+ }
+
+ public Date getLastVisited() {
+ return lastVisited;
+ }
+
+ public void setLastVisited(String lastVisited) {
+ if (lastVisited != null) {
+ try {
+ this.lastVisited = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH).parse(lastVisited);
+ } catch (ParseException e) {
+ this.lastVisited = null;
+ }
+ } else {
+ this.lastVisited = null;
+ }
+ }
+ public void setLastVisited(Date lastVisited) {
+ this.lastVisited = lastVisited;
+ }
+
+ public Date getExpires() {
+ return expires;
+ }
+
+ public void setExpires(String expires) {
+ if (expires != null) {
+ try {
+ this.expires = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH).parse(expires);
+ } catch (ParseException e) {
+ this.expires = null;
+ }
+ } else {
+ this.expires = null;
+ }
+ }
+ public void setExpires(Date expires) {
+ this.expires = expires;
+ }
+
+ public Long getVisitCount() {
+ return visitCount;
+ }
+
+ public void setVisitCount(Long visitCount) {
+ this.visitCount = visitCount;
+ }
+
+ public MusicDirectory getMusicDirectory() {
+ MusicDirectory dir = new MusicDirectory();
+ dir.addChildren(entries);
+ dir.setId(getId());
+ dir.setName(getName());
+ return dir;
+ }
+
+ public List<Entry> getEntries() {
+ return this.entries;
+ }
+
+ public void addEntry(Entry entry) {
+ entries.add(entry);
+ }
+ }
diff --git a/src/github/daneren2005/dsub/domain/User.java b/app/src/main/java/github/daneren2005/dsub/domain/User.java
index 179ac033..797a1271 100644
--- a/src/github/daneren2005/dsub/domain/User.java
+++ b/app/src/main/java/github/daneren2005/dsub/domain/User.java
@@ -1,117 +1,117 @@
-/*
- 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 2014 (C) Scott Jackson
-*/
-
-package github.daneren2005.dsub.domain;
-
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.List;
-
-public class User implements Serializable {
- public static final String SCROBBLING = "scrobblingEnabled";
- public static final String ADMIN = "adminRole";
- public static final String SETTINGS = "settingsRole";
- public static final String DOWNLOAD = "downloadRole";
- public static final String UPLOAD = "uploadRole";
- public static final String COVERART = "coverArtRole";
- public static final String COMMENT = "commentRole";
- public static final String PODCAST = "podcastRole";
- public static final String STREAM = "streamRole";
- public static final String JUKEBOX = "jukeboxRole";
- public static final String SHARE = "shareRole";
- public static final String LASTFM = "lastFMRole";
- public static final List<String> ROLES = new ArrayList<String>();
-
- static {
- ROLES.add(ADMIN);
- ROLES.add(SETTINGS);
- ROLES.add(STREAM);
- ROLES.add(DOWNLOAD);
- ROLES.add(UPLOAD);
- ROLES.add(COVERART);
- ROLES.add(COMMENT);
- ROLES.add(PODCAST);
- ROLES.add(JUKEBOX);
- ROLES.add(SHARE);
- }
-
- private String username;
- private String password;
- private String email;
-
- private List<Setting> settings = new ArrayList<Setting>();
-
- public User() {
-
- }
-
- public String getUsername() {
- return username;
- }
-
- public void setUsername(String username) {
- this.username = username;
- }
-
- public String getPassword() {
- return password;
- }
-
- public void setPassword(String password) {
- this.password = password;
- }
-
- public String getEmail() {
- return email;
- }
-
- public void setEmail(String email) {
- this.email = email;
- }
-
- public List<Setting> getSettings() {
- return settings;
- }
- public void setSettings(List<Setting> settings) {
- this.settings.clear();
- this.settings.addAll(settings);
- }
- public void addSetting(String name, Boolean value) {
- settings.add(new Setting(name, value));
- }
-
- public static class Setting implements Serializable {
- String name;
- Boolean value;
-
- public Setting() {
-
- }
- public Setting(String name, Boolean value) {
- this.name = name;
- this.value = value;
- }
-
- public String getName() {
- return name;
- }
- public Boolean getValue() {
- return value;
- }
- public void setValue(Boolean value) {
- this.value = value;
- }
- }
-}
+/*
+ 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 2014 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.domain;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+public class User implements Serializable {
+ public static final String SCROBBLING = "scrobblingEnabled";
+ public static final String ADMIN = "adminRole";
+ public static final String SETTINGS = "settingsRole";
+ public static final String DOWNLOAD = "downloadRole";
+ public static final String UPLOAD = "uploadRole";
+ public static final String COVERART = "coverArtRole";
+ public static final String COMMENT = "commentRole";
+ public static final String PODCAST = "podcastRole";
+ public static final String STREAM = "streamRole";
+ public static final String JUKEBOX = "jukeboxRole";
+ public static final String SHARE = "shareRole";
+ public static final String LASTFM = "lastFMRole";
+ public static final List<String> ROLES = new ArrayList<String>();
+
+ static {
+ ROLES.add(ADMIN);
+ ROLES.add(SETTINGS);
+ ROLES.add(STREAM);
+ ROLES.add(DOWNLOAD);
+ ROLES.add(UPLOAD);
+ ROLES.add(COVERART);
+ ROLES.add(COMMENT);
+ ROLES.add(PODCAST);
+ ROLES.add(JUKEBOX);
+ ROLES.add(SHARE);
+ }
+
+ private String username;
+ private String password;
+ private String email;
+
+ private List<Setting> settings = new ArrayList<Setting>();
+
+ public User() {
+
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public List<Setting> getSettings() {
+ return settings;
+ }
+ public void setSettings(List<Setting> settings) {
+ this.settings.clear();
+ this.settings.addAll(settings);
+ }
+ public void addSetting(String name, Boolean value) {
+ settings.add(new Setting(name, value));
+ }
+
+ public static class Setting implements Serializable {
+ String name;
+ Boolean value;
+
+ public Setting() {
+
+ }
+ public Setting(String name, Boolean value) {
+ this.name = name;
+ this.value = value;
+ }
+
+ public String getName() {
+ return name;
+ }
+ public Boolean getValue() {
+ return value;
+ }
+ public void setValue(Boolean value) {
+ this.value = value;
+ }
+ }
+}
diff --git a/src/github/daneren2005/dsub/domain/Version.java b/app/src/main/java/github/daneren2005/dsub/domain/Version.java
index 6b82ea99..6b82ea99 100644
--- a/src/github/daneren2005/dsub/domain/Version.java
+++ b/app/src/main/java/github/daneren2005/dsub/domain/Version.java
diff --git a/src/github/daneren2005/dsub/fragments/AdminFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/AdminFragment.java
index d59526b6..66ce5f15 100644
--- a/src/github/daneren2005/dsub/fragments/AdminFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/AdminFragment.java
@@ -1,147 +1,147 @@
-/*
- 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 2014 (C) Scott Jackson
-*/
-
-package github.daneren2005.dsub.fragments;
-
-import android.os.Bundle;
-import android.view.ContextMenu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.domain.User;
-import github.daneren2005.dsub.service.MusicService;
-import github.daneren2005.dsub.service.parser.SubsonicRESTException;
-import github.daneren2005.dsub.util.Constants;
-import github.daneren2005.dsub.util.ProgressListener;
-import github.daneren2005.dsub.util.UserUtil;
-import github.daneren2005.dsub.util.Util;
-import github.daneren2005.dsub.adapter.UserAdapter;
-
-public class AdminFragment extends SelectListFragment<User> {
- private static String TAG = AdminFragment.class.getSimpleName();
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if(super.onOptionsItemSelected(item)) {
- return true;
- }
-
- switch (item.getItemId()) {
- case R.id.menu_add_user:
- UserUtil.addNewUser(context, this);
- break;
- }
-
- return false;
- }
-
- @Override
- public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, view, menuInfo);
-
- MenuInflater inflater = context.getMenuInflater();
- if(UserUtil.isCurrentAdmin()) {
- inflater.inflate(R.menu.admin_context, menu);
- } else if(UserUtil.isCurrentRole(User.SETTINGS)) {
- inflater.inflate(R.menu.admin_context_user, menu);
- }
- }
-
- @Override
- public boolean onContextItemSelected(MenuItem menuItem) {
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
- User user = objects.get(info.position);
-
- switch(menuItem.getItemId()) {
- case R.id.admin_change_email:
- UserUtil.changeEmail(context, user);
- break;
- case R.id.admin_change_password:
- UserUtil.changePassword(context, user);
- break;
- case R.id.admin_delete_user:
- UserUtil.deleteUser(context, user, adapter);
- break;
- }
-
- return true;
- }
-
- @Override
- public int getOptionsMenu() {
- if(UserUtil.isCurrentAdmin()) {
- return R.menu.admin;
- } else {
- return R.menu.empty;
- }
- }
-
- @Override
- public ArrayAdapter getAdapter(List<User> objs) {
- return new UserAdapter(context, objs, getImageLoader());
- }
-
- @Override
- public List<User> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception {
- try {
- // Will only work if user is admin
- List<User> users = musicService.getUsers(refresh, context, listener);
- if(refresh) {
- UserUtil.refreshCurrentUser(context, true);
- }
- return users;
- } catch(SubsonicRESTException e) {
- // Delete cached users if not allowed to get them
- String s = Util.getRestUrl(context, null, false);
- String cache = "users-" + s.hashCode() + ".ser";
- File file = new File(context.getCacheDir(), cache);
- file.delete();
-
- List<User> users = new ArrayList<User>();
- User user = musicService.getUser(refresh, UserUtil.getCurrentUsername(context), context, listener);
- if(user != null) {
- users.add(user);
- }
-
- UserUtil.refreshCurrentUser(context, false);
- return users;
- }
- }
-
- @Override
- public int getTitleResource() {
- return R.string.button_bar_admin;
- }
-
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- User user = (User) parent.getItemAtPosition(position);
-
- SubsonicFragment fragment = new UserFragment();
- Bundle args = new Bundle();
- args.putSerializable(Constants.INTENT_EXTRA_NAME_ID, user);
- fragment.setArguments(args);
-
- replaceFragment(fragment);
- }
-}
+/*
+ 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 2014 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.fragments;
+
+import android.os.Bundle;
+import android.view.ContextMenu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.User;
+import github.daneren2005.dsub.service.MusicService;
+import github.daneren2005.dsub.service.parser.SubsonicRESTException;
+import github.daneren2005.dsub.util.Constants;
+import github.daneren2005.dsub.util.ProgressListener;
+import github.daneren2005.dsub.util.UserUtil;
+import github.daneren2005.dsub.util.Util;
+import github.daneren2005.dsub.adapter.UserAdapter;
+
+public class AdminFragment extends SelectListFragment<User> {
+ private static String TAG = AdminFragment.class.getSimpleName();
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if(super.onOptionsItemSelected(item)) {
+ return true;
+ }
+
+ switch (item.getItemId()) {
+ case R.id.menu_add_user:
+ UserUtil.addNewUser(context, this);
+ break;
+ }
+
+ return false;
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
+ super.onCreateContextMenu(menu, view, menuInfo);
+
+ MenuInflater inflater = context.getMenuInflater();
+ if(UserUtil.isCurrentAdmin()) {
+ inflater.inflate(R.menu.admin_context, menu);
+ } else if(UserUtil.isCurrentRole(User.SETTINGS)) {
+ inflater.inflate(R.menu.admin_context_user, menu);
+ }
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem menuItem) {
+ AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
+ User user = objects.get(info.position);
+
+ switch(menuItem.getItemId()) {
+ case R.id.admin_change_email:
+ UserUtil.changeEmail(context, user);
+ break;
+ case R.id.admin_change_password:
+ UserUtil.changePassword(context, user);
+ break;
+ case R.id.admin_delete_user:
+ UserUtil.deleteUser(context, user, adapter);
+ break;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int getOptionsMenu() {
+ if(UserUtil.isCurrentAdmin()) {
+ return R.menu.admin;
+ } else {
+ return R.menu.empty;
+ }
+ }
+
+ @Override
+ public ArrayAdapter getAdapter(List<User> objs) {
+ return new UserAdapter(context, objs, getImageLoader());
+ }
+
+ @Override
+ public List<User> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception {
+ try {
+ // Will only work if user is admin
+ List<User> users = musicService.getUsers(refresh, context, listener);
+ if(refresh) {
+ UserUtil.refreshCurrentUser(context, true);
+ }
+ return users;
+ } catch(SubsonicRESTException e) {
+ // Delete cached users if not allowed to get them
+ String s = Util.getRestUrl(context, null, false);
+ String cache = "users-" + s.hashCode() + ".ser";
+ File file = new File(context.getCacheDir(), cache);
+ file.delete();
+
+ List<User> users = new ArrayList<User>();
+ User user = musicService.getUser(refresh, UserUtil.getCurrentUsername(context), context, listener);
+ if(user != null) {
+ users.add(user);
+ }
+
+ UserUtil.refreshCurrentUser(context, false);
+ return users;
+ }
+ }
+
+ @Override
+ public int getTitleResource() {
+ return R.string.button_bar_admin;
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ User user = (User) parent.getItemAtPosition(position);
+
+ SubsonicFragment fragment = new UserFragment();
+ Bundle args = new Bundle();
+ args.putSerializable(Constants.INTENT_EXTRA_NAME_ID, user);
+ fragment.setArguments(args);
+
+ replaceFragment(fragment);
+ }
+}
diff --git a/src/github/daneren2005/dsub/fragments/ChatFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/ChatFragment.java
index 5d017fa7..3e48f1a6 100644
--- a/src/github/daneren2005/dsub/fragments/ChatFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/ChatFragment.java
@@ -1,250 +1,250 @@
-package github.daneren2005.dsub.fragments;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import android.os.Bundle;
-import android.os.Handler;
-import android.support.v4.widget.SwipeRefreshLayout;
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.EditText;
-import android.widget.ImageButton;
-import android.widget.ListView;
-import android.widget.TextView;
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.domain.ChatMessage;
-import github.daneren2005.dsub.service.MusicService;
-import github.daneren2005.dsub.service.MusicServiceFactory;
-import github.daneren2005.dsub.util.BackgroundTask;
-import github.daneren2005.dsub.util.TabBackgroundTask;
-import github.daneren2005.dsub.util.Util;
-import github.daneren2005.dsub.adapter.ChatAdapter;
-import github.daneren2005.dsub.util.Constants;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-
-/**
- * @author Joshua Bahnsen
- */
-public class ChatFragment extends SubsonicFragment {
- private static final String TAG = ChatFragment.class.getSimpleName();
- private ListView chatListView;
- private EditText messageEditText;
- private ImageButton sendButton;
- private Long lastChatMessageTime = (long) 0;
- private ArrayList<ChatMessage> messageList;
- private ScheduledExecutorService executorService;
-
- @Override
- public void onCreate(Bundle bundle) {
- super.onCreate(bundle);
-
- if(bundle != null) {
- List<ChatMessage> abstractList = (List<ChatMessage>) bundle.getSerializable(Constants.FRAGMENT_LIST);
- messageList = new ArrayList<ChatMessage>(abstractList);
- }
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putSerializable(Constants.FRAGMENT_LIST, (Serializable) messageList);
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
- rootView = inflater.inflate(R.layout.chat, container, false);
-
- messageEditText = (EditText) rootView.findViewById(R.id.chat_edittext);
- sendButton = (ImageButton) rootView.findViewById(R.id.chat_send);
-
- sendButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- sendMessage();
- }
- });
-
- chatListView = (ListView) rootView.findViewById(R.id.chat_entries);
-
- messageEditText.setImeActionLabel("Send", KeyEvent.KEYCODE_ENTER);
- messageEditText.addTextChangedListener(new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
- }
-
- @Override
- public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
- }
-
- @Override
- public void afterTextChanged(Editable editable) {
- sendButton.setEnabled(!Util.isNullOrWhiteSpace(editable.toString()));
- }
- });
-
- messageEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
-
- @Override
- public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
- if (actionId == EditorInfo.IME_ACTION_DONE || (actionId == EditorInfo.IME_NULL && event.getAction() == KeyEvent.ACTION_DOWN)) {
- sendMessage();
- return true;
- }
-
- return false;
- }
- });
-
- if(messageList == null) {
- messageList = new ArrayList<ChatMessage>();
- refresh(true);
- } else {
- for (ChatMessage message : messageList) {
- if (message.getTime() > lastChatMessageTime) {
- lastChatMessageTime = message.getTime();
- }
- }
-
- ChatAdapter chatAdapter = new ChatAdapter(context, messageList, getImageLoader());
- chatListView.setAdapter(chatAdapter);
- }
- setTitle(R.string.button_bar_chat);
-
- refreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.refresh_layout);
- refreshLayout.setOnRefreshListener(this);
- setupScrollList(chatListView);
-
- return rootView;
- }
-
- @Override
- public void onResume() {
- super.onResume();
-
- final Handler handler = new Handler();
- Runnable runnable = new Runnable() {
- @Override
- public void run() {
- handler.post(new Runnable() {
- @Override
- public void run() {
- if(primaryFragment) {
- load(false);
- } else {
- invalidated = true;
- }
- }
- });
- }
- };
-
- SharedPreferences prefs = Util.getPreferences(context);
- long refreshRate = Integer.parseInt(prefs.getString(Constants.PREFERENCES_KEY_CHAT_REFRESH, "30"));
- if(refreshRate > 0) {
- executorService = Executors.newSingleThreadScheduledExecutor();
- executorService.scheduleWithFixedDelay(runnable, refreshRate * 1000L, refreshRate * 1000L, TimeUnit.MILLISECONDS);
- }
- }
-
- @Override
- public void onPause() {
- super.onPause();
- if(executorService != null) {
- executorService.shutdown();
- executorService = null;
- }
- }
-
- @Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
- menuInflater.inflate(R.menu.abstract_top_menu, menu);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- return super.onOptionsItemSelected(item);
-
- }
-
- @Override
- protected void refresh(boolean refresh) {
- load(refresh);
- }
-
- private synchronized void load(final boolean refresh) {
- BackgroundTask<List<ChatMessage>> task = new TabBackgroundTask<List<ChatMessage>>(this) {
- @Override
- protected List<ChatMessage> doInBackground() throws Throwable {
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- return musicService.getChatMessages(refresh ? 0L : lastChatMessageTime, context, this);
- }
-
- @Override
- protected void done(List<ChatMessage> result) {
- if (result != null && !result.isEmpty()) {
- if(refresh) {
- messageList.clear();
- }
-
- // Reset lastChatMessageTime if we have a newer message
- for (ChatMessage message : result) {
- if (message.getTime() > lastChatMessageTime) {
- lastChatMessageTime = message.getTime();
- }
- }
-
- // Reverse results to show them on the bottom
- Collections.reverse(result);
- messageList.addAll(result);
-
- ChatAdapter chatAdapter = new ChatAdapter(context, messageList, getImageLoader());
- chatListView.setAdapter(chatAdapter);
- }
- }
- };
-
- task.execute();
- }
-
- private void sendMessage() {
- final String message = messageEditText.getText().toString();
-
- if (!Util.isNullOrWhiteSpace(message)) {
- messageEditText.setText("");
- InputMethodManager mgr = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
- mgr.hideSoftInputFromWindow(messageEditText.getWindowToken(), 0);
-
- BackgroundTask<Void> task = new TabBackgroundTask<Void>(this) {
- @Override
- protected Void doInBackground() throws Throwable {
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- musicService.addChatMessage(message, context, this);
- return null;
- }
-
- @Override
- protected void done(Void result) {
- load(false);
- }
- };
-
- task.execute();
- }
- }
+package github.daneren2005.dsub.fragments;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v4.widget.SwipeRefreshLayout;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.ListView;
+import android.widget.TextView;
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.ChatMessage;
+import github.daneren2005.dsub.service.MusicService;
+import github.daneren2005.dsub.service.MusicServiceFactory;
+import github.daneren2005.dsub.util.BackgroundTask;
+import github.daneren2005.dsub.util.TabBackgroundTask;
+import github.daneren2005.dsub.util.Util;
+import github.daneren2005.dsub.adapter.ChatAdapter;
+import github.daneren2005.dsub.util.Constants;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author Joshua Bahnsen
+ */
+public class ChatFragment extends SubsonicFragment {
+ private static final String TAG = ChatFragment.class.getSimpleName();
+ private ListView chatListView;
+ private EditText messageEditText;
+ private ImageButton sendButton;
+ private Long lastChatMessageTime = (long) 0;
+ private ArrayList<ChatMessage> messageList;
+ private ScheduledExecutorService executorService;
+
+ @Override
+ public void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+
+ if(bundle != null) {
+ List<ChatMessage> abstractList = (List<ChatMessage>) bundle.getSerializable(Constants.FRAGMENT_LIST);
+ messageList = new ArrayList<ChatMessage>(abstractList);
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putSerializable(Constants.FRAGMENT_LIST, (Serializable) messageList);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
+ rootView = inflater.inflate(R.layout.chat, container, false);
+
+ messageEditText = (EditText) rootView.findViewById(R.id.chat_edittext);
+ sendButton = (ImageButton) rootView.findViewById(R.id.chat_send);
+
+ sendButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ sendMessage();
+ }
+ });
+
+ chatListView = (ListView) rootView.findViewById(R.id.chat_entries);
+
+ messageEditText.setImeActionLabel("Send", KeyEvent.KEYCODE_ENTER);
+ messageEditText.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable editable) {
+ sendButton.setEnabled(!Util.isNullOrWhiteSpace(editable.toString()));
+ }
+ });
+
+ messageEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
+
+ @Override
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ if (actionId == EditorInfo.IME_ACTION_DONE || (actionId == EditorInfo.IME_NULL && event.getAction() == KeyEvent.ACTION_DOWN)) {
+ sendMessage();
+ return true;
+ }
+
+ return false;
+ }
+ });
+
+ if(messageList == null) {
+ messageList = new ArrayList<ChatMessage>();
+ refresh(true);
+ } else {
+ for (ChatMessage message : messageList) {
+ if (message.getTime() > lastChatMessageTime) {
+ lastChatMessageTime = message.getTime();
+ }
+ }
+
+ ChatAdapter chatAdapter = new ChatAdapter(context, messageList, getImageLoader());
+ chatListView.setAdapter(chatAdapter);
+ }
+ setTitle(R.string.button_bar_chat);
+
+ refreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.refresh_layout);
+ refreshLayout.setOnRefreshListener(this);
+ setupScrollList(chatListView);
+
+ return rootView;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ final Handler handler = new Handler();
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ if(primaryFragment) {
+ load(false);
+ } else {
+ invalidated = true;
+ }
+ }
+ });
+ }
+ };
+
+ SharedPreferences prefs = Util.getPreferences(context);
+ long refreshRate = Integer.parseInt(prefs.getString(Constants.PREFERENCES_KEY_CHAT_REFRESH, "30"));
+ if(refreshRate > 0) {
+ executorService = Executors.newSingleThreadScheduledExecutor();
+ executorService.scheduleWithFixedDelay(runnable, refreshRate * 1000L, refreshRate * 1000L, TimeUnit.MILLISECONDS);
+ }
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ if(executorService != null) {
+ executorService.shutdown();
+ executorService = null;
+ }
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
+ menuInflater.inflate(R.menu.abstract_top_menu, menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ return super.onOptionsItemSelected(item);
+
+ }
+
+ @Override
+ protected void refresh(boolean refresh) {
+ load(refresh);
+ }
+
+ private synchronized void load(final boolean refresh) {
+ BackgroundTask<List<ChatMessage>> task = new TabBackgroundTask<List<ChatMessage>>(this) {
+ @Override
+ protected List<ChatMessage> doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ return musicService.getChatMessages(refresh ? 0L : lastChatMessageTime, context, this);
+ }
+
+ @Override
+ protected void done(List<ChatMessage> result) {
+ if (result != null && !result.isEmpty()) {
+ if(refresh) {
+ messageList.clear();
+ }
+
+ // Reset lastChatMessageTime if we have a newer message
+ for (ChatMessage message : result) {
+ if (message.getTime() > lastChatMessageTime) {
+ lastChatMessageTime = message.getTime();
+ }
+ }
+
+ // Reverse results to show them on the bottom
+ Collections.reverse(result);
+ messageList.addAll(result);
+
+ ChatAdapter chatAdapter = new ChatAdapter(context, messageList, getImageLoader());
+ chatListView.setAdapter(chatAdapter);
+ }
+ }
+ };
+
+ task.execute();
+ }
+
+ private void sendMessage() {
+ final String message = messageEditText.getText().toString();
+
+ if (!Util.isNullOrWhiteSpace(message)) {
+ messageEditText.setText("");
+ InputMethodManager mgr = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
+ mgr.hideSoftInputFromWindow(messageEditText.getWindowToken(), 0);
+
+ BackgroundTask<Void> task = new TabBackgroundTask<Void>(this) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ musicService.addChatMessage(message, context, this);
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ load(false);
+ }
+ };
+
+ task.execute();
+ }
+ }
} \ No newline at end of file
diff --git a/src/github/daneren2005/dsub/fragments/DownloadFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/DownloadFragment.java
index 24c9d303..59229c3f 100644
--- a/src/github/daneren2005/dsub/fragments/DownloadFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/DownloadFragment.java
@@ -1,189 +1,189 @@
-/*
- 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 2014 (C) Scott Jackson
-*/
-
-package github.daneren2005.dsub.fragments;
-
-import android.content.DialogInterface;
-import android.os.Handler;
-import android.view.ContextMenu;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.domain.MusicDirectory;
-import github.daneren2005.dsub.service.DownloadFile;
-import github.daneren2005.dsub.service.DownloadService;
-import github.daneren2005.dsub.service.MusicService;
-import github.daneren2005.dsub.util.ProgressListener;
-import github.daneren2005.dsub.util.SilentBackgroundTask;
-import github.daneren2005.dsub.util.Util;
-import github.daneren2005.dsub.adapter.DownloadFileAdapter;
-
-public class DownloadFragment extends SelectListFragment<DownloadFile> {
- private long currentRevision;
- private ScheduledExecutorService executorService;
-
- public DownloadFragment() {
- serialize = false;
- }
-
- @Override
- public void onResume() {
- super.onResume();
-
- final Handler handler = new Handler();
- Runnable runnable = new Runnable() {
- @Override
- public void run() {
- handler.post(new Runnable() {
- @Override
- public void run() {
- update();
- }
- });
- }
- };
-
- executorService = Executors.newSingleThreadScheduledExecutor();
- executorService.scheduleWithFixedDelay(runnable, 0L, 1000L, TimeUnit.MILLISECONDS);
- }
-
- @Override
- public void onPause() {
- super.onPause();
- executorService.shutdown();
- }
-
- @Override
- public int getOptionsMenu() {
- return R.menu.downloading;
- }
-
- @Override
- public ArrayAdapter getAdapter(List<DownloadFile> objs) {
- return new DownloadFileAdapter(context, objs);
- }
-
- @Override
- public List<DownloadFile> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception {
- DownloadService downloadService = getDownloadService();
- if(downloadService == null) {
- return new ArrayList<DownloadFile>();
- }
-
- listView.setOnScrollListener(null);
- refreshLayout.setEnabled(false);
-
- List<DownloadFile> songList = new ArrayList<DownloadFile>();
- songList.addAll(downloadService.getBackgroundDownloads());
- currentRevision = downloadService.getDownloadListUpdateRevision();
- return songList;
- }
-
- @Override
- public int getTitleResource() {
- return R.string.button_bar_downloading;
- }
-
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem menuItem) {
- if(super.onOptionsItemSelected(menuItem)) {
- return true;
- }
-
- switch (menuItem.getItemId()) {
- case R.id.menu_remove_all:
- Util.confirmDialog(context, R.string.download_menu_remove_all, "", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- new SilentBackgroundTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- getDownloadService().clearBackground();
- return null;
- }
-
- @Override
- protected void done(Void result) {
- update();
- }
- }.execute();
- }
- });
- return true;
- }
-
- return false;
- }
-
- @Override
- public void onCreateContextMenu(android.view.ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, view, menuInfo);
-
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
- Object selectedItem = ((DownloadFile) listView.getItemAtPosition(info.position)).getSong();
- onCreateContextMenu(menu, view, menuInfo, selectedItem);
- if(selectedItem instanceof MusicDirectory.Entry && !((MusicDirectory.Entry) selectedItem).isVideo() && !Util.isOffline(context)) {
- menu.removeItem(R.id.song_menu_remove_playlist);
- }
-
- recreateContextMenu(menu);
- }
-
- @Override
- public boolean onContextItemSelected(MenuItem menuItem) {
- if(menuItem.getGroupId() != getSupportTag()) {
- return false;
- }
-
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
- Object selectedItem = ((DownloadFile) listView.getItemAtPosition(info.position)).getSong();
-
- if(onContextItemSelected(menuItem, selectedItem)) {
- return true;
- }
-
- return true;
- }
-
- private void update() {
- DownloadService downloadService = getDownloadService();
- if (downloadService == null || objects == null || adapter == null) {
- return;
- }
-
- if (currentRevision != downloadService.getDownloadListUpdateRevision()) {
- List<DownloadFile> downloadFileList = downloadService.getBackgroundDownloads();
- objects.clear();
- objects.addAll(downloadFileList);
- adapter.notifyDataSetChanged();
-
- currentRevision = downloadService.getDownloadListUpdateRevision();
- }
- }
-}
+/*
+ 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 2014 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.fragments;
+
+import android.content.DialogInterface;
+import android.os.Handler;
+import android.view.ContextMenu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.MusicDirectory;
+import github.daneren2005.dsub.service.DownloadFile;
+import github.daneren2005.dsub.service.DownloadService;
+import github.daneren2005.dsub.service.MusicService;
+import github.daneren2005.dsub.util.ProgressListener;
+import github.daneren2005.dsub.util.SilentBackgroundTask;
+import github.daneren2005.dsub.util.Util;
+import github.daneren2005.dsub.adapter.DownloadFileAdapter;
+
+public class DownloadFragment extends SelectListFragment<DownloadFile> {
+ private long currentRevision;
+ private ScheduledExecutorService executorService;
+
+ public DownloadFragment() {
+ serialize = false;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ final Handler handler = new Handler();
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ update();
+ }
+ });
+ }
+ };
+
+ executorService = Executors.newSingleThreadScheduledExecutor();
+ executorService.scheduleWithFixedDelay(runnable, 0L, 1000L, TimeUnit.MILLISECONDS);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ executorService.shutdown();
+ }
+
+ @Override
+ public int getOptionsMenu() {
+ return R.menu.downloading;
+ }
+
+ @Override
+ public ArrayAdapter getAdapter(List<DownloadFile> objs) {
+ return new DownloadFileAdapter(context, objs);
+ }
+
+ @Override
+ public List<DownloadFile> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception {
+ DownloadService downloadService = getDownloadService();
+ if(downloadService == null) {
+ return new ArrayList<DownloadFile>();
+ }
+
+ listView.setOnScrollListener(null);
+ refreshLayout.setEnabled(false);
+
+ List<DownloadFile> songList = new ArrayList<DownloadFile>();
+ songList.addAll(downloadService.getBackgroundDownloads());
+ currentRevision = downloadService.getDownloadListUpdateRevision();
+ return songList;
+ }
+
+ @Override
+ public int getTitleResource() {
+ return R.string.button_bar_downloading;
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem menuItem) {
+ if(super.onOptionsItemSelected(menuItem)) {
+ return true;
+ }
+
+ switch (menuItem.getItemId()) {
+ case R.id.menu_remove_all:
+ Util.confirmDialog(context, R.string.download_menu_remove_all, "", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ new SilentBackgroundTask<Void>(context) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ getDownloadService().clearBackground();
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ update();
+ }
+ }.execute();
+ }
+ });
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public void onCreateContextMenu(android.view.ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
+ super.onCreateContextMenu(menu, view, menuInfo);
+
+ AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
+ Object selectedItem = ((DownloadFile) listView.getItemAtPosition(info.position)).getSong();
+ onCreateContextMenu(menu, view, menuInfo, selectedItem);
+ if(selectedItem instanceof MusicDirectory.Entry && !((MusicDirectory.Entry) selectedItem).isVideo() && !Util.isOffline(context)) {
+ menu.removeItem(R.id.song_menu_remove_playlist);
+ }
+
+ recreateContextMenu(menu);
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem menuItem) {
+ if(menuItem.getGroupId() != getSupportTag()) {
+ return false;
+ }
+
+ AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
+ Object selectedItem = ((DownloadFile) listView.getItemAtPosition(info.position)).getSong();
+
+ if(onContextItemSelected(menuItem, selectedItem)) {
+ return true;
+ }
+
+ return true;
+ }
+
+ private void update() {
+ DownloadService downloadService = getDownloadService();
+ if (downloadService == null || objects == null || adapter == null) {
+ return;
+ }
+
+ if (currentRevision != downloadService.getDownloadListUpdateRevision()) {
+ List<DownloadFile> downloadFileList = downloadService.getBackgroundDownloads();
+ objects.clear();
+ objects.addAll(downloadFileList);
+ adapter.notifyDataSetChanged();
+
+ currentRevision = downloadService.getDownloadListUpdateRevision();
+ }
+ }
+}
diff --git a/src/github/daneren2005/dsub/fragments/EqualizerFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/EqualizerFragment.java
index d957b745..b7080a8e 100644
--- a/src/github/daneren2005/dsub/fragments/EqualizerFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/EqualizerFragment.java
@@ -1,441 +1,441 @@
-/*
- 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 2010 (C) Sindre Mehus
- */
-package github.daneren2005.dsub.fragments;
-
-import android.content.SharedPreferences;
-import android.media.audiofx.BassBoost;
-import android.media.audiofx.Equalizer;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.ContextMenu;
-import android.view.LayoutInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.CheckBox;
-import android.widget.CompoundButton;
-import android.widget.LinearLayout;
-import android.widget.SeekBar;
-import android.widget.TextView;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.audiofx.EqualizerController;
-import github.daneren2005.dsub.audiofx.LoudnessEnhancerController;
-import github.daneren2005.dsub.service.DownloadService;
-import github.daneren2005.dsub.util.Constants;
-import github.daneren2005.dsub.util.Util;
-
-/**
- * Created by Scott on 10/27/13.
- */
-public class EqualizerFragment extends SubsonicFragment {
- private static final String TAG = EqualizerFragment.class.getSimpleName();
-
- private static final int MENU_GROUP_PRESET = 100;
-
- private final Map<Short, SeekBar> bars = new HashMap<Short, SeekBar>();
- private SeekBar bassBar;
- private SeekBar loudnessBar;
- private EqualizerController equalizerController;
- private Equalizer equalizer;
- private BassBoost bass;
- private LoudnessEnhancerController loudnessEnhancer;
- private short masterLevel = 0;
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
- rootView = inflater.inflate(R.layout.equalizer, container, false);
-
- try {
- DownloadService service = DownloadService.getInstance();
- equalizerController = service.getEqualizerController();
- equalizer = equalizerController.getEqualizer();
- bass = equalizerController.getBassBoost();
- loudnessEnhancer = equalizerController.getLoudnessEnhancerController();
-
- initEqualizer();
- } catch(Exception e) {
- Log.e(TAG, "Failed to initialize EQ", e);
- Util.toast(context, "Failed to initialize EQ");
- context.onBackPressed();
- }
-
- final View presetButton = rootView.findViewById(R.id.equalizer_preset);
- registerForContextMenu(presetButton);
- presetButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- presetButton.showContextMenu();
- }
- });
-
- CheckBox enabledCheckBox = (CheckBox) rootView.findViewById(R.id.equalizer_enabled);
- enabledCheckBox.setChecked(equalizer.getEnabled());
- enabledCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
- try {
- setEqualizerEnabled(b);
- } catch(Exception e) {
- Log.e(TAG, "Failed to set EQ enabled", e);
- Util.toast(context, "Failed to set EQ enabled");
- context.onBackPressed();
- }
- }
- });
-
- setTitle(R.string.equalizer_label);
-
- return rootView;
- }
-
- @Override
- public void onPause() {
- super.onPause();
- equalizerController.saveSettings();
-
- if(!equalizer.getEnabled()) {
- equalizerController.release();
- }
- }
-
- @Override
- public void onResume() {
- super.onResume();
- equalizerController = DownloadService.getInstance().getEqualizerController();
- equalizer = equalizerController.getEqualizer();
- bass = equalizerController.getBassBoost();
- }
-
- @Override
- public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, view, menuInfo);
- if(!primaryFragment) {
- return;
- }
-
- short currentPreset;
- try {
- currentPreset = equalizer.getCurrentPreset();
- } catch (Exception x) {
- currentPreset = -1;
- }
-
- for (short preset = 0; preset < equalizer.getNumberOfPresets(); preset++) {
- MenuItem menuItem = menu.add(MENU_GROUP_PRESET, preset, preset, equalizer.getPresetName(preset));
- if (preset == currentPreset) {
- menuItem.setChecked(true);
- }
- }
- menu.setGroupCheckable(MENU_GROUP_PRESET, true, true);
- }
-
- @Override
- public boolean onContextItemSelected(MenuItem menuItem) {
- short preset = (short) menuItem.getItemId();
- for(int i = 0; i < 10; i++) {
- try {
- equalizer.usePreset(preset);
- i = 10;
- } catch (UnsupportedOperationException e) {
- equalizerController.release();
- equalizer = equalizerController.getEqualizer();
- bass = equalizerController.getBassBoost();
- loudnessEnhancer = equalizerController.getLoudnessEnhancerController();
- }
- }
- updateBars(false);
- return true;
- }
-
- private void setEqualizerEnabled(boolean enabled) {
- SharedPreferences prefs = Util.getPreferences(context);
- SharedPreferences.Editor editor = prefs.edit();
- editor.putBoolean(Constants.PREFERENCES_EQUALIZER_ON, enabled);
- editor.commit();
- for(int i = 0; i < 10; i++) {
- try {
- equalizer.setEnabled(enabled);
- updateBars(true);
- i = 10;
- } catch (UnsupportedOperationException e) {
- equalizerController.release();
- equalizer = equalizerController.getEqualizer();
- bass = equalizerController.getBassBoost();
- loudnessEnhancer = equalizerController.getLoudnessEnhancerController();
- }
- }
- }
-
- private void updateBars(boolean changedEnabled) {
- boolean isEnabled = equalizer.getEnabled();
- short minEQLevel = equalizer.getBandLevelRange()[0];
- short maxEQLevel = equalizer.getBandLevelRange()[1];
- for (Map.Entry<Short, SeekBar> entry : bars.entrySet()) {
- short band = entry.getKey();
- SeekBar bar = entry.getValue();
- bar.setEnabled(isEnabled);
- if(band >= (short)0) {
- short setLevel;
- if(changedEnabled) {
- setLevel = (short)(equalizer.getBandLevel(band) - masterLevel);
- if(isEnabled) {
- bar.setProgress(equalizer.getBandLevel(band) - minEQLevel);
- } else {
- bar.setProgress(-minEQLevel);
- }
- } else {
- bar.setProgress(equalizer.getBandLevel(band) - minEQLevel);
- setLevel = (short)(equalizer.getBandLevel(band) + masterLevel);
- }
- if(setLevel < minEQLevel) {
- setLevel = minEQLevel;
- } else if(setLevel > maxEQLevel) {
- setLevel = maxEQLevel;
- }
- equalizer.setBandLevel(band, setLevel);
- } else if(!isEnabled) {
- bar.setProgress(-minEQLevel);
- }
- }
-
- bassBar.setEnabled(isEnabled);
- if(loudnessBar != null) {
- loudnessBar.setEnabled(isEnabled);
- }
- if(changedEnabled && !isEnabled) {
- bass.setStrength((short) 0);
- bassBar.setProgress(0);
- if(loudnessBar != null) {
- loudnessEnhancer.setGain(0);
- loudnessBar.setProgress(0);
- }
- }
-
- if(!isEnabled) {
- masterLevel = 0;
- SharedPreferences prefs = Util.getPreferences(context);
- SharedPreferences.Editor editor = prefs.edit();
- editor.putInt(Constants.PREFERENCES_EQUALIZER_SETTINGS, masterLevel);
- editor.commit();
- }
- }
-
- private void initEqualizer() {
- LinearLayout layout = (LinearLayout) rootView.findViewById(R.id.equalizer_layout);
-
- final short minEQLevel = equalizer.getBandLevelRange()[0];
- final short maxEQLevel = equalizer.getBandLevelRange()[1];
-
- // Setup Pregain
- SharedPreferences prefs = Util.getPreferences(context);
- masterLevel = (short)prefs.getInt(Constants.PREFERENCES_EQUALIZER_SETTINGS, 0);
- initPregain(layout, minEQLevel, maxEQLevel);
-
- for (short i = 0; i < equalizer.getNumberOfBands(); i++) {
- final short band = i;
-
- View bandBar = LayoutInflater.from(context).inflate(R.layout.equalizer_bar, null);
- TextView freqTextView = (TextView) bandBar.findViewById(R.id.equalizer_frequency);
- final TextView levelTextView = (TextView) bandBar.findViewById(R.id.equalizer_level);
- SeekBar bar = (SeekBar) bandBar.findViewById(R.id.equalizer_bar);
-
- freqTextView.setText((equalizer.getCenterFreq(band) / 1000) + " Hz");
-
- bars.put(band, bar);
- bar.setMax(maxEQLevel - minEQLevel);
- short level = equalizer.getBandLevel(band);
- if(equalizer.getEnabled()) {
- level = (short) (level - masterLevel);
- }
- bar.setProgress(level - minEQLevel);
- bar.setEnabled(equalizer.getEnabled());
- updateLevelText(levelTextView, level);
-
- bar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
- @Override
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- short level = (short) (progress + minEQLevel);
- if (fromUser) {
- equalizer.setBandLevel(band, (short)(level + masterLevel));
- }
- updateLevelText(levelTextView, level);
- }
-
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {
- }
-
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {
- }
- });
- layout.addView(bandBar);
- }
-
- LinearLayout specialLayout = (LinearLayout) rootView.findViewById(R.id.special_effects_layout);
-
- // Setup bass booster
- View bandBar = LayoutInflater.from(context).inflate(R.layout.equalizer_bar, null);
- TextView freqTextView = (TextView) bandBar.findViewById(R.id.equalizer_frequency);
- final TextView bassTextView = (TextView) bandBar.findViewById(R.id.equalizer_level);
- bassBar = (SeekBar) bandBar.findViewById(R.id.equalizer_bar);
-
- freqTextView.setText(R.string.equalizer_bass_booster);
- bassBar.setEnabled(equalizer.getEnabled());
- short bassLevel = 0;
- if(bass.getEnabled()) {
- bassLevel = bass.getRoundedStrength();
- }
- bassTextView.setText(context.getResources().getString(R.string.equalizer_bass_size, bassLevel));
- bassBar.setMax(1000);
- bassBar.setProgress(bassLevel);
- bassBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
- @Override
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- try {
- bassTextView.setText(context.getResources().getString(R.string.equalizer_bass_size, progress));
- if (fromUser) {
- if (progress > 0) {
- if (!bass.getEnabled()) {
- bass.setEnabled(true);
- }
- bass.setStrength((short) progress);
- } else if (progress == 0 && bass.getEnabled()) {
- bass.setStrength((short) progress);
- bass.setEnabled(false);
- }
- }
- } catch(Exception e) {
- Log.w(TAG, "Error on changing bass: ", e);
- }
- }
-
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {
-
- }
-
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {
-
- }
- });
- specialLayout.addView(bandBar);
-
- if(loudnessEnhancer != null && loudnessEnhancer.isAvailable()) {
- // Setup loudness enhancer
- bandBar = LayoutInflater.from(context).inflate(R.layout.equalizer_bar, null);
- freqTextView = (TextView) bandBar.findViewById(R.id.equalizer_frequency);
- final TextView loudnessTextView = (TextView) bandBar.findViewById(R.id.equalizer_level);
- loudnessBar = (SeekBar) bandBar.findViewById(R.id.equalizer_bar);
-
- freqTextView.setText(R.string.equalizer_voice_booster);
- loudnessBar.setEnabled(equalizer.getEnabled());
- int loudnessLevel = 0;
- if(loudnessEnhancer.isEnabled()) {
- loudnessLevel = (int) loudnessEnhancer.getGain();
- }
- loudnessBar.setProgress(loudnessLevel / 100);
- loudnessTextView.setText(context.getResources().getString(R.string.equalizer_db_size, loudnessLevel / 100));
- loudnessBar.setMax(15);
- loudnessBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
- @Override
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- try {
- loudnessTextView.setText(context.getResources().getString(R.string.equalizer_db_size, progress));
- if(fromUser) {
- if(progress > 0) {
- if(!loudnessEnhancer.isEnabled()) {
- loudnessEnhancer.enable();
- }
- loudnessEnhancer.setGain(progress * 100);
- } else if(progress == 0 && loudnessEnhancer.isEnabled()) {
- loudnessEnhancer.setGain(progress * 100);
- loudnessEnhancer.disable();
- }
- }
- } catch(Exception e) {
- Log.w(TAG, "Error on changing loudness: ", e);
- }
- }
-
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {
-
- }
-
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {
-
- }
- });
- specialLayout.addView(bandBar);
- }
- }
-
- private void initPregain(LinearLayout layout, final short minEQLevel, final short maxEQLevel) {
- View bandBar = LayoutInflater.from(context).inflate(R.layout.equalizer_bar, null);
- TextView freqTextView = (TextView) bandBar.findViewById(R.id.equalizer_frequency);
- final TextView levelTextView = (TextView) bandBar.findViewById(R.id.equalizer_level);
- SeekBar bar = (SeekBar) bandBar.findViewById(R.id.equalizer_bar);
-
- freqTextView.setText("Master");
-
- bars.put((short)-1, bar);
- bar.setMax(maxEQLevel - minEQLevel);
- bar.setProgress(masterLevel - minEQLevel);
- bar.setEnabled(equalizer.getEnabled());
- updateLevelText(levelTextView, masterLevel);
-
- bar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
- @Override
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- masterLevel = (short) (progress + minEQLevel);
- if (fromUser) {
- SharedPreferences prefs = Util.getPreferences(context);
- SharedPreferences.Editor editor = prefs.edit();
- editor.putInt(Constants.PREFERENCES_EQUALIZER_SETTINGS, masterLevel);
- editor.commit();
- for (short i = 0; i < equalizer.getNumberOfBands(); i++) {
- short level = (short) ((bars.get(i).getProgress() + minEQLevel) + masterLevel);
- equalizer.setBandLevel(i, level);
- }
- }
- updateLevelText(levelTextView, masterLevel);
- }
-
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {
- }
-
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {
- }
- });
- layout.addView(bandBar);
- }
-
- private void updateLevelText(TextView levelTextView, short level) {
- levelTextView.setText((level > 0 ? "+" : "") + context.getResources().getString(R.string.equalizer_db_size, level / 100));
- }
-}
+/*
+ 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 2010 (C) Sindre Mehus
+ */
+package github.daneren2005.dsub.fragments;
+
+import android.content.SharedPreferences;
+import android.media.audiofx.BassBoost;
+import android.media.audiofx.Equalizer;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.LinearLayout;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.audiofx.EqualizerController;
+import github.daneren2005.dsub.audiofx.LoudnessEnhancerController;
+import github.daneren2005.dsub.service.DownloadService;
+import github.daneren2005.dsub.util.Constants;
+import github.daneren2005.dsub.util.Util;
+
+/**
+ * Created by Scott on 10/27/13.
+ */
+public class EqualizerFragment extends SubsonicFragment {
+ private static final String TAG = EqualizerFragment.class.getSimpleName();
+
+ private static final int MENU_GROUP_PRESET = 100;
+
+ private final Map<Short, SeekBar> bars = new HashMap<Short, SeekBar>();
+ private SeekBar bassBar;
+ private SeekBar loudnessBar;
+ private EqualizerController equalizerController;
+ private Equalizer equalizer;
+ private BassBoost bass;
+ private LoudnessEnhancerController loudnessEnhancer;
+ private short masterLevel = 0;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
+ rootView = inflater.inflate(R.layout.equalizer, container, false);
+
+ try {
+ DownloadService service = DownloadService.getInstance();
+ equalizerController = service.getEqualizerController();
+ equalizer = equalizerController.getEqualizer();
+ bass = equalizerController.getBassBoost();
+ loudnessEnhancer = equalizerController.getLoudnessEnhancerController();
+
+ initEqualizer();
+ } catch(Exception e) {
+ Log.e(TAG, "Failed to initialize EQ", e);
+ Util.toast(context, "Failed to initialize EQ");
+ context.onBackPressed();
+ }
+
+ final View presetButton = rootView.findViewById(R.id.equalizer_preset);
+ registerForContextMenu(presetButton);
+ presetButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ presetButton.showContextMenu();
+ }
+ });
+
+ CheckBox enabledCheckBox = (CheckBox) rootView.findViewById(R.id.equalizer_enabled);
+ enabledCheckBox.setChecked(equalizer.getEnabled());
+ enabledCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
+ try {
+ setEqualizerEnabled(b);
+ } catch(Exception e) {
+ Log.e(TAG, "Failed to set EQ enabled", e);
+ Util.toast(context, "Failed to set EQ enabled");
+ context.onBackPressed();
+ }
+ }
+ });
+
+ setTitle(R.string.equalizer_label);
+
+ return rootView;
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ equalizerController.saveSettings();
+
+ if(!equalizer.getEnabled()) {
+ equalizerController.release();
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ equalizerController = DownloadService.getInstance().getEqualizerController();
+ equalizer = equalizerController.getEqualizer();
+ bass = equalizerController.getBassBoost();
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
+ super.onCreateContextMenu(menu, view, menuInfo);
+ if(!primaryFragment) {
+ return;
+ }
+
+ short currentPreset;
+ try {
+ currentPreset = equalizer.getCurrentPreset();
+ } catch (Exception x) {
+ currentPreset = -1;
+ }
+
+ for (short preset = 0; preset < equalizer.getNumberOfPresets(); preset++) {
+ MenuItem menuItem = menu.add(MENU_GROUP_PRESET, preset, preset, equalizer.getPresetName(preset));
+ if (preset == currentPreset) {
+ menuItem.setChecked(true);
+ }
+ }
+ menu.setGroupCheckable(MENU_GROUP_PRESET, true, true);
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem menuItem) {
+ short preset = (short) menuItem.getItemId();
+ for(int i = 0; i < 10; i++) {
+ try {
+ equalizer.usePreset(preset);
+ i = 10;
+ } catch (UnsupportedOperationException e) {
+ equalizerController.release();
+ equalizer = equalizerController.getEqualizer();
+ bass = equalizerController.getBassBoost();
+ loudnessEnhancer = equalizerController.getLoudnessEnhancerController();
+ }
+ }
+ updateBars(false);
+ return true;
+ }
+
+ private void setEqualizerEnabled(boolean enabled) {
+ SharedPreferences prefs = Util.getPreferences(context);
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putBoolean(Constants.PREFERENCES_EQUALIZER_ON, enabled);
+ editor.commit();
+ for(int i = 0; i < 10; i++) {
+ try {
+ equalizer.setEnabled(enabled);
+ updateBars(true);
+ i = 10;
+ } catch (UnsupportedOperationException e) {
+ equalizerController.release();
+ equalizer = equalizerController.getEqualizer();
+ bass = equalizerController.getBassBoost();
+ loudnessEnhancer = equalizerController.getLoudnessEnhancerController();
+ }
+ }
+ }
+
+ private void updateBars(boolean changedEnabled) {
+ boolean isEnabled = equalizer.getEnabled();
+ short minEQLevel = equalizer.getBandLevelRange()[0];
+ short maxEQLevel = equalizer.getBandLevelRange()[1];
+ for (Map.Entry<Short, SeekBar> entry : bars.entrySet()) {
+ short band = entry.getKey();
+ SeekBar bar = entry.getValue();
+ bar.setEnabled(isEnabled);
+ if(band >= (short)0) {
+ short setLevel;
+ if(changedEnabled) {
+ setLevel = (short)(equalizer.getBandLevel(band) - masterLevel);
+ if(isEnabled) {
+ bar.setProgress(equalizer.getBandLevel(band) - minEQLevel);
+ } else {
+ bar.setProgress(-minEQLevel);
+ }
+ } else {
+ bar.setProgress(equalizer.getBandLevel(band) - minEQLevel);
+ setLevel = (short)(equalizer.getBandLevel(band) + masterLevel);
+ }
+ if(setLevel < minEQLevel) {
+ setLevel = minEQLevel;
+ } else if(setLevel > maxEQLevel) {
+ setLevel = maxEQLevel;
+ }
+ equalizer.setBandLevel(band, setLevel);
+ } else if(!isEnabled) {
+ bar.setProgress(-minEQLevel);
+ }
+ }
+
+ bassBar.setEnabled(isEnabled);
+ if(loudnessBar != null) {
+ loudnessBar.setEnabled(isEnabled);
+ }
+ if(changedEnabled && !isEnabled) {
+ bass.setStrength((short) 0);
+ bassBar.setProgress(0);
+ if(loudnessBar != null) {
+ loudnessEnhancer.setGain(0);
+ loudnessBar.setProgress(0);
+ }
+ }
+
+ if(!isEnabled) {
+ masterLevel = 0;
+ SharedPreferences prefs = Util.getPreferences(context);
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putInt(Constants.PREFERENCES_EQUALIZER_SETTINGS, masterLevel);
+ editor.commit();
+ }
+ }
+
+ private void initEqualizer() {
+ LinearLayout layout = (LinearLayout) rootView.findViewById(R.id.equalizer_layout);
+
+ final short minEQLevel = equalizer.getBandLevelRange()[0];
+ final short maxEQLevel = equalizer.getBandLevelRange()[1];
+
+ // Setup Pregain
+ SharedPreferences prefs = Util.getPreferences(context);
+ masterLevel = (short)prefs.getInt(Constants.PREFERENCES_EQUALIZER_SETTINGS, 0);
+ initPregain(layout, minEQLevel, maxEQLevel);
+
+ for (short i = 0; i < equalizer.getNumberOfBands(); i++) {
+ final short band = i;
+
+ View bandBar = LayoutInflater.from(context).inflate(R.layout.equalizer_bar, null);
+ TextView freqTextView = (TextView) bandBar.findViewById(R.id.equalizer_frequency);
+ final TextView levelTextView = (TextView) bandBar.findViewById(R.id.equalizer_level);
+ SeekBar bar = (SeekBar) bandBar.findViewById(R.id.equalizer_bar);
+
+ freqTextView.setText((equalizer.getCenterFreq(band) / 1000) + " Hz");
+
+ bars.put(band, bar);
+ bar.setMax(maxEQLevel - minEQLevel);
+ short level = equalizer.getBandLevel(band);
+ if(equalizer.getEnabled()) {
+ level = (short) (level - masterLevel);
+ }
+ bar.setProgress(level - minEQLevel);
+ bar.setEnabled(equalizer.getEnabled());
+ updateLevelText(levelTextView, level);
+
+ bar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ short level = (short) (progress + minEQLevel);
+ if (fromUser) {
+ equalizer.setBandLevel(band, (short)(level + masterLevel));
+ }
+ updateLevelText(levelTextView, level);
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ }
+ });
+ layout.addView(bandBar);
+ }
+
+ LinearLayout specialLayout = (LinearLayout) rootView.findViewById(R.id.special_effects_layout);
+
+ // Setup bass booster
+ View bandBar = LayoutInflater.from(context).inflate(R.layout.equalizer_bar, null);
+ TextView freqTextView = (TextView) bandBar.findViewById(R.id.equalizer_frequency);
+ final TextView bassTextView = (TextView) bandBar.findViewById(R.id.equalizer_level);
+ bassBar = (SeekBar) bandBar.findViewById(R.id.equalizer_bar);
+
+ freqTextView.setText(R.string.equalizer_bass_booster);
+ bassBar.setEnabled(equalizer.getEnabled());
+ short bassLevel = 0;
+ if(bass.getEnabled()) {
+ bassLevel = bass.getRoundedStrength();
+ }
+ bassTextView.setText(context.getResources().getString(R.string.equalizer_bass_size, bassLevel));
+ bassBar.setMax(1000);
+ bassBar.setProgress(bassLevel);
+ bassBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ try {
+ bassTextView.setText(context.getResources().getString(R.string.equalizer_bass_size, progress));
+ if (fromUser) {
+ if (progress > 0) {
+ if (!bass.getEnabled()) {
+ bass.setEnabled(true);
+ }
+ bass.setStrength((short) progress);
+ } else if (progress == 0 && bass.getEnabled()) {
+ bass.setStrength((short) progress);
+ bass.setEnabled(false);
+ }
+ }
+ } catch(Exception e) {
+ Log.w(TAG, "Error on changing bass: ", e);
+ }
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+
+ }
+ });
+ specialLayout.addView(bandBar);
+
+ if(loudnessEnhancer != null && loudnessEnhancer.isAvailable()) {
+ // Setup loudness enhancer
+ bandBar = LayoutInflater.from(context).inflate(R.layout.equalizer_bar, null);
+ freqTextView = (TextView) bandBar.findViewById(R.id.equalizer_frequency);
+ final TextView loudnessTextView = (TextView) bandBar.findViewById(R.id.equalizer_level);
+ loudnessBar = (SeekBar) bandBar.findViewById(R.id.equalizer_bar);
+
+ freqTextView.setText(R.string.equalizer_voice_booster);
+ loudnessBar.setEnabled(equalizer.getEnabled());
+ int loudnessLevel = 0;
+ if(loudnessEnhancer.isEnabled()) {
+ loudnessLevel = (int) loudnessEnhancer.getGain();
+ }
+ loudnessBar.setProgress(loudnessLevel / 100);
+ loudnessTextView.setText(context.getResources().getString(R.string.equalizer_db_size, loudnessLevel / 100));
+ loudnessBar.setMax(15);
+ loudnessBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ try {
+ loudnessTextView.setText(context.getResources().getString(R.string.equalizer_db_size, progress));
+ if(fromUser) {
+ if(progress > 0) {
+ if(!loudnessEnhancer.isEnabled()) {
+ loudnessEnhancer.enable();
+ }
+ loudnessEnhancer.setGain(progress * 100);
+ } else if(progress == 0 && loudnessEnhancer.isEnabled()) {
+ loudnessEnhancer.setGain(progress * 100);
+ loudnessEnhancer.disable();
+ }
+ }
+ } catch(Exception e) {
+ Log.w(TAG, "Error on changing loudness: ", e);
+ }
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+
+ }
+ });
+ specialLayout.addView(bandBar);
+ }
+ }
+
+ private void initPregain(LinearLayout layout, final short minEQLevel, final short maxEQLevel) {
+ View bandBar = LayoutInflater.from(context).inflate(R.layout.equalizer_bar, null);
+ TextView freqTextView = (TextView) bandBar.findViewById(R.id.equalizer_frequency);
+ final TextView levelTextView = (TextView) bandBar.findViewById(R.id.equalizer_level);
+ SeekBar bar = (SeekBar) bandBar.findViewById(R.id.equalizer_bar);
+
+ freqTextView.setText("Master");
+
+ bars.put((short)-1, bar);
+ bar.setMax(maxEQLevel - minEQLevel);
+ bar.setProgress(masterLevel - minEQLevel);
+ bar.setEnabled(equalizer.getEnabled());
+ updateLevelText(levelTextView, masterLevel);
+
+ bar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ masterLevel = (short) (progress + minEQLevel);
+ if (fromUser) {
+ SharedPreferences prefs = Util.getPreferences(context);
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putInt(Constants.PREFERENCES_EQUALIZER_SETTINGS, masterLevel);
+ editor.commit();
+ for (short i = 0; i < equalizer.getNumberOfBands(); i++) {
+ short level = (short) ((bars.get(i).getProgress() + minEQLevel) + masterLevel);
+ equalizer.setBandLevel(i, level);
+ }
+ }
+ updateLevelText(levelTextView, masterLevel);
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ }
+ });
+ layout.addView(bandBar);
+ }
+
+ private void updateLevelText(TextView levelTextView, short level) {
+ levelTextView.setText((level > 0 ? "+" : "") + context.getResources().getString(R.string.equalizer_db_size, level / 100));
+ }
+}
diff --git a/src/github/daneren2005/dsub/fragments/LyricsFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/LyricsFragment.java
index 826029f5..826029f5 100644
--- a/src/github/daneren2005/dsub/fragments/LyricsFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/LyricsFragment.java
diff --git a/src/github/daneren2005/dsub/fragments/MainFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/MainFragment.java
index ce488aff..ae38534a 100644
--- a/src/github/daneren2005/dsub/fragments/MainFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/MainFragment.java
@@ -1,586 +1,586 @@
-package github.daneren2005.dsub.fragments;
-
-import android.app.AlertDialog;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.StatFs;
-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.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.CheckBox;
-import android.widget.CompoundButton;
-import android.widget.ListView;
-import android.widget.TextView;
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.domain.MusicDirectory;
-import github.daneren2005.dsub.domain.ServerInfo;
-import github.daneren2005.dsub.service.DownloadService;
-import github.daneren2005.dsub.util.Constants;
-import github.daneren2005.dsub.util.FileUtil;
-import github.daneren2005.dsub.util.LoadingTask;
-import github.daneren2005.dsub.util.Pair;
-import github.daneren2005.dsub.util.UserUtil;
-import github.daneren2005.dsub.adapter.MergeAdapter;
-import github.daneren2005.dsub.util.Util;
-import github.daneren2005.dsub.service.MusicService;
-import github.daneren2005.dsub.service.MusicServiceFactory;
-import github.daneren2005.dsub.util.SilentBackgroundTask;
-import github.daneren2005.dsub.view.ChangeLog;
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-public class MainFragment extends SubsonicFragment {
- private static final String TAG = MainFragment.class.getSimpleName();
- private LayoutInflater inflater;
- private TextView countView;
-
- private static final int MENU_GROUP_SERVER = 10;
- private static final int MENU_ITEM_SERVER_BASE = 100;
-
- @Override
- public void onCreate(Bundle bundle) {
- super.onCreate(bundle);
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
- this.inflater = inflater;
- rootView = inflater.inflate(R.layout.home, container, false);
-
- createLayout();
-
- return rootView;
- }
-
- @Override
- public void onResume() {
- super.onResume();
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- }
-
- @Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
- menuInflater.inflate(R.menu.main, menu);
-
- try {
- if (!ServerInfo.isMadsonic(context) || !UserUtil.isCurrentAdmin()) {
- menu.setGroupVisible(R.id.madsonic, false);
- }
- } catch(Exception e) {
- Log.w(TAG, "Error on setting madsonic invisible", e);
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if(super.onOptionsItemSelected(item)) {
- return true;
- }
-
- switch (item.getItemId()) {
- case R.id.menu_log:
- getLogs();
- return true;
- case R.id.menu_about:
- showAboutDialog();
- return true;
- case R.id.menu_changelog:
- ChangeLog changeLog = new ChangeLog(context, Util.getPreferences(context));
- changeLog.getFullLogDialog().show();
- return true;
- case R.id.menu_faq:
- showFAQDialog();
- return true;
- case R.id.menu_rescan:
- rescanServer();
- return true;
- }
-
- return false;
- }
-
- @Override
- public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, view, menuInfo);
-
- int serverCount = Util.getServerCount(context);
- int activeServer = Util.getActiveServer(context);
- for(int i = 1; i <= serverCount; i++) {
- android.view.MenuItem menuItem = menu.add(MENU_GROUP_SERVER, MENU_ITEM_SERVER_BASE + i, MENU_ITEM_SERVER_BASE + i, Util.getServerName(context, i));
- if(i == activeServer) {
- menuItem.setChecked(true);
- }
- }
- menu.setGroupCheckable(MENU_GROUP_SERVER, true, true);
- menu.setHeaderTitle(R.string.main_select_server);
-
- recreateContextMenu(menu);
- }
-
- @Override
- public boolean onContextItemSelected(android.view.MenuItem menuItem) {
- if(menuItem.getGroupId() != getSupportTag()) {
- return false;
- }
-
- int activeServer = menuItem.getItemId() - MENU_ITEM_SERVER_BASE;
- setActiveServer(activeServer);
- return true;
- }
-
- @Override
- protected void refresh(boolean refresh) {
- createLayout();
- }
-
- private void createLayout() {
- View buttons = inflater.inflate(R.layout.main_buttons, null);
-
- final View serverButton = buttons.findViewById(R.id.main_select_server);
- final TextView serverTextView = (TextView) serverButton.findViewById(R.id.main_select_server_2);
- final TextView offlineButton = (TextView) buttons.findViewById(R.id.main_offline);
- offlineButton.setText(Util.isOffline(context) ? R.string.main_online : R.string.main_offline);
-
- final View albumsTitle = buttons.findViewById(R.id.main_albums);
- final View videoTitle = buttons.findViewById(R.id.main_video_section);
- final View albumsNewestButton = buttons.findViewById(R.id.main_albums_newest);
- countView = (TextView) buttons.findViewById(R.id.main_albums_recent_count);
- final View albumsRandomButton = buttons.findViewById(R.id.main_albums_random);
- final View albumsHighestButton = buttons.findViewById(R.id.main_albums_highest);
- final View albumsRecentButton = buttons.findViewById(R.id.main_albums_recent);
- final View albumsFrequentButton = buttons.findViewById(R.id.main_albums_frequent);
- final View albumsStarredButton = buttons.findViewById(R.id.main_albums_starred);
- final View albumsGenresButton = buttons.findViewById(R.id.main_albums_genres);
- final View albumsYearButton = buttons.findViewById(R.id.main_albums_year);
- final View albumsAlphabeticalButton = buttons.findViewById(R.id.main_albums_alphabetical);
- final View videosButton = buttons.findViewById(R.id.main_videos);
-
- final View dummyView = rootView.findViewById(R.id.main_dummy);
-
- final CheckBox albumsPerFolderCheckbox = (CheckBox) buttons.findViewById(R.id.main_albums_per_folder);
- if(!Util.isOffline(context) && ServerInfo.canAlbumListPerFolder(context)) {
- albumsPerFolderCheckbox.setChecked(Util.getAlbumListsPerFolder(context));
- albumsPerFolderCheckbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- Util.setAlbumListsPerFolder(context, isChecked);
- }
- });
- } else {
- albumsPerFolderCheckbox.setVisibility(View.GONE);
- }
-
- int instance = Util.getActiveServer(context);
- String name = Util.getServerName(context, instance);
- serverTextView.setText(name);
-
- ListView list = (ListView) rootView.findViewById(R.id.main_list);
-
- MergeAdapter adapter = new MergeAdapter();
- if (!Util.isOffline(context)) {
- adapter.addViews(Arrays.asList(serverButton), true);
- }
- adapter.addView(offlineButton, true);
- if (!Util.isOffline(context)) {
- adapter.addView(albumsTitle, false);
- adapter.addViews(Arrays.asList(albumsNewestButton, albumsRandomButton), true);
- if(ServerInfo.checkServerVersion(context, "1.8")) {
- adapter.addView(albumsAlphabeticalButton, true);
- }
- if(!Util.isTagBrowsing(context)) {
- adapter.addView(albumsHighestButton, true);
- }
- adapter.addViews(Arrays.asList(albumsStarredButton, albumsGenresButton, albumsYearButton, albumsRecentButton, albumsFrequentButton), true);
- if(ServerInfo.checkServerVersion(context, "1.8") && !Util.isTagBrowsing(context)) {
- adapter.addView(videoTitle, false);
- adapter.addView(videosButton, true);
- }
- }
- list.setAdapter(adapter);
- registerForContextMenu(dummyView);
-
- list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- if (view == serverButton) {
- dummyView.showContextMenu();
- } else if (view == offlineButton) {
- toggleOffline();
- } else if (view == albumsNewestButton) {
- showAlbumList("newest");
- } else if (view == albumsRandomButton) {
- showAlbumList("random");
- } else if (view == albumsHighestButton) {
- showAlbumList("highest");
- } else if (view == albumsRecentButton) {
- showAlbumList("recent");
- } else if (view == albumsFrequentButton) {
- showAlbumList("frequent");
- } else if (view == albumsStarredButton) {
- showAlbumList("starred");
- } else if(view == albumsGenresButton) {
- showAlbumList("genres");
- } else if(view == albumsYearButton) {
- showAlbumList("years");
- } else if(view == albumsAlphabeticalButton) {
- showAlbumList("alphabeticalByName");
- } else if(view == videosButton) {
- showVideos();
- }
- }
- });
- setTitle(R.string.common_appname);
-
- if(!Util.isOffline(context)) {
- getMostRecentCount();
- }
- }
-
- private void setActiveServer(int instance) {
- if (Util.getActiveServer(context) != instance) {
- final DownloadService service = getDownloadService();
- if (service != null) {
- new SilentBackgroundTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- service.clearIncomplete();
- return null;
- }
- }.execute();
-
- }
- Util.setActiveServer(context, instance);
- context.invalidate();
- UserUtil.refreshCurrentUser(context, false, true);
- }
- }
-
- private void toggleOffline() {
- boolean isOffline = Util.isOffline(context);
- Util.setOffline(context, !isOffline);
- context.invalidate();
- DownloadService service = getDownloadService();
- if (service != null) {
- service.setOnline(isOffline);
- }
-
- // Coming back online
- if(isOffline) {
- int scrobblesCount = Util.offlineScrobblesCount(context);
- int starsCount = Util.offlineStarsCount(context);
- if(scrobblesCount > 0 || starsCount > 0){
- showOfflineSyncDialog(scrobblesCount, starsCount);
- }
- }
-
- UserUtil.seedCurrentUser(context);
- }
-
- private void showAlbumList(String type) {
- if("genres".equals(type)) {
- SubsonicFragment fragment = new SelectGenreFragment();
- replaceFragment(fragment);
- } else if("years".equals(type)) {
- SubsonicFragment fragment = new SelectYearFragment();
- replaceFragment(fragment);
- } else {
- // Clear out recently added count when viewing
- if("newest".equals(type)) {
- SharedPreferences.Editor editor = Util.getPreferences(context).edit();
- editor.putInt(Constants.PREFERENCES_KEY_RECENT_COUNT + Util.getActiveServer(context), 0);
- editor.commit();
-
- // Clear immediately so doesn't still show when pressing back
- setMostRecentCount(0);
- }
-
- SubsonicFragment fragment = new SelectDirectoryFragment();
- Bundle args = new Bundle();
- args.putString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE, type);
- args.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 20);
- args.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0);
- fragment.setArguments(args);
-
- replaceFragment(fragment);
- }
- }
- private void showVideos() {
- SubsonicFragment fragment = new SelectVideoFragment();
- replaceFragment(fragment);
- }
-
- private void showOfflineSyncDialog(final int scrobbleCount, final int starsCount) {
- String syncDefault = Util.getSyncDefault(context);
- if(syncDefault != null) {
- if("sync".equals(syncDefault)) {
- syncOffline(scrobbleCount, starsCount);
- return;
- } else if("delete".equals(syncDefault)) {
- deleteOffline();
- return;
- }
- }
-
- View checkBoxView = context.getLayoutInflater().inflate(R.layout.sync_dialog, null);
- final CheckBox checkBox = (CheckBox)checkBoxView.findViewById(R.id.sync_default);
-
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
- builder.setIcon(android.R.drawable.ic_dialog_info)
- .setTitle(R.string.offline_sync_dialog_title)
- .setMessage(context.getResources().getString(R.string.offline_sync_dialog_message, scrobbleCount, starsCount))
- .setView(checkBoxView)
- .setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialogInterface, int i) {
- if(checkBox.isChecked()) {
- Util.setSyncDefault(context, "sync");
- }
- syncOffline(scrobbleCount, starsCount);
- }
- }).setNeutralButton(R.string.common_cancel, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialogInterface, int i) {
- dialogInterface.dismiss();
- }
- }).setNegativeButton(R.string.common_delete, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialogInterface, int i) {
- if(checkBox.isChecked()) {
- Util.setSyncDefault(context, "delete");
- }
- deleteOffline();
- }
- });
-
- builder.create().show();
- }
-
- private void syncOffline(final int scrobbleCount, final int starsCount) {
- new SilentBackgroundTask<Integer>(context) {
- @Override
- protected Integer doInBackground() throws Throwable {
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- return musicService.processOfflineSyncs(context, null);
- }
-
- @Override
- protected void done(Integer result) {
- if(result == scrobbleCount) {
- Util.toast(context, context.getResources().getString(R.string.offline_sync_success, result));
- } else {
- Util.toast(context, context.getResources().getString(R.string.offline_sync_partial, result, scrobbleCount + starsCount));
- }
- }
-
- @Override
- protected void error(Throwable error) {
- Log.w(TAG, "Failed to sync offline stats", error);
- String msg = context.getResources().getString(R.string.offline_sync_error) + " " + getErrorMessage(error);
- Util.toast(context, msg);
- }
- }.execute();
- }
- private void deleteOffline() {
- SharedPreferences.Editor offline = Util.getOfflineSync(context).edit();
- offline.putInt(Constants.OFFLINE_SCROBBLE_COUNT, 0);
- offline.putInt(Constants.OFFLINE_STAR_COUNT, 0);
- offline.commit();
- }
-
- private void showAboutDialog() {
- new LoadingTask<String>(context) {
- @Override
- protected String doInBackground() throws Throwable {
- File rootFolder = FileUtil.getMusicDirectory(context);
- StatFs stat = new StatFs(rootFolder.getPath());
- long bytesTotalFs = (long) stat.getBlockCount() * (long) stat.getBlockSize();
- long bytesAvailableFs = (long) stat.getAvailableBlocks() * (long) stat.getBlockSize();
-
- Pair<Long, Long> used = FileUtil.getUsedSize(context, rootFolder);
-
- return getResources().getString(R.string.main_about_text,
- context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName,
- used.getFirst(),
- Util.formatLocalizedBytes(used.getSecond(), context),
- Util.formatLocalizedBytes(Util.getCacheSizeMB(context) * 1024L * 1024L, context),
- Util.formatLocalizedBytes(bytesAvailableFs, context),
- Util.formatLocalizedBytes(bytesTotalFs, context));
- }
-
- @Override
- protected void done(String msg) {
- try {
- Util.info(context, R.string.main_about_title, msg);
- } catch(Exception e) {
- Util.toast(context, "Failed to open dialog");
- }
- }
- }.execute();
- }
-
- private void showFAQDialog() {
- Util.showHTMLDialog(context, R.string.main_faq_title, R.string.main_faq_text);
- }
-
- private void rescanServer() {
- new LoadingTask<Void>(context, false) {
- @Override
- protected Void doInBackground() throws Throwable {
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- musicService.startRescan(context, this);
- return null;
- }
-
- @Override
- protected void done(Void value) {
- Util.toast(context, R.string.main_scan_complete);
- }
- }.execute();
- }
-
- private void getLogs() {
- try {
- final String version = context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName;
- new LoadingTask<File>(context) {
- @Override
- protected File doInBackground() throws Throwable {
- updateProgress("Gathering Logs");
- File logcat = new File(FileUtil.getSubsonicDirectory(context), "logcat.txt");
- Util.delete(logcat);
- Process logcatProc = null;
-
- try {
- List<String> progs = new ArrayList<String>();
- progs.add("logcat");
- progs.add("-v");
- progs.add("time");
- progs.add("-d");
- progs.add("-f");
- progs.add(logcat.getCanonicalPath());
- progs.add("*:I");
-
- logcatProc = Runtime.getRuntime().exec(progs.toArray(new String[progs.size()]));
- logcatProc.waitFor();
- } catch(Exception e) {
- Util.toast(context, "Failed to gather logs");
- } finally {
- if(logcatProc != null) {
- logcatProc.destroy();
- }
- }
-
- return logcat;
- }
-
- @Override
- protected void done(File logcat) {
- String footer = "Android SDK: " + Build.VERSION.SDK;
- footer += "\nDevice Model: " + Build.MODEL;
- footer += "\nDevice Name: " + Build.MANUFACTURER + " " + Build.PRODUCT;
- footer += "\nROM: " + Build.DISPLAY;
-
- Intent email = new Intent(Intent.ACTION_SENDTO,
- Uri.fromParts("mailto", "dsub.android@gmail.com", null));
- email.putExtra(Intent.EXTRA_SUBJECT, "DSub " + version + " Error Logs");
- email.putExtra(Intent.EXTRA_TEXT, "Describe the problem here\n\n\n" + footer);
- Uri attachment = Uri.fromFile(logcat);
- email.putExtra(Intent.EXTRA_STREAM, attachment);
- startActivity(email);
- }
- }.execute();
- } catch(Exception e) {}
- }
-
- private void getMostRecentCount() {
- // Use stashed value until after refresh occurs
- SharedPreferences prefs = Util.getPreferences(context);
- final int startCount = prefs.getInt(Constants.PREFERENCES_KEY_RECENT_COUNT + Util.getActiveServer(context), 0);
- setMostRecentCount(startCount);
-
- new SilentBackgroundTask<Integer>(context) {
- @Override
- public Integer doInBackground() throws Exception {
- String recentAddedFile = Util.getCacheName(context, "recent_count");
- ArrayList<String> recents = FileUtil.deserialize(context, recentAddedFile, ArrayList.class);
- if(recents == null) {
- recents = new ArrayList<String>();
- }
-
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- MusicDirectory recentlyAdded = musicService.getAlbumList("newest", 20, 0, context, null);
-
- // If first run, just put everything in it and return 0
- boolean firstRun = recents.isEmpty();
-
- // Count how many new albums are in the list
- int count = 0;
- for(MusicDirectory.Entry album: recentlyAdded.getChildren()) {
- if(!recents.contains(album.getId())) {
- recents.add(album.getId());
- count++;
- }
- }
-
- // Keep recents list from growing infinitely
- while(recents.size() > 40) {
- recents.remove(0);
- }
- FileUtil.serialize(context, recents, recentAddedFile);
-
- if(firstRun) {
- return 0;
- } else {
- // Add the old count which will get cleared out after viewing recents
- count += startCount;
- SharedPreferences.Editor editor = Util.getPreferences(context).edit();
- editor.putInt(Constants.PREFERENCES_KEY_RECENT_COUNT + Util.getActiveServer(context), count);
- editor.commit();
-
- return count;
- }
- }
-
- @Override
- public void done(Integer result) {
- setMostRecentCount(result);
- }
-
- @Override
- public void error(Throwable x) {
- Log.w(TAG, "Failed to refresh most recent count", x);
- }
- }.execute();
- }
-
- private void setMostRecentCount(int count) {
- if(count <= 0) {
- countView.setVisibility(View.GONE);
- } else {
- String displayValue;
- if(count < 10) {
- displayValue = "0" + count;
- } else {
- displayValue = "" + count;
- }
-
- countView.setText(displayValue);
- countView.setVisibility(View.VISIBLE);
- }
- }
-}
+package github.daneren2005.dsub.fragments;
+
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.StatFs;
+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.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.ListView;
+import android.widget.TextView;
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.MusicDirectory;
+import github.daneren2005.dsub.domain.ServerInfo;
+import github.daneren2005.dsub.service.DownloadService;
+import github.daneren2005.dsub.util.Constants;
+import github.daneren2005.dsub.util.FileUtil;
+import github.daneren2005.dsub.util.LoadingTask;
+import github.daneren2005.dsub.util.Pair;
+import github.daneren2005.dsub.util.UserUtil;
+import github.daneren2005.dsub.adapter.MergeAdapter;
+import github.daneren2005.dsub.util.Util;
+import github.daneren2005.dsub.service.MusicService;
+import github.daneren2005.dsub.service.MusicServiceFactory;
+import github.daneren2005.dsub.util.SilentBackgroundTask;
+import github.daneren2005.dsub.view.ChangeLog;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class MainFragment extends SubsonicFragment {
+ private static final String TAG = MainFragment.class.getSimpleName();
+ private LayoutInflater inflater;
+ private TextView countView;
+
+ private static final int MENU_GROUP_SERVER = 10;
+ private static final int MENU_ITEM_SERVER_BASE = 100;
+
+ @Override
+ public void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
+ this.inflater = inflater;
+ rootView = inflater.inflate(R.layout.home, container, false);
+
+ createLayout();
+
+ return rootView;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
+ menuInflater.inflate(R.menu.main, menu);
+
+ try {
+ if (!ServerInfo.isMadsonic(context) || !UserUtil.isCurrentAdmin()) {
+ menu.setGroupVisible(R.id.madsonic, false);
+ }
+ } catch(Exception e) {
+ Log.w(TAG, "Error on setting madsonic invisible", e);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if(super.onOptionsItemSelected(item)) {
+ return true;
+ }
+
+ switch (item.getItemId()) {
+ case R.id.menu_log:
+ getLogs();
+ return true;
+ case R.id.menu_about:
+ showAboutDialog();
+ return true;
+ case R.id.menu_changelog:
+ ChangeLog changeLog = new ChangeLog(context, Util.getPreferences(context));
+ changeLog.getFullLogDialog().show();
+ return true;
+ case R.id.menu_faq:
+ showFAQDialog();
+ return true;
+ case R.id.menu_rescan:
+ rescanServer();
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
+ super.onCreateContextMenu(menu, view, menuInfo);
+
+ int serverCount = Util.getServerCount(context);
+ int activeServer = Util.getActiveServer(context);
+ for(int i = 1; i <= serverCount; i++) {
+ android.view.MenuItem menuItem = menu.add(MENU_GROUP_SERVER, MENU_ITEM_SERVER_BASE + i, MENU_ITEM_SERVER_BASE + i, Util.getServerName(context, i));
+ if(i == activeServer) {
+ menuItem.setChecked(true);
+ }
+ }
+ menu.setGroupCheckable(MENU_GROUP_SERVER, true, true);
+ menu.setHeaderTitle(R.string.main_select_server);
+
+ recreateContextMenu(menu);
+ }
+
+ @Override
+ public boolean onContextItemSelected(android.view.MenuItem menuItem) {
+ if(menuItem.getGroupId() != getSupportTag()) {
+ return false;
+ }
+
+ int activeServer = menuItem.getItemId() - MENU_ITEM_SERVER_BASE;
+ setActiveServer(activeServer);
+ return true;
+ }
+
+ @Override
+ protected void refresh(boolean refresh) {
+ createLayout();
+ }
+
+ private void createLayout() {
+ View buttons = inflater.inflate(R.layout.main_buttons, null);
+
+ final View serverButton = buttons.findViewById(R.id.main_select_server);
+ final TextView serverTextView = (TextView) serverButton.findViewById(R.id.main_select_server_2);
+ final TextView offlineButton = (TextView) buttons.findViewById(R.id.main_offline);
+ offlineButton.setText(Util.isOffline(context) ? R.string.main_online : R.string.main_offline);
+
+ final View albumsTitle = buttons.findViewById(R.id.main_albums);
+ final View videoTitle = buttons.findViewById(R.id.main_video_section);
+ final View albumsNewestButton = buttons.findViewById(R.id.main_albums_newest);
+ countView = (TextView) buttons.findViewById(R.id.main_albums_recent_count);
+ final View albumsRandomButton = buttons.findViewById(R.id.main_albums_random);
+ final View albumsHighestButton = buttons.findViewById(R.id.main_albums_highest);
+ final View albumsRecentButton = buttons.findViewById(R.id.main_albums_recent);
+ final View albumsFrequentButton = buttons.findViewById(R.id.main_albums_frequent);
+ final View albumsStarredButton = buttons.findViewById(R.id.main_albums_starred);
+ final View albumsGenresButton = buttons.findViewById(R.id.main_albums_genres);
+ final View albumsYearButton = buttons.findViewById(R.id.main_albums_year);
+ final View albumsAlphabeticalButton = buttons.findViewById(R.id.main_albums_alphabetical);
+ final View videosButton = buttons.findViewById(R.id.main_videos);
+
+ final View dummyView = rootView.findViewById(R.id.main_dummy);
+
+ final CheckBox albumsPerFolderCheckbox = (CheckBox) buttons.findViewById(R.id.main_albums_per_folder);
+ if(!Util.isOffline(context) && ServerInfo.canAlbumListPerFolder(context)) {
+ albumsPerFolderCheckbox.setChecked(Util.getAlbumListsPerFolder(context));
+ albumsPerFolderCheckbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ Util.setAlbumListsPerFolder(context, isChecked);
+ }
+ });
+ } else {
+ albumsPerFolderCheckbox.setVisibility(View.GONE);
+ }
+
+ int instance = Util.getActiveServer(context);
+ String name = Util.getServerName(context, instance);
+ serverTextView.setText(name);
+
+ ListView list = (ListView) rootView.findViewById(R.id.main_list);
+
+ MergeAdapter adapter = new MergeAdapter();
+ if (!Util.isOffline(context)) {
+ adapter.addViews(Arrays.asList(serverButton), true);
+ }
+ adapter.addView(offlineButton, true);
+ if (!Util.isOffline(context)) {
+ adapter.addView(albumsTitle, false);
+ adapter.addViews(Arrays.asList(albumsNewestButton, albumsRandomButton), true);
+ if(ServerInfo.checkServerVersion(context, "1.8")) {
+ adapter.addView(albumsAlphabeticalButton, true);
+ }
+ if(!Util.isTagBrowsing(context)) {
+ adapter.addView(albumsHighestButton, true);
+ }
+ adapter.addViews(Arrays.asList(albumsStarredButton, albumsGenresButton, albumsYearButton, albumsRecentButton, albumsFrequentButton), true);
+ if(ServerInfo.checkServerVersion(context, "1.8") && !Util.isTagBrowsing(context)) {
+ adapter.addView(videoTitle, false);
+ adapter.addView(videosButton, true);
+ }
+ }
+ list.setAdapter(adapter);
+ registerForContextMenu(dummyView);
+
+ list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ if (view == serverButton) {
+ dummyView.showContextMenu();
+ } else if (view == offlineButton) {
+ toggleOffline();
+ } else if (view == albumsNewestButton) {
+ showAlbumList("newest");
+ } else if (view == albumsRandomButton) {
+ showAlbumList("random");
+ } else if (view == albumsHighestButton) {
+ showAlbumList("highest");
+ } else if (view == albumsRecentButton) {
+ showAlbumList("recent");
+ } else if (view == albumsFrequentButton) {
+ showAlbumList("frequent");
+ } else if (view == albumsStarredButton) {
+ showAlbumList("starred");
+ } else if(view == albumsGenresButton) {
+ showAlbumList("genres");
+ } else if(view == albumsYearButton) {
+ showAlbumList("years");
+ } else if(view == albumsAlphabeticalButton) {
+ showAlbumList("alphabeticalByName");
+ } else if(view == videosButton) {
+ showVideos();
+ }
+ }
+ });
+ setTitle(R.string.common_appname);
+
+ if(!Util.isOffline(context)) {
+ getMostRecentCount();
+ }
+ }
+
+ private void setActiveServer(int instance) {
+ if (Util.getActiveServer(context) != instance) {
+ final DownloadService service = getDownloadService();
+ if (service != null) {
+ new SilentBackgroundTask<Void>(context) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ service.clearIncomplete();
+ return null;
+ }
+ }.execute();
+
+ }
+ Util.setActiveServer(context, instance);
+ context.invalidate();
+ UserUtil.refreshCurrentUser(context, false, true);
+ }
+ }
+
+ private void toggleOffline() {
+ boolean isOffline = Util.isOffline(context);
+ Util.setOffline(context, !isOffline);
+ context.invalidate();
+ DownloadService service = getDownloadService();
+ if (service != null) {
+ service.setOnline(isOffline);
+ }
+
+ // Coming back online
+ if(isOffline) {
+ int scrobblesCount = Util.offlineScrobblesCount(context);
+ int starsCount = Util.offlineStarsCount(context);
+ if(scrobblesCount > 0 || starsCount > 0){
+ showOfflineSyncDialog(scrobblesCount, starsCount);
+ }
+ }
+
+ UserUtil.seedCurrentUser(context);
+ }
+
+ private void showAlbumList(String type) {
+ if("genres".equals(type)) {
+ SubsonicFragment fragment = new SelectGenreFragment();
+ replaceFragment(fragment);
+ } else if("years".equals(type)) {
+ SubsonicFragment fragment = new SelectYearFragment();
+ replaceFragment(fragment);
+ } else {
+ // Clear out recently added count when viewing
+ if("newest".equals(type)) {
+ SharedPreferences.Editor editor = Util.getPreferences(context).edit();
+ editor.putInt(Constants.PREFERENCES_KEY_RECENT_COUNT + Util.getActiveServer(context), 0);
+ editor.commit();
+
+ // Clear immediately so doesn't still show when pressing back
+ setMostRecentCount(0);
+ }
+
+ SubsonicFragment fragment = new SelectDirectoryFragment();
+ Bundle args = new Bundle();
+ args.putString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE, type);
+ args.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 20);
+ args.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0);
+ fragment.setArguments(args);
+
+ replaceFragment(fragment);
+ }
+ }
+ private void showVideos() {
+ SubsonicFragment fragment = new SelectVideoFragment();
+ replaceFragment(fragment);
+ }
+
+ private void showOfflineSyncDialog(final int scrobbleCount, final int starsCount) {
+ String syncDefault = Util.getSyncDefault(context);
+ if(syncDefault != null) {
+ if("sync".equals(syncDefault)) {
+ syncOffline(scrobbleCount, starsCount);
+ return;
+ } else if("delete".equals(syncDefault)) {
+ deleteOffline();
+ return;
+ }
+ }
+
+ View checkBoxView = context.getLayoutInflater().inflate(R.layout.sync_dialog, null);
+ final CheckBox checkBox = (CheckBox)checkBoxView.findViewById(R.id.sync_default);
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setIcon(android.R.drawable.ic_dialog_info)
+ .setTitle(R.string.offline_sync_dialog_title)
+ .setMessage(context.getResources().getString(R.string.offline_sync_dialog_message, scrobbleCount, starsCount))
+ .setView(checkBoxView)
+ .setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialogInterface, int i) {
+ if(checkBox.isChecked()) {
+ Util.setSyncDefault(context, "sync");
+ }
+ syncOffline(scrobbleCount, starsCount);
+ }
+ }).setNeutralButton(R.string.common_cancel, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialogInterface, int i) {
+ dialogInterface.dismiss();
+ }
+ }).setNegativeButton(R.string.common_delete, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialogInterface, int i) {
+ if(checkBox.isChecked()) {
+ Util.setSyncDefault(context, "delete");
+ }
+ deleteOffline();
+ }
+ });
+
+ builder.create().show();
+ }
+
+ private void syncOffline(final int scrobbleCount, final int starsCount) {
+ new SilentBackgroundTask<Integer>(context) {
+ @Override
+ protected Integer doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ return musicService.processOfflineSyncs(context, null);
+ }
+
+ @Override
+ protected void done(Integer result) {
+ if(result == scrobbleCount) {
+ Util.toast(context, context.getResources().getString(R.string.offline_sync_success, result));
+ } else {
+ Util.toast(context, context.getResources().getString(R.string.offline_sync_partial, result, scrobbleCount + starsCount));
+ }
+ }
+
+ @Override
+ protected void error(Throwable error) {
+ Log.w(TAG, "Failed to sync offline stats", error);
+ String msg = context.getResources().getString(R.string.offline_sync_error) + " " + getErrorMessage(error);
+ Util.toast(context, msg);
+ }
+ }.execute();
+ }
+ private void deleteOffline() {
+ SharedPreferences.Editor offline = Util.getOfflineSync(context).edit();
+ offline.putInt(Constants.OFFLINE_SCROBBLE_COUNT, 0);
+ offline.putInt(Constants.OFFLINE_STAR_COUNT, 0);
+ offline.commit();
+ }
+
+ private void showAboutDialog() {
+ new LoadingTask<String>(context) {
+ @Override
+ protected String doInBackground() throws Throwable {
+ File rootFolder = FileUtil.getMusicDirectory(context);
+ StatFs stat = new StatFs(rootFolder.getPath());
+ long bytesTotalFs = (long) stat.getBlockCount() * (long) stat.getBlockSize();
+ long bytesAvailableFs = (long) stat.getAvailableBlocks() * (long) stat.getBlockSize();
+
+ Pair<Long, Long> used = FileUtil.getUsedSize(context, rootFolder);
+
+ return getResources().getString(R.string.main_about_text,
+ context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName,
+ used.getFirst(),
+ Util.formatLocalizedBytes(used.getSecond(), context),
+ Util.formatLocalizedBytes(Util.getCacheSizeMB(context) * 1024L * 1024L, context),
+ Util.formatLocalizedBytes(bytesAvailableFs, context),
+ Util.formatLocalizedBytes(bytesTotalFs, context));
+ }
+
+ @Override
+ protected void done(String msg) {
+ try {
+ Util.info(context, R.string.main_about_title, msg);
+ } catch(Exception e) {
+ Util.toast(context, "Failed to open dialog");
+ }
+ }
+ }.execute();
+ }
+
+ private void showFAQDialog() {
+ Util.showHTMLDialog(context, R.string.main_faq_title, R.string.main_faq_text);
+ }
+
+ private void rescanServer() {
+ new LoadingTask<Void>(context, false) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ musicService.startRescan(context, this);
+ return null;
+ }
+
+ @Override
+ protected void done(Void value) {
+ Util.toast(context, R.string.main_scan_complete);
+ }
+ }.execute();
+ }
+
+ private void getLogs() {
+ try {
+ final String version = context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName;
+ new LoadingTask<File>(context) {
+ @Override
+ protected File doInBackground() throws Throwable {
+ updateProgress("Gathering Logs");
+ File logcat = new File(FileUtil.getSubsonicDirectory(context), "logcat.txt");
+ Util.delete(logcat);
+ Process logcatProc = null;
+
+ try {
+ List<String> progs = new ArrayList<String>();
+ progs.add("logcat");
+ progs.add("-v");
+ progs.add("time");
+ progs.add("-d");
+ progs.add("-f");
+ progs.add(logcat.getCanonicalPath());
+ progs.add("*:I");
+
+ logcatProc = Runtime.getRuntime().exec(progs.toArray(new String[progs.size()]));
+ logcatProc.waitFor();
+ } catch(Exception e) {
+ Util.toast(context, "Failed to gather logs");
+ } finally {
+ if(logcatProc != null) {
+ logcatProc.destroy();
+ }
+ }
+
+ return logcat;
+ }
+
+ @Override
+ protected void done(File logcat) {
+ String footer = "Android SDK: " + Build.VERSION.SDK;
+ footer += "\nDevice Model: " + Build.MODEL;
+ footer += "\nDevice Name: " + Build.MANUFACTURER + " " + Build.PRODUCT;
+ footer += "\nROM: " + Build.DISPLAY;
+
+ Intent email = new Intent(Intent.ACTION_SENDTO,
+ Uri.fromParts("mailto", "dsub.android@gmail.com", null));
+ email.putExtra(Intent.EXTRA_SUBJECT, "DSub " + version + " Error Logs");
+ email.putExtra(Intent.EXTRA_TEXT, "Describe the problem here\n\n\n" + footer);
+ Uri attachment = Uri.fromFile(logcat);
+ email.putExtra(Intent.EXTRA_STREAM, attachment);
+ startActivity(email);
+ }
+ }.execute();
+ } catch(Exception e) {}
+ }
+
+ private void getMostRecentCount() {
+ // Use stashed value until after refresh occurs
+ SharedPreferences prefs = Util.getPreferences(context);
+ final int startCount = prefs.getInt(Constants.PREFERENCES_KEY_RECENT_COUNT + Util.getActiveServer(context), 0);
+ setMostRecentCount(startCount);
+
+ new SilentBackgroundTask<Integer>(context) {
+ @Override
+ public Integer doInBackground() throws Exception {
+ String recentAddedFile = Util.getCacheName(context, "recent_count");
+ ArrayList<String> recents = FileUtil.deserialize(context, recentAddedFile, ArrayList.class);
+ if(recents == null) {
+ recents = new ArrayList<String>();
+ }
+
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ MusicDirectory recentlyAdded = musicService.getAlbumList("newest", 20, 0, context, null);
+
+ // If first run, just put everything in it and return 0
+ boolean firstRun = recents.isEmpty();
+
+ // Count how many new albums are in the list
+ int count = 0;
+ for(MusicDirectory.Entry album: recentlyAdded.getChildren()) {
+ if(!recents.contains(album.getId())) {
+ recents.add(album.getId());
+ count++;
+ }
+ }
+
+ // Keep recents list from growing infinitely
+ while(recents.size() > 40) {
+ recents.remove(0);
+ }
+ FileUtil.serialize(context, recents, recentAddedFile);
+
+ if(firstRun) {
+ return 0;
+ } else {
+ // Add the old count which will get cleared out after viewing recents
+ count += startCount;
+ SharedPreferences.Editor editor = Util.getPreferences(context).edit();
+ editor.putInt(Constants.PREFERENCES_KEY_RECENT_COUNT + Util.getActiveServer(context), count);
+ editor.commit();
+
+ return count;
+ }
+ }
+
+ @Override
+ public void done(Integer result) {
+ setMostRecentCount(result);
+ }
+
+ @Override
+ public void error(Throwable x) {
+ Log.w(TAG, "Failed to refresh most recent count", x);
+ }
+ }.execute();
+ }
+
+ private void setMostRecentCount(int count) {
+ if(count <= 0) {
+ countView.setVisibility(View.GONE);
+ } else {
+ String displayValue;
+ if(count < 10) {
+ displayValue = "0" + count;
+ } else {
+ displayValue = "" + count;
+ }
+
+ countView.setText(displayValue);
+ countView.setVisibility(View.VISIBLE);
+ }
+ }
+}
diff --git a/src/github/daneren2005/dsub/fragments/NowPlayingFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/NowPlayingFragment.java
index 4a720ead..fa7e3404 100644
--- a/src/github/daneren2005/dsub/fragments/NowPlayingFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/NowPlayingFragment.java
@@ -1,1568 +1,1568 @@
-/*
- 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 2014 (C) Scott Jackson
-*/
-package github.daneren2005.dsub.fragments;
-
-import java.util.LinkedList;
-import java.util.List;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-
-import android.app.AlertDialog;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.res.Configuration;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
-import android.support.v4.view.MenuItemCompat;
-import android.support.v7.app.MediaRouteButton;
-import android.util.Log;
-import android.view.ContextMenu;
-import android.view.Display;
-import android.view.GestureDetector;
-import android.view.GestureDetector.OnGestureListener;
-import android.view.LayoutInflater;
-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.ViewGroup;
-import android.view.WindowManager;
-import android.view.animation.AnimationUtils;
-import android.widget.AdapterView;
-import android.widget.EditText;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.SeekBar;
-import android.widget.TextView;
-import android.widget.ViewFlipper;
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.activity.SubsonicFragmentActivity;
-import github.daneren2005.dsub.audiofx.EqualizerController;
-import github.daneren2005.dsub.domain.Bookmark;
-import github.daneren2005.dsub.domain.PlayerState;
-import github.daneren2005.dsub.domain.RepeatMode;
-import github.daneren2005.dsub.domain.ServerInfo;
-import github.daneren2005.dsub.service.DownloadFile;
-import github.daneren2005.dsub.service.DownloadService;
-import github.daneren2005.dsub.service.MusicService;
-import github.daneren2005.dsub.service.MusicServiceFactory;
-import github.daneren2005.dsub.service.OfflineException;
-import github.daneren2005.dsub.service.ServerTooOldException;
-import github.daneren2005.dsub.util.Constants;
-import github.daneren2005.dsub.util.SilentBackgroundTask;
-import github.daneren2005.dsub.adapter.DownloadFileAdapter;
-import github.daneren2005.dsub.view.FadeOutAnimation;
-import github.daneren2005.dsub.view.UpdateView;
-import github.daneren2005.dsub.util.Util;
-
-import static github.daneren2005.dsub.domain.MusicDirectory.Entry;
-import static github.daneren2005.dsub.domain.PlayerState.*;
-import github.daneren2005.dsub.util.*;
-import github.daneren2005.dsub.view.AutoRepeatButton;
-import java.util.ArrayList;
-import java.util.concurrent.ScheduledFuture;
-import com.mobeta.android.dslv.*;
-import github.daneren2005.dsub.activity.SubsonicActivity;
-
-public class NowPlayingFragment extends SubsonicFragment implements OnGestureListener {
- private static final String TAG = NowPlayingFragment.class.getSimpleName();
- private static final int PERCENTAGE_OF_SCREEN_FOR_SWIPE = 10;
- private static final int INCREMENT_TIME = 5000;
- private static final int SERVICE_BACKOFF = 200;
-
- private static final int ACTION_PREVIOUS = 1;
- private static final int ACTION_NEXT = 2;
- private static final int ACTION_REWIND = 3;
- private static final int ACTION_FORWARD = 4;
-
- private ViewFlipper playlistFlipper;
- private TextView emptyTextView;
- private TextView songTitleTextView;
- private ImageView albumArtImageView;
- private DragSortListView playlistView;
- private TextView positionTextView;
- private TextView durationTextView;
- private TextView statusTextView;
- private SeekBar progressBar;
- private AutoRepeatButton previousButton;
- private AutoRepeatButton nextButton;
- private View pauseButton;
- private View stopButton;
- private View startButton;
- private ImageButton repeatButton;
- private View toggleListButton;
- private ImageButton starButton;
- private ImageButton bookmarkButton;
- private ImageButton rateBadButton;
- private ImageButton rateGoodButton;
- private View mainLayout;
- private ScheduledExecutorService executorService;
- private DownloadFile currentPlaying;
- private long currentRevision;
- private int swipeDistance;
- private int swipeVelocity;
- private ScheduledFuture<?> hideControlsFuture;
- private List<DownloadFile> songList;
- private DownloadFileAdapter songListAdapter;
- private SilentBackgroundTask<Void> onProgressChangedTask;
- private SilentBackgroundTask<Void> onCurrentChangedTask;
- private SilentBackgroundTask<Void> onDownloadListChangedTask;
- private boolean seekInProgress = false;
- private boolean startFlipped = false;
- private boolean scrollWhenLoaded = false;
- private int lastY = 0;
-
- /**
- * Called when the activity is first created.
- */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- if(savedInstanceState != null) {
- if(savedInstanceState.getInt(Constants.FRAGMENT_DOWNLOAD_FLIPPER) == 1) {
- startFlipped = true;
- }
- }
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putInt(Constants.FRAGMENT_DOWNLOAD_FLIPPER, playlistFlipper.getDisplayedChild());
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
- rootView = inflater.inflate(R.layout.download, container, false);
- setTitle(R.string.button_bar_now_playing);
-
- mainLayout = rootView.findViewById(R.id.download_layout);
- if(!primaryFragment) {
- mainLayout.setVisibility(View.GONE);
- }
-
- WindowManager w = context.getWindowManager();
- Display d = w.getDefaultDisplay();
- swipeDistance = (d.getWidth() + d.getHeight()) * PERCENTAGE_OF_SCREEN_FOR_SWIPE / 100;
- swipeVelocity = (d.getWidth() + d.getHeight()) * PERCENTAGE_OF_SCREEN_FOR_SWIPE / 100;
- gestureScanner = new GestureDetector(this);
-
- playlistFlipper = (ViewFlipper)rootView.findViewById(R.id.download_playlist_flipper);
- emptyTextView = (TextView)rootView.findViewById(R.id.download_empty);
- songTitleTextView = (TextView)rootView.findViewById(R.id.download_song_title);
- albumArtImageView = (ImageView)rootView.findViewById(R.id.download_album_art_image);
- positionTextView = (TextView)rootView.findViewById(R.id.download_position);
- durationTextView = (TextView)rootView.findViewById(R.id.download_duration);
- statusTextView = (TextView)rootView.findViewById(R.id.download_status);
- progressBar = (SeekBar)rootView.findViewById(R.id.download_progress_bar);
- playlistView = (DragSortListView)rootView.findViewById(R.id.download_list);
- previousButton = (AutoRepeatButton)rootView.findViewById(R.id.download_previous);
- nextButton = (AutoRepeatButton)rootView.findViewById(R.id.download_next);
- pauseButton =rootView.findViewById(R.id.download_pause);
- stopButton =rootView.findViewById(R.id.download_stop);
- startButton =rootView.findViewById(R.id.download_start);
- repeatButton = (ImageButton)rootView.findViewById(R.id.download_repeat);
- bookmarkButton = (ImageButton) rootView.findViewById(R.id.download_bookmark);
- rateBadButton = (ImageButton) rootView.findViewById(R.id.download_rating_bad);
- rateGoodButton = (ImageButton) rootView.findViewById(R.id.download_rating_good);
- toggleListButton =rootView.findViewById(R.id.download_toggle_list);
-
- starButton = (ImageButton)rootView.findViewById(R.id.download_star);
- if(Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_MENU_STAR, true)) {
- starButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- DownloadFile currentDownload = getDownloadService().getCurrentPlaying();
- if (currentDownload != null) {
- final Entry currentSong = currentDownload.getSong();
- toggleStarred(currentSong, new OnStarChange() {
- @Override
- void starChange(boolean starred) {
- starButton.setImageResource(currentSong.isStarred() ? android.R.drawable.btn_star_big_on : android.R.drawable.btn_star_big_off);
- }
- });
- }
- }
- });
- } else {
- starButton.setVisibility(View.GONE);
- }
-
- View.OnTouchListener touchListener = new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent me) {
- return gestureScanner.onTouchEvent(me);
- }
- };
- pauseButton.setOnTouchListener(touchListener);
- stopButton.setOnTouchListener(touchListener);
- startButton.setOnTouchListener(touchListener);
- bookmarkButton.setOnTouchListener(touchListener);
- rateBadButton.setOnTouchListener(touchListener);
- rateGoodButton.setOnTouchListener(touchListener);
- emptyTextView.setOnTouchListener(touchListener);
- albumArtImageView.setOnTouchListener(new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent me) {
- if(me.getAction() == MotionEvent.ACTION_DOWN) {
- lastY = (int) me.getRawY();
- }
- return gestureScanner.onTouchEvent(me);
- }
- });
-
- previousButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- warnIfStorageUnavailable();
- new SilentBackgroundTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- getDownloadService().previous();
- return null;
- }
-
- @Override
- protected void done(Void result) {
- onCurrentChanged();
- onProgressChanged();
- }
- }.execute();
- setControlsVisible(true);
- }
- });
- previousButton.setOnRepeatListener(new Runnable() {
- public void run() {
- changeProgress(-INCREMENT_TIME);
- }
- });
-
- nextButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- warnIfStorageUnavailable();
- new SilentBackgroundTask<Boolean>(context) {
- @Override
- protected Boolean doInBackground() throws Throwable {
- getDownloadService().next();
- return true;
- }
-
- @Override
- protected void done(Boolean result) {
- if(result) {
- onCurrentChanged();
- onProgressChanged();
- }
- }
- }.execute();
- setControlsVisible(true);
- }
- });
- nextButton.setOnRepeatListener(new Runnable() {
- public void run() {
- changeProgress(INCREMENT_TIME);
- }
- });
-
- pauseButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- new SilentBackgroundTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- getDownloadService().pause();
- return null;
- }
-
- @Override
- protected void done(Void result) {
- onCurrentChanged();
- onProgressChanged();
- }
- }.execute();
- }
- });
-
- stopButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- new SilentBackgroundTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- getDownloadService().reset();
- return null;
- }
-
- @Override
- protected void done(Void result) {
- onCurrentChanged();
- onProgressChanged();
- }
- }.execute();
- }
- });
-
- startButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- warnIfStorageUnavailable();
- new SilentBackgroundTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- start();
- return null;
- }
-
- @Override
- protected void done(Void result) {
- onCurrentChanged();
- onProgressChanged();
- }
- }.execute();
- }
- });
-
- repeatButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- RepeatMode repeatMode = getDownloadService().getRepeatMode().next();
- getDownloadService().setRepeatMode(repeatMode);
- onDownloadListChanged();
- switch (repeatMode) {
- case OFF:
- Util.toast(context, R.string.download_repeat_off);
- break;
- case ALL:
- Util.toast(context, R.string.download_repeat_all);
- break;
- case SINGLE:
- Util.toast(context, R.string.download_repeat_single);
- break;
- default:
- break;
- }
- setControlsVisible(true);
- }
- });
-
- bookmarkButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- createBookmark();
- }
- });
-
- rateBadButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- DownloadService downloadService = getDownloadService();
- if(downloadService == null) {
- return;
- }
-
- DownloadFile downloadFile = downloadService.getCurrentPlaying();
- if(downloadFile == null) {
- return;
- }
- Entry entry = downloadFile.getSong();
-
- // If rating == 1, already set so unset
- if(entry.getRating() == 1) {
- setRating(entry, 0);
-
- if(context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
- rateBadButton.setImageResource(R.drawable.ic_action_rating_bad_dark);
- } else {
- rateBadButton.setImageResource(Util.getAttribute(context, R.attr.rating_bad));
- }
- } else {
- // Immediately skip to the next song
- downloadService.next(true);
-
- // Otherwise set rating to 1
- setRating(entry, 1);
- rateBadButton.setImageResource(R.drawable.ic_action_rating_bad_selected);
-
- // Make sure good rating is blank
- if (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
- rateGoodButton.setImageResource(R.drawable.ic_action_rating_good_dark);
- } else {
- rateGoodButton.setImageResource(Util.getAttribute(context, R.attr.rating_good));
- }
- }
- }
- });
- rateGoodButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- DownloadService downloadService = getDownloadService();
- if(downloadService == null) {
- return;
- }
-
- DownloadFile downloadFile = downloadService.getCurrentPlaying();
- if(downloadFile == null) {
- return;
- }
- Entry entry = downloadFile.getSong();
-
- // If rating == 5, already set so unset
- if(entry.getRating() == 5) {
- setRating(entry, 0);
-
- if (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
- rateGoodButton.setImageResource(R.drawable.ic_action_rating_good_dark);
- } else {
- rateGoodButton.setImageResource(Util.getAttribute(context, R.attr.rating_good));
- }
- } else {
- // Otherwise set rating to maximum
- setRating(entry, 5);
- rateGoodButton.setImageResource(R.drawable.ic_action_rating_good_selected);
-
- // Make sure bad rating is blank
- if (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
- rateBadButton.setImageResource(R.drawable.ic_action_rating_bad_dark);
- } else {
- rateBadButton.setImageResource(Util.getAttribute(context, R.attr.rating_bad));
- }
- }
- }
- });
-
- toggleListButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- toggleFullscreenAlbumArt();
- setControlsVisible(true);
- }
- });
-
- View overlay = rootView.findViewById(R.id.download_overlay_buttons);
- final int overlayHeight = overlay != null ? overlay.getHeight() : -1;
- albumArtImageView.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if(overlayHeight == -1 || lastY < (view.getBottom() - overlayHeight)) {
- toggleFullscreenAlbumArt();
- setControlsVisible(true);
- }
- }
- });
-
- progressBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
- @Override
- public void onStopTrackingTouch(final SeekBar seekBar) {
- new SilentBackgroundTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- getDownloadService().seekTo(progressBar.getProgress());
- return null;
- }
-
- @Override
- protected void done(Void result) {
- seekInProgress = false;
- NowPlayingFragment.this.onProgressChanged();
- }
- }.execute();
- }
-
- @Override
- public void onStartTrackingTouch(final SeekBar seekBar) {
- seekInProgress = true;
- }
-
- @Override
- public void onProgressChanged(final SeekBar seekBar, final int position, final boolean fromUser) {
- if (fromUser) {
- Util.toast(context, Util.formatDuration(position / 1000), true);
- setControlsVisible(true);
- }
- }
- });
- playlistView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, final int position, long id) {
- warnIfStorageUnavailable();
- new SilentBackgroundTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- getDownloadService().play(position);
- return null;
- }
-
- @Override
- protected void done(Void result) {
- onCurrentChanged();
- onProgressChanged();
- }
- }.execute();
- }
- });
- playlistView.setDropListener(new DragSortListView.DropListener() {
- @Override
- public void drop(final int from, final int to) {
- new SilentBackgroundTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- getDownloadService().swap(true, from, to);
- onDownloadListChanged();
-
- return null;
- }
- }.execute();
- }
- });
- playlistView.setRemoveListener(new DragSortListView.RemoveListener() {
- @Override
- public void remove(int which) {
- getDownloadService().remove(which);
- onDownloadListChanged();
- }
- });
-
- registerForContextMenu(playlistView);
-
- DownloadService downloadService = getDownloadService();
- if (downloadService != null && context.getIntent().getBooleanExtra(Constants.INTENT_EXTRA_NAME_SHUFFLE, false)) {
- context.getIntent().removeExtra(Constants.INTENT_EXTRA_NAME_SHUFFLE);
- warnIfStorageUnavailable();
- downloadService.setShufflePlayEnabled(true);
- }
-
- if(Build.MODEL.equals("Nexus 4") || Build.MODEL.equals("GT-I9100")) {
- View slider = rootView.findViewById(R.id.download_slider);
- slider.setPadding(0, 0, 0, 0);
- }
-
- return rootView;
- }
-
- @Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
- DownloadService downloadService = getDownloadService();
- if(Util.isOffline(context)) {
- menuInflater.inflate(R.menu.nowplaying_offline, menu);
- } else {
- menuInflater.inflate(R.menu.nowplaying, menu);
-
- if(downloadService != null && downloadService.getSleepTimer()) {
- menu.findItem(R.id.menu_toggle_timer).setTitle(R.string.download_stop_timer);
- }
- }
- if(downloadService != null && downloadService.getKeepScreenOn()) {
- menu.findItem(R.id.menu_screen_on_off).setChecked(true);
- }
- if(downloadService != null && downloadService.isRemovePlayed()) {
- menu.findItem(R.id.menu_remove_played).setChecked(true);
- }
-
- boolean equalizerAvailable = downloadService != null && downloadService.getEqualizerAvailable();
- if(equalizerAvailable && !downloadService.isRemoteEnabled()) {
- SharedPreferences prefs = Util.getPreferences(context);
- boolean equalizerOn = prefs.getBoolean(Constants.PREFERENCES_EQUALIZER_ON, false);
- if (equalizerOn && getDownloadService() != null && getDownloadService().getEqualizerController() != null &&
- getDownloadService().getEqualizerController().isEnabled()) {
- menu.findItem(R.id.menu_equalizer).setChecked(true);
- }
- } else {
- menu.removeItem(R.id.menu_equalizer);
- }
-
- if(downloadService != null) {
- MenuItem mediaRouteItem = menu.findItem(R.id.menu_mediaroute);
- if(mediaRouteItem != null) {
- MediaRouteButton mediaRouteButton = (MediaRouteButton) MenuItemCompat.getActionView(mediaRouteItem);
- mediaRouteButton.setRouteSelector(downloadService.getRemoteSelector());
- }
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem menuItem) {
- if(menuItemSelected(menuItem.getItemId(), null)) {
- return true;
- }
-
- return super.onOptionsItemSelected(menuItem);
- }
-
- @Override
- public void onCreateContextMenu(android.view.ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, view, menuInfo);
- if(!primaryFragment) {
- return;
- }
-
- if (view == playlistView) {
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
- DownloadFile downloadFile = (DownloadFile) playlistView.getItemAtPosition(info.position);
-
- android.view.MenuInflater inflater = context.getMenuInflater();
- if(Util.isOffline(context)) {
- inflater.inflate(R.menu.nowplaying_context_offline, menu);
- } else {
- inflater.inflate(R.menu.nowplaying_context, menu);
- menu.findItem(R.id.menu_star).setTitle(downloadFile.getSong().isStarred() ? R.string.common_unstar : R.string.common_star);
- }
-
- if (downloadFile.getSong().getParent() == null) {
- menu.findItem(R.id.menu_show_album).setVisible(false);
- menu.findItem(R.id.menu_show_artist).setVisible(false);
- }
-
- hideMenuItems(menu, (AdapterView.AdapterContextMenuInfo) menuInfo);
- }
- }
-
- @Override
- public boolean onContextItemSelected(android.view.MenuItem menuItem) {
- if(!primaryFragment) {
- return false;
- }
-
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
- DownloadFile downloadFile = (DownloadFile) playlistView.getItemAtPosition(info.position);
- return menuItemSelected(menuItem.getItemId(), downloadFile) || super.onContextItemSelected(menuItem);
- }
-
- private boolean menuItemSelected(int menuItemId, final DownloadFile song) {
- switch (menuItemId) {
- case R.id.menu_show_album: case R.id.menu_show_artist:
- Entry entry = song.getSong();
-
- Intent intent = new Intent(context, SubsonicFragmentActivity.class);
- intent.putExtra(Constants.INTENT_EXTRA_VIEW_ALBUM, true);
- String albumId;
- String albumName;
- if(menuItemId == R.id.menu_show_album) {
- if(Util.isTagBrowsing(context)) {
- albumId = entry.getAlbumId();
- } else {
- albumId = entry.getParent();
- }
- albumName = entry.getAlbum();
- } else {
- if(Util.isTagBrowsing(context)) {
- albumId = entry.getArtistId();
- } else {
- albumId = entry.getGrandParent();
- if(albumId == null) {
- intent.putExtra(Constants.INTENT_EXTRA_NAME_CHILD_ID, entry.getParent());
- }
- }
- albumName = entry.getArtist();
- intent.putExtra(Constants.INTENT_EXTRA_NAME_ARTIST, true);
- }
- intent.putExtra(Constants.INTENT_EXTRA_NAME_ID, albumId);
- intent.putExtra(Constants.INTENT_EXTRA_NAME_NAME, albumName);
- intent.putExtra(Constants.INTENT_EXTRA_FRAGMENT_TYPE, "Artist");
-
- if(Util.isOffline(context)) {
- try {
- // This should only be successful if this is a online song in offline mode
- Integer.parseInt(entry.getParent());
- String root = FileUtil.getMusicDirectory(context).getPath();
- String id = root + "/" + entry.getPath();
- id = id.substring(0, id.lastIndexOf("/"));
- if(menuItemId == R.id.menu_show_album) {
- intent.putExtra(Constants.INTENT_EXTRA_NAME_ID, id);
- }
- id = id.substring(0, id.lastIndexOf("/"));
- if(menuItemId != R.id.menu_show_album) {
- intent.putExtra(Constants.INTENT_EXTRA_NAME_ID, id);
- intent.putExtra(Constants.INTENT_EXTRA_NAME_NAME, entry.getArtist());
- intent.removeExtra(Constants.INTENT_EXTRA_NAME_CHILD_ID);
- }
- } catch(Exception e) {
- // Do nothing, entry.getParent() is fine
- }
- }
-
- intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- Util.startActivityWithoutTransition(context, intent);
- return true;
- case R.id.menu_lyrics: {
- SubsonicFragment fragment = new LyricsFragment();
- Bundle args = new Bundle();
- args.putString(Constants.INTENT_EXTRA_NAME_ARTIST, song.getSong().getArtist());
- args.putString(Constants.INTENT_EXTRA_NAME_TITLE, song.getSong().getTitle());
- fragment.setArguments(args);
-
- replaceFragment(fragment);
- return true;
- } case R.id.menu_remove:
- new SilentBackgroundTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- getDownloadService().remove(song);
- return null;
- }
-
- @Override
- protected void done(Void result) {
- onDownloadListChanged();
- }
- }.execute();
- return true;
- case R.id.menu_delete:
- List<Entry> songs = new ArrayList<Entry>(1);
- songs.add(song.getSong());
- getDownloadService().delete(songs);
- return true;
- case R.id.menu_remove_all:
- Util.confirmDialog(context, R.string.download_menu_remove_all, "", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- new SilentBackgroundTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- getDownloadService().setShufflePlayEnabled(false);
- getDownloadService().clear();
- return null;
- }
-
- @Override
- protected void done(Void result) {
- onDownloadListChanged();
- }
- }.execute();
- }
- });
- return true;
- case R.id.menu_screen_on_off:
- if (getDownloadService().getKeepScreenOn()) {
- context.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
- getDownloadService().setKeepScreenOn(false);
- } else {
- context.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
- getDownloadService().setKeepScreenOn(true);
- }
- context.supportInvalidateOptionsMenu();
- return true;
- case R.id.menu_remove_played:
- if (getDownloadService().isRemovePlayed()) {
- getDownloadService().setRemovePlayed(false);
- } else {
- getDownloadService().setRemovePlayed(true);
- }
- context.supportInvalidateOptionsMenu();
- return true;
- case R.id.menu_shuffle:
- new SilentBackgroundTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- getDownloadService().shuffle();
- return null;
- }
-
- @Override
- protected void done(Void result) {
- Util.toast(context, R.string.download_menu_shuffle_notification);
- }
- }.execute();
- return true;
- case R.id.menu_save_playlist:
- List<Entry> entries = new LinkedList<Entry>();
- for (DownloadFile downloadFile : getDownloadService().getSongs()) {
- entries.add(downloadFile.getSong());
- }
- createNewPlaylist(entries, true);
- return true;
- case R.id.menu_star:
- toggleStarred(song.getSong());
- return true;
- case R.id.menu_rate:
- setRating(song.getSong());
- return true;
- case R.id.menu_toggle_timer:
- if(getDownloadService().getSleepTimer()) {
- getDownloadService().stopSleepTimer();
- context.supportInvalidateOptionsMenu();
- } else {
- startTimer();
- }
- return true;
- case R.id.menu_add_playlist:
- songs = new ArrayList<Entry>(1);
- songs.add(song.getSong());
- addToPlaylist(songs);
- return true;
- case R.id.menu_info:
- displaySongInfo(song.getSong());
- return true;
- case R.id.menu_share:
- songs = new ArrayList<Entry>(1);
- songs.add(song.getSong());
- createShare(songs);
- return true;
- case R.id.menu_equalizer: {
- DownloadService downloadService = getDownloadService();
- if (downloadService != null) {
- EqualizerController controller = downloadService.getEqualizerController();
- if(controller != null) {
- SubsonicFragment fragment = new EqualizerFragment();
- replaceFragment(fragment);
- setControlsVisible(true);
-
- return true;
- }
- }
-
- // Any failed condition will get here
- Util.toast(context, "Failed to start equalizer. Try restarting.");
- return true;
- } default:
- return false;
- }
- }
-
- @Override
- public void onResume() {
- super.onResume();
-
- final Handler handler = new Handler();
- Runnable runnable = new Runnable() {
- @Override
- public void run() {
- handler.post(new Runnable() {
- @Override
- public void run() {
- update();
- }
- });
- }
- };
-
- executorService = Executors.newSingleThreadScheduledExecutor();
- executorService.scheduleWithFixedDelay(runnable, 0L, 1000L, TimeUnit.MILLISECONDS);
-
- setControlsVisible(true);
-
- DownloadService downloadService = getDownloadService();
- if (downloadService == null || downloadService.getCurrentPlaying() == null || startFlipped) {
- playlistFlipper.setDisplayedChild(1);
- }
- if (downloadService != null && downloadService.getKeepScreenOn()) {
- context.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
- } else {
- context.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
- }
-
- updateButtons();
-
- if(currentPlaying == null && downloadService != null && currentPlaying == downloadService.getCurrentPlaying()) {
- getImageLoader().loadImage(albumArtImageView, (Entry) null, true, false);
- }
- if(downloadService != null) {
- downloadService.startRemoteScan();
- } else {
- // Make sure to call remote scan once the service is ready
- final Runnable waitForService = new Runnable() {
- @Override
- public void run() {
- DownloadService service = getDownloadService();
- if(service != null) {
- service.startRemoteScan();
- } else {
- handler.postDelayed(this, SERVICE_BACKOFF);
- }
- }
- };
-
- handler.postDelayed(waitForService, SERVICE_BACKOFF);
- }
- }
-
- @Override
- public void onPause() {
- super.onPause();
- executorService.shutdown();
- if(getDownloadService() != null) {
- getDownloadService().stopRemoteScan();
- }
- }
-
- @Override
- public void setPrimaryFragment(boolean primary) {
- super.setPrimaryFragment(primary);
- if(rootView != null) {
- if(primary) {
- mainLayout.setVisibility(View.VISIBLE);
- updateButtons();
- } else {
- mainLayout.setVisibility(View.GONE);
- }
- }
- }
-
- private void scheduleHideControls() {
- if (hideControlsFuture != null) {
- hideControlsFuture.cancel(false);
- }
-
- final Handler handler = new Handler();
- Runnable runnable = new Runnable() {
- @Override
- public void run() {
- handler.post(new Runnable() {
- @Override
- public void run() {
- setControlsVisible(false);
- }
- });
- }
- };
- hideControlsFuture = executorService.schedule(runnable, 3000L, TimeUnit.MILLISECONDS);
- }
-
- private void setControlsVisible(boolean visible) {
- try {
- long duration = 1700L;
- FadeOutAnimation.createAndStart(rootView.findViewById(R.id.download_overlay_buttons), !visible, duration);
-
- if (visible) {
- scheduleHideControls();
- }
- } catch(Exception e) {
-
- }
- }
-
- private void updateButtons() {
- if(context == null) {
- return;
- }
-
- if(Util.isOffline(context)) {
- bookmarkButton.setVisibility(View.GONE);
- rateBadButton.setVisibility(View.GONE);
- rateGoodButton.setVisibility(View.GONE);
- } else {
- if(ServerInfo.canBookmark(context)) {
- bookmarkButton.setVisibility(View.VISIBLE);
- } else {
- bookmarkButton.setVisibility(View.GONE);
- }
- rateBadButton.setVisibility(View.VISIBLE);
- rateGoodButton.setVisibility(View.VISIBLE);
- }
- }
-
- // Scroll to current playing/downloading.
- private void scrollToCurrent() {
- if (getDownloadService() == null || songListAdapter == null) {
- scrollWhenLoaded = true;
- return;
- }
-
- for (int i = 0; i < songListAdapter.getCount(); i++) {
- if (currentPlaying == playlistView.getItemAtPosition(i)) {
- playlistView.setSelectionFromTop(i, 40);
- return;
- }
- }
- DownloadFile currentDownloading = getDownloadService().getCurrentDownloading();
- for (int i = 0; i < songListAdapter.getCount(); i++) {
- if (currentDownloading == playlistView.getItemAtPosition(i)) {
- playlistView.setSelectionFromTop(i, 40);
- return;
- }
- }
- }
-
- private void update() {
- if (getDownloadService() == null) {
- return;
- }
-
- if (currentRevision != getDownloadService().getDownloadListUpdateRevision()) {
- onDownloadListChanged();
- }
-
- if (currentPlaying != getDownloadService().getCurrentPlaying()) {
- onCurrentChanged();
- }
-
- if(startFlipped) {
- startFlipped = false;
- scrollToCurrent();
- }
-
- onProgressChanged();
- }
-
- protected void startTimer() {
- View dialogView = context.getLayoutInflater().inflate(R.layout.start_timer, null);
-
- // Setup length label
- final TextView lengthBox = (TextView) dialogView.findViewById(R.id.timer_length_label);
- final SharedPreferences prefs = Util.getPreferences(context);
- String lengthString = prefs.getString(Constants.PREFERENCES_KEY_SLEEP_TIMER_DURATION, "5");
- int length = Integer.parseInt(lengthString);
- lengthBox.setText(Util.formatDuration(length));
-
- // Setup length slider
- final SeekBar lengthBar = (SeekBar) dialogView.findViewById(R.id.timer_length_bar);
- lengthBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
- @Override
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- if (fromUser) {
- int length = getMinutes(progress);
- lengthBox.setText(Util.formatDuration(length));
- seekBar.setProgress(progress);
- }
- }
-
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {
- }
-
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {
- }
- });
- lengthBar.setProgress(length - 1);
-
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
- builder.setTitle(R.string.menu_set_timer)
- .setView(dialogView)
- .setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int id) {
- int length = getMinutes(lengthBar.getProgress());
-
- SharedPreferences.Editor editor = prefs.edit();
- editor.putString(Constants.PREFERENCES_KEY_SLEEP_TIMER_DURATION, Integer.toString(length));
- editor.commit();
-
- getDownloadService().setSleepTimerDuration(length);
- getDownloadService().startSleepTimer();
- context.supportInvalidateOptionsMenu();
- }
- })
- .setNegativeButton(R.string.common_cancel, null);
- AlertDialog dialog = builder.create();
- dialog.show();
- }
-
- private int getMinutes(int progress) {
- if(progress < 30) {
- return progress + 1;
- } else if(progress < 61) {
- return (progress - 30) * 5 + getMinutes(29);
- } else {
- return (progress - 61) * 15 + getMinutes(60);
- }
- }
-
- private void toggleFullscreenAlbumArt() {
- if (playlistFlipper.getDisplayedChild() == 1) {
- playlistFlipper.setInAnimation(AnimationUtils.loadAnimation(context, R.anim.push_down_in));
- playlistFlipper.setOutAnimation(AnimationUtils.loadAnimation(context, R.anim.push_down_out));
- playlistFlipper.setDisplayedChild(0);
- } else {
- scrollToCurrent();
- playlistFlipper.setInAnimation(AnimationUtils.loadAnimation(context, R.anim.push_up_in));
- playlistFlipper.setOutAnimation(AnimationUtils.loadAnimation(context, R.anim.push_up_out));
- playlistFlipper.setDisplayedChild(1);
-
- UpdateView.triggerUpdate();
- }
- }
-
- private void start() {
- DownloadService service = getDownloadService();
- PlayerState state = service.getPlayerState();
- if (state == PAUSED || state == COMPLETED || state == STOPPED) {
- service.start();
- } else if (state == STOPPED || state == IDLE) {
- warnIfStorageUnavailable();
- int current = service.getCurrentPlayingIndex();
- // TODO: Use play() method.
- if (current == -1) {
- service.play(0);
- } else {
- service.play(current);
- }
- }
- }
- private void onDownloadListChanged() {
- onDownloadListChanged(false);
- }
- private void onDownloadListChanged(final boolean refresh) {
- final DownloadService downloadService = getDownloadService();
- if (downloadService == null || onDownloadListChangedTask != null) {
- return;
- }
-
- onDownloadListChangedTask = new SilentBackgroundTask<Void>(context) {
- int currentPlayingIndex;
- int size;
-
- @Override
- protected Void doInBackground() throws Throwable {
- currentPlayingIndex = downloadService.getCurrentPlayingIndex() + 1;
- size = downloadService.size();
-
- return null;
- }
-
- @Override
- protected void done(Void result) {
- List<DownloadFile> list;
- list = downloadService.getSongs();
-
- if(downloadService.isShufflePlayEnabled()) {
- emptyTextView.setText(R.string.download_shuffle_loading);
- }
- else {
- emptyTextView.setText(R.string.download_empty);
- }
-
- if(songListAdapter == null || refresh) {
- songList = new ArrayList<DownloadFile>();
- songList.addAll(list);
- playlistView.setAdapter(songListAdapter = new DownloadFileAdapter(context, songList));
- } else {
- songList.clear();
- songList.addAll(list);
- songListAdapter.notifyDataSetChanged();
- }
-
- emptyTextView.setVisibility(list.isEmpty() ? View.VISIBLE : View.GONE);
- currentRevision = downloadService.getDownloadListUpdateRevision();
-
- switch (downloadService.getRepeatMode()) {
- case OFF:
- if("light".equals(SubsonicActivity.getThemeName()) | "light_fullscreen".equals(SubsonicActivity.getThemeName())) {
- repeatButton.setImageResource(R.drawable.media_repeat_off_light);
- } else {
- repeatButton.setImageResource(R.drawable.media_repeat_off);
- }
- break;
- case ALL:
- repeatButton.setImageResource(R.drawable.media_repeat_all);
- break;
- case SINGLE:
- repeatButton.setImageResource(R.drawable.media_repeat_single);
- break;
- default:
- break;
- }
-
- if(scrollWhenLoaded) {
- scrollToCurrent();
- scrollWhenLoaded = false;
- }
-
- setSubtitle(context.getResources().getString(R.string.download_playing_out_of, currentPlayingIndex, size));
- onDownloadListChangedTask = null;
- if(onCurrentChangedTask != null) {
- onCurrentChangedTask.execute();
- } else if(onProgressChangedTask != null) {
- onProgressChangedTask.execute();
- }
- }
- };
- onDownloadListChangedTask.execute();
- }
-
- private void onCurrentChanged() {
- final DownloadService downloadService = getDownloadService();
- if (downloadService == null || onCurrentChangedTask != null) {
- return;
- }
-
- onCurrentChangedTask = new SilentBackgroundTask<Void>(context) {
- int currentPlayingIndex;
- int currentPlayingSize;
-
- @Override
- protected Void doInBackground() throws Throwable {
- currentPlaying = downloadService.getCurrentPlaying();
- currentPlayingIndex = downloadService.getCurrentPlayingIndex() + 1;
- currentPlayingSize = downloadService.size();
- return null;
- }
-
- @Override
- protected void done(Void result) {
- if (currentPlaying != null) {
- Entry song = currentPlaying.getSong();
- songTitleTextView.setText(song.getTitle());
- getImageLoader().loadImage(albumArtImageView, song, true, true);
- starButton.setImageResource(song.isStarred() ? android.R.drawable.btn_star_big_on : android.R.drawable.btn_star_big_off);
- setSubtitle(context.getResources().getString(R.string.download_playing_out_of, currentPlayingIndex, currentPlayingSize));
-
- int badRating, goodRating, bookmark;
- if(song.getRating() == 1) {
- badRating = R.drawable.ic_action_rating_bad_selected;
- } else if(context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
- badRating = R.drawable.ic_action_rating_bad_dark;
- } else {
- badRating = Util.getAttribute(context, R.attr.rating_bad);
- }
- rateBadButton.setImageResource(badRating);
-
- if(song.getRating() == 5) {
- goodRating = R.drawable.ic_action_rating_good_selected;
- } else if(context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
- goodRating = R.drawable.ic_action_rating_good_dark;
- } else {
- goodRating = Util.getAttribute(context, R.attr.rating_good);
- }
- rateGoodButton.setImageResource(goodRating);
-
- if(song.getBookmark() != null) {
- bookmark = R.drawable.ic_menu_bookmark_selected;
- } else if(context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
- bookmark = R.drawable.ic_menu_bookmark_dark;
- } else {
- bookmark = Util.getAttribute(context, R.attr.bookmark);
- }
- bookmarkButton.setImageResource(bookmark);
- } else {
- songTitleTextView.setText(null);
- getImageLoader().loadImage(albumArtImageView, (Entry) null, true, false);
- starButton.setImageResource(android.R.drawable.btn_star_big_off);
- setSubtitle(null);
- }
- onCurrentChangedTask = null;
- if(onProgressChangedTask != null) {
- onProgressChangedTask.execute();
- }
- }
- };
-
- if(onDownloadListChangedTask == null) {
- onCurrentChangedTask.execute();
- }
- }
-
- private void onProgressChanged() {
- // Make sure to only be trying to run one of these at a time
- if (getDownloadService() == null || onProgressChangedTask != null) {
- return;
- }
-
- onProgressChangedTask = new SilentBackgroundTask<Void>(context) {
- DownloadService downloadService;
- int millisPlayed;
- Integer duration;
- PlayerState playerState;
- boolean isSeekable;
-
- @Override
- protected Void doInBackground() throws Throwable {
- downloadService = getDownloadService();
- millisPlayed = Math.max(0, downloadService.getPlayerPosition());
- duration = downloadService.getPlayerDuration();
- playerState = getDownloadService().getPlayerState();
- isSeekable = downloadService.isSeekable();
- return null;
- }
-
- @Override
- protected void done(Void result) {
- if (currentPlaying != null) {
- int millisTotal = duration == null ? 0 : duration;
-
- positionTextView.setText(Util.formatDuration(millisPlayed / 1000));
- if(millisTotal > 0) {
- durationTextView.setText(Util.formatDuration(millisTotal / 1000));
- } else {
- durationTextView.setText("-:--");
- }
- progressBar.setMax(millisTotal == 0 ? 100 : millisTotal); // Work-around for apparent bug.
- if(!seekInProgress) {
- progressBar.setProgress(millisPlayed);
- }
- progressBar.setEnabled(isSeekable);
- } else {
- positionTextView.setText("0:00");
- durationTextView.setText("-:--");
- progressBar.setProgress(0);
- progressBar.setEnabled(false);
- }
-
- switch (playerState) {
- case DOWNLOADING:
- if(currentPlaying != null) {
- long bytes = currentPlaying.getPartialFile().length();
- statusTextView.setText(context.getResources().getString(R.string.download_playerstate_downloading, Util.formatLocalizedBytes(bytes, context)));
- }
- break;
- case PREPARING:
- statusTextView.setText(R.string.download_playerstate_buffering);
- break;
- default:
- if(currentPlaying != null) {
- String artist = "";
- if(currentPlaying.getSong().getArtist() != null) {
- artist = currentPlaying.getSong().getArtist() + " - ";
- }
- statusTextView.setText(artist + currentPlaying.getSong().getAlbum());
- } else {
- statusTextView.setText(null);
- }
- break;
- }
-
- switch (playerState) {
- case STARTED:
- pauseButton.setVisibility(View.VISIBLE);
- stopButton.setVisibility(View.INVISIBLE);
- startButton.setVisibility(View.INVISIBLE);
- break;
- case DOWNLOADING:
- case PREPARING:
- pauseButton.setVisibility(View.INVISIBLE);
- stopButton.setVisibility(View.VISIBLE);
- startButton.setVisibility(View.INVISIBLE);
- break;
- default:
- pauseButton.setVisibility(View.INVISIBLE);
- stopButton.setVisibility(View.INVISIBLE);
- startButton.setVisibility(View.VISIBLE);
- break;
- }
-
- onProgressChangedTask = null;
- }
- };
- if(onDownloadListChangedTask == null && onCurrentChangedTask == null) {
- onProgressChangedTask.execute();
- }
- }
-
- private void changeProgress(final int ms) {
- final DownloadService downloadService = getDownloadService();
- if(downloadService == null) {
- return;
- }
-
- new SilentBackgroundTask<Void>(context) {
- boolean isJukeboxEnabled;
- int msPlayed;
- Integer duration;
- PlayerState playerState;
- int seekTo;
-
- @Override
- protected Void doInBackground() throws Throwable {
- msPlayed = Math.max(0, downloadService.getPlayerPosition());
- duration = downloadService.getPlayerDuration();
- playerState = getDownloadService().getPlayerState();
- int msTotal = duration == null ? 0 : duration;
- if(msPlayed + ms > msTotal) {
- seekTo = msTotal;
- } else {
- seekTo = msPlayed + ms;
- }
- downloadService.seekTo(seekTo);
- return null;
- }
-
- @Override
- protected void done(Void result) {
- progressBar.setProgress(seekTo);
- }
- }.execute();
- }
-
- private void createBookmark() {
- DownloadService downloadService = getDownloadService();
- if(downloadService == null) {
- return;
- }
-
- final DownloadFile currentDownload = downloadService.getCurrentPlaying();
- if(currentDownload == null) {
- return;
- }
-
- View dialogView = context.getLayoutInflater().inflate(R.layout.create_bookmark, null);
- final EditText commentBox = (EditText)dialogView.findViewById(R.id.comment_text);
-
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
- builder.setTitle(R.string.download_save_bookmark_title)
- .setView(dialogView)
- .setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int id) {
- String comment = commentBox.getText().toString();
-
- createBookmark(currentDownload, comment);
- }
- })
- .setNegativeButton(R.string.common_cancel, null);
- AlertDialog dialog = builder.create();
- dialog.show();
- }
- private void createBookmark(final DownloadFile currentDownload, final String comment) {
- DownloadService downloadService = getDownloadService();
- if(downloadService == null) {
- return;
- }
-
- final Entry currentSong = currentDownload.getSong();
- final int position = downloadService.getPlayerPosition();
- final Bookmark oldBookmark = currentSong.getBookmark();
- currentSong.setBookmark(new Bookmark(position));
- bookmarkButton.setImageResource(R.drawable.ic_menu_bookmark_selected);
-
- new SilentBackgroundTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- musicService.createBookmark(currentSong, position, comment, context, null);
-
- new EntryInstanceUpdater(currentSong) {
- @Override
- public void update(Entry found) {
- found.setBookmark(new Bookmark(position));
- }
- }.execute();
-
- return null;
- }
-
- @Override
- protected void done(Void result) {
- Util.toast(context, R.string.download_save_bookmark);
- setControlsVisible(true);
- }
-
- @Override
- protected void error(Throwable error) {
- Log.w(TAG, "Failed to create bookmark", error);
- currentSong.setBookmark(oldBookmark);
-
- // If no bookmark at start, then return to no bookmark
- if(oldBookmark == null) {
- int bookmark;
- if(context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
- bookmark = R.drawable.ic_menu_bookmark_dark;
- } else {
- bookmark = Util.getAttribute(context, R.attr.bookmark);
- }
- bookmarkButton.setImageResource(bookmark);
- }
-
- String msg;
- if(error instanceof OfflineException || error instanceof ServerTooOldException) {
- msg = getErrorMessage(error);
- } else {
- msg = context.getResources().getString(R.string.download_save_bookmark_failed) + getErrorMessage(error);
- }
-
- Util.toast(context, msg, false);
- }
- }.execute();
- }
-
- @Override
- public boolean onDown(MotionEvent me) {
- setControlsVisible(true);
- return false;
- }
-
- @Override
- public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
- final DownloadService downloadService = getDownloadService();
- if (downloadService == null || e1 == null || e2 == null) {
- return false;
- }
-
- // Right to Left swipe
- int action = 0;
- if (e1.getX() - e2.getX() > swipeDistance && Math.abs(velocityX) > swipeVelocity) {
- action = ACTION_NEXT;
- }
- // Left to Right swipe
- else if (e2.getX() - e1.getX() > swipeDistance && Math.abs(velocityX) > swipeVelocity) {
- action = ACTION_PREVIOUS;
- }
- // Top to Bottom swipe
- else if (e2.getY() - e1.getY() > swipeDistance && Math.abs(velocityY) > swipeVelocity) {
- action = ACTION_FORWARD;
- }
- // Bottom to Top swipe
- else if (e1.getY() - e2.getY() > swipeDistance && Math.abs(velocityY) > swipeVelocity) {
- action = ACTION_REWIND;
- }
-
- if(action > 0) {
- final int performAction = action;
- warnIfStorageUnavailable();
- new SilentBackgroundTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- switch(performAction) {
- case ACTION_NEXT:
- downloadService.next();
- break;
- case ACTION_PREVIOUS:
- downloadService.previous();
- break;
- case ACTION_FORWARD:
- downloadService.seekTo(downloadService.getPlayerPosition() + DownloadService.FAST_FORWARD);
- break;
- case ACTION_REWIND:
- downloadService.seekTo(downloadService.getPlayerPosition() - DownloadService.REWIND);
- break;
- }
-
- onProgressChanged();
- if(performAction == ACTION_NEXT || performAction == ACTION_PREVIOUS) {
- onCurrentChanged();
- }
- return null;
- }
- }.execute();
-
- return true;
- } else {
- return false;
- }
- }
-
- @Override
- public void onLongPress(MotionEvent e) {
- }
-
- @Override
- public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
- return false;
- }
-
- @Override
- public void onShowPress(MotionEvent e) {
- }
-
- @Override
- public boolean onSingleTapUp(MotionEvent e) {
- return false;
- }
-}
+/*
+ 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 2014 (C) Scott Jackson
+*/
+package github.daneren2005.dsub.fragments;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.res.Configuration;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v4.view.MenuItemCompat;
+import android.support.v7.app.MediaRouteButton;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.Display;
+import android.view.GestureDetector;
+import android.view.GestureDetector.OnGestureListener;
+import android.view.LayoutInflater;
+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.ViewGroup;
+import android.view.WindowManager;
+import android.view.animation.AnimationUtils;
+import android.widget.AdapterView;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.SeekBar;
+import android.widget.TextView;
+import android.widget.ViewFlipper;
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.activity.SubsonicFragmentActivity;
+import github.daneren2005.dsub.audiofx.EqualizerController;
+import github.daneren2005.dsub.domain.Bookmark;
+import github.daneren2005.dsub.domain.PlayerState;
+import github.daneren2005.dsub.domain.RepeatMode;
+import github.daneren2005.dsub.domain.ServerInfo;
+import github.daneren2005.dsub.service.DownloadFile;
+import github.daneren2005.dsub.service.DownloadService;
+import github.daneren2005.dsub.service.MusicService;
+import github.daneren2005.dsub.service.MusicServiceFactory;
+import github.daneren2005.dsub.service.OfflineException;
+import github.daneren2005.dsub.service.ServerTooOldException;
+import github.daneren2005.dsub.util.Constants;
+import github.daneren2005.dsub.util.SilentBackgroundTask;
+import github.daneren2005.dsub.adapter.DownloadFileAdapter;
+import github.daneren2005.dsub.view.FadeOutAnimation;
+import github.daneren2005.dsub.view.UpdateView;
+import github.daneren2005.dsub.util.Util;
+
+import static github.daneren2005.dsub.domain.MusicDirectory.Entry;
+import static github.daneren2005.dsub.domain.PlayerState.*;
+import github.daneren2005.dsub.util.*;
+import github.daneren2005.dsub.view.AutoRepeatButton;
+import java.util.ArrayList;
+import java.util.concurrent.ScheduledFuture;
+import com.mobeta.android.dslv.*;
+import github.daneren2005.dsub.activity.SubsonicActivity;
+
+public class NowPlayingFragment extends SubsonicFragment implements OnGestureListener {
+ private static final String TAG = NowPlayingFragment.class.getSimpleName();
+ private static final int PERCENTAGE_OF_SCREEN_FOR_SWIPE = 10;
+ private static final int INCREMENT_TIME = 5000;
+ private static final int SERVICE_BACKOFF = 200;
+
+ private static final int ACTION_PREVIOUS = 1;
+ private static final int ACTION_NEXT = 2;
+ private static final int ACTION_REWIND = 3;
+ private static final int ACTION_FORWARD = 4;
+
+ private ViewFlipper playlistFlipper;
+ private TextView emptyTextView;
+ private TextView songTitleTextView;
+ private ImageView albumArtImageView;
+ private DragSortListView playlistView;
+ private TextView positionTextView;
+ private TextView durationTextView;
+ private TextView statusTextView;
+ private SeekBar progressBar;
+ private AutoRepeatButton previousButton;
+ private AutoRepeatButton nextButton;
+ private View pauseButton;
+ private View stopButton;
+ private View startButton;
+ private ImageButton repeatButton;
+ private View toggleListButton;
+ private ImageButton starButton;
+ private ImageButton bookmarkButton;
+ private ImageButton rateBadButton;
+ private ImageButton rateGoodButton;
+ private View mainLayout;
+ private ScheduledExecutorService executorService;
+ private DownloadFile currentPlaying;
+ private long currentRevision;
+ private int swipeDistance;
+ private int swipeVelocity;
+ private ScheduledFuture<?> hideControlsFuture;
+ private List<DownloadFile> songList;
+ private DownloadFileAdapter songListAdapter;
+ private SilentBackgroundTask<Void> onProgressChangedTask;
+ private SilentBackgroundTask<Void> onCurrentChangedTask;
+ private SilentBackgroundTask<Void> onDownloadListChangedTask;
+ private boolean seekInProgress = false;
+ private boolean startFlipped = false;
+ private boolean scrollWhenLoaded = false;
+ private int lastY = 0;
+
+ /**
+ * Called when the activity is first created.
+ */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if(savedInstanceState != null) {
+ if(savedInstanceState.getInt(Constants.FRAGMENT_DOWNLOAD_FLIPPER) == 1) {
+ startFlipped = true;
+ }
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putInt(Constants.FRAGMENT_DOWNLOAD_FLIPPER, playlistFlipper.getDisplayedChild());
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
+ rootView = inflater.inflate(R.layout.download, container, false);
+ setTitle(R.string.button_bar_now_playing);
+
+ mainLayout = rootView.findViewById(R.id.download_layout);
+ if(!primaryFragment) {
+ mainLayout.setVisibility(View.GONE);
+ }
+
+ WindowManager w = context.getWindowManager();
+ Display d = w.getDefaultDisplay();
+ swipeDistance = (d.getWidth() + d.getHeight()) * PERCENTAGE_OF_SCREEN_FOR_SWIPE / 100;
+ swipeVelocity = (d.getWidth() + d.getHeight()) * PERCENTAGE_OF_SCREEN_FOR_SWIPE / 100;
+ gestureScanner = new GestureDetector(this);
+
+ playlistFlipper = (ViewFlipper)rootView.findViewById(R.id.download_playlist_flipper);
+ emptyTextView = (TextView)rootView.findViewById(R.id.download_empty);
+ songTitleTextView = (TextView)rootView.findViewById(R.id.download_song_title);
+ albumArtImageView = (ImageView)rootView.findViewById(R.id.download_album_art_image);
+ positionTextView = (TextView)rootView.findViewById(R.id.download_position);
+ durationTextView = (TextView)rootView.findViewById(R.id.download_duration);
+ statusTextView = (TextView)rootView.findViewById(R.id.download_status);
+ progressBar = (SeekBar)rootView.findViewById(R.id.download_progress_bar);
+ playlistView = (DragSortListView)rootView.findViewById(R.id.download_list);
+ previousButton = (AutoRepeatButton)rootView.findViewById(R.id.download_previous);
+ nextButton = (AutoRepeatButton)rootView.findViewById(R.id.download_next);
+ pauseButton =rootView.findViewById(R.id.download_pause);
+ stopButton =rootView.findViewById(R.id.download_stop);
+ startButton =rootView.findViewById(R.id.download_start);
+ repeatButton = (ImageButton)rootView.findViewById(R.id.download_repeat);
+ bookmarkButton = (ImageButton) rootView.findViewById(R.id.download_bookmark);
+ rateBadButton = (ImageButton) rootView.findViewById(R.id.download_rating_bad);
+ rateGoodButton = (ImageButton) rootView.findViewById(R.id.download_rating_good);
+ toggleListButton =rootView.findViewById(R.id.download_toggle_list);
+
+ starButton = (ImageButton)rootView.findViewById(R.id.download_star);
+ if(Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_MENU_STAR, true)) {
+ starButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ DownloadFile currentDownload = getDownloadService().getCurrentPlaying();
+ if (currentDownload != null) {
+ final Entry currentSong = currentDownload.getSong();
+ toggleStarred(currentSong, new OnStarChange() {
+ @Override
+ void starChange(boolean starred) {
+ starButton.setImageResource(currentSong.isStarred() ? android.R.drawable.btn_star_big_on : android.R.drawable.btn_star_big_off);
+ }
+ });
+ }
+ }
+ });
+ } else {
+ starButton.setVisibility(View.GONE);
+ }
+
+ View.OnTouchListener touchListener = new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent me) {
+ return gestureScanner.onTouchEvent(me);
+ }
+ };
+ pauseButton.setOnTouchListener(touchListener);
+ stopButton.setOnTouchListener(touchListener);
+ startButton.setOnTouchListener(touchListener);
+ bookmarkButton.setOnTouchListener(touchListener);
+ rateBadButton.setOnTouchListener(touchListener);
+ rateGoodButton.setOnTouchListener(touchListener);
+ emptyTextView.setOnTouchListener(touchListener);
+ albumArtImageView.setOnTouchListener(new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent me) {
+ if(me.getAction() == MotionEvent.ACTION_DOWN) {
+ lastY = (int) me.getRawY();
+ }
+ return gestureScanner.onTouchEvent(me);
+ }
+ });
+
+ previousButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ warnIfStorageUnavailable();
+ new SilentBackgroundTask<Void>(context) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ getDownloadService().previous();
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ onCurrentChanged();
+ onProgressChanged();
+ }
+ }.execute();
+ setControlsVisible(true);
+ }
+ });
+ previousButton.setOnRepeatListener(new Runnable() {
+ public void run() {
+ changeProgress(-INCREMENT_TIME);
+ }
+ });
+
+ nextButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ warnIfStorageUnavailable();
+ new SilentBackgroundTask<Boolean>(context) {
+ @Override
+ protected Boolean doInBackground() throws Throwable {
+ getDownloadService().next();
+ return true;
+ }
+
+ @Override
+ protected void done(Boolean result) {
+ if(result) {
+ onCurrentChanged();
+ onProgressChanged();
+ }
+ }
+ }.execute();
+ setControlsVisible(true);
+ }
+ });
+ nextButton.setOnRepeatListener(new Runnable() {
+ public void run() {
+ changeProgress(INCREMENT_TIME);
+ }
+ });
+
+ pauseButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ new SilentBackgroundTask<Void>(context) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ getDownloadService().pause();
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ onCurrentChanged();
+ onProgressChanged();
+ }
+ }.execute();
+ }
+ });
+
+ stopButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ new SilentBackgroundTask<Void>(context) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ getDownloadService().reset();
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ onCurrentChanged();
+ onProgressChanged();
+ }
+ }.execute();
+ }
+ });
+
+ startButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ warnIfStorageUnavailable();
+ new SilentBackgroundTask<Void>(context) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ start();
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ onCurrentChanged();
+ onProgressChanged();
+ }
+ }.execute();
+ }
+ });
+
+ repeatButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ RepeatMode repeatMode = getDownloadService().getRepeatMode().next();
+ getDownloadService().setRepeatMode(repeatMode);
+ onDownloadListChanged();
+ switch (repeatMode) {
+ case OFF:
+ Util.toast(context, R.string.download_repeat_off);
+ break;
+ case ALL:
+ Util.toast(context, R.string.download_repeat_all);
+ break;
+ case SINGLE:
+ Util.toast(context, R.string.download_repeat_single);
+ break;
+ default:
+ break;
+ }
+ setControlsVisible(true);
+ }
+ });
+
+ bookmarkButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ createBookmark();
+ }
+ });
+
+ rateBadButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ DownloadService downloadService = getDownloadService();
+ if(downloadService == null) {
+ return;
+ }
+
+ DownloadFile downloadFile = downloadService.getCurrentPlaying();
+ if(downloadFile == null) {
+ return;
+ }
+ Entry entry = downloadFile.getSong();
+
+ // If rating == 1, already set so unset
+ if(entry.getRating() == 1) {
+ setRating(entry, 0);
+
+ if(context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
+ rateBadButton.setImageResource(R.drawable.ic_action_rating_bad_dark);
+ } else {
+ rateBadButton.setImageResource(Util.getAttribute(context, R.attr.rating_bad));
+ }
+ } else {
+ // Immediately skip to the next song
+ downloadService.next(true);
+
+ // Otherwise set rating to 1
+ setRating(entry, 1);
+ rateBadButton.setImageResource(R.drawable.ic_action_rating_bad_selected);
+
+ // Make sure good rating is blank
+ if (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
+ rateGoodButton.setImageResource(R.drawable.ic_action_rating_good_dark);
+ } else {
+ rateGoodButton.setImageResource(Util.getAttribute(context, R.attr.rating_good));
+ }
+ }
+ }
+ });
+ rateGoodButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ DownloadService downloadService = getDownloadService();
+ if(downloadService == null) {
+ return;
+ }
+
+ DownloadFile downloadFile = downloadService.getCurrentPlaying();
+ if(downloadFile == null) {
+ return;
+ }
+ Entry entry = downloadFile.getSong();
+
+ // If rating == 5, already set so unset
+ if(entry.getRating() == 5) {
+ setRating(entry, 0);
+
+ if (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
+ rateGoodButton.setImageResource(R.drawable.ic_action_rating_good_dark);
+ } else {
+ rateGoodButton.setImageResource(Util.getAttribute(context, R.attr.rating_good));
+ }
+ } else {
+ // Otherwise set rating to maximum
+ setRating(entry, 5);
+ rateGoodButton.setImageResource(R.drawable.ic_action_rating_good_selected);
+
+ // Make sure bad rating is blank
+ if (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
+ rateBadButton.setImageResource(R.drawable.ic_action_rating_bad_dark);
+ } else {
+ rateBadButton.setImageResource(Util.getAttribute(context, R.attr.rating_bad));
+ }
+ }
+ }
+ });
+
+ toggleListButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ toggleFullscreenAlbumArt();
+ setControlsVisible(true);
+ }
+ });
+
+ View overlay = rootView.findViewById(R.id.download_overlay_buttons);
+ final int overlayHeight = overlay != null ? overlay.getHeight() : -1;
+ albumArtImageView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if(overlayHeight == -1 || lastY < (view.getBottom() - overlayHeight)) {
+ toggleFullscreenAlbumArt();
+ setControlsVisible(true);
+ }
+ }
+ });
+
+ progressBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onStopTrackingTouch(final SeekBar seekBar) {
+ new SilentBackgroundTask<Void>(context) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ getDownloadService().seekTo(progressBar.getProgress());
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ seekInProgress = false;
+ NowPlayingFragment.this.onProgressChanged();
+ }
+ }.execute();
+ }
+
+ @Override
+ public void onStartTrackingTouch(final SeekBar seekBar) {
+ seekInProgress = true;
+ }
+
+ @Override
+ public void onProgressChanged(final SeekBar seekBar, final int position, final boolean fromUser) {
+ if (fromUser) {
+ Util.toast(context, Util.formatDuration(position / 1000), true);
+ setControlsVisible(true);
+ }
+ }
+ });
+ playlistView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, final int position, long id) {
+ warnIfStorageUnavailable();
+ new SilentBackgroundTask<Void>(context) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ getDownloadService().play(position);
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ onCurrentChanged();
+ onProgressChanged();
+ }
+ }.execute();
+ }
+ });
+ playlistView.setDropListener(new DragSortListView.DropListener() {
+ @Override
+ public void drop(final int from, final int to) {
+ new SilentBackgroundTask<Void>(context) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ getDownloadService().swap(true, from, to);
+ onDownloadListChanged();
+
+ return null;
+ }
+ }.execute();
+ }
+ });
+ playlistView.setRemoveListener(new DragSortListView.RemoveListener() {
+ @Override
+ public void remove(int which) {
+ getDownloadService().remove(which);
+ onDownloadListChanged();
+ }
+ });
+
+ registerForContextMenu(playlistView);
+
+ DownloadService downloadService = getDownloadService();
+ if (downloadService != null && context.getIntent().getBooleanExtra(Constants.INTENT_EXTRA_NAME_SHUFFLE, false)) {
+ context.getIntent().removeExtra(Constants.INTENT_EXTRA_NAME_SHUFFLE);
+ warnIfStorageUnavailable();
+ downloadService.setShufflePlayEnabled(true);
+ }
+
+ if(Build.MODEL.equals("Nexus 4") || Build.MODEL.equals("GT-I9100")) {
+ View slider = rootView.findViewById(R.id.download_slider);
+ slider.setPadding(0, 0, 0, 0);
+ }
+
+ return rootView;
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
+ DownloadService downloadService = getDownloadService();
+ if(Util.isOffline(context)) {
+ menuInflater.inflate(R.menu.nowplaying_offline, menu);
+ } else {
+ menuInflater.inflate(R.menu.nowplaying, menu);
+
+ if(downloadService != null && downloadService.getSleepTimer()) {
+ menu.findItem(R.id.menu_toggle_timer).setTitle(R.string.download_stop_timer);
+ }
+ }
+ if(downloadService != null && downloadService.getKeepScreenOn()) {
+ menu.findItem(R.id.menu_screen_on_off).setChecked(true);
+ }
+ if(downloadService != null && downloadService.isRemovePlayed()) {
+ menu.findItem(R.id.menu_remove_played).setChecked(true);
+ }
+
+ boolean equalizerAvailable = downloadService != null && downloadService.getEqualizerAvailable();
+ if(equalizerAvailable && !downloadService.isRemoteEnabled()) {
+ SharedPreferences prefs = Util.getPreferences(context);
+ boolean equalizerOn = prefs.getBoolean(Constants.PREFERENCES_EQUALIZER_ON, false);
+ if (equalizerOn && getDownloadService() != null && getDownloadService().getEqualizerController() != null &&
+ getDownloadService().getEqualizerController().isEnabled()) {
+ menu.findItem(R.id.menu_equalizer).setChecked(true);
+ }
+ } else {
+ menu.removeItem(R.id.menu_equalizer);
+ }
+
+ if(downloadService != null) {
+ MenuItem mediaRouteItem = menu.findItem(R.id.menu_mediaroute);
+ if(mediaRouteItem != null) {
+ MediaRouteButton mediaRouteButton = (MediaRouteButton) MenuItemCompat.getActionView(mediaRouteItem);
+ mediaRouteButton.setRouteSelector(downloadService.getRemoteSelector());
+ }
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem menuItem) {
+ if(menuItemSelected(menuItem.getItemId(), null)) {
+ return true;
+ }
+
+ return super.onOptionsItemSelected(menuItem);
+ }
+
+ @Override
+ public void onCreateContextMenu(android.view.ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
+ super.onCreateContextMenu(menu, view, menuInfo);
+ if(!primaryFragment) {
+ return;
+ }
+
+ if (view == playlistView) {
+ AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
+ DownloadFile downloadFile = (DownloadFile) playlistView.getItemAtPosition(info.position);
+
+ android.view.MenuInflater inflater = context.getMenuInflater();
+ if(Util.isOffline(context)) {
+ inflater.inflate(R.menu.nowplaying_context_offline, menu);
+ } else {
+ inflater.inflate(R.menu.nowplaying_context, menu);
+ menu.findItem(R.id.menu_star).setTitle(downloadFile.getSong().isStarred() ? R.string.common_unstar : R.string.common_star);
+ }
+
+ if (downloadFile.getSong().getParent() == null) {
+ menu.findItem(R.id.menu_show_album).setVisible(false);
+ menu.findItem(R.id.menu_show_artist).setVisible(false);
+ }
+
+ hideMenuItems(menu, (AdapterView.AdapterContextMenuInfo) menuInfo);
+ }
+ }
+
+ @Override
+ public boolean onContextItemSelected(android.view.MenuItem menuItem) {
+ if(!primaryFragment) {
+ return false;
+ }
+
+ AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
+ DownloadFile downloadFile = (DownloadFile) playlistView.getItemAtPosition(info.position);
+ return menuItemSelected(menuItem.getItemId(), downloadFile) || super.onContextItemSelected(menuItem);
+ }
+
+ private boolean menuItemSelected(int menuItemId, final DownloadFile song) {
+ switch (menuItemId) {
+ case R.id.menu_show_album: case R.id.menu_show_artist:
+ Entry entry = song.getSong();
+
+ Intent intent = new Intent(context, SubsonicFragmentActivity.class);
+ intent.putExtra(Constants.INTENT_EXTRA_VIEW_ALBUM, true);
+ String albumId;
+ String albumName;
+ if(menuItemId == R.id.menu_show_album) {
+ if(Util.isTagBrowsing(context)) {
+ albumId = entry.getAlbumId();
+ } else {
+ albumId = entry.getParent();
+ }
+ albumName = entry.getAlbum();
+ } else {
+ if(Util.isTagBrowsing(context)) {
+ albumId = entry.getArtistId();
+ } else {
+ albumId = entry.getGrandParent();
+ if(albumId == null) {
+ intent.putExtra(Constants.INTENT_EXTRA_NAME_CHILD_ID, entry.getParent());
+ }
+ }
+ albumName = entry.getArtist();
+ intent.putExtra(Constants.INTENT_EXTRA_NAME_ARTIST, true);
+ }
+ intent.putExtra(Constants.INTENT_EXTRA_NAME_ID, albumId);
+ intent.putExtra(Constants.INTENT_EXTRA_NAME_NAME, albumName);
+ intent.putExtra(Constants.INTENT_EXTRA_FRAGMENT_TYPE, "Artist");
+
+ if(Util.isOffline(context)) {
+ try {
+ // This should only be successful if this is a online song in offline mode
+ Integer.parseInt(entry.getParent());
+ String root = FileUtil.getMusicDirectory(context).getPath();
+ String id = root + "/" + entry.getPath();
+ id = id.substring(0, id.lastIndexOf("/"));
+ if(menuItemId == R.id.menu_show_album) {
+ intent.putExtra(Constants.INTENT_EXTRA_NAME_ID, id);
+ }
+ id = id.substring(0, id.lastIndexOf("/"));
+ if(menuItemId != R.id.menu_show_album) {
+ intent.putExtra(Constants.INTENT_EXTRA_NAME_ID, id);
+ intent.putExtra(Constants.INTENT_EXTRA_NAME_NAME, entry.getArtist());
+ intent.removeExtra(Constants.INTENT_EXTRA_NAME_CHILD_ID);
+ }
+ } catch(Exception e) {
+ // Do nothing, entry.getParent() is fine
+ }
+ }
+
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ Util.startActivityWithoutTransition(context, intent);
+ return true;
+ case R.id.menu_lyrics: {
+ SubsonicFragment fragment = new LyricsFragment();
+ Bundle args = new Bundle();
+ args.putString(Constants.INTENT_EXTRA_NAME_ARTIST, song.getSong().getArtist());
+ args.putString(Constants.INTENT_EXTRA_NAME_TITLE, song.getSong().getTitle());
+ fragment.setArguments(args);
+
+ replaceFragment(fragment);
+ return true;
+ } case R.id.menu_remove:
+ new SilentBackgroundTask<Void>(context) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ getDownloadService().remove(song);
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ onDownloadListChanged();
+ }
+ }.execute();
+ return true;
+ case R.id.menu_delete:
+ List<Entry> songs = new ArrayList<Entry>(1);
+ songs.add(song.getSong());
+ getDownloadService().delete(songs);
+ return true;
+ case R.id.menu_remove_all:
+ Util.confirmDialog(context, R.string.download_menu_remove_all, "", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ new SilentBackgroundTask<Void>(context) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ getDownloadService().setShufflePlayEnabled(false);
+ getDownloadService().clear();
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ onDownloadListChanged();
+ }
+ }.execute();
+ }
+ });
+ return true;
+ case R.id.menu_screen_on_off:
+ if (getDownloadService().getKeepScreenOn()) {
+ context.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ getDownloadService().setKeepScreenOn(false);
+ } else {
+ context.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ getDownloadService().setKeepScreenOn(true);
+ }
+ context.supportInvalidateOptionsMenu();
+ return true;
+ case R.id.menu_remove_played:
+ if (getDownloadService().isRemovePlayed()) {
+ getDownloadService().setRemovePlayed(false);
+ } else {
+ getDownloadService().setRemovePlayed(true);
+ }
+ context.supportInvalidateOptionsMenu();
+ return true;
+ case R.id.menu_shuffle:
+ new SilentBackgroundTask<Void>(context) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ getDownloadService().shuffle();
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ Util.toast(context, R.string.download_menu_shuffle_notification);
+ }
+ }.execute();
+ return true;
+ case R.id.menu_save_playlist:
+ List<Entry> entries = new LinkedList<Entry>();
+ for (DownloadFile downloadFile : getDownloadService().getSongs()) {
+ entries.add(downloadFile.getSong());
+ }
+ createNewPlaylist(entries, true);
+ return true;
+ case R.id.menu_star:
+ toggleStarred(song.getSong());
+ return true;
+ case R.id.menu_rate:
+ setRating(song.getSong());
+ return true;
+ case R.id.menu_toggle_timer:
+ if(getDownloadService().getSleepTimer()) {
+ getDownloadService().stopSleepTimer();
+ context.supportInvalidateOptionsMenu();
+ } else {
+ startTimer();
+ }
+ return true;
+ case R.id.menu_add_playlist:
+ songs = new ArrayList<Entry>(1);
+ songs.add(song.getSong());
+ addToPlaylist(songs);
+ return true;
+ case R.id.menu_info:
+ displaySongInfo(song.getSong());
+ return true;
+ case R.id.menu_share:
+ songs = new ArrayList<Entry>(1);
+ songs.add(song.getSong());
+ createShare(songs);
+ return true;
+ case R.id.menu_equalizer: {
+ DownloadService downloadService = getDownloadService();
+ if (downloadService != null) {
+ EqualizerController controller = downloadService.getEqualizerController();
+ if(controller != null) {
+ SubsonicFragment fragment = new EqualizerFragment();
+ replaceFragment(fragment);
+ setControlsVisible(true);
+
+ return true;
+ }
+ }
+
+ // Any failed condition will get here
+ Util.toast(context, "Failed to start equalizer. Try restarting.");
+ return true;
+ } default:
+ return false;
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ final Handler handler = new Handler();
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ update();
+ }
+ });
+ }
+ };
+
+ executorService = Executors.newSingleThreadScheduledExecutor();
+ executorService.scheduleWithFixedDelay(runnable, 0L, 1000L, TimeUnit.MILLISECONDS);
+
+ setControlsVisible(true);
+
+ DownloadService downloadService = getDownloadService();
+ if (downloadService == null || downloadService.getCurrentPlaying() == null || startFlipped) {
+ playlistFlipper.setDisplayedChild(1);
+ }
+ if (downloadService != null && downloadService.getKeepScreenOn()) {
+ context.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ } else {
+ context.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ }
+
+ updateButtons();
+
+ if(currentPlaying == null && downloadService != null && currentPlaying == downloadService.getCurrentPlaying()) {
+ getImageLoader().loadImage(albumArtImageView, (Entry) null, true, false);
+ }
+ if(downloadService != null) {
+ downloadService.startRemoteScan();
+ } else {
+ // Make sure to call remote scan once the service is ready
+ final Runnable waitForService = new Runnable() {
+ @Override
+ public void run() {
+ DownloadService service = getDownloadService();
+ if(service != null) {
+ service.startRemoteScan();
+ } else {
+ handler.postDelayed(this, SERVICE_BACKOFF);
+ }
+ }
+ };
+
+ handler.postDelayed(waitForService, SERVICE_BACKOFF);
+ }
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ executorService.shutdown();
+ if(getDownloadService() != null) {
+ getDownloadService().stopRemoteScan();
+ }
+ }
+
+ @Override
+ public void setPrimaryFragment(boolean primary) {
+ super.setPrimaryFragment(primary);
+ if(rootView != null) {
+ if(primary) {
+ mainLayout.setVisibility(View.VISIBLE);
+ updateButtons();
+ } else {
+ mainLayout.setVisibility(View.GONE);
+ }
+ }
+ }
+
+ private void scheduleHideControls() {
+ if (hideControlsFuture != null) {
+ hideControlsFuture.cancel(false);
+ }
+
+ final Handler handler = new Handler();
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ setControlsVisible(false);
+ }
+ });
+ }
+ };
+ hideControlsFuture = executorService.schedule(runnable, 3000L, TimeUnit.MILLISECONDS);
+ }
+
+ private void setControlsVisible(boolean visible) {
+ try {
+ long duration = 1700L;
+ FadeOutAnimation.createAndStart(rootView.findViewById(R.id.download_overlay_buttons), !visible, duration);
+
+ if (visible) {
+ scheduleHideControls();
+ }
+ } catch(Exception e) {
+
+ }
+ }
+
+ private void updateButtons() {
+ if(context == null) {
+ return;
+ }
+
+ if(Util.isOffline(context)) {
+ bookmarkButton.setVisibility(View.GONE);
+ rateBadButton.setVisibility(View.GONE);
+ rateGoodButton.setVisibility(View.GONE);
+ } else {
+ if(ServerInfo.canBookmark(context)) {
+ bookmarkButton.setVisibility(View.VISIBLE);
+ } else {
+ bookmarkButton.setVisibility(View.GONE);
+ }
+ rateBadButton.setVisibility(View.VISIBLE);
+ rateGoodButton.setVisibility(View.VISIBLE);
+ }
+ }
+
+ // Scroll to current playing/downloading.
+ private void scrollToCurrent() {
+ if (getDownloadService() == null || songListAdapter == null) {
+ scrollWhenLoaded = true;
+ return;
+ }
+
+ for (int i = 0; i < songListAdapter.getCount(); i++) {
+ if (currentPlaying == playlistView.getItemAtPosition(i)) {
+ playlistView.setSelectionFromTop(i, 40);
+ return;
+ }
+ }
+ DownloadFile currentDownloading = getDownloadService().getCurrentDownloading();
+ for (int i = 0; i < songListAdapter.getCount(); i++) {
+ if (currentDownloading == playlistView.getItemAtPosition(i)) {
+ playlistView.setSelectionFromTop(i, 40);
+ return;
+ }
+ }
+ }
+
+ private void update() {
+ if (getDownloadService() == null) {
+ return;
+ }
+
+ if (currentRevision != getDownloadService().getDownloadListUpdateRevision()) {
+ onDownloadListChanged();
+ }
+
+ if (currentPlaying != getDownloadService().getCurrentPlaying()) {
+ onCurrentChanged();
+ }
+
+ if(startFlipped) {
+ startFlipped = false;
+ scrollToCurrent();
+ }
+
+ onProgressChanged();
+ }
+
+ protected void startTimer() {
+ View dialogView = context.getLayoutInflater().inflate(R.layout.start_timer, null);
+
+ // Setup length label
+ final TextView lengthBox = (TextView) dialogView.findViewById(R.id.timer_length_label);
+ final SharedPreferences prefs = Util.getPreferences(context);
+ String lengthString = prefs.getString(Constants.PREFERENCES_KEY_SLEEP_TIMER_DURATION, "5");
+ int length = Integer.parseInt(lengthString);
+ lengthBox.setText(Util.formatDuration(length));
+
+ // Setup length slider
+ final SeekBar lengthBar = (SeekBar) dialogView.findViewById(R.id.timer_length_bar);
+ lengthBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ if (fromUser) {
+ int length = getMinutes(progress);
+ lengthBox.setText(Util.formatDuration(length));
+ seekBar.setProgress(progress);
+ }
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ }
+ });
+ lengthBar.setProgress(length - 1);
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.menu_set_timer)
+ .setView(dialogView)
+ .setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ int length = getMinutes(lengthBar.getProgress());
+
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putString(Constants.PREFERENCES_KEY_SLEEP_TIMER_DURATION, Integer.toString(length));
+ editor.commit();
+
+ getDownloadService().setSleepTimerDuration(length);
+ getDownloadService().startSleepTimer();
+ context.supportInvalidateOptionsMenu();
+ }
+ })
+ .setNegativeButton(R.string.common_cancel, null);
+ AlertDialog dialog = builder.create();
+ dialog.show();
+ }
+
+ private int getMinutes(int progress) {
+ if(progress < 30) {
+ return progress + 1;
+ } else if(progress < 61) {
+ return (progress - 30) * 5 + getMinutes(29);
+ } else {
+ return (progress - 61) * 15 + getMinutes(60);
+ }
+ }
+
+ private void toggleFullscreenAlbumArt() {
+ if (playlistFlipper.getDisplayedChild() == 1) {
+ playlistFlipper.setInAnimation(AnimationUtils.loadAnimation(context, R.anim.push_down_in));
+ playlistFlipper.setOutAnimation(AnimationUtils.loadAnimation(context, R.anim.push_down_out));
+ playlistFlipper.setDisplayedChild(0);
+ } else {
+ scrollToCurrent();
+ playlistFlipper.setInAnimation(AnimationUtils.loadAnimation(context, R.anim.push_up_in));
+ playlistFlipper.setOutAnimation(AnimationUtils.loadAnimation(context, R.anim.push_up_out));
+ playlistFlipper.setDisplayedChild(1);
+
+ UpdateView.triggerUpdate();
+ }
+ }
+
+ private void start() {
+ DownloadService service = getDownloadService();
+ PlayerState state = service.getPlayerState();
+ if (state == PAUSED || state == COMPLETED || state == STOPPED) {
+ service.start();
+ } else if (state == STOPPED || state == IDLE) {
+ warnIfStorageUnavailable();
+ int current = service.getCurrentPlayingIndex();
+ // TODO: Use play() method.
+ if (current == -1) {
+ service.play(0);
+ } else {
+ service.play(current);
+ }
+ }
+ }
+ private void onDownloadListChanged() {
+ onDownloadListChanged(false);
+ }
+ private void onDownloadListChanged(final boolean refresh) {
+ final DownloadService downloadService = getDownloadService();
+ if (downloadService == null || onDownloadListChangedTask != null) {
+ return;
+ }
+
+ onDownloadListChangedTask = new SilentBackgroundTask<Void>(context) {
+ int currentPlayingIndex;
+ int size;
+
+ @Override
+ protected Void doInBackground() throws Throwable {
+ currentPlayingIndex = downloadService.getCurrentPlayingIndex() + 1;
+ size = downloadService.size();
+
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ List<DownloadFile> list;
+ list = downloadService.getSongs();
+
+ if(downloadService.isShufflePlayEnabled()) {
+ emptyTextView.setText(R.string.download_shuffle_loading);
+ }
+ else {
+ emptyTextView.setText(R.string.download_empty);
+ }
+
+ if(songListAdapter == null || refresh) {
+ songList = new ArrayList<DownloadFile>();
+ songList.addAll(list);
+ playlistView.setAdapter(songListAdapter = new DownloadFileAdapter(context, songList));
+ } else {
+ songList.clear();
+ songList.addAll(list);
+ songListAdapter.notifyDataSetChanged();
+ }
+
+ emptyTextView.setVisibility(list.isEmpty() ? View.VISIBLE : View.GONE);
+ currentRevision = downloadService.getDownloadListUpdateRevision();
+
+ switch (downloadService.getRepeatMode()) {
+ case OFF:
+ if("light".equals(SubsonicActivity.getThemeName()) | "light_fullscreen".equals(SubsonicActivity.getThemeName())) {
+ repeatButton.setImageResource(R.drawable.media_repeat_off_light);
+ } else {
+ repeatButton.setImageResource(R.drawable.media_repeat_off);
+ }
+ break;
+ case ALL:
+ repeatButton.setImageResource(R.drawable.media_repeat_all);
+ break;
+ case SINGLE:
+ repeatButton.setImageResource(R.drawable.media_repeat_single);
+ break;
+ default:
+ break;
+ }
+
+ if(scrollWhenLoaded) {
+ scrollToCurrent();
+ scrollWhenLoaded = false;
+ }
+
+ setSubtitle(context.getResources().getString(R.string.download_playing_out_of, currentPlayingIndex, size));
+ onDownloadListChangedTask = null;
+ if(onCurrentChangedTask != null) {
+ onCurrentChangedTask.execute();
+ } else if(onProgressChangedTask != null) {
+ onProgressChangedTask.execute();
+ }
+ }
+ };
+ onDownloadListChangedTask.execute();
+ }
+
+ private void onCurrentChanged() {
+ final DownloadService downloadService = getDownloadService();
+ if (downloadService == null || onCurrentChangedTask != null) {
+ return;
+ }
+
+ onCurrentChangedTask = new SilentBackgroundTask<Void>(context) {
+ int currentPlayingIndex;
+ int currentPlayingSize;
+
+ @Override
+ protected Void doInBackground() throws Throwable {
+ currentPlaying = downloadService.getCurrentPlaying();
+ currentPlayingIndex = downloadService.getCurrentPlayingIndex() + 1;
+ currentPlayingSize = downloadService.size();
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ if (currentPlaying != null) {
+ Entry song = currentPlaying.getSong();
+ songTitleTextView.setText(song.getTitle());
+ getImageLoader().loadImage(albumArtImageView, song, true, true);
+ starButton.setImageResource(song.isStarred() ? android.R.drawable.btn_star_big_on : android.R.drawable.btn_star_big_off);
+ setSubtitle(context.getResources().getString(R.string.download_playing_out_of, currentPlayingIndex, currentPlayingSize));
+
+ int badRating, goodRating, bookmark;
+ if(song.getRating() == 1) {
+ badRating = R.drawable.ic_action_rating_bad_selected;
+ } else if(context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
+ badRating = R.drawable.ic_action_rating_bad_dark;
+ } else {
+ badRating = Util.getAttribute(context, R.attr.rating_bad);
+ }
+ rateBadButton.setImageResource(badRating);
+
+ if(song.getRating() == 5) {
+ goodRating = R.drawable.ic_action_rating_good_selected;
+ } else if(context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
+ goodRating = R.drawable.ic_action_rating_good_dark;
+ } else {
+ goodRating = Util.getAttribute(context, R.attr.rating_good);
+ }
+ rateGoodButton.setImageResource(goodRating);
+
+ if(song.getBookmark() != null) {
+ bookmark = R.drawable.ic_menu_bookmark_selected;
+ } else if(context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
+ bookmark = R.drawable.ic_menu_bookmark_dark;
+ } else {
+ bookmark = Util.getAttribute(context, R.attr.bookmark);
+ }
+ bookmarkButton.setImageResource(bookmark);
+ } else {
+ songTitleTextView.setText(null);
+ getImageLoader().loadImage(albumArtImageView, (Entry) null, true, false);
+ starButton.setImageResource(android.R.drawable.btn_star_big_off);
+ setSubtitle(null);
+ }
+ onCurrentChangedTask = null;
+ if(onProgressChangedTask != null) {
+ onProgressChangedTask.execute();
+ }
+ }
+ };
+
+ if(onDownloadListChangedTask == null) {
+ onCurrentChangedTask.execute();
+ }
+ }
+
+ private void onProgressChanged() {
+ // Make sure to only be trying to run one of these at a time
+ if (getDownloadService() == null || onProgressChangedTask != null) {
+ return;
+ }
+
+ onProgressChangedTask = new SilentBackgroundTask<Void>(context) {
+ DownloadService downloadService;
+ int millisPlayed;
+ Integer duration;
+ PlayerState playerState;
+ boolean isSeekable;
+
+ @Override
+ protected Void doInBackground() throws Throwable {
+ downloadService = getDownloadService();
+ millisPlayed = Math.max(0, downloadService.getPlayerPosition());
+ duration = downloadService.getPlayerDuration();
+ playerState = getDownloadService().getPlayerState();
+ isSeekable = downloadService.isSeekable();
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ if (currentPlaying != null) {
+ int millisTotal = duration == null ? 0 : duration;
+
+ positionTextView.setText(Util.formatDuration(millisPlayed / 1000));
+ if(millisTotal > 0) {
+ durationTextView.setText(Util.formatDuration(millisTotal / 1000));
+ } else {
+ durationTextView.setText("-:--");
+ }
+ progressBar.setMax(millisTotal == 0 ? 100 : millisTotal); // Work-around for apparent bug.
+ if(!seekInProgress) {
+ progressBar.setProgress(millisPlayed);
+ }
+ progressBar.setEnabled(isSeekable);
+ } else {
+ positionTextView.setText("0:00");
+ durationTextView.setText("-:--");
+ progressBar.setProgress(0);
+ progressBar.setEnabled(false);
+ }
+
+ switch (playerState) {
+ case DOWNLOADING:
+ if(currentPlaying != null) {
+ long bytes = currentPlaying.getPartialFile().length();
+ statusTextView.setText(context.getResources().getString(R.string.download_playerstate_downloading, Util.formatLocalizedBytes(bytes, context)));
+ }
+ break;
+ case PREPARING:
+ statusTextView.setText(R.string.download_playerstate_buffering);
+ break;
+ default:
+ if(currentPlaying != null) {
+ String artist = "";
+ if(currentPlaying.getSong().getArtist() != null) {
+ artist = currentPlaying.getSong().getArtist() + " - ";
+ }
+ statusTextView.setText(artist + currentPlaying.getSong().getAlbum());
+ } else {
+ statusTextView.setText(null);
+ }
+ break;
+ }
+
+ switch (playerState) {
+ case STARTED:
+ pauseButton.setVisibility(View.VISIBLE);
+ stopButton.setVisibility(View.INVISIBLE);
+ startButton.setVisibility(View.INVISIBLE);
+ break;
+ case DOWNLOADING:
+ case PREPARING:
+ pauseButton.setVisibility(View.INVISIBLE);
+ stopButton.setVisibility(View.VISIBLE);
+ startButton.setVisibility(View.INVISIBLE);
+ break;
+ default:
+ pauseButton.setVisibility(View.INVISIBLE);
+ stopButton.setVisibility(View.INVISIBLE);
+ startButton.setVisibility(View.VISIBLE);
+ break;
+ }
+
+ onProgressChangedTask = null;
+ }
+ };
+ if(onDownloadListChangedTask == null && onCurrentChangedTask == null) {
+ onProgressChangedTask.execute();
+ }
+ }
+
+ private void changeProgress(final int ms) {
+ final DownloadService downloadService = getDownloadService();
+ if(downloadService == null) {
+ return;
+ }
+
+ new SilentBackgroundTask<Void>(context) {
+ boolean isJukeboxEnabled;
+ int msPlayed;
+ Integer duration;
+ PlayerState playerState;
+ int seekTo;
+
+ @Override
+ protected Void doInBackground() throws Throwable {
+ msPlayed = Math.max(0, downloadService.getPlayerPosition());
+ duration = downloadService.getPlayerDuration();
+ playerState = getDownloadService().getPlayerState();
+ int msTotal = duration == null ? 0 : duration;
+ if(msPlayed + ms > msTotal) {
+ seekTo = msTotal;
+ } else {
+ seekTo = msPlayed + ms;
+ }
+ downloadService.seekTo(seekTo);
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ progressBar.setProgress(seekTo);
+ }
+ }.execute();
+ }
+
+ private void createBookmark() {
+ DownloadService downloadService = getDownloadService();
+ if(downloadService == null) {
+ return;
+ }
+
+ final DownloadFile currentDownload = downloadService.getCurrentPlaying();
+ if(currentDownload == null) {
+ return;
+ }
+
+ View dialogView = context.getLayoutInflater().inflate(R.layout.create_bookmark, null);
+ final EditText commentBox = (EditText)dialogView.findViewById(R.id.comment_text);
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.download_save_bookmark_title)
+ .setView(dialogView)
+ .setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ String comment = commentBox.getText().toString();
+
+ createBookmark(currentDownload, comment);
+ }
+ })
+ .setNegativeButton(R.string.common_cancel, null);
+ AlertDialog dialog = builder.create();
+ dialog.show();
+ }
+ private void createBookmark(final DownloadFile currentDownload, final String comment) {
+ DownloadService downloadService = getDownloadService();
+ if(downloadService == null) {
+ return;
+ }
+
+ final Entry currentSong = currentDownload.getSong();
+ final int position = downloadService.getPlayerPosition();
+ final Bookmark oldBookmark = currentSong.getBookmark();
+ currentSong.setBookmark(new Bookmark(position));
+ bookmarkButton.setImageResource(R.drawable.ic_menu_bookmark_selected);
+
+ new SilentBackgroundTask<Void>(context) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ musicService.createBookmark(currentSong, position, comment, context, null);
+
+ new EntryInstanceUpdater(currentSong) {
+ @Override
+ public void update(Entry found) {
+ found.setBookmark(new Bookmark(position));
+ }
+ }.execute();
+
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ Util.toast(context, R.string.download_save_bookmark);
+ setControlsVisible(true);
+ }
+
+ @Override
+ protected void error(Throwable error) {
+ Log.w(TAG, "Failed to create bookmark", error);
+ currentSong.setBookmark(oldBookmark);
+
+ // If no bookmark at start, then return to no bookmark
+ if(oldBookmark == null) {
+ int bookmark;
+ if(context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
+ bookmark = R.drawable.ic_menu_bookmark_dark;
+ } else {
+ bookmark = Util.getAttribute(context, R.attr.bookmark);
+ }
+ bookmarkButton.setImageResource(bookmark);
+ }
+
+ String msg;
+ if(error instanceof OfflineException || error instanceof ServerTooOldException) {
+ msg = getErrorMessage(error);
+ } else {
+ msg = context.getResources().getString(R.string.download_save_bookmark_failed) + getErrorMessage(error);
+ }
+
+ Util.toast(context, msg, false);
+ }
+ }.execute();
+ }
+
+ @Override
+ public boolean onDown(MotionEvent me) {
+ setControlsVisible(true);
+ return false;
+ }
+
+ @Override
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
+ final DownloadService downloadService = getDownloadService();
+ if (downloadService == null || e1 == null || e2 == null) {
+ return false;
+ }
+
+ // Right to Left swipe
+ int action = 0;
+ if (e1.getX() - e2.getX() > swipeDistance && Math.abs(velocityX) > swipeVelocity) {
+ action = ACTION_NEXT;
+ }
+ // Left to Right swipe
+ else if (e2.getX() - e1.getX() > swipeDistance && Math.abs(velocityX) > swipeVelocity) {
+ action = ACTION_PREVIOUS;
+ }
+ // Top to Bottom swipe
+ else if (e2.getY() - e1.getY() > swipeDistance && Math.abs(velocityY) > swipeVelocity) {
+ action = ACTION_FORWARD;
+ }
+ // Bottom to Top swipe
+ else if (e1.getY() - e2.getY() > swipeDistance && Math.abs(velocityY) > swipeVelocity) {
+ action = ACTION_REWIND;
+ }
+
+ if(action > 0) {
+ final int performAction = action;
+ warnIfStorageUnavailable();
+ new SilentBackgroundTask<Void>(context) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ switch(performAction) {
+ case ACTION_NEXT:
+ downloadService.next();
+ break;
+ case ACTION_PREVIOUS:
+ downloadService.previous();
+ break;
+ case ACTION_FORWARD:
+ downloadService.seekTo(downloadService.getPlayerPosition() + DownloadService.FAST_FORWARD);
+ break;
+ case ACTION_REWIND:
+ downloadService.seekTo(downloadService.getPlayerPosition() - DownloadService.REWIND);
+ break;
+ }
+
+ onProgressChanged();
+ if(performAction == ACTION_NEXT || performAction == ACTION_PREVIOUS) {
+ onCurrentChanged();
+ }
+ return null;
+ }
+ }.execute();
+
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public void onLongPress(MotionEvent e) {
+ }
+
+ @Override
+ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
+ return false;
+ }
+
+ @Override
+ public void onShowPress(MotionEvent e) {
+ }
+
+ @Override
+ public boolean onSingleTapUp(MotionEvent e) {
+ return false;
+ }
+}
diff --git a/src/github/daneren2005/dsub/fragments/PreferenceCompatFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/PreferenceCompatFragment.java
index 9f413b3b..9f413b3b 100644
--- a/src/github/daneren2005/dsub/fragments/PreferenceCompatFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/PreferenceCompatFragment.java
diff --git a/src/github/daneren2005/dsub/fragments/SearchFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SearchFragment.java
index 0ab08776..0f1598dd 100644
--- a/src/github/daneren2005/dsub/fragments/SearchFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SearchFragment.java
@@ -1,368 +1,368 @@
-package github.daneren2005.dsub.fragments;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Arrays;
-
-import android.content.Intent;
-import android.os.Bundle;
-import android.support.v4.widget.SwipeRefreshLayout;
-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.net.Uri;
-import android.view.ViewGroup;
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.domain.Artist;
-import github.daneren2005.dsub.domain.MusicDirectory;
-import github.daneren2005.dsub.domain.SearchCritera;
-import github.daneren2005.dsub.domain.SearchResult;
-import github.daneren2005.dsub.service.MusicService;
-import github.daneren2005.dsub.service.MusicServiceFactory;
-import github.daneren2005.dsub.service.DownloadService;
-import github.daneren2005.dsub.adapter.ArtistAdapter;
-import github.daneren2005.dsub.util.BackgroundTask;
-import github.daneren2005.dsub.util.Constants;
-import github.daneren2005.dsub.adapter.EntryAdapter;
-import github.daneren2005.dsub.adapter.MergeAdapter;
-import github.daneren2005.dsub.util.TabBackgroundTask;
-import github.daneren2005.dsub.util.Util;
-
-public class SearchFragment extends SubsonicFragment {
- private static final String TAG = SearchFragment.class.getSimpleName();
-
- private static final int DEFAULT_ARTISTS = 3;
- private static final int DEFAULT_ALBUMS = 5;
- private static final int DEFAULT_SONGS = 10;
-
- private static final int MAX_ARTISTS = 10;
- private static final int MAX_ALBUMS = 20;
- private static final int MAX_SONGS = 25;
- private ListView list;
-
- private View artistsHeading;
- private View albumsHeading;
- private View songsHeading;
- 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;
- private boolean skipSearch = false;
- private String currentQuery;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- if(savedInstanceState != null) {
- searchResult = (SearchResult) savedInstanceState.getSerializable(Constants.FRAGMENT_LIST);
- }
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putSerializable(Constants.FRAGMENT_LIST, searchResult);
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
- rootView = inflater.inflate(R.layout.abstract_list_fragment, container, false);
- setTitle(R.string.search_title);
-
- View buttons = inflater.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);
-
- moreArtistsButton = buttons.findViewById(R.id.search_more_artists);
- moreAlbumsButton = buttons.findViewById(R.id.search_more_albums);
- moreSongsButton = buttons.findViewById(R.id.search_more_songs);
-
- refreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.refresh_layout);
- refreshLayout.setEnabled(false);
-
- list = (ListView) rootView.findViewById(R.id.fragment_list);
-
- list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- 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, false);
- } 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);
- }
-
- }
- }
- }
- });
- registerForContextMenu(list);
- context.onNewIntent(context.getIntent());
-
- if(searchResult != null) {
- skipSearch = true;
- populateList();
- }
-
- return rootView;
- }
-
- @Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
- menuInflater.inflate(R.menu.search, menu);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.menu_search:
- context.startSearch(currentQuery, false, null, false);
- return true;
- }
-
- return super.onOptionsItemSelected(item);
-
- }
-
- @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);
- onCreateContextMenu(menu, view, menuInfo, selectedItem);
- if(selectedItem instanceof MusicDirectory.Entry && !((MusicDirectory.Entry) selectedItem).isVideo() && !Util.isOffline(context)) {
- menu.removeItem(R.id.song_menu_remove_playlist);
- }
-
- recreateContextMenu(menu);
- }
-
- @Override
- public boolean onContextItemSelected(MenuItem menuItem) {
- if(menuItem.getGroupId() != getSupportTag()) {
- return false;
- }
-
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
- Object selectedItem = list.getItemAtPosition(info.position);
-
- if(onContextItemSelected(menuItem, selectedItem)) {
- return true;
- }
-
- return true;
- }
-
- @Override
- public void setPrimaryFragment(boolean primary) {
- super.setPrimaryFragment(primary);
- }
-
- @Override
- public void refresh(boolean refresh) {
- context.onNewIntent(context.getIntent());
- }
-
- public void search(final String query, final boolean autoplay) {
- if(skipSearch) {
- skipSearch = false;
- return;
- }
- currentQuery = query;
-
- mergeAdapter = new MergeAdapter();
- list.setAdapter(mergeAdapter);
-
- BackgroundTask<SearchResult> task = new TabBackgroundTask<SearchResult>(this) {
- @Override
- protected SearchResult doInBackground() throws Throwable {
- SearchCritera criteria = new SearchCritera(query, MAX_ARTISTS, MAX_ALBUMS, MAX_SONGS);
- MusicService service = MusicServiceFactory.getMusicService(context);
- return service.search(criteria, context, this);
- }
-
- @Override
- protected void done(SearchResult result) {
- searchResult = result;
- populateList();
- if (autoplay) {
- autoplay(query);
- }
-
- }
- };
- task.execute();
- }
-
- public void populateList() {
- mergeAdapter = new MergeAdapter();
-
- 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(context, 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(context, 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(context, 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();
- if(empty) {
- setEmpty(true);
- }
- }
-
- 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, boolean autoplay) {
- SubsonicFragment fragment = new SelectDirectoryFragment();
- Bundle args = new Bundle();
- args.putString(Constants.INTENT_EXTRA_NAME_ID, artist.getId());
- args.putString(Constants.INTENT_EXTRA_NAME_NAME, artist.getName());
- if(autoplay) {
- args.putBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, true);
- }
- args.putBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, true);
- fragment.setArguments(args);
-
- replaceFragment(fragment);
- }
-
- private void onAlbumSelected(MusicDirectory.Entry album, boolean autoplay) {
- SubsonicFragment fragment = new SelectDirectoryFragment();
- Bundle args = new Bundle();
- args.putString(Constants.INTENT_EXTRA_NAME_ID, album.getId());
- args.putString(Constants.INTENT_EXTRA_NAME_NAME, album.getTitle());
- if(autoplay) {
- args.putBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, true);
- }
- fragment.setArguments(args);
-
- replaceFragment(fragment);
- }
-
- 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, false);
- if (autoplay) {
- downloadService.play(downloadService.size() - 1);
- }
-
- Util.toast(context, getResources().getQuantityString(R.plurals.select_album_n_songs_added, 1, 1));
- }
- }
-
- private void onVideoSelected(MusicDirectory.Entry entry) {
- int maxBitrate = Util.getMaxVideoBitrate(context);
-
- Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.setData(Uri.parse(MusicServiceFactory.getMusicService(context).getVideoUrl(maxBitrate, context, entry.getId())));
- startActivity(intent);
- }
-
- private void autoplay(String query) {
- Artist artist = searchResult.getArtists().isEmpty() ? null : searchResult.getArtists().get(0);
- MusicDirectory.Entry album = searchResult.getAlbums().isEmpty() ? null : searchResult.getAlbums().get(0);
- MusicDirectory.Entry song = searchResult.getSongs().isEmpty() ? null : searchResult.getSongs().get(0);
-
- if(artist != null && query.equals(artist.getName())) {
- onArtistSelected(artist, true);
- } else if(album != null && query.equals(album.getTitle())) {
- onAlbumSelected(album, true);
- } else if(song != null) {
- onSongSelected(song, false, false, true, false);
- } else if(album != null) {
- onAlbumSelected(album, true);
- }
- }
-}
+package github.daneren2005.dsub.fragments;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Arrays;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.widget.SwipeRefreshLayout;
+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.net.Uri;
+import android.view.ViewGroup;
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.Artist;
+import github.daneren2005.dsub.domain.MusicDirectory;
+import github.daneren2005.dsub.domain.SearchCritera;
+import github.daneren2005.dsub.domain.SearchResult;
+import github.daneren2005.dsub.service.MusicService;
+import github.daneren2005.dsub.service.MusicServiceFactory;
+import github.daneren2005.dsub.service.DownloadService;
+import github.daneren2005.dsub.adapter.ArtistAdapter;
+import github.daneren2005.dsub.util.BackgroundTask;
+import github.daneren2005.dsub.util.Constants;
+import github.daneren2005.dsub.adapter.EntryAdapter;
+import github.daneren2005.dsub.adapter.MergeAdapter;
+import github.daneren2005.dsub.util.TabBackgroundTask;
+import github.daneren2005.dsub.util.Util;
+
+public class SearchFragment extends SubsonicFragment {
+ private static final String TAG = SearchFragment.class.getSimpleName();
+
+ private static final int DEFAULT_ARTISTS = 3;
+ private static final int DEFAULT_ALBUMS = 5;
+ private static final int DEFAULT_SONGS = 10;
+
+ private static final int MAX_ARTISTS = 10;
+ private static final int MAX_ALBUMS = 20;
+ private static final int MAX_SONGS = 25;
+ private ListView list;
+
+ private View artistsHeading;
+ private View albumsHeading;
+ private View songsHeading;
+ 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;
+ private boolean skipSearch = false;
+ private String currentQuery;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if(savedInstanceState != null) {
+ searchResult = (SearchResult) savedInstanceState.getSerializable(Constants.FRAGMENT_LIST);
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putSerializable(Constants.FRAGMENT_LIST, searchResult);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
+ rootView = inflater.inflate(R.layout.abstract_list_fragment, container, false);
+ setTitle(R.string.search_title);
+
+ View buttons = inflater.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);
+
+ moreArtistsButton = buttons.findViewById(R.id.search_more_artists);
+ moreAlbumsButton = buttons.findViewById(R.id.search_more_albums);
+ moreSongsButton = buttons.findViewById(R.id.search_more_songs);
+
+ refreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.refresh_layout);
+ refreshLayout.setEnabled(false);
+
+ list = (ListView) rootView.findViewById(R.id.fragment_list);
+
+ list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ 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, false);
+ } 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);
+ }
+
+ }
+ }
+ }
+ });
+ registerForContextMenu(list);
+ context.onNewIntent(context.getIntent());
+
+ if(searchResult != null) {
+ skipSearch = true;
+ populateList();
+ }
+
+ return rootView;
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
+ menuInflater.inflate(R.menu.search, menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.menu_search:
+ context.startSearch(currentQuery, false, null, false);
+ return true;
+ }
+
+ return super.onOptionsItemSelected(item);
+
+ }
+
+ @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);
+ onCreateContextMenu(menu, view, menuInfo, selectedItem);
+ if(selectedItem instanceof MusicDirectory.Entry && !((MusicDirectory.Entry) selectedItem).isVideo() && !Util.isOffline(context)) {
+ menu.removeItem(R.id.song_menu_remove_playlist);
+ }
+
+ recreateContextMenu(menu);
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem menuItem) {
+ if(menuItem.getGroupId() != getSupportTag()) {
+ return false;
+ }
+
+ AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
+ Object selectedItem = list.getItemAtPosition(info.position);
+
+ if(onContextItemSelected(menuItem, selectedItem)) {
+ return true;
+ }
+
+ return true;
+ }
+
+ @Override
+ public void setPrimaryFragment(boolean primary) {
+ super.setPrimaryFragment(primary);
+ }
+
+ @Override
+ public void refresh(boolean refresh) {
+ context.onNewIntent(context.getIntent());
+ }
+
+ public void search(final String query, final boolean autoplay) {
+ if(skipSearch) {
+ skipSearch = false;
+ return;
+ }
+ currentQuery = query;
+
+ mergeAdapter = new MergeAdapter();
+ list.setAdapter(mergeAdapter);
+
+ BackgroundTask<SearchResult> task = new TabBackgroundTask<SearchResult>(this) {
+ @Override
+ protected SearchResult doInBackground() throws Throwable {
+ SearchCritera criteria = new SearchCritera(query, MAX_ARTISTS, MAX_ALBUMS, MAX_SONGS);
+ MusicService service = MusicServiceFactory.getMusicService(context);
+ return service.search(criteria, context, this);
+ }
+
+ @Override
+ protected void done(SearchResult result) {
+ searchResult = result;
+ populateList();
+ if (autoplay) {
+ autoplay(query);
+ }
+
+ }
+ };
+ task.execute();
+ }
+
+ public void populateList() {
+ mergeAdapter = new MergeAdapter();
+
+ 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(context, 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(context, 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(context, 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();
+ if(empty) {
+ setEmpty(true);
+ }
+ }
+
+ 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, boolean autoplay) {
+ SubsonicFragment fragment = new SelectDirectoryFragment();
+ Bundle args = new Bundle();
+ args.putString(Constants.INTENT_EXTRA_NAME_ID, artist.getId());
+ args.putString(Constants.INTENT_EXTRA_NAME_NAME, artist.getName());
+ if(autoplay) {
+ args.putBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, true);
+ }
+ args.putBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, true);
+ fragment.setArguments(args);
+
+ replaceFragment(fragment);
+ }
+
+ private void onAlbumSelected(MusicDirectory.Entry album, boolean autoplay) {
+ SubsonicFragment fragment = new SelectDirectoryFragment();
+ Bundle args = new Bundle();
+ args.putString(Constants.INTENT_EXTRA_NAME_ID, album.getId());
+ args.putString(Constants.INTENT_EXTRA_NAME_NAME, album.getTitle());
+ if(autoplay) {
+ args.putBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, true);
+ }
+ fragment.setArguments(args);
+
+ replaceFragment(fragment);
+ }
+
+ 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, false);
+ if (autoplay) {
+ downloadService.play(downloadService.size() - 1);
+ }
+
+ Util.toast(context, getResources().getQuantityString(R.plurals.select_album_n_songs_added, 1, 1));
+ }
+ }
+
+ private void onVideoSelected(MusicDirectory.Entry entry) {
+ int maxBitrate = Util.getMaxVideoBitrate(context);
+
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setData(Uri.parse(MusicServiceFactory.getMusicService(context).getVideoUrl(maxBitrate, context, entry.getId())));
+ startActivity(intent);
+ }
+
+ private void autoplay(String query) {
+ Artist artist = searchResult.getArtists().isEmpty() ? null : searchResult.getArtists().get(0);
+ MusicDirectory.Entry album = searchResult.getAlbums().isEmpty() ? null : searchResult.getAlbums().get(0);
+ MusicDirectory.Entry song = searchResult.getSongs().isEmpty() ? null : searchResult.getSongs().get(0);
+
+ if(artist != null && query.equals(artist.getName())) {
+ onArtistSelected(artist, true);
+ } else if(album != null && query.equals(album.getTitle())) {
+ onAlbumSelected(album, true);
+ } else if(song != null) {
+ onSongSelected(song, false, false, true, false);
+ } else if(album != null) {
+ onAlbumSelected(album, true);
+ }
+ }
+}
diff --git a/src/github/daneren2005/dsub/fragments/SelectArtistFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectArtistFragment.java
index 8bf8a8ec..5488c95b 100644
--- a/src/github/daneren2005/dsub/fragments/SelectArtistFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectArtistFragment.java
@@ -1,333 +1,333 @@
-package github.daneren2005.dsub.fragments;
-
-import android.annotation.TargetApi;
-import android.os.Build;
-import android.os.Bundle;
-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.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.domain.Artist;
-import github.daneren2005.dsub.domain.Indexes;
-import github.daneren2005.dsub.domain.MusicDirectory;
-import github.daneren2005.dsub.domain.MusicFolder;
-import github.daneren2005.dsub.service.MusicService;
-import github.daneren2005.dsub.util.Constants;
-import github.daneren2005.dsub.util.ProgressListener;
-import github.daneren2005.dsub.util.Util;
-import github.daneren2005.dsub.adapter.ArtistAdapter;
-
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.List;
-
-public class SelectArtistFragment extends SelectListFragment<Artist> {
- private static final String TAG = SelectArtistFragment.class.getSimpleName();
- private static final int MENU_GROUP_MUSIC_FOLDER = 10;
-
- private View folderButtonParent;
- private View folderButton;
- private TextView folderName;
- private List<MusicFolder> musicFolders = null;
- private List<MusicDirectory.Entry> entries;
- private String groupId;
- private String groupName;
-
- public SelectArtistFragment() {
- super();
- }
-
- @Override
- public void onCreate(Bundle bundle) {
- super.onCreate(bundle);
-
- if(bundle != null) {
- musicFolders = (List<MusicFolder>) bundle.getSerializable(Constants.FRAGMENT_LIST2);
- }
- artist = true;
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putSerializable(Constants.FRAGMENT_LIST2, (Serializable) musicFolders);
- }
-
- @TargetApi(Build.VERSION_CODES.HONEYCOMB)
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
- Bundle args = getArguments();
- if(args != null) {
- groupId = args.getString(Constants.INTENT_EXTRA_NAME_ID);
- groupName = args.getString(Constants.INTENT_EXTRA_NAME_NAME);
-
- if(groupName != null) {
- setTitle(groupName);
- context.invalidateOptionsMenu();
- }
- }
-
- folderButton = null;
- super.onCreateView(inflater, container, bundle);
-
- if("4.4.2".equals(Build.VERSION.RELEASE)) {
- listView.setFastScrollAlwaysVisible(true);
- }
-
- if(objects != null && currentTask == null) {
- if (Util.isOffline(context) || Util.isTagBrowsing(context) || groupId != null) {
- folderButton.setVisibility(View.GONE);
- }
- setMusicFolders();
- }
-
- return rootView;
- }
-
- @Override
- public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, view, menuInfo);
-
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
- Object entry = listView.getItemAtPosition(info.position);
-
- if (entry instanceof Artist) {
- onCreateContextMenu(menu, view, menuInfo, entry);
- } else if (info.position == 0) {
- String musicFolderId = Util.getSelectedMusicFolderId(context);
- MenuItem menuItem = menu.add(MENU_GROUP_MUSIC_FOLDER, -1, 0, R.string.select_artist_all_folders);
- if (musicFolderId == null) {
- menuItem.setChecked(true);
- }
- if (musicFolders != null) {
- for (int i = 0; i < musicFolders.size(); i++) {
- MusicFolder musicFolder = musicFolders.get(i);
- menuItem = menu.add(MENU_GROUP_MUSIC_FOLDER, i, i + 1, musicFolder.getName());
- if (musicFolder.getId().equals(musicFolderId)) {
- menuItem.setChecked(true);
- }
- }
- }
- menu.setGroupCheckable(MENU_GROUP_MUSIC_FOLDER, true, true);
- }
-
- recreateContextMenu(menu);
- }
-
- @Override
- public boolean onContextItemSelected(MenuItem menuItem) {
- if(menuItem.getGroupId() != getSupportTag()) {
- return false;
- }
-
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
- Artist artist = (Artist) listView.getItemAtPosition(info.position);
-
- if (artist != null) {
- return onContextItemSelected(menuItem, artist);
- } else if (info.position == 0) {
- MusicFolder selectedFolder = menuItem.getItemId() == -1 ? null : musicFolders.get(menuItem.getItemId());
- String musicFolderId = selectedFolder == null ? null : selectedFolder.getId();
- String musicFolderName = selectedFolder == null ? context.getString(R.string.select_artist_all_folders)
- : selectedFolder.getName();
- Util.setSelectedMusicFolderId(context, musicFolderId);
- folderName.setText(musicFolderName);
- context.invalidate();
- }
-
- return true;
- }
-
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- if (view == folderButtonParent) {
- selectFolder();
- } else {
- Artist artist = (Artist) parent.getItemAtPosition(position);
-
- SubsonicFragment fragment;
- if((Util.isFirstLevelArtist(context) || Util.isOffline(context) || Util.isTagBrowsing(context)) || "root".equals(artist.getId()) || groupId != null) {
- fragment = new SelectDirectoryFragment();
- Bundle args = new Bundle();
- args.putString(Constants.INTENT_EXTRA_NAME_ID, artist.getId());
- args.putString(Constants.INTENT_EXTRA_NAME_NAME, artist.getName());
- if ("root".equals(artist.getId())) {
- args.putSerializable(Constants.FRAGMENT_LIST, (Serializable) entries);
- }
- args.putBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, true);
- fragment.setArguments(args);
- } else {
- fragment = new SelectArtistFragment();
- Bundle args = new Bundle();
- args.putString(Constants.INTENT_EXTRA_NAME_ID, artist.getId());
- args.putString(Constants.INTENT_EXTRA_NAME_NAME, artist.getName());
- fragment.setArguments(args);
- }
-
- replaceFragment(fragment);
- }
- }
-
- @Override
- public void onFinishRefresh() {
- setMusicFolders();
- }
-
- @Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
- super.onCreateOptionsMenu(menu, menuInflater);
-
- if(Util.isOffline(context) || Util.isTagBrowsing(context) || groupId != null) {
- menu.removeItem(R.id.menu_first_level_artist);
- } else {
- if (Util.isFirstLevelArtist(context)) {
- menu.findItem(R.id.menu_first_level_artist).setChecked(true);
- }
- }
- }
-
- @Override
- public int getOptionsMenu() {
- return R.menu.select_artist;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if(super.onOptionsItemSelected(item)) {
- return true;
- }
-
- switch (item.getItemId()) {
- case R.id.menu_first_level_artist:
- toggleFirstLevelArtist();
- break;
- }
-
- return false;
- }
-
- @Override
- public ArrayAdapter getAdapter(List<Artist> objects) {
- createMusicFolderButton();
- return new ArtistAdapter(context, objects);
- }
-
- @Override
- public List<Artist> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception {
- List<Artist> artists;
- if(groupId == null) {
- if (!Util.isOffline(context) && !Util.isTagBrowsing(context)) {
- musicFolders = musicService.getMusicFolders(refresh, context, listener);
-
- // Hide folders option if there is only one
- if (musicFolders.size() == 1) {
- musicFolders = null;
- Util.setSelectedMusicFolderId(context, null);
- }
- }
- String musicFolderId = Util.getSelectedMusicFolderId(context);
-
- Indexes indexes = musicService.getIndexes(musicFolderId, refresh, context, listener);
- artists = new ArrayList<Artist>(indexes.getShortcuts().size() + indexes.getArtists().size());
- artists.addAll(indexes.getShortcuts());
- artists.addAll(indexes.getArtists());
- entries = indexes.getEntries();
- } else {
- artists = new ArrayList<Artist>();
- MusicDirectory dir = musicService.getMusicDirectory(groupId, groupName, refresh, context, listener);
- for(MusicDirectory.Entry entry: dir.getChildren(true, false)) {
- Artist artist = new Artist();
- artist.setId(entry.getId());
- artist.setName(entry.getTitle());
- artist.setStarred(entry.isStarred());
- artists.add(artist);
- }
-
- entries = new ArrayList<MusicDirectory.Entry>();
- entries.addAll(dir.getChildren(false, true));
- if(!entries.isEmpty()) {
- Artist root = new Artist();
- root.setId("root");
- root.setName("Root");
- root.setIndex("#");
- artists.add(root);
- }
- }
-
- return artists;
- }
-
- @Override
- public int getTitleResource() {
- return groupId == null ? R.string.button_bar_browse : 0;
- }
-
- private void createMusicFolderButton() {
- if(folderButton == null) {
- folderButtonParent = context.getLayoutInflater().inflate(R.layout.select_artist_header, listView, false);
- folderName = (TextView) folderButtonParent.findViewById(R.id.select_artist_folder_2);
- listView.addHeaderView(folderButtonParent);
- folderButton = folderButtonParent.findViewById(R.id.select_artist_folder);
- }
-
- if (Util.isOffline(context) || Util.isTagBrowsing(context) || musicFolders == null) {
- folderButton.setVisibility(View.GONE);
- } else {
- folderButton.setVisibility(View.VISIBLE);
- }
- }
-
- @Override
- public void setEmpty(boolean empty) {
- super.setEmpty(empty);
-
- if(empty && !Util.isOffline(context)) {
- createMusicFolderButton();
- setMusicFolders();
-
- objects.clear();
- listView.setAdapter(new ArtistAdapter(context, objects));
- listView.setVisibility(View.VISIBLE);
-
- View view = rootView.findViewById(R.id.tab_progress);
- LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) view.getLayoutParams();
- params.height = 0;
- params.weight = 5;
- view.setLayoutParams(params);
- }
- }
-
- private void setMusicFolders() {
- // Display selected music folder
- if (musicFolders != null) {
- String musicFolderId = Util.getSelectedMusicFolderId(context);
- if (musicFolderId == null) {
- folderName.setText(R.string.select_artist_all_folders);
- } else {
- for (MusicFolder musicFolder : musicFolders) {
- if (musicFolder.getId().equals(musicFolderId)) {
- folderName.setText(musicFolder.getName());
- break;
- }
- }
- }
- }
- }
-
- private void selectFolder() {
- folderButton.showContextMenu();
- }
-
- private void toggleFirstLevelArtist() {
- Util.toggleFirstLevelArtist(context);
- context.invalidateOptionsMenu();
- }
-}
+package github.daneren2005.dsub.fragments;
+
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.os.Bundle;
+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.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.Artist;
+import github.daneren2005.dsub.domain.Indexes;
+import github.daneren2005.dsub.domain.MusicDirectory;
+import github.daneren2005.dsub.domain.MusicFolder;
+import github.daneren2005.dsub.service.MusicService;
+import github.daneren2005.dsub.util.Constants;
+import github.daneren2005.dsub.util.ProgressListener;
+import github.daneren2005.dsub.util.Util;
+import github.daneren2005.dsub.adapter.ArtistAdapter;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+public class SelectArtistFragment extends SelectListFragment<Artist> {
+ private static final String TAG = SelectArtistFragment.class.getSimpleName();
+ private static final int MENU_GROUP_MUSIC_FOLDER = 10;
+
+ private View folderButtonParent;
+ private View folderButton;
+ private TextView folderName;
+ private List<MusicFolder> musicFolders = null;
+ private List<MusicDirectory.Entry> entries;
+ private String groupId;
+ private String groupName;
+
+ public SelectArtistFragment() {
+ super();
+ }
+
+ @Override
+ public void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+
+ if(bundle != null) {
+ musicFolders = (List<MusicFolder>) bundle.getSerializable(Constants.FRAGMENT_LIST2);
+ }
+ artist = true;
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putSerializable(Constants.FRAGMENT_LIST2, (Serializable) musicFolders);
+ }
+
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
+ Bundle args = getArguments();
+ if(args != null) {
+ groupId = args.getString(Constants.INTENT_EXTRA_NAME_ID);
+ groupName = args.getString(Constants.INTENT_EXTRA_NAME_NAME);
+
+ if(groupName != null) {
+ setTitle(groupName);
+ context.invalidateOptionsMenu();
+ }
+ }
+
+ folderButton = null;
+ super.onCreateView(inflater, container, bundle);
+
+ if("4.4.2".equals(Build.VERSION.RELEASE)) {
+ listView.setFastScrollAlwaysVisible(true);
+ }
+
+ if(objects != null && currentTask == null) {
+ if (Util.isOffline(context) || Util.isTagBrowsing(context) || groupId != null) {
+ folderButton.setVisibility(View.GONE);
+ }
+ setMusicFolders();
+ }
+
+ return rootView;
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
+ super.onCreateContextMenu(menu, view, menuInfo);
+
+ AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
+ Object entry = listView.getItemAtPosition(info.position);
+
+ if (entry instanceof Artist) {
+ onCreateContextMenu(menu, view, menuInfo, entry);
+ } else if (info.position == 0) {
+ String musicFolderId = Util.getSelectedMusicFolderId(context);
+ MenuItem menuItem = menu.add(MENU_GROUP_MUSIC_FOLDER, -1, 0, R.string.select_artist_all_folders);
+ if (musicFolderId == null) {
+ menuItem.setChecked(true);
+ }
+ if (musicFolders != null) {
+ for (int i = 0; i < musicFolders.size(); i++) {
+ MusicFolder musicFolder = musicFolders.get(i);
+ menuItem = menu.add(MENU_GROUP_MUSIC_FOLDER, i, i + 1, musicFolder.getName());
+ if (musicFolder.getId().equals(musicFolderId)) {
+ menuItem.setChecked(true);
+ }
+ }
+ }
+ menu.setGroupCheckable(MENU_GROUP_MUSIC_FOLDER, true, true);
+ }
+
+ recreateContextMenu(menu);
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem menuItem) {
+ if(menuItem.getGroupId() != getSupportTag()) {
+ return false;
+ }
+
+ AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
+ Artist artist = (Artist) listView.getItemAtPosition(info.position);
+
+ if (artist != null) {
+ return onContextItemSelected(menuItem, artist);
+ } else if (info.position == 0) {
+ MusicFolder selectedFolder = menuItem.getItemId() == -1 ? null : musicFolders.get(menuItem.getItemId());
+ String musicFolderId = selectedFolder == null ? null : selectedFolder.getId();
+ String musicFolderName = selectedFolder == null ? context.getString(R.string.select_artist_all_folders)
+ : selectedFolder.getName();
+ Util.setSelectedMusicFolderId(context, musicFolderId);
+ folderName.setText(musicFolderName);
+ context.invalidate();
+ }
+
+ return true;
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ if (view == folderButtonParent) {
+ selectFolder();
+ } else {
+ Artist artist = (Artist) parent.getItemAtPosition(position);
+
+ SubsonicFragment fragment;
+ if((Util.isFirstLevelArtist(context) || Util.isOffline(context) || Util.isTagBrowsing(context)) || "root".equals(artist.getId()) || groupId != null) {
+ fragment = new SelectDirectoryFragment();
+ Bundle args = new Bundle();
+ args.putString(Constants.INTENT_EXTRA_NAME_ID, artist.getId());
+ args.putString(Constants.INTENT_EXTRA_NAME_NAME, artist.getName());
+ if ("root".equals(artist.getId())) {
+ args.putSerializable(Constants.FRAGMENT_LIST, (Serializable) entries);
+ }
+ args.putBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, true);
+ fragment.setArguments(args);
+ } else {
+ fragment = new SelectArtistFragment();
+ Bundle args = new Bundle();
+ args.putString(Constants.INTENT_EXTRA_NAME_ID, artist.getId());
+ args.putString(Constants.INTENT_EXTRA_NAME_NAME, artist.getName());
+ fragment.setArguments(args);
+ }
+
+ replaceFragment(fragment);
+ }
+ }
+
+ @Override
+ public void onFinishRefresh() {
+ setMusicFolders();
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
+ super.onCreateOptionsMenu(menu, menuInflater);
+
+ if(Util.isOffline(context) || Util.isTagBrowsing(context) || groupId != null) {
+ menu.removeItem(R.id.menu_first_level_artist);
+ } else {
+ if (Util.isFirstLevelArtist(context)) {
+ menu.findItem(R.id.menu_first_level_artist).setChecked(true);
+ }
+ }
+ }
+
+ @Override
+ public int getOptionsMenu() {
+ return R.menu.select_artist;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if(super.onOptionsItemSelected(item)) {
+ return true;
+ }
+
+ switch (item.getItemId()) {
+ case R.id.menu_first_level_artist:
+ toggleFirstLevelArtist();
+ break;
+ }
+
+ return false;
+ }
+
+ @Override
+ public ArrayAdapter getAdapter(List<Artist> objects) {
+ createMusicFolderButton();
+ return new ArtistAdapter(context, objects);
+ }
+
+ @Override
+ public List<Artist> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception {
+ List<Artist> artists;
+ if(groupId == null) {
+ if (!Util.isOffline(context) && !Util.isTagBrowsing(context)) {
+ musicFolders = musicService.getMusicFolders(refresh, context, listener);
+
+ // Hide folders option if there is only one
+ if (musicFolders.size() == 1) {
+ musicFolders = null;
+ Util.setSelectedMusicFolderId(context, null);
+ }
+ }
+ String musicFolderId = Util.getSelectedMusicFolderId(context);
+
+ Indexes indexes = musicService.getIndexes(musicFolderId, refresh, context, listener);
+ artists = new ArrayList<Artist>(indexes.getShortcuts().size() + indexes.getArtists().size());
+ artists.addAll(indexes.getShortcuts());
+ artists.addAll(indexes.getArtists());
+ entries = indexes.getEntries();
+ } else {
+ artists = new ArrayList<Artist>();
+ MusicDirectory dir = musicService.getMusicDirectory(groupId, groupName, refresh, context, listener);
+ for(MusicDirectory.Entry entry: dir.getChildren(true, false)) {
+ Artist artist = new Artist();
+ artist.setId(entry.getId());
+ artist.setName(entry.getTitle());
+ artist.setStarred(entry.isStarred());
+ artists.add(artist);
+ }
+
+ entries = new ArrayList<MusicDirectory.Entry>();
+ entries.addAll(dir.getChildren(false, true));
+ if(!entries.isEmpty()) {
+ Artist root = new Artist();
+ root.setId("root");
+ root.setName("Root");
+ root.setIndex("#");
+ artists.add(root);
+ }
+ }
+
+ return artists;
+ }
+
+ @Override
+ public int getTitleResource() {
+ return groupId == null ? R.string.button_bar_browse : 0;
+ }
+
+ private void createMusicFolderButton() {
+ if(folderButton == null) {
+ folderButtonParent = context.getLayoutInflater().inflate(R.layout.select_artist_header, listView, false);
+ folderName = (TextView) folderButtonParent.findViewById(R.id.select_artist_folder_2);
+ listView.addHeaderView(folderButtonParent);
+ folderButton = folderButtonParent.findViewById(R.id.select_artist_folder);
+ }
+
+ if (Util.isOffline(context) || Util.isTagBrowsing(context) || musicFolders == null) {
+ folderButton.setVisibility(View.GONE);
+ } else {
+ folderButton.setVisibility(View.VISIBLE);
+ }
+ }
+
+ @Override
+ public void setEmpty(boolean empty) {
+ super.setEmpty(empty);
+
+ if(empty && !Util.isOffline(context)) {
+ createMusicFolderButton();
+ setMusicFolders();
+
+ objects.clear();
+ listView.setAdapter(new ArtistAdapter(context, objects));
+ listView.setVisibility(View.VISIBLE);
+
+ View view = rootView.findViewById(R.id.tab_progress);
+ LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) view.getLayoutParams();
+ params.height = 0;
+ params.weight = 5;
+ view.setLayoutParams(params);
+ }
+ }
+
+ private void setMusicFolders() {
+ // Display selected music folder
+ if (musicFolders != null) {
+ String musicFolderId = Util.getSelectedMusicFolderId(context);
+ if (musicFolderId == null) {
+ folderName.setText(R.string.select_artist_all_folders);
+ } else {
+ for (MusicFolder musicFolder : musicFolders) {
+ if (musicFolder.getId().equals(musicFolderId)) {
+ folderName.setText(musicFolder.getName());
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ private void selectFolder() {
+ folderButton.showContextMenu();
+ }
+
+ private void toggleFirstLevelArtist() {
+ Util.toggleFirstLevelArtist(context);
+ context.invalidateOptionsMenu();
+ }
+}
diff --git a/src/github/daneren2005/dsub/fragments/SelectBookmarkFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectBookmarkFragment.java
index c71d99f6..c71d99f6 100644
--- a/src/github/daneren2005/dsub/fragments/SelectBookmarkFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectBookmarkFragment.java
diff --git a/src/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java
index cfb3af69..841a6369 100644
--- a/src/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java
@@ -1,1597 +1,1597 @@
-package github.daneren2005.dsub.fragments;
-
-import android.annotation.TargetApi;
-import android.app.AlertDialog;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
-import android.support.v4.widget.SwipeRefreshLayout;
-import android.text.Html;
-import android.text.SpannableString;
-import android.text.Spanned;
-import android.text.method.LinkMovementMethod;
-import android.util.Log;
-import android.view.ContextMenu;
-import android.view.Display;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.BaseAdapter;
-import android.widget.GridView;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.ListAdapter;
-import android.widget.ListView;
-import android.widget.RatingBar;
-import android.widget.RelativeLayout;
-import android.widget.TextView;
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.domain.ArtistInfo;
-import github.daneren2005.dsub.domain.MusicDirectory;
-import github.daneren2005.dsub.domain.ServerInfo;
-import github.daneren2005.dsub.domain.Share;
-import github.daneren2005.dsub.service.DownloadService;
-import github.daneren2005.dsub.util.ImageLoader;
-import github.daneren2005.dsub.adapter.AlbumGridAdapter;
-import github.daneren2005.dsub.adapter.EntryAdapter;
-
-import java.io.Serializable;
-import java.util.Iterator;
-import java.util.List;
-
-import github.daneren2005.dsub.activity.DownloadActivity;
-import github.daneren2005.dsub.domain.PodcastEpisode;
-import github.daneren2005.dsub.service.MusicService;
-import github.daneren2005.dsub.service.MusicServiceFactory;
-import github.daneren2005.dsub.service.OfflineException;
-import github.daneren2005.dsub.service.ServerTooOldException;
-import github.daneren2005.dsub.util.Constants;
-import github.daneren2005.dsub.util.LoadingTask;
-import github.daneren2005.dsub.util.Pair;
-import github.daneren2005.dsub.util.SilentBackgroundTask;
-import github.daneren2005.dsub.util.TabBackgroundTask;
-import github.daneren2005.dsub.util.UserUtil;
-import github.daneren2005.dsub.util.Util;
-import github.daneren2005.dsub.adapter.AlbumListAdapter;
-import github.daneren2005.dsub.view.HeaderGridView;
-import github.daneren2005.dsub.view.MyLeadingMarginSpan2;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
-
-import static github.daneren2005.dsub.domain.MusicDirectory.Entry;
-
-public class SelectDirectoryFragment extends SubsonicFragment implements AdapterView.OnItemClickListener {
- private static final String TAG = SelectDirectoryFragment.class.getSimpleName();
-
- private GridView albumList;
- private ListView entryList;
- private Boolean licenseValid;
- private EntryAdapter entryAdapter;
- private List<Entry> albums;
- private List<Entry> entries;
- private boolean albumContext = false;
- private boolean addAlbumHeader = false;
- private LoadTask currentTask;
- private ArtistInfo artistInfo;
- private String artistInfoDelayed;
-
- String id;
- String name;
- Entry directory;
- String playlistId;
- String playlistName;
- boolean playlistOwner;
- String podcastId;
- String podcastName;
- String podcastDescription;
- String albumListType;
- String albumListExtra;
- int albumListSize;
- boolean refreshListing = false;
- boolean showAll = false;
- boolean restoredInstance = false;
- boolean lookupParent = false;
- boolean largeAlbums = false;
- boolean topTracks = false;
- String lookupEntry;
-
- public SelectDirectoryFragment() {
- super();
- }
-
- @Override
- public void onCreate(Bundle bundle) {
- super.onCreate(bundle);
- if(bundle != null) {
- entries = (List<Entry>) bundle.getSerializable(Constants.FRAGMENT_LIST);
- albums = (List<Entry>) bundle.getSerializable(Constants.FRAGMENT_LIST2);
- artistInfo = (ArtistInfo) bundle.getSerializable(Constants.FRAGMENT_EXTRA);
- restoredInstance = true;
- }
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putSerializable(Constants.FRAGMENT_LIST, (Serializable) entries);
- outState.putSerializable(Constants.FRAGMENT_LIST2, (Serializable) albums);
- outState.putSerializable(Constants.FRAGMENT_EXTRA, (Serializable) artistInfo);
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
- Bundle args = getArguments();
- if(args != null) {
- id = args.getString(Constants.INTENT_EXTRA_NAME_ID);
- name = args.getString(Constants.INTENT_EXTRA_NAME_NAME);
- directory = (Entry) args.getSerializable(Constants.INTENT_EXTRA_NAME_DIRECTORY);
- playlistId = args.getString(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID);
- playlistName = args.getString(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME);
- playlistOwner = args.getBoolean(Constants.INTENT_EXTRA_NAME_PLAYLIST_OWNER, false);
- podcastId = args.getString(Constants.INTENT_EXTRA_NAME_PODCAST_ID);
- podcastName = args.getString(Constants.INTENT_EXTRA_NAME_PODCAST_NAME);
- podcastDescription = args.getString(Constants.INTENT_EXTRA_NAME_PODCAST_DESCRIPTION);
- Object shareObj = args.getSerializable(Constants.INTENT_EXTRA_NAME_SHARE);
- share = (shareObj != null) ? (Share) shareObj : null;
- albumListType = args.getString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE);
- albumListExtra = args.getString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_EXTRA);
- albumListSize = args.getInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 0);
- refreshListing = args.getBoolean(Constants.INTENT_EXTRA_REFRESH_LISTINGS);
- artist = args.getBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, false);
- lookupEntry = args.getString(Constants.INTENT_EXTRA_SEARCH_SONG);
- topTracks = args.getBoolean(Constants.INTENT_EXTRA_TOP_TRACKS);
- showAll = args.getBoolean(Constants.INTENT_EXTRA_SHOW_ALL);
-
- String childId = args.getString(Constants.INTENT_EXTRA_NAME_CHILD_ID);
- if(childId != null) {
- id = childId;
- lookupParent = true;
- }
- if(entries == null) {
- entries = (List<Entry>) args.getSerializable(Constants.FRAGMENT_LIST);
- albums = (List<Entry>) args.getSerializable(Constants.FRAGMENT_LIST2);
-
- if(albums == null) {
- albums = new ArrayList<Entry>();
- }
- }
- }
-
- rootView = inflater.inflate(R.layout.select_album, container, false);
-
- refreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.refresh_layout);
- refreshLayout.setOnRefreshListener(this);
-
- entryList = (ListView) rootView.findViewById(R.id.select_album_entries);
- entryList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
- entryList.setOnItemClickListener(this);
- setupScrollList(entryList);
-
- if(Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_LARGE_ALBUM_ART, true)) {
- largeAlbums = true;
- }
-
- if(albumListType == null || "starred".equals(albumListType) || !largeAlbums) {
- albumList = (GridView) inflater.inflate(R.layout.unscrollable_grid_view, entryList, false);
- addAlbumHeader = true;
- } else {
- ViewGroup rootGroup = (ViewGroup) rootView.findViewById(R.id.select_album_layout);
- albumList = (GridView) inflater.inflate(R.layout.grid_view, rootGroup, false);
- rootGroup.removeView(entryList);
- rootGroup.addView(albumList);
-
- setupScrollList(albumList);
- }
- registerForContextMenu(entryList);
- setupAlbumList();
-
- if(entries == null) {
- if(primaryFragment || secondaryFragment) {
- load(false);
- } else {
- invalidated = true;
- }
- } else {
- licenseValid = true;
- finishLoading();
- }
-
- if(name != null) {
- setTitle(name);
- }
-
- return rootView;
- }
-
- @Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
- if(licenseValid == null) {
- menuInflater.inflate(R.menu.empty, menu);
- } else if(albumListType != null && !"starred".equals(albumListType)) {
- menuInflater.inflate(R.menu.select_album_list, menu);
- } else if(artist && !showAll) {
- menuInflater.inflate(R.menu.select_album, menu);
-
- if(!ServerInfo.isMadsonic(context)) {
- menu.removeItem(R.id.menu_top_tracks);
- }
- if(!ServerInfo.checkServerVersion(context, "1.11") || (id != null && "root".equals(id))) {
- menu.removeItem(R.id.menu_radio);
- menu.removeItem(R.id.menu_similar_artists);
- } else if(ServerInfo.isMadsonic(context)) {
- menu.removeItem(R.id.menu_similar_artists);
- }
- } else {
- if(podcastId == null) {
- if(Util.isOffline(context)) {
- menuInflater.inflate(R.menu.select_song_offline, menu);
- }
- else {
- menuInflater.inflate(R.menu.select_song, menu);
-
- if(playlistId == null || !playlistOwner) {
- menu.removeItem(R.id.menu_remove_playlist);
- }
- }
-
- SharedPreferences prefs = Util.getPreferences(context);
- if(!prefs.getBoolean(Constants.PREFERENCES_KEY_MENU_PLAY_NEXT, true)) {
- menu.setGroupVisible(R.id.hide_play_next, false);
- }
- if(!prefs.getBoolean(Constants.PREFERENCES_KEY_MENU_PLAY_LAST, true)) {
- menu.setGroupVisible(R.id.hide_play_last, false);
- }
- } else {
- if(Util.isOffline(context)) {
- menuInflater.inflate(R.menu.select_podcast_episode_offline, menu);
- }
- else {
- menuInflater.inflate(R.menu.select_podcast_episode, menu);
-
- if(!UserUtil.canPodcast()) {
- menu.removeItem(R.id.menu_download_all);
- }
- }
- }
- }
-
- if("starred".equals(albumListType)) {
- menuInflater.inflate(R.menu.unstar, menu);
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.menu_play_now:
- playNow(false, false);
- return true;
- case R.id.menu_play_last:
- playNow(false, true);
- return true;
- case R.id.menu_play_next:
- playNow(false, true, true);
- return true;
- case R.id.menu_shuffle:
- playNow(true, false);
- return true;
- case R.id.menu_download:
- downloadBackground(false);
- selectAll(false, false);
- return true;
- case R.id.menu_cache:
- downloadBackground(true);
- selectAll(false, false);
- return true;
- case R.id.menu_delete:
- delete();
- selectAll(false, false);
- return true;
- case R.id.menu_add_playlist:
- if(getSelectedSongs().isEmpty()) {
- selectAll(true, false);
- }
- addToPlaylist(getSelectedSongs());
- return true;
- case R.id.menu_remove_playlist:
- removeFromPlaylist(playlistId, playlistName, getSelectedIndexes());
- return true;
- case R.id.menu_download_all:
- downloadAllPodcastEpisodes();
- return true;
- case R.id.menu_show_all:
- setShowAll();
- return true;
- case R.id.menu_unstar:
- unstarSelected();
- return true;
- case R.id.menu_top_tracks:
- showTopTracks();
- return true;
- case R.id.menu_similar_artists:
- showSimilarArtists(id);
- return true;
- case R.id.menu_radio:
- startArtistRadio(id);
- return true;
- }
-
- return super.onOptionsItemSelected(item);
-
- }
-
- @Override
- public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, view, menuInfo);
-
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
-
- Entry entry;
- if(view.getId() == R.id.select_album_entries) {
- if(info.position == 0) {
- return;
- }
- entry = (Entry) entryList.getItemAtPosition(info.position);
- // When List has Grid embedded in header, this is called against the header as well
- if(entry != null) {
- albumContext = false;
- }
- } else {
- entry = (Entry) albumList.getItemAtPosition(info.position);
- albumContext = true;
- }
-
- // Don't try to display a context menu if error here
- if(entry == null) {
- return;
- }
-
- onCreateContextMenu(menu, view, menuInfo, entry);
- if(!entry.isVideo() && !Util.isOffline(context) && (playlistId == null || !playlistOwner) && (podcastId == null || Util.isOffline(context) && podcastId != null)) {
- menu.removeItem(R.id.song_menu_remove_playlist);
- }
- // Remove show artists if parent is not set and if not on a album list
- if((albumListType == null || (entry.getParent() == null && entry.getArtistId() == null)) && !Util.isOffline(context)) {
- menu.removeItem(R.id.album_menu_show_artist);
- }
- if(podcastId != null && !Util.isOffline(context)) {
- if(UserUtil.canPodcast()) {
- String status = ((PodcastEpisode)entry).getStatus();
- if("completed".equals(status)) {
- menu.removeItem(R.id.song_menu_server_download);
- }
- } else {
- menu.removeItem(R.id.song_menu_server_download);
- menu.removeItem(R.id.song_menu_server_delete);
- }
- }
-
- recreateContextMenu(menu);
- }
-
- @Override
- public boolean onContextItemSelected(MenuItem menuItem) {
- if(menuItem.getGroupId() != getSupportTag()) {
- return false;
- }
-
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
- Object selectedItem;
- int headers = entryList.getHeaderViewsCount();
- if(albumContext) {
- selectedItem = albumList.getItemAtPosition(info.position);
- } else {
- if(info.position == 0) {
- return false;
- }
- selectedItem = entries.get(info.position - headers);
- }
-
- if(Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_PLAY_NOW_AFTER, false) && menuItem.getItemId() == R.id.song_menu_play_now) {
- List<Entry> songs = new ArrayList<Entry>();
- Iterator it = entries.listIterator(info.position - headers);
- while(it.hasNext()) {
- songs.add((Entry) it.next());
- }
-
- playNow(songs);
- return true;
- }
-
- if(onContextItemSelected(menuItem, selectedItem)) {
- return true;
- }
-
- switch (menuItem.getItemId()) {
- case R.id.song_menu_remove_playlist:
- removeFromPlaylist(playlistId, playlistName, Arrays.<Integer>asList(info.position - headers));
- break;
- case R.id.song_menu_server_download:
- downloadPodcastEpisode((PodcastEpisode)selectedItem);
- break;
- case R.id.song_menu_server_delete:
- deletePodcastEpisode((PodcastEpisode)selectedItem);
- break;
- }
-
- return true;
- }
-
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- if (position >= 0) {
- Entry entry = (Entry) parent.getItemAtPosition(position);
- if (entry.isDirectory()) {
- SubsonicFragment fragment = new SelectDirectoryFragment();
- Bundle args = new Bundle();
- args.putString(Constants.INTENT_EXTRA_NAME_ID, entry.getId());
- args.putString(Constants.INTENT_EXTRA_NAME_NAME, entry.getTitle());
- args.putSerializable(Constants.INTENT_EXTRA_NAME_DIRECTORY, entry);
- if ("newest".equals(albumListType)) {
- args.putBoolean(Constants.INTENT_EXTRA_REFRESH_LISTINGS, true);
- }
- if(entry.getArtist() == null && entry.getParent() == null) {
- args.putBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, true);
- }
- fragment.setArguments(args);
-
- replaceFragment(fragment, true);
- } else if (entry.isVideo()) {
- playVideo(entry);
- } else if(entry instanceof PodcastEpisode) {
- String status = ((PodcastEpisode)entry).getStatus();
- if("error".equals(status)) {
- Util.toast(context, R.string.select_podcasts_error);
- return;
- } else if(!"completed".equals(status)) {
- Util.toast(context, R.string.select_podcasts_skipped);
- return;
- }
-
- playNow(Arrays.asList(entry));
- }
- }
- }
-
- @Override
- protected void refresh(boolean refresh) {
- if(!"root".equals(id)) {
- load(refresh);
- }
- }
-
- private void load(boolean refresh) {
- if(refreshListing) {
- refresh = true;
- }
-
- if(currentTask != null) {
- currentTask.cancel();
- }
-
- entryList.setVisibility(View.INVISIBLE);
- if (playlistId != null) {
- getPlaylist(playlistId, playlistName, refresh);
- } else if(podcastId != null) {
- getPodcast(podcastId, podcastName, refresh);
- } else if (share != null) {
- if(showAll) {
- getRecursiveMusicDirectory(share.getId(), share.getName(), refresh);
- } else {
- getShare(share, refresh);
- }
- } else if (albumListType != null) {
- getAlbumList(albumListType, albumListSize);
- } else {
- if(showAll) {
- getRecursiveMusicDirectory(id, name, refresh);
- } else if(topTracks) {
- getTopTracks(id, name, refresh);
- } else {
- getMusicDirectory(id, name, refresh);
- }
- }
- }
-
- private void getMusicDirectory(final String id, final String name, final boolean refresh) {
- setTitle(name);
-
- new LoadTask(refresh) {
- @Override
- protected MusicDirectory load(MusicService service) throws Exception {
- MusicDirectory dir = getMusicDirectory(id, name, refresh, service, this);
-
- if(lookupParent && dir.getParent() != null) {
- dir = getMusicDirectory(dir.getParent(), name, refresh, service, this);
-
- // Update the fragment pointers so other stuff works correctly
- SelectDirectoryFragment.this.id = dir.getId();
- SelectDirectoryFragment.this.name = dir.getName();
- } else if(id != null && directory == null && dir.getParent() != null && !artist) {
- // View Album, try to lookup parent to get a complete entry to use for starring
- MusicDirectory parentDir = getMusicDirectory(dir.getParent(), name, refresh, true, service, this);
- for(Entry child: parentDir.getChildren()) {
- if(id.equals(child.getId())) {
- directory = child;
- break;
- }
- }
- }
-
- return dir;
- }
-
- @Override
- protected void done(Pair<MusicDirectory, Boolean> result) {
- SelectDirectoryFragment.this.name = result.getFirst().getName();
- setTitle(SelectDirectoryFragment.this.name);
- super.done(result);
- }
- }.execute();
- }
-
- private void getRecursiveMusicDirectory(final String id, final String name, final boolean refresh) {
- setTitle(name);
-
- new LoadTask(refresh) {
- @Override
- protected MusicDirectory load(MusicService service) throws Exception {
- MusicDirectory root;
- if(share == null) {
- root = getMusicDirectory(id, name, refresh, service, this);
- } else {
- root = share.getMusicDirectory();
- }
- List<Entry> songs = new ArrayList<Entry>();
- getSongsRecursively(root, songs);
- root.replaceChildren(songs);
- return root;
- }
-
- private void getSongsRecursively(MusicDirectory parent, List<Entry> songs) throws Exception {
- songs.addAll(parent.getChildren(false, true));
- for (Entry dir : parent.getChildren(true, false)) {
- MusicService musicService = MusicServiceFactory.getMusicService(context);
-
- MusicDirectory musicDirectory;
- if(Util.isTagBrowsing(context) && !Util.isOffline(context)) {
- musicDirectory = musicService.getAlbum(dir.getId(), dir.getTitle(), false, context, this);
- } else {
- musicDirectory = musicService.getMusicDirectory(dir.getId(), dir.getTitle(), false, context, this);
- }
- getSongsRecursively(musicDirectory, songs);
- }
- }
-
- @Override
- protected void done(Pair<MusicDirectory, Boolean> result) {
- SelectDirectoryFragment.this.name = result.getFirst().getName();
- setTitle(SelectDirectoryFragment.this.name);
- super.done(result);
- }
- }.execute();
- }
-
- private void getPlaylist(final String playlistId, final String playlistName, final boolean refresh) {
- setTitle(playlistName);
-
- new LoadTask(refresh) {
- @Override
- protected MusicDirectory load(MusicService service) throws Exception {
- return service.getPlaylist(refresh, playlistId, playlistName, context, this);
- }
- }.execute();
- }
-
- private void getPodcast(final String podcastId, final String podcastName, final boolean refresh) {
- setTitle(podcastName);
-
- new LoadTask(refresh) {
- @Override
- protected MusicDirectory load(MusicService service) throws Exception {
- return service.getPodcastEpisodes(refresh, podcastId, context, this);
- }
- }.execute();
- }
-
- private void getShare(final Share share, final boolean refresh) {
- setTitle(share.getName());
-
- new LoadTask(refresh) {
- @Override
- protected MusicDirectory load(MusicService service) throws Exception {
- return share.getMusicDirectory();
- }
- }.execute();
- }
-
- private void getTopTracks(final String id, final String name, final boolean refresh) {
- setTitle(name);
-
- new LoadTask(refresh) {
- @Override
- protected MusicDirectory load(MusicService service) throws Exception {
- return service.getTopTrackSongs(name, 20, context, this);
- }
- }.execute();
- }
-
- private void getAlbumList(final String albumListType, final int size) {
- 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("genres".equals(albumListType) || "years".equals(albumListType)) {
- setTitle(albumListExtra);
- } else if("alphabeticalByName".equals(albumListType)) {
- setTitle(R.string.main_albums_alphabetical);
- }
-
- new LoadTask(true) {
- @Override
- protected MusicDirectory load(MusicService service) throws Exception {
- MusicDirectory result;
- if ("starred".equals(albumListType)) {
- result = service.getStarredList(context, this);
- } else if(("genres".equals(albumListType) && ServerInfo.checkServerVersion(context, "1.10.0")) || "years".equals(albumListType)) {
- result = service.getAlbumList(albumListType, albumListExtra, size, 0, context, this);
- if(result.getChildrenSize() == 0 && "genres".equals(albumListType)) {
- SelectDirectoryFragment.this.albumListType = "genres-songs";
- result = service.getSongsByGenre(albumListExtra, size, 0, context, this);
- }
- } else if("genres".equals(albumListType) || "genres-songs".equals(albumListType)) {
- result = service.getSongsByGenre(albumListExtra, size, 0, context, this);
- } else {
- result = service.getAlbumList(albumListType, size, 0, context, this);
- }
- return result;
- }
- }.execute();
- }
-
- private abstract class LoadTask extends TabBackgroundTask<Pair<MusicDirectory, Boolean>> {
- private boolean refresh;
-
- public LoadTask(boolean refresh) {
- super(SelectDirectoryFragment.this);
- this.refresh = refresh;
-
- currentTask = this;
- }
-
- protected abstract MusicDirectory load(MusicService service) throws Exception;
-
- @Override
- protected Pair<MusicDirectory, Boolean> doInBackground() throws Throwable {
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- MusicDirectory dir = load(musicService);
- licenseValid = musicService.isLicenseValid(context, this);
-
- albums = dir.getChildren(true, false);
- if(largeAlbums) {
- entries = dir.getChildren(false, true);
- } else {
- entries = dir.getChildren();
- }
-
- // This isn't really an artist if no albums on it!
- if(albums.size() == 0) {
- artist = false;
- }
-
- // If artist, we want to load the artist info to use later
- if(artist && ServerInfo.hasArtistInfo(context) && !Util.isOffline(context)) {
- try {
- String artistId;
- if(id.indexOf(';') == -1) {
- artistId = id;
- } else {
- artistId = id.substring(0, id.indexOf(';'));
- }
-
- artistInfo = musicService.getArtistInfo(artistId, refresh, false, context, this);
-
- if(artistInfo == null) {
- artistInfoDelayed = artistId;
- }
- } catch(Exception e) {
- Log.w(TAG, "Failed to get Artist Info even though it should be supported");
- }
- }
-
- return new Pair<MusicDirectory, Boolean>(dir, licenseValid);
- }
-
- @Override
- protected void done(Pair<MusicDirectory, Boolean> result) {
- finishLoading();
- currentTask = null;
- }
- }
-
- private void finishLoading() {
- // Show header if not album list type and not root and not artist
- // For Subsonic 5.1+ display a header for artists with getArtistInfo data if it exists
- View header = null;
- if(albumListType == null && !"root".equals(id) && (!artist || artistInfo != null || artistInfoDelayed != null)) {
- header = createHeader();
-
- if(header != null && artistInfoDelayed != null) {
- final View finalHeader = header.findViewById(R.id.select_album_header);
- final View headerProgress = header.findViewById(R.id.header_progress);
-
- finalHeader.setVisibility(View.INVISIBLE);
- headerProgress.setVisibility(View.VISIBLE);
-
- new SilentBackgroundTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- artistInfo = musicService.getArtistInfo(artistInfoDelayed, false, true, context, this);
-
- return null;
- }
-
- @Override
- protected void done(Void result) {
- /*if(albumList instanceof HeaderGridView) {
- HeaderGridView headerGridView = (HeaderGridView) albumList;
- headerGridView.invalidateRowHeight();
- ((BaseAdapter) headerGridView.getAdapter()).notifyDataSetChanged();
- }*/
-
- setupCoverArt(finalHeader);
- setupTextDisplay(finalHeader);
- setupButtonEvents(finalHeader);
-
- finalHeader.setVisibility(View.VISIBLE);
- headerProgress.setVisibility(View.GONE);
- }
- }.execute();
- }
-
- // Only add header to entry list if we aren't going recreate album grid as root anyways
- if(header != null && entryList != null && (!addAlbumHeader || entries.size() > 0)) {
- entryList.addHeaderView(header, null, false);
- header = null;
- }
- }
-
- // Needs to be added here, GB crashes if you to try to remove the header view before adapter is set
- if(addAlbumHeader) {
- if(entries.size() > 0 || playlistId != null || podcastId != null) {
- entryList.addHeaderView(albumList);
- } else {
- ViewGroup rootGroup = (ViewGroup) rootView.findViewById(R.id.select_album_layout);
- albumList = (GridView) context.getLayoutInflater().inflate(R.layout.grid_view, rootGroup, false);
- rootGroup.removeView(entryList);
- rootGroup.addView(albumList);
-
- setupScrollList(albumList);
- setupAlbumList();
-
- // This should only not be null for a artist with only albums
- if(header != null) {
- HeaderGridView headerGridView = (HeaderGridView) albumList;
- headerGridView.addHeaderView(header);
- }
- }
- addAlbumHeader = false;
- }
-
- boolean validData = !entries.isEmpty() || !albums.isEmpty();
- if(!validData) {
- setEmpty(true);
- }
- // Always going to have entries in entryAdapter
- entryAdapter = new EntryAdapter(context, getImageLoader(), entries, (podcastId == null));
- ListAdapter listAdapter = entryAdapter;
- // Song-only genre needs to always be entry list + infinite adapter
- if("genres-songs".equals(albumListType)) {
- ViewGroup rootGroup = (ViewGroup) rootView.findViewById(R.id.select_album_layout);
- if(rootGroup.findViewById(R.id.gridview) != null && largeAlbums) {
- rootGroup.removeView(albumList);
- rootGroup.addView(entryList);
- }
-
- listAdapter = new AlbumListAdapter(context, entryAdapter, albumListType, albumListExtra, albumListSize);
- } else if(albumListType == null || "starred".equals(albumListType)) {
- // Only set standard album adapter if not album list and largeAlbums is true
- if(largeAlbums) {
- albumList.setAdapter(new AlbumGridAdapter(context, getImageLoader(), albums, !artist));
- }
- } else {
- // If album list, use infinite adapters for either depending on whether or not largeAlbums is true
- if(largeAlbums) {
- albumList.setAdapter(new AlbumListAdapter(context, new AlbumGridAdapter(context, getImageLoader(), albums, true), albumListType, albumListExtra, albumListSize));
- } else {
- listAdapter = new AlbumListAdapter(context, entryAdapter, albumListType, albumListExtra, albumListSize);
- }
- }
- entryList.setAdapter(listAdapter);
- if(validData) {
- entryList.setVisibility(View.VISIBLE);
- }
- context.supportInvalidateOptionsMenu();
-
- if(lookupEntry != null) {
- for(int i = 0; i < entries.size(); i++) {
- if(lookupEntry.equals(entries.get(i).getTitle())) {
- entryList.setSelection(i + entryList.getHeaderViewsCount());
- lookupEntry = null;
- break;
- }
- }
- }
-
- Bundle args = getArguments();
- boolean playAll = args.getBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, false);
- if (playAll && !restoredInstance) {
- playAll(args.getBoolean(Constants.INTENT_EXTRA_NAME_SHUFFLE, false), false);
- }
- }
-
- private void setupAlbumList() {
- albumList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- Entry entry = (Entry) parent.getItemAtPosition(position);
- SubsonicFragment fragment = new SelectDirectoryFragment();
- Bundle args = new Bundle();
- args.putString(Constants.INTENT_EXTRA_NAME_ID, entry.getId());
- args.putString(Constants.INTENT_EXTRA_NAME_NAME, entry.getTitle());
- args.putSerializable(Constants.INTENT_EXTRA_NAME_DIRECTORY, entry);
- if ("newest".equals(albumListType)) {
- args.putBoolean(Constants.INTENT_EXTRA_REFRESH_LISTINGS, true);
- }
- if(entry.getArtist() == null && entry.getParent() == null) {
- args.putBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, true);
- }
- fragment.setArguments(args);
-
- replaceFragment(fragment, true);
- }
- });
-
- registerForContextMenu(entryList);
- registerForContextMenu(albumList);
- }
-
- private void playNow(final boolean shuffle, final boolean append) {
- playNow(shuffle, append, false);
- }
- private void playNow(final boolean shuffle, final boolean append, final boolean playNext) {
- if(getSelectedSongs().size() > 0) {
- download(append, false, !append, playNext, shuffle);
- selectAll(false, false);
- }
- else {
- playAll(shuffle, append);
- }
- }
- private void playAll(final boolean shuffle, final boolean append) {
- boolean hasSubFolders = false;
- for (int i = 0; i < entryList.getCount(); i++) {
- Entry entry = (Entry) entryList.getItemAtPosition(i);
- if (entry != null && entry.isDirectory()) {
- hasSubFolders = true;
- break;
- }
- }
- if(albums.size() > 0) {
- hasSubFolders = true;
- }
-
- if (hasSubFolders && (id != null || share != null || "starred".equals(albumListType))) {
- downloadRecursively(id, false, append, !append, shuffle, false);
- } else if(hasSubFolders && albumListType != null) {
- downloadRecursively(albums, shuffle, append);
- } else {
- selectAll(true, false);
- download(append, false, !append, false, shuffle);
- selectAll(false, false);
- }
- }
-
- private void selectAll(boolean selected, boolean toast) {
- int count = entryList.getCount();
- int selectedCount = 0;
- for (int i = 0; i < count; i++) {
- Entry entry = (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(context, context.getString(toastResId, selectedCount));
- }
- }
-
- private List<Entry> getSelectedSongs() {
- List<Entry> songs = new ArrayList<Entry>(10);
- int count = entryList.getCount();
- for (int i = 0; i < count; i++) {
- if (entryList.isItemChecked(i)) {
- Entry entry = (Entry) entryList.getItemAtPosition(i);
- // Don't try to add directories or 1-starred songs
- if(!entry.isDirectory() && entry.getRating() != 1) {
- songs.add(entry);
- }
- }
- }
- return songs;
- }
-
- private List<Integer> getSelectedIndexes() {
- List<Integer> indexes = new ArrayList<Integer>();
-
- int count = entryList.getCount();
- int headers = entryList.getHeaderViewsCount();
- for (int i = 0; i < count; i++) {
- if (entryList.isItemChecked(i)) {
- indexes.add(i - headers);
- }
- }
-
- return indexes;
- }
-
- private void download(final boolean append, final boolean save, final boolean autoplay, final boolean playNext, final boolean shuffle) {
- if (getDownloadService() == null) {
- return;
- }
-
- final List<Entry> songs = getSelectedSongs();
- warnIfStorageUnavailable();
-
- // Conditions for using play now button
- if(!append && !save && autoplay && !playNext && !shuffle) {
- // Call playNow which goes through and tries to use bookmark information
- playNow(songs, playlistName, playlistId);
- return;
- }
-
- LoadingTask<Void> onValid = new LoadingTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- if (!append) {
- getDownloadService().clear();
- }
-
- getDownloadService().download(songs, save, autoplay, playNext, shuffle);
- if (playlistName != null) {
- getDownloadService().setSuggestedPlaylistName(playlistName, playlistId);
- } else {
- getDownloadService().setSuggestedPlaylistName(null, null);
- }
- return null;
- }
-
- @Override
- protected void done(Void result) {
- if (autoplay) {
- Util.startActivityWithoutTransition(context, DownloadActivity.class);
- } else if (save) {
- Util.toast(context,
- context.getResources().getQuantityString(R.plurals.select_album_n_songs_downloading, songs.size(), songs.size()));
- } else if (append) {
- Util.toast(context,
- context.getResources().getQuantityString(R.plurals.select_album_n_songs_added, songs.size(), songs.size()));
- }
- }
- };
-
- checkLicenseAndTrialPeriod(onValid);
- }
- private void downloadBackground(final boolean save) {
- if(playlistId != null) {
- selectAll(true, false);
- }
-
- List<Entry> songs = getSelectedSongs();
- if(songs.isEmpty()) {
- // Get both songs and albums
- downloadRecursively(id, save, false, false, false, true);
- } else {
- downloadBackground(save, songs);
- }
- }
- private void downloadBackground(final boolean save, final List<Entry> songs) {
- if (getDownloadService() == null) {
- return;
- }
-
- warnIfStorageUnavailable();
- LoadingTask<Void> onValid = new LoadingTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- getDownloadService().downloadBackground(songs, save);
- return null;
- }
-
- @Override
- protected void done(Void result) {
- Util.toast(context, context.getResources().getQuantityString(R.plurals.select_album_n_songs_downloading, songs.size(), songs.size()));
- }
- };
-
- checkLicenseAndTrialPeriod(onValid);
- }
-
- private void delete() {
- List<Entry> songs = getSelectedSongs();
- if(songs.isEmpty()) {
- selectAll(true, false);
- songs = getSelectedSongs();
-
- // Also delete all directories
- for(Entry album: albums) {
- deleteRecursively(album);
- }
- }
- if (getDownloadService() != null) {
- getDownloadService().delete(songs);
- }
- }
-
- public void removeFromPlaylist(final String id, final String name, final List<Integer> indexes) {
- new LoadingTask<Void>(context, true) {
- @Override
- protected Void doInBackground() throws Throwable {
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- musicService.removeFromPlaylist(id, indexes, context, null);
- return null;
- }
-
- @Override
- protected void done(Void result) {
- for(int i = indexes.size() - 1; i >= 0; i--) {
- entryList.setItemChecked(indexes.get(i) + 1, false);
- entryAdapter.removeAt(indexes.get(i));
- }
- entryAdapter.notifyDataSetChanged();
- Util.toast(context, context.getResources().getString(R.string.removed_playlist, indexes.size(), name));
- }
-
- @Override
- protected void error(Throwable error) {
- String msg;
- if (error instanceof OfflineException || error instanceof ServerTooOldException) {
- msg = getErrorMessage(error);
- } else {
- msg = context.getResources().getString(R.string.updated_playlist_error, name) + " " + getErrorMessage(error);
- }
-
- Util.toast(context, msg, false);
- }
- }.execute();
- }
-
- public void downloadAllPodcastEpisodes() {
- new LoadingTask<Void>(context, true) {
- @Override
- protected Void doInBackground() throws Throwable {
- MusicService musicService = MusicServiceFactory.getMusicService(context);
-
- for(int i = 0; i < entries.size(); i++) {
- PodcastEpisode episode = (PodcastEpisode) entries.get(i);
- if("skipped".equals(episode.getStatus())) {
- musicService.downloadPodcastEpisode(episode.getEpisodeId(), context, null);
- }
- }
- return null;
- }
-
- @Override
- protected void done(Void result) {
- Util.toast(context, context.getResources().getString(R.string.select_podcasts_downloading, podcastName));
- }
-
- @Override
- protected void error(Throwable error) {
- Util.toast(context, getErrorMessage(error), false);
- }
- }.execute();
- }
-
- public void downloadPodcastEpisode(final PodcastEpisode episode) {
- new LoadingTask<Void>(context, true) {
- @Override
- protected Void doInBackground() throws Throwable {
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- musicService.downloadPodcastEpisode(episode.getEpisodeId(), context, null);
- return null;
- }
-
- @Override
- protected void done(Void result) {
- Util.toast(context, context.getResources().getString(R.string.select_podcasts_downloading, episode.getTitle()));
- }
-
- @Override
- protected void error(Throwable error) {
- Util.toast(context, getErrorMessage(error), false);
- }
- }.execute();
- }
-
- public void deletePodcastEpisode(final PodcastEpisode episode) {
- Util.confirmDialog(context, R.string.common_delete, episode.getTitle(), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- new LoadingTask<Void>(context, true) {
- @Override
- protected Void doInBackground() throws Throwable {
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- musicService.deletePodcastEpisode(episode.getEpisodeId(), episode.getParent(), null, context);
- if (getDownloadService() != null) {
- List<Entry> episodeList = new ArrayList<Entry>(1);
- episodeList.add(episode);
- getDownloadService().delete(episodeList);
- }
- return null;
- }
-
- @Override
- protected void done(Void result) {
- entries.remove(episode);
- entryAdapter.notifyDataSetChanged();
- }
-
- @Override
- protected void error(Throwable error) {
- Log.w(TAG, "Failed to delete podcast episode", error);
- Util.toast(context, getErrorMessage(error), false);
- }
- }.execute();
- }
- });
- }
-
- public void unstarSelected() {
- List<Entry> selected = getSelectedSongs();
- if(selected.size() == 0) {
- selected = entries;
- }
- if(selected.size() == 0) {
- return;
- }
- final List<Entry> unstar = new ArrayList<Entry>();
- unstar.addAll(selected);
-
- new LoadingTask<Void>(context, true) {
- @Override
- protected Void doInBackground() throws Throwable {
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- List<Entry> entries = new ArrayList<Entry>();
- List<Entry> artists = new ArrayList<Entry>();
- List<Entry> albums = new ArrayList<Entry>();
- for(Entry entry: unstar) {
- if(entry.isDirectory()) {
- if(entry.isAlbum()) {
- albums.add(entry);
- } else {
- artists.add(entry);
- }
- } else {
- entries.add(entry);
- }
- }
- musicService.setStarred(entries, artists, albums, false, this, context);
-
- for(Entry entry: unstar) {
- new EntryInstanceUpdater(entry) {
- @Override
- public void update(Entry found) {
- found.setStarred(false);
- }
- }.execute();
- }
-
- return null;
- }
-
- @Override
- protected void done(Void result) {
- Util.toast(context, context.getResources().getString(R.string.starring_content_unstarred, Integer.toString(unstar.size())));
-
- for(Entry entry: unstar) {
- entries.remove(entry);
- }
- entryAdapter.notifyDataSetChanged();
- selectAll(false, false);
- }
-
- @Override
- protected void error(Throwable error) {
- String msg;
- if (error instanceof OfflineException || error instanceof ServerTooOldException) {
- msg = getErrorMessage(error);
- } else {
- msg = context.getResources().getString(R.string.starring_content_error, Integer.toString(unstar.size())) + " " + getErrorMessage(error);
- }
-
- Util.toast(context, msg, false);
- }
- }.execute();
- }
-
- private void checkLicenseAndTrialPeriod(LoadingTask onValid) {
- if (licenseValid) {
- onValid.execute();
- return;
- }
-
- int trialDaysLeft = Util.getRemainingTrialDays(context);
- 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(context, context.getResources().getString(R.string.select_album_not_licensed, trialDaysLeft));
- onValid.execute();
- }
- }
-
- private void showDonationDialog(int trialDaysLeft, final LoadingTask onValid) {
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
- 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(context.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.execute();
- }
- }
- });
-
- builder.create().show();
- }
-
- private void showTopTracks() {
- SubsonicFragment fragment = new SelectDirectoryFragment();
- Bundle args = new Bundle(getArguments());
- args.putBoolean(Constants.INTENT_EXTRA_TOP_TRACKS, true);
- fragment.setArguments(args);
-
- replaceFragment(fragment, true);
- }
-
- private void setShowAll() {
- SubsonicFragment fragment = new SelectDirectoryFragment();
- Bundle args = new Bundle(getArguments());
- args.putBoolean(Constants.INTENT_EXTRA_SHOW_ALL, true);
- fragment.setArguments(args);
-
- replaceFragment(fragment, true);
- }
-
- private void showSimilarArtists(String artistId) {
- SubsonicFragment fragment = new SimilarArtistFragment();
- Bundle args = new Bundle();
- args.putString(Constants.INTENT_EXTRA_NAME_ARTIST, artistId);
- fragment.setArguments(args);
-
- replaceFragment(fragment, true);
- }
-
- private void startArtistRadio(final String artistId) {
- new LoadingTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- DownloadService downloadService = getDownloadService();
- downloadService.setArtistRadio(artistId);
- if(downloadService.size() == 0) {
- Log.e(TAG, "Failed to create artist radio");
- throw new Exception("Failed to create artist radio");
- }
- return null;
- }
-
- @Override
- protected void done(Void result) {
- Util.startActivityWithoutTransition(context, DownloadActivity.class);
- }
- }.execute();
- }
-
- private View createHeader() {
- View header = entryList.findViewById(R.id.select_album_header_wrapper);
- boolean add = false;
- if(header == null) {
- header = LayoutInflater.from(context).inflate(R.layout.select_album_header, entryList, false);
- add = true;
- }
-
- setupCoverArt(header);
- setupTextDisplay(header);
-
- if(add) {
- setupButtonEvents(header);
- }
-
- if(add) {
- return header;
- } else {
- return null;
- }
- }
-
- private void setupCoverArt(View header) {
- final ImageLoader imageLoader = getImageLoader();
- View coverArtView = header.findViewById(R.id.select_album_art);
-
- // Try a few times to get a random cover art
- if(artistInfo != null) {
- final String url = artistInfo.getImageUrl();
- coverArtView.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (url == null) {
- return;
- }
-
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
- ImageView fullScreenView = new ImageView(context);
- imageLoader.loadImage(fullScreenView, url, true);
- builder.setCancelable(true);
-
- AlertDialog imageDialog = builder.create();
- // Set view here with unecessary 0's to remove top/bottom border
- imageDialog.setView(fullScreenView, 0, 0, 0, 0);
- imageDialog.show();
- }
- });
- imageLoader.loadImage(coverArtView, url, false);
- } else if(entries.size() > 0) {
- Entry coverArt = null;
- for (int i = 0; (i < 3) && (coverArt == null || coverArt.getCoverArt() == null); i++) {
- coverArt = entries.get(random.nextInt(entries.size()));
- }
-
- final Entry albumRep = coverArt;
- coverArtView.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (albumRep.getCoverArt() == null) {
- return;
- }
-
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
- ImageView fullScreenView = new ImageView(context);
- imageLoader.loadImage(fullScreenView, albumRep, true, true);
- builder.setCancelable(true);
-
- AlertDialog imageDialog = builder.create();
- // Set view here with unecessary 0's to remove top/bottom border
- imageDialog.setView(fullScreenView, 0, 0, 0, 0);
- imageDialog.show();
- }
- });
- imageLoader.loadImage(coverArtView, albumRep, false, true);
- }
- }
- private void setupTextDisplay(final View header) {
- final TextView titleView = (TextView) header.findViewById(R.id.select_album_title);
- if(playlistName != null) {
- titleView.setText(playlistName);
- } else if(podcastName != null) {
- titleView.setText(podcastName);
- titleView.setPadding(0, 6, 4, 8);
- } else if(name != null) {
- titleView.setText(name);
-
- if(artistInfo != null) {
- titleView.setPadding(0, 6, 4, 8);
- }
- } else if(share != null) {
- titleView.setVisibility(View.GONE);
- }
-
- int songCount = 0;
-
- Set<String> artists = new HashSet<String>();
- Set<Integer> years = new HashSet<Integer>();
- Integer totalDuration = 0;
- for (Entry entry : entries) {
- if (!entry.isDirectory()) {
- songCount++;
- if (entry.getArtist() != null) {
- artists.add(entry.getArtist());
- }
- if(entry.getYear() != null) {
- years.add(entry.getYear());
- }
- Integer duration = entry.getDuration();
- if(duration != null) {
- totalDuration += duration;
- }
- }
- }
-
- final TextView artistView = (TextView) header.findViewById(R.id.select_album_artist);
- if(podcastDescription != null || artistInfo != null) {
- artistView.setVisibility(View.VISIBLE);
- String text = podcastDescription != null ? podcastDescription : artistInfo.getBiography();
- Spanned spanned = null;
- if(text != null) {
- spanned = Html.fromHtml(text);
- }
- artistView.setText(spanned);
- artistView.setSingleLine(false);
- final int minLines = context.getResources().getInteger(R.integer.TextDescriptionLength);
- artistView.setLines(minLines);
- artistView.setTextAppearance(context, android.R.style.TextAppearance_Small);
-
- final Spanned spannedText = spanned;
- artistView.setOnClickListener(new View.OnClickListener() {
- @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
- @Override
- public void onClick(View v) {
- if(artistView.getMaxLines() == minLines) {
- // Use LeadingMarginSpan2 to try to make text flow around image
- Display display = context.getWindowManager().getDefaultDisplay();
- ImageView coverArtView = (ImageView) header.findViewById(R.id.select_album_art);
- coverArtView.measure(display.getWidth(), display.getHeight());
-
- int height, width;
- ViewGroup.MarginLayoutParams vlp = (ViewGroup.MarginLayoutParams) coverArtView.getLayoutParams();
- if(coverArtView.getDrawable() != null) {
- height = coverArtView.getMeasuredHeight() + coverArtView.getPaddingBottom();
- width = coverArtView.getWidth() + coverArtView.getPaddingRight();
- } else {
- height = coverArtView.getHeight();
- width = coverArtView.getWidth() + coverArtView.getPaddingRight();
- }
- float textLineHeight = artistView.getPaint().getTextSize();
- int lines = (int) Math.ceil(height / textLineHeight);
-
- SpannableString ss = new SpannableString(spannedText);
- ss.setSpan(new MyLeadingMarginSpan2(lines, width), 0, ss.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
-
- View linearLayout = header.findViewById(R.id.select_album_text_layout);
- RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) linearLayout.getLayoutParams();
- int[]rules = params.getRules();
- rules[RelativeLayout.RIGHT_OF] = 0;
- params.leftMargin = vlp.rightMargin;
-
- artistView.setText(ss);
- artistView.setMaxLines(100);
-
- vlp = (ViewGroup.MarginLayoutParams) titleView.getLayoutParams();
- vlp.leftMargin = width;
- } else {
- artistView.setMaxLines(minLines);
- }
-
- if(albumList instanceof HeaderGridView) {
- HeaderGridView headerGridView = (HeaderGridView) albumList;
- ((BaseAdapter) headerGridView.getAdapter()).notifyDataSetChanged();
- }
- }
- });
- artistView.setMovementMethod(LinkMovementMethod.getInstance());
- } else if(topTracks) {
- artistView.setText(R.string.menu_top_tracks);
- artistView.setVisibility(View.VISIBLE);
- } else if(showAll) {
- artistView.setText(R.string.menu_show_all);
- artistView.setVisibility(View.VISIBLE);
- } else if (artists.size() == 1) {
- String artistText = artists.iterator().next();
- if(years.size() == 1) {
- artistText += " - " + years.iterator().next();
- }
- artistView.setText(artistText);
- artistView.setVisibility(View.VISIBLE);
- } else {
- artistView.setVisibility(View.GONE);
- }
-
- TextView songCountView = (TextView) header.findViewById(R.id.select_album_song_count);
- TextView songLengthView = (TextView) header.findViewById(R.id.select_album_song_length);
- if(podcastDescription != null || artistInfo != null) {
- songCountView.setVisibility(View.GONE);
- songLengthView.setVisibility(View.GONE);
- } else {
- String s = context.getResources().getQuantityString(R.plurals.select_album_n_songs, songCount, songCount);
- songCountView.setText(s.toUpperCase());
- songLengthView.setText(Util.formatDuration(totalDuration));
- }
- }
- private void setupButtonEvents(View header) {
- ImageView shareButton = (ImageView) header.findViewById(R.id.select_album_share);
- if(share != null || podcastId != null || !Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_MENU_SHARED, true) || Util.isOffline(context) || !UserUtil.canShare() || artistInfo != null) {
- shareButton.setVisibility(View.GONE);
- } else {
- shareButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- createShare(SelectDirectoryFragment.this.entries);
- }
- });
- }
-
- final ImageButton starButton = (ImageButton) header.findViewById(R.id.select_album_star);
- if(directory != null && Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_MENU_STAR, true) && artistInfo == null) {
- starButton.setImageResource(directory.isStarred() ? android.R.drawable.btn_star_big_on : android.R.drawable.btn_star_big_off);
- starButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- toggleStarred(directory, new OnStarChange() {
- @Override
- void starChange(boolean starred) {
- starButton.setImageResource(directory.isStarred() ? android.R.drawable.btn_star_big_on : android.R.drawable.btn_star_big_off);
- }
- });
- }
- });
- } else {
- starButton.setVisibility(View.GONE);
- }
-
- View ratingBarWrapper = header.findViewById(R.id.select_album_rate_wrapper);
- final RatingBar ratingBar = (RatingBar) header.findViewById(R.id.select_album_rate);
- if(directory != null && Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_MENU_RATING, true) && !Util.isOffline(context) && artistInfo == null) {
- ratingBar.setRating(directory.getRating());
- ratingBarWrapper.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- setRating(directory, new OnRatingChange() {
- @Override
- void ratingChange(int rating) {
- ratingBar.setRating(directory.getRating());
- }
- });
- }
- });
- } else {
- ratingBar.setVisibility(View.GONE);
- }
- }
-}
+package github.daneren2005.dsub.fragments;
+
+import android.annotation.TargetApi;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.v4.widget.SwipeRefreshLayout;
+import android.text.Html;
+import android.text.SpannableString;
+import android.text.Spanned;
+import android.text.method.LinkMovementMethod;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.Display;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.GridView;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.RatingBar;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.ArtistInfo;
+import github.daneren2005.dsub.domain.MusicDirectory;
+import github.daneren2005.dsub.domain.ServerInfo;
+import github.daneren2005.dsub.domain.Share;
+import github.daneren2005.dsub.service.DownloadService;
+import github.daneren2005.dsub.util.ImageLoader;
+import github.daneren2005.dsub.adapter.AlbumGridAdapter;
+import github.daneren2005.dsub.adapter.EntryAdapter;
+
+import java.io.Serializable;
+import java.util.Iterator;
+import java.util.List;
+
+import github.daneren2005.dsub.activity.DownloadActivity;
+import github.daneren2005.dsub.domain.PodcastEpisode;
+import github.daneren2005.dsub.service.MusicService;
+import github.daneren2005.dsub.service.MusicServiceFactory;
+import github.daneren2005.dsub.service.OfflineException;
+import github.daneren2005.dsub.service.ServerTooOldException;
+import github.daneren2005.dsub.util.Constants;
+import github.daneren2005.dsub.util.LoadingTask;
+import github.daneren2005.dsub.util.Pair;
+import github.daneren2005.dsub.util.SilentBackgroundTask;
+import github.daneren2005.dsub.util.TabBackgroundTask;
+import github.daneren2005.dsub.util.UserUtil;
+import github.daneren2005.dsub.util.Util;
+import github.daneren2005.dsub.adapter.AlbumListAdapter;
+import github.daneren2005.dsub.view.HeaderGridView;
+import github.daneren2005.dsub.view.MyLeadingMarginSpan2;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import static github.daneren2005.dsub.domain.MusicDirectory.Entry;
+
+public class SelectDirectoryFragment extends SubsonicFragment implements AdapterView.OnItemClickListener {
+ private static final String TAG = SelectDirectoryFragment.class.getSimpleName();
+
+ private GridView albumList;
+ private ListView entryList;
+ private Boolean licenseValid;
+ private EntryAdapter entryAdapter;
+ private List<Entry> albums;
+ private List<Entry> entries;
+ private boolean albumContext = false;
+ private boolean addAlbumHeader = false;
+ private LoadTask currentTask;
+ private ArtistInfo artistInfo;
+ private String artistInfoDelayed;
+
+ String id;
+ String name;
+ Entry directory;
+ String playlistId;
+ String playlistName;
+ boolean playlistOwner;
+ String podcastId;
+ String podcastName;
+ String podcastDescription;
+ String albumListType;
+ String albumListExtra;
+ int albumListSize;
+ boolean refreshListing = false;
+ boolean showAll = false;
+ boolean restoredInstance = false;
+ boolean lookupParent = false;
+ boolean largeAlbums = false;
+ boolean topTracks = false;
+ String lookupEntry;
+
+ public SelectDirectoryFragment() {
+ super();
+ }
+
+ @Override
+ public void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+ if(bundle != null) {
+ entries = (List<Entry>) bundle.getSerializable(Constants.FRAGMENT_LIST);
+ albums = (List<Entry>) bundle.getSerializable(Constants.FRAGMENT_LIST2);
+ artistInfo = (ArtistInfo) bundle.getSerializable(Constants.FRAGMENT_EXTRA);
+ restoredInstance = true;
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putSerializable(Constants.FRAGMENT_LIST, (Serializable) entries);
+ outState.putSerializable(Constants.FRAGMENT_LIST2, (Serializable) albums);
+ outState.putSerializable(Constants.FRAGMENT_EXTRA, (Serializable) artistInfo);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
+ Bundle args = getArguments();
+ if(args != null) {
+ id = args.getString(Constants.INTENT_EXTRA_NAME_ID);
+ name = args.getString(Constants.INTENT_EXTRA_NAME_NAME);
+ directory = (Entry) args.getSerializable(Constants.INTENT_EXTRA_NAME_DIRECTORY);
+ playlistId = args.getString(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID);
+ playlistName = args.getString(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME);
+ playlistOwner = args.getBoolean(Constants.INTENT_EXTRA_NAME_PLAYLIST_OWNER, false);
+ podcastId = args.getString(Constants.INTENT_EXTRA_NAME_PODCAST_ID);
+ podcastName = args.getString(Constants.INTENT_EXTRA_NAME_PODCAST_NAME);
+ podcastDescription = args.getString(Constants.INTENT_EXTRA_NAME_PODCAST_DESCRIPTION);
+ Object shareObj = args.getSerializable(Constants.INTENT_EXTRA_NAME_SHARE);
+ share = (shareObj != null) ? (Share) shareObj : null;
+ albumListType = args.getString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE);
+ albumListExtra = args.getString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_EXTRA);
+ albumListSize = args.getInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 0);
+ refreshListing = args.getBoolean(Constants.INTENT_EXTRA_REFRESH_LISTINGS);
+ artist = args.getBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, false);
+ lookupEntry = args.getString(Constants.INTENT_EXTRA_SEARCH_SONG);
+ topTracks = args.getBoolean(Constants.INTENT_EXTRA_TOP_TRACKS);
+ showAll = args.getBoolean(Constants.INTENT_EXTRA_SHOW_ALL);
+
+ String childId = args.getString(Constants.INTENT_EXTRA_NAME_CHILD_ID);
+ if(childId != null) {
+ id = childId;
+ lookupParent = true;
+ }
+ if(entries == null) {
+ entries = (List<Entry>) args.getSerializable(Constants.FRAGMENT_LIST);
+ albums = (List<Entry>) args.getSerializable(Constants.FRAGMENT_LIST2);
+
+ if(albums == null) {
+ albums = new ArrayList<Entry>();
+ }
+ }
+ }
+
+ rootView = inflater.inflate(R.layout.select_album, container, false);
+
+ refreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.refresh_layout);
+ refreshLayout.setOnRefreshListener(this);
+
+ entryList = (ListView) rootView.findViewById(R.id.select_album_entries);
+ entryList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
+ entryList.setOnItemClickListener(this);
+ setupScrollList(entryList);
+
+ if(Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_LARGE_ALBUM_ART, true)) {
+ largeAlbums = true;
+ }
+
+ if(albumListType == null || "starred".equals(albumListType) || !largeAlbums) {
+ albumList = (GridView) inflater.inflate(R.layout.unscrollable_grid_view, entryList, false);
+ addAlbumHeader = true;
+ } else {
+ ViewGroup rootGroup = (ViewGroup) rootView.findViewById(R.id.select_album_layout);
+ albumList = (GridView) inflater.inflate(R.layout.grid_view, rootGroup, false);
+ rootGroup.removeView(entryList);
+ rootGroup.addView(albumList);
+
+ setupScrollList(albumList);
+ }
+ registerForContextMenu(entryList);
+ setupAlbumList();
+
+ if(entries == null) {
+ if(primaryFragment || secondaryFragment) {
+ load(false);
+ } else {
+ invalidated = true;
+ }
+ } else {
+ licenseValid = true;
+ finishLoading();
+ }
+
+ if(name != null) {
+ setTitle(name);
+ }
+
+ return rootView;
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
+ if(licenseValid == null) {
+ menuInflater.inflate(R.menu.empty, menu);
+ } else if(albumListType != null && !"starred".equals(albumListType)) {
+ menuInflater.inflate(R.menu.select_album_list, menu);
+ } else if(artist && !showAll) {
+ menuInflater.inflate(R.menu.select_album, menu);
+
+ if(!ServerInfo.isMadsonic(context)) {
+ menu.removeItem(R.id.menu_top_tracks);
+ }
+ if(!ServerInfo.checkServerVersion(context, "1.11") || (id != null && "root".equals(id))) {
+ menu.removeItem(R.id.menu_radio);
+ menu.removeItem(R.id.menu_similar_artists);
+ } else if(ServerInfo.isMadsonic(context)) {
+ menu.removeItem(R.id.menu_similar_artists);
+ }
+ } else {
+ if(podcastId == null) {
+ if(Util.isOffline(context)) {
+ menuInflater.inflate(R.menu.select_song_offline, menu);
+ }
+ else {
+ menuInflater.inflate(R.menu.select_song, menu);
+
+ if(playlistId == null || !playlistOwner) {
+ menu.removeItem(R.id.menu_remove_playlist);
+ }
+ }
+
+ SharedPreferences prefs = Util.getPreferences(context);
+ if(!prefs.getBoolean(Constants.PREFERENCES_KEY_MENU_PLAY_NEXT, true)) {
+ menu.setGroupVisible(R.id.hide_play_next, false);
+ }
+ if(!prefs.getBoolean(Constants.PREFERENCES_KEY_MENU_PLAY_LAST, true)) {
+ menu.setGroupVisible(R.id.hide_play_last, false);
+ }
+ } else {
+ if(Util.isOffline(context)) {
+ menuInflater.inflate(R.menu.select_podcast_episode_offline, menu);
+ }
+ else {
+ menuInflater.inflate(R.menu.select_podcast_episode, menu);
+
+ if(!UserUtil.canPodcast()) {
+ menu.removeItem(R.id.menu_download_all);
+ }
+ }
+ }
+ }
+
+ if("starred".equals(albumListType)) {
+ menuInflater.inflate(R.menu.unstar, menu);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.menu_play_now:
+ playNow(false, false);
+ return true;
+ case R.id.menu_play_last:
+ playNow(false, true);
+ return true;
+ case R.id.menu_play_next:
+ playNow(false, true, true);
+ return true;
+ case R.id.menu_shuffle:
+ playNow(true, false);
+ return true;
+ case R.id.menu_download:
+ downloadBackground(false);
+ selectAll(false, false);
+ return true;
+ case R.id.menu_cache:
+ downloadBackground(true);
+ selectAll(false, false);
+ return true;
+ case R.id.menu_delete:
+ delete();
+ selectAll(false, false);
+ return true;
+ case R.id.menu_add_playlist:
+ if(getSelectedSongs().isEmpty()) {
+ selectAll(true, false);
+ }
+ addToPlaylist(getSelectedSongs());
+ return true;
+ case R.id.menu_remove_playlist:
+ removeFromPlaylist(playlistId, playlistName, getSelectedIndexes());
+ return true;
+ case R.id.menu_download_all:
+ downloadAllPodcastEpisodes();
+ return true;
+ case R.id.menu_show_all:
+ setShowAll();
+ return true;
+ case R.id.menu_unstar:
+ unstarSelected();
+ return true;
+ case R.id.menu_top_tracks:
+ showTopTracks();
+ return true;
+ case R.id.menu_similar_artists:
+ showSimilarArtists(id);
+ return true;
+ case R.id.menu_radio:
+ startArtistRadio(id);
+ return true;
+ }
+
+ return super.onOptionsItemSelected(item);
+
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
+ super.onCreateContextMenu(menu, view, menuInfo);
+
+ AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
+
+ Entry entry;
+ if(view.getId() == R.id.select_album_entries) {
+ if(info.position == 0) {
+ return;
+ }
+ entry = (Entry) entryList.getItemAtPosition(info.position);
+ // When List has Grid embedded in header, this is called against the header as well
+ if(entry != null) {
+ albumContext = false;
+ }
+ } else {
+ entry = (Entry) albumList.getItemAtPosition(info.position);
+ albumContext = true;
+ }
+
+ // Don't try to display a context menu if error here
+ if(entry == null) {
+ return;
+ }
+
+ onCreateContextMenu(menu, view, menuInfo, entry);
+ if(!entry.isVideo() && !Util.isOffline(context) && (playlistId == null || !playlistOwner) && (podcastId == null || Util.isOffline(context) && podcastId != null)) {
+ menu.removeItem(R.id.song_menu_remove_playlist);
+ }
+ // Remove show artists if parent is not set and if not on a album list
+ if((albumListType == null || (entry.getParent() == null && entry.getArtistId() == null)) && !Util.isOffline(context)) {
+ menu.removeItem(R.id.album_menu_show_artist);
+ }
+ if(podcastId != null && !Util.isOffline(context)) {
+ if(UserUtil.canPodcast()) {
+ String status = ((PodcastEpisode)entry).getStatus();
+ if("completed".equals(status)) {
+ menu.removeItem(R.id.song_menu_server_download);
+ }
+ } else {
+ menu.removeItem(R.id.song_menu_server_download);
+ menu.removeItem(R.id.song_menu_server_delete);
+ }
+ }
+
+ recreateContextMenu(menu);
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem menuItem) {
+ if(menuItem.getGroupId() != getSupportTag()) {
+ return false;
+ }
+
+ AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
+ Object selectedItem;
+ int headers = entryList.getHeaderViewsCount();
+ if(albumContext) {
+ selectedItem = albumList.getItemAtPosition(info.position);
+ } else {
+ if(info.position == 0) {
+ return false;
+ }
+ selectedItem = entries.get(info.position - headers);
+ }
+
+ if(Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_PLAY_NOW_AFTER, false) && menuItem.getItemId() == R.id.song_menu_play_now) {
+ List<Entry> songs = new ArrayList<Entry>();
+ Iterator it = entries.listIterator(info.position - headers);
+ while(it.hasNext()) {
+ songs.add((Entry) it.next());
+ }
+
+ playNow(songs);
+ return true;
+ }
+
+ if(onContextItemSelected(menuItem, selectedItem)) {
+ return true;
+ }
+
+ switch (menuItem.getItemId()) {
+ case R.id.song_menu_remove_playlist:
+ removeFromPlaylist(playlistId, playlistName, Arrays.<Integer>asList(info.position - headers));
+ break;
+ case R.id.song_menu_server_download:
+ downloadPodcastEpisode((PodcastEpisode)selectedItem);
+ break;
+ case R.id.song_menu_server_delete:
+ deletePodcastEpisode((PodcastEpisode)selectedItem);
+ break;
+ }
+
+ return true;
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ if (position >= 0) {
+ Entry entry = (Entry) parent.getItemAtPosition(position);
+ if (entry.isDirectory()) {
+ SubsonicFragment fragment = new SelectDirectoryFragment();
+ Bundle args = new Bundle();
+ args.putString(Constants.INTENT_EXTRA_NAME_ID, entry.getId());
+ args.putString(Constants.INTENT_EXTRA_NAME_NAME, entry.getTitle());
+ args.putSerializable(Constants.INTENT_EXTRA_NAME_DIRECTORY, entry);
+ if ("newest".equals(albumListType)) {
+ args.putBoolean(Constants.INTENT_EXTRA_REFRESH_LISTINGS, true);
+ }
+ if(entry.getArtist() == null && entry.getParent() == null) {
+ args.putBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, true);
+ }
+ fragment.setArguments(args);
+
+ replaceFragment(fragment, true);
+ } else if (entry.isVideo()) {
+ playVideo(entry);
+ } else if(entry instanceof PodcastEpisode) {
+ String status = ((PodcastEpisode)entry).getStatus();
+ if("error".equals(status)) {
+ Util.toast(context, R.string.select_podcasts_error);
+ return;
+ } else if(!"completed".equals(status)) {
+ Util.toast(context, R.string.select_podcasts_skipped);
+ return;
+ }
+
+ playNow(Arrays.asList(entry));
+ }
+ }
+ }
+
+ @Override
+ protected void refresh(boolean refresh) {
+ if(!"root".equals(id)) {
+ load(refresh);
+ }
+ }
+
+ private void load(boolean refresh) {
+ if(refreshListing) {
+ refresh = true;
+ }
+
+ if(currentTask != null) {
+ currentTask.cancel();
+ }
+
+ entryList.setVisibility(View.INVISIBLE);
+ if (playlistId != null) {
+ getPlaylist(playlistId, playlistName, refresh);
+ } else if(podcastId != null) {
+ getPodcast(podcastId, podcastName, refresh);
+ } else if (share != null) {
+ if(showAll) {
+ getRecursiveMusicDirectory(share.getId(), share.getName(), refresh);
+ } else {
+ getShare(share, refresh);
+ }
+ } else if (albumListType != null) {
+ getAlbumList(albumListType, albumListSize);
+ } else {
+ if(showAll) {
+ getRecursiveMusicDirectory(id, name, refresh);
+ } else if(topTracks) {
+ getTopTracks(id, name, refresh);
+ } else {
+ getMusicDirectory(id, name, refresh);
+ }
+ }
+ }
+
+ private void getMusicDirectory(final String id, final String name, final boolean refresh) {
+ setTitle(name);
+
+ new LoadTask(refresh) {
+ @Override
+ protected MusicDirectory load(MusicService service) throws Exception {
+ MusicDirectory dir = getMusicDirectory(id, name, refresh, service, this);
+
+ if(lookupParent && dir.getParent() != null) {
+ dir = getMusicDirectory(dir.getParent(), name, refresh, service, this);
+
+ // Update the fragment pointers so other stuff works correctly
+ SelectDirectoryFragment.this.id = dir.getId();
+ SelectDirectoryFragment.this.name = dir.getName();
+ } else if(id != null && directory == null && dir.getParent() != null && !artist) {
+ // View Album, try to lookup parent to get a complete entry to use for starring
+ MusicDirectory parentDir = getMusicDirectory(dir.getParent(), name, refresh, true, service, this);
+ for(Entry child: parentDir.getChildren()) {
+ if(id.equals(child.getId())) {
+ directory = child;
+ break;
+ }
+ }
+ }
+
+ return dir;
+ }
+
+ @Override
+ protected void done(Pair<MusicDirectory, Boolean> result) {
+ SelectDirectoryFragment.this.name = result.getFirst().getName();
+ setTitle(SelectDirectoryFragment.this.name);
+ super.done(result);
+ }
+ }.execute();
+ }
+
+ private void getRecursiveMusicDirectory(final String id, final String name, final boolean refresh) {
+ setTitle(name);
+
+ new LoadTask(refresh) {
+ @Override
+ protected MusicDirectory load(MusicService service) throws Exception {
+ MusicDirectory root;
+ if(share == null) {
+ root = getMusicDirectory(id, name, refresh, service, this);
+ } else {
+ root = share.getMusicDirectory();
+ }
+ List<Entry> songs = new ArrayList<Entry>();
+ getSongsRecursively(root, songs);
+ root.replaceChildren(songs);
+ return root;
+ }
+
+ private void getSongsRecursively(MusicDirectory parent, List<Entry> songs) throws Exception {
+ songs.addAll(parent.getChildren(false, true));
+ for (Entry dir : parent.getChildren(true, false)) {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+
+ MusicDirectory musicDirectory;
+ if(Util.isTagBrowsing(context) && !Util.isOffline(context)) {
+ musicDirectory = musicService.getAlbum(dir.getId(), dir.getTitle(), false, context, this);
+ } else {
+ musicDirectory = musicService.getMusicDirectory(dir.getId(), dir.getTitle(), false, context, this);
+ }
+ getSongsRecursively(musicDirectory, songs);
+ }
+ }
+
+ @Override
+ protected void done(Pair<MusicDirectory, Boolean> result) {
+ SelectDirectoryFragment.this.name = result.getFirst().getName();
+ setTitle(SelectDirectoryFragment.this.name);
+ super.done(result);
+ }
+ }.execute();
+ }
+
+ private void getPlaylist(final String playlistId, final String playlistName, final boolean refresh) {
+ setTitle(playlistName);
+
+ new LoadTask(refresh) {
+ @Override
+ protected MusicDirectory load(MusicService service) throws Exception {
+ return service.getPlaylist(refresh, playlistId, playlistName, context, this);
+ }
+ }.execute();
+ }
+
+ private void getPodcast(final String podcastId, final String podcastName, final boolean refresh) {
+ setTitle(podcastName);
+
+ new LoadTask(refresh) {
+ @Override
+ protected MusicDirectory load(MusicService service) throws Exception {
+ return service.getPodcastEpisodes(refresh, podcastId, context, this);
+ }
+ }.execute();
+ }
+
+ private void getShare(final Share share, final boolean refresh) {
+ setTitle(share.getName());
+
+ new LoadTask(refresh) {
+ @Override
+ protected MusicDirectory load(MusicService service) throws Exception {
+ return share.getMusicDirectory();
+ }
+ }.execute();
+ }
+
+ private void getTopTracks(final String id, final String name, final boolean refresh) {
+ setTitle(name);
+
+ new LoadTask(refresh) {
+ @Override
+ protected MusicDirectory load(MusicService service) throws Exception {
+ return service.getTopTrackSongs(name, 20, context, this);
+ }
+ }.execute();
+ }
+
+ private void getAlbumList(final String albumListType, final int size) {
+ 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("genres".equals(albumListType) || "years".equals(albumListType)) {
+ setTitle(albumListExtra);
+ } else if("alphabeticalByName".equals(albumListType)) {
+ setTitle(R.string.main_albums_alphabetical);
+ }
+
+ new LoadTask(true) {
+ @Override
+ protected MusicDirectory load(MusicService service) throws Exception {
+ MusicDirectory result;
+ if ("starred".equals(albumListType)) {
+ result = service.getStarredList(context, this);
+ } else if(("genres".equals(albumListType) && ServerInfo.checkServerVersion(context, "1.10.0")) || "years".equals(albumListType)) {
+ result = service.getAlbumList(albumListType, albumListExtra, size, 0, context, this);
+ if(result.getChildrenSize() == 0 && "genres".equals(albumListType)) {
+ SelectDirectoryFragment.this.albumListType = "genres-songs";
+ result = service.getSongsByGenre(albumListExtra, size, 0, context, this);
+ }
+ } else if("genres".equals(albumListType) || "genres-songs".equals(albumListType)) {
+ result = service.getSongsByGenre(albumListExtra, size, 0, context, this);
+ } else {
+ result = service.getAlbumList(albumListType, size, 0, context, this);
+ }
+ return result;
+ }
+ }.execute();
+ }
+
+ private abstract class LoadTask extends TabBackgroundTask<Pair<MusicDirectory, Boolean>> {
+ private boolean refresh;
+
+ public LoadTask(boolean refresh) {
+ super(SelectDirectoryFragment.this);
+ this.refresh = refresh;
+
+ currentTask = this;
+ }
+
+ protected abstract MusicDirectory load(MusicService service) throws Exception;
+
+ @Override
+ protected Pair<MusicDirectory, Boolean> doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ MusicDirectory dir = load(musicService);
+ licenseValid = musicService.isLicenseValid(context, this);
+
+ albums = dir.getChildren(true, false);
+ if(largeAlbums) {
+ entries = dir.getChildren(false, true);
+ } else {
+ entries = dir.getChildren();
+ }
+
+ // This isn't really an artist if no albums on it!
+ if(albums.size() == 0) {
+ artist = false;
+ }
+
+ // If artist, we want to load the artist info to use later
+ if(artist && ServerInfo.hasArtistInfo(context) && !Util.isOffline(context)) {
+ try {
+ String artistId;
+ if(id.indexOf(';') == -1) {
+ artistId = id;
+ } else {
+ artistId = id.substring(0, id.indexOf(';'));
+ }
+
+ artistInfo = musicService.getArtistInfo(artistId, refresh, false, context, this);
+
+ if(artistInfo == null) {
+ artistInfoDelayed = artistId;
+ }
+ } catch(Exception e) {
+ Log.w(TAG, "Failed to get Artist Info even though it should be supported");
+ }
+ }
+
+ return new Pair<MusicDirectory, Boolean>(dir, licenseValid);
+ }
+
+ @Override
+ protected void done(Pair<MusicDirectory, Boolean> result) {
+ finishLoading();
+ currentTask = null;
+ }
+ }
+
+ private void finishLoading() {
+ // Show header if not album list type and not root and not artist
+ // For Subsonic 5.1+ display a header for artists with getArtistInfo data if it exists
+ View header = null;
+ if(albumListType == null && !"root".equals(id) && (!artist || artistInfo != null || artistInfoDelayed != null)) {
+ header = createHeader();
+
+ if(header != null && artistInfoDelayed != null) {
+ final View finalHeader = header.findViewById(R.id.select_album_header);
+ final View headerProgress = header.findViewById(R.id.header_progress);
+
+ finalHeader.setVisibility(View.INVISIBLE);
+ headerProgress.setVisibility(View.VISIBLE);
+
+ new SilentBackgroundTask<Void>(context) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ artistInfo = musicService.getArtistInfo(artistInfoDelayed, false, true, context, this);
+
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ /*if(albumList instanceof HeaderGridView) {
+ HeaderGridView headerGridView = (HeaderGridView) albumList;
+ headerGridView.invalidateRowHeight();
+ ((BaseAdapter) headerGridView.getAdapter()).notifyDataSetChanged();
+ }*/
+
+ setupCoverArt(finalHeader);
+ setupTextDisplay(finalHeader);
+ setupButtonEvents(finalHeader);
+
+ finalHeader.setVisibility(View.VISIBLE);
+ headerProgress.setVisibility(View.GONE);
+ }
+ }.execute();
+ }
+
+ // Only add header to entry list if we aren't going recreate album grid as root anyways
+ if(header != null && entryList != null && (!addAlbumHeader || entries.size() > 0)) {
+ entryList.addHeaderView(header, null, false);
+ header = null;
+ }
+ }
+
+ // Needs to be added here, GB crashes if you to try to remove the header view before adapter is set
+ if(addAlbumHeader) {
+ if(entries.size() > 0 || playlistId != null || podcastId != null) {
+ entryList.addHeaderView(albumList);
+ } else {
+ ViewGroup rootGroup = (ViewGroup) rootView.findViewById(R.id.select_album_layout);
+ albumList = (GridView) context.getLayoutInflater().inflate(R.layout.grid_view, rootGroup, false);
+ rootGroup.removeView(entryList);
+ rootGroup.addView(albumList);
+
+ setupScrollList(albumList);
+ setupAlbumList();
+
+ // This should only not be null for a artist with only albums
+ if(header != null) {
+ HeaderGridView headerGridView = (HeaderGridView) albumList;
+ headerGridView.addHeaderView(header);
+ }
+ }
+ addAlbumHeader = false;
+ }
+
+ boolean validData = !entries.isEmpty() || !albums.isEmpty();
+ if(!validData) {
+ setEmpty(true);
+ }
+ // Always going to have entries in entryAdapter
+ entryAdapter = new EntryAdapter(context, getImageLoader(), entries, (podcastId == null));
+ ListAdapter listAdapter = entryAdapter;
+ // Song-only genre needs to always be entry list + infinite adapter
+ if("genres-songs".equals(albumListType)) {
+ ViewGroup rootGroup = (ViewGroup) rootView.findViewById(R.id.select_album_layout);
+ if(rootGroup.findViewById(R.id.gridview) != null && largeAlbums) {
+ rootGroup.removeView(albumList);
+ rootGroup.addView(entryList);
+ }
+
+ listAdapter = new AlbumListAdapter(context, entryAdapter, albumListType, albumListExtra, albumListSize);
+ } else if(albumListType == null || "starred".equals(albumListType)) {
+ // Only set standard album adapter if not album list and largeAlbums is true
+ if(largeAlbums) {
+ albumList.setAdapter(new AlbumGridAdapter(context, getImageLoader(), albums, !artist));
+ }
+ } else {
+ // If album list, use infinite adapters for either depending on whether or not largeAlbums is true
+ if(largeAlbums) {
+ albumList.setAdapter(new AlbumListAdapter(context, new AlbumGridAdapter(context, getImageLoader(), albums, true), albumListType, albumListExtra, albumListSize));
+ } else {
+ listAdapter = new AlbumListAdapter(context, entryAdapter, albumListType, albumListExtra, albumListSize);
+ }
+ }
+ entryList.setAdapter(listAdapter);
+ if(validData) {
+ entryList.setVisibility(View.VISIBLE);
+ }
+ context.supportInvalidateOptionsMenu();
+
+ if(lookupEntry != null) {
+ for(int i = 0; i < entries.size(); i++) {
+ if(lookupEntry.equals(entries.get(i).getTitle())) {
+ entryList.setSelection(i + entryList.getHeaderViewsCount());
+ lookupEntry = null;
+ break;
+ }
+ }
+ }
+
+ Bundle args = getArguments();
+ boolean playAll = args.getBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, false);
+ if (playAll && !restoredInstance) {
+ playAll(args.getBoolean(Constants.INTENT_EXTRA_NAME_SHUFFLE, false), false);
+ }
+ }
+
+ private void setupAlbumList() {
+ albumList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ Entry entry = (Entry) parent.getItemAtPosition(position);
+ SubsonicFragment fragment = new SelectDirectoryFragment();
+ Bundle args = new Bundle();
+ args.putString(Constants.INTENT_EXTRA_NAME_ID, entry.getId());
+ args.putString(Constants.INTENT_EXTRA_NAME_NAME, entry.getTitle());
+ args.putSerializable(Constants.INTENT_EXTRA_NAME_DIRECTORY, entry);
+ if ("newest".equals(albumListType)) {
+ args.putBoolean(Constants.INTENT_EXTRA_REFRESH_LISTINGS, true);
+ }
+ if(entry.getArtist() == null && entry.getParent() == null) {
+ args.putBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, true);
+ }
+ fragment.setArguments(args);
+
+ replaceFragment(fragment, true);
+ }
+ });
+
+ registerForContextMenu(entryList);
+ registerForContextMenu(albumList);
+ }
+
+ private void playNow(final boolean shuffle, final boolean append) {
+ playNow(shuffle, append, false);
+ }
+ private void playNow(final boolean shuffle, final boolean append, final boolean playNext) {
+ if(getSelectedSongs().size() > 0) {
+ download(append, false, !append, playNext, shuffle);
+ selectAll(false, false);
+ }
+ else {
+ playAll(shuffle, append);
+ }
+ }
+ private void playAll(final boolean shuffle, final boolean append) {
+ boolean hasSubFolders = false;
+ for (int i = 0; i < entryList.getCount(); i++) {
+ Entry entry = (Entry) entryList.getItemAtPosition(i);
+ if (entry != null && entry.isDirectory()) {
+ hasSubFolders = true;
+ break;
+ }
+ }
+ if(albums.size() > 0) {
+ hasSubFolders = true;
+ }
+
+ if (hasSubFolders && (id != null || share != null || "starred".equals(albumListType))) {
+ downloadRecursively(id, false, append, !append, shuffle, false);
+ } else if(hasSubFolders && albumListType != null) {
+ downloadRecursively(albums, shuffle, append);
+ } else {
+ selectAll(true, false);
+ download(append, false, !append, false, shuffle);
+ selectAll(false, false);
+ }
+ }
+
+ private void selectAll(boolean selected, boolean toast) {
+ int count = entryList.getCount();
+ int selectedCount = 0;
+ for (int i = 0; i < count; i++) {
+ Entry entry = (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(context, context.getString(toastResId, selectedCount));
+ }
+ }
+
+ private List<Entry> getSelectedSongs() {
+ List<Entry> songs = new ArrayList<Entry>(10);
+ int count = entryList.getCount();
+ for (int i = 0; i < count; i++) {
+ if (entryList.isItemChecked(i)) {
+ Entry entry = (Entry) entryList.getItemAtPosition(i);
+ // Don't try to add directories or 1-starred songs
+ if(!entry.isDirectory() && entry.getRating() != 1) {
+ songs.add(entry);
+ }
+ }
+ }
+ return songs;
+ }
+
+ private List<Integer> getSelectedIndexes() {
+ List<Integer> indexes = new ArrayList<Integer>();
+
+ int count = entryList.getCount();
+ int headers = entryList.getHeaderViewsCount();
+ for (int i = 0; i < count; i++) {
+ if (entryList.isItemChecked(i)) {
+ indexes.add(i - headers);
+ }
+ }
+
+ return indexes;
+ }
+
+ private void download(final boolean append, final boolean save, final boolean autoplay, final boolean playNext, final boolean shuffle) {
+ if (getDownloadService() == null) {
+ return;
+ }
+
+ final List<Entry> songs = getSelectedSongs();
+ warnIfStorageUnavailable();
+
+ // Conditions for using play now button
+ if(!append && !save && autoplay && !playNext && !shuffle) {
+ // Call playNow which goes through and tries to use bookmark information
+ playNow(songs, playlistName, playlistId);
+ return;
+ }
+
+ LoadingTask<Void> onValid = new LoadingTask<Void>(context) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ if (!append) {
+ getDownloadService().clear();
+ }
+
+ getDownloadService().download(songs, save, autoplay, playNext, shuffle);
+ if (playlistName != null) {
+ getDownloadService().setSuggestedPlaylistName(playlistName, playlistId);
+ } else {
+ getDownloadService().setSuggestedPlaylistName(null, null);
+ }
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ if (autoplay) {
+ Util.startActivityWithoutTransition(context, DownloadActivity.class);
+ } else if (save) {
+ Util.toast(context,
+ context.getResources().getQuantityString(R.plurals.select_album_n_songs_downloading, songs.size(), songs.size()));
+ } else if (append) {
+ Util.toast(context,
+ context.getResources().getQuantityString(R.plurals.select_album_n_songs_added, songs.size(), songs.size()));
+ }
+ }
+ };
+
+ checkLicenseAndTrialPeriod(onValid);
+ }
+ private void downloadBackground(final boolean save) {
+ if(playlistId != null) {
+ selectAll(true, false);
+ }
+
+ List<Entry> songs = getSelectedSongs();
+ if(songs.isEmpty()) {
+ // Get both songs and albums
+ downloadRecursively(id, save, false, false, false, true);
+ } else {
+ downloadBackground(save, songs);
+ }
+ }
+ private void downloadBackground(final boolean save, final List<Entry> songs) {
+ if (getDownloadService() == null) {
+ return;
+ }
+
+ warnIfStorageUnavailable();
+ LoadingTask<Void> onValid = new LoadingTask<Void>(context) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ getDownloadService().downloadBackground(songs, save);
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ Util.toast(context, context.getResources().getQuantityString(R.plurals.select_album_n_songs_downloading, songs.size(), songs.size()));
+ }
+ };
+
+ checkLicenseAndTrialPeriod(onValid);
+ }
+
+ private void delete() {
+ List<Entry> songs = getSelectedSongs();
+ if(songs.isEmpty()) {
+ selectAll(true, false);
+ songs = getSelectedSongs();
+
+ // Also delete all directories
+ for(Entry album: albums) {
+ deleteRecursively(album);
+ }
+ }
+ if (getDownloadService() != null) {
+ getDownloadService().delete(songs);
+ }
+ }
+
+ public void removeFromPlaylist(final String id, final String name, final List<Integer> indexes) {
+ new LoadingTask<Void>(context, true) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ musicService.removeFromPlaylist(id, indexes, context, null);
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ for(int i = indexes.size() - 1; i >= 0; i--) {
+ entryList.setItemChecked(indexes.get(i) + 1, false);
+ entryAdapter.removeAt(indexes.get(i));
+ }
+ entryAdapter.notifyDataSetChanged();
+ Util.toast(context, context.getResources().getString(R.string.removed_playlist, indexes.size(), name));
+ }
+
+ @Override
+ protected void error(Throwable error) {
+ String msg;
+ if (error instanceof OfflineException || error instanceof ServerTooOldException) {
+ msg = getErrorMessage(error);
+ } else {
+ msg = context.getResources().getString(R.string.updated_playlist_error, name) + " " + getErrorMessage(error);
+ }
+
+ Util.toast(context, msg, false);
+ }
+ }.execute();
+ }
+
+ public void downloadAllPodcastEpisodes() {
+ new LoadingTask<Void>(context, true) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+
+ for(int i = 0; i < entries.size(); i++) {
+ PodcastEpisode episode = (PodcastEpisode) entries.get(i);
+ if("skipped".equals(episode.getStatus())) {
+ musicService.downloadPodcastEpisode(episode.getEpisodeId(), context, null);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ Util.toast(context, context.getResources().getString(R.string.select_podcasts_downloading, podcastName));
+ }
+
+ @Override
+ protected void error(Throwable error) {
+ Util.toast(context, getErrorMessage(error), false);
+ }
+ }.execute();
+ }
+
+ public void downloadPodcastEpisode(final PodcastEpisode episode) {
+ new LoadingTask<Void>(context, true) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ musicService.downloadPodcastEpisode(episode.getEpisodeId(), context, null);
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ Util.toast(context, context.getResources().getString(R.string.select_podcasts_downloading, episode.getTitle()));
+ }
+
+ @Override
+ protected void error(Throwable error) {
+ Util.toast(context, getErrorMessage(error), false);
+ }
+ }.execute();
+ }
+
+ public void deletePodcastEpisode(final PodcastEpisode episode) {
+ Util.confirmDialog(context, R.string.common_delete, episode.getTitle(), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ new LoadingTask<Void>(context, true) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ musicService.deletePodcastEpisode(episode.getEpisodeId(), episode.getParent(), null, context);
+ if (getDownloadService() != null) {
+ List<Entry> episodeList = new ArrayList<Entry>(1);
+ episodeList.add(episode);
+ getDownloadService().delete(episodeList);
+ }
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ entries.remove(episode);
+ entryAdapter.notifyDataSetChanged();
+ }
+
+ @Override
+ protected void error(Throwable error) {
+ Log.w(TAG, "Failed to delete podcast episode", error);
+ Util.toast(context, getErrorMessage(error), false);
+ }
+ }.execute();
+ }
+ });
+ }
+
+ public void unstarSelected() {
+ List<Entry> selected = getSelectedSongs();
+ if(selected.size() == 0) {
+ selected = entries;
+ }
+ if(selected.size() == 0) {
+ return;
+ }
+ final List<Entry> unstar = new ArrayList<Entry>();
+ unstar.addAll(selected);
+
+ new LoadingTask<Void>(context, true) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ List<Entry> entries = new ArrayList<Entry>();
+ List<Entry> artists = new ArrayList<Entry>();
+ List<Entry> albums = new ArrayList<Entry>();
+ for(Entry entry: unstar) {
+ if(entry.isDirectory()) {
+ if(entry.isAlbum()) {
+ albums.add(entry);
+ } else {
+ artists.add(entry);
+ }
+ } else {
+ entries.add(entry);
+ }
+ }
+ musicService.setStarred(entries, artists, albums, false, this, context);
+
+ for(Entry entry: unstar) {
+ new EntryInstanceUpdater(entry) {
+ @Override
+ public void update(Entry found) {
+ found.setStarred(false);
+ }
+ }.execute();
+ }
+
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ Util.toast(context, context.getResources().getString(R.string.starring_content_unstarred, Integer.toString(unstar.size())));
+
+ for(Entry entry: unstar) {
+ entries.remove(entry);
+ }
+ entryAdapter.notifyDataSetChanged();
+ selectAll(false, false);
+ }
+
+ @Override
+ protected void error(Throwable error) {
+ String msg;
+ if (error instanceof OfflineException || error instanceof ServerTooOldException) {
+ msg = getErrorMessage(error);
+ } else {
+ msg = context.getResources().getString(R.string.starring_content_error, Integer.toString(unstar.size())) + " " + getErrorMessage(error);
+ }
+
+ Util.toast(context, msg, false);
+ }
+ }.execute();
+ }
+
+ private void checkLicenseAndTrialPeriod(LoadingTask onValid) {
+ if (licenseValid) {
+ onValid.execute();
+ return;
+ }
+
+ int trialDaysLeft = Util.getRemainingTrialDays(context);
+ 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(context, context.getResources().getString(R.string.select_album_not_licensed, trialDaysLeft));
+ onValid.execute();
+ }
+ }
+
+ private void showDonationDialog(int trialDaysLeft, final LoadingTask onValid) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ 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(context.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.execute();
+ }
+ }
+ });
+
+ builder.create().show();
+ }
+
+ private void showTopTracks() {
+ SubsonicFragment fragment = new SelectDirectoryFragment();
+ Bundle args = new Bundle(getArguments());
+ args.putBoolean(Constants.INTENT_EXTRA_TOP_TRACKS, true);
+ fragment.setArguments(args);
+
+ replaceFragment(fragment, true);
+ }
+
+ private void setShowAll() {
+ SubsonicFragment fragment = new SelectDirectoryFragment();
+ Bundle args = new Bundle(getArguments());
+ args.putBoolean(Constants.INTENT_EXTRA_SHOW_ALL, true);
+ fragment.setArguments(args);
+
+ replaceFragment(fragment, true);
+ }
+
+ private void showSimilarArtists(String artistId) {
+ SubsonicFragment fragment = new SimilarArtistFragment();
+ Bundle args = new Bundle();
+ args.putString(Constants.INTENT_EXTRA_NAME_ARTIST, artistId);
+ fragment.setArguments(args);
+
+ replaceFragment(fragment, true);
+ }
+
+ private void startArtistRadio(final String artistId) {
+ new LoadingTask<Void>(context) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ DownloadService downloadService = getDownloadService();
+ downloadService.setArtistRadio(artistId);
+ if(downloadService.size() == 0) {
+ Log.e(TAG, "Failed to create artist radio");
+ throw new Exception("Failed to create artist radio");
+ }
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ Util.startActivityWithoutTransition(context, DownloadActivity.class);
+ }
+ }.execute();
+ }
+
+ private View createHeader() {
+ View header = entryList.findViewById(R.id.select_album_header_wrapper);
+ boolean add = false;
+ if(header == null) {
+ header = LayoutInflater.from(context).inflate(R.layout.select_album_header, entryList, false);
+ add = true;
+ }
+
+ setupCoverArt(header);
+ setupTextDisplay(header);
+
+ if(add) {
+ setupButtonEvents(header);
+ }
+
+ if(add) {
+ return header;
+ } else {
+ return null;
+ }
+ }
+
+ private void setupCoverArt(View header) {
+ final ImageLoader imageLoader = getImageLoader();
+ View coverArtView = header.findViewById(R.id.select_album_art);
+
+ // Try a few times to get a random cover art
+ if(artistInfo != null) {
+ final String url = artistInfo.getImageUrl();
+ coverArtView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (url == null) {
+ return;
+ }
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ ImageView fullScreenView = new ImageView(context);
+ imageLoader.loadImage(fullScreenView, url, true);
+ builder.setCancelable(true);
+
+ AlertDialog imageDialog = builder.create();
+ // Set view here with unecessary 0's to remove top/bottom border
+ imageDialog.setView(fullScreenView, 0, 0, 0, 0);
+ imageDialog.show();
+ }
+ });
+ imageLoader.loadImage(coverArtView, url, false);
+ } else if(entries.size() > 0) {
+ Entry coverArt = null;
+ for (int i = 0; (i < 3) && (coverArt == null || coverArt.getCoverArt() == null); i++) {
+ coverArt = entries.get(random.nextInt(entries.size()));
+ }
+
+ final Entry albumRep = coverArt;
+ coverArtView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (albumRep.getCoverArt() == null) {
+ return;
+ }
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ ImageView fullScreenView = new ImageView(context);
+ imageLoader.loadImage(fullScreenView, albumRep, true, true);
+ builder.setCancelable(true);
+
+ AlertDialog imageDialog = builder.create();
+ // Set view here with unecessary 0's to remove top/bottom border
+ imageDialog.setView(fullScreenView, 0, 0, 0, 0);
+ imageDialog.show();
+ }
+ });
+ imageLoader.loadImage(coverArtView, albumRep, false, true);
+ }
+ }
+ private void setupTextDisplay(final View header) {
+ final TextView titleView = (TextView) header.findViewById(R.id.select_album_title);
+ if(playlistName != null) {
+ titleView.setText(playlistName);
+ } else if(podcastName != null) {
+ titleView.setText(podcastName);
+ titleView.setPadding(0, 6, 4, 8);
+ } else if(name != null) {
+ titleView.setText(name);
+
+ if(artistInfo != null) {
+ titleView.setPadding(0, 6, 4, 8);
+ }
+ } else if(share != null) {
+ titleView.setVisibility(View.GONE);
+ }
+
+ int songCount = 0;
+
+ Set<String> artists = new HashSet<String>();
+ Set<Integer> years = new HashSet<Integer>();
+ Integer totalDuration = 0;
+ for (Entry entry : entries) {
+ if (!entry.isDirectory()) {
+ songCount++;
+ if (entry.getArtist() != null) {
+ artists.add(entry.getArtist());
+ }
+ if(entry.getYear() != null) {
+ years.add(entry.getYear());
+ }
+ Integer duration = entry.getDuration();
+ if(duration != null) {
+ totalDuration += duration;
+ }
+ }
+ }
+
+ final TextView artistView = (TextView) header.findViewById(R.id.select_album_artist);
+ if(podcastDescription != null || artistInfo != null) {
+ artistView.setVisibility(View.VISIBLE);
+ String text = podcastDescription != null ? podcastDescription : artistInfo.getBiography();
+ Spanned spanned = null;
+ if(text != null) {
+ spanned = Html.fromHtml(text);
+ }
+ artistView.setText(spanned);
+ artistView.setSingleLine(false);
+ final int minLines = context.getResources().getInteger(R.integer.TextDescriptionLength);
+ artistView.setLines(minLines);
+ artistView.setTextAppearance(context, android.R.style.TextAppearance_Small);
+
+ final Spanned spannedText = spanned;
+ artistView.setOnClickListener(new View.OnClickListener() {
+ @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+ @Override
+ public void onClick(View v) {
+ if(artistView.getMaxLines() == minLines) {
+ // Use LeadingMarginSpan2 to try to make text flow around image
+ Display display = context.getWindowManager().getDefaultDisplay();
+ ImageView coverArtView = (ImageView) header.findViewById(R.id.select_album_art);
+ coverArtView.measure(display.getWidth(), display.getHeight());
+
+ int height, width;
+ ViewGroup.MarginLayoutParams vlp = (ViewGroup.MarginLayoutParams) coverArtView.getLayoutParams();
+ if(coverArtView.getDrawable() != null) {
+ height = coverArtView.getMeasuredHeight() + coverArtView.getPaddingBottom();
+ width = coverArtView.getWidth() + coverArtView.getPaddingRight();
+ } else {
+ height = coverArtView.getHeight();
+ width = coverArtView.getWidth() + coverArtView.getPaddingRight();
+ }
+ float textLineHeight = artistView.getPaint().getTextSize();
+ int lines = (int) Math.ceil(height / textLineHeight);
+
+ SpannableString ss = new SpannableString(spannedText);
+ ss.setSpan(new MyLeadingMarginSpan2(lines, width), 0, ss.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+ View linearLayout = header.findViewById(R.id.select_album_text_layout);
+ RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) linearLayout.getLayoutParams();
+ int[]rules = params.getRules();
+ rules[RelativeLayout.RIGHT_OF] = 0;
+ params.leftMargin = vlp.rightMargin;
+
+ artistView.setText(ss);
+ artistView.setMaxLines(100);
+
+ vlp = (ViewGroup.MarginLayoutParams) titleView.getLayoutParams();
+ vlp.leftMargin = width;
+ } else {
+ artistView.setMaxLines(minLines);
+ }
+
+ if(albumList instanceof HeaderGridView) {
+ HeaderGridView headerGridView = (HeaderGridView) albumList;
+ ((BaseAdapter) headerGridView.getAdapter()).notifyDataSetChanged();
+ }
+ }
+ });
+ artistView.setMovementMethod(LinkMovementMethod.getInstance());
+ } else if(topTracks) {
+ artistView.setText(R.string.menu_top_tracks);
+ artistView.setVisibility(View.VISIBLE);
+ } else if(showAll) {
+ artistView.setText(R.string.menu_show_all);
+ artistView.setVisibility(View.VISIBLE);
+ } else if (artists.size() == 1) {
+ String artistText = artists.iterator().next();
+ if(years.size() == 1) {
+ artistText += " - " + years.iterator().next();
+ }
+ artistView.setText(artistText);
+ artistView.setVisibility(View.VISIBLE);
+ } else {
+ artistView.setVisibility(View.GONE);
+ }
+
+ TextView songCountView = (TextView) header.findViewById(R.id.select_album_song_count);
+ TextView songLengthView = (TextView) header.findViewById(R.id.select_album_song_length);
+ if(podcastDescription != null || artistInfo != null) {
+ songCountView.setVisibility(View.GONE);
+ songLengthView.setVisibility(View.GONE);
+ } else {
+ String s = context.getResources().getQuantityString(R.plurals.select_album_n_songs, songCount, songCount);
+ songCountView.setText(s.toUpperCase());
+ songLengthView.setText(Util.formatDuration(totalDuration));
+ }
+ }
+ private void setupButtonEvents(View header) {
+ ImageView shareButton = (ImageView) header.findViewById(R.id.select_album_share);
+ if(share != null || podcastId != null || !Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_MENU_SHARED, true) || Util.isOffline(context) || !UserUtil.canShare() || artistInfo != null) {
+ shareButton.setVisibility(View.GONE);
+ } else {
+ shareButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ createShare(SelectDirectoryFragment.this.entries);
+ }
+ });
+ }
+
+ final ImageButton starButton = (ImageButton) header.findViewById(R.id.select_album_star);
+ if(directory != null && Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_MENU_STAR, true) && artistInfo == null) {
+ starButton.setImageResource(directory.isStarred() ? android.R.drawable.btn_star_big_on : android.R.drawable.btn_star_big_off);
+ starButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ toggleStarred(directory, new OnStarChange() {
+ @Override
+ void starChange(boolean starred) {
+ starButton.setImageResource(directory.isStarred() ? android.R.drawable.btn_star_big_on : android.R.drawable.btn_star_big_off);
+ }
+ });
+ }
+ });
+ } else {
+ starButton.setVisibility(View.GONE);
+ }
+
+ View ratingBarWrapper = header.findViewById(R.id.select_album_rate_wrapper);
+ final RatingBar ratingBar = (RatingBar) header.findViewById(R.id.select_album_rate);
+ if(directory != null && Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_MENU_RATING, true) && !Util.isOffline(context) && artistInfo == null) {
+ ratingBar.setRating(directory.getRating());
+ ratingBarWrapper.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ setRating(directory, new OnRatingChange() {
+ @Override
+ void ratingChange(int rating) {
+ ratingBar.setRating(directory.getRating());
+ }
+ });
+ }
+ });
+ } else {
+ ratingBar.setVisibility(View.GONE);
+ }
+ }
+}
diff --git a/src/github/daneren2005/dsub/fragments/SelectGenreFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectGenreFragment.java
index 4068dca2..2d310172 100644
--- a/src/github/daneren2005/dsub/fragments/SelectGenreFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectGenreFragment.java
@@ -1,71 +1,71 @@
-/*
- 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 2010 (C) Sindre Mehus
- */
-package github.daneren2005.dsub.fragments;
-
-import android.os.Bundle;
-import android.view.View;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.domain.Genre;
-import github.daneren2005.dsub.service.MusicService;
-import github.daneren2005.dsub.util.Constants;
-import github.daneren2005.dsub.util.ProgressListener;
-import github.daneren2005.dsub.adapter.GenreAdapter;
-
-import java.util.List;
-
-public class SelectGenreFragment extends SelectListFragment<Genre> {
- private static final String TAG = SelectGenreFragment.class.getSimpleName();
-
- @Override
- public int getOptionsMenu() {
- return R.menu.empty;
- }
-
- @Override
- public ArrayAdapter getAdapter(List<Genre> objs) {
- return new GenreAdapter(context, objs);
- }
-
- @Override
- public List<Genre> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception {
- return musicService.getGenres(refresh, context, listener);
- }
-
- @Override
- public int getTitleResource() {
- return R.string.main_albums_genres;
- }
-
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- Genre genre = (Genre) parent.getItemAtPosition(position);
-
- SubsonicFragment fragment = new SelectDirectoryFragment();
- Bundle args = new Bundle();
- args.putString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE, "genres");
- args.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 20);
- args.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0);
- args.putString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_EXTRA, genre.getName());
- fragment.setArguments(args);
-
- replaceFragment(fragment);
- }
-}
+/*
+ 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 2010 (C) Sindre Mehus
+ */
+package github.daneren2005.dsub.fragments;
+
+import android.os.Bundle;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.Genre;
+import github.daneren2005.dsub.service.MusicService;
+import github.daneren2005.dsub.util.Constants;
+import github.daneren2005.dsub.util.ProgressListener;
+import github.daneren2005.dsub.adapter.GenreAdapter;
+
+import java.util.List;
+
+public class SelectGenreFragment extends SelectListFragment<Genre> {
+ private static final String TAG = SelectGenreFragment.class.getSimpleName();
+
+ @Override
+ public int getOptionsMenu() {
+ return R.menu.empty;
+ }
+
+ @Override
+ public ArrayAdapter getAdapter(List<Genre> objs) {
+ return new GenreAdapter(context, objs);
+ }
+
+ @Override
+ public List<Genre> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception {
+ return musicService.getGenres(refresh, context, listener);
+ }
+
+ @Override
+ public int getTitleResource() {
+ return R.string.main_albums_genres;
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ Genre genre = (Genre) parent.getItemAtPosition(position);
+
+ SubsonicFragment fragment = new SelectDirectoryFragment();
+ Bundle args = new Bundle();
+ args.putString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE, "genres");
+ args.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 20);
+ args.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0);
+ args.putString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_EXTRA, genre.getName());
+ fragment.setArguments(args);
+
+ replaceFragment(fragment);
+ }
+}
diff --git a/src/github/daneren2005/dsub/fragments/SelectListFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectListFragment.java
index 749ef842..6f73f6e8 100644
--- a/src/github/daneren2005/dsub/fragments/SelectListFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectListFragment.java
@@ -1,163 +1,163 @@
-/*
- 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 2010 (C) Sindre Mehus
- */
-package github.daneren2005.dsub.fragments;
-
-import android.os.Bundle;
-import android.support.v4.widget.SwipeRefreshLayout;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.ListView;
-
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.List;
-
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.service.MusicService;
-import github.daneren2005.dsub.service.MusicServiceFactory;
-import github.daneren2005.dsub.util.BackgroundTask;
-import github.daneren2005.dsub.util.Constants;
-import github.daneren2005.dsub.util.ProgressListener;
-import github.daneren2005.dsub.util.TabBackgroundTask;
-
-public abstract class SelectListFragment<T> extends SubsonicFragment implements AdapterView.OnItemClickListener {
- private static final String TAG = SelectListFragment.class.getSimpleName();
- protected ListView listView;
- protected ArrayAdapter adapter;
- protected BackgroundTask<List<T>> currentTask;
- protected List<T> objects;
- protected boolean serialize = true;
-
- @Override
- public void onCreate(Bundle bundle) {
- super.onCreate(bundle);
-
- if(bundle != null && serialize) {
- objects = (List<T>) bundle.getSerializable(Constants.FRAGMENT_LIST);
- }
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- if(serialize) {
- outState.putSerializable(Constants.FRAGMENT_LIST, (Serializable) objects);
- }
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
- rootView = inflater.inflate(R.layout.abstract_list_fragment, container, false);
-
- refreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.refresh_layout);
- refreshLayout.setOnRefreshListener(this);
-
- listView = (ListView)rootView.findViewById(R.id.fragment_list);
- listView.setOnItemClickListener(this);
- setupScrollList(listView);
- registerForContextMenu(listView);
-
- if(objects == null) {
- refresh(false);
- } else {
- listView.setAdapter(adapter = getAdapter(objects));
- }
-
- return rootView;
- }
-
- @Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
- if(!primaryFragment) {
- return;
- }
-
- menuInflater.inflate(getOptionsMenu(), menu);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- return super.onOptionsItemSelected(item);
- }
-
- @Override
- protected void refresh(final boolean refresh) {
- int titleRes = getTitleResource();
- if(titleRes != 0) {
- setTitle(getTitleResource());
- }
- listView.setVisibility(View.GONE);
-
- // Cancel current running task before starting another one
- if(currentTask != null) {
- currentTask.cancel();
- }
-
- currentTask = new TabBackgroundTask<List<T>>(this) {
- @Override
- protected List<T> doInBackground() throws Throwable {
- MusicService musicService = MusicServiceFactory.getMusicService(context);
-
- objects = new ArrayList<T>();
-
- try {
- objects = getObjects(musicService, refresh, this);
- } catch (Exception x) {
- Log.e(TAG, "Failed to load", x);
- }
-
- return objects;
- }
-
- @Override
- protected void done(List<T> result) {
- if (result != null && !result.isEmpty()) {
- // Toggle fast scroll to get around issue when length of list changes
- listView.setFastScrollEnabled(false);
- listView.setAdapter(adapter = getAdapter(result));
- listView.setFastScrollEnabled(true);
-
- onFinishRefresh();
- listView.setVisibility(View.VISIBLE);
- } else {
- setEmpty(true);
- }
-
- currentTask = null;
- }
- };
- currentTask.execute();
- }
-
- public abstract int getOptionsMenu();
- public abstract ArrayAdapter getAdapter(List<T> objs);
- public abstract List<T> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception;
- public abstract int getTitleResource();
-
- public void onFinishRefresh() {
-
- }
-}
+/*
+ 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 2010 (C) Sindre Mehus
+ */
+package github.daneren2005.dsub.fragments;
+
+import android.os.Bundle;
+import android.support.v4.widget.SwipeRefreshLayout;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.service.MusicService;
+import github.daneren2005.dsub.service.MusicServiceFactory;
+import github.daneren2005.dsub.util.BackgroundTask;
+import github.daneren2005.dsub.util.Constants;
+import github.daneren2005.dsub.util.ProgressListener;
+import github.daneren2005.dsub.util.TabBackgroundTask;
+
+public abstract class SelectListFragment<T> extends SubsonicFragment implements AdapterView.OnItemClickListener {
+ private static final String TAG = SelectListFragment.class.getSimpleName();
+ protected ListView listView;
+ protected ArrayAdapter adapter;
+ protected BackgroundTask<List<T>> currentTask;
+ protected List<T> objects;
+ protected boolean serialize = true;
+
+ @Override
+ public void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+
+ if(bundle != null && serialize) {
+ objects = (List<T>) bundle.getSerializable(Constants.FRAGMENT_LIST);
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ if(serialize) {
+ outState.putSerializable(Constants.FRAGMENT_LIST, (Serializable) objects);
+ }
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
+ rootView = inflater.inflate(R.layout.abstract_list_fragment, container, false);
+
+ refreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.refresh_layout);
+ refreshLayout.setOnRefreshListener(this);
+
+ listView = (ListView)rootView.findViewById(R.id.fragment_list);
+ listView.setOnItemClickListener(this);
+ setupScrollList(listView);
+ registerForContextMenu(listView);
+
+ if(objects == null) {
+ refresh(false);
+ } else {
+ listView.setAdapter(adapter = getAdapter(objects));
+ }
+
+ return rootView;
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
+ if(!primaryFragment) {
+ return;
+ }
+
+ menuInflater.inflate(getOptionsMenu(), menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ protected void refresh(final boolean refresh) {
+ int titleRes = getTitleResource();
+ if(titleRes != 0) {
+ setTitle(getTitleResource());
+ }
+ listView.setVisibility(View.GONE);
+
+ // Cancel current running task before starting another one
+ if(currentTask != null) {
+ currentTask.cancel();
+ }
+
+ currentTask = new TabBackgroundTask<List<T>>(this) {
+ @Override
+ protected List<T> doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+
+ objects = new ArrayList<T>();
+
+ try {
+ objects = getObjects(musicService, refresh, this);
+ } catch (Exception x) {
+ Log.e(TAG, "Failed to load", x);
+ }
+
+ return objects;
+ }
+
+ @Override
+ protected void done(List<T> result) {
+ if (result != null && !result.isEmpty()) {
+ // Toggle fast scroll to get around issue when length of list changes
+ listView.setFastScrollEnabled(false);
+ listView.setAdapter(adapter = getAdapter(result));
+ listView.setFastScrollEnabled(true);
+
+ onFinishRefresh();
+ listView.setVisibility(View.VISIBLE);
+ } else {
+ setEmpty(true);
+ }
+
+ currentTask = null;
+ }
+ };
+ currentTask.execute();
+ }
+
+ public abstract int getOptionsMenu();
+ public abstract ArrayAdapter getAdapter(List<T> objs);
+ public abstract List<T> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception;
+ public abstract int getTitleResource();
+
+ public void onFinishRefresh() {
+
+ }
+}
diff --git a/src/github/daneren2005/dsub/fragments/SelectPlaylistFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectPlaylistFragment.java
index af372076..3d7e664f 100644
--- a/src/github/daneren2005/dsub/fragments/SelectPlaylistFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectPlaylistFragment.java
@@ -1,303 +1,303 @@
-package github.daneren2005.dsub.fragments;
-
-import android.app.AlertDialog;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.support.v4.app.FragmentTransaction;
-import android.view.ContextMenu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.CheckBox;
-import android.widget.EditText;
-
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.domain.MusicDirectory;
-import github.daneren2005.dsub.domain.Playlist;
-import github.daneren2005.dsub.domain.ServerInfo;
-import github.daneren2005.dsub.service.DownloadFile;
-import github.daneren2005.dsub.service.MusicService;
-import github.daneren2005.dsub.service.MusicServiceFactory;
-import github.daneren2005.dsub.service.OfflineException;
-import github.daneren2005.dsub.service.ServerTooOldException;
-import github.daneren2005.dsub.util.ProgressListener;
-import github.daneren2005.dsub.util.SyncUtil;
-import github.daneren2005.dsub.util.CacheCleaner;
-import github.daneren2005.dsub.util.Constants;
-import github.daneren2005.dsub.util.LoadingTask;
-import github.daneren2005.dsub.util.UserUtil;
-import github.daneren2005.dsub.util.Util;
-import github.daneren2005.dsub.adapter.PlaylistAdapter;
-
-import java.util.List;
-
-public class SelectPlaylistFragment extends SelectListFragment<Playlist> {
- private static final String TAG = SelectPlaylistFragment.class.getSimpleName();
-
- @Override
- public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, view, menuInfo);
-
- MenuInflater inflater = context.getMenuInflater();
- if (Util.isOffline(context)) {
- inflater.inflate(R.menu.select_playlist_context_offline, menu);
- }
- else {
- inflater.inflate(R.menu.select_playlist_context, menu);
-
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
- Playlist playlist = (Playlist) listView.getItemAtPosition(info.position);
- if(SyncUtil.isSyncedPlaylist(context, playlist.getId())) {
- menu.removeItem(R.id.playlist_menu_sync);
- } else {
- menu.removeItem(R.id.playlist_menu_stop_sync);
- }
-
- if(!ServerInfo.checkServerVersion(context, "1.8")) {
- menu.removeItem(R.id.playlist_update_info);
- } else if(playlist.getPublic() != null && playlist.getPublic() == true && playlist.getId().indexOf(".m3u") == -1 && !UserUtil.getCurrentUsername(context).equals(playlist.getOwner())) {
- menu.removeItem(R.id.playlist_update_info);
- menu.removeItem(R.id.playlist_menu_delete);
- }
- }
-
- recreateContextMenu(menu);
- }
-
- @Override
- public boolean onContextItemSelected(MenuItem menuItem) {
- if(menuItem.getGroupId() != getSupportTag()) {
- return false;
- }
-
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
- Playlist playlist = (Playlist) listView.getItemAtPosition(info.position);
-
- SubsonicFragment fragment;
- Bundle args;
- FragmentTransaction trans;
- switch (menuItem.getItemId()) {
- case R.id.playlist_menu_download:
- downloadPlaylist(playlist.getId(), playlist.getName(), false, true, false, false, true);
- break;
- case R.id.playlist_menu_sync:
- syncPlaylist(playlist);
- break;
- case R.id.playlist_menu_stop_sync:
- stopSyncPlaylist(playlist);
- break;
- case R.id.playlist_menu_play_now:
- fragment = new SelectDirectoryFragment();
- args = new Bundle();
- args.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID, playlist.getId());
- args.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME, playlist.getName());
- args.putBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, true);
- fragment.setArguments(args);
-
- replaceFragment(fragment);
- break;
- case R.id.playlist_menu_play_shuffled:
- fragment = new SelectDirectoryFragment();
- args = new Bundle();
- args.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID, playlist.getId());
- args.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME, playlist.getName());
- args.putBoolean(Constants.INTENT_EXTRA_NAME_SHUFFLE, true);
- args.putBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, true);
- fragment.setArguments(args);
-
- replaceFragment(fragment);
- break;
- case R.id.playlist_menu_delete:
- deletePlaylist(playlist);
- break;
- case R.id.playlist_info:
- displayPlaylistInfo(playlist);
- break;
- case R.id.playlist_update_info:
- updatePlaylistInfo(playlist);
- break;
- default:
- return false;
- }
- return true;
- }
-
- @Override
- public int getOptionsMenu() {
- return R.menu.abstract_top_menu;
- }
-
- @Override
- public ArrayAdapter getAdapter(List<Playlist> playlists) {
- return new PlaylistAdapter(context, playlists);
- }
-
- @Override
- public List<Playlist> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception {
- List<Playlist> playlists = musicService.getPlaylists(refresh, context, listener);
- if(!Util.isOffline(context) && refresh) {
- new CacheCleaner(context, getDownloadService()).cleanPlaylists(playlists);
- }
- return playlists;
- }
-
- @Override
- public int getTitleResource() {
- return R.string.playlist_label;
- }
-
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- Playlist playlist = (Playlist) parent.getItemAtPosition(position);
-
- SubsonicFragment fragment = new SelectDirectoryFragment();
- Bundle args = new Bundle();
- args.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID, playlist.getId());
- args.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME, playlist.getName());
- if(ServerInfo.checkServerVersion(context, "1.8") && (playlist.getOwner() != null && playlist.getOwner().equals(UserUtil.getCurrentUsername(context)) || playlist.getId().indexOf(".m3u") != -1)) {
- args.putBoolean(Constants.INTENT_EXTRA_NAME_PLAYLIST_OWNER, true);
- }
- fragment.setArguments(args);
-
- replaceFragment(fragment);
- }
-
- private void deletePlaylist(final Playlist playlist) {
- Util.confirmDialog(context, R.string.common_delete, playlist.getName(), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- new LoadingTask<Void>(context, false) {
- @Override
- protected Void doInBackground() throws Throwable {
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- musicService.deletePlaylist(playlist.getId(), context, null);
- SyncUtil.removeSyncedPlaylist(context, playlist.getId());
- return null;
- }
-
- @Override
- protected void done(Void result) {
- adapter.remove(playlist);
- adapter.notifyDataSetChanged();
- Util.toast(context, context.getResources().getString(R.string.menu_deleted_playlist, playlist.getName()));
- }
-
- @Override
- protected void error(Throwable error) {
- String msg;
- if (error instanceof OfflineException || error instanceof ServerTooOldException) {
- msg = getErrorMessage(error);
- } else {
- msg = context.getResources().getString(R.string.menu_deleted_playlist_error, playlist.getName()) + " " + getErrorMessage(error);
- }
-
- Util.toast(context, msg, false);
- }
- }.execute();
- }
- });
- }
-
- private void displayPlaylistInfo(final Playlist playlist) {
- String message = "Owner: " + playlist.getOwner() + "\nComments: " +
- ((playlist.getComment() == null) ? "" : playlist.getComment()) +
- "\nSong Count: " + playlist.getSongCount() +
- ((playlist.getPublic() == null) ? "" : ("\nPublic: " + playlist.getPublic())) +
- "\nCreation Date: " + playlist.getCreated().replace('T', ' ');
- Util.info(context, playlist.getName(), message);
- }
-
- private void updatePlaylistInfo(final Playlist playlist) {
- View dialogView = context.getLayoutInflater().inflate(R.layout.update_playlist, null);
- final EditText nameBox = (EditText)dialogView.findViewById(R.id.get_playlist_name);
- final EditText commentBox = (EditText)dialogView.findViewById(R.id.get_playlist_comment);
- final CheckBox publicBox = (CheckBox)dialogView.findViewById(R.id.get_playlist_public);
-
- nameBox.setText(playlist.getName());
- commentBox.setText(playlist.getComment());
- Boolean pub = playlist.getPublic();
- if(pub == null) {
- publicBox.setEnabled(false);
- } else {
- publicBox.setChecked(pub);
- }
-
- new AlertDialog.Builder(context)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setTitle(R.string.playlist_update_info)
- .setView(dialogView)
- .setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- new LoadingTask<Void>(context, false) {
- @Override
- protected Void doInBackground() throws Throwable {
- String name = nameBox.getText().toString();
- String comment = commentBox.getText().toString();
- boolean isPublic = publicBox.isChecked();
-
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- musicService.updatePlaylist(playlist.getId(), name, comment, isPublic, context, null);
-
- playlist.setName(name);
- playlist.setComment(comment);
- playlist.setPublic(isPublic);
-
- return null;
- }
-
- @Override
- protected void done(Void result) {
- Util.toast(context, context.getResources().getString(R.string.playlist_updated_info, playlist.getName()));
- }
-
- @Override
- protected void error(Throwable error) {
- String msg;
- if (error instanceof OfflineException || error instanceof ServerTooOldException) {
- msg = getErrorMessage(error);
- } else {
- msg = context.getResources().getString(R.string.playlist_updated_info_error, playlist.getName()) + " " + getErrorMessage(error);
- }
-
- Util.toast(context, msg, false);
- }
- }.execute();
- }
-
- })
- .setNegativeButton(R.string.common_cancel, null)
- .show();
- }
-
- private void syncPlaylist(Playlist playlist) {
- SyncUtil.addSyncedPlaylist(context, playlist.getId());
- downloadPlaylist(playlist.getId(), playlist.getName(), true, true, false, false, true);
- }
-
- private void stopSyncPlaylist(final Playlist playlist) {
- SyncUtil.removeSyncedPlaylist(context, playlist.getId());
-
- new LoadingTask<Void>(context, false) {
- @Override
- protected Void doInBackground() throws Throwable {
- // Unpin all of the songs in playlist
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- MusicDirectory root = musicService.getPlaylist(true, playlist.getId(), playlist.getName(), context, this);
- for(MusicDirectory.Entry entry: root.getChildren()) {
- DownloadFile file = new DownloadFile(context, entry, false);
- file.unpin();
- }
-
- return null;
- }
-
- @Override
- protected void done(Void result) {
-
- }
- }.execute();
- }
-}
+package github.daneren2005.dsub.fragments;
+
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.v4.app.FragmentTransaction;
+import android.view.ContextMenu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.CheckBox;
+import android.widget.EditText;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.MusicDirectory;
+import github.daneren2005.dsub.domain.Playlist;
+import github.daneren2005.dsub.domain.ServerInfo;
+import github.daneren2005.dsub.service.DownloadFile;
+import github.daneren2005.dsub.service.MusicService;
+import github.daneren2005.dsub.service.MusicServiceFactory;
+import github.daneren2005.dsub.service.OfflineException;
+import github.daneren2005.dsub.service.ServerTooOldException;
+import github.daneren2005.dsub.util.ProgressListener;
+import github.daneren2005.dsub.util.SyncUtil;
+import github.daneren2005.dsub.util.CacheCleaner;
+import github.daneren2005.dsub.util.Constants;
+import github.daneren2005.dsub.util.LoadingTask;
+import github.daneren2005.dsub.util.UserUtil;
+import github.daneren2005.dsub.util.Util;
+import github.daneren2005.dsub.adapter.PlaylistAdapter;
+
+import java.util.List;
+
+public class SelectPlaylistFragment extends SelectListFragment<Playlist> {
+ private static final String TAG = SelectPlaylistFragment.class.getSimpleName();
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
+ super.onCreateContextMenu(menu, view, menuInfo);
+
+ MenuInflater inflater = context.getMenuInflater();
+ if (Util.isOffline(context)) {
+ inflater.inflate(R.menu.select_playlist_context_offline, menu);
+ }
+ else {
+ inflater.inflate(R.menu.select_playlist_context, menu);
+
+ AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
+ Playlist playlist = (Playlist) listView.getItemAtPosition(info.position);
+ if(SyncUtil.isSyncedPlaylist(context, playlist.getId())) {
+ menu.removeItem(R.id.playlist_menu_sync);
+ } else {
+ menu.removeItem(R.id.playlist_menu_stop_sync);
+ }
+
+ if(!ServerInfo.checkServerVersion(context, "1.8")) {
+ menu.removeItem(R.id.playlist_update_info);
+ } else if(playlist.getPublic() != null && playlist.getPublic() == true && playlist.getId().indexOf(".m3u") == -1 && !UserUtil.getCurrentUsername(context).equals(playlist.getOwner())) {
+ menu.removeItem(R.id.playlist_update_info);
+ menu.removeItem(R.id.playlist_menu_delete);
+ }
+ }
+
+ recreateContextMenu(menu);
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem menuItem) {
+ if(menuItem.getGroupId() != getSupportTag()) {
+ return false;
+ }
+
+ AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
+ Playlist playlist = (Playlist) listView.getItemAtPosition(info.position);
+
+ SubsonicFragment fragment;
+ Bundle args;
+ FragmentTransaction trans;
+ switch (menuItem.getItemId()) {
+ case R.id.playlist_menu_download:
+ downloadPlaylist(playlist.getId(), playlist.getName(), false, true, false, false, true);
+ break;
+ case R.id.playlist_menu_sync:
+ syncPlaylist(playlist);
+ break;
+ case R.id.playlist_menu_stop_sync:
+ stopSyncPlaylist(playlist);
+ break;
+ case R.id.playlist_menu_play_now:
+ fragment = new SelectDirectoryFragment();
+ args = new Bundle();
+ args.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID, playlist.getId());
+ args.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME, playlist.getName());
+ args.putBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, true);
+ fragment.setArguments(args);
+
+ replaceFragment(fragment);
+ break;
+ case R.id.playlist_menu_play_shuffled:
+ fragment = new SelectDirectoryFragment();
+ args = new Bundle();
+ args.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID, playlist.getId());
+ args.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME, playlist.getName());
+ args.putBoolean(Constants.INTENT_EXTRA_NAME_SHUFFLE, true);
+ args.putBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, true);
+ fragment.setArguments(args);
+
+ replaceFragment(fragment);
+ break;
+ case R.id.playlist_menu_delete:
+ deletePlaylist(playlist);
+ break;
+ case R.id.playlist_info:
+ displayPlaylistInfo(playlist);
+ break;
+ case R.id.playlist_update_info:
+ updatePlaylistInfo(playlist);
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int getOptionsMenu() {
+ return R.menu.abstract_top_menu;
+ }
+
+ @Override
+ public ArrayAdapter getAdapter(List<Playlist> playlists) {
+ return new PlaylistAdapter(context, playlists);
+ }
+
+ @Override
+ public List<Playlist> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception {
+ List<Playlist> playlists = musicService.getPlaylists(refresh, context, listener);
+ if(!Util.isOffline(context) && refresh) {
+ new CacheCleaner(context, getDownloadService()).cleanPlaylists(playlists);
+ }
+ return playlists;
+ }
+
+ @Override
+ public int getTitleResource() {
+ return R.string.playlist_label;
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ Playlist playlist = (Playlist) parent.getItemAtPosition(position);
+
+ SubsonicFragment fragment = new SelectDirectoryFragment();
+ Bundle args = new Bundle();
+ args.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID, playlist.getId());
+ args.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME, playlist.getName());
+ if(ServerInfo.checkServerVersion(context, "1.8") && (playlist.getOwner() != null && playlist.getOwner().equals(UserUtil.getCurrentUsername(context)) || playlist.getId().indexOf(".m3u") != -1)) {
+ args.putBoolean(Constants.INTENT_EXTRA_NAME_PLAYLIST_OWNER, true);
+ }
+ fragment.setArguments(args);
+
+ replaceFragment(fragment);
+ }
+
+ private void deletePlaylist(final Playlist playlist) {
+ Util.confirmDialog(context, R.string.common_delete, playlist.getName(), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ new LoadingTask<Void>(context, false) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ musicService.deletePlaylist(playlist.getId(), context, null);
+ SyncUtil.removeSyncedPlaylist(context, playlist.getId());
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ adapter.remove(playlist);
+ adapter.notifyDataSetChanged();
+ Util.toast(context, context.getResources().getString(R.string.menu_deleted_playlist, playlist.getName()));
+ }
+
+ @Override
+ protected void error(Throwable error) {
+ String msg;
+ if (error instanceof OfflineException || error instanceof ServerTooOldException) {
+ msg = getErrorMessage(error);
+ } else {
+ msg = context.getResources().getString(R.string.menu_deleted_playlist_error, playlist.getName()) + " " + getErrorMessage(error);
+ }
+
+ Util.toast(context, msg, false);
+ }
+ }.execute();
+ }
+ });
+ }
+
+ private void displayPlaylistInfo(final Playlist playlist) {
+ String message = "Owner: " + playlist.getOwner() + "\nComments: " +
+ ((playlist.getComment() == null) ? "" : playlist.getComment()) +
+ "\nSong Count: " + playlist.getSongCount() +
+ ((playlist.getPublic() == null) ? "" : ("\nPublic: " + playlist.getPublic())) +
+ "\nCreation Date: " + playlist.getCreated().replace('T', ' ');
+ Util.info(context, playlist.getName(), message);
+ }
+
+ private void updatePlaylistInfo(final Playlist playlist) {
+ View dialogView = context.getLayoutInflater().inflate(R.layout.update_playlist, null);
+ final EditText nameBox = (EditText)dialogView.findViewById(R.id.get_playlist_name);
+ final EditText commentBox = (EditText)dialogView.findViewById(R.id.get_playlist_comment);
+ final CheckBox publicBox = (CheckBox)dialogView.findViewById(R.id.get_playlist_public);
+
+ nameBox.setText(playlist.getName());
+ commentBox.setText(playlist.getComment());
+ Boolean pub = playlist.getPublic();
+ if(pub == null) {
+ publicBox.setEnabled(false);
+ } else {
+ publicBox.setChecked(pub);
+ }
+
+ new AlertDialog.Builder(context)
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setTitle(R.string.playlist_update_info)
+ .setView(dialogView)
+ .setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ new LoadingTask<Void>(context, false) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ String name = nameBox.getText().toString();
+ String comment = commentBox.getText().toString();
+ boolean isPublic = publicBox.isChecked();
+
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ musicService.updatePlaylist(playlist.getId(), name, comment, isPublic, context, null);
+
+ playlist.setName(name);
+ playlist.setComment(comment);
+ playlist.setPublic(isPublic);
+
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ Util.toast(context, context.getResources().getString(R.string.playlist_updated_info, playlist.getName()));
+ }
+
+ @Override
+ protected void error(Throwable error) {
+ String msg;
+ if (error instanceof OfflineException || error instanceof ServerTooOldException) {
+ msg = getErrorMessage(error);
+ } else {
+ msg = context.getResources().getString(R.string.playlist_updated_info_error, playlist.getName()) + " " + getErrorMessage(error);
+ }
+
+ Util.toast(context, msg, false);
+ }
+ }.execute();
+ }
+
+ })
+ .setNegativeButton(R.string.common_cancel, null)
+ .show();
+ }
+
+ private void syncPlaylist(Playlist playlist) {
+ SyncUtil.addSyncedPlaylist(context, playlist.getId());
+ downloadPlaylist(playlist.getId(), playlist.getName(), true, true, false, false, true);
+ }
+
+ private void stopSyncPlaylist(final Playlist playlist) {
+ SyncUtil.removeSyncedPlaylist(context, playlist.getId());
+
+ new LoadingTask<Void>(context, false) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ // Unpin all of the songs in playlist
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ MusicDirectory root = musicService.getPlaylist(true, playlist.getId(), playlist.getName(), context, this);
+ for(MusicDirectory.Entry entry: root.getChildren()) {
+ DownloadFile file = new DownloadFile(context, entry, false);
+ file.unpin();
+ }
+
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+
+ }
+ }.execute();
+ }
+}
diff --git a/src/github/daneren2005/dsub/fragments/SelectPodcastsFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectPodcastsFragment.java
index 09307476..3a564f1c 100644
--- a/src/github/daneren2005/dsub/fragments/SelectPodcastsFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectPodcastsFragment.java
@@ -1,308 +1,308 @@
-/*
- 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 2010 (C) Sindre Mehus
- */
-package github.daneren2005.dsub.fragments;
-
-import android.app.AlertDialog;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.view.ContextMenu;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.TextView;
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.domain.MusicDirectory;
-import github.daneren2005.dsub.domain.PodcastChannel;
-import github.daneren2005.dsub.service.MusicService;
-import github.daneren2005.dsub.service.MusicServiceFactory;
-import github.daneren2005.dsub.service.OfflineException;
-import github.daneren2005.dsub.service.ServerTooOldException;
-import github.daneren2005.dsub.util.ProgressListener;
-import github.daneren2005.dsub.util.SyncUtil;
-import github.daneren2005.dsub.util.Constants;
-import github.daneren2005.dsub.util.LoadingTask;
-import github.daneren2005.dsub.util.SilentBackgroundTask;
-import github.daneren2005.dsub.util.UserUtil;
-import github.daneren2005.dsub.util.Util;
-import github.daneren2005.dsub.adapter.PodcastChannelAdapter;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- *
- * @author Scott
- */
-public class SelectPodcastsFragment extends SelectListFragment<PodcastChannel> {
- private static final String TAG = SelectPodcastsFragment.class.getSimpleName();
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if(super.onOptionsItemSelected(item)) {
- return true;
- }
-
- switch (item.getItemId()) {
- case R.id.menu_check:
- refreshPodcasts();
- break;
- case R.id.menu_add_podcast:
- addNewPodcast();
- break;
- }
-
- return false;
- }
-
- @Override
- public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, view, menuInfo);
-
- android.view.MenuInflater inflater = context.getMenuInflater();
- if(!Util.isOffline(context) && UserUtil.canPodcast()) {
- inflater.inflate(R.menu.select_podcasts_context, menu);
-
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
- PodcastChannel podcast = (PodcastChannel) listView.getItemAtPosition(info.position);
- if(SyncUtil.isSyncedPodcast(context, podcast.getId())) {
- menu.removeItem(R.id.podcast_menu_sync);
- } else {
- menu.removeItem(R.id.podcast_menu_stop_sync);
- }
- } else {
- inflater.inflate(R.menu.select_podcasts_context_offline, menu);
- }
-
- recreateContextMenu(menu);
- }
-
- @Override
- public boolean onContextItemSelected(MenuItem menuItem) {
- if(menuItem.getGroupId() != getSupportTag()) {
- return false;
- }
-
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
- PodcastChannel channel = (PodcastChannel) listView.getItemAtPosition(info.position);
-
- switch (menuItem.getItemId()) {
- case R.id.podcast_menu_sync:
- syncPodcast(channel);
- break;
- case R.id.podcast_menu_stop_sync:
- stopSyncPodcast(channel);
- break;
- case R.id.podcast_channel_info:
- displayPodcastInfo(channel);
- break;
- case R.id.podcast_channel_delete:
- deletePodcast(channel);
- break;
- }
-
- return true;
- }
-
- @Override
- public int getOptionsMenu() {
- return (UserUtil.canPodcast() && !Util.isOffline(context)) ? R.menu.select_podcasts : R.menu.abstract_top_menu;
- }
-
- @Override
- public ArrayAdapter getAdapter(List<PodcastChannel> channels) {
- return new PodcastChannelAdapter(context, channels);
- }
-
- @Override
- public List<PodcastChannel> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception {
- return musicService.getPodcastChannels(refresh, context, listener);
- }
-
- @Override
- public int getTitleResource() {
- return R.string.button_bar_podcasts;
- }
-
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- PodcastChannel channel = (PodcastChannel) parent.getItemAtPosition(position);
-
- if("error".equals(channel.getStatus())) {
- Util.toast(context, context.getResources().getString(R.string.select_podcasts_invalid_podcast_channel, channel.getErrorMessage() == null ? "error" : channel.getErrorMessage()));
- } else if("downloading".equals(channel.getStatus())) {
- Util.toast(context, R.string.select_podcasts_initializing);
- } else {
- SubsonicFragment fragment = new SelectDirectoryFragment();
- Bundle args = new Bundle();
- args.putString(Constants.INTENT_EXTRA_NAME_PODCAST_ID, channel.getId());
- args.putString(Constants.INTENT_EXTRA_NAME_PODCAST_NAME, channel.getName());
- args.putString(Constants.INTENT_EXTRA_NAME_PODCAST_DESCRIPTION, channel.getDescription());
- fragment.setArguments(args);
-
- replaceFragment(fragment);
- }
- }
-
- public void refreshPodcasts() {
- new SilentBackgroundTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- musicService.refreshPodcasts(context, null);
- return null;
- }
-
- @Override
- protected void done(Void result) {
- Util.toast(context, R.string.select_podcasts_refreshing);
- }
-
- @Override
- protected void error(Throwable error) {
- Util.toast(context, getErrorMessage(error), false);
- }
- }.execute();
- }
-
- private void addNewPodcast() {
- View dialogView = context.getLayoutInflater().inflate(R.layout.create_podcast, null);
- final TextView urlBox = (TextView) dialogView.findViewById(R.id.create_podcast_url);
-
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
- builder.setTitle(R.string.menu_add_podcast)
- .setView(dialogView)
- .setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int id) {
- addNewPodcast(urlBox.getText().toString());
- }
- })
- .setNegativeButton(R.string.common_cancel, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int id) {
- dialog.cancel();
- }
- })
- .setCancelable(true);
-
- AlertDialog dialog = builder.create();
- dialog.show();
- }
- private void addNewPodcast(final String url) {
- new LoadingTask<Void>(context, false) {
- @Override
- protected Void doInBackground() throws Throwable {
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- musicService.createPodcastChannel(url, context, null);
- return null;
- }
-
- @Override
- protected void done(Void result) {
- refresh();
- }
-
- @Override
- protected void error(Throwable error) {
- String msg;
- if (error instanceof OfflineException || error instanceof ServerTooOldException) {
- msg = getErrorMessage(error);
- } else {
- msg = context.getResources().getString(R.string.select_podcasts_created_error) + " " + getErrorMessage(error);
- }
-
- Util.toast(context, msg, false);
- }
- }.execute();
- }
-
- private void displayPodcastInfo(final PodcastChannel channel) {
- String message = ((channel.getName()) == null ? "" : "Title: " + channel.getName()) +
- "\nURL: " + channel.getUrl() +
- "\nStatus: " + channel.getStatus() +
- ((channel.getErrorMessage()) == null ? "" : "\nError Message: " + channel.getErrorMessage()) +
- ((channel.getDescription()) == null ? "" : "\n\nDescription: " + channel.getDescription());
-
- Util.info(context, channel.getName(), message);
- }
-
- private void deletePodcast(final PodcastChannel channel) {
- Util.confirmDialog(context, R.string.common_delete, channel.getName(), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- new LoadingTask<Void>(context, false) {
- @Override
- protected Void doInBackground() throws Throwable {
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- musicService.deletePodcastChannel(channel.getId(), context, null);
- stopSyncPodcast(channel);
- return null;
- }
-
- @Override
- protected void done(Void result) {
- adapter.remove(channel);
- adapter.notifyDataSetChanged();
- Util.toast(context, context.getResources().getString(R.string.select_podcasts_deleted, channel.getName()));
- }
-
- @Override
- protected void error(Throwable error) {
- String msg;
- if (error instanceof OfflineException || error instanceof ServerTooOldException) {
- msg = getErrorMessage(error);
- } else {
- msg = context.getResources().getString(R.string.select_podcasts_deleted_error, channel.getName()) + " " + getErrorMessage(error);
- }
-
- Util.toast(context, msg, false);
- }
- }.execute();
- }
- });
- }
-
- private void syncPodcast(final PodcastChannel podcast) {
- new LoadingTask<MusicDirectory>(context, false) {
- @Override
- protected MusicDirectory doInBackground() throws Throwable {
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- return musicService.getPodcastEpisodes(true, podcast.getId(), context, this);
- }
-
- @Override
- protected void done(MusicDirectory result) {
- List<String> existingEpisodes = new ArrayList<String>();
- for(MusicDirectory.Entry entry: result.getChildren()) {
- String id = entry.getId();
- if(id != null) {
- existingEpisodes.add(entry.getId());
- }
- }
-
- SyncUtil.addSyncedPodcast(context, podcast.getId(), existingEpisodes);
- }
- }.execute();
- }
-
- private void stopSyncPodcast(PodcastChannel podcast) {
- SyncUtil.removeSyncedPodcast(context, podcast.getId());
- }
-}
+/*
+ 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 2010 (C) Sindre Mehus
+ */
+package github.daneren2005.dsub.fragments;
+
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.view.ContextMenu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.TextView;
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.MusicDirectory;
+import github.daneren2005.dsub.domain.PodcastChannel;
+import github.daneren2005.dsub.service.MusicService;
+import github.daneren2005.dsub.service.MusicServiceFactory;
+import github.daneren2005.dsub.service.OfflineException;
+import github.daneren2005.dsub.service.ServerTooOldException;
+import github.daneren2005.dsub.util.ProgressListener;
+import github.daneren2005.dsub.util.SyncUtil;
+import github.daneren2005.dsub.util.Constants;
+import github.daneren2005.dsub.util.LoadingTask;
+import github.daneren2005.dsub.util.SilentBackgroundTask;
+import github.daneren2005.dsub.util.UserUtil;
+import github.daneren2005.dsub.util.Util;
+import github.daneren2005.dsub.adapter.PodcastChannelAdapter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ *
+ * @author Scott
+ */
+public class SelectPodcastsFragment extends SelectListFragment<PodcastChannel> {
+ private static final String TAG = SelectPodcastsFragment.class.getSimpleName();
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if(super.onOptionsItemSelected(item)) {
+ return true;
+ }
+
+ switch (item.getItemId()) {
+ case R.id.menu_check:
+ refreshPodcasts();
+ break;
+ case R.id.menu_add_podcast:
+ addNewPodcast();
+ break;
+ }
+
+ return false;
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
+ super.onCreateContextMenu(menu, view, menuInfo);
+
+ android.view.MenuInflater inflater = context.getMenuInflater();
+ if(!Util.isOffline(context) && UserUtil.canPodcast()) {
+ inflater.inflate(R.menu.select_podcasts_context, menu);
+
+ AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
+ PodcastChannel podcast = (PodcastChannel) listView.getItemAtPosition(info.position);
+ if(SyncUtil.isSyncedPodcast(context, podcast.getId())) {
+ menu.removeItem(R.id.podcast_menu_sync);
+ } else {
+ menu.removeItem(R.id.podcast_menu_stop_sync);
+ }
+ } else {
+ inflater.inflate(R.menu.select_podcasts_context_offline, menu);
+ }
+
+ recreateContextMenu(menu);
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem menuItem) {
+ if(menuItem.getGroupId() != getSupportTag()) {
+ return false;
+ }
+
+ AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
+ PodcastChannel channel = (PodcastChannel) listView.getItemAtPosition(info.position);
+
+ switch (menuItem.getItemId()) {
+ case R.id.podcast_menu_sync:
+ syncPodcast(channel);
+ break;
+ case R.id.podcast_menu_stop_sync:
+ stopSyncPodcast(channel);
+ break;
+ case R.id.podcast_channel_info:
+ displayPodcastInfo(channel);
+ break;
+ case R.id.podcast_channel_delete:
+ deletePodcast(channel);
+ break;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int getOptionsMenu() {
+ return (UserUtil.canPodcast() && !Util.isOffline(context)) ? R.menu.select_podcasts : R.menu.abstract_top_menu;
+ }
+
+ @Override
+ public ArrayAdapter getAdapter(List<PodcastChannel> channels) {
+ return new PodcastChannelAdapter(context, channels);
+ }
+
+ @Override
+ public List<PodcastChannel> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception {
+ return musicService.getPodcastChannels(refresh, context, listener);
+ }
+
+ @Override
+ public int getTitleResource() {
+ return R.string.button_bar_podcasts;
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ PodcastChannel channel = (PodcastChannel) parent.getItemAtPosition(position);
+
+ if("error".equals(channel.getStatus())) {
+ Util.toast(context, context.getResources().getString(R.string.select_podcasts_invalid_podcast_channel, channel.getErrorMessage() == null ? "error" : channel.getErrorMessage()));
+ } else if("downloading".equals(channel.getStatus())) {
+ Util.toast(context, R.string.select_podcasts_initializing);
+ } else {
+ SubsonicFragment fragment = new SelectDirectoryFragment();
+ Bundle args = new Bundle();
+ args.putString(Constants.INTENT_EXTRA_NAME_PODCAST_ID, channel.getId());
+ args.putString(Constants.INTENT_EXTRA_NAME_PODCAST_NAME, channel.getName());
+ args.putString(Constants.INTENT_EXTRA_NAME_PODCAST_DESCRIPTION, channel.getDescription());
+ fragment.setArguments(args);
+
+ replaceFragment(fragment);
+ }
+ }
+
+ public void refreshPodcasts() {
+ new SilentBackgroundTask<Void>(context) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ musicService.refreshPodcasts(context, null);
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ Util.toast(context, R.string.select_podcasts_refreshing);
+ }
+
+ @Override
+ protected void error(Throwable error) {
+ Util.toast(context, getErrorMessage(error), false);
+ }
+ }.execute();
+ }
+
+ private void addNewPodcast() {
+ View dialogView = context.getLayoutInflater().inflate(R.layout.create_podcast, null);
+ final TextView urlBox = (TextView) dialogView.findViewById(R.id.create_podcast_url);
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.menu_add_podcast)
+ .setView(dialogView)
+ .setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ addNewPodcast(urlBox.getText().toString());
+ }
+ })
+ .setNegativeButton(R.string.common_cancel, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ dialog.cancel();
+ }
+ })
+ .setCancelable(true);
+
+ AlertDialog dialog = builder.create();
+ dialog.show();
+ }
+ private void addNewPodcast(final String url) {
+ new LoadingTask<Void>(context, false) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ musicService.createPodcastChannel(url, context, null);
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ refresh();
+ }
+
+ @Override
+ protected void error(Throwable error) {
+ String msg;
+ if (error instanceof OfflineException || error instanceof ServerTooOldException) {
+ msg = getErrorMessage(error);
+ } else {
+ msg = context.getResources().getString(R.string.select_podcasts_created_error) + " " + getErrorMessage(error);
+ }
+
+ Util.toast(context, msg, false);
+ }
+ }.execute();
+ }
+
+ private void displayPodcastInfo(final PodcastChannel channel) {
+ String message = ((channel.getName()) == null ? "" : "Title: " + channel.getName()) +
+ "\nURL: " + channel.getUrl() +
+ "\nStatus: " + channel.getStatus() +
+ ((channel.getErrorMessage()) == null ? "" : "\nError Message: " + channel.getErrorMessage()) +
+ ((channel.getDescription()) == null ? "" : "\n\nDescription: " + channel.getDescription());
+
+ Util.info(context, channel.getName(), message);
+ }
+
+ private void deletePodcast(final PodcastChannel channel) {
+ Util.confirmDialog(context, R.string.common_delete, channel.getName(), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ new LoadingTask<Void>(context, false) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ musicService.deletePodcastChannel(channel.getId(), context, null);
+ stopSyncPodcast(channel);
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ adapter.remove(channel);
+ adapter.notifyDataSetChanged();
+ Util.toast(context, context.getResources().getString(R.string.select_podcasts_deleted, channel.getName()));
+ }
+
+ @Override
+ protected void error(Throwable error) {
+ String msg;
+ if (error instanceof OfflineException || error instanceof ServerTooOldException) {
+ msg = getErrorMessage(error);
+ } else {
+ msg = context.getResources().getString(R.string.select_podcasts_deleted_error, channel.getName()) + " " + getErrorMessage(error);
+ }
+
+ Util.toast(context, msg, false);
+ }
+ }.execute();
+ }
+ });
+ }
+
+ private void syncPodcast(final PodcastChannel podcast) {
+ new LoadingTask<MusicDirectory>(context, false) {
+ @Override
+ protected MusicDirectory doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ return musicService.getPodcastEpisodes(true, podcast.getId(), context, this);
+ }
+
+ @Override
+ protected void done(MusicDirectory result) {
+ List<String> existingEpisodes = new ArrayList<String>();
+ for(MusicDirectory.Entry entry: result.getChildren()) {
+ String id = entry.getId();
+ if(id != null) {
+ existingEpisodes.add(entry.getId());
+ }
+ }
+
+ SyncUtil.addSyncedPodcast(context, podcast.getId(), existingEpisodes);
+ }
+ }.execute();
+ }
+
+ private void stopSyncPodcast(PodcastChannel podcast) {
+ SyncUtil.removeSyncedPodcast(context, podcast.getId());
+ }
+}
diff --git a/src/github/daneren2005/dsub/fragments/SelectShareFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectShareFragment.java
index d15f170d..07cd3bef 100644
--- a/src/github/daneren2005/dsub/fragments/SelectShareFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectShareFragment.java
@@ -1,216 +1,216 @@
-package github.daneren2005.dsub.fragments;
-
-import android.app.AlertDialog;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.view.ContextMenu;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.CheckBox;
-import android.widget.CompoundButton;
-import android.widget.DatePicker;
-import android.widget.EditText;
-
-import java.util.Date;
-import java.util.List;
-
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.domain.Share;
-import github.daneren2005.dsub.service.MusicService;
-import github.daneren2005.dsub.service.MusicServiceFactory;
-import github.daneren2005.dsub.service.OfflineException;
-import github.daneren2005.dsub.service.ServerTooOldException;
-import github.daneren2005.dsub.util.Constants;
-import github.daneren2005.dsub.util.LoadingTask;
-import github.daneren2005.dsub.util.ProgressListener;
-import github.daneren2005.dsub.util.Util;
-import github.daneren2005.dsub.adapter.ShareAdapter;
-
-/**
- * Created by Scott on 12/28/13.
- */
-public class SelectShareFragment extends SelectListFragment<Share> {
- private static final String TAG = SelectShareFragment.class.getSimpleName();
-
- @Override
- public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, view, menuInfo);
- android.view.MenuInflater inflater = context.getMenuInflater();
- inflater.inflate(R.menu.select_share_context, menu);
- recreateContextMenu(menu);
- }
-
- @Override
- public boolean onContextItemSelected(MenuItem menuItem) {
- if(menuItem.getGroupId() != getSupportTag()) {
- return false;
- }
-
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
- Share share = (Share) listView.getItemAtPosition(info.position);
-
- switch (menuItem.getItemId()) {
- case R.id.share_menu_share:
- shareExternal(share);
- break;
- case R.id.share_menu_info:
- displayShareInfo(share);
- break;
- case R.id.share_menu_delete:
- deleteShare(share);
- break;
- case R.id.share_update_info:
- updateShareInfo(share);
- break;
- }
-
- return true;
- }
-
- @Override
- public int getOptionsMenu() {
- return R.menu.abstract_top_menu;
- }
-
- @Override
- public ArrayAdapter getAdapter(List<Share> objs) {
- return new ShareAdapter(context, objs);
- }
-
- @Override
- public List<Share> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception {
- return musicService.getShares(context, listener);
- }
-
- @Override
- public int getTitleResource() {
- return R.string.button_bar_shares;
- }
-
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- Share share = (Share) parent.getItemAtPosition(position);
-
- SubsonicFragment fragment = new SelectDirectoryFragment();
- Bundle args = new Bundle();
- args.putSerializable(Constants.INTENT_EXTRA_NAME_SHARE, share);
- fragment.setArguments(args);
-
- replaceFragment(fragment);
- }
-
- private void displayShareInfo(final Share share) {
- String message = context.getResources().getString(R.string.share_info,
- share.getUsername(), (share.getDescription() != null) ? share.getDescription() : "", share.getUrl(),
- Util.formatDate(share.getCreated()), Util.formatDate(share.getLastVisited()), Util.formatDate(share.getExpires()), share.getVisitCount());
- Util.info(context, share.getName(), message);
- }
-
- private void updateShareInfo(final Share share) {
- View dialogView = context.getLayoutInflater().inflate(R.layout.update_share, null);
- final EditText nameBox = (EditText)dialogView.findViewById(R.id.get_share_name);
- final DatePicker expireBox = (DatePicker)dialogView.findViewById(R.id.get_share_expire);
- final CheckBox noExpiresBox = (CheckBox)dialogView.findViewById(R.id.get_share_no_expire);
-
- nameBox.setText(share.getDescription());
- Date expires = share.getExpires();
- if(expires != null) {
- expireBox.updateDate(expires.getYear() + 1900, expires.getMonth(), expires.getDate());
- }
-
- boolean noExpires = share.getExpires() == null;
- if(noExpires) {
- expireBox.setEnabled(false);
- noExpiresBox.setChecked(true);
- }
-
- noExpiresBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- expireBox.setEnabled(!isChecked);
- }
- });
-
- new AlertDialog.Builder(context)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setTitle(R.string.playlist_update_info)
- .setView(dialogView)
- .setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- new LoadingTask<Void>(context, false) {
- @Override
- protected Void doInBackground() throws Throwable {
- Long expiresIn = 0L;
- if (!noExpiresBox.isChecked()) {
- Date expires = new Date(expireBox.getYear() - 1900, expireBox.getMonth(), expireBox.getDayOfMonth());
- expiresIn = expires.getTime();
- }
-
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- musicService.updateShare(share.getId(), nameBox.getText().toString(), expiresIn, context, null);
- return null;
- }
-
- @Override
- protected void done(Void result) {
- refresh();
- Util.toast(context, context.getResources().getString(R.string.share_updated_info, share.getName()));
- }
-
- @Override
- protected void error(Throwable error) {
- String msg;
- if (error instanceof OfflineException || error instanceof ServerTooOldException) {
- msg = getErrorMessage(error);
- } else {
- msg = context.getResources().getString(R.string.share_updated_info_error, share.getName()) + " " + getErrorMessage(error);
- }
-
- Util.toast(context, msg, false);
- }
- }.execute();
- }
-
- })
- .setNegativeButton(R.string.common_cancel, null)
- .show();
- }
-
- private void deleteShare(final Share share) {
- Util.confirmDialog(context, R.string.common_delete, share.getName(), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- new LoadingTask<Void>(context, false) {
- @Override
- protected Void doInBackground() throws Throwable {
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- musicService.deleteShare(share.getId(), context, null);
- return null;
- }
-
- @Override
- protected void done(Void result) {
- adapter.remove(share);
- adapter.notifyDataSetChanged();
- Util.toast(context, context.getResources().getString(R.string.share_deleted, share.getName()));
- }
-
- @Override
- protected void error(Throwable error) {
- String msg;
- if (error instanceof OfflineException || error instanceof ServerTooOldException) {
- msg = getErrorMessage(error);
- } else {
- msg = context.getResources().getString(R.string.share_deleted_error, share.getName()) + " " + getErrorMessage(error);
- }
-
- Util.toast(context, msg, false);
- }
- }.execute();
- }
- });
- }
-}
+package github.daneren2005.dsub.fragments;
+
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.view.ContextMenu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.DatePicker;
+import android.widget.EditText;
+
+import java.util.Date;
+import java.util.List;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.Share;
+import github.daneren2005.dsub.service.MusicService;
+import github.daneren2005.dsub.service.MusicServiceFactory;
+import github.daneren2005.dsub.service.OfflineException;
+import github.daneren2005.dsub.service.ServerTooOldException;
+import github.daneren2005.dsub.util.Constants;
+import github.daneren2005.dsub.util.LoadingTask;
+import github.daneren2005.dsub.util.ProgressListener;
+import github.daneren2005.dsub.util.Util;
+import github.daneren2005.dsub.adapter.ShareAdapter;
+
+/**
+ * Created by Scott on 12/28/13.
+ */
+public class SelectShareFragment extends SelectListFragment<Share> {
+ private static final String TAG = SelectShareFragment.class.getSimpleName();
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
+ super.onCreateContextMenu(menu, view, menuInfo);
+ android.view.MenuInflater inflater = context.getMenuInflater();
+ inflater.inflate(R.menu.select_share_context, menu);
+ recreateContextMenu(menu);
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem menuItem) {
+ if(menuItem.getGroupId() != getSupportTag()) {
+ return false;
+ }
+
+ AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
+ Share share = (Share) listView.getItemAtPosition(info.position);
+
+ switch (menuItem.getItemId()) {
+ case R.id.share_menu_share:
+ shareExternal(share);
+ break;
+ case R.id.share_menu_info:
+ displayShareInfo(share);
+ break;
+ case R.id.share_menu_delete:
+ deleteShare(share);
+ break;
+ case R.id.share_update_info:
+ updateShareInfo(share);
+ break;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int getOptionsMenu() {
+ return R.menu.abstract_top_menu;
+ }
+
+ @Override
+ public ArrayAdapter getAdapter(List<Share> objs) {
+ return new ShareAdapter(context, objs);
+ }
+
+ @Override
+ public List<Share> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception {
+ return musicService.getShares(context, listener);
+ }
+
+ @Override
+ public int getTitleResource() {
+ return R.string.button_bar_shares;
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ Share share = (Share) parent.getItemAtPosition(position);
+
+ SubsonicFragment fragment = new SelectDirectoryFragment();
+ Bundle args = new Bundle();
+ args.putSerializable(Constants.INTENT_EXTRA_NAME_SHARE, share);
+ fragment.setArguments(args);
+
+ replaceFragment(fragment);
+ }
+
+ private void displayShareInfo(final Share share) {
+ String message = context.getResources().getString(R.string.share_info,
+ share.getUsername(), (share.getDescription() != null) ? share.getDescription() : "", share.getUrl(),
+ Util.formatDate(share.getCreated()), Util.formatDate(share.getLastVisited()), Util.formatDate(share.getExpires()), share.getVisitCount());
+ Util.info(context, share.getName(), message);
+ }
+
+ private void updateShareInfo(final Share share) {
+ View dialogView = context.getLayoutInflater().inflate(R.layout.update_share, null);
+ final EditText nameBox = (EditText)dialogView.findViewById(R.id.get_share_name);
+ final DatePicker expireBox = (DatePicker)dialogView.findViewById(R.id.get_share_expire);
+ final CheckBox noExpiresBox = (CheckBox)dialogView.findViewById(R.id.get_share_no_expire);
+
+ nameBox.setText(share.getDescription());
+ Date expires = share.getExpires();
+ if(expires != null) {
+ expireBox.updateDate(expires.getYear() + 1900, expires.getMonth(), expires.getDate());
+ }
+
+ boolean noExpires = share.getExpires() == null;
+ if(noExpires) {
+ expireBox.setEnabled(false);
+ noExpiresBox.setChecked(true);
+ }
+
+ noExpiresBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ expireBox.setEnabled(!isChecked);
+ }
+ });
+
+ new AlertDialog.Builder(context)
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setTitle(R.string.playlist_update_info)
+ .setView(dialogView)
+ .setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ new LoadingTask<Void>(context, false) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ Long expiresIn = 0L;
+ if (!noExpiresBox.isChecked()) {
+ Date expires = new Date(expireBox.getYear() - 1900, expireBox.getMonth(), expireBox.getDayOfMonth());
+ expiresIn = expires.getTime();
+ }
+
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ musicService.updateShare(share.getId(), nameBox.getText().toString(), expiresIn, context, null);
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ refresh();
+ Util.toast(context, context.getResources().getString(R.string.share_updated_info, share.getName()));
+ }
+
+ @Override
+ protected void error(Throwable error) {
+ String msg;
+ if (error instanceof OfflineException || error instanceof ServerTooOldException) {
+ msg = getErrorMessage(error);
+ } else {
+ msg = context.getResources().getString(R.string.share_updated_info_error, share.getName()) + " " + getErrorMessage(error);
+ }
+
+ Util.toast(context, msg, false);
+ }
+ }.execute();
+ }
+
+ })
+ .setNegativeButton(R.string.common_cancel, null)
+ .show();
+ }
+
+ private void deleteShare(final Share share) {
+ Util.confirmDialog(context, R.string.common_delete, share.getName(), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ new LoadingTask<Void>(context, false) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ musicService.deleteShare(share.getId(), context, null);
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ adapter.remove(share);
+ adapter.notifyDataSetChanged();
+ Util.toast(context, context.getResources().getString(R.string.share_deleted, share.getName()));
+ }
+
+ @Override
+ protected void error(Throwable error) {
+ String msg;
+ if (error instanceof OfflineException || error instanceof ServerTooOldException) {
+ msg = getErrorMessage(error);
+ } else {
+ msg = context.getResources().getString(R.string.share_deleted_error, share.getName()) + " " + getErrorMessage(error);
+ }
+
+ Util.toast(context, msg, false);
+ }
+ }.execute();
+ }
+ });
+ }
+}
diff --git a/src/github/daneren2005/dsub/fragments/SelectVideoFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectVideoFragment.java
index b4d34ff9..b4d34ff9 100644
--- a/src/github/daneren2005/dsub/fragments/SelectVideoFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectVideoFragment.java
diff --git a/src/github/daneren2005/dsub/fragments/SelectYearFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectYearFragment.java
index 03d62307..dc19acad 100644
--- a/src/github/daneren2005/dsub/fragments/SelectYearFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectYearFragment.java
@@ -1,78 +1,78 @@
-/*
- 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 2010 (C) Sindre Mehus
-*/
-package github.daneren2005.dsub.fragments;
-
-import android.os.Bundle;
-import android.view.View;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.service.MusicService;
-import github.daneren2005.dsub.util.Constants;
-import github.daneren2005.dsub.util.ProgressListener;
-
-/**
- * Created by Scott on 12/23/13.
- */
-public class SelectYearFragment extends SelectListFragment<Integer> {
-
- @Override
- public int getOptionsMenu() {
- return R.menu.empty;
- }
-
- @Override
- public ArrayAdapter getAdapter(List<Integer> objs) {
- return new ArrayAdapter<Integer>(context, android.R.layout.simple_list_item_1, objs);
- }
-
- @Override
- public List<Integer> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception {
- List<Integer> decades = new ArrayList<Integer>();
- for(int i = 2010; i >= 1920; i -= 10) {
- decades.add(i);
- }
-
- return decades;
- }
-
- @Override
- public int getTitleResource() {
- return R.string.main_albums_year;
- }
-
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- Integer decade = (Integer) parent.getItemAtPosition(position);
-
- SubsonicFragment fragment = new SelectDirectoryFragment();
- Bundle args = new Bundle();
- args.putString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE, "years");
- args.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 20);
- args.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0);
- args.putString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_EXTRA, Integer.toString(decade));
- fragment.setArguments(args);
-
- replaceFragment(fragment);
- }
-}
+/*
+ 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 2010 (C) Sindre Mehus
+*/
+package github.daneren2005.dsub.fragments;
+
+import android.os.Bundle;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.service.MusicService;
+import github.daneren2005.dsub.util.Constants;
+import github.daneren2005.dsub.util.ProgressListener;
+
+/**
+ * Created by Scott on 12/23/13.
+ */
+public class SelectYearFragment extends SelectListFragment<Integer> {
+
+ @Override
+ public int getOptionsMenu() {
+ return R.menu.empty;
+ }
+
+ @Override
+ public ArrayAdapter getAdapter(List<Integer> objs) {
+ return new ArrayAdapter<Integer>(context, android.R.layout.simple_list_item_1, objs);
+ }
+
+ @Override
+ public List<Integer> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception {
+ List<Integer> decades = new ArrayList<Integer>();
+ for(int i = 2010; i >= 1920; i -= 10) {
+ decades.add(i);
+ }
+
+ return decades;
+ }
+
+ @Override
+ public int getTitleResource() {
+ return R.string.main_albums_year;
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ Integer decade = (Integer) parent.getItemAtPosition(position);
+
+ SubsonicFragment fragment = new SelectDirectoryFragment();
+ Bundle args = new Bundle();
+ args.putString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE, "years");
+ args.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 20);
+ args.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0);
+ args.putString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_EXTRA, Integer.toString(decade));
+ fragment.setArguments(args);
+
+ replaceFragment(fragment);
+ }
+}
diff --git a/src/github/daneren2005/dsub/fragments/SettingsFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SettingsFragment.java
index 3be21a67..3be21a67 100644
--- a/src/github/daneren2005/dsub/fragments/SettingsFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SettingsFragment.java
diff --git a/src/github/daneren2005/dsub/fragments/SimilarArtistFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SimilarArtistFragment.java
index 79e759cc..79e759cc 100644
--- a/src/github/daneren2005/dsub/fragments/SimilarArtistFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SimilarArtistFragment.java
diff --git a/src/github/daneren2005/dsub/fragments/SubsonicFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java
index b6b67a7b..109983ba 100644
--- a/src/github/daneren2005/dsub/fragments/SubsonicFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java
@@ -1,1817 +1,1817 @@
-/*
- 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 github.daneren2005.dsub.fragments;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.media.MediaMetadataRetriever;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.StatFs;
-import android.support.v4.app.Fragment;
-import android.support.v4.widget.SwipeRefreshLayout;
-import android.util.Log;
-import android.view.ContextMenu;
-import android.view.GestureDetector;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AbsListView;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.Button;
-import android.widget.CheckBox;
-import android.widget.EditText;
-import android.widget.RatingBar;
-import android.widget.TextView;
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.activity.DownloadActivity;
-import github.daneren2005.dsub.activity.SubsonicActivity;
-import github.daneren2005.dsub.activity.SubsonicFragmentActivity;
-import github.daneren2005.dsub.domain.Artist;
-import github.daneren2005.dsub.domain.Bookmark;
-import github.daneren2005.dsub.domain.Genre;
-import github.daneren2005.dsub.domain.MusicDirectory;
-import github.daneren2005.dsub.domain.Playlist;
-import github.daneren2005.dsub.domain.PodcastEpisode;
-import github.daneren2005.dsub.domain.ServerInfo;
-import github.daneren2005.dsub.domain.Share;
-import github.daneren2005.dsub.service.DownloadFile;
-import github.daneren2005.dsub.service.DownloadService;
-import github.daneren2005.dsub.service.MediaStoreService;
-import github.daneren2005.dsub.service.MusicService;
-import github.daneren2005.dsub.service.MusicServiceFactory;
-import github.daneren2005.dsub.service.OfflineException;
-import github.daneren2005.dsub.service.ServerTooOldException;
-import github.daneren2005.dsub.util.Constants;
-import github.daneren2005.dsub.util.FileUtil;
-import github.daneren2005.dsub.util.ImageLoader;
-import github.daneren2005.dsub.util.ProgressListener;
-import github.daneren2005.dsub.util.SilentBackgroundTask;
-import github.daneren2005.dsub.util.LoadingTask;
-import github.daneren2005.dsub.util.UserUtil;
-import github.daneren2005.dsub.util.Util;
-import github.daneren2005.dsub.view.AlbumCell;
-import github.daneren2005.dsub.view.AlbumView;
-import github.daneren2005.dsub.view.ArtistEntryView;
-import github.daneren2005.dsub.view.ArtistView;
-import github.daneren2005.dsub.view.PlaylistSongView;
-import github.daneren2005.dsub.view.SongView;
-import github.daneren2005.dsub.view.UpdateView;
-
-import java.io.File;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Date;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Random;
-
-import static github.daneren2005.dsub.domain.MusicDirectory.Entry;
-
-public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
- private static final String TAG = SubsonicFragment.class.getSimpleName();
- private static int TAG_INC = 10;
- private int tag;
-
- protected SubsonicActivity context;
- protected CharSequence title = null;
- protected CharSequence subtitle = null;
- protected View rootView;
- protected boolean primaryFragment = false;
- protected boolean secondaryFragment = false;
- protected boolean invalidated = false;
- protected static Random random = new Random();
- protected GestureDetector gestureScanner;
- protected Share share;
- protected boolean artist = false;
- protected boolean artistOverride = false;
- protected SwipeRefreshLayout refreshLayout;
- protected boolean firstRun;
-
- public SubsonicFragment() {
- super();
- tag = TAG_INC++;
- }
-
- @Override
- public void onCreate(Bundle bundle) {
- super.onCreate(bundle);
-
- if(bundle != null) {
- String name = bundle.getString(Constants.FRAGMENT_NAME);
- if(name != null) {
- title = name;
- }
- }
- firstRun = true;
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- if(title != null) {
- outState.putString(Constants.FRAGMENT_NAME, title.toString());
- }
- }
-
- @Override
- public void onResume() {
- super.onResume();
- if(firstRun) {
- firstRun = false;
- } else {
- UpdateView.triggerUpdate();
- }
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- }
-
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- context = (SubsonicActivity)activity;
- }
-
- public void setContext(SubsonicActivity context) {
- this.context = context;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.menu_shuffle:
- onShuffleRequested();
- return true;
- case R.id.menu_search:
- context.onSearchRequested();
- return true;
- case R.id.menu_exit:
- exit();
- return true;
- case R.id.menu_refresh:
- refresh();
- return true;
- }
-
- return false;
- }
-
- public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo, Object selected) {
- MenuInflater inflater = context.getMenuInflater();
-
- if(selected instanceof Entry) {
- Entry entry = (Entry) selected;
- if(entry instanceof PodcastEpisode && !entry.isVideo()) {
- if(Util.isOffline(context)) {
- inflater.inflate(R.menu.select_podcast_episode_context_offline, menu);
- }
- else {
- inflater.inflate(R.menu.select_podcast_episode_context, menu);
-
- if(entry.getBookmark() == null) {
- menu.removeItem(R.id.bookmark_menu_delete);
- }
- }
- }
- else if (entry.isDirectory()) {
- if(Util.isOffline(context)) {
- inflater.inflate(R.menu.select_album_context_offline, menu);
- }
- else {
- inflater.inflate(R.menu.select_album_context, menu);
-
- if(Util.isTagBrowsing(context)) {
- menu.removeItem(R.id.menu_rate);
- }
- }
- menu.findItem(entry.isDirectory() ? R.id.album_menu_star : R.id.song_menu_star).setTitle(entry.isStarred() ? R.string.common_unstar : R.string.common_star);
- } else if(!entry.isVideo()) {
- if(Util.isOffline(context)) {
- inflater.inflate(R.menu.select_song_context_offline, menu);
- }
- else {
- inflater.inflate(R.menu.select_song_context, menu);
-
- if(entry.getBookmark() == null) {
- menu.removeItem(R.id.bookmark_menu_delete);
- }
- }
- menu.findItem(entry.isDirectory() ? R.id.album_menu_star : R.id.song_menu_star).setTitle(entry.isStarred() ? R.string.common_unstar : R.string.common_star);
- } else {
- if(Util.isOffline(context)) {
- inflater.inflate(R.menu.select_video_context_offline, menu);
- }
- else {
- inflater.inflate(R.menu.select_video_context, menu);
- }
- }
- } else if(selected instanceof Artist) {
- Artist artist = (Artist) selected;
- if(Util.isOffline(context)) {
- inflater.inflate(R.menu.select_artist_context_offline, menu);
- }
- else {
- inflater.inflate(R.menu.select_artist_context, menu);
-
- menu.findItem(R.id.artist_menu_star).setTitle(artist.isStarred() ? R.string.common_unstar : R.string.common_star);
- }
- }
-
- hideMenuItems(menu, (AdapterView.AdapterContextMenuInfo) menuInfo);
- }
-
- protected void hideMenuItems(ContextMenu menu, AdapterView.AdapterContextMenuInfo info) {
- if(!ServerInfo.checkServerVersion(context, "1.8")) {
- menu.setGroupVisible(R.id.server_1_8, false);
- menu.setGroupVisible(R.id.hide_star, false);
- }
- if(!ServerInfo.checkServerVersion(context, "1.9")) {
- menu.setGroupVisible(R.id.server_1_9, false);
- }
- if(!ServerInfo.checkServerVersion(context, "1.10.1")) {
- menu.setGroupVisible(R.id.server_1_10, false);
- }
-
- SharedPreferences prefs = Util.getPreferences(context);
- if(!prefs.getBoolean(Constants.PREFERENCES_KEY_MENU_PLAY_NEXT, true)) {
- menu.setGroupVisible(R.id.hide_play_next, false);
- }
- if(!prefs.getBoolean(Constants.PREFERENCES_KEY_MENU_PLAY_LAST, true)) {
- menu.setGroupVisible(R.id.hide_play_last, false);
- }
- if(!prefs.getBoolean(Constants.PREFERENCES_KEY_MENU_STAR, true)) {
- menu.setGroupVisible(R.id.hide_star, false);
- }
- if(!prefs.getBoolean(Constants.PREFERENCES_KEY_MENU_SHARED, true) || !UserUtil.canShare()) {
- menu.setGroupVisible(R.id.hide_share, false);
- }
- if(!prefs.getBoolean(Constants.PREFERENCES_KEY_MENU_RATING, true)) {
- menu.setGroupVisible(R.id.hide_rating, false);
- }
-
- if(!Util.isOffline(context)) {
- // If we are looking at a standard song view, get downloadFile to cache what options to show
- if(info.targetView instanceof SongView) {
- SongView songView = (SongView) info.targetView;
- DownloadFile downloadFile = songView.getDownloadFile();
-
- try {
- if(downloadFile != null) {
- if(downloadFile.isWorkDone()) {
- // Remove permanent cache menu if already perma cached
- if(downloadFile.isSaved()) {
- menu.removeItem(R.id.song_menu_pin);
- }
-
- // Remove cache option no matter what if already downloaded
- menu.removeItem(R.id.song_menu_download);
- } else {
- // Remove delete option if nothing to delete
- menu.removeItem(R.id.song_menu_delete);
- }
- }
- } catch(Exception e) {
- Log.w(TAG, "Failed to lookup downloadFile info", e);
- }
- }
- // Apply similar logic to album views
- else if(info.targetView instanceof AlbumCell || info.targetView instanceof AlbumView
- || info.targetView instanceof ArtistView || info.targetView instanceof ArtistEntryView) {
- File folder = null;
- int id = 0;
- if(info.targetView instanceof AlbumCell) {
- folder = ((AlbumCell) info.targetView).getFile();
- id = R.id.album_menu_delete;
- } else if(info.targetView instanceof AlbumView) {
- folder = ((AlbumView) info.targetView).getFile();
- id = R.id.album_menu_delete;
- } else if(info.targetView instanceof ArtistView) {
- folder = ((ArtistView) info.targetView).getFile();
- id = R.id.artist_menu_delete;
- } else if(info.targetView instanceof ArtistEntryView) {
- folder = ((ArtistEntryView) info.targetView).getFile();
- id = R.id.artist_menu_delete;
- }
-
- try {
- if(folder != null && !folder.exists()) {
- menu.removeItem(id);
- }
- } catch(Exception e) {
- Log.w(TAG, "Failed to lookup album directory info", e);
- }
- }
- }
- }
-
- protected void recreateContextMenu(ContextMenu menu) {
- List<MenuItem> menuItems = new ArrayList<MenuItem>();
- for(int i = 0; i < menu.size(); i++) {
- MenuItem item = menu.getItem(i);
- if(item.isVisible()) {
- menuItems.add(item);
- }
- }
- menu.clear();
- for(int i = 0; i < menuItems.size(); i++) {
- MenuItem item = menuItems.get(i);
- menu.add(tag, item.getItemId(), Menu.NONE, item.getTitle());
- }
- }
-
- public boolean onContextItemSelected(MenuItem menuItem, Object selectedItem) {
- Artist artist = selectedItem instanceof Artist ? (Artist) selectedItem : null;
- Entry entry = selectedItem instanceof Entry ? (Entry) selectedItem : null;
- List<Entry> songs = new ArrayList<Entry>(1);
- songs.add(entry);
-
- switch (menuItem.getItemId()) {
- case R.id.artist_menu_play_now:
- downloadRecursively(artist.getId(), false, false, true, false, false);
- break;
- case R.id.artist_menu_play_shuffled:
- downloadRecursively(artist.getId(), false, false, true, true, false);
- break;
- case R.id.artist_menu_play_next:
- downloadRecursively(artist.getId(), false, true, false, false, false, true);
- break;
- case R.id.artist_menu_play_last:
- downloadRecursively(artist.getId(), false, true, false, false, false);
- break;
- case R.id.artist_menu_download:
- downloadRecursively(artist.getId(), false, true, false, false, true);
- break;
- case R.id.artist_menu_pin:
- downloadRecursively(artist.getId(), true, true, false, false, true);
- break;
- case R.id.artist_menu_delete:
- deleteRecursively(artist);
- break;
- case R.id.artist_menu_star:
- toggleStarred(artist);
- break;
- case R.id.album_menu_play_now:
- artistOverride = true;
- downloadRecursively(entry.getId(), false, false, true, false, false);
- break;
- case R.id.album_menu_play_shuffled:
- artistOverride = true;
- downloadRecursively(entry.getId(), false, false, true, true, false);
- break;
- case R.id.album_menu_play_next:
- artistOverride = true;
- downloadRecursively(entry.getId(), false, true, false, false, false, true);
- break;
- case R.id.album_menu_play_last:
- artistOverride = true;
- downloadRecursively(entry.getId(), false, true, false, false, false);
- break;
- case R.id.album_menu_download:
- artistOverride = true;
- downloadRecursively(entry.getId(), false, true, false, false, true);
- break;
- case R.id.album_menu_pin:
- artistOverride = true;
- downloadRecursively(entry.getId(), true, true, false, false, true);
- break;
- case R.id.album_menu_star:
- toggleStarred(entry);
- break;
- case R.id.album_menu_delete:
- deleteRecursively(entry);
- break;
- case R.id.album_menu_info:
- displaySongInfo(entry);
- break;
- case R.id.album_menu_show_artist:
- showAlbumArtist((Entry) selectedItem);
- break;
- case R.id.album_menu_share:
- createShare(songs);
- break;
- case R.id.song_menu_play_now:
- playNow(songs);
- break;
- case R.id.song_menu_play_next:
- getDownloadService().download(songs, false, false, true, false);
- break;
- case R.id.song_menu_play_last:
- getDownloadService().download(songs, false, false, false, false);
- break;
- case R.id.song_menu_download:
- getDownloadService().downloadBackground(songs, false);
- break;
- case R.id.song_menu_pin:
- getDownloadService().downloadBackground(songs, true);
- break;
- case R.id.song_menu_delete:
- getDownloadService().delete(songs);
- break;
- case R.id.song_menu_add_playlist:
- addToPlaylist(songs);
- break;
- case R.id.song_menu_star:
- toggleStarred(entry);
- break;
- case R.id.song_menu_play_external:
- playExternalPlayer(entry);
- break;
- case R.id.song_menu_info:
- displaySongInfo(entry);
- break;
- case R.id.song_menu_stream_external:
- streamExternalPlayer(entry);
- break;
- case R.id.song_menu_share:
- createShare(songs);
- break;
- case R.id.song_menu_show_album:
- showAlbum((Entry) selectedItem);
- break;
- case R.id.song_menu_show_artist:
- showArtist((Entry) selectedItem);
- break;
- case R.id.bookmark_menu_delete:
- deleteBookmark(entry, null);
- break;
- case R.id.menu_rate:
- setRating(entry);
- break;
- default:
- return false;
- }
-
- return true;
- }
-
- public void replaceFragment(SubsonicFragment fragment) {
- replaceFragment(fragment, true);
- }
- public void replaceFragment(SubsonicFragment fragment, boolean replaceCurrent) {
- context.replaceFragment(fragment, fragment.getSupportTag(), secondaryFragment && replaceCurrent);
- }
-
- public int getRootId() {
- return rootView.getId();
- }
-
- public void setSupportTag(int tag) { this.tag = tag; }
- public void setSupportTag(String tag) { this.tag = Integer.parseInt(tag); }
- public int getSupportTag() {
- return tag;
- }
-
- public void setPrimaryFragment(boolean primary) {
- primaryFragment = primary;
- if(primary) {
- if(context != null && title != null) {
- context.setTitle(title);
- context.setSubtitle(subtitle);
- }
- if(invalidated) {
- invalidated = false;
- refresh(false);
- }
- }
- }
- public void setPrimaryFragment(boolean primary, boolean secondary) {
- setPrimaryFragment(primary);
- secondaryFragment = secondary;
- }
- public void setSecondaryFragment(boolean secondary) {
- secondaryFragment = secondary;
- }
-
- public void invalidate() {
- if(primaryFragment) {
- refresh(true);
- } else {
- invalidated = true;
- }
- }
-
- public DownloadService getDownloadService() {
- return context != null ? context.getDownloadService() : null;
- }
-
- protected void refresh() {
- refresh(true);
- }
- protected void refresh(boolean refresh) {
-
- }
-
- @Override
- public void onRefresh() {
- refreshLayout.setRefreshing(false);
- refresh();
- }
-
- protected void exit() {
- if(((Object) context).getClass() != SubsonicFragmentActivity.class) {
- Intent intent = new Intent(context, SubsonicFragmentActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- intent.putExtra(Constants.INTENT_EXTRA_NAME_EXIT, true);
- Util.startActivityWithoutTransition(context, intent);
- } else {
- context.stopService(new Intent(context, DownloadService.class));
- context.finish();
- }
- }
-
- public void setProgressVisible(boolean visible) {
- View view = rootView.findViewById(R.id.tab_progress);
- if (view != null) {
- view.setVisibility(visible ? View.VISIBLE : View.GONE);
-
- if(visible) {
- View progress = rootView.findViewById(R.id.tab_progress_spinner);
- progress.setVisibility(View.VISIBLE);
- }
- }
- }
-
- public void updateProgress(String message) {
- TextView view = (TextView) rootView.findViewById(R.id.tab_progress_message);
- if (view != null) {
- view.setText(message);
- }
- }
-
- public void setEmpty(boolean empty) {
- View view = rootView.findViewById(R.id.tab_progress);
- if(empty) {
- view.setVisibility(View.VISIBLE);
-
- View progress = view.findViewById(R.id.tab_progress_spinner);
- progress.setVisibility(View.GONE);
-
- TextView text = (TextView) view.findViewById(R.id.tab_progress_message);
- text.setText(R.string.common_empty);
- } else {
- view.setVisibility(View.GONE);
- }
- }
-
- protected synchronized ImageLoader getImageLoader() {
- return context.getImageLoader();
- }
- public synchronized static ImageLoader getStaticImageLoader(Context context) {
- return SubsonicActivity.getStaticImageLoader(context);
- }
-
- public void setTitle(CharSequence title) {
- this.title = title;
- context.setTitle(title);
- }
- public void setTitle(int title) {
- this.title = context.getResources().getString(title);
- context.setTitle(this.title);
- }
- public void setSubtitle(CharSequence title) {
- this.subtitle = title;
- context.setSubtitle(title);
- }
- public CharSequence getTitle() {
- return this.title;
- }
-
- protected void setupScrollList(final AbsListView listView) {
- if(!context.isTouchscreen()) {
- refreshLayout.setEnabled(false);
- } else {
- listView.setOnScrollListener(new AbsListView.OnScrollListener() {
- @Override
- public void onScrollStateChanged(AbsListView view, int scrollState) {
- }
-
- @Override
- public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
- int topRowVerticalPosition = (listView.getChildCount() == 0) ? 0 : listView.getChildAt(0).getTop();
- refreshLayout.setEnabled(topRowVerticalPosition >= 0 && listView.getFirstVisiblePosition() == 0);
- }
- });
-
- refreshLayout.setColorScheme(
- R.color.holo_blue_light,
- R.color.holo_orange_light,
- R.color.holo_green_light,
- R.color.holo_red_light);
- }
- }
-
- protected void warnIfStorageUnavailable() {
- if (!Util.isExternalStoragePresent()) {
- Util.toast(context, R.string.select_album_no_sdcard);
- }
-
- try {
- StatFs stat = new StatFs(FileUtil.getMusicDirectory(context).getPath());
- long bytesAvailableFs = (long) stat.getAvailableBlocks() * (long) stat.getBlockSize();
- if (bytesAvailableFs < 50000000L) {
- Util.toast(context, context.getResources().getString(R.string.select_album_no_room, Util.formatBytes(bytesAvailableFs)));
- }
- } catch(Exception e) {
- Log.w(TAG, "Error while checking storage space for music directory", e);
- }
- }
-
- protected void onShuffleRequested() {
- if(Util.isOffline(context)) {
- Intent intent = new Intent(context, DownloadActivity.class);
- intent.putExtra(Constants.INTENT_EXTRA_NAME_SHUFFLE, true);
- Util.startActivityWithoutTransition(context, intent);
- return;
- }
-
- View dialogView = context.getLayoutInflater().inflate(R.layout.shuffle_dialog, null);
- final EditText startYearBox = (EditText)dialogView.findViewById(R.id.start_year);
- final EditText endYearBox = (EditText)dialogView.findViewById(R.id.end_year);
- final EditText genreBox = (EditText)dialogView.findViewById(R.id.genre);
- final Button genreCombo = (Button)dialogView.findViewById(R.id.genre_combo);
-
- final SharedPreferences prefs = Util.getPreferences(context);
- final String oldStartYear = prefs.getString(Constants.PREFERENCES_KEY_SHUFFLE_START_YEAR, "");
- final String oldEndYear = prefs.getString(Constants.PREFERENCES_KEY_SHUFFLE_END_YEAR, "");
- final String oldGenre = prefs.getString(Constants.PREFERENCES_KEY_SHUFFLE_GENRE, "");
-
- boolean _useCombo = false;
- if(ServerInfo.checkServerVersion(context, "1.9.0")) {
- genreBox.setVisibility(View.GONE);
- genreCombo.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- new LoadingTask<List<Genre>>(context, true) {
- @Override
- protected List<Genre> doInBackground() throws Throwable {
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- return musicService.getGenres(false, context, this);
- }
-
- @Override
- protected void done(final List<Genre> genres) {
- List<String> names = new ArrayList<String>();
- String blank = context.getResources().getString(R.string.select_genre_blank);
- names.add(blank);
- for(Genre genre: genres) {
- names.add(genre.getName());
- }
- final List<String> finalNames = names;
-
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
- builder.setTitle(R.string.shuffle_pick_genre)
- .setItems(names.toArray(new CharSequence[names.size()]), new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- if(which == 0) {
- genreCombo.setText("");
- } else {
- genreCombo.setText(finalNames.get(which));
- }
- }
- });
- AlertDialog dialog = builder.create();
- dialog.show();
- }
-
- @Override
- protected void error(Throwable error) {
- String msg;
- if (error instanceof OfflineException || error instanceof ServerTooOldException) {
- msg = getErrorMessage(error);
- } else {
- msg = context.getResources().getString(R.string.playlist_error) + " " + getErrorMessage(error);
- }
-
- Util.toast(context, msg, false);
- }
- }.execute();
- }
- });
- _useCombo = true;
- } else {
- genreCombo.setVisibility(View.GONE);
- }
- final boolean useCombo = _useCombo;
-
- startYearBox.setText(oldStartYear);
- endYearBox.setText(oldEndYear);
- genreBox.setText(oldGenre);
- genreCombo.setText(oldGenre);
-
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
- builder.setTitle(R.string.shuffle_title)
- .setView(dialogView)
- .setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int id) {
- Intent intent = new Intent(context, DownloadActivity.class);
- intent.putExtra(Constants.INTENT_EXTRA_NAME_SHUFFLE, true);
- String genre;
- if(useCombo) {
- genre = genreCombo.getText().toString();
- } else {
- genre = genreBox.getText().toString();
- }
- String startYear = startYearBox.getText().toString();
- String endYear = endYearBox.getText().toString();
-
- SharedPreferences.Editor editor = prefs.edit();
- editor.putString(Constants.PREFERENCES_KEY_SHUFFLE_START_YEAR, startYear);
- editor.putString(Constants.PREFERENCES_KEY_SHUFFLE_END_YEAR, endYear);
- editor.putString(Constants.PREFERENCES_KEY_SHUFFLE_GENRE, genre);
- editor.commit();
-
- Util.startActivityWithoutTransition(context, intent);
- }
- })
- .setNegativeButton(R.string.common_cancel, null);
- AlertDialog dialog = builder.create();
- dialog.show();
- }
-
- public void toggleStarred(Entry entry) {
- toggleStarred(entry, null);
- }
- public void toggleStarred(final Entry entry, final OnStarChange onStarChange) {
- final boolean starred = !entry.isStarred();
- entry.setStarred(starred);
- if(onStarChange != null) {
- onStarChange.starChange(starred);
- }
-
- new SilentBackgroundTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- if(entry.isDirectory() && Util.isTagBrowsing(context) && !Util.isOffline(context)) {
- if(entry.isAlbum()) {
- musicService.setStarred(null, null, Arrays.asList(entry), starred, null, context);
- } else {
- musicService.setStarred(null, Arrays.asList(entry), null, starred, null, context);
- }
- } else {
- musicService.setStarred(Arrays.asList(entry), null, null, starred, null, context);
- }
-
- new EntryInstanceUpdater(entry) {
- @Override
- public void update(Entry found) {
- found.setStarred(starred);
- }
- }.execute();
-
- return null;
- }
-
- @Override
- protected void done(Void result) {
- // UpdateView
- Util.toast(context, context.getResources().getString(starred ? R.string.starring_content_starred : R.string.starring_content_unstarred, entry.getTitle()));
- }
-
- @Override
- protected void error(Throwable error) {
- Log.w(TAG, "Failed to star", error);
- entry.setStarred(!starred);
- if(onStarChange != null) {
- onStarChange.starChange(!starred);
- }
-
- String msg;
- if (error instanceof OfflineException || error instanceof ServerTooOldException) {
- msg = getErrorMessage(error);
- } else {
- msg = context.getResources().getString(R.string.starring_content_error, entry.getTitle()) + " " + getErrorMessage(error);
- }
-
- Util.toast(context, msg, false);
- }
- }.execute();
- }
-
- public void toggleStarred(final Artist entry) {
- final boolean starred = !entry.isStarred();
- entry.setStarred(starred);
-
- new SilentBackgroundTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- if(Util.isTagBrowsing(context) && !Util.isOffline(context)) {
- musicService.setStarred(null, Arrays.asList(new Entry(entry)), null, starred, null, context);
- } else {
- musicService.setStarred(Arrays.asList(new Entry(entry)), null, null, starred, null, context);
- }
- return null;
- }
-
- @Override
- protected void done(Void result) {
- // UpdateView
- Util.toast(context, context.getResources().getString(starred ? R.string.starring_content_starred : R.string.starring_content_unstarred, entry.getName()));
- }
-
- @Override
- protected void error(Throwable error) {
- Log.w(TAG, "Failed to star", error);
- entry.setStarred(!starred);
-
- String msg;
- if (error instanceof OfflineException || error instanceof ServerTooOldException) {
- msg = getErrorMessage(error);
- } else {
- msg = context.getResources().getString(R.string.starring_content_error, entry.getName()) + " " + getErrorMessage(error);
- }
-
- Util.toast(context, msg, false);
- }
- }.execute();
- }
-
- protected void downloadRecursively(final String id, final boolean save, final boolean append, final boolean autoplay, final boolean shuffle, final boolean background) {
- downloadRecursively(id, "", true, save, append, autoplay, shuffle, background);
- }
- protected void downloadRecursively(final String id, final boolean save, final boolean append, final boolean autoplay, final boolean shuffle, final boolean background, final boolean playNext) {
- downloadRecursively(id, "", true, save, append, autoplay, shuffle, background, playNext);
- }
- protected void downloadPlaylist(final String id, final String name, final boolean save, final boolean append, final boolean autoplay, final boolean shuffle, final boolean background) {
- downloadRecursively(id, name, false, save, append, autoplay, shuffle, background);
- }
- protected void downloadRecursively(final String id, final String name, final boolean isDirectory, final boolean save, final boolean append, final boolean autoplay, final boolean shuffle, final boolean background) {
- downloadRecursively(id, name, isDirectory, save, append, autoplay, shuffle, background, false);
- }
- protected void downloadRecursively(final String id, final String name, final boolean isDirectory, final boolean save, final boolean append, final boolean autoplay, final boolean shuffle, final boolean background, final boolean playNext) {
- new RecursiveLoader(context) {
- @Override
- protected Boolean doInBackground() throws Throwable {
- musicService = MusicServiceFactory.getMusicService(context);
- MusicDirectory root;
- if(share != null) {
- root = share.getMusicDirectory();
- }
- else if(isDirectory) {
- if(id != null) {
- root = getMusicDirectory(id, name, false, musicService, this);
- } else {
- root = musicService.getStarredList(context, this);
- }
- }
- else {
- root = musicService.getPlaylist(true, id, name, context, this);
- }
-
- if(shuffle) {
- Collections.shuffle(root.getChildren());
- }
-
- songs = new LinkedList<Entry>();
- getSongsRecursively(root, songs);
-
- DownloadService downloadService = getDownloadService();
- boolean transition = false;
- if (!songs.isEmpty() && downloadService != null) {
- // Conditions for a standard play now operation
- if(!append && !save && autoplay && !playNext && !shuffle && !background) {
- playNowOverride = true;
- return false;
- }
-
- if (!append && !background) {
- downloadService.clear();
- }
- if(!background) {
- downloadService.download(songs, save, autoplay, playNext, false);
- if(!append) {
- transition = true;
- }
- }
- else {
- downloadService.downloadBackground(songs, save);
- }
- }
- artistOverride = false;
-
- return transition;
- }
- }.execute();
- }
-
- protected void downloadRecursively(final List<Entry> albums, final boolean shuffle, final boolean append) {
- new RecursiveLoader(context) {
- @Override
- protected Boolean doInBackground() throws Throwable {
- musicService = MusicServiceFactory.getMusicService(context);
-
- if(shuffle) {
- Collections.shuffle(albums);
- }
-
- songs = new LinkedList<Entry>();
- MusicDirectory root = new MusicDirectory();
- root.addChildren(albums);
- getSongsRecursively(root, songs);
-
- DownloadService downloadService = getDownloadService();
- boolean transition = false;
- if (!songs.isEmpty() && downloadService != null) {
- // Conditions for a standard play now operation
- if(!append && !shuffle) {
- playNowOverride = true;
- return false;
- }
-
- if (!append) {
- downloadService.clear();
- }
-
- downloadService.download(songs, false, true, false, false);
- if(!append) {
- transition = true;
- }
- }
- artistOverride = false;
-
- return transition;
- }
- }.execute();
- }
-
- protected MusicDirectory getMusicDirectory(String id, String name, boolean refresh, MusicService service, ProgressListener listener) throws Exception {
- return getMusicDirectory(id, name, refresh, false, service, listener);
- }
- protected MusicDirectory getMusicDirectory(String id, String name, boolean refresh, boolean forceArtist, MusicService service, ProgressListener listener) throws Exception {
- if(Util.isTagBrowsing(context) && !Util.isOffline(context)) {
- if(artist && !artistOverride || forceArtist) {
- return service.getArtist(id, name, refresh, context, listener);
- } else {
- return service.getAlbum(id, name, refresh, context, listener);
- }
- } else {
- return service.getMusicDirectory(id, name, refresh, context, listener);
- }
- }
-
- protected void addToPlaylist(final List<Entry> songs) {
- if(songs.isEmpty()) {
- Util.toast(context, "No songs selected");
- return;
- }
-
- new LoadingTask<List<Playlist>>(context, true) {
- @Override
- protected List<Playlist> doInBackground() throws Throwable {
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- List<Playlist> playlists = new ArrayList<Playlist>();
- playlists.addAll(musicService.getPlaylists(false, context, this));
-
- // Iterate through and remove all non owned public playlists
- Iterator<Playlist> it = playlists.iterator();
- while(it.hasNext()) {
- Playlist playlist = it.next();
- if(playlist.getPublic() == true && playlist.getId().indexOf(".m3u") == -1 && !UserUtil.getCurrentUsername(context).equals(playlist.getOwner())) {
- it.remove();
- }
- }
-
- return playlists;
- }
-
- @Override
- protected void done(final List<Playlist> playlists) {
- // Create adapter to show playlists
- Playlist createNew = new Playlist("-1", context.getResources().getString(R.string.playlist_create_new));
- playlists.add(0, createNew);
- ArrayAdapter playlistAdapter = new ArrayAdapter<Playlist>(context, R.layout.basic_count_item, playlists) {
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- Playlist playlist = getItem(position);
-
- // Create new if not getting a convert view to use
- PlaylistSongView view;
- if(convertView instanceof PlaylistSongView) {
- view = (PlaylistSongView) convertView;
- } else {
- view = new PlaylistSongView(context);
- }
-
- view.setObject(playlist, songs);
-
- return view;
- }
- };
-
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
- builder.setTitle(R.string.playlist_add_to)
- .setAdapter(playlistAdapter, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- if (which > 0) {
- addToPlaylist(playlists.get(which), songs);
- } else {
- createNewPlaylist(songs, false);
- }
- }
- });
- AlertDialog dialog = builder.create();
- dialog.show();
- }
-
- @Override
- protected void error(Throwable error) {
- String msg;
- if (error instanceof OfflineException || error instanceof ServerTooOldException) {
- msg = getErrorMessage(error);
- } else {
- msg = context.getResources().getString(R.string.playlist_error) + " " + getErrorMessage(error);
- }
-
- Util.toast(context, msg, false);
- }
- }.execute();
- }
-
- private void addToPlaylist(final Playlist playlist, final List<Entry> songs) {
- new SilentBackgroundTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- musicService.addToPlaylist(playlist.getId(), songs, context, null);
- return null;
- }
-
- @Override
- protected void done(Void result) {
- Util.toast(context, context.getResources().getString(R.string.updated_playlist, songs.size(), playlist.getName()));
- }
-
- @Override
- protected void error(Throwable error) {
- String msg;
- if (error instanceof OfflineException || error instanceof ServerTooOldException) {
- msg = getErrorMessage(error);
- } else {
- msg = context.getResources().getString(R.string.updated_playlist_error, playlist.getName()) + " " + getErrorMessage(error);
- }
-
- Util.toast(context, msg, false);
- }
- }.execute();
- }
-
- protected void createNewPlaylist(final List<Entry> songs, final boolean getSuggestion) {
- View layout = context.getLayoutInflater().inflate(R.layout.save_playlist, null);
- final EditText playlistNameView = (EditText) layout.findViewById(R.id.save_playlist_name);
- final CheckBox overwriteCheckBox = (CheckBox) layout.findViewById(R.id.save_playlist_overwrite);
- if(getSuggestion) {
- String playlistName = (getDownloadService() != null) ? getDownloadService().getSuggestedPlaylistName() : null;
- if (playlistName != null) {
- playlistNameView.setText(playlistName);
- try {
- if(ServerInfo.checkServerVersion(context, "1.8.0") && Integer.parseInt(getDownloadService().getSuggestedPlaylistId()) != -1) {
- overwriteCheckBox.setChecked(true);
- overwriteCheckBox.setVisibility(View.VISIBLE);
- }
- } catch(Exception e) {
- Log.d(TAG, "Playlist id isn't a integer, probably MusicCabinet");
- }
- } else {
- DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
- playlistNameView.setText(dateFormat.format(new Date()));
- }
- } else {
- DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
- playlistNameView.setText(dateFormat.format(new Date()));
- }
-
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
- builder.setTitle(R.string.download_playlist_title)
- .setMessage(R.string.download_playlist_name)
- .setView(layout)
- .setPositiveButton(R.string.common_save, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int id) {
- String playlistName = String.valueOf(playlistNameView.getText());
- if(overwriteCheckBox.isChecked()) {
- overwritePlaylist(songs, playlistName, getDownloadService().getSuggestedPlaylistId());
- } else {
- createNewPlaylist(songs, playlistName);
-
- if(getSuggestion) {
- DownloadService downloadService = getDownloadService();
- if(downloadService != null) {
- downloadService.setSuggestedPlaylistName(playlistName, null);
- }
- }
- }
- }
- })
- .setNegativeButton(R.string.common_cancel, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int id) {
- dialog.cancel();
- }
- })
- .setCancelable(true);
-
- AlertDialog dialog = builder.create();
- dialog.show();
- }
- private void createNewPlaylist(final List<Entry> songs, final String name) {
- new SilentBackgroundTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- musicService.createPlaylist(null, name, songs, context, null);
- return null;
- }
-
- @Override
- protected void done(Void result) {
- Util.toast(context, R.string.download_playlist_done);
- }
-
- @Override
- protected void error(Throwable error) {
- String msg = context.getResources().getString(R.string.download_playlist_error) + " " + getErrorMessage(error);
- Util.toast(context, msg);
- }
- }.execute();
- }
- private void overwritePlaylist(final List<Entry> songs, final String name, final String id) {
- new SilentBackgroundTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- MusicDirectory playlist = musicService.getPlaylist(true, id, name, context, null);
- List<Entry> toDelete = playlist.getChildren();
- musicService.overwritePlaylist(id, name, toDelete.size(), songs, context, null);
- return null;
- }
-
- @Override
- protected void done(Void result) {
- Util.toast(context, R.string.download_playlist_done);
- }
-
- @Override
- protected void error(Throwable error) {
- String msg;
- if (error instanceof OfflineException || error instanceof ServerTooOldException) {
- msg = getErrorMessage(error);
- } else {
- msg = context.getResources().getString(R.string.download_playlist_error) + " " + getErrorMessage(error);
- }
-
- Util.toast(context, msg, false);
- }
- }.execute();
- }
-
- public void displaySongInfo(final Entry song) {
- Integer duration = null;
- Integer bitrate = null;
- String format = null;
- long size = 0;
- if(!song.isDirectory()) {
- try {
- DownloadFile downloadFile = new DownloadFile(context, song, false);
- File file = downloadFile.getCompleteFile();
- if(file.exists()) {
- MediaMetadataRetriever metadata = new MediaMetadataRetriever();
- metadata.setDataSource(file.getAbsolutePath());
-
- String tmp = metadata.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
- duration = Integer.parseInt((tmp != null) ? tmp : "0") / 1000;
- format = FileUtil.getExtension(file.getName());
- size = file.length();
-
- // If no duration try to read bitrate tag
- if(duration == null) {
- tmp = metadata.extractMetadata(MediaMetadataRetriever.METADATA_KEY_BITRATE);
- bitrate = Integer.parseInt((tmp != null) ? tmp : "0") / 1000;
- } else {
- // Otherwise do a calculation for it
- // Divide by 1000 so in kbps
- bitrate = (int) (size / duration) / 1000 * 8;
- }
-
- if(Util.isOffline(context)) {
- song.setGenre(metadata.extractMetadata(MediaMetadataRetriever.METADATA_KEY_GENRE));
- String year = metadata.extractMetadata(MediaMetadataRetriever.METADATA_KEY_YEAR);
- song.setYear(Integer.parseInt((year != null) ? year : "0"));
- }
- }
- } catch(Exception e) {
- Log.i(TAG, "Device doesn't properly support MediaMetadataRetreiver");
- }
- }
-
- String msg = "";
- if(song instanceof PodcastEpisode) {
- msg += "Podcast: " + song.getArtist() + "\nStatus: " + ((PodcastEpisode)song).getStatus();
- } else if(!song.isVideo()) {
- if(song.getArtist() != null && !"".equals(song.getArtist())) {
- msg += "Artist: " + song.getArtist();
- }
- if(song.getAlbum() != null && !"".equals(song.getAlbum())) {
- msg += "\nAlbum: " + song.getAlbum();
- }
- }
- if(song.getTrack() != null && song.getTrack() != 0) {
- msg += "\nTrack: " + song.getTrack();
- }
- if(song.getGenre() != null && !"".equals(song.getGenre())) {
- msg += "\nGenre: " + song.getGenre();
- }
- if(song.getYear() != null && song.getYear() != 0) {
- msg += "\nYear: " + song.getYear();
- }
- if(!Util.isOffline(context) && song.getSuffix() != null) {
- msg += "\nServer Format: " + song.getSuffix();
- if(song.getBitRate() != null && song.getBitRate() != 0) {
- msg += "\nServer Bitrate: " + song.getBitRate() + " kbps";
- }
- }
- if(format != null && !"".equals(format)) {
- msg += "\nCached Format: " + format;
- }
- if(bitrate != null && bitrate != 0) {
- msg += "\nCached Bitrate: " + bitrate + " kbps";
- }
- if(size != 0) {
- msg += "\nSize: " + Util.formatLocalizedBytes(size, context);
- }
- if(song.getDuration() != null && song.getDuration() != 0) {
- msg += "\nLength: " + Util.formatDuration(song.getDuration());
- }
- if(song.getBookmark() != null) {
- msg += "\nBookmark Position: " + Util.formatDuration(song.getBookmark().getPosition() / 1000);
- }
- if(song.getRating() != 0) {
- msg += "\nRating: " + song.getRating() + " stars";
- }
- if(song instanceof PodcastEpisode) {
- msg += "\n\nDescription: " + song.getAlbum();
- }
-
- Util.info(context, song.getTitle(), msg);
- }
-
- protected void playVideo(Entry entry) {
- if(entryExists(entry)) {
- playExternalPlayer(entry);
- } else {
- streamExternalPlayer(entry);
- }
- }
-
- protected void playWebView(Entry entry) {
- int maxBitrate = Util.getMaxVideoBitrate(context);
-
- Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.setData(Uri.parse(MusicServiceFactory.getMusicService(context).getVideoUrl(maxBitrate, context, entry.getId())));
-
- startActivity(intent);
- }
- protected void playExternalPlayer(Entry entry) {
- if(!entryExists(entry)) {
- Util.toast(context, R.string.download_need_download);
- } else {
- DownloadFile check = new DownloadFile(context, entry, false);
- File file = check.getCompleteFile();
-
- Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.setDataAndType(Uri.fromFile(file), "video/*");
- intent.putExtra(Intent.EXTRA_TITLE, entry.getTitle());
-
- List<ResolveInfo> intents = context.getPackageManager()
- .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
- if(intents != null && intents.size() > 0) {
- startActivity(intent);
- }else {
- Util.toast(context, R.string.download_no_streaming_player);
- }
- }
- }
- protected void streamExternalPlayer(Entry entry) {
- String videoPlayerType = Util.getVideoPlayerType(context);
- if("flash".equals(videoPlayerType)) {
- playWebView(entry);
- } else if("hls".equals(videoPlayerType)) {
- streamExternalPlayer(entry, "hls");
- } else if("raw".equals(videoPlayerType)) {
- streamExternalPlayer(entry, "raw");
- } else {
- streamExternalPlayer(entry, entry.getTranscodedSuffix());
- }
- }
- protected void streamExternalPlayer(Entry entry, String format) {
- try {
- int maxBitrate = Util.getMaxVideoBitrate(context);
-
- Intent intent = new Intent(Intent.ACTION_VIEW);
- if("hls".equals(format)) {
- intent.setDataAndType(Uri.parse(MusicServiceFactory.getMusicService(context).getHlsUrl(entry.getId(), maxBitrate, context)), "application/x-mpegURL");
- } else {
- intent.setDataAndType(Uri.parse(MusicServiceFactory.getMusicService(context).getVideoStreamUrl(format, maxBitrate, context, entry.getId())), "video/*");
- }
- intent.putExtra("title", entry.getTitle());
-
- List<ResolveInfo> intents = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
- if(intents != null && intents.size() > 0) {
- startActivity(intent);
- } else {
- Util.toast(context, R.string.download_no_streaming_player);
- }
- } catch(Exception error) {
- String msg;
- if (error instanceof OfflineException || error instanceof ServerTooOldException) {
- msg = error.getMessage();
- } else {
- msg = context.getResources().getString(R.string.download_no_streaming_player) + " " + error.getMessage();
- }
-
- Util.toast(context, msg, false);
- }
- }
-
- protected boolean entryExists(Entry entry) {
- DownloadFile check = new DownloadFile(context, entry, false);
- return check.isCompleteFileAvailable();
- }
-
- public void deleteRecursively(Artist artist) {
- deleteRecursively(FileUtil.getArtistDirectory(context, artist));
- }
-
- public void deleteRecursively(Entry album) {
- deleteRecursively(FileUtil.getAlbumDirectory(context, album));
-
- }
- public void deleteRecursively(final File dir) {
- if(dir == null) {
- return;
- }
-
- new LoadingTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- MediaStoreService mediaStore = new MediaStoreService(context);
- FileUtil.recursiveDelete(dir, mediaStore);
- return null;
- }
-
- @Override
- protected void done(Void result) {
- if(Util.isOffline(context)) {
- refresh();
- } else {
- UpdateView.triggerUpdate();
- }
- }
- }.execute();
- }
-
- public void showAlbumArtist(Entry entry) {
- SubsonicFragment fragment = new SelectDirectoryFragment();
- Bundle args = new Bundle();
- if(Util.isTagBrowsing(context)) {
- args.putString(Constants.INTENT_EXTRA_NAME_ID, entry.getArtistId());
- } else {
- args.putString(Constants.INTENT_EXTRA_NAME_ID, entry.getParent());
- }
- args.putString(Constants.INTENT_EXTRA_NAME_NAME, entry.getArtist());
- args.putBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, true);
- fragment.setArguments(args);
-
- replaceFragment(fragment, true);
- }
- public void showArtist(Entry entry) {
- SubsonicFragment fragment = new SelectDirectoryFragment();
- Bundle args = new Bundle();
- if(Util.isTagBrowsing(context)) {
- args.putString(Constants.INTENT_EXTRA_NAME_ID, entry.getArtistId());
- } else {
- if(entry.getGrandParent() == null) {
- args.putString(Constants.INTENT_EXTRA_NAME_CHILD_ID, entry.getParent());
- } else {
- args.putString(Constants.INTENT_EXTRA_NAME_ID, entry.getGrandParent());
- }
- }
- args.putString(Constants.INTENT_EXTRA_NAME_NAME, entry.getArtist());
- args.putBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, true);
- fragment.setArguments(args);
-
- replaceFragment(fragment, true);
- }
- public void showAlbum(Entry entry) {
- SubsonicFragment fragment = new SelectDirectoryFragment();
- Bundle args = new Bundle();
- if(Util.isTagBrowsing(context)) {
- args.putString(Constants.INTENT_EXTRA_NAME_ID, entry.getAlbumId());
- } else {
- args.putString(Constants.INTENT_EXTRA_NAME_ID, entry.getParent());
- }
- args.putString(Constants.INTENT_EXTRA_NAME_NAME, entry.getAlbum());
- fragment.setArguments(args);
-
- replaceFragment(fragment, true);
- }
-
- public void createShare(final List<Entry> entries) {
- new LoadingTask<List<Share>>(context, true) {
- @Override
- protected List<Share> doInBackground() throws Throwable {
- List<String> ids = new ArrayList<String>(entries.size());
- for(Entry entry: entries) {
- ids.add(entry.getId());
- }
-
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- return musicService.createShare(ids, null, 0L, context, this);
- }
-
- @Override
- protected void done(final List<Share> shares) {
- if(shares.size() > 0) {
- Share share = shares.get(0);
- shareExternal(share);
- } else {
- Util.toast(context, context.getResources().getString(R.string.playlist_error), false);
- }
- }
-
- @Override
- protected void error(Throwable error) {
- String msg;
- if (error instanceof OfflineException || error instanceof ServerTooOldException) {
- msg = getErrorMessage(error);
- } else {
- msg = context.getResources().getString(R.string.playlist_error) + " " + getErrorMessage(error);
- }
-
- Util.toast(context, msg, false);
- }
- }.execute();
- }
- public void shareExternal(Share share) {
- Intent intent = new Intent(Intent.ACTION_SEND);
- intent.setType("text/plain");
- intent.putExtra(Intent.EXTRA_TEXT, share.getUrl());
- context.startActivity(Intent.createChooser(intent, context.getResources().getString(R.string.share_via)));
- }
-
- public GestureDetector getGestureDetector() {
- return gestureScanner;
- }
-
- protected void playBookmark(List<Entry> songs, Entry song) {
- playBookmark(songs, song, null, null);
- }
- protected void playBookmark(final List<Entry> songs, final Entry song, final String playlistName, final String playlistId) {
- final Integer position = song.getBookmark().getPosition();
-
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
- builder.setTitle(R.string.bookmark_resume_title)
- .setMessage(getResources().getString(R.string.bookmark_resume, song.getTitle(), Util.formatDuration(position / 1000)))
- .setPositiveButton(R.string.bookmark_action_resume, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int id) {
- playNow(songs, song, position);
- }
- })
- .setNegativeButton(R.string.bookmark_action_start_over, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int id) {
- final Bookmark oldBookmark = song.getBookmark();
- song.setBookmark(null);
-
- new SilentBackgroundTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- musicService.deleteBookmark(song, context, null);
-
- return null;
- }
-
- @Override
- protected void error(Throwable error) {
- song.setBookmark(oldBookmark);
-
- String msg;
- if (error instanceof OfflineException || error instanceof ServerTooOldException) {
- msg = getErrorMessage(error);
- } else {
- msg = context.getResources().getString(R.string.bookmark_deleted_error, song.getTitle()) + " " + getErrorMessage(error);
- }
-
- Util.toast(context, msg, false);
- }
- }.execute();
-
- playNow(songs, 0);
- }
- });
- AlertDialog dialog = builder.create();
- dialog.show();
- }
-
- protected void playNow(List<Entry> entries) {
- playNow(entries, null, null);
- }
- protected void playNow(List<Entry> entries, String playlistName, String playlistId) {
- Entry bookmark = null;
- for(Entry entry: entries) {
- if(entry.getBookmark() != null) {
- bookmark = entry;
- break;
- }
- }
-
- // If no bookmark found, just play from start
- if(bookmark == null) {
- playNow(entries, 0, playlistName, playlistId);
- } else {
- // If bookmark found, then give user choice to start from there or to start over
- playBookmark(entries, bookmark, playlistName, playlistId);
- }
- }
- protected void playNow(List<Entry> entries, int position) {
- playNow(entries, position, null, null);
- }
- protected void playNow(List<Entry> entries, int position, String playlistName, String playlistId) {
- Entry selected = entries.isEmpty() ? null : entries.get(0);
- playNow(entries, selected, position, playlistName, playlistId);
- }
- protected void playNow(List<Entry> entries, Entry song, int position) {
- playNow(entries, song, position, null, null);
- }
- protected void playNow(final List<Entry> entries, final Entry song, final int position, final String playlistName, final String playlistId) {
- new LoadingTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- DownloadService downloadService = getDownloadService();
- if(downloadService == null) {
- return null;
- }
-
- downloadService.clear();
- downloadService.download(entries, false, true, true, false, entries.indexOf(song), position);
- downloadService.setSuggestedPlaylistName(playlistName, playlistId);
-
- return null;
- }
-
- @Override
- protected void done(Void result) {
- Util.startActivityWithoutTransition(context, DownloadActivity.class);
- }
- }.execute();
- }
-
- protected void deleteBookmark(final MusicDirectory.Entry entry, final ArrayAdapter adapter) {
- Util.confirmDialog(context, R.string.bookmark_delete_title, entry.getTitle(), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- final Bookmark oldBookmark = entry.getBookmark();
- entry.setBookmark(null);
-
- new LoadingTask<Void>(context, false) {
- @Override
- protected Void doInBackground() throws Throwable {
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- musicService.deleteBookmark(entry, context, null);
-
- new EntryInstanceUpdater(entry) {
- @Override
- public void update(Entry found) {
- found.setBookmark(null);
- }
- }.execute();
-
- return null;
- }
-
- @Override
- protected void done(Void result) {
- if (adapter != null) {
- adapter.remove(entry);
- adapter.notifyDataSetChanged();
- }
- Util.toast(context, context.getResources().getString(R.string.bookmark_deleted, entry.getTitle()));
- }
-
- @Override
- protected void error(Throwable error) {
- entry.setBookmark(oldBookmark);
-
- String msg;
- if (error instanceof OfflineException || error instanceof ServerTooOldException) {
- msg = getErrorMessage(error);
- } else {
- msg = context.getResources().getString(R.string.bookmark_deleted_error, entry.getTitle()) + " " + getErrorMessage(error);
- }
-
- Util.toast(context, msg, false);
- }
- }.execute();
- }
- });
- }
-
- protected void setRating(Entry entry) {
- setRating(entry, null);
- }
- protected void setRating(final Entry entry, final OnRatingChange onRatingChange) {
- View layout = context.getLayoutInflater().inflate(R.layout.rating, null);
- final RatingBar ratingBar = (RatingBar) layout.findViewById(R.id.rating_bar);
- ratingBar.setRating((float) entry.getRating());
-
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
- builder.setTitle(context.getResources().getString(R.string.rating_title, entry.getTitle()))
- .setView(layout)
- .setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int id) {
- int rating = (int) ratingBar.getRating();
- setRating(entry, rating, onRatingChange);
- }
- })
- .setNegativeButton(R.string.common_cancel, null);
-
- AlertDialog dialog = builder.create();
- dialog.show();
- }
-
- protected void setRating(Entry entry, int rating) {
- setRating(entry, rating, null);
- }
- protected void setRating(final Entry entry, final int rating, final OnRatingChange onRatingChange) {
- final int oldRating = entry.getRating();
- entry.setRating(rating);
-
- if(onRatingChange != null) {
- onRatingChange.ratingChange(rating);
- }
-
- new SilentBackgroundTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- musicService.setRating(entry, rating, context, null);
-
- new EntryInstanceUpdater(entry) {
- @Override
- public void update(Entry found) {
- found.setRating(rating);
- }
- }.execute();
- return null;
- }
-
- @Override
- protected void done(Void result) {
- Util.toast(context, context.getResources().getString(rating > 0 ? R.string.rating_set_rating : R.string.rating_remove_rating, entry.getTitle()));
- }
-
- @Override
- protected void error(Throwable error) {
- entry.setRating(oldRating);
- if(onRatingChange != null) {
- onRatingChange.ratingChange(oldRating);
- }
-
- String msg;
- if (error instanceof OfflineException || error instanceof ServerTooOldException) {
- msg = getErrorMessage(error);
- } else {
- msg = context.getResources().getString(rating > 0 ? R.string.rating_set_rating_failed : R.string.rating_remove_rating_failed, entry.getTitle()) + " " + getErrorMessage(error);
- }
-
- Util.toast(context, msg, false);
- }
- }.execute();
- }
-
- protected abstract class EntryInstanceUpdater {
- private Entry entry;
-
- public EntryInstanceUpdater(Entry entry) {
- this.entry = entry;
- }
-
- public abstract void update(Entry found);
-
- public void execute() {
- DownloadService downloadService = getDownloadService();
- if(downloadService != null && !entry.isDirectory()) {
- boolean serializeChanges = false;
- List<DownloadFile> downloadFiles = downloadService.getDownloads();
- for(DownloadFile file: downloadFiles) {
- Entry check = file.getSong();
- if(entry.getId().equals(check.getId())) {
- update(entry);
- serializeChanges = true;
- }
- }
-
- if(serializeChanges) {
- downloadService.serializeQueue();
- }
- }
-
- Entry find = UpdateView.findEntry(entry);
- if(find != null) {
- update(find);
- }
- }
- }
-
- public abstract class OnRatingChange {
- abstract void ratingChange(int rating);
- }
- public abstract class OnStarChange {
- abstract void starChange(boolean starred);
- }
-
- public abstract class RecursiveLoader extends LoadingTask<Boolean> {
- protected MusicService musicService;
- protected static final int MAX_SONGS = 500;
- protected boolean playNowOverride = false;
- protected List<Entry> songs;
-
- public RecursiveLoader(Activity context) {
- super(context);
- }
-
- protected void getSongsRecursively(MusicDirectory parent, List<Entry> songs) throws Exception {
- if (songs.size() > MAX_SONGS) {
- return;
- }
-
- for (Entry song : parent.getChildren(false, true)) {
- if (!song.isVideo() && song.getRating() != 1) {
- songs.add(song);
- }
- }
- for (Entry dir : parent.getChildren(true, false)) {
- if(dir.getRating() == 1) {
- continue;
- }
-
- MusicDirectory musicDirectory;
- if(Util.isTagBrowsing(context) && !Util.isOffline(context)) {
- musicDirectory = musicService.getAlbum(dir.getId(), dir.getTitle(), false, context, this);
- } else {
- musicDirectory = musicService.getMusicDirectory(dir.getId(), dir.getTitle(), false, context, this);
- }
- getSongsRecursively(musicDirectory, songs);
- }
- }
-
- @Override
- protected void done(Boolean result) {
- warnIfStorageUnavailable();
-
- if(playNowOverride) {
- playNow(songs);
- return;
- }
-
- if(result) {
- Util.startActivityWithoutTransition(context, DownloadActivity.class);
- }
- }
- }
-}
+/*
+ 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 github.daneren2005.dsub.fragments;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.media.MediaMetadataRetriever;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.StatFs;
+import android.support.v4.app.Fragment;
+import android.support.v4.widget.SwipeRefreshLayout;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.GestureDetector;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.RatingBar;
+import android.widget.TextView;
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.activity.DownloadActivity;
+import github.daneren2005.dsub.activity.SubsonicActivity;
+import github.daneren2005.dsub.activity.SubsonicFragmentActivity;
+import github.daneren2005.dsub.domain.Artist;
+import github.daneren2005.dsub.domain.Bookmark;
+import github.daneren2005.dsub.domain.Genre;
+import github.daneren2005.dsub.domain.MusicDirectory;
+import github.daneren2005.dsub.domain.Playlist;
+import github.daneren2005.dsub.domain.PodcastEpisode;
+import github.daneren2005.dsub.domain.ServerInfo;
+import github.daneren2005.dsub.domain.Share;
+import github.daneren2005.dsub.service.DownloadFile;
+import github.daneren2005.dsub.service.DownloadService;
+import github.daneren2005.dsub.service.MediaStoreService;
+import github.daneren2005.dsub.service.MusicService;
+import github.daneren2005.dsub.service.MusicServiceFactory;
+import github.daneren2005.dsub.service.OfflineException;
+import github.daneren2005.dsub.service.ServerTooOldException;
+import github.daneren2005.dsub.util.Constants;
+import github.daneren2005.dsub.util.FileUtil;
+import github.daneren2005.dsub.util.ImageLoader;
+import github.daneren2005.dsub.util.ProgressListener;
+import github.daneren2005.dsub.util.SilentBackgroundTask;
+import github.daneren2005.dsub.util.LoadingTask;
+import github.daneren2005.dsub.util.UserUtil;
+import github.daneren2005.dsub.util.Util;
+import github.daneren2005.dsub.view.AlbumCell;
+import github.daneren2005.dsub.view.AlbumView;
+import github.daneren2005.dsub.view.ArtistEntryView;
+import github.daneren2005.dsub.view.ArtistView;
+import github.daneren2005.dsub.view.PlaylistSongView;
+import github.daneren2005.dsub.view.SongView;
+import github.daneren2005.dsub.view.UpdateView;
+
+import java.io.File;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Random;
+
+import static github.daneren2005.dsub.domain.MusicDirectory.Entry;
+
+public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
+ private static final String TAG = SubsonicFragment.class.getSimpleName();
+ private static int TAG_INC = 10;
+ private int tag;
+
+ protected SubsonicActivity context;
+ protected CharSequence title = null;
+ protected CharSequence subtitle = null;
+ protected View rootView;
+ protected boolean primaryFragment = false;
+ protected boolean secondaryFragment = false;
+ protected boolean invalidated = false;
+ protected static Random random = new Random();
+ protected GestureDetector gestureScanner;
+ protected Share share;
+ protected boolean artist = false;
+ protected boolean artistOverride = false;
+ protected SwipeRefreshLayout refreshLayout;
+ protected boolean firstRun;
+
+ public SubsonicFragment() {
+ super();
+ tag = TAG_INC++;
+ }
+
+ @Override
+ public void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+
+ if(bundle != null) {
+ String name = bundle.getString(Constants.FRAGMENT_NAME);
+ if(name != null) {
+ title = name;
+ }
+ }
+ firstRun = true;
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ if(title != null) {
+ outState.putString(Constants.FRAGMENT_NAME, title.toString());
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if(firstRun) {
+ firstRun = false;
+ } else {
+ UpdateView.triggerUpdate();
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ context = (SubsonicActivity)activity;
+ }
+
+ public void setContext(SubsonicActivity context) {
+ this.context = context;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.menu_shuffle:
+ onShuffleRequested();
+ return true;
+ case R.id.menu_search:
+ context.onSearchRequested();
+ return true;
+ case R.id.menu_exit:
+ exit();
+ return true;
+ case R.id.menu_refresh:
+ refresh();
+ return true;
+ }
+
+ return false;
+ }
+
+ public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo, Object selected) {
+ MenuInflater inflater = context.getMenuInflater();
+
+ if(selected instanceof Entry) {
+ Entry entry = (Entry) selected;
+ if(entry instanceof PodcastEpisode && !entry.isVideo()) {
+ if(Util.isOffline(context)) {
+ inflater.inflate(R.menu.select_podcast_episode_context_offline, menu);
+ }
+ else {
+ inflater.inflate(R.menu.select_podcast_episode_context, menu);
+
+ if(entry.getBookmark() == null) {
+ menu.removeItem(R.id.bookmark_menu_delete);
+ }
+ }
+ }
+ else if (entry.isDirectory()) {
+ if(Util.isOffline(context)) {
+ inflater.inflate(R.menu.select_album_context_offline, menu);
+ }
+ else {
+ inflater.inflate(R.menu.select_album_context, menu);
+
+ if(Util.isTagBrowsing(context)) {
+ menu.removeItem(R.id.menu_rate);
+ }
+ }
+ menu.findItem(entry.isDirectory() ? R.id.album_menu_star : R.id.song_menu_star).setTitle(entry.isStarred() ? R.string.common_unstar : R.string.common_star);
+ } else if(!entry.isVideo()) {
+ if(Util.isOffline(context)) {
+ inflater.inflate(R.menu.select_song_context_offline, menu);
+ }
+ else {
+ inflater.inflate(R.menu.select_song_context, menu);
+
+ if(entry.getBookmark() == null) {
+ menu.removeItem(R.id.bookmark_menu_delete);
+ }
+ }
+ menu.findItem(entry.isDirectory() ? R.id.album_menu_star : R.id.song_menu_star).setTitle(entry.isStarred() ? R.string.common_unstar : R.string.common_star);
+ } else {
+ if(Util.isOffline(context)) {
+ inflater.inflate(R.menu.select_video_context_offline, menu);
+ }
+ else {
+ inflater.inflate(R.menu.select_video_context, menu);
+ }
+ }
+ } else if(selected instanceof Artist) {
+ Artist artist = (Artist) selected;
+ if(Util.isOffline(context)) {
+ inflater.inflate(R.menu.select_artist_context_offline, menu);
+ }
+ else {
+ inflater.inflate(R.menu.select_artist_context, menu);
+
+ menu.findItem(R.id.artist_menu_star).setTitle(artist.isStarred() ? R.string.common_unstar : R.string.common_star);
+ }
+ }
+
+ hideMenuItems(menu, (AdapterView.AdapterContextMenuInfo) menuInfo);
+ }
+
+ protected void hideMenuItems(ContextMenu menu, AdapterView.AdapterContextMenuInfo info) {
+ if(!ServerInfo.checkServerVersion(context, "1.8")) {
+ menu.setGroupVisible(R.id.server_1_8, false);
+ menu.setGroupVisible(R.id.hide_star, false);
+ }
+ if(!ServerInfo.checkServerVersion(context, "1.9")) {
+ menu.setGroupVisible(R.id.server_1_9, false);
+ }
+ if(!ServerInfo.checkServerVersion(context, "1.10.1")) {
+ menu.setGroupVisible(R.id.server_1_10, false);
+ }
+
+ SharedPreferences prefs = Util.getPreferences(context);
+ if(!prefs.getBoolean(Constants.PREFERENCES_KEY_MENU_PLAY_NEXT, true)) {
+ menu.setGroupVisible(R.id.hide_play_next, false);
+ }
+ if(!prefs.getBoolean(Constants.PREFERENCES_KEY_MENU_PLAY_LAST, true)) {
+ menu.setGroupVisible(R.id.hide_play_last, false);
+ }
+ if(!prefs.getBoolean(Constants.PREFERENCES_KEY_MENU_STAR, true)) {
+ menu.setGroupVisible(R.id.hide_star, false);
+ }
+ if(!prefs.getBoolean(Constants.PREFERENCES_KEY_MENU_SHARED, true) || !UserUtil.canShare()) {
+ menu.setGroupVisible(R.id.hide_share, false);
+ }
+ if(!prefs.getBoolean(Constants.PREFERENCES_KEY_MENU_RATING, true)) {
+ menu.setGroupVisible(R.id.hide_rating, false);
+ }
+
+ if(!Util.isOffline(context)) {
+ // If we are looking at a standard song view, get downloadFile to cache what options to show
+ if(info.targetView instanceof SongView) {
+ SongView songView = (SongView) info.targetView;
+ DownloadFile downloadFile = songView.getDownloadFile();
+
+ try {
+ if(downloadFile != null) {
+ if(downloadFile.isWorkDone()) {
+ // Remove permanent cache menu if already perma cached
+ if(downloadFile.isSaved()) {
+ menu.removeItem(R.id.song_menu_pin);
+ }
+
+ // Remove cache option no matter what if already downloaded
+ menu.removeItem(R.id.song_menu_download);
+ } else {
+ // Remove delete option if nothing to delete
+ menu.removeItem(R.id.song_menu_delete);
+ }
+ }
+ } catch(Exception e) {
+ Log.w(TAG, "Failed to lookup downloadFile info", e);
+ }
+ }
+ // Apply similar logic to album views
+ else if(info.targetView instanceof AlbumCell || info.targetView instanceof AlbumView
+ || info.targetView instanceof ArtistView || info.targetView instanceof ArtistEntryView) {
+ File folder = null;
+ int id = 0;
+ if(info.targetView instanceof AlbumCell) {
+ folder = ((AlbumCell) info.targetView).getFile();
+ id = R.id.album_menu_delete;
+ } else if(info.targetView instanceof AlbumView) {
+ folder = ((AlbumView) info.targetView).getFile();
+ id = R.id.album_menu_delete;
+ } else if(info.targetView instanceof ArtistView) {
+ folder = ((ArtistView) info.targetView).getFile();
+ id = R.id.artist_menu_delete;
+ } else if(info.targetView instanceof ArtistEntryView) {
+ folder = ((ArtistEntryView) info.targetView).getFile();
+ id = R.id.artist_menu_delete;
+ }
+
+ try {
+ if(folder != null && !folder.exists()) {
+ menu.removeItem(id);
+ }
+ } catch(Exception e) {
+ Log.w(TAG, "Failed to lookup album directory info", e);
+ }
+ }
+ }
+ }
+
+ protected void recreateContextMenu(ContextMenu menu) {
+ List<MenuItem> menuItems = new ArrayList<MenuItem>();
+ for(int i = 0; i < menu.size(); i++) {
+ MenuItem item = menu.getItem(i);
+ if(item.isVisible()) {
+ menuItems.add(item);
+ }
+ }
+ menu.clear();
+ for(int i = 0; i < menuItems.size(); i++) {
+ MenuItem item = menuItems.get(i);
+ menu.add(tag, item.getItemId(), Menu.NONE, item.getTitle());
+ }
+ }
+
+ public boolean onContextItemSelected(MenuItem menuItem, Object selectedItem) {
+ Artist artist = selectedItem instanceof Artist ? (Artist) selectedItem : null;
+ Entry entry = selectedItem instanceof Entry ? (Entry) selectedItem : null;
+ List<Entry> songs = new ArrayList<Entry>(1);
+ songs.add(entry);
+
+ switch (menuItem.getItemId()) {
+ case R.id.artist_menu_play_now:
+ downloadRecursively(artist.getId(), false, false, true, false, false);
+ break;
+ case R.id.artist_menu_play_shuffled:
+ downloadRecursively(artist.getId(), false, false, true, true, false);
+ break;
+ case R.id.artist_menu_play_next:
+ downloadRecursively(artist.getId(), false, true, false, false, false, true);
+ break;
+ case R.id.artist_menu_play_last:
+ downloadRecursively(artist.getId(), false, true, false, false, false);
+ break;
+ case R.id.artist_menu_download:
+ downloadRecursively(artist.getId(), false, true, false, false, true);
+ break;
+ case R.id.artist_menu_pin:
+ downloadRecursively(artist.getId(), true, true, false, false, true);
+ break;
+ case R.id.artist_menu_delete:
+ deleteRecursively(artist);
+ break;
+ case R.id.artist_menu_star:
+ toggleStarred(artist);
+ break;
+ case R.id.album_menu_play_now:
+ artistOverride = true;
+ downloadRecursively(entry.getId(), false, false, true, false, false);
+ break;
+ case R.id.album_menu_play_shuffled:
+ artistOverride = true;
+ downloadRecursively(entry.getId(), false, false, true, true, false);
+ break;
+ case R.id.album_menu_play_next:
+ artistOverride = true;
+ downloadRecursively(entry.getId(), false, true, false, false, false, true);
+ break;
+ case R.id.album_menu_play_last:
+ artistOverride = true;
+ downloadRecursively(entry.getId(), false, true, false, false, false);
+ break;
+ case R.id.album_menu_download:
+ artistOverride = true;
+ downloadRecursively(entry.getId(), false, true, false, false, true);
+ break;
+ case R.id.album_menu_pin:
+ artistOverride = true;
+ downloadRecursively(entry.getId(), true, true, false, false, true);
+ break;
+ case R.id.album_menu_star:
+ toggleStarred(entry);
+ break;
+ case R.id.album_menu_delete:
+ deleteRecursively(entry);
+ break;
+ case R.id.album_menu_info:
+ displaySongInfo(entry);
+ break;
+ case R.id.album_menu_show_artist:
+ showAlbumArtist((Entry) selectedItem);
+ break;
+ case R.id.album_menu_share:
+ createShare(songs);
+ break;
+ case R.id.song_menu_play_now:
+ playNow(songs);
+ break;
+ case R.id.song_menu_play_next:
+ getDownloadService().download(songs, false, false, true, false);
+ break;
+ case R.id.song_menu_play_last:
+ getDownloadService().download(songs, false, false, false, false);
+ break;
+ case R.id.song_menu_download:
+ getDownloadService().downloadBackground(songs, false);
+ break;
+ case R.id.song_menu_pin:
+ getDownloadService().downloadBackground(songs, true);
+ break;
+ case R.id.song_menu_delete:
+ getDownloadService().delete(songs);
+ break;
+ case R.id.song_menu_add_playlist:
+ addToPlaylist(songs);
+ break;
+ case R.id.song_menu_star:
+ toggleStarred(entry);
+ break;
+ case R.id.song_menu_play_external:
+ playExternalPlayer(entry);
+ break;
+ case R.id.song_menu_info:
+ displaySongInfo(entry);
+ break;
+ case R.id.song_menu_stream_external:
+ streamExternalPlayer(entry);
+ break;
+ case R.id.song_menu_share:
+ createShare(songs);
+ break;
+ case R.id.song_menu_show_album:
+ showAlbum((Entry) selectedItem);
+ break;
+ case R.id.song_menu_show_artist:
+ showArtist((Entry) selectedItem);
+ break;
+ case R.id.bookmark_menu_delete:
+ deleteBookmark(entry, null);
+ break;
+ case R.id.menu_rate:
+ setRating(entry);
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+ }
+
+ public void replaceFragment(SubsonicFragment fragment) {
+ replaceFragment(fragment, true);
+ }
+ public void replaceFragment(SubsonicFragment fragment, boolean replaceCurrent) {
+ context.replaceFragment(fragment, fragment.getSupportTag(), secondaryFragment && replaceCurrent);
+ }
+
+ public int getRootId() {
+ return rootView.getId();
+ }
+
+ public void setSupportTag(int tag) { this.tag = tag; }
+ public void setSupportTag(String tag) { this.tag = Integer.parseInt(tag); }
+ public int getSupportTag() {
+ return tag;
+ }
+
+ public void setPrimaryFragment(boolean primary) {
+ primaryFragment = primary;
+ if(primary) {
+ if(context != null && title != null) {
+ context.setTitle(title);
+ context.setSubtitle(subtitle);
+ }
+ if(invalidated) {
+ invalidated = false;
+ refresh(false);
+ }
+ }
+ }
+ public void setPrimaryFragment(boolean primary, boolean secondary) {
+ setPrimaryFragment(primary);
+ secondaryFragment = secondary;
+ }
+ public void setSecondaryFragment(boolean secondary) {
+ secondaryFragment = secondary;
+ }
+
+ public void invalidate() {
+ if(primaryFragment) {
+ refresh(true);
+ } else {
+ invalidated = true;
+ }
+ }
+
+ public DownloadService getDownloadService() {
+ return context != null ? context.getDownloadService() : null;
+ }
+
+ protected void refresh() {
+ refresh(true);
+ }
+ protected void refresh(boolean refresh) {
+
+ }
+
+ @Override
+ public void onRefresh() {
+ refreshLayout.setRefreshing(false);
+ refresh();
+ }
+
+ protected void exit() {
+ if(((Object) context).getClass() != SubsonicFragmentActivity.class) {
+ Intent intent = new Intent(context, SubsonicFragmentActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ intent.putExtra(Constants.INTENT_EXTRA_NAME_EXIT, true);
+ Util.startActivityWithoutTransition(context, intent);
+ } else {
+ context.stopService(new Intent(context, DownloadService.class));
+ context.finish();
+ }
+ }
+
+ public void setProgressVisible(boolean visible) {
+ View view = rootView.findViewById(R.id.tab_progress);
+ if (view != null) {
+ view.setVisibility(visible ? View.VISIBLE : View.GONE);
+
+ if(visible) {
+ View progress = rootView.findViewById(R.id.tab_progress_spinner);
+ progress.setVisibility(View.VISIBLE);
+ }
+ }
+ }
+
+ public void updateProgress(String message) {
+ TextView view = (TextView) rootView.findViewById(R.id.tab_progress_message);
+ if (view != null) {
+ view.setText(message);
+ }
+ }
+
+ public void setEmpty(boolean empty) {
+ View view = rootView.findViewById(R.id.tab_progress);
+ if(empty) {
+ view.setVisibility(View.VISIBLE);
+
+ View progress = view.findViewById(R.id.tab_progress_spinner);
+ progress.setVisibility(View.GONE);
+
+ TextView text = (TextView) view.findViewById(R.id.tab_progress_message);
+ text.setText(R.string.common_empty);
+ } else {
+ view.setVisibility(View.GONE);
+ }
+ }
+
+ protected synchronized ImageLoader getImageLoader() {
+ return context.getImageLoader();
+ }
+ public synchronized static ImageLoader getStaticImageLoader(Context context) {
+ return SubsonicActivity.getStaticImageLoader(context);
+ }
+
+ public void setTitle(CharSequence title) {
+ this.title = title;
+ context.setTitle(title);
+ }
+ public void setTitle(int title) {
+ this.title = context.getResources().getString(title);
+ context.setTitle(this.title);
+ }
+ public void setSubtitle(CharSequence title) {
+ this.subtitle = title;
+ context.setSubtitle(title);
+ }
+ public CharSequence getTitle() {
+ return this.title;
+ }
+
+ protected void setupScrollList(final AbsListView listView) {
+ if(!context.isTouchscreen()) {
+ refreshLayout.setEnabled(false);
+ } else {
+ listView.setOnScrollListener(new AbsListView.OnScrollListener() {
+ @Override
+ public void onScrollStateChanged(AbsListView view, int scrollState) {
+ }
+
+ @Override
+ public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
+ int topRowVerticalPosition = (listView.getChildCount() == 0) ? 0 : listView.getChildAt(0).getTop();
+ refreshLayout.setEnabled(topRowVerticalPosition >= 0 && listView.getFirstVisiblePosition() == 0);
+ }
+ });
+
+ refreshLayout.setColorScheme(
+ R.color.holo_blue_light,
+ R.color.holo_orange_light,
+ R.color.holo_green_light,
+ R.color.holo_red_light);
+ }
+ }
+
+ protected void warnIfStorageUnavailable() {
+ if (!Util.isExternalStoragePresent()) {
+ Util.toast(context, R.string.select_album_no_sdcard);
+ }
+
+ try {
+ StatFs stat = new StatFs(FileUtil.getMusicDirectory(context).getPath());
+ long bytesAvailableFs = (long) stat.getAvailableBlocks() * (long) stat.getBlockSize();
+ if (bytesAvailableFs < 50000000L) {
+ Util.toast(context, context.getResources().getString(R.string.select_album_no_room, Util.formatBytes(bytesAvailableFs)));
+ }
+ } catch(Exception e) {
+ Log.w(TAG, "Error while checking storage space for music directory", e);
+ }
+ }
+
+ protected void onShuffleRequested() {
+ if(Util.isOffline(context)) {
+ Intent intent = new Intent(context, DownloadActivity.class);
+ intent.putExtra(Constants.INTENT_EXTRA_NAME_SHUFFLE, true);
+ Util.startActivityWithoutTransition(context, intent);
+ return;
+ }
+
+ View dialogView = context.getLayoutInflater().inflate(R.layout.shuffle_dialog, null);
+ final EditText startYearBox = (EditText)dialogView.findViewById(R.id.start_year);
+ final EditText endYearBox = (EditText)dialogView.findViewById(R.id.end_year);
+ final EditText genreBox = (EditText)dialogView.findViewById(R.id.genre);
+ final Button genreCombo = (Button)dialogView.findViewById(R.id.genre_combo);
+
+ final SharedPreferences prefs = Util.getPreferences(context);
+ final String oldStartYear = prefs.getString(Constants.PREFERENCES_KEY_SHUFFLE_START_YEAR, "");
+ final String oldEndYear = prefs.getString(Constants.PREFERENCES_KEY_SHUFFLE_END_YEAR, "");
+ final String oldGenre = prefs.getString(Constants.PREFERENCES_KEY_SHUFFLE_GENRE, "");
+
+ boolean _useCombo = false;
+ if(ServerInfo.checkServerVersion(context, "1.9.0")) {
+ genreBox.setVisibility(View.GONE);
+ genreCombo.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ new LoadingTask<List<Genre>>(context, true) {
+ @Override
+ protected List<Genre> doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ return musicService.getGenres(false, context, this);
+ }
+
+ @Override
+ protected void done(final List<Genre> genres) {
+ List<String> names = new ArrayList<String>();
+ String blank = context.getResources().getString(R.string.select_genre_blank);
+ names.add(blank);
+ for(Genre genre: genres) {
+ names.add(genre.getName());
+ }
+ final List<String> finalNames = names;
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.shuffle_pick_genre)
+ .setItems(names.toArray(new CharSequence[names.size()]), new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ if(which == 0) {
+ genreCombo.setText("");
+ } else {
+ genreCombo.setText(finalNames.get(which));
+ }
+ }
+ });
+ AlertDialog dialog = builder.create();
+ dialog.show();
+ }
+
+ @Override
+ protected void error(Throwable error) {
+ String msg;
+ if (error instanceof OfflineException || error instanceof ServerTooOldException) {
+ msg = getErrorMessage(error);
+ } else {
+ msg = context.getResources().getString(R.string.playlist_error) + " " + getErrorMessage(error);
+ }
+
+ Util.toast(context, msg, false);
+ }
+ }.execute();
+ }
+ });
+ _useCombo = true;
+ } else {
+ genreCombo.setVisibility(View.GONE);
+ }
+ final boolean useCombo = _useCombo;
+
+ startYearBox.setText(oldStartYear);
+ endYearBox.setText(oldEndYear);
+ genreBox.setText(oldGenre);
+ genreCombo.setText(oldGenre);
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.shuffle_title)
+ .setView(dialogView)
+ .setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ Intent intent = new Intent(context, DownloadActivity.class);
+ intent.putExtra(Constants.INTENT_EXTRA_NAME_SHUFFLE, true);
+ String genre;
+ if(useCombo) {
+ genre = genreCombo.getText().toString();
+ } else {
+ genre = genreBox.getText().toString();
+ }
+ String startYear = startYearBox.getText().toString();
+ String endYear = endYearBox.getText().toString();
+
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putString(Constants.PREFERENCES_KEY_SHUFFLE_START_YEAR, startYear);
+ editor.putString(Constants.PREFERENCES_KEY_SHUFFLE_END_YEAR, endYear);
+ editor.putString(Constants.PREFERENCES_KEY_SHUFFLE_GENRE, genre);
+ editor.commit();
+
+ Util.startActivityWithoutTransition(context, intent);
+ }
+ })
+ .setNegativeButton(R.string.common_cancel, null);
+ AlertDialog dialog = builder.create();
+ dialog.show();
+ }
+
+ public void toggleStarred(Entry entry) {
+ toggleStarred(entry, null);
+ }
+ public void toggleStarred(final Entry entry, final OnStarChange onStarChange) {
+ final boolean starred = !entry.isStarred();
+ entry.setStarred(starred);
+ if(onStarChange != null) {
+ onStarChange.starChange(starred);
+ }
+
+ new SilentBackgroundTask<Void>(context) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ if(entry.isDirectory() && Util.isTagBrowsing(context) && !Util.isOffline(context)) {
+ if(entry.isAlbum()) {
+ musicService.setStarred(null, null, Arrays.asList(entry), starred, null, context);
+ } else {
+ musicService.setStarred(null, Arrays.asList(entry), null, starred, null, context);
+ }
+ } else {
+ musicService.setStarred(Arrays.asList(entry), null, null, starred, null, context);
+ }
+
+ new EntryInstanceUpdater(entry) {
+ @Override
+ public void update(Entry found) {
+ found.setStarred(starred);
+ }
+ }.execute();
+
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ // UpdateView
+ Util.toast(context, context.getResources().getString(starred ? R.string.starring_content_starred : R.string.starring_content_unstarred, entry.getTitle()));
+ }
+
+ @Override
+ protected void error(Throwable error) {
+ Log.w(TAG, "Failed to star", error);
+ entry.setStarred(!starred);
+ if(onStarChange != null) {
+ onStarChange.starChange(!starred);
+ }
+
+ String msg;
+ if (error instanceof OfflineException || error instanceof ServerTooOldException) {
+ msg = getErrorMessage(error);
+ } else {
+ msg = context.getResources().getString(R.string.starring_content_error, entry.getTitle()) + " " + getErrorMessage(error);
+ }
+
+ Util.toast(context, msg, false);
+ }
+ }.execute();
+ }
+
+ public void toggleStarred(final Artist entry) {
+ final boolean starred = !entry.isStarred();
+ entry.setStarred(starred);
+
+ new SilentBackgroundTask<Void>(context) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ if(Util.isTagBrowsing(context) && !Util.isOffline(context)) {
+ musicService.setStarred(null, Arrays.asList(new Entry(entry)), null, starred, null, context);
+ } else {
+ musicService.setStarred(Arrays.asList(new Entry(entry)), null, null, starred, null, context);
+ }
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ // UpdateView
+ Util.toast(context, context.getResources().getString(starred ? R.string.starring_content_starred : R.string.starring_content_unstarred, entry.getName()));
+ }
+
+ @Override
+ protected void error(Throwable error) {
+ Log.w(TAG, "Failed to star", error);
+ entry.setStarred(!starred);
+
+ String msg;
+ if (error instanceof OfflineException || error instanceof ServerTooOldException) {
+ msg = getErrorMessage(error);
+ } else {
+ msg = context.getResources().getString(R.string.starring_content_error, entry.getName()) + " " + getErrorMessage(error);
+ }
+
+ Util.toast(context, msg, false);
+ }
+ }.execute();
+ }
+
+ protected void downloadRecursively(final String id, final boolean save, final boolean append, final boolean autoplay, final boolean shuffle, final boolean background) {
+ downloadRecursively(id, "", true, save, append, autoplay, shuffle, background);
+ }
+ protected void downloadRecursively(final String id, final boolean save, final boolean append, final boolean autoplay, final boolean shuffle, final boolean background, final boolean playNext) {
+ downloadRecursively(id, "", true, save, append, autoplay, shuffle, background, playNext);
+ }
+ protected void downloadPlaylist(final String id, final String name, final boolean save, final boolean append, final boolean autoplay, final boolean shuffle, final boolean background) {
+ downloadRecursively(id, name, false, save, append, autoplay, shuffle, background);
+ }
+ protected void downloadRecursively(final String id, final String name, final boolean isDirectory, final boolean save, final boolean append, final boolean autoplay, final boolean shuffle, final boolean background) {
+ downloadRecursively(id, name, isDirectory, save, append, autoplay, shuffle, background, false);
+ }
+ protected void downloadRecursively(final String id, final String name, final boolean isDirectory, final boolean save, final boolean append, final boolean autoplay, final boolean shuffle, final boolean background, final boolean playNext) {
+ new RecursiveLoader(context) {
+ @Override
+ protected Boolean doInBackground() throws Throwable {
+ musicService = MusicServiceFactory.getMusicService(context);
+ MusicDirectory root;
+ if(share != null) {
+ root = share.getMusicDirectory();
+ }
+ else if(isDirectory) {
+ if(id != null) {
+ root = getMusicDirectory(id, name, false, musicService, this);
+ } else {
+ root = musicService.getStarredList(context, this);
+ }
+ }
+ else {
+ root = musicService.getPlaylist(true, id, name, context, this);
+ }
+
+ if(shuffle) {
+ Collections.shuffle(root.getChildren());
+ }
+
+ songs = new LinkedList<Entry>();
+ getSongsRecursively(root, songs);
+
+ DownloadService downloadService = getDownloadService();
+ boolean transition = false;
+ if (!songs.isEmpty() && downloadService != null) {
+ // Conditions for a standard play now operation
+ if(!append && !save && autoplay && !playNext && !shuffle && !background) {
+ playNowOverride = true;
+ return false;
+ }
+
+ if (!append && !background) {
+ downloadService.clear();
+ }
+ if(!background) {
+ downloadService.download(songs, save, autoplay, playNext, false);
+ if(!append) {
+ transition = true;
+ }
+ }
+ else {
+ downloadService.downloadBackground(songs, save);
+ }
+ }
+ artistOverride = false;
+
+ return transition;
+ }
+ }.execute();
+ }
+
+ protected void downloadRecursively(final List<Entry> albums, final boolean shuffle, final boolean append) {
+ new RecursiveLoader(context) {
+ @Override
+ protected Boolean doInBackground() throws Throwable {
+ musicService = MusicServiceFactory.getMusicService(context);
+
+ if(shuffle) {
+ Collections.shuffle(albums);
+ }
+
+ songs = new LinkedList<Entry>();
+ MusicDirectory root = new MusicDirectory();
+ root.addChildren(albums);
+ getSongsRecursively(root, songs);
+
+ DownloadService downloadService = getDownloadService();
+ boolean transition = false;
+ if (!songs.isEmpty() && downloadService != null) {
+ // Conditions for a standard play now operation
+ if(!append && !shuffle) {
+ playNowOverride = true;
+ return false;
+ }
+
+ if (!append) {
+ downloadService.clear();
+ }
+
+ downloadService.download(songs, false, true, false, false);
+ if(!append) {
+ transition = true;
+ }
+ }
+ artistOverride = false;
+
+ return transition;
+ }
+ }.execute();
+ }
+
+ protected MusicDirectory getMusicDirectory(String id, String name, boolean refresh, MusicService service, ProgressListener listener) throws Exception {
+ return getMusicDirectory(id, name, refresh, false, service, listener);
+ }
+ protected MusicDirectory getMusicDirectory(String id, String name, boolean refresh, boolean forceArtist, MusicService service, ProgressListener listener) throws Exception {
+ if(Util.isTagBrowsing(context) && !Util.isOffline(context)) {
+ if(artist && !artistOverride || forceArtist) {
+ return service.getArtist(id, name, refresh, context, listener);
+ } else {
+ return service.getAlbum(id, name, refresh, context, listener);
+ }
+ } else {
+ return service.getMusicDirectory(id, name, refresh, context, listener);
+ }
+ }
+
+ protected void addToPlaylist(final List<Entry> songs) {
+ if(songs.isEmpty()) {
+ Util.toast(context, "No songs selected");
+ return;
+ }
+
+ new LoadingTask<List<Playlist>>(context, true) {
+ @Override
+ protected List<Playlist> doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ List<Playlist> playlists = new ArrayList<Playlist>();
+ playlists.addAll(musicService.getPlaylists(false, context, this));
+
+ // Iterate through and remove all non owned public playlists
+ Iterator<Playlist> it = playlists.iterator();
+ while(it.hasNext()) {
+ Playlist playlist = it.next();
+ if(playlist.getPublic() == true && playlist.getId().indexOf(".m3u") == -1 && !UserUtil.getCurrentUsername(context).equals(playlist.getOwner())) {
+ it.remove();
+ }
+ }
+
+ return playlists;
+ }
+
+ @Override
+ protected void done(final List<Playlist> playlists) {
+ // Create adapter to show playlists
+ Playlist createNew = new Playlist("-1", context.getResources().getString(R.string.playlist_create_new));
+ playlists.add(0, createNew);
+ ArrayAdapter playlistAdapter = new ArrayAdapter<Playlist>(context, R.layout.basic_count_item, playlists) {
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ Playlist playlist = getItem(position);
+
+ // Create new if not getting a convert view to use
+ PlaylistSongView view;
+ if(convertView instanceof PlaylistSongView) {
+ view = (PlaylistSongView) convertView;
+ } else {
+ view = new PlaylistSongView(context);
+ }
+
+ view.setObject(playlist, songs);
+
+ return view;
+ }
+ };
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.playlist_add_to)
+ .setAdapter(playlistAdapter, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ if (which > 0) {
+ addToPlaylist(playlists.get(which), songs);
+ } else {
+ createNewPlaylist(songs, false);
+ }
+ }
+ });
+ AlertDialog dialog = builder.create();
+ dialog.show();
+ }
+
+ @Override
+ protected void error(Throwable error) {
+ String msg;
+ if (error instanceof OfflineException || error instanceof ServerTooOldException) {
+ msg = getErrorMessage(error);
+ } else {
+ msg = context.getResources().getString(R.string.playlist_error) + " " + getErrorMessage(error);
+ }
+
+ Util.toast(context, msg, false);
+ }
+ }.execute();
+ }
+
+ private void addToPlaylist(final Playlist playlist, final List<Entry> songs) {
+ new SilentBackgroundTask<Void>(context) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ musicService.addToPlaylist(playlist.getId(), songs, context, null);
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ Util.toast(context, context.getResources().getString(R.string.updated_playlist, songs.size(), playlist.getName()));
+ }
+
+ @Override
+ protected void error(Throwable error) {
+ String msg;
+ if (error instanceof OfflineException || error instanceof ServerTooOldException) {
+ msg = getErrorMessage(error);
+ } else {
+ msg = context.getResources().getString(R.string.updated_playlist_error, playlist.getName()) + " " + getErrorMessage(error);
+ }
+
+ Util.toast(context, msg, false);
+ }
+ }.execute();
+ }
+
+ protected void createNewPlaylist(final List<Entry> songs, final boolean getSuggestion) {
+ View layout = context.getLayoutInflater().inflate(R.layout.save_playlist, null);
+ final EditText playlistNameView = (EditText) layout.findViewById(R.id.save_playlist_name);
+ final CheckBox overwriteCheckBox = (CheckBox) layout.findViewById(R.id.save_playlist_overwrite);
+ if(getSuggestion) {
+ String playlistName = (getDownloadService() != null) ? getDownloadService().getSuggestedPlaylistName() : null;
+ if (playlistName != null) {
+ playlistNameView.setText(playlistName);
+ try {
+ if(ServerInfo.checkServerVersion(context, "1.8.0") && Integer.parseInt(getDownloadService().getSuggestedPlaylistId()) != -1) {
+ overwriteCheckBox.setChecked(true);
+ overwriteCheckBox.setVisibility(View.VISIBLE);
+ }
+ } catch(Exception e) {
+ Log.d(TAG, "Playlist id isn't a integer, probably MusicCabinet");
+ }
+ } else {
+ DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
+ playlistNameView.setText(dateFormat.format(new Date()));
+ }
+ } else {
+ DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
+ playlistNameView.setText(dateFormat.format(new Date()));
+ }
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.download_playlist_title)
+ .setMessage(R.string.download_playlist_name)
+ .setView(layout)
+ .setPositiveButton(R.string.common_save, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ String playlistName = String.valueOf(playlistNameView.getText());
+ if(overwriteCheckBox.isChecked()) {
+ overwritePlaylist(songs, playlistName, getDownloadService().getSuggestedPlaylistId());
+ } else {
+ createNewPlaylist(songs, playlistName);
+
+ if(getSuggestion) {
+ DownloadService downloadService = getDownloadService();
+ if(downloadService != null) {
+ downloadService.setSuggestedPlaylistName(playlistName, null);
+ }
+ }
+ }
+ }
+ })
+ .setNegativeButton(R.string.common_cancel, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ dialog.cancel();
+ }
+ })
+ .setCancelable(true);
+
+ AlertDialog dialog = builder.create();
+ dialog.show();
+ }
+ private void createNewPlaylist(final List<Entry> songs, final String name) {
+ new SilentBackgroundTask<Void>(context) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ musicService.createPlaylist(null, name, songs, context, null);
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ Util.toast(context, R.string.download_playlist_done);
+ }
+
+ @Override
+ protected void error(Throwable error) {
+ String msg = context.getResources().getString(R.string.download_playlist_error) + " " + getErrorMessage(error);
+ Util.toast(context, msg);
+ }
+ }.execute();
+ }
+ private void overwritePlaylist(final List<Entry> songs, final String name, final String id) {
+ new SilentBackgroundTask<Void>(context) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ MusicDirectory playlist = musicService.getPlaylist(true, id, name, context, null);
+ List<Entry> toDelete = playlist.getChildren();
+ musicService.overwritePlaylist(id, name, toDelete.size(), songs, context, null);
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ Util.toast(context, R.string.download_playlist_done);
+ }
+
+ @Override
+ protected void error(Throwable error) {
+ String msg;
+ if (error instanceof OfflineException || error instanceof ServerTooOldException) {
+ msg = getErrorMessage(error);
+ } else {
+ msg = context.getResources().getString(R.string.download_playlist_error) + " " + getErrorMessage(error);
+ }
+
+ Util.toast(context, msg, false);
+ }
+ }.execute();
+ }
+
+ public void displaySongInfo(final Entry song) {
+ Integer duration = null;
+ Integer bitrate = null;
+ String format = null;
+ long size = 0;
+ if(!song.isDirectory()) {
+ try {
+ DownloadFile downloadFile = new DownloadFile(context, song, false);
+ File file = downloadFile.getCompleteFile();
+ if(file.exists()) {
+ MediaMetadataRetriever metadata = new MediaMetadataRetriever();
+ metadata.setDataSource(file.getAbsolutePath());
+
+ String tmp = metadata.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
+ duration = Integer.parseInt((tmp != null) ? tmp : "0") / 1000;
+ format = FileUtil.getExtension(file.getName());
+ size = file.length();
+
+ // If no duration try to read bitrate tag
+ if(duration == null) {
+ tmp = metadata.extractMetadata(MediaMetadataRetriever.METADATA_KEY_BITRATE);
+ bitrate = Integer.parseInt((tmp != null) ? tmp : "0") / 1000;
+ } else {
+ // Otherwise do a calculation for it
+ // Divide by 1000 so in kbps
+ bitrate = (int) (size / duration) / 1000 * 8;
+ }
+
+ if(Util.isOffline(context)) {
+ song.setGenre(metadata.extractMetadata(MediaMetadataRetriever.METADATA_KEY_GENRE));
+ String year = metadata.extractMetadata(MediaMetadataRetriever.METADATA_KEY_YEAR);
+ song.setYear(Integer.parseInt((year != null) ? year : "0"));
+ }
+ }
+ } catch(Exception e) {
+ Log.i(TAG, "Device doesn't properly support MediaMetadataRetreiver");
+ }
+ }
+
+ String msg = "";
+ if(song instanceof PodcastEpisode) {
+ msg += "Podcast: " + song.getArtist() + "\nStatus: " + ((PodcastEpisode)song).getStatus();
+ } else if(!song.isVideo()) {
+ if(song.getArtist() != null && !"".equals(song.getArtist())) {
+ msg += "Artist: " + song.getArtist();
+ }
+ if(song.getAlbum() != null && !"".equals(song.getAlbum())) {
+ msg += "\nAlbum: " + song.getAlbum();
+ }
+ }
+ if(song.getTrack() != null && song.getTrack() != 0) {
+ msg += "\nTrack: " + song.getTrack();
+ }
+ if(song.getGenre() != null && !"".equals(song.getGenre())) {
+ msg += "\nGenre: " + song.getGenre();
+ }
+ if(song.getYear() != null && song.getYear() != 0) {
+ msg += "\nYear: " + song.getYear();
+ }
+ if(!Util.isOffline(context) && song.getSuffix() != null) {
+ msg += "\nServer Format: " + song.getSuffix();
+ if(song.getBitRate() != null && song.getBitRate() != 0) {
+ msg += "\nServer Bitrate: " + song.getBitRate() + " kbps";
+ }
+ }
+ if(format != null && !"".equals(format)) {
+ msg += "\nCached Format: " + format;
+ }
+ if(bitrate != null && bitrate != 0) {
+ msg += "\nCached Bitrate: " + bitrate + " kbps";
+ }
+ if(size != 0) {
+ msg += "\nSize: " + Util.formatLocalizedBytes(size, context);
+ }
+ if(song.getDuration() != null && song.getDuration() != 0) {
+ msg += "\nLength: " + Util.formatDuration(song.getDuration());
+ }
+ if(song.getBookmark() != null) {
+ msg += "\nBookmark Position: " + Util.formatDuration(song.getBookmark().getPosition() / 1000);
+ }
+ if(song.getRating() != 0) {
+ msg += "\nRating: " + song.getRating() + " stars";
+ }
+ if(song instanceof PodcastEpisode) {
+ msg += "\n\nDescription: " + song.getAlbum();
+ }
+
+ Util.info(context, song.getTitle(), msg);
+ }
+
+ protected void playVideo(Entry entry) {
+ if(entryExists(entry)) {
+ playExternalPlayer(entry);
+ } else {
+ streamExternalPlayer(entry);
+ }
+ }
+
+ protected void playWebView(Entry entry) {
+ int maxBitrate = Util.getMaxVideoBitrate(context);
+
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setData(Uri.parse(MusicServiceFactory.getMusicService(context).getVideoUrl(maxBitrate, context, entry.getId())));
+
+ startActivity(intent);
+ }
+ protected void playExternalPlayer(Entry entry) {
+ if(!entryExists(entry)) {
+ Util.toast(context, R.string.download_need_download);
+ } else {
+ DownloadFile check = new DownloadFile(context, entry, false);
+ File file = check.getCompleteFile();
+
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setDataAndType(Uri.fromFile(file), "video/*");
+ intent.putExtra(Intent.EXTRA_TITLE, entry.getTitle());
+
+ List<ResolveInfo> intents = context.getPackageManager()
+ .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
+ if(intents != null && intents.size() > 0) {
+ startActivity(intent);
+ }else {
+ Util.toast(context, R.string.download_no_streaming_player);
+ }
+ }
+ }
+ protected void streamExternalPlayer(Entry entry) {
+ String videoPlayerType = Util.getVideoPlayerType(context);
+ if("flash".equals(videoPlayerType)) {
+ playWebView(entry);
+ } else if("hls".equals(videoPlayerType)) {
+ streamExternalPlayer(entry, "hls");
+ } else if("raw".equals(videoPlayerType)) {
+ streamExternalPlayer(entry, "raw");
+ } else {
+ streamExternalPlayer(entry, entry.getTranscodedSuffix());
+ }
+ }
+ protected void streamExternalPlayer(Entry entry, String format) {
+ try {
+ int maxBitrate = Util.getMaxVideoBitrate(context);
+
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ if("hls".equals(format)) {
+ intent.setDataAndType(Uri.parse(MusicServiceFactory.getMusicService(context).getHlsUrl(entry.getId(), maxBitrate, context)), "application/x-mpegURL");
+ } else {
+ intent.setDataAndType(Uri.parse(MusicServiceFactory.getMusicService(context).getVideoStreamUrl(format, maxBitrate, context, entry.getId())), "video/*");
+ }
+ intent.putExtra("title", entry.getTitle());
+
+ List<ResolveInfo> intents = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
+ if(intents != null && intents.size() > 0) {
+ startActivity(intent);
+ } else {
+ Util.toast(context, R.string.download_no_streaming_player);
+ }
+ } catch(Exception error) {
+ String msg;
+ if (error instanceof OfflineException || error instanceof ServerTooOldException) {
+ msg = error.getMessage();
+ } else {
+ msg = context.getResources().getString(R.string.download_no_streaming_player) + " " + error.getMessage();
+ }
+
+ Util.toast(context, msg, false);
+ }
+ }
+
+ protected boolean entryExists(Entry entry) {
+ DownloadFile check = new DownloadFile(context, entry, false);
+ return check.isCompleteFileAvailable();
+ }
+
+ public void deleteRecursively(Artist artist) {
+ deleteRecursively(FileUtil.getArtistDirectory(context, artist));
+ }
+
+ public void deleteRecursively(Entry album) {
+ deleteRecursively(FileUtil.getAlbumDirectory(context, album));
+
+ }
+ public void deleteRecursively(final File dir) {
+ if(dir == null) {
+ return;
+ }
+
+ new LoadingTask<Void>(context) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ MediaStoreService mediaStore = new MediaStoreService(context);
+ FileUtil.recursiveDelete(dir, mediaStore);
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ if(Util.isOffline(context)) {
+ refresh();
+ } else {
+ UpdateView.triggerUpdate();
+ }
+ }
+ }.execute();
+ }
+
+ public void showAlbumArtist(Entry entry) {
+ SubsonicFragment fragment = new SelectDirectoryFragment();
+ Bundle args = new Bundle();
+ if(Util.isTagBrowsing(context)) {
+ args.putString(Constants.INTENT_EXTRA_NAME_ID, entry.getArtistId());
+ } else {
+ args.putString(Constants.INTENT_EXTRA_NAME_ID, entry.getParent());
+ }
+ args.putString(Constants.INTENT_EXTRA_NAME_NAME, entry.getArtist());
+ args.putBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, true);
+ fragment.setArguments(args);
+
+ replaceFragment(fragment, true);
+ }
+ public void showArtist(Entry entry) {
+ SubsonicFragment fragment = new SelectDirectoryFragment();
+ Bundle args = new Bundle();
+ if(Util.isTagBrowsing(context)) {
+ args.putString(Constants.INTENT_EXTRA_NAME_ID, entry.getArtistId());
+ } else {
+ if(entry.getGrandParent() == null) {
+ args.putString(Constants.INTENT_EXTRA_NAME_CHILD_ID, entry.getParent());
+ } else {
+ args.putString(Constants.INTENT_EXTRA_NAME_ID, entry.getGrandParent());
+ }
+ }
+ args.putString(Constants.INTENT_EXTRA_NAME_NAME, entry.getArtist());
+ args.putBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, true);
+ fragment.setArguments(args);
+
+ replaceFragment(fragment, true);
+ }
+ public void showAlbum(Entry entry) {
+ SubsonicFragment fragment = new SelectDirectoryFragment();
+ Bundle args = new Bundle();
+ if(Util.isTagBrowsing(context)) {
+ args.putString(Constants.INTENT_EXTRA_NAME_ID, entry.getAlbumId());
+ } else {
+ args.putString(Constants.INTENT_EXTRA_NAME_ID, entry.getParent());
+ }
+ args.putString(Constants.INTENT_EXTRA_NAME_NAME, entry.getAlbum());
+ fragment.setArguments(args);
+
+ replaceFragment(fragment, true);
+ }
+
+ public void createShare(final List<Entry> entries) {
+ new LoadingTask<List<Share>>(context, true) {
+ @Override
+ protected List<Share> doInBackground() throws Throwable {
+ List<String> ids = new ArrayList<String>(entries.size());
+ for(Entry entry: entries) {
+ ids.add(entry.getId());
+ }
+
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ return musicService.createShare(ids, null, 0L, context, this);
+ }
+
+ @Override
+ protected void done(final List<Share> shares) {
+ if(shares.size() > 0) {
+ Share share = shares.get(0);
+ shareExternal(share);
+ } else {
+ Util.toast(context, context.getResources().getString(R.string.playlist_error), false);
+ }
+ }
+
+ @Override
+ protected void error(Throwable error) {
+ String msg;
+ if (error instanceof OfflineException || error instanceof ServerTooOldException) {
+ msg = getErrorMessage(error);
+ } else {
+ msg = context.getResources().getString(R.string.playlist_error) + " " + getErrorMessage(error);
+ }
+
+ Util.toast(context, msg, false);
+ }
+ }.execute();
+ }
+ public void shareExternal(Share share) {
+ Intent intent = new Intent(Intent.ACTION_SEND);
+ intent.setType("text/plain");
+ intent.putExtra(Intent.EXTRA_TEXT, share.getUrl());
+ context.startActivity(Intent.createChooser(intent, context.getResources().getString(R.string.share_via)));
+ }
+
+ public GestureDetector getGestureDetector() {
+ return gestureScanner;
+ }
+
+ protected void playBookmark(List<Entry> songs, Entry song) {
+ playBookmark(songs, song, null, null);
+ }
+ protected void playBookmark(final List<Entry> songs, final Entry song, final String playlistName, final String playlistId) {
+ final Integer position = song.getBookmark().getPosition();
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.bookmark_resume_title)
+ .setMessage(getResources().getString(R.string.bookmark_resume, song.getTitle(), Util.formatDuration(position / 1000)))
+ .setPositiveButton(R.string.bookmark_action_resume, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ playNow(songs, song, position);
+ }
+ })
+ .setNegativeButton(R.string.bookmark_action_start_over, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ final Bookmark oldBookmark = song.getBookmark();
+ song.setBookmark(null);
+
+ new SilentBackgroundTask<Void>(context) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ musicService.deleteBookmark(song, context, null);
+
+ return null;
+ }
+
+ @Override
+ protected void error(Throwable error) {
+ song.setBookmark(oldBookmark);
+
+ String msg;
+ if (error instanceof OfflineException || error instanceof ServerTooOldException) {
+ msg = getErrorMessage(error);
+ } else {
+ msg = context.getResources().getString(R.string.bookmark_deleted_error, song.getTitle()) + " " + getErrorMessage(error);
+ }
+
+ Util.toast(context, msg, false);
+ }
+ }.execute();
+
+ playNow(songs, 0);
+ }
+ });
+ AlertDialog dialog = builder.create();
+ dialog.show();
+ }
+
+ protected void playNow(List<Entry> entries) {
+ playNow(entries, null, null);
+ }
+ protected void playNow(List<Entry> entries, String playlistName, String playlistId) {
+ Entry bookmark = null;
+ for(Entry entry: entries) {
+ if(entry.getBookmark() != null) {
+ bookmark = entry;
+ break;
+ }
+ }
+
+ // If no bookmark found, just play from start
+ if(bookmark == null) {
+ playNow(entries, 0, playlistName, playlistId);
+ } else {
+ // If bookmark found, then give user choice to start from there or to start over
+ playBookmark(entries, bookmark, playlistName, playlistId);
+ }
+ }
+ protected void playNow(List<Entry> entries, int position) {
+ playNow(entries, position, null, null);
+ }
+ protected void playNow(List<Entry> entries, int position, String playlistName, String playlistId) {
+ Entry selected = entries.isEmpty() ? null : entries.get(0);
+ playNow(entries, selected, position, playlistName, playlistId);
+ }
+ protected void playNow(List<Entry> entries, Entry song, int position) {
+ playNow(entries, song, position, null, null);
+ }
+ protected void playNow(final List<Entry> entries, final Entry song, final int position, final String playlistName, final String playlistId) {
+ new LoadingTask<Void>(context) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ DownloadService downloadService = getDownloadService();
+ if(downloadService == null) {
+ return null;
+ }
+
+ downloadService.clear();
+ downloadService.download(entries, false, true, true, false, entries.indexOf(song), position);
+ downloadService.setSuggestedPlaylistName(playlistName, playlistId);
+
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ Util.startActivityWithoutTransition(context, DownloadActivity.class);
+ }
+ }.execute();
+ }
+
+ protected void deleteBookmark(final MusicDirectory.Entry entry, final ArrayAdapter adapter) {
+ Util.confirmDialog(context, R.string.bookmark_delete_title, entry.getTitle(), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ final Bookmark oldBookmark = entry.getBookmark();
+ entry.setBookmark(null);
+
+ new LoadingTask<Void>(context, false) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ musicService.deleteBookmark(entry, context, null);
+
+ new EntryInstanceUpdater(entry) {
+ @Override
+ public void update(Entry found) {
+ found.setBookmark(null);
+ }
+ }.execute();
+
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ if (adapter != null) {
+ adapter.remove(entry);
+ adapter.notifyDataSetChanged();
+ }
+ Util.toast(context, context.getResources().getString(R.string.bookmark_deleted, entry.getTitle()));
+ }
+
+ @Override
+ protected void error(Throwable error) {
+ entry.setBookmark(oldBookmark);
+
+ String msg;
+ if (error instanceof OfflineException || error instanceof ServerTooOldException) {
+ msg = getErrorMessage(error);
+ } else {
+ msg = context.getResources().getString(R.string.bookmark_deleted_error, entry.getTitle()) + " " + getErrorMessage(error);
+ }
+
+ Util.toast(context, msg, false);
+ }
+ }.execute();
+ }
+ });
+ }
+
+ protected void setRating(Entry entry) {
+ setRating(entry, null);
+ }
+ protected void setRating(final Entry entry, final OnRatingChange onRatingChange) {
+ View layout = context.getLayoutInflater().inflate(R.layout.rating, null);
+ final RatingBar ratingBar = (RatingBar) layout.findViewById(R.id.rating_bar);
+ ratingBar.setRating((float) entry.getRating());
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(context.getResources().getString(R.string.rating_title, entry.getTitle()))
+ .setView(layout)
+ .setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ int rating = (int) ratingBar.getRating();
+ setRating(entry, rating, onRatingChange);
+ }
+ })
+ .setNegativeButton(R.string.common_cancel, null);
+
+ AlertDialog dialog = builder.create();
+ dialog.show();
+ }
+
+ protected void setRating(Entry entry, int rating) {
+ setRating(entry, rating, null);
+ }
+ protected void setRating(final Entry entry, final int rating, final OnRatingChange onRatingChange) {
+ final int oldRating = entry.getRating();
+ entry.setRating(rating);
+
+ if(onRatingChange != null) {
+ onRatingChange.ratingChange(rating);
+ }
+
+ new SilentBackgroundTask<Void>(context) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ musicService.setRating(entry, rating, context, null);
+
+ new EntryInstanceUpdater(entry) {
+ @Override
+ public void update(Entry found) {
+ found.setRating(rating);
+ }
+ }.execute();
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ Util.toast(context, context.getResources().getString(rating > 0 ? R.string.rating_set_rating : R.string.rating_remove_rating, entry.getTitle()));
+ }
+
+ @Override
+ protected void error(Throwable error) {
+ entry.setRating(oldRating);
+ if(onRatingChange != null) {
+ onRatingChange.ratingChange(oldRating);
+ }
+
+ String msg;
+ if (error instanceof OfflineException || error instanceof ServerTooOldException) {
+ msg = getErrorMessage(error);
+ } else {
+ msg = context.getResources().getString(rating > 0 ? R.string.rating_set_rating_failed : R.string.rating_remove_rating_failed, entry.getTitle()) + " " + getErrorMessage(error);
+ }
+
+ Util.toast(context, msg, false);
+ }
+ }.execute();
+ }
+
+ protected abstract class EntryInstanceUpdater {
+ private Entry entry;
+
+ public EntryInstanceUpdater(Entry entry) {
+ this.entry = entry;
+ }
+
+ public abstract void update(Entry found);
+
+ public void execute() {
+ DownloadService downloadService = getDownloadService();
+ if(downloadService != null && !entry.isDirectory()) {
+ boolean serializeChanges = false;
+ List<DownloadFile> downloadFiles = downloadService.getDownloads();
+ for(DownloadFile file: downloadFiles) {
+ Entry check = file.getSong();
+ if(entry.getId().equals(check.getId())) {
+ update(entry);
+ serializeChanges = true;
+ }
+ }
+
+ if(serializeChanges) {
+ downloadService.serializeQueue();
+ }
+ }
+
+ Entry find = UpdateView.findEntry(entry);
+ if(find != null) {
+ update(find);
+ }
+ }
+ }
+
+ public abstract class OnRatingChange {
+ abstract void ratingChange(int rating);
+ }
+ public abstract class OnStarChange {
+ abstract void starChange(boolean starred);
+ }
+
+ public abstract class RecursiveLoader extends LoadingTask<Boolean> {
+ protected MusicService musicService;
+ protected static final int MAX_SONGS = 500;
+ protected boolean playNowOverride = false;
+ protected List<Entry> songs;
+
+ public RecursiveLoader(Activity context) {
+ super(context);
+ }
+
+ protected void getSongsRecursively(MusicDirectory parent, List<Entry> songs) throws Exception {
+ if (songs.size() > MAX_SONGS) {
+ return;
+ }
+
+ for (Entry song : parent.getChildren(false, true)) {
+ if (!song.isVideo() && song.getRating() != 1) {
+ songs.add(song);
+ }
+ }
+ for (Entry dir : parent.getChildren(true, false)) {
+ if(dir.getRating() == 1) {
+ continue;
+ }
+
+ MusicDirectory musicDirectory;
+ if(Util.isTagBrowsing(context) && !Util.isOffline(context)) {
+ musicDirectory = musicService.getAlbum(dir.getId(), dir.getTitle(), false, context, this);
+ } else {
+ musicDirectory = musicService.getMusicDirectory(dir.getId(), dir.getTitle(), false, context, this);
+ }
+ getSongsRecursively(musicDirectory, songs);
+ }
+ }
+
+ @Override
+ protected void done(Boolean result) {
+ warnIfStorageUnavailable();
+
+ if(playNowOverride) {
+ playNow(songs);
+ return;
+ }
+
+ if(result) {
+ Util.startActivityWithoutTransition(context, DownloadActivity.class);
+ }
+ }
+ }
+}
diff --git a/src/github/daneren2005/dsub/fragments/UserFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/UserFragment.java
index 2fa804cc..00c7c603 100644
--- a/src/github/daneren2005/dsub/fragments/UserFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/UserFragment.java
@@ -1,125 +1,125 @@
-/*
- 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 2014 (C) Scott Jackson
-*/
-
-package github.daneren2005.dsub.fragments;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.support.v4.widget.SwipeRefreshLayout;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.ListView;
-import android.widget.TextView;
-
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.activity.SubsonicActivity;
-import github.daneren2005.dsub.domain.ServerInfo;
-import github.daneren2005.dsub.domain.User;
-import github.daneren2005.dsub.util.Constants;
-import github.daneren2005.dsub.util.ImageLoader;
-import github.daneren2005.dsub.util.UserUtil;
-import github.daneren2005.dsub.adapter.SettingsAdapter;
-
-public class UserFragment extends SubsonicFragment{
- private ListView listView;
- private User user;
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
- rootView = inflater.inflate(R.layout.abstract_list_fragment, container, false);
-
- refreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.refresh_layout);
- refreshLayout.setEnabled(false);
-
- Bundle args = getArguments();
- user = (User) args.getSerializable(Constants.INTENT_EXTRA_NAME_ID);
-
- listView = (ListView)rootView.findViewById(R.id.fragment_list);
- createHeader();
- listView.setAdapter(new SettingsAdapter(context, user.getSettings(), UserUtil.isCurrentAdmin() && ServerInfo.checkServerVersion(context, "1.10")));
-
- setTitle(user.getUsername());
-
- return rootView;
- }
-
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- ((SubsonicActivity) activity).supportInvalidateOptionsMenu();
- }
-
- @Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
- // For some reason this is called before onAttach
- if(!primaryFragment || context == null) {
- return;
- }
-
- if(UserUtil.isCurrentAdmin() && ServerInfo.checkServerVersion(context, "1.10")) {
- menuInflater.inflate(R.menu.user, menu);
- } else if(UserUtil.isCurrentRole(User.SETTINGS)) {
- menuInflater.inflate(R.menu.user_user, menu);
- } else {
- menuInflater.inflate(R.menu.empty, menu);
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if(super.onOptionsItemSelected(item)) {
- return true;
- }
-
- switch (item.getItemId()) {
- case R.id.menu_update_permissions:
- UserUtil.updateSettings(context, user);
- return true;
- case R.id.menu_change_password:
- UserUtil.changePassword(context, user);
- return true;
- case R.id.menu_change_email:
- UserUtil.changeEmail(context, user);
- return true;
- }
-
- return false;
- }
-
- private void createHeader() {
- View header = LayoutInflater.from(context).inflate(R.layout.user_header, listView, false);
-
- final ImageLoader imageLoader = getImageLoader();
- ImageView coverArtView = (ImageView) header.findViewById(R.id.user_avatar);
- imageLoader.loadAvatar(context, coverArtView, user.getUsername());
-
- TextView usernameView = (TextView) header.findViewById(R.id.user_username);
- usernameView.setText(user.getUsername());
-
- final TextView emailView = (TextView) header.findViewById(R.id.user_email);
- if(user.getEmail() != null) {
- emailView.setText(user.getEmail());
- } else {
- emailView.setVisibility(View.GONE);
- }
-
- listView.addHeaderView(header);
- }
-}
+/*
+ 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 2014 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.fragments;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.v4.widget.SwipeRefreshLayout;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.activity.SubsonicActivity;
+import github.daneren2005.dsub.domain.ServerInfo;
+import github.daneren2005.dsub.domain.User;
+import github.daneren2005.dsub.util.Constants;
+import github.daneren2005.dsub.util.ImageLoader;
+import github.daneren2005.dsub.util.UserUtil;
+import github.daneren2005.dsub.adapter.SettingsAdapter;
+
+public class UserFragment extends SubsonicFragment{
+ private ListView listView;
+ private User user;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
+ rootView = inflater.inflate(R.layout.abstract_list_fragment, container, false);
+
+ refreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.refresh_layout);
+ refreshLayout.setEnabled(false);
+
+ Bundle args = getArguments();
+ user = (User) args.getSerializable(Constants.INTENT_EXTRA_NAME_ID);
+
+ listView = (ListView)rootView.findViewById(R.id.fragment_list);
+ createHeader();
+ listView.setAdapter(new SettingsAdapter(context, user.getSettings(), UserUtil.isCurrentAdmin() && ServerInfo.checkServerVersion(context, "1.10")));
+
+ setTitle(user.getUsername());
+
+ return rootView;
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ ((SubsonicActivity) activity).supportInvalidateOptionsMenu();
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
+ // For some reason this is called before onAttach
+ if(!primaryFragment || context == null) {
+ return;
+ }
+
+ if(UserUtil.isCurrentAdmin() && ServerInfo.checkServerVersion(context, "1.10")) {
+ menuInflater.inflate(R.menu.user, menu);
+ } else if(UserUtil.isCurrentRole(User.SETTINGS)) {
+ menuInflater.inflate(R.menu.user_user, menu);
+ } else {
+ menuInflater.inflate(R.menu.empty, menu);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if(super.onOptionsItemSelected(item)) {
+ return true;
+ }
+
+ switch (item.getItemId()) {
+ case R.id.menu_update_permissions:
+ UserUtil.updateSettings(context, user);
+ return true;
+ case R.id.menu_change_password:
+ UserUtil.changePassword(context, user);
+ return true;
+ case R.id.menu_change_email:
+ UserUtil.changeEmail(context, user);
+ return true;
+ }
+
+ return false;
+ }
+
+ private void createHeader() {
+ View header = LayoutInflater.from(context).inflate(R.layout.user_header, listView, false);
+
+ final ImageLoader imageLoader = getImageLoader();
+ ImageView coverArtView = (ImageView) header.findViewById(R.id.user_avatar);
+ imageLoader.loadAvatar(context, coverArtView, user.getUsername());
+
+ TextView usernameView = (TextView) header.findViewById(R.id.user_username);
+ usernameView.setText(user.getUsername());
+
+ final TextView emailView = (TextView) header.findViewById(R.id.user_email);
+ if(user.getEmail() != null) {
+ emailView.setText(user.getEmail());
+ } else {
+ emailView.setVisibility(View.GONE);
+ }
+
+ listView.addHeaderView(header);
+ }
+}
diff --git a/src/github/daneren2005/dsub/provider/DLNARouteProvider.java b/app/src/main/java/github/daneren2005/dsub/provider/DLNARouteProvider.java
index 73d4b5de..73d4b5de 100644
--- a/src/github/daneren2005/dsub/provider/DLNARouteProvider.java
+++ b/app/src/main/java/github/daneren2005/dsub/provider/DLNARouteProvider.java
diff --git a/src/github/daneren2005/dsub/provider/DSubSearchProvider.java b/app/src/main/java/github/daneren2005/dsub/provider/DSubSearchProvider.java
index 63bbaaa4..63bbaaa4 100644
--- a/src/github/daneren2005/dsub/provider/DSubSearchProvider.java
+++ b/app/src/main/java/github/daneren2005/dsub/provider/DSubSearchProvider.java
diff --git a/src/github/daneren2005/dsub/provider/DSubWidget4x1.java b/app/src/main/java/github/daneren2005/dsub/provider/DSubWidget4x1.java
index 9919eb22..c78257c4 100644
--- a/src/github/daneren2005/dsub/provider/DSubWidget4x1.java
+++ b/app/src/main/java/github/daneren2005/dsub/provider/DSubWidget4x1.java
@@ -1,28 +1,28 @@
-/*
- 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 2010 (C) Sindre Mehus
- */
-package github.daneren2005.dsub.provider;
-
-import github.daneren2005.dsub.R;
-
-public class DSubWidget4x1 extends DSubWidgetProvider {
- @Override
- protected int getLayout() {
- return R.layout.appwidget4x1;
- }
-}
+/*
+ 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 2010 (C) Sindre Mehus
+ */
+package github.daneren2005.dsub.provider;
+
+import github.daneren2005.dsub.R;
+
+public class DSubWidget4x1 extends DSubWidgetProvider {
+ @Override
+ protected int getLayout() {
+ return R.layout.appwidget4x1;
+ }
+}
diff --git a/src/github/daneren2005/dsub/provider/DSubWidget4x2.java b/app/src/main/java/github/daneren2005/dsub/provider/DSubWidget4x2.java
index 2aa6beae..4c7d637c 100644
--- a/src/github/daneren2005/dsub/provider/DSubWidget4x2.java
+++ b/app/src/main/java/github/daneren2005/dsub/provider/DSubWidget4x2.java
@@ -1,28 +1,28 @@
-/*
- 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 2010 (C) Sindre Mehus
- */
-package github.daneren2005.dsub.provider;
-
-import github.daneren2005.dsub.R;
-
-public class DSubWidget4x2 extends DSubWidgetProvider {
- @Override
- protected int getLayout() {
- return R.layout.appwidget4x2;
- }
-}
+/*
+ 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 2010 (C) Sindre Mehus
+ */
+package github.daneren2005.dsub.provider;
+
+import github.daneren2005.dsub.R;
+
+public class DSubWidget4x2 extends DSubWidgetProvider {
+ @Override
+ protected int getLayout() {
+ return R.layout.appwidget4x2;
+ }
+}
diff --git a/src/github/daneren2005/dsub/provider/DSubWidget4x3.java b/app/src/main/java/github/daneren2005/dsub/provider/DSubWidget4x3.java
index dfe071c0..b4f91a45 100644
--- a/src/github/daneren2005/dsub/provider/DSubWidget4x3.java
+++ b/app/src/main/java/github/daneren2005/dsub/provider/DSubWidget4x3.java
@@ -1,28 +1,28 @@
-/*
- 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 2010 (C) Sindre Mehus
- */
-package github.daneren2005.dsub.provider;
-
-import github.daneren2005.dsub.R;
-
-public class DSubWidget4x3 extends DSubWidgetProvider {
- @Override
- protected int getLayout() {
- return R.layout.appwidget4x3;
- }
-}
+/*
+ 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 2010 (C) Sindre Mehus
+ */
+package github.daneren2005.dsub.provider;
+
+import github.daneren2005.dsub.R;
+
+public class DSubWidget4x3 extends DSubWidgetProvider {
+ @Override
+ protected int getLayout() {
+ return R.layout.appwidget4x3;
+ }
+}
diff --git a/src/github/daneren2005/dsub/provider/DSubWidget4x4.java b/app/src/main/java/github/daneren2005/dsub/provider/DSubWidget4x4.java
index fa53918d..a591ff29 100644
--- a/src/github/daneren2005/dsub/provider/DSubWidget4x4.java
+++ b/app/src/main/java/github/daneren2005/dsub/provider/DSubWidget4x4.java
@@ -1,28 +1,28 @@
-/*
- 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 2010 (C) Sindre Mehus
- */
-package github.daneren2005.dsub.provider;
-
-import github.daneren2005.dsub.R;
-
-public class DSubWidget4x4 extends DSubWidgetProvider {
- @Override
- protected int getLayout() {
- return R.layout.appwidget4x4;
- }
-}
+/*
+ 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 2010 (C) Sindre Mehus
+ */
+package github.daneren2005.dsub.provider;
+
+import github.daneren2005.dsub.R;
+
+public class DSubWidget4x4 extends DSubWidgetProvider {
+ @Override
+ protected int getLayout() {
+ return R.layout.appwidget4x4;
+ }
+}
diff --git a/src/github/daneren2005/dsub/provider/DSubWidgetProvider.java b/app/src/main/java/github/daneren2005/dsub/provider/DSubWidgetProvider.java
index 444b6cff..444b6cff 100644
--- a/src/github/daneren2005/dsub/provider/DSubWidgetProvider.java
+++ b/app/src/main/java/github/daneren2005/dsub/provider/DSubWidgetProvider.java
diff --git a/src/github/daneren2005/dsub/provider/JukeboxRouteProvider.java b/app/src/main/java/github/daneren2005/dsub/provider/JukeboxRouteProvider.java
index 0d2a5ff5..0d2a5ff5 100644
--- a/src/github/daneren2005/dsub/provider/JukeboxRouteProvider.java
+++ b/app/src/main/java/github/daneren2005/dsub/provider/JukeboxRouteProvider.java
diff --git a/src/github/daneren2005/dsub/provider/MostRecentStubProvider.java b/app/src/main/java/github/daneren2005/dsub/provider/MostRecentStubProvider.java
index 17447cda..80dd93cc 100644
--- a/src/github/daneren2005/dsub/provider/MostRecentStubProvider.java
+++ b/app/src/main/java/github/daneren2005/dsub/provider/MostRecentStubProvider.java
@@ -1,61 +1,61 @@
-/*
- 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 github.daneren2005.dsub.provider;
-
-import android.content.ContentProvider;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.net.Uri;
-
-/**
- * Created by Scott on 8/28/13.
- */
-
-public class MostRecentStubProvider extends ContentProvider {
- @Override
- public boolean onCreate() {
- return true;
- }
-
- @Override
- public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
- return null;
- }
-
- @Override
- public String getType(Uri uri) {
- return "";
- }
-
- @Override
- public Uri insert(Uri uri, ContentValues values) {
- return null;
- }
-
- @Override
- public int delete(Uri uri, String selection, String[] selectionArgs) {
- return 0;
- }
-
- @Override
- public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
- return 0;
- }
-}
+/*
+ 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 github.daneren2005.dsub.provider;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+
+/**
+ * Created by Scott on 8/28/13.
+ */
+
+public class MostRecentStubProvider extends ContentProvider {
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
+ return null;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ return "";
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ return null;
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ return 0;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ return 0;
+ }
+}
diff --git a/src/github/daneren2005/dsub/provider/PlaylistStubProvider.java b/app/src/main/java/github/daneren2005/dsub/provider/PlaylistStubProvider.java
index e817c047..1481557f 100644
--- a/src/github/daneren2005/dsub/provider/PlaylistStubProvider.java
+++ b/app/src/main/java/github/daneren2005/dsub/provider/PlaylistStubProvider.java
@@ -1,61 +1,61 @@
-/*
- 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 github.daneren2005.dsub.provider;
-
-import android.content.ContentProvider;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.net.Uri;
-
-/**
- * Created by Scott on 8/28/13.
- */
-
-public class PlaylistStubProvider extends ContentProvider {
- @Override
- public boolean onCreate() {
- return true;
- }
-
- @Override
- public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
- return null;
- }
-
- @Override
- public String getType(Uri uri) {
- return "";
- }
-
- @Override
- public Uri insert(Uri uri, ContentValues values) {
- return null;
- }
-
- @Override
- public int delete(Uri uri, String selection, String[] selectionArgs) {
- return 0;
- }
-
- @Override
- public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
- return 0;
- }
-}
+/*
+ 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 github.daneren2005.dsub.provider;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+
+/**
+ * Created by Scott on 8/28/13.
+ */
+
+public class PlaylistStubProvider extends ContentProvider {
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
+ return null;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ return "";
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ return null;
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ return 0;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ return 0;
+ }
+}
diff --git a/src/github/daneren2005/dsub/provider/PodcastStubProvider.java b/app/src/main/java/github/daneren2005/dsub/provider/PodcastStubProvider.java
index 1c70e7f4..7d9bfca1 100644
--- a/src/github/daneren2005/dsub/provider/PodcastStubProvider.java
+++ b/app/src/main/java/github/daneren2005/dsub/provider/PodcastStubProvider.java
@@ -1,61 +1,61 @@
-/*
- 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 github.daneren2005.dsub.provider;
-
-import android.content.ContentProvider;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.net.Uri;
-
-/**
- * Created by Scott on 8/28/13.
- */
-
-public class PodcastStubProvider extends ContentProvider {
- @Override
- public boolean onCreate() {
- return true;
- }
-
- @Override
- public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
- return null;
- }
-
- @Override
- public String getType(Uri uri) {
- return "";
- }
-
- @Override
- public Uri insert(Uri uri, ContentValues values) {
- return null;
- }
-
- @Override
- public int delete(Uri uri, String selection, String[] selectionArgs) {
- return 0;
- }
-
- @Override
- public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
- return 0;
- }
-}
+/*
+ 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 github.daneren2005.dsub.provider;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+
+/**
+ * Created by Scott on 8/28/13.
+ */
+
+public class PodcastStubProvider extends ContentProvider {
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
+ return null;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ return "";
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ return null;
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ return 0;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ return 0;
+ }
+}
diff --git a/src/github/daneren2005/dsub/provider/StarredStubProvider.java b/app/src/main/java/github/daneren2005/dsub/provider/StarredStubProvider.java
index 27a84e3f..f638c348 100644
--- a/src/github/daneren2005/dsub/provider/StarredStubProvider.java
+++ b/app/src/main/java/github/daneren2005/dsub/provider/StarredStubProvider.java
@@ -1,61 +1,61 @@
-/*
- 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 github.daneren2005.dsub.provider;
-
-import android.content.ContentProvider;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.net.Uri;
-
-/**
- * Created by Scott on 8/28/13.
- */
-
-public class StarredStubProvider extends ContentProvider {
- @Override
- public boolean onCreate() {
- return true;
- }
-
- @Override
- public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
- return null;
- }
-
- @Override
- public String getType(Uri uri) {
- return "";
- }
-
- @Override
- public Uri insert(Uri uri, ContentValues values) {
- return null;
- }
-
- @Override
- public int delete(Uri uri, String selection, String[] selectionArgs) {
- return 0;
- }
-
- @Override
- public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
- return 0;
- }
-}
+/*
+ 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 github.daneren2005.dsub.provider;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+
+/**
+ * Created by Scott on 8/28/13.
+ */
+
+public class StarredStubProvider extends ContentProvider {
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
+ return null;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ return "";
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ return null;
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ return 0;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ return 0;
+ }
+}
diff --git a/src/github/daneren2005/dsub/receiver/A2dpIntentReceiver.java b/app/src/main/java/github/daneren2005/dsub/receiver/A2dpIntentReceiver.java
index f1837fd7..8f6ab810 100644
--- a/src/github/daneren2005/dsub/receiver/A2dpIntentReceiver.java
+++ b/app/src/main/java/github/daneren2005/dsub/receiver/A2dpIntentReceiver.java
@@ -1,47 +1,47 @@
-package github.daneren2005.dsub.receiver;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.util.Log;
-import github.daneren2005.dsub.service.DownloadService;
-
-public class A2dpIntentReceiver extends BroadcastReceiver {
- private static final String PLAYSTATUS_RESPONSE = "com.android.music.playstatusresponse";
- private String TAG = A2dpIntentReceiver.class.getSimpleName();
-
- @Override
- public void onReceive(Context context, Intent intent) {
- Log.i(TAG, "GOT INTENT " + intent);
-
- DownloadService downloadService = DownloadService.getInstance();
-
- if (downloadService != null){
-
- Intent avrcpIntent = new Intent(PLAYSTATUS_RESPONSE);
-
- avrcpIntent.putExtra("duration", (long) downloadService.getPlayerDuration());
- avrcpIntent.putExtra("position", (long) downloadService.getPlayerPosition());
- avrcpIntent.putExtra("ListSize", (long) downloadService.getSongs().size());
-
- switch (downloadService.getPlayerState()){
- case STARTED:
- avrcpIntent.putExtra("playing", true);
- break;
- case STOPPED:
- avrcpIntent.putExtra("playing", false);
- break;
- case PAUSED:
- avrcpIntent.putExtra("playing", false);
- break;
- case COMPLETED:
- avrcpIntent.putExtra("playing", false);
- break;
- default:
- return;
- }
-
- context.sendBroadcast(avrcpIntent);
- }
- }
+package github.daneren2005.dsub.receiver;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+import github.daneren2005.dsub.service.DownloadService;
+
+public class A2dpIntentReceiver extends BroadcastReceiver {
+ private static final String PLAYSTATUS_RESPONSE = "com.android.music.playstatusresponse";
+ private String TAG = A2dpIntentReceiver.class.getSimpleName();
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.i(TAG, "GOT INTENT " + intent);
+
+ DownloadService downloadService = DownloadService.getInstance();
+
+ if (downloadService != null){
+
+ Intent avrcpIntent = new Intent(PLAYSTATUS_RESPONSE);
+
+ avrcpIntent.putExtra("duration", (long) downloadService.getPlayerDuration());
+ avrcpIntent.putExtra("position", (long) downloadService.getPlayerPosition());
+ avrcpIntent.putExtra("ListSize", (long) downloadService.getSongs().size());
+
+ switch (downloadService.getPlayerState()){
+ case STARTED:
+ avrcpIntent.putExtra("playing", true);
+ break;
+ case STOPPED:
+ avrcpIntent.putExtra("playing", false);
+ break;
+ case PAUSED:
+ avrcpIntent.putExtra("playing", false);
+ break;
+ case COMPLETED:
+ avrcpIntent.putExtra("playing", false);
+ break;
+ default:
+ return;
+ }
+
+ context.sendBroadcast(avrcpIntent);
+ }
+ }
} \ No newline at end of file
diff --git a/src/github/daneren2005/dsub/receiver/AudioNoisyReceiver.java b/app/src/main/java/github/daneren2005/dsub/receiver/AudioNoisyReceiver.java
index b4ace297..cb2a433e 100644
--- a/src/github/daneren2005/dsub/receiver/AudioNoisyReceiver.java
+++ b/app/src/main/java/github/daneren2005/dsub/receiver/AudioNoisyReceiver.java
@@ -1,51 +1,51 @@
-/*
- 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 2014 (C) Scott Jackson
-*/
-
-package github.daneren2005.dsub.receiver;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.media.AudioManager;
-import android.util.Log;
-
-import github.daneren2005.dsub.domain.PlayerState;
-import github.daneren2005.dsub.service.DownloadService;
-import github.daneren2005.dsub.util.Constants;
-import github.daneren2005.dsub.util.Util;
-
-public class AudioNoisyReceiver extends BroadcastReceiver {
- private static final String TAG = AudioNoisyReceiver.class.getSimpleName();
-
- @Override
- public void onReceive(Context context, Intent intent) {
- DownloadService downloadService = DownloadService.getInstance();
- // Don't do anything if downloadService is not started
- if(downloadService == null) {
- return;
- }
-
- if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals (intent.getAction ())) {
- if(!downloadService.isRemoteEnabled() && (downloadService.getPlayerState() == PlayerState.STARTED || downloadService.getPlayerState() == PlayerState.PAUSED_TEMP)) {
- SharedPreferences prefs = Util.getPreferences(downloadService);
- int pausePref = Integer.parseInt(prefs.getString(Constants.PREFERENCES_KEY_PAUSE_DISCONNECT, "0"));
- if(pausePref == 0) {
- downloadService.pause();
- }
- }
- }
- }
-}
+/*
+ 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 2014 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.receiver;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.media.AudioManager;
+import android.util.Log;
+
+import github.daneren2005.dsub.domain.PlayerState;
+import github.daneren2005.dsub.service.DownloadService;
+import github.daneren2005.dsub.util.Constants;
+import github.daneren2005.dsub.util.Util;
+
+public class AudioNoisyReceiver extends BroadcastReceiver {
+ private static final String TAG = AudioNoisyReceiver.class.getSimpleName();
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ DownloadService downloadService = DownloadService.getInstance();
+ // Don't do anything if downloadService is not started
+ if(downloadService == null) {
+ return;
+ }
+
+ if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals (intent.getAction ())) {
+ if(!downloadService.isRemoteEnabled() && (downloadService.getPlayerState() == PlayerState.STARTED || downloadService.getPlayerState() == PlayerState.PAUSED_TEMP)) {
+ SharedPreferences prefs = Util.getPreferences(downloadService);
+ int pausePref = Integer.parseInt(prefs.getString(Constants.PREFERENCES_KEY_PAUSE_DISCONNECT, "0"));
+ if(pausePref == 0) {
+ downloadService.pause();
+ }
+ }
+ }
+ }
+}
diff --git a/src/github/daneren2005/dsub/receiver/BootReceiver.java b/app/src/main/java/github/daneren2005/dsub/receiver/BootReceiver.java
index 634aeeee..634aeeee 100644
--- a/src/github/daneren2005/dsub/receiver/BootReceiver.java
+++ b/app/src/main/java/github/daneren2005/dsub/receiver/BootReceiver.java
diff --git a/src/github/daneren2005/dsub/receiver/HeadphonePlugReceiver.java b/app/src/main/java/github/daneren2005/dsub/receiver/HeadphonePlugReceiver.java
index 77948c41..77948c41 100644
--- a/src/github/daneren2005/dsub/receiver/HeadphonePlugReceiver.java
+++ b/app/src/main/java/github/daneren2005/dsub/receiver/HeadphonePlugReceiver.java
diff --git a/src/github/daneren2005/dsub/receiver/MediaButtonIntentReceiver.java b/app/src/main/java/github/daneren2005/dsub/receiver/MediaButtonIntentReceiver.java
index 8119ef2d..8119ef2d 100644
--- a/src/github/daneren2005/dsub/receiver/MediaButtonIntentReceiver.java
+++ b/app/src/main/java/github/daneren2005/dsub/receiver/MediaButtonIntentReceiver.java
diff --git a/src/github/daneren2005/dsub/receiver/PlayActionReceiver.java b/app/src/main/java/github/daneren2005/dsub/receiver/PlayActionReceiver.java
index 2c04d829..2c04d829 100644
--- a/src/github/daneren2005/dsub/receiver/PlayActionReceiver.java
+++ b/app/src/main/java/github/daneren2005/dsub/receiver/PlayActionReceiver.java
diff --git a/src/github/daneren2005/dsub/service/CachedMusicService.java b/app/src/main/java/github/daneren2005/dsub/service/CachedMusicService.java
index 61d6205a..61d6205a 100644
--- a/src/github/daneren2005/dsub/service/CachedMusicService.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/CachedMusicService.java
diff --git a/src/github/daneren2005/dsub/service/ChromeCastController.java b/app/src/main/java/github/daneren2005/dsub/service/ChromeCastController.java
index a729ed4e..a729ed4e 100644
--- a/src/github/daneren2005/dsub/service/ChromeCastController.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/ChromeCastController.java
diff --git a/src/github/daneren2005/dsub/service/DLNAController.java b/app/src/main/java/github/daneren2005/dsub/service/DLNAController.java
index 036b111e..64abce8a 100644
--- a/src/github/daneren2005/dsub/service/DLNAController.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/DLNAController.java
@@ -1,687 +1,687 @@
-/*
- 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 2014 (C) Scott Jackson
-*/
-
-package github.daneren2005.dsub.service;
-
-import android.content.SharedPreferences;
-import android.os.Looper;
-import android.util.Log;
-
-import org.fourthline.cling.controlpoint.ActionCallback;
-import org.fourthline.cling.controlpoint.ControlPoint;
-import org.fourthline.cling.controlpoint.SubscriptionCallback;
-import org.fourthline.cling.model.action.ActionInvocation;
-import org.fourthline.cling.model.gena.CancelReason;
-import org.fourthline.cling.model.gena.GENASubscription;
-import org.fourthline.cling.model.message.UpnpResponse;
-import org.fourthline.cling.model.meta.Action;
-import org.fourthline.cling.model.meta.Service;
-import org.fourthline.cling.model.meta.StateVariable;
-import org.fourthline.cling.model.state.StateVariableValue;
-import org.fourthline.cling.model.types.ServiceType;
-import org.fourthline.cling.model.types.UnsignedIntegerFourBytes;
-import org.fourthline.cling.support.avtransport.callback.GetPositionInfo;
-import org.fourthline.cling.support.avtransport.callback.Pause;
-import org.fourthline.cling.support.avtransport.callback.Play;
-import org.fourthline.cling.support.avtransport.callback.Seek;
-import org.fourthline.cling.support.avtransport.callback.SetAVTransportURI;
-import org.fourthline.cling.support.avtransport.callback.Stop;
-import org.fourthline.cling.support.avtransport.lastchange.AVTransportLastChangeParser;
-import org.fourthline.cling.support.avtransport.lastchange.AVTransportVariable;
-import org.fourthline.cling.support.contentdirectory.DIDLParser;
-import org.fourthline.cling.support.lastchange.LastChange;
-import org.fourthline.cling.support.model.DIDLContent;
-import org.fourthline.cling.support.model.DIDLObject;
-import org.fourthline.cling.support.model.PositionInfo;
-import org.fourthline.cling.support.model.Res;
-import org.fourthline.cling.support.model.SeekMode;
-import org.fourthline.cling.support.model.item.Item;
-import org.fourthline.cling.support.model.item.MusicTrack;
-import org.fourthline.cling.support.model.item.VideoItem;
-import org.fourthline.cling.support.renderingcontrol.callback.SetVolume;
-import org.seamless.util.MimeType;
-
-import java.io.File;
-import java.net.URI;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Map;
-import java.util.TimeZone;
-import java.util.concurrent.atomic.AtomicLong;
-
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.domain.DLNADevice;
-import github.daneren2005.dsub.domain.MusicDirectory;
-import github.daneren2005.dsub.domain.PlayerState;
-import github.daneren2005.dsub.util.Constants;
-import github.daneren2005.dsub.util.FileUtil;
-import github.daneren2005.dsub.util.Pair;
-import github.daneren2005.dsub.util.Util;
-import github.daneren2005.serverproxy.FileProxy;
-import github.daneren2005.serverproxy.ServerProxy;
-import github.daneren2005.serverproxy.WebProxy;
-
-public class DLNAController extends RemoteController {
- private static final String TAG = DLNAController.class.getSimpleName();
- private static final long SEARCH_UPDATE_INTERVAL_SECONDS = 10L * 60L * 1000L;
- private static final long STATUS_UPDATE_INTERVAL_SECONDS = 3000L;
-
- DLNADevice device;
- ControlPoint controlPoint;
- SubscriptionCallback callback;
- boolean supportsSeek = false;
- boolean supportsSetupNext = false;
-
- private ServerProxy proxy;
- String rootLocation = "";
- boolean error = false;
-
- final AtomicLong lastUpdate = new AtomicLong();
- int currentPosition = 0;
- String currentPlayingURI;
- String nextPlayingURI;
- DownloadFile nextPlaying;
- boolean running = true;
- boolean hasDuration = false;
- Runnable searchDLNA = new Runnable() {
- @Override
- public void run() {
- if(controlPoint == null || !running) {
- return;
- }
-
- controlPoint.search();
- downloadService.postDelayed(searchDLNA, SEARCH_UPDATE_INTERVAL_SECONDS);
- }
- };
-
- public DLNAController(DownloadService downloadService, ControlPoint controlPoint, DLNADevice device) {
- this.downloadService = downloadService;
- this.controlPoint = controlPoint;
- this.device = device;
-
- SharedPreferences prefs = Util.getPreferences(downloadService);
- rootLocation = prefs.getString(Constants.PREFERENCES_KEY_CACHE_LOCATION, null);
- nextSupported = true;
- }
-
- @Override
- public void create(final boolean playing, final int seconds) {
- downloadService.setPlayerState(PlayerState.PREPARING);
-
- callback = new SubscriptionCallback(getTransportService(), 600) {
- @Override
- protected void failed(GENASubscription genaSubscription, UpnpResponse upnpResponse, Exception e, String msg) {
- Log.w(TAG, "Register subscription callback failed: " + msg, e);
- }
-
- @Override
- protected void established(GENASubscription genaSubscription) {
- Action seekAction = genaSubscription.getService().getAction("Seek");
- if(seekAction != null) {
- StateVariable seekMode = genaSubscription.getService().getStateVariable("A_ARG_TYPE_SeekMode");
- for(String allowedValue: seekMode.getTypeDetails().getAllowedValues()) {
- if("REL_TIME".equals(allowedValue)) {
- supportsSeek = true;
- }
- }
- }
- Action setupNextAction = genaSubscription.getService().getAction("SetNextAVTransportURI");
- if(setupNextAction != null) {
- supportsSetupNext = true;
- }
-
- startSong(downloadService.getCurrentPlaying(), playing, seconds);
- downloadService.postDelayed(searchDLNA, SEARCH_UPDATE_INTERVAL_SECONDS);
- }
-
- @Override
- protected void ended(GENASubscription genaSubscription, CancelReason cancelReason, UpnpResponse upnpResponse) {
- Log.i(TAG, "Ended subscription");
- if(cancelReason != null) {
- Log.i(TAG, "Cancel Reason: " + cancelReason.toString());
- }
- if(upnpResponse != null) {
- Log.i(TAG, "Reponse Message: " + upnpResponse.getStatusMessage());
- Log.i(TAG, "Response Details: " + upnpResponse.getResponseDetails());
- }
- }
-
- @Override
- protected void eventReceived(GENASubscription genaSubscription) {
- Map<String, StateVariableValue> m = genaSubscription.getCurrentValues();
- try {
- LastChange lastChange = new LastChange(new AVTransportLastChangeParser(), m.get("LastChange").toString());
- if (lastChange.getEventedValue(0, AVTransportVariable.TransportState.class) == null) {
- return;
- }
-
- switch (lastChange.getEventedValue(0, AVTransportVariable.TransportState.class).getValue()) {
- case PLAYING:
- downloadService.setPlayerState(PlayerState.STARTED);
-
- // Try to setup next playing after playback start has been registered
- if(supportsSetupNext && downloadService.getNextPlayerState() == PlayerState.IDLE) {
- downloadService.setNextPlaying();
- }
- break;
- case PAUSED_PLAYBACK:
- downloadService.setPlayerState(PlayerState.PAUSED);
- break;
- case STOPPED:
- boolean failed = false;
- for(StateVariableValue val: m.values()) {
- if(val.toString().indexOf("TransportStatus val=\"ERROR_OCCURRED\"") != -1) {
- Log.w(TAG, "Failed to load with event: " + val.toString());
- failed = true;
- }
- }
-
- if(failed) {
- failedLoad();
- } else if(downloadService.getPlayerState() == PlayerState.STARTED) {
- // Played until the end
- downloadService.setPlayerState(PlayerState.COMPLETED);
- downloadService.postPlayCleanup();
- downloadService.onSongCompleted();
- } else {
- downloadService.setPlayerState(PlayerState.STOPPED);
- }
- break;
- case TRANSITIONING:
- downloadService.setPlayerState(PlayerState.PREPARING);
- break;
- case NO_MEDIA_PRESENT:
- downloadService.setPlayerState(PlayerState.IDLE);
- break;
- default:
- }
- }
- catch (Exception e) {
- Log.w(TAG, "Failed to parse UPNP event", e);
- failedLoad();
- }
- }
-
- @Override
- protected void eventsMissed(GENASubscription genaSubscription, int i) {
- Log.w(TAG, "Event missed: " + i);
- }
- };
- controlPoint.execute(callback);
- }
-
- @Override
- public void start() {
- if(error) {
- Log.w(TAG, "Attempting to restart song");
- startSong(downloadService.getCurrentPlaying(), true, 0);
- return;
- }
-
- try {
- controlPoint.execute(new Play(getTransportService()) {
- @Override
- public void success(ActionInvocation invocation) {
- lastUpdate.set(System.currentTimeMillis());
- downloadService.setPlayerState(PlayerState.STARTED);
- }
-
- @Override
- public void failure(ActionInvocation actionInvocation, UpnpResponse upnpResponse, String msg) {
- Log.w(TAG, "Failed to start playing: " + msg);
- failedLoad();
- }
- });
- } catch(Exception e) {
- Log.w(TAG, "Failed to start", e);
- }
- }
-
- @Override
- public void stop() {
- try {
- controlPoint.execute(new Pause(getTransportService()) {
- @Override
- public void success(ActionInvocation invocation) {
- int secondsSinceLastUpdate = (int) ((System.currentTimeMillis() - lastUpdate.get()) / 1000L);
- currentPosition += secondsSinceLastUpdate;
-
- downloadService.setPlayerState(PlayerState.PAUSED);
- }
-
- @Override
- public void failure(ActionInvocation actionInvocation, UpnpResponse upnpResponse, String msg) {
- Log.w(TAG, "Failed to pause playing: " + msg);
- }
- });
- } catch(Exception e) {
- Log.w(TAG, "Failed to stop", e);
- }
- }
-
- @Override
- public void shutdown() {
- try {
- controlPoint.execute(new Stop(getTransportService()) {
- @Override
- public void failure(ActionInvocation invocation, org.fourthline.cling.model.message.UpnpResponse operation, String defaultMessage) {
- Log.w(TAG, "Stop failed: " + defaultMessage);
- }
- });
- } catch(Exception e) {
- Log.w(TAG, "Failed to shutdown", e);
- }
-
- if(callback != null) {
- callback.end();
- callback = null;
- }
-
- if(proxy != null) {
- proxy.stop();
- proxy = null;
- }
-
- running = false;
- }
-
- @Override
- public void updatePlaylist() {
- if(downloadService.getCurrentPlaying() == null) {
- startSong(null, false, 0);
- }
- }
-
- @Override
- public void changePosition(int seconds) {
- SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");
- df.setTimeZone(TimeZone.getTimeZone("UTC"));
- controlPoint.execute(new Seek(getTransportService(), SeekMode.REL_TIME, df.format(new Date(seconds * 1000))) {
- @SuppressWarnings("rawtypes")
- @Override
- public void failure(ActionInvocation invocation, UpnpResponse operation, String defaultMessage) {
- Log.w(TAG, "Seek failed: " + defaultMessage);
- }
- });
- }
-
- @Override
- public void changeTrack(int index, DownloadFile song) {
- startSong(song, true, 0);
- }
-
- @Override
- public void changeNextTrack(DownloadFile song) {
- setupNextSong(song);
- }
-
- @Override
- public void setVolume(int volume) {
- if(volume < 0) {
- volume = 0;
- } else if(volume > device.volumeMax) {
- volume = device.volumeMax;
- }
-
- device.volume = volume;
- try {
- controlPoint.execute(new SetVolume(device.renderer.findService(new ServiceType("schemas-upnp-org", "RenderingControl")), volume) {
- @SuppressWarnings("rawtypes")
- @Override
- public void failure(ActionInvocation invocation, UpnpResponse operation, String defaultMessage) {
- Log.w(TAG, "Set volume failed: " + defaultMessage);
- }
- });
- } catch(Exception e) {
- Log.w(TAG, "Failed to set volume");
- }
- }
-
- @Override
- public void updateVolume(boolean up) {
- int increment = device.volumeMax / 10;
- setVolume(device.volume + (up ? increment : -increment));
- }
-
- @Override
- public double getVolume() {
- return device.volume;
- }
-
- @Override
- public int getRemotePosition() {
- if(downloadService.getPlayerState() == PlayerState.STARTED) {
- int secondsSinceLastUpdate = (int) ((System.currentTimeMillis() - lastUpdate.get()) / 1000L);
- return currentPosition + secondsSinceLastUpdate;
- } else {
- return currentPosition;
- }
- }
-
- @Override
- public boolean isSeekable() {
- return supportsSeek && hasDuration;
- }
-
- private void startSong(final DownloadFile currentPlaying, final boolean autoStart, final int position) {
- try {
- controlPoint.execute(new Stop(getTransportService()) {
- @Override
- public void success(ActionInvocation invocation) {
- startSongRemote(currentPlaying, autoStart, position);
- }
-
- @Override
- public void failure(ActionInvocation invocation, org.fourthline.cling.model.message.UpnpResponse operation, String defaultMessage) {
- Log.w(TAG, "Stop failed before startSong: " + defaultMessage);
- startSongRemote(currentPlaying, autoStart, position);
- }
- });
- } catch(Exception e) {
- Log.w(TAG, "Failed to stop before startSong", e);
- startSongRemote(currentPlaying, autoStart, position);
- }
- }
- private void startSongRemote(final DownloadFile currentPlaying, final boolean autoStart, final int position) {
- if(currentPlaying == null) {
- downloadService.setPlayerState(PlayerState.IDLE);
- return;
- }
- error = false;
-
- downloadService.setPlayerState(PlayerState.PREPARING);
-
- try {
- Pair<String, String> songInfo = getSongInfo(currentPlaying);
-
- currentPlayingURI = songInfo.getFirst();
- downloadService.setNextPlayerState(PlayerState.IDLE);
- controlPoint.execute(new SetAVTransportURI(getTransportService(), songInfo.getFirst(), songInfo.getSecond()) {
- @Override
- public void success(ActionInvocation invocation) {
- if(position != 0) {
- changePosition(position);
- }
-
- if (autoStart) {
- start();
- } else {
- downloadService.setPlayerState(PlayerState.PAUSED);
- }
-
- currentPosition = position;
- lastUpdate.set(System.currentTimeMillis());
- getUpdatedStatus();
- }
-
- @Override
- public void failure(ActionInvocation actionInvocation, UpnpResponse upnpResponse, String msg) {
- Log.w(TAG, "Set URI failed: " + msg);
- failedLoad();
- }
- });
- } catch (Exception e) {
- Log.w(TAG, "Failed startSong", e);
- failedLoad();
- }
- }
- private void setupNextSong(final DownloadFile nextPlaying) {
- this.nextPlaying = nextPlaying;
- nextPlayingURI = null;
- if(nextPlaying == null) {
- downloadService.setNextPlayerState(PlayerState.IDLE);
- Log.i(TAG, "Nothing to play next");
- return;
- }
-
- downloadService.setNextPlayerState(PlayerState.PREPARING);
- try {
- Pair<String, String> songInfo = getSongInfo(nextPlaying);
-
- nextPlayingURI = songInfo.getFirst();
- controlPoint.execute(new SetNextAVTransportURI(getTransportService(), songInfo.getFirst(), songInfo.getSecond()) {
- @Override
- public void success(ActionInvocation invocation) {
- downloadService.setNextPlayerState(PlayerState.PREPARED);
- }
-
- @Override
- public void failure(ActionInvocation actionInvocation, UpnpResponse upnpResponse, String msg) {
- Log.w(TAG, "Set next URI failed: " + msg);
- nextPlayingURI = null;
- DLNAController.this.nextPlaying = null;
- downloadService.setNextPlayerState(PlayerState.IDLE);
- }
- });
- } catch (Exception e) {
- Log.w(TAG, "Failed to setup next song", e);
- nextPlayingURI = null;
- this.nextPlaying = null;
- downloadService.setNextPlayerState(PlayerState.IDLE);
- }
- }
-
- Pair<String, String> getSongInfo(final DownloadFile downloadFile) throws Exception {
- MusicDirectory.Entry song = downloadFile.getSong();
-
- // Get url for entry
- MusicService musicService = MusicServiceFactory.getMusicService(downloadService);
- String url;
- // In offline mode or playing offline song
- if(Util.isOffline(downloadService) || song.getId().indexOf(rootLocation) != -1) {
- if(proxy == null) {
- proxy = new FileProxy(downloadService);
- proxy.start();
- }
-
- // Offline song
- if(song.getId().indexOf(rootLocation) != -1) {
- url = proxy.getPublicAddress(song.getId());
- } else {
- // Playing online song in offline mode
- url = proxy.getPublicAddress(downloadFile.getCompleteFile().getPath());
- }
- } else {
- // Check if we want a proxy going still
- if(Util.isCastProxy(downloadService)) {
- if(proxy instanceof FileProxy) {
- proxy.stop();
- proxy = null;
- }
-
- if(proxy == null) {
- proxy = createWebProxy();
- proxy.start();
- }
- } else if(proxy != null) {
- proxy.stop();
- proxy = null;
- }
-
- if(song.isVideo()) {
- url = musicService.getHlsUrl(song.getId(), downloadFile.getBitRate(), downloadService);
- } else {
- url = musicService.getMusicUrl(downloadService, song, downloadFile.getBitRate());
- }
-
- // If proxy is going, it is a WebProxy
- if(proxy != null) {
- url = proxy.getPublicAddress(url);
- }
- }
-
- // Create metadata for entry
- Item track;
- if(song.isVideo()) {
- track = new VideoItem(song.getId(), song.getParent(), song.getTitle(), song.getArtist());
- } else {
- String contentType = null;
- if(song.getTranscodedContentType() != null) {
- contentType = song.getTranscodedContentType();
- } else if(song.getContentType() != null) {
- contentType = song.getContentType();
- }
-
- MimeType mimeType;
- // If we can parse the content type, use it instead of hard coding
- if(contentType != null && contentType.indexOf("/") != -1 && contentType.indexOf("/") != (contentType.length() - 1)) {
- String[] typeParts = contentType.split("/");
- mimeType = new MimeType(typeParts[0], typeParts[1]);
- } else {
- mimeType = new MimeType("audio", "mpeg");
- }
-
- Res res = new Res(mimeType, song.getSize(), url);
-
- if(song.getDuration() != null) {
- SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");
- df.setTimeZone(TimeZone.getTimeZone("UTC"));
- res.setDuration(df.format(new Date(song.getDuration() * 1000)));
- }
-
- MusicTrack musicTrack = new MusicTrack(song.getId(), song.getParent(), song.getTitle(), song.getArtist(), song.getAlbum(), song.getArtist(), res);
- musicTrack.setOriginalTrackNumber(song.getTrack());
-
- if(song.getCoverArt() != null) {
- String coverArt = null;
- if(proxy == null || proxy instanceof WebProxy) {
- coverArt = musicService.getCoverArtUrl(downloadService, song);
-
- // If proxy is going, it is a web proxy
- if(proxy != null) {
- coverArt = proxy.getPublicAddress(coverArt);
- }
- } else {
- File coverArtFile = FileUtil.getAlbumArtFile(downloadService, song);
- if(coverArtFile != null && coverArtFile.exists()) {
- coverArt = proxy.getPublicAddress(coverArtFile.getPath());
- }
- }
-
- if(coverArt != null) {
- DIDLObject.Property.UPNP.ALBUM_ART_URI albumArtUri = new DIDLObject.Property.UPNP.ALBUM_ART_URI(URI.create(coverArt));
- musicTrack.addProperty(albumArtUri);
- }
- }
-
- track = musicTrack;
- }
-
- DIDLParser parser = new DIDLParser();
- DIDLContent didl = new DIDLContent();
- didl.addItem(track);
-
- String metadata = "";
- try {
- metadata = parser.generate(didl);
- } catch(Exception e) {
- Log.w(TAG, "Metadata generation failed", e);
- }
-
- return new Pair<String, String>(url, metadata);
- }
-
- private void failedLoad() {
- downloadService.setPlayerState(PlayerState.STOPPED);
- error = true;
-
- if(Looper.myLooper() != Looper.getMainLooper()) {
- downloadService.post(new Runnable() {
- @Override
- public void run() {
- Util.toast(downloadService, downloadService.getResources().getString(R.string.download_failed_to_load));
- }
- });
- } else {
- Util.toast(downloadService, downloadService.getResources().getString(R.string.download_failed_to_load));
- }
- }
-
- private Service getTransportService() {
- return device.renderer.findService(new ServiceType("schemas-upnp-org", "AVTransport"));
- }
-
- private void getUpdatedStatus() {
- // Don't care if shutdown in the meantime
- if(!running) {
- return;
- }
-
- controlPoint.execute(new GetPositionInfo(getTransportService()) {
- @Override
- public void received(ActionInvocation actionInvocation, PositionInfo positionInfo) {
- // Don't care if shutdown in the meantime
- if(!running) {
- return;
- }
-
- long duration = positionInfo.getTrackDurationSeconds();
- hasDuration = duration > 0;
-
- lastUpdate.set(System.currentTimeMillis());
-
- // Let's get the updated position
- currentPosition = (int) positionInfo.getTrackElapsedSeconds();
-
- if(positionInfo.getTrackURI() != null && positionInfo.getTrackURI().equals(nextPlayingURI) && downloadService.getNextPlayerState() == PlayerState.PREPARED) {
- downloadService.setCurrentPlaying(nextPlaying, true);
- downloadService.setPlayerState(PlayerState.STARTED);
- downloadService.setNextPlaying();
- }
-
- downloadService.postDelayed(new Runnable() {
- @Override
- public void run() {
- getUpdatedStatus();
- }
- }, STATUS_UPDATE_INTERVAL_SECONDS);
- }
-
- @Override
- public void failure(ActionInvocation actionInvocation, UpnpResponse upnpResponse, String s) {
- Log.w(TAG, "Failed to get an update");
-
- downloadService.postDelayed(new Runnable() {
- @Override
- public void run() {
- getUpdatedStatus();
- }
- }, STATUS_UPDATE_INTERVAL_SECONDS);
- }
- });
- }
-
- private abstract class SetNextAVTransportURI extends ActionCallback {
- public SetNextAVTransportURI(Service service, String uri) {
- this(new UnsignedIntegerFourBytes(0), service, uri, null);
- }
-
- public SetNextAVTransportURI(Service service, String uri, String metadata) {
- this(new UnsignedIntegerFourBytes(0), service, uri, metadata);
- }
-
- public SetNextAVTransportURI(UnsignedIntegerFourBytes instanceId, Service service, String uri) {
- this(instanceId, service, uri, null);
- }
-
- public SetNextAVTransportURI(UnsignedIntegerFourBytes instanceId, Service service, String uri, String metadata) {
- super(new ActionInvocation(service.getAction("SetNextAVTransportURI")));
- getActionInvocation().setInput("InstanceID", instanceId);
- getActionInvocation().setInput("NextURI", uri);
- getActionInvocation().setInput("NextURIMetaData", metadata);
- }
- }
-}
+/*
+ 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 2014 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.service;
+
+import android.content.SharedPreferences;
+import android.os.Looper;
+import android.util.Log;
+
+import org.fourthline.cling.controlpoint.ActionCallback;
+import org.fourthline.cling.controlpoint.ControlPoint;
+import org.fourthline.cling.controlpoint.SubscriptionCallback;
+import org.fourthline.cling.model.action.ActionInvocation;
+import org.fourthline.cling.model.gena.CancelReason;
+import org.fourthline.cling.model.gena.GENASubscription;
+import org.fourthline.cling.model.message.UpnpResponse;
+import org.fourthline.cling.model.meta.Action;
+import org.fourthline.cling.model.meta.Service;
+import org.fourthline.cling.model.meta.StateVariable;
+import org.fourthline.cling.model.state.StateVariableValue;
+import org.fourthline.cling.model.types.ServiceType;
+import org.fourthline.cling.model.types.UnsignedIntegerFourBytes;
+import org.fourthline.cling.support.avtransport.callback.GetPositionInfo;
+import org.fourthline.cling.support.avtransport.callback.Pause;
+import org.fourthline.cling.support.avtransport.callback.Play;
+import org.fourthline.cling.support.avtransport.callback.Seek;
+import org.fourthline.cling.support.avtransport.callback.SetAVTransportURI;
+import org.fourthline.cling.support.avtransport.callback.Stop;
+import org.fourthline.cling.support.avtransport.lastchange.AVTransportLastChangeParser;
+import org.fourthline.cling.support.avtransport.lastchange.AVTransportVariable;
+import org.fourthline.cling.support.contentdirectory.DIDLParser;
+import org.fourthline.cling.support.lastchange.LastChange;
+import org.fourthline.cling.support.model.DIDLContent;
+import org.fourthline.cling.support.model.DIDLObject;
+import org.fourthline.cling.support.model.PositionInfo;
+import org.fourthline.cling.support.model.Res;
+import org.fourthline.cling.support.model.SeekMode;
+import org.fourthline.cling.support.model.item.Item;
+import org.fourthline.cling.support.model.item.MusicTrack;
+import org.fourthline.cling.support.model.item.VideoItem;
+import org.fourthline.cling.support.renderingcontrol.callback.SetVolume;
+import org.seamless.util.MimeType;
+
+import java.io.File;
+import java.net.URI;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Map;
+import java.util.TimeZone;
+import java.util.concurrent.atomic.AtomicLong;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.DLNADevice;
+import github.daneren2005.dsub.domain.MusicDirectory;
+import github.daneren2005.dsub.domain.PlayerState;
+import github.daneren2005.dsub.util.Constants;
+import github.daneren2005.dsub.util.FileUtil;
+import github.daneren2005.dsub.util.Pair;
+import github.daneren2005.dsub.util.Util;
+import github.daneren2005.serverproxy.FileProxy;
+import github.daneren2005.serverproxy.ServerProxy;
+import github.daneren2005.serverproxy.WebProxy;
+
+public class DLNAController extends RemoteController {
+ private static final String TAG = DLNAController.class.getSimpleName();
+ private static final long SEARCH_UPDATE_INTERVAL_SECONDS = 10L * 60L * 1000L;
+ private static final long STATUS_UPDATE_INTERVAL_SECONDS = 3000L;
+
+ DLNADevice device;
+ ControlPoint controlPoint;
+ SubscriptionCallback callback;
+ boolean supportsSeek = false;
+ boolean supportsSetupNext = false;
+
+ private ServerProxy proxy;
+ String rootLocation = "";
+ boolean error = false;
+
+ final AtomicLong lastUpdate = new AtomicLong();
+ int currentPosition = 0;
+ String currentPlayingURI;
+ String nextPlayingURI;
+ DownloadFile nextPlaying;
+ boolean running = true;
+ boolean hasDuration = false;
+ Runnable searchDLNA = new Runnable() {
+ @Override
+ public void run() {
+ if(controlPoint == null || !running) {
+ return;
+ }
+
+ controlPoint.search();
+ downloadService.postDelayed(searchDLNA, SEARCH_UPDATE_INTERVAL_SECONDS);
+ }
+ };
+
+ public DLNAController(DownloadService downloadService, ControlPoint controlPoint, DLNADevice device) {
+ this.downloadService = downloadService;
+ this.controlPoint = controlPoint;
+ this.device = device;
+
+ SharedPreferences prefs = Util.getPreferences(downloadService);
+ rootLocation = prefs.getString(Constants.PREFERENCES_KEY_CACHE_LOCATION, null);
+ nextSupported = true;
+ }
+
+ @Override
+ public void create(final boolean playing, final int seconds) {
+ downloadService.setPlayerState(PlayerState.PREPARING);
+
+ callback = new SubscriptionCallback(getTransportService(), 600) {
+ @Override
+ protected void failed(GENASubscription genaSubscription, UpnpResponse upnpResponse, Exception e, String msg) {
+ Log.w(TAG, "Register subscription callback failed: " + msg, e);
+ }
+
+ @Override
+ protected void established(GENASubscription genaSubscription) {
+ Action seekAction = genaSubscription.getService().getAction("Seek");
+ if(seekAction != null) {
+ StateVariable seekMode = genaSubscription.getService().getStateVariable("A_ARG_TYPE_SeekMode");
+ for(String allowedValue: seekMode.getTypeDetails().getAllowedValues()) {
+ if("REL_TIME".equals(allowedValue)) {
+ supportsSeek = true;
+ }
+ }
+ }
+ Action setupNextAction = genaSubscription.getService().getAction("SetNextAVTransportURI");
+ if(setupNextAction != null) {
+ supportsSetupNext = true;
+ }
+
+ startSong(downloadService.getCurrentPlaying(), playing, seconds);
+ downloadService.postDelayed(searchDLNA, SEARCH_UPDATE_INTERVAL_SECONDS);
+ }
+
+ @Override
+ protected void ended(GENASubscription genaSubscription, CancelReason cancelReason, UpnpResponse upnpResponse) {
+ Log.i(TAG, "Ended subscription");
+ if(cancelReason != null) {
+ Log.i(TAG, "Cancel Reason: " + cancelReason.toString());
+ }
+ if(upnpResponse != null) {
+ Log.i(TAG, "Reponse Message: " + upnpResponse.getStatusMessage());
+ Log.i(TAG, "Response Details: " + upnpResponse.getResponseDetails());
+ }
+ }
+
+ @Override
+ protected void eventReceived(GENASubscription genaSubscription) {
+ Map<String, StateVariableValue> m = genaSubscription.getCurrentValues();
+ try {
+ LastChange lastChange = new LastChange(new AVTransportLastChangeParser(), m.get("LastChange").toString());
+ if (lastChange.getEventedValue(0, AVTransportVariable.TransportState.class) == null) {
+ return;
+ }
+
+ switch (lastChange.getEventedValue(0, AVTransportVariable.TransportState.class).getValue()) {
+ case PLAYING:
+ downloadService.setPlayerState(PlayerState.STARTED);
+
+ // Try to setup next playing after playback start has been registered
+ if(supportsSetupNext && downloadService.getNextPlayerState() == PlayerState.IDLE) {
+ downloadService.setNextPlaying();
+ }
+ break;
+ case PAUSED_PLAYBACK:
+ downloadService.setPlayerState(PlayerState.PAUSED);
+ break;
+ case STOPPED:
+ boolean failed = false;
+ for(StateVariableValue val: m.values()) {
+ if(val.toString().indexOf("TransportStatus val=\"ERROR_OCCURRED\"") != -1) {
+ Log.w(TAG, "Failed to load with event: " + val.toString());
+ failed = true;
+ }
+ }
+
+ if(failed) {
+ failedLoad();
+ } else if(downloadService.getPlayerState() == PlayerState.STARTED) {
+ // Played until the end
+ downloadService.setPlayerState(PlayerState.COMPLETED);
+ downloadService.postPlayCleanup();
+ downloadService.onSongCompleted();
+ } else {
+ downloadService.setPlayerState(PlayerState.STOPPED);
+ }
+ break;
+ case TRANSITIONING:
+ downloadService.setPlayerState(PlayerState.PREPARING);
+ break;
+ case NO_MEDIA_PRESENT:
+ downloadService.setPlayerState(PlayerState.IDLE);
+ break;
+ default:
+ }
+ }
+ catch (Exception e) {
+ Log.w(TAG, "Failed to parse UPNP event", e);
+ failedLoad();
+ }
+ }
+
+ @Override
+ protected void eventsMissed(GENASubscription genaSubscription, int i) {
+ Log.w(TAG, "Event missed: " + i);
+ }
+ };
+ controlPoint.execute(callback);
+ }
+
+ @Override
+ public void start() {
+ if(error) {
+ Log.w(TAG, "Attempting to restart song");
+ startSong(downloadService.getCurrentPlaying(), true, 0);
+ return;
+ }
+
+ try {
+ controlPoint.execute(new Play(getTransportService()) {
+ @Override
+ public void success(ActionInvocation invocation) {
+ lastUpdate.set(System.currentTimeMillis());
+ downloadService.setPlayerState(PlayerState.STARTED);
+ }
+
+ @Override
+ public void failure(ActionInvocation actionInvocation, UpnpResponse upnpResponse, String msg) {
+ Log.w(TAG, "Failed to start playing: " + msg);
+ failedLoad();
+ }
+ });
+ } catch(Exception e) {
+ Log.w(TAG, "Failed to start", e);
+ }
+ }
+
+ @Override
+ public void stop() {
+ try {
+ controlPoint.execute(new Pause(getTransportService()) {
+ @Override
+ public void success(ActionInvocation invocation) {
+ int secondsSinceLastUpdate = (int) ((System.currentTimeMillis() - lastUpdate.get()) / 1000L);
+ currentPosition += secondsSinceLastUpdate;
+
+ downloadService.setPlayerState(PlayerState.PAUSED);
+ }
+
+ @Override
+ public void failure(ActionInvocation actionInvocation, UpnpResponse upnpResponse, String msg) {
+ Log.w(TAG, "Failed to pause playing: " + msg);
+ }
+ });
+ } catch(Exception e) {
+ Log.w(TAG, "Failed to stop", e);
+ }
+ }
+
+ @Override
+ public void shutdown() {
+ try {
+ controlPoint.execute(new Stop(getTransportService()) {
+ @Override
+ public void failure(ActionInvocation invocation, org.fourthline.cling.model.message.UpnpResponse operation, String defaultMessage) {
+ Log.w(TAG, "Stop failed: " + defaultMessage);
+ }
+ });
+ } catch(Exception e) {
+ Log.w(TAG, "Failed to shutdown", e);
+ }
+
+ if(callback != null) {
+ callback.end();
+ callback = null;
+ }
+
+ if(proxy != null) {
+ proxy.stop();
+ proxy = null;
+ }
+
+ running = false;
+ }
+
+ @Override
+ public void updatePlaylist() {
+ if(downloadService.getCurrentPlaying() == null) {
+ startSong(null, false, 0);
+ }
+ }
+
+ @Override
+ public void changePosition(int seconds) {
+ SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");
+ df.setTimeZone(TimeZone.getTimeZone("UTC"));
+ controlPoint.execute(new Seek(getTransportService(), SeekMode.REL_TIME, df.format(new Date(seconds * 1000))) {
+ @SuppressWarnings("rawtypes")
+ @Override
+ public void failure(ActionInvocation invocation, UpnpResponse operation, String defaultMessage) {
+ Log.w(TAG, "Seek failed: " + defaultMessage);
+ }
+ });
+ }
+
+ @Override
+ public void changeTrack(int index, DownloadFile song) {
+ startSong(song, true, 0);
+ }
+
+ @Override
+ public void changeNextTrack(DownloadFile song) {
+ setupNextSong(song);
+ }
+
+ @Override
+ public void setVolume(int volume) {
+ if(volume < 0) {
+ volume = 0;
+ } else if(volume > device.volumeMax) {
+ volume = device.volumeMax;
+ }
+
+ device.volume = volume;
+ try {
+ controlPoint.execute(new SetVolume(device.renderer.findService(new ServiceType("schemas-upnp-org", "RenderingControl")), volume) {
+ @SuppressWarnings("rawtypes")
+ @Override
+ public void failure(ActionInvocation invocation, UpnpResponse operation, String defaultMessage) {
+ Log.w(TAG, "Set volume failed: " + defaultMessage);
+ }
+ });
+ } catch(Exception e) {
+ Log.w(TAG, "Failed to set volume");
+ }
+ }
+
+ @Override
+ public void updateVolume(boolean up) {
+ int increment = device.volumeMax / 10;
+ setVolume(device.volume + (up ? increment : -increment));
+ }
+
+ @Override
+ public double getVolume() {
+ return device.volume;
+ }
+
+ @Override
+ public int getRemotePosition() {
+ if(downloadService.getPlayerState() == PlayerState.STARTED) {
+ int secondsSinceLastUpdate = (int) ((System.currentTimeMillis() - lastUpdate.get()) / 1000L);
+ return currentPosition + secondsSinceLastUpdate;
+ } else {
+ return currentPosition;
+ }
+ }
+
+ @Override
+ public boolean isSeekable() {
+ return supportsSeek && hasDuration;
+ }
+
+ private void startSong(final DownloadFile currentPlaying, final boolean autoStart, final int position) {
+ try {
+ controlPoint.execute(new Stop(getTransportService()) {
+ @Override
+ public void success(ActionInvocation invocation) {
+ startSongRemote(currentPlaying, autoStart, position);
+ }
+
+ @Override
+ public void failure(ActionInvocation invocation, org.fourthline.cling.model.message.UpnpResponse operation, String defaultMessage) {
+ Log.w(TAG, "Stop failed before startSong: " + defaultMessage);
+ startSongRemote(currentPlaying, autoStart, position);
+ }
+ });
+ } catch(Exception e) {
+ Log.w(TAG, "Failed to stop before startSong", e);
+ startSongRemote(currentPlaying, autoStart, position);
+ }
+ }
+ private void startSongRemote(final DownloadFile currentPlaying, final boolean autoStart, final int position) {
+ if(currentPlaying == null) {
+ downloadService.setPlayerState(PlayerState.IDLE);
+ return;
+ }
+ error = false;
+
+ downloadService.setPlayerState(PlayerState.PREPARING);
+
+ try {
+ Pair<String, String> songInfo = getSongInfo(currentPlaying);
+
+ currentPlayingURI = songInfo.getFirst();
+ downloadService.setNextPlayerState(PlayerState.IDLE);
+ controlPoint.execute(new SetAVTransportURI(getTransportService(), songInfo.getFirst(), songInfo.getSecond()) {
+ @Override
+ public void success(ActionInvocation invocation) {
+ if(position != 0) {
+ changePosition(position);
+ }
+
+ if (autoStart) {
+ start();
+ } else {
+ downloadService.setPlayerState(PlayerState.PAUSED);
+ }
+
+ currentPosition = position;
+ lastUpdate.set(System.currentTimeMillis());
+ getUpdatedStatus();
+ }
+
+ @Override
+ public void failure(ActionInvocation actionInvocation, UpnpResponse upnpResponse, String msg) {
+ Log.w(TAG, "Set URI failed: " + msg);
+ failedLoad();
+ }
+ });
+ } catch (Exception e) {
+ Log.w(TAG, "Failed startSong", e);
+ failedLoad();
+ }
+ }
+ private void setupNextSong(final DownloadFile nextPlaying) {
+ this.nextPlaying = nextPlaying;
+ nextPlayingURI = null;
+ if(nextPlaying == null) {
+ downloadService.setNextPlayerState(PlayerState.IDLE);
+ Log.i(TAG, "Nothing to play next");
+ return;
+ }
+
+ downloadService.setNextPlayerState(PlayerState.PREPARING);
+ try {
+ Pair<String, String> songInfo = getSongInfo(nextPlaying);
+
+ nextPlayingURI = songInfo.getFirst();
+ controlPoint.execute(new SetNextAVTransportURI(getTransportService(), songInfo.getFirst(), songInfo.getSecond()) {
+ @Override
+ public void success(ActionInvocation invocation) {
+ downloadService.setNextPlayerState(PlayerState.PREPARED);
+ }
+
+ @Override
+ public void failure(ActionInvocation actionInvocation, UpnpResponse upnpResponse, String msg) {
+ Log.w(TAG, "Set next URI failed: " + msg);
+ nextPlayingURI = null;
+ DLNAController.this.nextPlaying = null;
+ downloadService.setNextPlayerState(PlayerState.IDLE);
+ }
+ });
+ } catch (Exception e) {
+ Log.w(TAG, "Failed to setup next song", e);
+ nextPlayingURI = null;
+ this.nextPlaying = null;
+ downloadService.setNextPlayerState(PlayerState.IDLE);
+ }
+ }
+
+ Pair<String, String> getSongInfo(final DownloadFile downloadFile) throws Exception {
+ MusicDirectory.Entry song = downloadFile.getSong();
+
+ // Get url for entry
+ MusicService musicService = MusicServiceFactory.getMusicService(downloadService);
+ String url;
+ // In offline mode or playing offline song
+ if(Util.isOffline(downloadService) || song.getId().indexOf(rootLocation) != -1) {
+ if(proxy == null) {
+ proxy = new FileProxy(downloadService);
+ proxy.start();
+ }
+
+ // Offline song
+ if(song.getId().indexOf(rootLocation) != -1) {
+ url = proxy.getPublicAddress(song.getId());
+ } else {
+ // Playing online song in offline mode
+ url = proxy.getPublicAddress(downloadFile.getCompleteFile().getPath());
+ }
+ } else {
+ // Check if we want a proxy going still
+ if(Util.isCastProxy(downloadService)) {
+ if(proxy instanceof FileProxy) {
+ proxy.stop();
+ proxy = null;
+ }
+
+ if(proxy == null) {
+ proxy = createWebProxy();
+ proxy.start();
+ }
+ } else if(proxy != null) {
+ proxy.stop();
+ proxy = null;
+ }
+
+ if(song.isVideo()) {
+ url = musicService.getHlsUrl(song.getId(), downloadFile.getBitRate(), downloadService);
+ } else {
+ url = musicService.getMusicUrl(downloadService, song, downloadFile.getBitRate());
+ }
+
+ // If proxy is going, it is a WebProxy
+ if(proxy != null) {
+ url = proxy.getPublicAddress(url);
+ }
+ }
+
+ // Create metadata for entry
+ Item track;
+ if(song.isVideo()) {
+ track = new VideoItem(song.getId(), song.getParent(), song.getTitle(), song.getArtist());
+ } else {
+ String contentType = null;
+ if(song.getTranscodedContentType() != null) {
+ contentType = song.getTranscodedContentType();
+ } else if(song.getContentType() != null) {
+ contentType = song.getContentType();
+ }
+
+ MimeType mimeType;
+ // If we can parse the content type, use it instead of hard coding
+ if(contentType != null && contentType.indexOf("/") != -1 && contentType.indexOf("/") != (contentType.length() - 1)) {
+ String[] typeParts = contentType.split("/");
+ mimeType = new MimeType(typeParts[0], typeParts[1]);
+ } else {
+ mimeType = new MimeType("audio", "mpeg");
+ }
+
+ Res res = new Res(mimeType, song.getSize(), url);
+
+ if(song.getDuration() != null) {
+ SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");
+ df.setTimeZone(TimeZone.getTimeZone("UTC"));
+ res.setDuration(df.format(new Date(song.getDuration() * 1000)));
+ }
+
+ MusicTrack musicTrack = new MusicTrack(song.getId(), song.getParent(), song.getTitle(), song.getArtist(), song.getAlbum(), song.getArtist(), res);
+ musicTrack.setOriginalTrackNumber(song.getTrack());
+
+ if(song.getCoverArt() != null) {
+ String coverArt = null;
+ if(proxy == null || proxy instanceof WebProxy) {
+ coverArt = musicService.getCoverArtUrl(downloadService, song);
+
+ // If proxy is going, it is a web proxy
+ if(proxy != null) {
+ coverArt = proxy.getPublicAddress(coverArt);
+ }
+ } else {
+ File coverArtFile = FileUtil.getAlbumArtFile(downloadService, song);
+ if(coverArtFile != null && coverArtFile.exists()) {
+ coverArt = proxy.getPublicAddress(coverArtFile.getPath());
+ }
+ }
+
+ if(coverArt != null) {
+ DIDLObject.Property.UPNP.ALBUM_ART_URI albumArtUri = new DIDLObject.Property.UPNP.ALBUM_ART_URI(URI.create(coverArt));
+ musicTrack.addProperty(albumArtUri);
+ }
+ }
+
+ track = musicTrack;
+ }
+
+ DIDLParser parser = new DIDLParser();
+ DIDLContent didl = new DIDLContent();
+ didl.addItem(track);
+
+ String metadata = "";
+ try {
+ metadata = parser.generate(didl);
+ } catch(Exception e) {
+ Log.w(TAG, "Metadata generation failed", e);
+ }
+
+ return new Pair<String, String>(url, metadata);
+ }
+
+ private void failedLoad() {
+ downloadService.setPlayerState(PlayerState.STOPPED);
+ error = true;
+
+ if(Looper.myLooper() != Looper.getMainLooper()) {
+ downloadService.post(new Runnable() {
+ @Override
+ public void run() {
+ Util.toast(downloadService, downloadService.getResources().getString(R.string.download_failed_to_load));
+ }
+ });
+ } else {
+ Util.toast(downloadService, downloadService.getResources().getString(R.string.download_failed_to_load));
+ }
+ }
+
+ private Service getTransportService() {
+ return device.renderer.findService(new ServiceType("schemas-upnp-org", "AVTransport"));
+ }
+
+ private void getUpdatedStatus() {
+ // Don't care if shutdown in the meantime
+ if(!running) {
+ return;
+ }
+
+ controlPoint.execute(new GetPositionInfo(getTransportService()) {
+ @Override
+ public void received(ActionInvocation actionInvocation, PositionInfo positionInfo) {
+ // Don't care if shutdown in the meantime
+ if(!running) {
+ return;
+ }
+
+ long duration = positionInfo.getTrackDurationSeconds();
+ hasDuration = duration > 0;
+
+ lastUpdate.set(System.currentTimeMillis());
+
+ // Let's get the updated position
+ currentPosition = (int) positionInfo.getTrackElapsedSeconds();
+
+ if(positionInfo.getTrackURI() != null && positionInfo.getTrackURI().equals(nextPlayingURI) && downloadService.getNextPlayerState() == PlayerState.PREPARED) {
+ downloadService.setCurrentPlaying(nextPlaying, true);
+ downloadService.setPlayerState(PlayerState.STARTED);
+ downloadService.setNextPlaying();
+ }
+
+ downloadService.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ getUpdatedStatus();
+ }
+ }, STATUS_UPDATE_INTERVAL_SECONDS);
+ }
+
+ @Override
+ public void failure(ActionInvocation actionInvocation, UpnpResponse upnpResponse, String s) {
+ Log.w(TAG, "Failed to get an update");
+
+ downloadService.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ getUpdatedStatus();
+ }
+ }, STATUS_UPDATE_INTERVAL_SECONDS);
+ }
+ });
+ }
+
+ private abstract class SetNextAVTransportURI extends ActionCallback {
+ public SetNextAVTransportURI(Service service, String uri) {
+ this(new UnsignedIntegerFourBytes(0), service, uri, null);
+ }
+
+ public SetNextAVTransportURI(Service service, String uri, String metadata) {
+ this(new UnsignedIntegerFourBytes(0), service, uri, metadata);
+ }
+
+ public SetNextAVTransportURI(UnsignedIntegerFourBytes instanceId, Service service, String uri) {
+ this(instanceId, service, uri, null);
+ }
+
+ public SetNextAVTransportURI(UnsignedIntegerFourBytes instanceId, Service service, String uri, String metadata) {
+ super(new ActionInvocation(service.getAction("SetNextAVTransportURI")));
+ getActionInvocation().setInput("InstanceID", instanceId);
+ getActionInvocation().setInput("NextURI", uri);
+ getActionInvocation().setInput("NextURIMetaData", metadata);
+ }
+ }
+}
diff --git a/src/github/daneren2005/dsub/service/DownloadFile.java b/app/src/main/java/github/daneren2005/dsub/service/DownloadFile.java
index 505e4a6d..505e4a6d 100644
--- a/src/github/daneren2005/dsub/service/DownloadFile.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/DownloadFile.java
diff --git a/src/github/daneren2005/dsub/service/DownloadService.java b/app/src/main/java/github/daneren2005/dsub/service/DownloadService.java
index 3f1c022c..3f1c022c 100644
--- a/src/github/daneren2005/dsub/service/DownloadService.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/DownloadService.java
diff --git a/src/github/daneren2005/dsub/service/DownloadServiceLifecycleSupport.java b/app/src/main/java/github/daneren2005/dsub/service/DownloadServiceLifecycleSupport.java
index c9f92f41..c9f92f41 100644
--- a/src/github/daneren2005/dsub/service/DownloadServiceLifecycleSupport.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/DownloadServiceLifecycleSupport.java
diff --git a/src/github/daneren2005/dsub/service/HeadphoneListenerService.java b/app/src/main/java/github/daneren2005/dsub/service/HeadphoneListenerService.java
index f4375c50..f4375c50 100644
--- a/src/github/daneren2005/dsub/service/HeadphoneListenerService.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/HeadphoneListenerService.java
diff --git a/src/github/daneren2005/dsub/service/JukeboxController.java b/app/src/main/java/github/daneren2005/dsub/service/JukeboxController.java
index e9d7cbc8..e9d7cbc8 100644
--- a/src/github/daneren2005/dsub/service/JukeboxController.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/JukeboxController.java
diff --git a/src/github/daneren2005/dsub/service/MediaStoreService.java b/app/src/main/java/github/daneren2005/dsub/service/MediaStoreService.java
index 0aa3269f..0aa3269f 100644
--- a/src/github/daneren2005/dsub/service/MediaStoreService.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/MediaStoreService.java
diff --git a/src/github/daneren2005/dsub/service/MusicService.java b/app/src/main/java/github/daneren2005/dsub/service/MusicService.java
index 4d014462..4d014462 100644
--- a/src/github/daneren2005/dsub/service/MusicService.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/MusicService.java
diff --git a/src/github/daneren2005/dsub/service/MusicServiceFactory.java b/app/src/main/java/github/daneren2005/dsub/service/MusicServiceFactory.java
index e04522ff..e04522ff 100644
--- a/src/github/daneren2005/dsub/service/MusicServiceFactory.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/MusicServiceFactory.java
diff --git a/src/github/daneren2005/dsub/service/OfflineException.java b/app/src/main/java/github/daneren2005/dsub/service/OfflineException.java
index e3a8d460..e3a8d460 100644
--- a/src/github/daneren2005/dsub/service/OfflineException.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/OfflineException.java
diff --git a/src/github/daneren2005/dsub/service/OfflineMusicService.java b/app/src/main/java/github/daneren2005/dsub/service/OfflineMusicService.java
index b4105d07..b4105d07 100644
--- a/src/github/daneren2005/dsub/service/OfflineMusicService.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/OfflineMusicService.java
diff --git a/src/github/daneren2005/dsub/service/RESTMusicService.java b/app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java
index 459c8c9e..459c8c9e 100644
--- a/src/github/daneren2005/dsub/service/RESTMusicService.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java
diff --git a/src/github/daneren2005/dsub/service/RemoteController.java b/app/src/main/java/github/daneren2005/dsub/service/RemoteController.java
index cac28c09..cac28c09 100644
--- a/src/github/daneren2005/dsub/service/RemoteController.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/RemoteController.java
diff --git a/src/github/daneren2005/dsub/service/Scrobbler.java b/app/src/main/java/github/daneren2005/dsub/service/Scrobbler.java
index 1f8538c9..1f8538c9 100644
--- a/src/github/daneren2005/dsub/service/Scrobbler.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/Scrobbler.java
diff --git a/src/github/daneren2005/dsub/service/ServerTooOldException.java b/app/src/main/java/github/daneren2005/dsub/service/ServerTooOldException.java
index e4a951de..e4a951de 100644
--- a/src/github/daneren2005/dsub/service/ServerTooOldException.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/ServerTooOldException.java
diff --git a/src/github/daneren2005/dsub/service/parser/AbstractParser.java b/app/src/main/java/github/daneren2005/dsub/service/parser/AbstractParser.java
index bc5d2199..bc5d2199 100644
--- a/src/github/daneren2005/dsub/service/parser/AbstractParser.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/parser/AbstractParser.java
diff --git a/src/github/daneren2005/dsub/service/parser/AlbumListParser.java b/app/src/main/java/github/daneren2005/dsub/service/parser/AlbumListParser.java
index 773c241b..773c241b 100644
--- a/src/github/daneren2005/dsub/service/parser/AlbumListParser.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/parser/AlbumListParser.java
diff --git a/src/github/daneren2005/dsub/service/parser/ArtistInfoParser.java b/app/src/main/java/github/daneren2005/dsub/service/parser/ArtistInfoParser.java
index 5c3d2412..5c3d2412 100644
--- a/src/github/daneren2005/dsub/service/parser/ArtistInfoParser.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/parser/ArtistInfoParser.java
diff --git a/src/github/daneren2005/dsub/service/parser/BookmarkParser.java b/app/src/main/java/github/daneren2005/dsub/service/parser/BookmarkParser.java
index 8e04749c..8e04749c 100644
--- a/src/github/daneren2005/dsub/service/parser/BookmarkParser.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/parser/BookmarkParser.java
diff --git a/src/github/daneren2005/dsub/service/parser/ChatMessageParser.java b/app/src/main/java/github/daneren2005/dsub/service/parser/ChatMessageParser.java
index 2692d42b..36835fce 100644
--- a/src/github/daneren2005/dsub/service/parser/ChatMessageParser.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/parser/ChatMessageParser.java
@@ -1,65 +1,65 @@
-/*
- 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 github.daneren2005.dsub.service.parser;
-
-import android.content.Context;
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.domain.ChatMessage;
-import github.daneren2005.dsub.util.ProgressListener;
-import org.xmlpull.v1.XmlPullParser;
-
-import java.io.Reader;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * @author Joshua Bahnsen
- */
-public class ChatMessageParser extends AbstractParser {
-
- public ChatMessageParser(Context context, int instance) {
- super(context, instance);
- }
-
- public List<ChatMessage> parse(Reader reader, ProgressListener progressListener) throws Exception {
- init(reader);
- List<ChatMessage> result = new ArrayList<ChatMessage>();
- int eventType;
-
- do {
- eventType = nextParseEvent();
- if (eventType == XmlPullParser.START_TAG) {
- String name = getElementName();
- if ("chatMessage".equals(name)) {
- ChatMessage chatMessage = new ChatMessage();
- chatMessage.setUsername(get("username"));
- chatMessage.setTime(getLong("time"));
- chatMessage.setMessage(get("message"));
- result.add(chatMessage);
- } else if ("error".equals(name)) {
- handleError();
- }
- }
- } while (eventType != XmlPullParser.END_DOCUMENT);
-
- validate();
-
- return result;
- }
-}
+/*
+ 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 github.daneren2005.dsub.service.parser;
+
+import android.content.Context;
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.ChatMessage;
+import github.daneren2005.dsub.util.ProgressListener;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Joshua Bahnsen
+ */
+public class ChatMessageParser extends AbstractParser {
+
+ public ChatMessageParser(Context context, int instance) {
+ super(context, instance);
+ }
+
+ public List<ChatMessage> parse(Reader reader, ProgressListener progressListener) throws Exception {
+ init(reader);
+ List<ChatMessage> result = new ArrayList<ChatMessage>();
+ int eventType;
+
+ do {
+ eventType = nextParseEvent();
+ if (eventType == XmlPullParser.START_TAG) {
+ String name = getElementName();
+ if ("chatMessage".equals(name)) {
+ ChatMessage chatMessage = new ChatMessage();
+ chatMessage.setUsername(get("username"));
+ chatMessage.setTime(getLong("time"));
+ chatMessage.setMessage(get("message"));
+ result.add(chatMessage);
+ } else if ("error".equals(name)) {
+ handleError();
+ }
+ }
+ } while (eventType != XmlPullParser.END_DOCUMENT);
+
+ validate();
+
+ return result;
+ }
+}
diff --git a/src/github/daneren2005/dsub/service/parser/ErrorParser.java b/app/src/main/java/github/daneren2005/dsub/service/parser/ErrorParser.java
index afb05928..afb05928 100644
--- a/src/github/daneren2005/dsub/service/parser/ErrorParser.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/parser/ErrorParser.java
diff --git a/src/github/daneren2005/dsub/service/parser/GenreParser.java b/app/src/main/java/github/daneren2005/dsub/service/parser/GenreParser.java
index a9860f84..ddb03544 100644
--- a/src/github/daneren2005/dsub/service/parser/GenreParser.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/parser/GenreParser.java
@@ -1,122 +1,122 @@
-/*
- 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 2010 (C) Sindre Mehus
- */
-package github.daneren2005.dsub.service.parser;
-
-import android.content.Context;
-import android.text.Html;
-import android.util.Log;
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.domain.Genre;
-import github.daneren2005.dsub.util.ProgressListener;
-
-import org.xmlpull.v1.XmlPullParser;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.Reader;
-import java.io.StringReader;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * @author Joshua Bahnsen
- */
-public class GenreParser extends AbstractParser {
- private static final String TAG = GenreParser.class.getSimpleName();
-
- public GenreParser(Context context, int instance) {
- super(context, instance);
- }
-
- public List<Genre> parse(Reader reader, ProgressListener progressListener) throws Exception {
- List<Genre> result = new ArrayList<Genre>();
- StringReader sr = null;
-
- try {
- BufferedReader br = new BufferedReader(reader);
- String xml = null;
- String line = null;
-
- while ((line = br.readLine()) != null) {
- if (xml == null) {
- xml = line;
- } else {
- xml += line;
- }
- }
- br.close();
-
- // Replace double escaped ampersand (&amp;apos;)
- xml = xml.replaceAll("(?:&amp;)(amp;|lt;|gt;|#37;|apos;)", "&$1");
-
- // Replace unescaped ampersand
- xml = xml.replaceAll("&(?!amp;|lt;|gt;|#37;|apos;)", "&amp;");
-
- // Replace unescaped percent symbol
- // No replacements for <> at this time
- xml = xml.replaceAll("%", "&#37;");
-
- xml = xml.replaceAll("'", "&apos;");
-
- sr = new StringReader(xml);
- } catch (IOException ioe) {
- Log.e(TAG, "Error parsing Genre XML", ioe);
- }
-
- if (sr == null) {
- Log.w(TAG, "Unable to parse Genre XML, returning empty list");
- return result;
- }
-
- init(sr);
-
- Genre genre = null;
-
- int eventType;
- do {
- eventType = nextParseEvent();
- if (eventType == XmlPullParser.START_TAG) {
- String name = getElementName();
- if ("genre".equals(name)) {
- genre = new Genre();
- genre.setSongCount(getInteger("songCount"));
- genre.setAlbumCount(getInteger("albumCount"));
- } else if ("error".equals(name)) {
- handleError();
- } else {
- genre = null;
- }
- } else if (eventType == XmlPullParser.TEXT) {
- if (genre != null) {
- String value = getText();
- if (genre != null) {
- genre.setName(Html.fromHtml(value).toString());
- genre.setIndex(value.substring(0, 1));
- result.add(genre);
- genre = null;
- }
- }
- }
- } while (eventType != XmlPullParser.END_DOCUMENT);
-
- validate();
-
- return Genre.GenreComparator.sort(result);
- }
-}
+/*
+ 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 2010 (C) Sindre Mehus
+ */
+package github.daneren2005.dsub.service.parser;
+
+import android.content.Context;
+import android.text.Html;
+import android.util.Log;
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.Genre;
+import github.daneren2005.dsub.util.ProgressListener;
+
+import org.xmlpull.v1.XmlPullParser;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Joshua Bahnsen
+ */
+public class GenreParser extends AbstractParser {
+ private static final String TAG = GenreParser.class.getSimpleName();
+
+ public GenreParser(Context context, int instance) {
+ super(context, instance);
+ }
+
+ public List<Genre> parse(Reader reader, ProgressListener progressListener) throws Exception {
+ List<Genre> result = new ArrayList<Genre>();
+ StringReader sr = null;
+
+ try {
+ BufferedReader br = new BufferedReader(reader);
+ String xml = null;
+ String line = null;
+
+ while ((line = br.readLine()) != null) {
+ if (xml == null) {
+ xml = line;
+ } else {
+ xml += line;
+ }
+ }
+ br.close();
+
+ // Replace double escaped ampersand (&amp;apos;)
+ xml = xml.replaceAll("(?:&amp;)(amp;|lt;|gt;|#37;|apos;)", "&$1");
+
+ // Replace unescaped ampersand
+ xml = xml.replaceAll("&(?!amp;|lt;|gt;|#37;|apos;)", "&amp;");
+
+ // Replace unescaped percent symbol
+ // No replacements for <> at this time
+ xml = xml.replaceAll("%", "&#37;");
+
+ xml = xml.replaceAll("'", "&apos;");
+
+ sr = new StringReader(xml);
+ } catch (IOException ioe) {
+ Log.e(TAG, "Error parsing Genre XML", ioe);
+ }
+
+ if (sr == null) {
+ Log.w(TAG, "Unable to parse Genre XML, returning empty list");
+ return result;
+ }
+
+ init(sr);
+
+ Genre genre = null;
+
+ int eventType;
+ do {
+ eventType = nextParseEvent();
+ if (eventType == XmlPullParser.START_TAG) {
+ String name = getElementName();
+ if ("genre".equals(name)) {
+ genre = new Genre();
+ genre.setSongCount(getInteger("songCount"));
+ genre.setAlbumCount(getInteger("albumCount"));
+ } else if ("error".equals(name)) {
+ handleError();
+ } else {
+ genre = null;
+ }
+ } else if (eventType == XmlPullParser.TEXT) {
+ if (genre != null) {
+ String value = getText();
+ if (genre != null) {
+ genre.setName(Html.fromHtml(value).toString());
+ genre.setIndex(value.substring(0, 1));
+ result.add(genre);
+ genre = null;
+ }
+ }
+ }
+ } while (eventType != XmlPullParser.END_DOCUMENT);
+
+ validate();
+
+ return Genre.GenreComparator.sort(result);
+ }
+}
diff --git a/src/github/daneren2005/dsub/service/parser/IndexesParser.java b/app/src/main/java/github/daneren2005/dsub/service/parser/IndexesParser.java
index 0ac86476..0ac86476 100644
--- a/src/github/daneren2005/dsub/service/parser/IndexesParser.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/parser/IndexesParser.java
diff --git a/src/github/daneren2005/dsub/service/parser/JukeboxStatusParser.java b/app/src/main/java/github/daneren2005/dsub/service/parser/JukeboxStatusParser.java
index 95529635..95529635 100644
--- a/src/github/daneren2005/dsub/service/parser/JukeboxStatusParser.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/parser/JukeboxStatusParser.java
diff --git a/src/github/daneren2005/dsub/service/parser/LicenseParser.java b/app/src/main/java/github/daneren2005/dsub/service/parser/LicenseParser.java
index 78790062..78790062 100644
--- a/src/github/daneren2005/dsub/service/parser/LicenseParser.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/parser/LicenseParser.java
diff --git a/src/github/daneren2005/dsub/service/parser/LyricsParser.java b/app/src/main/java/github/daneren2005/dsub/service/parser/LyricsParser.java
index e7ce7a4b..e7ce7a4b 100644
--- a/src/github/daneren2005/dsub/service/parser/LyricsParser.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/parser/LyricsParser.java
diff --git a/src/github/daneren2005/dsub/service/parser/MusicDirectoryEntryParser.java b/app/src/main/java/github/daneren2005/dsub/service/parser/MusicDirectoryEntryParser.java
index 9542324e..9542324e 100644
--- a/src/github/daneren2005/dsub/service/parser/MusicDirectoryEntryParser.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/parser/MusicDirectoryEntryParser.java
diff --git a/src/github/daneren2005/dsub/service/parser/MusicDirectoryParser.java b/app/src/main/java/github/daneren2005/dsub/service/parser/MusicDirectoryParser.java
index a786bceb..a786bceb 100644
--- a/src/github/daneren2005/dsub/service/parser/MusicDirectoryParser.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/parser/MusicDirectoryParser.java
diff --git a/src/github/daneren2005/dsub/service/parser/MusicFoldersParser.java b/app/src/main/java/github/daneren2005/dsub/service/parser/MusicFoldersParser.java
index a525084e..a525084e 100644
--- a/src/github/daneren2005/dsub/service/parser/MusicFoldersParser.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/parser/MusicFoldersParser.java
diff --git a/src/github/daneren2005/dsub/service/parser/PlayQueueParser.java b/app/src/main/java/github/daneren2005/dsub/service/parser/PlayQueueParser.java
index ec161d2b..ec161d2b 100644
--- a/src/github/daneren2005/dsub/service/parser/PlayQueueParser.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/parser/PlayQueueParser.java
diff --git a/src/github/daneren2005/dsub/service/parser/PlaylistParser.java b/app/src/main/java/github/daneren2005/dsub/service/parser/PlaylistParser.java
index 5bb07dfd..5bb07dfd 100644
--- a/src/github/daneren2005/dsub/service/parser/PlaylistParser.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/parser/PlaylistParser.java
diff --git a/src/github/daneren2005/dsub/service/parser/PlaylistsParser.java b/app/src/main/java/github/daneren2005/dsub/service/parser/PlaylistsParser.java
index 6f01d510..6f01d510 100644
--- a/src/github/daneren2005/dsub/service/parser/PlaylistsParser.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/parser/PlaylistsParser.java
diff --git a/src/github/daneren2005/dsub/service/parser/PodcastChannelParser.java b/app/src/main/java/github/daneren2005/dsub/service/parser/PodcastChannelParser.java
index 8c77e2bc..36ed17de 100644
--- a/src/github/daneren2005/dsub/service/parser/PodcastChannelParser.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/parser/PodcastChannelParser.java
@@ -1,66 +1,66 @@
-/*
- 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 github.daneren2005.dsub.service.parser;
-
-import android.content.Context;
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.domain.PodcastChannel;
-import github.daneren2005.dsub.util.ProgressListener;
-import java.io.Reader;
-import java.util.ArrayList;
-import java.util.List;
-import org.xmlpull.v1.XmlPullParser;
-
-/**
- *
- * @author Scott
- */
-public class PodcastChannelParser extends AbstractParser {
- public PodcastChannelParser(Context context, int instance) {
- super(context, instance);
- }
-
- public List<PodcastChannel> parse(Reader reader, ProgressListener progressListener) throws Exception {
- init(reader);
-
- List<PodcastChannel> channels = new ArrayList<PodcastChannel>();
- int eventType;
- do {
- eventType = nextParseEvent();
- if (eventType == XmlPullParser.START_TAG) {
- String name = getElementName();
- if ("channel".equals(name)) {
- PodcastChannel channel = new PodcastChannel();
- channel.setId(get("id"));
- channel.setUrl(get("url"));
- channel.setName(get("title"));
- channel.setDescription(get("description"));
- channel.setStatus(get("status"));
- channel.setErrorMessage(get("errorMessage"));
- channels.add(channel);
- } else if ("error".equals(name)) {
- handleError();
- }
- }
- } while (eventType != XmlPullParser.END_DOCUMENT);
-
- validate();
- return PodcastChannel.PodcastComparator.sort(channels, context);
- }
-}
+/*
+ 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 github.daneren2005.dsub.service.parser;
+
+import android.content.Context;
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.PodcastChannel;
+import github.daneren2005.dsub.util.ProgressListener;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.List;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ *
+ * @author Scott
+ */
+public class PodcastChannelParser extends AbstractParser {
+ public PodcastChannelParser(Context context, int instance) {
+ super(context, instance);
+ }
+
+ public List<PodcastChannel> parse(Reader reader, ProgressListener progressListener) throws Exception {
+ init(reader);
+
+ List<PodcastChannel> channels = new ArrayList<PodcastChannel>();
+ int eventType;
+ do {
+ eventType = nextParseEvent();
+ if (eventType == XmlPullParser.START_TAG) {
+ String name = getElementName();
+ if ("channel".equals(name)) {
+ PodcastChannel channel = new PodcastChannel();
+ channel.setId(get("id"));
+ channel.setUrl(get("url"));
+ channel.setName(get("title"));
+ channel.setDescription(get("description"));
+ channel.setStatus(get("status"));
+ channel.setErrorMessage(get("errorMessage"));
+ channels.add(channel);
+ } else if ("error".equals(name)) {
+ handleError();
+ }
+ }
+ } while (eventType != XmlPullParser.END_DOCUMENT);
+
+ validate();
+ return PodcastChannel.PodcastComparator.sort(channels, context);
+ }
+}
diff --git a/src/github/daneren2005/dsub/service/parser/PodcastEntryParser.java b/app/src/main/java/github/daneren2005/dsub/service/parser/PodcastEntryParser.java
index fe742819..00089363 100644
--- a/src/github/daneren2005/dsub/service/parser/PodcastEntryParser.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/parser/PodcastEntryParser.java
@@ -1,112 +1,112 @@
-/*
- 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 github.daneren2005.dsub.service.parser;
-
-import android.content.Context;
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.domain.Bookmark;
-import github.daneren2005.dsub.domain.MusicDirectory;
-import github.daneren2005.dsub.domain.PodcastEpisode;
-import github.daneren2005.dsub.util.FileUtil;
-import github.daneren2005.dsub.util.ProgressListener;
-import java.io.Reader;
-import org.xmlpull.v1.XmlPullParser;
-
-/**
- *
- * @author Scott
- */
-public class PodcastEntryParser extends AbstractParser {
- private static int bogusId = -1;
-
- public PodcastEntryParser(Context context, int instance) {
- super(context, instance);
- }
-
- public MusicDirectory parse(String channel, Reader reader, ProgressListener progressListener) throws Exception {
- init(reader);
-
- MusicDirectory episodes = new MusicDirectory();
- int eventType;
- boolean valid = false;
- do {
- eventType = nextParseEvent();
- if (eventType == XmlPullParser.START_TAG) {
- String name = getElementName();
- if ("channel".equals(name)) {
- String id = get("id");
- if(id.equals(channel)) {
- episodes.setId(id);
- episodes.setName(get("title"));
- valid = true;
- } else {
- valid = false;
- }
- }
- else if ("episode".equals(name) && valid) {
- PodcastEpisode episode = new PodcastEpisode();
- episode.setEpisodeId(get("id"));
- episode.setId(get("streamId"));
- episode.setTitle(get("title"));
- episode.setParent(episodes.getId());
- episode.setArtist(episodes.getName());
- episode.setAlbum(get("description"));
- episode.setDate(get("publishDate"));
- if(episode.getDate() == null) {
- episode.setDate(get("created"));
- }
- if(episode.getDate() != null && episode.getDate().indexOf("T") != -1) {
- episode.setDate(episode.getDate().replace("T", " "));
- }
- episode.setStatus(get("status"));
- episode.setCoverArt(get("coverArt"));
- episode.setSize(getLong("size"));
- episode.setContentType(get("contentType"));
- episode.setSuffix(get("suffix"));
- episode.setDuration(getInteger("duration"));
- episode.setBitRate(getInteger("bitRate"));
- episode.setVideo(getBoolean("isVideo"));
- episode.setPath(get("path"));
- if(episode.getPath() == null) {
- episode.setPath(FileUtil.getPodcastPath(context, episode));
- } else if(episode.getPath().indexOf("Podcasts/") == 0) {
- episode.setPath(episode.getPath().substring("Podcasts/".length()));
- }
-
- Integer bookmark = getInteger("bookmarkPosition");
- if(bookmark != null) {
- episode.setBookmark(new Bookmark(bookmark));
- }
- episode.setType(MusicDirectory.Entry.TYPE_PODCAST);
-
- if(episode.getId() == null) {
- episode.setId(String.valueOf(bogusId));
- bogusId--;
- }
- episodes.addChild(episode);
- } else if ("error".equals(name)) {
- handleError();
- }
- }
- } while (eventType != XmlPullParser.END_DOCUMENT);
-
- validate();
- return episodes;
- }
-}
+/*
+ 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 github.daneren2005.dsub.service.parser;
+
+import android.content.Context;
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.Bookmark;
+import github.daneren2005.dsub.domain.MusicDirectory;
+import github.daneren2005.dsub.domain.PodcastEpisode;
+import github.daneren2005.dsub.util.FileUtil;
+import github.daneren2005.dsub.util.ProgressListener;
+import java.io.Reader;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ *
+ * @author Scott
+ */
+public class PodcastEntryParser extends AbstractParser {
+ private static int bogusId = -1;
+
+ public PodcastEntryParser(Context context, int instance) {
+ super(context, instance);
+ }
+
+ public MusicDirectory parse(String channel, Reader reader, ProgressListener progressListener) throws Exception {
+ init(reader);
+
+ MusicDirectory episodes = new MusicDirectory();
+ int eventType;
+ boolean valid = false;
+ do {
+ eventType = nextParseEvent();
+ if (eventType == XmlPullParser.START_TAG) {
+ String name = getElementName();
+ if ("channel".equals(name)) {
+ String id = get("id");
+ if(id.equals(channel)) {
+ episodes.setId(id);
+ episodes.setName(get("title"));
+ valid = true;
+ } else {
+ valid = false;
+ }
+ }
+ else if ("episode".equals(name) && valid) {
+ PodcastEpisode episode = new PodcastEpisode();
+ episode.setEpisodeId(get("id"));
+ episode.setId(get("streamId"));
+ episode.setTitle(get("title"));
+ episode.setParent(episodes.getId());
+ episode.setArtist(episodes.getName());
+ episode.setAlbum(get("description"));
+ episode.setDate(get("publishDate"));
+ if(episode.getDate() == null) {
+ episode.setDate(get("created"));
+ }
+ if(episode.getDate() != null && episode.getDate().indexOf("T") != -1) {
+ episode.setDate(episode.getDate().replace("T", " "));
+ }
+ episode.setStatus(get("status"));
+ episode.setCoverArt(get("coverArt"));
+ episode.setSize(getLong("size"));
+ episode.setContentType(get("contentType"));
+ episode.setSuffix(get("suffix"));
+ episode.setDuration(getInteger("duration"));
+ episode.setBitRate(getInteger("bitRate"));
+ episode.setVideo(getBoolean("isVideo"));
+ episode.setPath(get("path"));
+ if(episode.getPath() == null) {
+ episode.setPath(FileUtil.getPodcastPath(context, episode));
+ } else if(episode.getPath().indexOf("Podcasts/") == 0) {
+ episode.setPath(episode.getPath().substring("Podcasts/".length()));
+ }
+
+ Integer bookmark = getInteger("bookmarkPosition");
+ if(bookmark != null) {
+ episode.setBookmark(new Bookmark(bookmark));
+ }
+ episode.setType(MusicDirectory.Entry.TYPE_PODCAST);
+
+ if(episode.getId() == null) {
+ episode.setId(String.valueOf(bogusId));
+ bogusId--;
+ }
+ episodes.addChild(episode);
+ } else if ("error".equals(name)) {
+ handleError();
+ }
+ }
+ } while (eventType != XmlPullParser.END_DOCUMENT);
+
+ validate();
+ return episodes;
+ }
+}
diff --git a/src/github/daneren2005/dsub/service/parser/RandomSongsParser.java b/app/src/main/java/github/daneren2005/dsub/service/parser/RandomSongsParser.java
index 37057723..37057723 100644
--- a/src/github/daneren2005/dsub/service/parser/RandomSongsParser.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/parser/RandomSongsParser.java
diff --git a/src/github/daneren2005/dsub/service/parser/ScanStatusParser.java b/app/src/main/java/github/daneren2005/dsub/service/parser/ScanStatusParser.java
index 395dbcb6..ffb3ba05 100644
--- a/src/github/daneren2005/dsub/service/parser/ScanStatusParser.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/parser/ScanStatusParser.java
@@ -1,56 +1,56 @@
-/*
- 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 2014 (C) Scott Jackson
-*/
-
-package github.daneren2005.dsub.service.parser;
-
-import android.content.Context;
-import org.xmlpull.v1.XmlPullParser;
-
-import java.io.Reader;
-
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.util.ProgressListener;
-
-public class ScanStatusParser extends AbstractParser {
-
- public ScanStatusParser(Context context, int instance) {
- super(context, instance);
- }
-
- public boolean parse(Reader reader, ProgressListener progressListener) throws Exception {
- init(reader);
-
- Boolean started = null;
- int eventType;
- do {
- eventType = nextParseEvent();
- if (eventType == XmlPullParser.START_TAG) {
- String name = getElementName();
- if("status".equals(name)) {
- started = getBoolean("started");
-
- String msg = context.getResources().getString(R.string.parser_scan_count, getInteger("count"));
- progressListener.updateProgress(msg);
- } else if ("error".equals(name)) {
- handleError();
- }
- }
- } while (eventType != XmlPullParser.END_DOCUMENT);
-
- validate();
-
- return started != null && started;
- }
+/*
+ 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 2014 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.service.parser;
+
+import android.content.Context;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.io.Reader;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.util.ProgressListener;
+
+public class ScanStatusParser extends AbstractParser {
+
+ public ScanStatusParser(Context context, int instance) {
+ super(context, instance);
+ }
+
+ public boolean parse(Reader reader, ProgressListener progressListener) throws Exception {
+ init(reader);
+
+ Boolean started = null;
+ int eventType;
+ do {
+ eventType = nextParseEvent();
+ if (eventType == XmlPullParser.START_TAG) {
+ String name = getElementName();
+ if("status".equals(name)) {
+ started = getBoolean("started");
+
+ String msg = context.getResources().getString(R.string.parser_scan_count, getInteger("count"));
+ progressListener.updateProgress(msg);
+ } else if ("error".equals(name)) {
+ handleError();
+ }
+ }
+ } while (eventType != XmlPullParser.END_DOCUMENT);
+
+ validate();
+
+ return started != null && started;
+ }
} \ No newline at end of file
diff --git a/src/github/daneren2005/dsub/service/parser/SearchResult2Parser.java b/app/src/main/java/github/daneren2005/dsub/service/parser/SearchResult2Parser.java
index 8cc0c50d..8cc0c50d 100644
--- a/src/github/daneren2005/dsub/service/parser/SearchResult2Parser.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/parser/SearchResult2Parser.java
diff --git a/src/github/daneren2005/dsub/service/parser/SearchResultParser.java b/app/src/main/java/github/daneren2005/dsub/service/parser/SearchResultParser.java
index 252a7b20..252a7b20 100644
--- a/src/github/daneren2005/dsub/service/parser/SearchResultParser.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/parser/SearchResultParser.java
diff --git a/src/github/daneren2005/dsub/service/parser/ShareParser.java b/app/src/main/java/github/daneren2005/dsub/service/parser/ShareParser.java
index 375629d9..418393d1 100644
--- a/src/github/daneren2005/dsub/service/parser/ShareParser.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/parser/ShareParser.java
@@ -1,126 +1,126 @@
-/*
- 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 github.daneren2005.dsub.service.parser;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.util.Log;
-
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.domain.ServerInfo;
-import github.daneren2005.dsub.domain.Share;
-import github.daneren2005.dsub.util.Constants;
-import github.daneren2005.dsub.util.ProgressListener;
-import github.daneren2005.dsub.util.Util;
-
-import org.xmlpull.v1.XmlPullParser;
-import java.io.Reader;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-import java.util.Locale;
-import java.util.TimeZone;
-
-/**
- * @author Joshua Bahnsen
- */
-public class ShareParser extends MusicDirectoryEntryParser {
- private static final String TAG = ShareParser.class.getSimpleName();
-
- public ShareParser(Context context, int instance) {
- super(context, instance);
- }
-
- public List<Share> parse(Reader reader, ProgressListener progressListener) throws Exception {
- init(reader);
-
- List<Share> dir = new ArrayList<Share>();
- Share share = null;
- int eventType;
-
- SharedPreferences prefs = Util.getPreferences(context);
- int instance = prefs.getInt(Constants.PREFERENCES_KEY_SERVER_INSTANCE, 1);
- String serverUrl = prefs.getString(Constants.PREFERENCES_KEY_SERVER_URL + instance, null);
- if(serverUrl.charAt(serverUrl.length() - 1) != '/') {
- serverUrl += '/';
- }
- serverUrl += "share/";
-
- boolean isDateNormalized = ServerInfo.checkServerVersion(context, "1.11");
- SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH);
- if(isDateNormalized) {
- dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
- }
-
- do {
- eventType = nextParseEvent();
-
- if (eventType == XmlPullParser.START_TAG) {
- String name = getElementName();
-
- if ("share".equals(name)) {
- share = new Share();
-
- try {
- share.setCreated(dateFormat.parse(get("created")));
- } catch (Exception e) {
- share.setCreated((Date) null);
- }
-
- String url = get("url");
- if(url != null && url.indexOf(".php") == -1) {
- url = url.replaceFirst(".*/([^/?]+).*", serverUrl + "$1");
- }
- share.setUrl(url);
-
- share.setDescription(get("description"));
-
- try {
- share.setExpires(dateFormat.parse(get("expires")));
- } catch (Exception e) {
- share.setExpires((Date) null);
- }
- share.setId(get("id"));
-
- try {
- share.setLastVisited(dateFormat.parse(get("lastVisited")));
- } catch (Exception e) {
- share.setLastVisited((Date) null);
- }
-
- share.setUsername(get("username"));
- share.setVisitCount(getLong("visitCount"));
- dir.add(share);
- } else if ("entry".equals(name)) {
- if(share != null) {
- share.addEntry(parseEntry(null));
- }
- } else if ("error".equals(name)) {
- handleError();
- }
- }
- } while (eventType != XmlPullParser.END_DOCUMENT);
-
- validate();
-
- return dir;
- }
+/*
+ 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 github.daneren2005.dsub.service.parser;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.util.Log;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.ServerInfo;
+import github.daneren2005.dsub.domain.Share;
+import github.daneren2005.dsub.util.Constants;
+import github.daneren2005.dsub.util.ProgressListener;
+import github.daneren2005.dsub.util.Util;
+
+import org.xmlpull.v1.XmlPullParser;
+import java.io.Reader;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+import java.util.TimeZone;
+
+/**
+ * @author Joshua Bahnsen
+ */
+public class ShareParser extends MusicDirectoryEntryParser {
+ private static final String TAG = ShareParser.class.getSimpleName();
+
+ public ShareParser(Context context, int instance) {
+ super(context, instance);
+ }
+
+ public List<Share> parse(Reader reader, ProgressListener progressListener) throws Exception {
+ init(reader);
+
+ List<Share> dir = new ArrayList<Share>();
+ Share share = null;
+ int eventType;
+
+ SharedPreferences prefs = Util.getPreferences(context);
+ int instance = prefs.getInt(Constants.PREFERENCES_KEY_SERVER_INSTANCE, 1);
+ String serverUrl = prefs.getString(Constants.PREFERENCES_KEY_SERVER_URL + instance, null);
+ if(serverUrl.charAt(serverUrl.length() - 1) != '/') {
+ serverUrl += '/';
+ }
+ serverUrl += "share/";
+
+ boolean isDateNormalized = ServerInfo.checkServerVersion(context, "1.11");
+ SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH);
+ if(isDateNormalized) {
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ }
+
+ do {
+ eventType = nextParseEvent();
+
+ if (eventType == XmlPullParser.START_TAG) {
+ String name = getElementName();
+
+ if ("share".equals(name)) {
+ share = new Share();
+
+ try {
+ share.setCreated(dateFormat.parse(get("created")));
+ } catch (Exception e) {
+ share.setCreated((Date) null);
+ }
+
+ String url = get("url");
+ if(url != null && url.indexOf(".php") == -1) {
+ url = url.replaceFirst(".*/([^/?]+).*", serverUrl + "$1");
+ }
+ share.setUrl(url);
+
+ share.setDescription(get("description"));
+
+ try {
+ share.setExpires(dateFormat.parse(get("expires")));
+ } catch (Exception e) {
+ share.setExpires((Date) null);
+ }
+ share.setId(get("id"));
+
+ try {
+ share.setLastVisited(dateFormat.parse(get("lastVisited")));
+ } catch (Exception e) {
+ share.setLastVisited((Date) null);
+ }
+
+ share.setUsername(get("username"));
+ share.setVisitCount(getLong("visitCount"));
+ dir.add(share);
+ } else if ("entry".equals(name)) {
+ if(share != null) {
+ share.addEntry(parseEntry(null));
+ }
+ } else if ("error".equals(name)) {
+ handleError();
+ }
+ }
+ } while (eventType != XmlPullParser.END_DOCUMENT);
+
+ validate();
+
+ return dir;
+ }
} \ No newline at end of file
diff --git a/src/github/daneren2005/dsub/service/parser/StarredListParser.java b/app/src/main/java/github/daneren2005/dsub/service/parser/StarredListParser.java
index 59652e29..59652e29 100644
--- a/src/github/daneren2005/dsub/service/parser/StarredListParser.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/parser/StarredListParser.java
diff --git a/src/github/daneren2005/dsub/service/parser/SubsonicRESTException.java b/app/src/main/java/github/daneren2005/dsub/service/parser/SubsonicRESTException.java
index 096597a1..096597a1 100644
--- a/src/github/daneren2005/dsub/service/parser/SubsonicRESTException.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/parser/SubsonicRESTException.java
diff --git a/src/github/daneren2005/dsub/service/parser/UserParser.java b/app/src/main/java/github/daneren2005/dsub/service/parser/UserParser.java
index c8b56080..e20556c0 100644
--- a/src/github/daneren2005/dsub/service/parser/UserParser.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/parser/UserParser.java
@@ -1,73 +1,73 @@
-/*
- 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 2014 (C) Scott Jackson
-*/
-
-package github.daneren2005.dsub.service.parser;
-
-import android.content.Context;
-
-import org.xmlpull.v1.XmlPullParser;
-
-import java.io.Reader;
-import java.util.ArrayList;
-import java.util.List;
-
-import github.daneren2005.dsub.domain.User;
-import github.daneren2005.dsub.util.ProgressListener;
-
-public class UserParser extends AbstractParser {
-
- public UserParser(Context context, int instance) {
- super(context, instance);
- }
-
- public List<User> parse(Reader reader, ProgressListener progressListener) throws Exception {
- init(reader);
- List<User> result = new ArrayList<User>();
- int eventType;
-
- do {
- eventType = nextParseEvent();
- if (eventType == XmlPullParser.START_TAG) {
- String name = getElementName();
- if ("user".equals(name)) {
- User user = new User();
-
- user.setUsername(get("username"));
- user.setEmail(get("email"));
- parseSetting(user, User.SCROBBLING);
- for(String role: User.ROLES) {
- parseSetting(user, role);
- }
- parseSetting(user, User.LASTFM);
-
- result.add(user);
- } else if ("error".equals(name)) {
- handleError();
- }
- }
- } while (eventType != XmlPullParser.END_DOCUMENT);
-
- validate();
-
- return result;
- }
-
- private void parseSetting(User user, String name) {
- String value = get(name);
- if(value != null) {
- user.addSetting(name, "true".equals(value));
- }
- }
-}
+/*
+ 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 2014 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.service.parser;
+
+import android.content.Context;
+
+import org.xmlpull.v1.XmlPullParser;
+
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.List;
+
+import github.daneren2005.dsub.domain.User;
+import github.daneren2005.dsub.util.ProgressListener;
+
+public class UserParser extends AbstractParser {
+
+ public UserParser(Context context, int instance) {
+ super(context, instance);
+ }
+
+ public List<User> parse(Reader reader, ProgressListener progressListener) throws Exception {
+ init(reader);
+ List<User> result = new ArrayList<User>();
+ int eventType;
+
+ do {
+ eventType = nextParseEvent();
+ if (eventType == XmlPullParser.START_TAG) {
+ String name = getElementName();
+ if ("user".equals(name)) {
+ User user = new User();
+
+ user.setUsername(get("username"));
+ user.setEmail(get("email"));
+ parseSetting(user, User.SCROBBLING);
+ for(String role: User.ROLES) {
+ parseSetting(user, role);
+ }
+ parseSetting(user, User.LASTFM);
+
+ result.add(user);
+ } else if ("error".equals(name)) {
+ handleError();
+ }
+ }
+ } while (eventType != XmlPullParser.END_DOCUMENT);
+
+ validate();
+
+ return result;
+ }
+
+ private void parseSetting(User user, String name) {
+ String value = get(name);
+ if(value != null) {
+ user.addSetting(name, "true".equals(value));
+ }
+ }
+}
diff --git a/src/github/daneren2005/dsub/service/parser/VideosParser.java b/app/src/main/java/github/daneren2005/dsub/service/parser/VideosParser.java
index f22c4a4a..f22c4a4a 100644
--- a/src/github/daneren2005/dsub/service/parser/VideosParser.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/parser/VideosParser.java
diff --git a/src/github/daneren2005/dsub/service/ssl/SSLSocketFactory.java b/app/src/main/java/github/daneren2005/dsub/service/ssl/SSLSocketFactory.java
index 3b1203c7..3b1203c7 100644
--- a/src/github/daneren2005/dsub/service/ssl/SSLSocketFactory.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/ssl/SSLSocketFactory.java
diff --git a/src/github/daneren2005/dsub/service/ssl/TrustManagerDecorator.java b/app/src/main/java/github/daneren2005/dsub/service/ssl/TrustManagerDecorator.java
index f2364368..f2364368 100644
--- a/src/github/daneren2005/dsub/service/ssl/TrustManagerDecorator.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/ssl/TrustManagerDecorator.java
diff --git a/src/github/daneren2005/dsub/service/ssl/TrustSelfSignedStrategy.java b/app/src/main/java/github/daneren2005/dsub/service/ssl/TrustSelfSignedStrategy.java
index 637a8931..637a8931 100644
--- a/src/github/daneren2005/dsub/service/ssl/TrustSelfSignedStrategy.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/ssl/TrustSelfSignedStrategy.java
diff --git a/src/github/daneren2005/dsub/service/ssl/TrustStrategy.java b/app/src/main/java/github/daneren2005/dsub/service/ssl/TrustStrategy.java
index 334a97c5..334a97c5 100644
--- a/src/github/daneren2005/dsub/service/ssl/TrustStrategy.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/ssl/TrustStrategy.java
diff --git a/src/github/daneren2005/dsub/service/sync/AuthenticatorService.java b/app/src/main/java/github/daneren2005/dsub/service/sync/AuthenticatorService.java
index ae12c88e..89fccb91 100644
--- a/src/github/daneren2005/dsub/service/sync/AuthenticatorService.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/sync/AuthenticatorService.java
@@ -1,90 +1,90 @@
-/*
- 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 github.daneren2005.dsub.service.sync;
-
-import android.accounts.AbstractAccountAuthenticator;
-import android.accounts.Account;
-import android.accounts.AccountAuthenticatorResponse;
-import android.accounts.NetworkErrorException;
-import android.app.Service;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.IBinder;
-
-/**
- * Created by Scott on 8/28/13.
- */
-
-public class AuthenticatorService extends Service {
- private SubsonicAuthenticator authenticator;
-
- @Override
- public void onCreate() {
- authenticator = new SubsonicAuthenticator(this);
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- return authenticator.getIBinder();
-
- }
-
- private class SubsonicAuthenticator extends AbstractAccountAuthenticator {
- public SubsonicAuthenticator(Context context) {
- super(context);
- }
-
- @Override
- public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
- return null;
- }
-
- @Override
- public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException {
- return null;
- }
-
- @Override
- public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) throws NetworkErrorException {
- return null;
- }
-
- @Override
- public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
- return null;
- }
-
- @Override
- public String getAuthTokenLabel(String authTokenType) {
- return null;
- }
-
- @Override
- public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
- return null;
- }
-
- @Override
- public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException {
- return null;
- }
- }
-}
+/*
+ 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 github.daneren2005.dsub.service.sync;
+
+import android.accounts.AbstractAccountAuthenticator;
+import android.accounts.Account;
+import android.accounts.AccountAuthenticatorResponse;
+import android.accounts.NetworkErrorException;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+
+/**
+ * Created by Scott on 8/28/13.
+ */
+
+public class AuthenticatorService extends Service {
+ private SubsonicAuthenticator authenticator;
+
+ @Override
+ public void onCreate() {
+ authenticator = new SubsonicAuthenticator(this);
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return authenticator.getIBinder();
+
+ }
+
+ private class SubsonicAuthenticator extends AbstractAccountAuthenticator {
+ public SubsonicAuthenticator(Context context) {
+ super(context);
+ }
+
+ @Override
+ public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
+ return null;
+ }
+
+ @Override
+ public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException {
+ return null;
+ }
+
+ @Override
+ public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) throws NetworkErrorException {
+ return null;
+ }
+
+ @Override
+ public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
+ return null;
+ }
+
+ @Override
+ public String getAuthTokenLabel(String authTokenType) {
+ return null;
+ }
+
+ @Override
+ public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
+ return null;
+ }
+
+ @Override
+ public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException {
+ return null;
+ }
+ }
+}
diff --git a/src/github/daneren2005/dsub/service/sync/MostRecentSyncAdapter.java b/app/src/main/java/github/daneren2005/dsub/service/sync/MostRecentSyncAdapter.java
index a0727201..f7a8634e 100644
--- a/src/github/daneren2005/dsub/service/sync/MostRecentSyncAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/sync/MostRecentSyncAdapter.java
@@ -1,105 +1,105 @@
-/*
- 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 github.daneren2005.dsub.service.sync;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.util.Log;
-
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.List;
-
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.domain.MusicDirectory;
-import github.daneren2005.dsub.domain.PodcastEpisode;
-import github.daneren2005.dsub.service.DownloadFile;
-import github.daneren2005.dsub.util.FileUtil;
-import github.daneren2005.dsub.util.Notifications;
-import github.daneren2005.dsub.util.SyncUtil;
-import github.daneren2005.dsub.util.SyncUtil.SyncSet;
-import github.daneren2005.dsub.util.Util;
-
-/**
- * Created by Scott on 8/28/13.
- */
-
-public class MostRecentSyncAdapter extends SubsonicSyncAdapter {
- private static String TAG = MostRecentSyncAdapter.class.getSimpleName();
-
- public MostRecentSyncAdapter(Context context, boolean autoInitialize) {
- super(context, autoInitialize);
- }
- @TargetApi(14)
- public MostRecentSyncAdapter(Context context, boolean autoInitialize, boolean allowParallelSyncs) {
- super(context, autoInitialize, allowParallelSyncs);
- }
-
- @Override
- public void onExecuteSync(Context context, int instance) {
- try {
- ArrayList<String> syncedList = SyncUtil.getSyncedMostRecent(context, instance);
- MusicDirectory albumList = musicService.getAlbumList("newest", 20, 0, context, null);
- List<String> updated = new ArrayList<String>();
- boolean firstRun = false;
- if(syncedList.size() == 0) {
- // Get the initial set of albums on first run, don't sync any of these!
- for(MusicDirectory.Entry album: albumList.getChildren()) {
- syncedList.add(album.getId());
- }
- firstRun = true;
- } else {
- for(MusicDirectory.Entry album: albumList.getChildren()) {
- if(!syncedList.contains(album.getId())) {
- if(!"Podcast".equals(album.getGenre())) {
- try {
- if(downloadRecursively(null, getMusicDirectory(album), context, false)) {
- updated.add(album.getTitle());
- }
- } catch(Exception e) {
- Log.w(TAG, "Failed to get songs for " + album.getId() + " on " + Util.getServerName(context, instance));
- }
- }
- syncedList.add(album.getId());
- }
- }
- }
-
- if(updated.size() > 0) {
- while(syncedList.size() > 40) {
- syncedList.remove(0);
- }
-
- FileUtil.serialize(context, syncedList, SyncUtil.getMostRecentSyncFile(context, instance));
-
- // If there is a new album on the active server, chances are artists need to be refreshed
- if(Util.getActiveServer(context) == instance) {
- musicService.getIndexes(Util.getSelectedMusicFolderId(context), true, context, null);
- }
-
- Notifications.showSyncNotification(context, R.string.sync_new_albums, SyncUtil.joinNames(updated));
- } else if(firstRun) {
- FileUtil.serialize(context, syncedList, SyncUtil.getMostRecentSyncFile(context, instance));
- }
- } catch(Exception e) {
- Log.e(TAG, "Failed to get most recent list for " + Util.getServerName(context, instance));
- }
- }
-}
+/*
+ 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 github.daneren2005.dsub.service.sync;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.util.Log;
+
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.MusicDirectory;
+import github.daneren2005.dsub.domain.PodcastEpisode;
+import github.daneren2005.dsub.service.DownloadFile;
+import github.daneren2005.dsub.util.FileUtil;
+import github.daneren2005.dsub.util.Notifications;
+import github.daneren2005.dsub.util.SyncUtil;
+import github.daneren2005.dsub.util.SyncUtil.SyncSet;
+import github.daneren2005.dsub.util.Util;
+
+/**
+ * Created by Scott on 8/28/13.
+ */
+
+public class MostRecentSyncAdapter extends SubsonicSyncAdapter {
+ private static String TAG = MostRecentSyncAdapter.class.getSimpleName();
+
+ public MostRecentSyncAdapter(Context context, boolean autoInitialize) {
+ super(context, autoInitialize);
+ }
+ @TargetApi(14)
+ public MostRecentSyncAdapter(Context context, boolean autoInitialize, boolean allowParallelSyncs) {
+ super(context, autoInitialize, allowParallelSyncs);
+ }
+
+ @Override
+ public void onExecuteSync(Context context, int instance) {
+ try {
+ ArrayList<String> syncedList = SyncUtil.getSyncedMostRecent(context, instance);
+ MusicDirectory albumList = musicService.getAlbumList("newest", 20, 0, context, null);
+ List<String> updated = new ArrayList<String>();
+ boolean firstRun = false;
+ if(syncedList.size() == 0) {
+ // Get the initial set of albums on first run, don't sync any of these!
+ for(MusicDirectory.Entry album: albumList.getChildren()) {
+ syncedList.add(album.getId());
+ }
+ firstRun = true;
+ } else {
+ for(MusicDirectory.Entry album: albumList.getChildren()) {
+ if(!syncedList.contains(album.getId())) {
+ if(!"Podcast".equals(album.getGenre())) {
+ try {
+ if(downloadRecursively(null, getMusicDirectory(album), context, false)) {
+ updated.add(album.getTitle());
+ }
+ } catch(Exception e) {
+ Log.w(TAG, "Failed to get songs for " + album.getId() + " on " + Util.getServerName(context, instance));
+ }
+ }
+ syncedList.add(album.getId());
+ }
+ }
+ }
+
+ if(updated.size() > 0) {
+ while(syncedList.size() > 40) {
+ syncedList.remove(0);
+ }
+
+ FileUtil.serialize(context, syncedList, SyncUtil.getMostRecentSyncFile(context, instance));
+
+ // If there is a new album on the active server, chances are artists need to be refreshed
+ if(Util.getActiveServer(context) == instance) {
+ musicService.getIndexes(Util.getSelectedMusicFolderId(context), true, context, null);
+ }
+
+ Notifications.showSyncNotification(context, R.string.sync_new_albums, SyncUtil.joinNames(updated));
+ } else if(firstRun) {
+ FileUtil.serialize(context, syncedList, SyncUtil.getMostRecentSyncFile(context, instance));
+ }
+ } catch(Exception e) {
+ Log.e(TAG, "Failed to get most recent list for " + Util.getServerName(context, instance));
+ }
+ }
+}
diff --git a/src/github/daneren2005/dsub/service/sync/MostRecentSyncService.java b/app/src/main/java/github/daneren2005/dsub/service/sync/MostRecentSyncService.java
index 378fe432..49ea4a0e 100644
--- a/src/github/daneren2005/dsub/service/sync/MostRecentSyncService.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/sync/MostRecentSyncService.java
@@ -1,48 +1,48 @@
-/*
- 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 github.daneren2005.dsub.service.sync;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
-
-/**
- * Created by Scott on 8/28/13.
- */
-
-public class MostRecentSyncService extends Service {
- private static MostRecentSyncAdapter mostRecentSyncAdapter;
- private static final Object syncLock = new Object();
-
- @Override
- public void onCreate() {
- synchronized (syncLock) {
- if(mostRecentSyncAdapter == null) {
- mostRecentSyncAdapter = new MostRecentSyncAdapter(getApplicationContext(), true);
- }
- }
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- return mostRecentSyncAdapter.getSyncAdapterBinder();
-
- }
-}
+/*
+ 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 github.daneren2005.dsub.service.sync;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+/**
+ * Created by Scott on 8/28/13.
+ */
+
+public class MostRecentSyncService extends Service {
+ private static MostRecentSyncAdapter mostRecentSyncAdapter;
+ private static final Object syncLock = new Object();
+
+ @Override
+ public void onCreate() {
+ synchronized (syncLock) {
+ if(mostRecentSyncAdapter == null) {
+ mostRecentSyncAdapter = new MostRecentSyncAdapter(getApplicationContext(), true);
+ }
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mostRecentSyncAdapter.getSyncAdapterBinder();
+
+ }
+}
diff --git a/src/github/daneren2005/dsub/service/sync/PlaylistSyncAdapter.java b/app/src/main/java/github/daneren2005/dsub/service/sync/PlaylistSyncAdapter.java
index e96ccb3e..a0996628 100644
--- a/src/github/daneren2005/dsub/service/sync/PlaylistSyncAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/sync/PlaylistSyncAdapter.java
@@ -1,153 +1,153 @@
-/*
- 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 github.daneren2005.dsub.service.sync;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.util.Log;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.domain.MusicDirectory;
-import github.daneren2005.dsub.domain.Playlist;
-import github.daneren2005.dsub.service.DownloadFile;
-import github.daneren2005.dsub.service.parser.SubsonicRESTException;
-import github.daneren2005.dsub.util.FileUtil;
-import github.daneren2005.dsub.util.Notifications;
-import github.daneren2005.dsub.util.SyncUtil;
-import github.daneren2005.dsub.util.SyncUtil.SyncSet;
-import github.daneren2005.dsub.util.Util;
-
-/**
- * Created by Scott on 8/28/13.
-*/
-
-public class PlaylistSyncAdapter extends SubsonicSyncAdapter {
- private static String TAG = PlaylistSyncAdapter.class.getSimpleName();
- // Update playlists every day to make sure they are still valid
- private static int MAX_PLAYLIST_AGE = 24;
-
- public PlaylistSyncAdapter(Context context, boolean autoInitialize) {
- super(context, autoInitialize);
- }
- @TargetApi(14)
- public PlaylistSyncAdapter(Context context, boolean autoInitialize, boolean allowParallelSyncs) {
- super(context, autoInitialize, allowParallelSyncs);
- }
-
- @Override
- public void onExecuteSync(Context context, int instance) {
- String serverName = Util.getServerName(context, instance);
-
- List<Playlist> remainder = null;
- try {
- // Just update playlist listings so user doesn't have to
- remainder = musicService.getPlaylists(true, context, null);
- } catch(Exception e) {
- Log.e(TAG, "Failed to refresh playlist list for " + serverName);
- }
-
- ArrayList<SyncSet> playlistList = SyncUtil.getSyncedPlaylists(context, instance);
- List<String> updated = new ArrayList<String>();
- boolean removed = false;
- for(int i = 0; i < playlistList.size(); i++) {
- SyncSet cachedPlaylist = playlistList.get(i);
- String id = cachedPlaylist.id;
-
- // Remove playlist from remainder list
- if(remainder != null) {
- remainder.remove(new Playlist(id, ""));
- }
-
- try {
- MusicDirectory playlist = musicService.getPlaylist(true, id, serverName, context, null);
-
- // Get list of original paths
- List<String> origPathList = new ArrayList<String>();
- if(cachedPlaylist.synced != null) {
- origPathList.addAll(cachedPlaylist.synced);
- } else {
- cachedPlaylist.synced = new ArrayList<String>();
- }
-
- for(MusicDirectory.Entry entry: playlist.getChildren()) {
- DownloadFile file = new DownloadFile(context, entry, true);
- String path = file.getCompleteFile().getPath();
- while(!file.isSaved() && !file.isFailedMax()) {
- file.downloadNow(musicService);
- if(file.isSaved() && !updated.contains(playlist.getName())) {
- updated.add(playlist.getName());
- }
- }
-
- // Add to cached path set if saved
- if(file.isSaved() && !cachedPlaylist.synced.contains(path)) {
- cachedPlaylist.synced.add(path);
- }
-
- origPathList.remove(path);
- }
-
- // Check to unpin all paths which are no longer in playlist
- if(origPathList.size() > 0) {
- for(String path: origPathList) {
- File saveFile = new File(path);
- FileUtil.unpinSong(context, saveFile);
- cachedPlaylist.synced.remove(path);
- }
-
- removed = true;
- }
- } catch(SubsonicRESTException e) {
- if(e.getCode() == 70) {
- SyncUtil.removeSyncedPlaylist(context, id, instance);
- Log.i(TAG, "Unsync deleted playlist " + id + " for " + serverName);
- }
- } catch(Exception e) {
- Log.e(TAG, "Failed to get playlist " + id + " for " + serverName, e);
- }
-
- if(updated.size() > 0 || removed) {
- SyncUtil.setSyncedPlaylists(context, instance, playlistList);
- }
- }
-
- // For remaining playlists, check to make sure they have been updated recently
- if(remainder != null) {
- for (Playlist playlist : remainder) {
- MusicDirectory dir = FileUtil.deserialize(context, Util.getCacheName(context, instance, "playlist", playlist.getId()), MusicDirectory.class, MAX_PLAYLIST_AGE);
- if (dir == null) {
- try {
- musicService.getPlaylist(true, playlist.getId(), serverName, context, null);
- } catch(Exception e) {
- Log.w(TAG, "Failed to update playlist for " + playlist.getName());
- }
- }
- }
- }
-
- if(updated.size() > 0) {
- Notifications.showSyncNotification(context, R.string.sync_new_playlists, SyncUtil.joinNames(updated));
- }
- }
-}
+/*
+ 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 github.daneren2005.dsub.service.sync;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.util.Log;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.MusicDirectory;
+import github.daneren2005.dsub.domain.Playlist;
+import github.daneren2005.dsub.service.DownloadFile;
+import github.daneren2005.dsub.service.parser.SubsonicRESTException;
+import github.daneren2005.dsub.util.FileUtil;
+import github.daneren2005.dsub.util.Notifications;
+import github.daneren2005.dsub.util.SyncUtil;
+import github.daneren2005.dsub.util.SyncUtil.SyncSet;
+import github.daneren2005.dsub.util.Util;
+
+/**
+ * Created by Scott on 8/28/13.
+*/
+
+public class PlaylistSyncAdapter extends SubsonicSyncAdapter {
+ private static String TAG = PlaylistSyncAdapter.class.getSimpleName();
+ // Update playlists every day to make sure they are still valid
+ private static int MAX_PLAYLIST_AGE = 24;
+
+ public PlaylistSyncAdapter(Context context, boolean autoInitialize) {
+ super(context, autoInitialize);
+ }
+ @TargetApi(14)
+ public PlaylistSyncAdapter(Context context, boolean autoInitialize, boolean allowParallelSyncs) {
+ super(context, autoInitialize, allowParallelSyncs);
+ }
+
+ @Override
+ public void onExecuteSync(Context context, int instance) {
+ String serverName = Util.getServerName(context, instance);
+
+ List<Playlist> remainder = null;
+ try {
+ // Just update playlist listings so user doesn't have to
+ remainder = musicService.getPlaylists(true, context, null);
+ } catch(Exception e) {
+ Log.e(TAG, "Failed to refresh playlist list for " + serverName);
+ }
+
+ ArrayList<SyncSet> playlistList = SyncUtil.getSyncedPlaylists(context, instance);
+ List<String> updated = new ArrayList<String>();
+ boolean removed = false;
+ for(int i = 0; i < playlistList.size(); i++) {
+ SyncSet cachedPlaylist = playlistList.get(i);
+ String id = cachedPlaylist.id;
+
+ // Remove playlist from remainder list
+ if(remainder != null) {
+ remainder.remove(new Playlist(id, ""));
+ }
+
+ try {
+ MusicDirectory playlist = musicService.getPlaylist(true, id, serverName, context, null);
+
+ // Get list of original paths
+ List<String> origPathList = new ArrayList<String>();
+ if(cachedPlaylist.synced != null) {
+ origPathList.addAll(cachedPlaylist.synced);
+ } else {
+ cachedPlaylist.synced = new ArrayList<String>();
+ }
+
+ for(MusicDirectory.Entry entry: playlist.getChildren()) {
+ DownloadFile file = new DownloadFile(context, entry, true);
+ String path = file.getCompleteFile().getPath();
+ while(!file.isSaved() && !file.isFailedMax()) {
+ file.downloadNow(musicService);
+ if(file.isSaved() && !updated.contains(playlist.getName())) {
+ updated.add(playlist.getName());
+ }
+ }
+
+ // Add to cached path set if saved
+ if(file.isSaved() && !cachedPlaylist.synced.contains(path)) {
+ cachedPlaylist.synced.add(path);
+ }
+
+ origPathList.remove(path);
+ }
+
+ // Check to unpin all paths which are no longer in playlist
+ if(origPathList.size() > 0) {
+ for(String path: origPathList) {
+ File saveFile = new File(path);
+ FileUtil.unpinSong(context, saveFile);
+ cachedPlaylist.synced.remove(path);
+ }
+
+ removed = true;
+ }
+ } catch(SubsonicRESTException e) {
+ if(e.getCode() == 70) {
+ SyncUtil.removeSyncedPlaylist(context, id, instance);
+ Log.i(TAG, "Unsync deleted playlist " + id + " for " + serverName);
+ }
+ } catch(Exception e) {
+ Log.e(TAG, "Failed to get playlist " + id + " for " + serverName, e);
+ }
+
+ if(updated.size() > 0 || removed) {
+ SyncUtil.setSyncedPlaylists(context, instance, playlistList);
+ }
+ }
+
+ // For remaining playlists, check to make sure they have been updated recently
+ if(remainder != null) {
+ for (Playlist playlist : remainder) {
+ MusicDirectory dir = FileUtil.deserialize(context, Util.getCacheName(context, instance, "playlist", playlist.getId()), MusicDirectory.class, MAX_PLAYLIST_AGE);
+ if (dir == null) {
+ try {
+ musicService.getPlaylist(true, playlist.getId(), serverName, context, null);
+ } catch(Exception e) {
+ Log.w(TAG, "Failed to update playlist for " + playlist.getName());
+ }
+ }
+ }
+ }
+
+ if(updated.size() > 0) {
+ Notifications.showSyncNotification(context, R.string.sync_new_playlists, SyncUtil.joinNames(updated));
+ }
+ }
+}
diff --git a/src/github/daneren2005/dsub/service/sync/PlaylistSyncService.java b/app/src/main/java/github/daneren2005/dsub/service/sync/PlaylistSyncService.java
index 80ec5564..dd1f3859 100644
--- a/src/github/daneren2005/dsub/service/sync/PlaylistSyncService.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/sync/PlaylistSyncService.java
@@ -1,48 +1,48 @@
-/*
- 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 github.daneren2005.dsub.service.sync;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
-
-/**
- * Created by Scott on 8/28/13.
- */
-
-public class PlaylistSyncService extends Service {
- private static PlaylistSyncAdapter playlistSyncAdapter;
- private static final Object syncLock = new Object();
-
- @Override
- public void onCreate() {
- synchronized (syncLock) {
- if(playlistSyncAdapter == null) {
- playlistSyncAdapter = new PlaylistSyncAdapter(getApplicationContext(), true);
- }
- }
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- return playlistSyncAdapter.getSyncAdapterBinder();
-
- }
-}
+/*
+ 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 github.daneren2005.dsub.service.sync;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+/**
+ * Created by Scott on 8/28/13.
+ */
+
+public class PlaylistSyncService extends Service {
+ private static PlaylistSyncAdapter playlistSyncAdapter;
+ private static final Object syncLock = new Object();
+
+ @Override
+ public void onCreate() {
+ synchronized (syncLock) {
+ if(playlistSyncAdapter == null) {
+ playlistSyncAdapter = new PlaylistSyncAdapter(getApplicationContext(), true);
+ }
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return playlistSyncAdapter.getSyncAdapterBinder();
+
+ }
+}
diff --git a/src/github/daneren2005/dsub/service/sync/PodcastSyncAdapter.java b/app/src/main/java/github/daneren2005/dsub/service/sync/PodcastSyncAdapter.java
index c34ce678..985a7267 100644
--- a/src/github/daneren2005/dsub/service/sync/PodcastSyncAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/sync/PodcastSyncAdapter.java
@@ -1,113 +1,113 @@
-/*
- 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 github.daneren2005.dsub.service.sync;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.util.Log;
-
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.List;
-
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.domain.MusicDirectory;
-import github.daneren2005.dsub.domain.PodcastEpisode;
-import github.daneren2005.dsub.service.DownloadFile;
-import github.daneren2005.dsub.service.parser.SubsonicRESTException;
-import github.daneren2005.dsub.util.Notifications;
-import github.daneren2005.dsub.util.SyncUtil;
-import github.daneren2005.dsub.util.SyncUtil.SyncSet;
-import github.daneren2005.dsub.util.FileUtil;
-import github.daneren2005.dsub.util.Util;
-
-/**
- * Created by Scott on 8/28/13.
- */
-
-public class PodcastSyncAdapter extends SubsonicSyncAdapter {
- private static String TAG = PodcastSyncAdapter.class.getSimpleName();
-
- public PodcastSyncAdapter(Context context, boolean autoInitialize) {
- super(context, autoInitialize);
- }
- @TargetApi(14)
- public PodcastSyncAdapter(Context context, boolean autoInitialize, boolean allowParallelSyncs) {
- super(context, autoInitialize, allowParallelSyncs);
- }
-
- @Override
- public void onExecuteSync(Context context, int instance) {
- ArrayList<SyncSet> podcastList = SyncUtil.getSyncedPodcasts(context, instance);
-
- try {
- // Only refresh if syncs exist (implies a server where supported)
- if(podcastList.size() > 0) {
- // Just update podcast listings so user doesn't have to
- musicService.getPodcastChannels(true, context, null);
-
- // Refresh podcast listings before syncing
- musicService.refreshPodcasts(context, null);
- }
-
- List<String> updated = new ArrayList<String>();
- for(int i = 0; i < podcastList.size(); i++) {
- SyncSet set = podcastList.get(i);
- String id = set.id;
- List<String> existingEpisodes = set.synced;
- try {
- MusicDirectory podcasts = musicService.getPodcastEpisodes(true, id, context, null);
-
- for(MusicDirectory.Entry entry: podcasts.getChildren()) {
- // Make sure podcast is valid and not already synced
- if(entry.getId() != null && "completed".equals(((PodcastEpisode)entry).getStatus()) && !existingEpisodes.contains(entry.getId())) {
- DownloadFile file = new DownloadFile(context, entry, false);
- while(!file.isCompleteFileAvailable() && !file.isFailedMax()) {
- file.downloadNow(musicService);
- }
- // Only add if actualy downloaded correctly
- if(file.isCompleteFileAvailable()) {
- existingEpisodes.add(entry.getId());
- if(!updated.contains(podcasts.getName())) {
- updated.add(podcasts.getName());
- }
- }
- }
- }
- } catch(SubsonicRESTException e) {
- if(e.getCode() == 70) {
- SyncUtil.removeSyncedPodcast(context, id, instance);
- Log.i(TAG, "Unsync deleted podcasts for " + id + " on " + Util.getServerName(context, instance));
- }
- } catch (Exception e) {
- Log.w(TAG, "Failed to get podcasts for " + id + " on " + Util.getServerName(context, instance));
- }
- }
-
- // Make sure there are is at least one change before re-syncing
- if(updated.size() > 0) {
- FileUtil.serialize(context, podcastList, SyncUtil.getPodcastSyncFile(context, instance));
- Notifications.showSyncNotification(context, R.string.sync_new_podcasts, SyncUtil.joinNames(updated));
- }
- } catch(Exception e) {
- Log.w(TAG, "Failed to get podcasts for " + Util.getServerName(context, instance));
- }
- }
-}
+/*
+ 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 github.daneren2005.dsub.service.sync;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.util.Log;
+
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.MusicDirectory;
+import github.daneren2005.dsub.domain.PodcastEpisode;
+import github.daneren2005.dsub.service.DownloadFile;
+import github.daneren2005.dsub.service.parser.SubsonicRESTException;
+import github.daneren2005.dsub.util.Notifications;
+import github.daneren2005.dsub.util.SyncUtil;
+import github.daneren2005.dsub.util.SyncUtil.SyncSet;
+import github.daneren2005.dsub.util.FileUtil;
+import github.daneren2005.dsub.util.Util;
+
+/**
+ * Created by Scott on 8/28/13.
+ */
+
+public class PodcastSyncAdapter extends SubsonicSyncAdapter {
+ private static String TAG = PodcastSyncAdapter.class.getSimpleName();
+
+ public PodcastSyncAdapter(Context context, boolean autoInitialize) {
+ super(context, autoInitialize);
+ }
+ @TargetApi(14)
+ public PodcastSyncAdapter(Context context, boolean autoInitialize, boolean allowParallelSyncs) {
+ super(context, autoInitialize, allowParallelSyncs);
+ }
+
+ @Override
+ public void onExecuteSync(Context context, int instance) {
+ ArrayList<SyncSet> podcastList = SyncUtil.getSyncedPodcasts(context, instance);
+
+ try {
+ // Only refresh if syncs exist (implies a server where supported)
+ if(podcastList.size() > 0) {
+ // Just update podcast listings so user doesn't have to
+ musicService.getPodcastChannels(true, context, null);
+
+ // Refresh podcast listings before syncing
+ musicService.refreshPodcasts(context, null);
+ }
+
+ List<String> updated = new ArrayList<String>();
+ for(int i = 0; i < podcastList.size(); i++) {
+ SyncSet set = podcastList.get(i);
+ String id = set.id;
+ List<String> existingEpisodes = set.synced;
+ try {
+ MusicDirectory podcasts = musicService.getPodcastEpisodes(true, id, context, null);
+
+ for(MusicDirectory.Entry entry: podcasts.getChildren()) {
+ // Make sure podcast is valid and not already synced
+ if(entry.getId() != null && "completed".equals(((PodcastEpisode)entry).getStatus()) && !existingEpisodes.contains(entry.getId())) {
+ DownloadFile file = new DownloadFile(context, entry, false);
+ while(!file.isCompleteFileAvailable() && !file.isFailedMax()) {
+ file.downloadNow(musicService);
+ }
+ // Only add if actualy downloaded correctly
+ if(file.isCompleteFileAvailable()) {
+ existingEpisodes.add(entry.getId());
+ if(!updated.contains(podcasts.getName())) {
+ updated.add(podcasts.getName());
+ }
+ }
+ }
+ }
+ } catch(SubsonicRESTException e) {
+ if(e.getCode() == 70) {
+ SyncUtil.removeSyncedPodcast(context, id, instance);
+ Log.i(TAG, "Unsync deleted podcasts for " + id + " on " + Util.getServerName(context, instance));
+ }
+ } catch (Exception e) {
+ Log.w(TAG, "Failed to get podcasts for " + id + " on " + Util.getServerName(context, instance));
+ }
+ }
+
+ // Make sure there are is at least one change before re-syncing
+ if(updated.size() > 0) {
+ FileUtil.serialize(context, podcastList, SyncUtil.getPodcastSyncFile(context, instance));
+ Notifications.showSyncNotification(context, R.string.sync_new_podcasts, SyncUtil.joinNames(updated));
+ }
+ } catch(Exception e) {
+ Log.w(TAG, "Failed to get podcasts for " + Util.getServerName(context, instance));
+ }
+ }
+}
diff --git a/src/github/daneren2005/dsub/service/sync/PodcastSyncService.java b/app/src/main/java/github/daneren2005/dsub/service/sync/PodcastSyncService.java
index ff9ef5f1..e4936581 100644
--- a/src/github/daneren2005/dsub/service/sync/PodcastSyncService.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/sync/PodcastSyncService.java
@@ -1,48 +1,48 @@
-/*
- 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 github.daneren2005.dsub.service.sync;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
-
-/**
- * Created by Scott on 8/28/13.
- */
-
-public class PodcastSyncService extends Service {
- private static PodcastSyncAdapter podcastSyncAdapter;
- private static final Object syncLock = new Object();
-
- @Override
- public void onCreate() {
- synchronized (syncLock) {
- if(podcastSyncAdapter == null) {
- podcastSyncAdapter = new PodcastSyncAdapter(getApplicationContext(), true);
- }
- }
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- return podcastSyncAdapter.getSyncAdapterBinder();
-
- }
-}
+/*
+ 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 github.daneren2005.dsub.service.sync;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+/**
+ * Created by Scott on 8/28/13.
+ */
+
+public class PodcastSyncService extends Service {
+ private static PodcastSyncAdapter podcastSyncAdapter;
+ private static final Object syncLock = new Object();
+
+ @Override
+ public void onCreate() {
+ synchronized (syncLock) {
+ if(podcastSyncAdapter == null) {
+ podcastSyncAdapter = new PodcastSyncAdapter(getApplicationContext(), true);
+ }
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return podcastSyncAdapter.getSyncAdapterBinder();
+
+ }
+}
diff --git a/src/github/daneren2005/dsub/service/sync/StarredSyncAdapter.java b/app/src/main/java/github/daneren2005/dsub/service/sync/StarredSyncAdapter.java
index 7cff68aa..cf985227 100644
--- a/src/github/daneren2005/dsub/service/sync/StarredSyncAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/sync/StarredSyncAdapter.java
@@ -1,80 +1,80 @@
-/*
- 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 github.daneren2005.dsub.service.sync;
-
-import android.annotation.TargetApi;
-import android.app.Notification;
-import android.content.Context;
-import android.util.Log;
-
-import java.io.File;
-import java.util.ArrayList;
-
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.domain.MusicDirectory;
-import github.daneren2005.dsub.util.FileUtil;
-import github.daneren2005.dsub.util.Notifications;
-import github.daneren2005.dsub.util.SyncUtil;
-import github.daneren2005.dsub.util.Util;
-
-/**
- * Created by Scott on 8/28/13.
- */
-
-public class StarredSyncAdapter extends SubsonicSyncAdapter {
- private static String TAG = StarredSyncAdapter.class.getSimpleName();
-
- public StarredSyncAdapter(Context context, boolean autoInitialize) {
- super(context, autoInitialize);
- }
- @TargetApi(14)
- public StarredSyncAdapter(Context context, boolean autoInitialize, boolean allowParallelSyncs) {
- super(context, autoInitialize, allowParallelSyncs);
- }
-
- @Override
- public void onExecuteSync(Context context, int instance) {
- try {
- ArrayList<String> syncedList = new ArrayList<String>();
- MusicDirectory starredList = musicService.getStarredList(context, null);
-
- // Pin all the starred stuff
- boolean updated = downloadRecursively(syncedList, starredList, context, true);
-
- // Get old starred list
- ArrayList<String> oldSyncedList = SyncUtil.getSyncedStarred(context, instance);
-
- // Check to make sure there aren't any old starred songs that now need to be removed
- oldSyncedList.removeAll(syncedList);
-
- for(String path: oldSyncedList) {
- File saveFile = new File(path);
- FileUtil.unpinSong(context, saveFile);
- }
-
- SyncUtil.setSyncedStarred(syncedList, context, instance);
- if(updated) {
- Notifications.showSyncNotification(context, R.string.sync_new_starred, null);
- }
- } catch(Exception e) {
- Log.e(TAG, "Failed to get starred list for " + Util.getServerName(context, instance));
- }
- }
-}
+/*
+ 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 github.daneren2005.dsub.service.sync;
+
+import android.annotation.TargetApi;
+import android.app.Notification;
+import android.content.Context;
+import android.util.Log;
+
+import java.io.File;
+import java.util.ArrayList;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.MusicDirectory;
+import github.daneren2005.dsub.util.FileUtil;
+import github.daneren2005.dsub.util.Notifications;
+import github.daneren2005.dsub.util.SyncUtil;
+import github.daneren2005.dsub.util.Util;
+
+/**
+ * Created by Scott on 8/28/13.
+ */
+
+public class StarredSyncAdapter extends SubsonicSyncAdapter {
+ private static String TAG = StarredSyncAdapter.class.getSimpleName();
+
+ public StarredSyncAdapter(Context context, boolean autoInitialize) {
+ super(context, autoInitialize);
+ }
+ @TargetApi(14)
+ public StarredSyncAdapter(Context context, boolean autoInitialize, boolean allowParallelSyncs) {
+ super(context, autoInitialize, allowParallelSyncs);
+ }
+
+ @Override
+ public void onExecuteSync(Context context, int instance) {
+ try {
+ ArrayList<String> syncedList = new ArrayList<String>();
+ MusicDirectory starredList = musicService.getStarredList(context, null);
+
+ // Pin all the starred stuff
+ boolean updated = downloadRecursively(syncedList, starredList, context, true);
+
+ // Get old starred list
+ ArrayList<String> oldSyncedList = SyncUtil.getSyncedStarred(context, instance);
+
+ // Check to make sure there aren't any old starred songs that now need to be removed
+ oldSyncedList.removeAll(syncedList);
+
+ for(String path: oldSyncedList) {
+ File saveFile = new File(path);
+ FileUtil.unpinSong(context, saveFile);
+ }
+
+ SyncUtil.setSyncedStarred(syncedList, context, instance);
+ if(updated) {
+ Notifications.showSyncNotification(context, R.string.sync_new_starred, null);
+ }
+ } catch(Exception e) {
+ Log.e(TAG, "Failed to get starred list for " + Util.getServerName(context, instance));
+ }
+ }
+}
diff --git a/src/github/daneren2005/dsub/service/sync/StarredSyncService.java b/app/src/main/java/github/daneren2005/dsub/service/sync/StarredSyncService.java
index 378ab996..9806d09b 100644
--- a/src/github/daneren2005/dsub/service/sync/StarredSyncService.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/sync/StarredSyncService.java
@@ -1,48 +1,48 @@
-/*
- 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 github.daneren2005.dsub.service.sync;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
-
-/**
- * Created by Scott on 8/28/13.
- */
-
-public class StarredSyncService extends Service {
- private static StarredSyncAdapter starredSyncAdapter;
- private static final Object syncLock = new Object();
-
- @Override
- public void onCreate() {
- synchronized (syncLock) {
- if(starredSyncAdapter == null) {
- starredSyncAdapter = new StarredSyncAdapter(getApplicationContext(), true);
- }
- }
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- return starredSyncAdapter.getSyncAdapterBinder();
-
- }
-}
+/*
+ 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 github.daneren2005.dsub.service.sync;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+/**
+ * Created by Scott on 8/28/13.
+ */
+
+public class StarredSyncService extends Service {
+ private static StarredSyncAdapter starredSyncAdapter;
+ private static final Object syncLock = new Object();
+
+ @Override
+ public void onCreate() {
+ synchronized (syncLock) {
+ if(starredSyncAdapter == null) {
+ starredSyncAdapter = new StarredSyncAdapter(getApplicationContext(), true);
+ }
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return starredSyncAdapter.getSyncAdapterBinder();
+
+ }
+}
diff --git a/src/github/daneren2005/dsub/service/sync/SubsonicSyncAdapter.java b/app/src/main/java/github/daneren2005/dsub/service/sync/SubsonicSyncAdapter.java
index 661f126d..661f126d 100644
--- a/src/github/daneren2005/dsub/service/sync/SubsonicSyncAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/sync/SubsonicSyncAdapter.java
diff --git a/src/github/daneren2005/dsub/updates/Updater.java b/app/src/main/java/github/daneren2005/dsub/updates/Updater.java
index c3157a3c..a2870941 100644
--- a/src/github/daneren2005/dsub/updates/Updater.java
+++ b/app/src/main/java/github/daneren2005/dsub/updates/Updater.java
@@ -1,98 +1,98 @@
-/*
- 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 github.daneren2005.dsub.updates;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.util.Log;
-import github.daneren2005.dsub.util.Constants;
-import github.daneren2005.dsub.util.SilentBackgroundTask;
-import github.daneren2005.dsub.util.Util;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- *
- * @author Scott
- */
-public class Updater {
- protected String TAG = Updater.class.getSimpleName();
- protected int version;
- protected Context context;
-
- public Updater(int version) {
- this.version = version;
- }
-
- public void checkUpdates(Context context) {
- this.context = context;
- List<Updater> updaters = new ArrayList<Updater>();
- updaters.add(new Updater403());
-
- SharedPreferences prefs = Util.getPreferences(context);
- int lastVersion = prefs.getInt(Constants.LAST_VERSION, 0);
- if(lastVersion == 0) {
- SharedPreferences.Editor editor = prefs.edit();
- editor.putInt(Constants.LAST_VERSION, version);
- editor.commit();
- }
- else if(version > lastVersion) {
- SharedPreferences.Editor editor = prefs.edit();
- editor.putInt(Constants.LAST_VERSION, version);
- editor.commit();
-
- Log.i(TAG, "Updating from version " + lastVersion + " to " + version);
- for(Updater updater: updaters) {
- if(updater.shouldUpdate(lastVersion)) {
- new BackgroundUpdate(context, updater).execute();
- }
- }
- }
- }
-
- public String getName() {
- return this.TAG;
- }
-
- private class BackgroundUpdate extends SilentBackgroundTask<Void> {
- private final Updater updater;
-
- public BackgroundUpdate(Context context, Updater updater) {
- super(context);
- this.updater = updater;
- }
-
- @Override
- protected Void doInBackground() {
- try {
- updater.update(context);
- } catch(Exception e) {
- Log.w(TAG, "Failed to run update for " + updater.getName());
- }
- return null;
- }
- }
-
- public boolean shouldUpdate(int version) {
- return this.version > version;
- }
- public void update(Context context) {
-
- }
-}
+/*
+ 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 github.daneren2005.dsub.updates;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.util.Log;
+import github.daneren2005.dsub.util.Constants;
+import github.daneren2005.dsub.util.SilentBackgroundTask;
+import github.daneren2005.dsub.util.Util;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ *
+ * @author Scott
+ */
+public class Updater {
+ protected String TAG = Updater.class.getSimpleName();
+ protected int version;
+ protected Context context;
+
+ public Updater(int version) {
+ this.version = version;
+ }
+
+ public void checkUpdates(Context context) {
+ this.context = context;
+ List<Updater> updaters = new ArrayList<Updater>();
+ updaters.add(new Updater403());
+
+ SharedPreferences prefs = Util.getPreferences(context);
+ int lastVersion = prefs.getInt(Constants.LAST_VERSION, 0);
+ if(lastVersion == 0) {
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putInt(Constants.LAST_VERSION, version);
+ editor.commit();
+ }
+ else if(version > lastVersion) {
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putInt(Constants.LAST_VERSION, version);
+ editor.commit();
+
+ Log.i(TAG, "Updating from version " + lastVersion + " to " + version);
+ for(Updater updater: updaters) {
+ if(updater.shouldUpdate(lastVersion)) {
+ new BackgroundUpdate(context, updater).execute();
+ }
+ }
+ }
+ }
+
+ public String getName() {
+ return this.TAG;
+ }
+
+ private class BackgroundUpdate extends SilentBackgroundTask<Void> {
+ private final Updater updater;
+
+ public BackgroundUpdate(Context context, Updater updater) {
+ super(context);
+ this.updater = updater;
+ }
+
+ @Override
+ protected Void doInBackground() {
+ try {
+ updater.update(context);
+ } catch(Exception e) {
+ Log.w(TAG, "Failed to run update for " + updater.getName());
+ }
+ return null;
+ }
+ }
+
+ public boolean shouldUpdate(int version) {
+ return this.version > version;
+ }
+ public void update(Context context) {
+
+ }
+}
diff --git a/src/github/daneren2005/dsub/updates/Updater403.java b/app/src/main/java/github/daneren2005/dsub/updates/Updater403.java
index 17947ce5..4f2cbf43 100644
--- a/src/github/daneren2005/dsub/updates/Updater403.java
+++ b/app/src/main/java/github/daneren2005/dsub/updates/Updater403.java
@@ -1,58 +1,58 @@
-/*
- 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 github.daneren2005.dsub.updates;
-
-import android.content.Context;
-import android.util.Log;
-import github.daneren2005.dsub.updates.Updater;
-import github.daneren2005.dsub.util.Constants;
-import github.daneren2005.dsub.util.FileUtil;
-import java.io.File;
-
-/**
- *
- * @author Scott
- */
-public class Updater403 extends Updater {
- public Updater403() {
- super(403);
- TAG = Updater403.class.getSimpleName();
- }
-
- @Override
- public void update(Context context) {
- // Rename cover.jpeg to cover.jpg
- Log.i(TAG, "Running Updater403: updating cover.jpg to albumart.jpg");
- File dir = FileUtil.getMusicDirectory(context);
- if(dir != null) {
- moveArt(dir);
- }
- }
-
- private void moveArt(File dir) {
- for(File file: dir.listFiles()) {
- if(file.isDirectory()) {
- moveArt(file);
- } else if("cover.jpg".equals(file.getName()) || "cover.jpeg".equals(file.getName())) {
- File renamed = new File(dir, Constants.ALBUM_ART_FILE);
- file.renameTo(renamed);
- }
- }
- }
-}
+/*
+ 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 github.daneren2005.dsub.updates;
+
+import android.content.Context;
+import android.util.Log;
+import github.daneren2005.dsub.updates.Updater;
+import github.daneren2005.dsub.util.Constants;
+import github.daneren2005.dsub.util.FileUtil;
+import java.io.File;
+
+/**
+ *
+ * @author Scott
+ */
+public class Updater403 extends Updater {
+ public Updater403() {
+ super(403);
+ TAG = Updater403.class.getSimpleName();
+ }
+
+ @Override
+ public void update(Context context) {
+ // Rename cover.jpeg to cover.jpg
+ Log.i(TAG, "Running Updater403: updating cover.jpg to albumart.jpg");
+ File dir = FileUtil.getMusicDirectory(context);
+ if(dir != null) {
+ moveArt(dir);
+ }
+ }
+
+ private void moveArt(File dir) {
+ for(File file: dir.listFiles()) {
+ if(file.isDirectory()) {
+ moveArt(file);
+ } else if("cover.jpg".equals(file.getName()) || "cover.jpeg".equals(file.getName())) {
+ File renamed = new File(dir, Constants.ALBUM_ART_FILE);
+ file.renameTo(renamed);
+ }
+ }
+ }
+}
diff --git a/src/github/daneren2005/dsub/util/ArtistRadioBuffer.java b/app/src/main/java/github/daneren2005/dsub/util/ArtistRadioBuffer.java
index 6e9b8309..6e9b8309 100644
--- a/src/github/daneren2005/dsub/util/ArtistRadioBuffer.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/ArtistRadioBuffer.java
diff --git a/src/github/daneren2005/dsub/util/BackgroundTask.java b/app/src/main/java/github/daneren2005/dsub/util/BackgroundTask.java
index 9b39ac82..9b39ac82 100644
--- a/src/github/daneren2005/dsub/util/BackgroundTask.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/BackgroundTask.java
diff --git a/src/github/daneren2005/dsub/util/CacheCleaner.java b/app/src/main/java/github/daneren2005/dsub/util/CacheCleaner.java
index ac8fa72a..ac8fa72a 100644
--- a/src/github/daneren2005/dsub/util/CacheCleaner.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/CacheCleaner.java
diff --git a/src/github/daneren2005/dsub/util/Constants.java b/app/src/main/java/github/daneren2005/dsub/util/Constants.java
index 31c5bef2..31c5bef2 100644
--- a/src/github/daneren2005/dsub/util/Constants.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/Constants.java
diff --git a/src/github/daneren2005/dsub/util/FileUtil.java b/app/src/main/java/github/daneren2005/dsub/util/FileUtil.java
index 990eae06..990eae06 100644
--- a/src/github/daneren2005/dsub/util/FileUtil.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/FileUtil.java
diff --git a/src/github/daneren2005/dsub/util/ImageLoader.java b/app/src/main/java/github/daneren2005/dsub/util/ImageLoader.java
index 1a0e8242..1a0e8242 100644
--- a/src/github/daneren2005/dsub/util/ImageLoader.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/ImageLoader.java
diff --git a/src/github/daneren2005/dsub/util/LoadingTask.java b/app/src/main/java/github/daneren2005/dsub/util/LoadingTask.java
index 77622e1e..116da816 100644
--- a/src/github/daneren2005/dsub/util/LoadingTask.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/LoadingTask.java
@@ -1,73 +1,73 @@
-package github.daneren2005.dsub.util;
-
-import android.app.Activity;
-import android.app.ProgressDialog;
-import android.content.DialogInterface;
-
-import github.daneren2005.dsub.activity.SubsonicActivity;
-
-/**
- * @author Sindre Mehus
- * @version $Id$
- */
-public abstract class LoadingTask<T> extends BackgroundTask<T> {
-
- private final Activity tabActivity;
- private ProgressDialog loading;
- private final boolean cancellable;
-
- public LoadingTask(Activity activity) {
- super(activity);
- tabActivity = activity;
- this.cancellable = true;
- }
- public LoadingTask(Activity activity, final boolean cancellable) {
- super(activity);
- tabActivity = activity;
- this.cancellable = cancellable;
- }
-
- @Override
- public void execute() {
- loading = ProgressDialog.show(tabActivity, "", "Loading. Please Wait...", true, cancellable, new DialogInterface.OnCancelListener() {
- public void onCancel(DialogInterface dialog) {
- cancel();
- }
- });
-
- queue.offer(task = new Task() {
- @Override
- public void onDone(T result) {
- if(loading.isShowing()) {
- loading.dismiss();
- }
- done(result);
- }
-
- @Override
- public void onError(Throwable t) {
- if(loading.isShowing()) {
- loading.dismiss();
- }
- error(t);
- }
- });
- }
-
- @Override
- public boolean isCancelled() {
- return (tabActivity instanceof SubsonicActivity && ((SubsonicActivity) tabActivity).isDestroyedCompat()) || cancelled.get();
- }
-
- @Override
- public void updateProgress(final String message) {
- if(!cancelled.get()) {
- getHandler().post(new Runnable() {
- @Override
- public void run() {
- loading.setMessage(message);
- }
- });
- }
- }
-}
+package github.daneren2005.dsub.util;
+
+import android.app.Activity;
+import android.app.ProgressDialog;
+import android.content.DialogInterface;
+
+import github.daneren2005.dsub.activity.SubsonicActivity;
+
+/**
+ * @author Sindre Mehus
+ * @version $Id$
+ */
+public abstract class LoadingTask<T> extends BackgroundTask<T> {
+
+ private final Activity tabActivity;
+ private ProgressDialog loading;
+ private final boolean cancellable;
+
+ public LoadingTask(Activity activity) {
+ super(activity);
+ tabActivity = activity;
+ this.cancellable = true;
+ }
+ public LoadingTask(Activity activity, final boolean cancellable) {
+ super(activity);
+ tabActivity = activity;
+ this.cancellable = cancellable;
+ }
+
+ @Override
+ public void execute() {
+ loading = ProgressDialog.show(tabActivity, "", "Loading. Please Wait...", true, cancellable, new DialogInterface.OnCancelListener() {
+ public void onCancel(DialogInterface dialog) {
+ cancel();
+ }
+ });
+
+ queue.offer(task = new Task() {
+ @Override
+ public void onDone(T result) {
+ if(loading.isShowing()) {
+ loading.dismiss();
+ }
+ done(result);
+ }
+
+ @Override
+ public void onError(Throwable t) {
+ if(loading.isShowing()) {
+ loading.dismiss();
+ }
+ error(t);
+ }
+ });
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return (tabActivity instanceof SubsonicActivity && ((SubsonicActivity) tabActivity).isDestroyedCompat()) || cancelled.get();
+ }
+
+ @Override
+ public void updateProgress(final String message) {
+ if(!cancelled.get()) {
+ getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ loading.setMessage(message);
+ }
+ });
+ }
+ }
+}
diff --git a/src/github/daneren2005/dsub/util/MediaRouteManager.java b/app/src/main/java/github/daneren2005/dsub/util/MediaRouteManager.java
index 9aa54c4b..9aa54c4b 100644
--- a/src/github/daneren2005/dsub/util/MediaRouteManager.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/MediaRouteManager.java
diff --git a/src/github/daneren2005/dsub/util/Notifications.java b/app/src/main/java/github/daneren2005/dsub/util/Notifications.java
index 0ff149b0..d078d77e 100644
--- a/src/github/daneren2005/dsub/util/Notifications.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/Notifications.java
@@ -1,348 +1,348 @@
-/*
- 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 2014 (C) Scott Jackson
-*/
-
-package github.daneren2005.dsub.util;
-
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.os.Build;
-import android.os.Handler;
-import android.support.v4.app.NotificationCompat;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.LinearLayout;
-import android.widget.RemoteViews;
-import android.widget.TextView;
-
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.activity.SubsonicActivity;
-import github.daneren2005.dsub.activity.SubsonicFragmentActivity;
-import github.daneren2005.dsub.domain.MusicDirectory;
-import github.daneren2005.dsub.domain.PlayerState;
-import github.daneren2005.dsub.provider.DSubWidgetProvider;
-import github.daneren2005.dsub.service.DownloadFile;
-import github.daneren2005.dsub.service.DownloadService;
-
-public final class Notifications {
- private static final String TAG = Notifications.class.getSimpleName();
-
- // Notification IDs.
- public static final int NOTIFICATION_ID_PLAYING = 100;
- public static final int NOTIFICATION_ID_DOWNLOADING = 102;
- public static final String NOTIFICATION_SYNC_GROUP = "github.daneren2005.dsub.sync";
-
- private static boolean playShowing = false;
- private static boolean downloadShowing = false;
- private static boolean downloadForeground = false;
-
- private final static Pair<Integer, Integer> NOTIFICATION_TEXT_COLORS = new Pair<Integer, Integer>();
-
- public static void showPlayingNotification(final Context context, final DownloadService downloadService, final Handler handler, MusicDirectory.Entry song) {
- // Set the icon, scrolling text and timestamp
- final Notification notification = new Notification(R.drawable.stat_notify_playing, song.getTitle(), System.currentTimeMillis());
-
- final boolean playing = downloadService.getPlayerState() == PlayerState.STARTED;
- if(playing) {
- notification.flags |= Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
- }
- boolean remote = downloadService.isRemoteEnabled();
- if (Build.VERSION.SDK_INT>= Build.VERSION_CODES.JELLY_BEAN){
- RemoteViews expandedContentView = new RemoteViews(context.getPackageName(), R.layout.notification_expanded);
- setupViews(expandedContentView ,context, song, true, playing, remote);
- notification.bigContentView = expandedContentView;
- notification.priority = Notification.PRIORITY_HIGH;
- }
- if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- notification.visibility = Notification.VISIBILITY_PUBLIC;
- }
-
- RemoteViews smallContentView = new RemoteViews(context.getPackageName(), R.layout.notification);
- setupViews(smallContentView, context, song, false, playing, remote);
- notification.contentView = smallContentView;
-
- Intent notificationIntent = new Intent(context, SubsonicFragmentActivity.class);
- notificationIntent.putExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD, true);
- notificationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- notification.contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);
-
- playShowing = true;
- if(downloadForeground && downloadShowing) {
- downloadForeground = false;
- handler.post(new Runnable() {
- @Override
- public void run() {
- downloadService.stopForeground(true);
- showDownloadingNotification(context, downloadService, handler, downloadService.getCurrentDownloading(), downloadService.getBackgroundDownloads().size());
- downloadService.startForeground(NOTIFICATION_ID_PLAYING, notification);
- }
- });
- } else {
- handler.post(new Runnable() {
- @Override
- public void run() {
- if(playing) {
- downloadService.startForeground(NOTIFICATION_ID_PLAYING, notification);
- } else {
- playShowing = false;
- NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
- downloadService.stopForeground(true);
- notificationManager.notify(NOTIFICATION_ID_PLAYING, notification);
- }
- }
- });
- }
-
- // Update widget
- DSubWidgetProvider.notifyInstances(context, downloadService, playing);
- }
-
- private static void setupViews(RemoteViews rv, Context context, MusicDirectory.Entry song, boolean expanded, boolean playing, boolean remote){
-
- // Use the same text for the ticker and the expanded notification
- String title = song.getTitle();
- String arist = song.getArtist();
- String album = song.getAlbum();
-
- // Set the album art.
- try {
- ImageLoader imageLoader = SubsonicActivity.getStaticImageLoader(context);
- Bitmap bitmap = null;
- if(imageLoader != null) {
- bitmap = imageLoader.getCachedImage(context, song, false);
- }
- if (bitmap == null) {
- // set default album art
- rv.setImageViewResource(R.id.notification_image, R.drawable.unknown_album);
- } else {
- rv.setImageViewBitmap(R.id.notification_image, bitmap);
- }
- } catch (Exception x) {
- Log.w(TAG, "Failed to get notification cover art", x);
- rv.setImageViewResource(R.id.notification_image, R.drawable.unknown_album);
- }
-
- // set the text for the notifications
- rv.setTextViewText(R.id.notification_title, title);
- rv.setTextViewText(R.id.notification_artist, arist);
- rv.setTextViewText(R.id.notification_album, album);
-
- boolean persistent = Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_PERSISTENT_NOTIFICATION, false);
- if(persistent) {
- if(expanded) {
- rv.setImageViewResource(R.id.control_pause, playing ? R.drawable.notification_pause : R.drawable.notification_play);
- } else {
- rv.setImageViewResource(R.id.control_previous, playing ? R.drawable.notification_pause : R.drawable.notification_play);
- rv.setImageViewResource(R.id.control_pause, R.drawable.notification_next);
- rv.setImageViewResource(R.id.control_next, R.drawable.notification_close);
- }
- }
-
- // Create actions for media buttons
- PendingIntent pendingIntent;
- int previous = 0, pause = 0, next = 0, close = 0;
- if(persistent && !expanded) {
- pause = R.id.control_previous;
- next = R.id.control_pause;
- close = R.id.control_next;
- } else {
- previous = R.id.control_previous;
- pause = R.id.control_pause;
- next = R.id.control_next;
- }
-
- if((remote || persistent) && close == 0 && expanded) {
- close = R.id.notification_close;
- rv.setViewVisibility(close, View.VISIBLE);
- }
-
- if(previous > 0) {
- Intent prevIntent = new Intent("KEYCODE_MEDIA_PREVIOUS");
- prevIntent.setComponent(new ComponentName(context, DownloadService.class));
- prevIntent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PREVIOUS));
- pendingIntent = PendingIntent.getService(context, 0, prevIntent, 0);
- rv.setOnClickPendingIntent(previous, pendingIntent);
- }
- if(pause > 0) {
- if(playing) {
- Intent pauseIntent = new Intent("KEYCODE_MEDIA_PLAY_PAUSE");
- pauseIntent.setComponent(new ComponentName(context, DownloadService.class));
- pauseIntent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE));
- pendingIntent = PendingIntent.getService(context, 0, pauseIntent, 0);
- rv.setOnClickPendingIntent(pause, pendingIntent);
- } else {
- Intent prevIntent = new Intent("KEYCODE_MEDIA_START");
- prevIntent.setComponent(new ComponentName(context, DownloadService.class));
- prevIntent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PLAY));
- pendingIntent = PendingIntent.getService(context, 0, prevIntent, 0);
- rv.setOnClickPendingIntent(pause, pendingIntent);
- }
- }
- if(next > 0) {
- Intent nextIntent = new Intent("KEYCODE_MEDIA_NEXT");
- nextIntent.setComponent(new ComponentName(context, DownloadService.class));
- nextIntent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_NEXT));
- pendingIntent = PendingIntent.getService(context, 0, nextIntent, 0);
- rv.setOnClickPendingIntent(next, pendingIntent);
- }
- if(close > 0) {
- Intent prevIntent = new Intent("KEYCODE_MEDIA_STOP");
- prevIntent.setComponent(new ComponentName(context, DownloadService.class));
- prevIntent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_STOP));
- pendingIntent = PendingIntent.getService(context, 0, prevIntent, 0);
- rv.setOnClickPendingIntent(close, pendingIntent);
- }
- }
-
- public static void hidePlayingNotification(final Context context, final DownloadService downloadService, Handler handler) {
- playShowing = false;
-
- // Remove notification and remove the service from the foreground
- handler.post(new Runnable() {
- @Override
- public void run() {
- downloadService.stopForeground(true);
- }
- });
-
- // Get downloadNotification in foreground if playing
- if(downloadShowing) {
- showDownloadingNotification(context, downloadService, handler, downloadService.getCurrentDownloading(), downloadService.getBackgroundDownloads().size());
- }
-
- // Update widget
- DSubWidgetProvider.notifyInstances(context, downloadService, false);
- }
-
- public static void showDownloadingNotification(final Context context, final DownloadService downloadService, Handler handler, DownloadFile file, int size) {
- Intent cancelIntent = new Intent(context, DownloadService.class);
- cancelIntent.setAction(DownloadService.CANCEL_DOWNLOADS);
- PendingIntent cancelPI = PendingIntent.getService(context, 0, cancelIntent, 0);
-
- String currentDownloading, currentSize;
- if(file != null) {
- currentDownloading = file.getSong().getTitle();
- currentSize = Util.formatLocalizedBytes(file.getEstimatedSize(), context);
- } else {
- currentDownloading = "none";
- currentSize = "0";
- }
-
- NotificationCompat.Builder builder;
- builder = new NotificationCompat.Builder(context)
- .setSmallIcon(android.R.drawable.stat_sys_download)
- .setContentTitle(context.getResources().getString(R.string.download_downloading_title, size))
- .setContentText(context.getResources().getString(R.string.download_downloading_summary, currentDownloading))
- .setStyle(new NotificationCompat.BigTextStyle()
- .bigText(context.getResources().getString(R.string.download_downloading_summary_expanded, currentDownloading, currentSize)))
- .setProgress(10, 5, true)
- .setOngoing(true)
- .addAction(R.drawable.notification_close,
- context.getResources().getString(R.string.common_cancel),
- cancelPI);
-
- Intent notificationIntent = new Intent(context, SubsonicFragmentActivity.class);
- notificationIntent.putExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD, true);
- notificationIntent.putExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD_VIEW, true);
- notificationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- builder.setContentIntent(PendingIntent.getActivity(context, 1, notificationIntent, 0));
-
- final Notification notification = builder.build();
- downloadShowing = true;
- if(playShowing) {
- NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
- notificationManager.notify(NOTIFICATION_ID_DOWNLOADING, notification);
- } else {
- downloadForeground = true;
- handler.post(new Runnable() {
- @Override
- public void run() {
- downloadService.startForeground(NOTIFICATION_ID_DOWNLOADING, notification);
- }
- });
- }
-
- }
- public static void hideDownloadingNotification(final Context context, final DownloadService downloadService, Handler handler) {
- downloadShowing = false;
- if(playShowing) {
- NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
- notificationManager.cancel(NOTIFICATION_ID_DOWNLOADING);
- } else {
- downloadForeground = false;
- handler.post(new Runnable() {
- @Override
- public void run() {
- downloadService.stopForeground(true);
- }
- });
- }
- }
-
- public static void showSyncNotification(final Context context, int stringId, String extra) {
- if(Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_SYNC_NOTIFICATION, true)) {
- if(extra == null) {
- extra = "";
- }
-
- NotificationCompat.Builder builder;
- builder = new NotificationCompat.Builder(context)
- .setSmallIcon(R.drawable.stat_notify_sync)
- .setContentTitle(context.getResources().getString(stringId))
- .setContentText(extra)
- .setStyle(new NotificationCompat.BigTextStyle().bigText(extra.replace(", ", "\n")))
- .setOngoing(false)
- .setGroup(NOTIFICATION_SYNC_GROUP)
- .setPriority(NotificationCompat.PRIORITY_LOW)
- .setAutoCancel(true);
-
- Intent notificationIntent = new Intent(context, SubsonicFragmentActivity.class);
- notificationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
-
- String tab = null, type = null;
- switch(stringId) {
- case R.string.sync_new_albums:
- type = "newest";
- break;
- case R.string.sync_new_playlists:
- tab = "Playlist";
- break;
- case R.string.sync_new_podcasts:
- tab = "Podcast";
- break;
- case R.string.sync_new_starred:
- type = "starred";
- break;
- }
- if(tab != null) {
- notificationIntent.putExtra(Constants.INTENT_EXTRA_FRAGMENT_TYPE, tab);
- }
- if(type != null) {
- notificationIntent.putExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE, type);
- }
-
- builder.setContentIntent(PendingIntent.getActivity(context, stringId, notificationIntent, 0));
-
- NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
- notificationManager.notify(stringId, builder.build());
- }
- }
-}
+/*
+ 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 2014 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.util;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.os.Build;
+import android.os.Handler;
+import android.support.v4.app.NotificationCompat;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.RemoteViews;
+import android.widget.TextView;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.activity.SubsonicActivity;
+import github.daneren2005.dsub.activity.SubsonicFragmentActivity;
+import github.daneren2005.dsub.domain.MusicDirectory;
+import github.daneren2005.dsub.domain.PlayerState;
+import github.daneren2005.dsub.provider.DSubWidgetProvider;
+import github.daneren2005.dsub.service.DownloadFile;
+import github.daneren2005.dsub.service.DownloadService;
+
+public final class Notifications {
+ private static final String TAG = Notifications.class.getSimpleName();
+
+ // Notification IDs.
+ public static final int NOTIFICATION_ID_PLAYING = 100;
+ public static final int NOTIFICATION_ID_DOWNLOADING = 102;
+ public static final String NOTIFICATION_SYNC_GROUP = "github.daneren2005.dsub.sync";
+
+ private static boolean playShowing = false;
+ private static boolean downloadShowing = false;
+ private static boolean downloadForeground = false;
+
+ private final static Pair<Integer, Integer> NOTIFICATION_TEXT_COLORS = new Pair<Integer, Integer>();
+
+ public static void showPlayingNotification(final Context context, final DownloadService downloadService, final Handler handler, MusicDirectory.Entry song) {
+ // Set the icon, scrolling text and timestamp
+ final Notification notification = new Notification(R.drawable.stat_notify_playing, song.getTitle(), System.currentTimeMillis());
+
+ final boolean playing = downloadService.getPlayerState() == PlayerState.STARTED;
+ if(playing) {
+ notification.flags |= Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
+ }
+ boolean remote = downloadService.isRemoteEnabled();
+ if (Build.VERSION.SDK_INT>= Build.VERSION_CODES.JELLY_BEAN){
+ RemoteViews expandedContentView = new RemoteViews(context.getPackageName(), R.layout.notification_expanded);
+ setupViews(expandedContentView ,context, song, true, playing, remote);
+ notification.bigContentView = expandedContentView;
+ notification.priority = Notification.PRIORITY_HIGH;
+ }
+ if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ notification.visibility = Notification.VISIBILITY_PUBLIC;
+ }
+
+ RemoteViews smallContentView = new RemoteViews(context.getPackageName(), R.layout.notification);
+ setupViews(smallContentView, context, song, false, playing, remote);
+ notification.contentView = smallContentView;
+
+ Intent notificationIntent = new Intent(context, SubsonicFragmentActivity.class);
+ notificationIntent.putExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD, true);
+ notificationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ notification.contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);
+
+ playShowing = true;
+ if(downloadForeground && downloadShowing) {
+ downloadForeground = false;
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ downloadService.stopForeground(true);
+ showDownloadingNotification(context, downloadService, handler, downloadService.getCurrentDownloading(), downloadService.getBackgroundDownloads().size());
+ downloadService.startForeground(NOTIFICATION_ID_PLAYING, notification);
+ }
+ });
+ } else {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ if(playing) {
+ downloadService.startForeground(NOTIFICATION_ID_PLAYING, notification);
+ } else {
+ playShowing = false;
+ NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+ downloadService.stopForeground(true);
+ notificationManager.notify(NOTIFICATION_ID_PLAYING, notification);
+ }
+ }
+ });
+ }
+
+ // Update widget
+ DSubWidgetProvider.notifyInstances(context, downloadService, playing);
+ }
+
+ private static void setupViews(RemoteViews rv, Context context, MusicDirectory.Entry song, boolean expanded, boolean playing, boolean remote){
+
+ // Use the same text for the ticker and the expanded notification
+ String title = song.getTitle();
+ String arist = song.getArtist();
+ String album = song.getAlbum();
+
+ // Set the album art.
+ try {
+ ImageLoader imageLoader = SubsonicActivity.getStaticImageLoader(context);
+ Bitmap bitmap = null;
+ if(imageLoader != null) {
+ bitmap = imageLoader.getCachedImage(context, song, false);
+ }
+ if (bitmap == null) {
+ // set default album art
+ rv.setImageViewResource(R.id.notification_image, R.drawable.unknown_album);
+ } else {
+ rv.setImageViewBitmap(R.id.notification_image, bitmap);
+ }
+ } catch (Exception x) {
+ Log.w(TAG, "Failed to get notification cover art", x);
+ rv.setImageViewResource(R.id.notification_image, R.drawable.unknown_album);
+ }
+
+ // set the text for the notifications
+ rv.setTextViewText(R.id.notification_title, title);
+ rv.setTextViewText(R.id.notification_artist, arist);
+ rv.setTextViewText(R.id.notification_album, album);
+
+ boolean persistent = Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_PERSISTENT_NOTIFICATION, false);
+ if(persistent) {
+ if(expanded) {
+ rv.setImageViewResource(R.id.control_pause, playing ? R.drawable.notification_pause : R.drawable.notification_play);
+ } else {
+ rv.setImageViewResource(R.id.control_previous, playing ? R.drawable.notification_pause : R.drawable.notification_play);
+ rv.setImageViewResource(R.id.control_pause, R.drawable.notification_next);
+ rv.setImageViewResource(R.id.control_next, R.drawable.notification_close);
+ }
+ }
+
+ // Create actions for media buttons
+ PendingIntent pendingIntent;
+ int previous = 0, pause = 0, next = 0, close = 0;
+ if(persistent && !expanded) {
+ pause = R.id.control_previous;
+ next = R.id.control_pause;
+ close = R.id.control_next;
+ } else {
+ previous = R.id.control_previous;
+ pause = R.id.control_pause;
+ next = R.id.control_next;
+ }
+
+ if((remote || persistent) && close == 0 && expanded) {
+ close = R.id.notification_close;
+ rv.setViewVisibility(close, View.VISIBLE);
+ }
+
+ if(previous > 0) {
+ Intent prevIntent = new Intent("KEYCODE_MEDIA_PREVIOUS");
+ prevIntent.setComponent(new ComponentName(context, DownloadService.class));
+ prevIntent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PREVIOUS));
+ pendingIntent = PendingIntent.getService(context, 0, prevIntent, 0);
+ rv.setOnClickPendingIntent(previous, pendingIntent);
+ }
+ if(pause > 0) {
+ if(playing) {
+ Intent pauseIntent = new Intent("KEYCODE_MEDIA_PLAY_PAUSE");
+ pauseIntent.setComponent(new ComponentName(context, DownloadService.class));
+ pauseIntent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE));
+ pendingIntent = PendingIntent.getService(context, 0, pauseIntent, 0);
+ rv.setOnClickPendingIntent(pause, pendingIntent);
+ } else {
+ Intent prevIntent = new Intent("KEYCODE_MEDIA_START");
+ prevIntent.setComponent(new ComponentName(context, DownloadService.class));
+ prevIntent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PLAY));
+ pendingIntent = PendingIntent.getService(context, 0, prevIntent, 0);
+ rv.setOnClickPendingIntent(pause, pendingIntent);
+ }
+ }
+ if(next > 0) {
+ Intent nextIntent = new Intent("KEYCODE_MEDIA_NEXT");
+ nextIntent.setComponent(new ComponentName(context, DownloadService.class));
+ nextIntent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_NEXT));
+ pendingIntent = PendingIntent.getService(context, 0, nextIntent, 0);
+ rv.setOnClickPendingIntent(next, pendingIntent);
+ }
+ if(close > 0) {
+ Intent prevIntent = new Intent("KEYCODE_MEDIA_STOP");
+ prevIntent.setComponent(new ComponentName(context, DownloadService.class));
+ prevIntent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_STOP));
+ pendingIntent = PendingIntent.getService(context, 0, prevIntent, 0);
+ rv.setOnClickPendingIntent(close, pendingIntent);
+ }
+ }
+
+ public static void hidePlayingNotification(final Context context, final DownloadService downloadService, Handler handler) {
+ playShowing = false;
+
+ // Remove notification and remove the service from the foreground
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ downloadService.stopForeground(true);
+ }
+ });
+
+ // Get downloadNotification in foreground if playing
+ if(downloadShowing) {
+ showDownloadingNotification(context, downloadService, handler, downloadService.getCurrentDownloading(), downloadService.getBackgroundDownloads().size());
+ }
+
+ // Update widget
+ DSubWidgetProvider.notifyInstances(context, downloadService, false);
+ }
+
+ public static void showDownloadingNotification(final Context context, final DownloadService downloadService, Handler handler, DownloadFile file, int size) {
+ Intent cancelIntent = new Intent(context, DownloadService.class);
+ cancelIntent.setAction(DownloadService.CANCEL_DOWNLOADS);
+ PendingIntent cancelPI = PendingIntent.getService(context, 0, cancelIntent, 0);
+
+ String currentDownloading, currentSize;
+ if(file != null) {
+ currentDownloading = file.getSong().getTitle();
+ currentSize = Util.formatLocalizedBytes(file.getEstimatedSize(), context);
+ } else {
+ currentDownloading = "none";
+ currentSize = "0";
+ }
+
+ NotificationCompat.Builder builder;
+ builder = new NotificationCompat.Builder(context)
+ .setSmallIcon(android.R.drawable.stat_sys_download)
+ .setContentTitle(context.getResources().getString(R.string.download_downloading_title, size))
+ .setContentText(context.getResources().getString(R.string.download_downloading_summary, currentDownloading))
+ .setStyle(new NotificationCompat.BigTextStyle()
+ .bigText(context.getResources().getString(R.string.download_downloading_summary_expanded, currentDownloading, currentSize)))
+ .setProgress(10, 5, true)
+ .setOngoing(true)
+ .addAction(R.drawable.notification_close,
+ context.getResources().getString(R.string.common_cancel),
+ cancelPI);
+
+ Intent notificationIntent = new Intent(context, SubsonicFragmentActivity.class);
+ notificationIntent.putExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD, true);
+ notificationIntent.putExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD_VIEW, true);
+ notificationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ builder.setContentIntent(PendingIntent.getActivity(context, 1, notificationIntent, 0));
+
+ final Notification notification = builder.build();
+ downloadShowing = true;
+ if(playShowing) {
+ NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+ notificationManager.notify(NOTIFICATION_ID_DOWNLOADING, notification);
+ } else {
+ downloadForeground = true;
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ downloadService.startForeground(NOTIFICATION_ID_DOWNLOADING, notification);
+ }
+ });
+ }
+
+ }
+ public static void hideDownloadingNotification(final Context context, final DownloadService downloadService, Handler handler) {
+ downloadShowing = false;
+ if(playShowing) {
+ NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+ notificationManager.cancel(NOTIFICATION_ID_DOWNLOADING);
+ } else {
+ downloadForeground = false;
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ downloadService.stopForeground(true);
+ }
+ });
+ }
+ }
+
+ public static void showSyncNotification(final Context context, int stringId, String extra) {
+ if(Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_SYNC_NOTIFICATION, true)) {
+ if(extra == null) {
+ extra = "";
+ }
+
+ NotificationCompat.Builder builder;
+ builder = new NotificationCompat.Builder(context)
+ .setSmallIcon(R.drawable.stat_notify_sync)
+ .setContentTitle(context.getResources().getString(stringId))
+ .setContentText(extra)
+ .setStyle(new NotificationCompat.BigTextStyle().bigText(extra.replace(", ", "\n")))
+ .setOngoing(false)
+ .setGroup(NOTIFICATION_SYNC_GROUP)
+ .setPriority(NotificationCompat.PRIORITY_LOW)
+ .setAutoCancel(true);
+
+ Intent notificationIntent = new Intent(context, SubsonicFragmentActivity.class);
+ notificationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+
+ String tab = null, type = null;
+ switch(stringId) {
+ case R.string.sync_new_albums:
+ type = "newest";
+ break;
+ case R.string.sync_new_playlists:
+ tab = "Playlist";
+ break;
+ case R.string.sync_new_podcasts:
+ tab = "Podcast";
+ break;
+ case R.string.sync_new_starred:
+ type = "starred";
+ break;
+ }
+ if(tab != null) {
+ notificationIntent.putExtra(Constants.INTENT_EXTRA_FRAGMENT_TYPE, tab);
+ }
+ if(type != null) {
+ notificationIntent.putExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE, type);
+ }
+
+ builder.setContentIntent(PendingIntent.getActivity(context, stringId, notificationIntent, 0));
+
+ NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+ notificationManager.notify(stringId, builder.build());
+ }
+ }
+}
diff --git a/src/github/daneren2005/dsub/util/Pair.java b/app/src/main/java/github/daneren2005/dsub/util/Pair.java
index 54386a62..54386a62 100644
--- a/src/github/daneren2005/dsub/util/Pair.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/Pair.java
diff --git a/src/github/daneren2005/dsub/util/ProgressListener.java b/app/src/main/java/github/daneren2005/dsub/util/ProgressListener.java
index c6d58f42..c6d58f42 100644
--- a/src/github/daneren2005/dsub/util/ProgressListener.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/ProgressListener.java
diff --git a/src/github/daneren2005/dsub/util/SettingsBackupAgent.java b/app/src/main/java/github/daneren2005/dsub/util/SettingsBackupAgent.java
index 7eb6d137..7eb6d137 100644
--- a/src/github/daneren2005/dsub/util/SettingsBackupAgent.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/SettingsBackupAgent.java
diff --git a/src/github/daneren2005/dsub/util/ShufflePlayBuffer.java b/app/src/main/java/github/daneren2005/dsub/util/ShufflePlayBuffer.java
index 7da35f77..7da35f77 100644
--- a/src/github/daneren2005/dsub/util/ShufflePlayBuffer.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/ShufflePlayBuffer.java
diff --git a/src/github/daneren2005/dsub/util/SilentBackgroundTask.java b/app/src/main/java/github/daneren2005/dsub/util/SilentBackgroundTask.java
index b99b7e0e..b99b7e0e 100644
--- a/src/github/daneren2005/dsub/util/SilentBackgroundTask.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/SilentBackgroundTask.java
diff --git a/src/github/daneren2005/dsub/util/SimpleServiceBinder.java b/app/src/main/java/github/daneren2005/dsub/util/SimpleServiceBinder.java
index 9c0b36a9..9c0b36a9 100644
--- a/src/github/daneren2005/dsub/util/SimpleServiceBinder.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/SimpleServiceBinder.java
diff --git a/src/github/daneren2005/dsub/util/SyncUtil.java b/app/src/main/java/github/daneren2005/dsub/util/SyncUtil.java
index 15fa2d47..a369715f 100644
--- a/src/github/daneren2005/dsub/util/SyncUtil.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/SyncUtil.java
@@ -1,222 +1,222 @@
-package github.daneren2005.dsub.util;
-
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.support.v4.app.NotificationCompat;
-
-import java.io.File;
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.List;
-
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.activity.SubsonicFragmentActivity;
-
-/**
- * Created by Scott on 11/24/13.
- */
-public final class SyncUtil {
- private static String TAG = SyncUtil.class.getSimpleName();
- private static ArrayList<SyncSet> syncedPlaylists;
- private static ArrayList<SyncSet> syncedPodcasts;
- private static String url;
-
- private static void checkRestURL(Context context) {
- int instance = Util.getActiveServer(context);
- String newURL = Util.getRestUrl(context, null, instance, false);
- if(url == null || !url.equals(newURL)) {
- syncedPlaylists = null;
- syncedPodcasts = null;
- url = newURL;
- }
- }
-
- // Playlist sync
- public static boolean isSyncedPlaylist(Context context, String playlistId) {
- checkRestURL(context);
- if(syncedPlaylists == null) {
- syncedPlaylists = getSyncedPlaylists(context);
- }
- return syncedPlaylists.contains(new SyncSet(playlistId));
- }
- public static ArrayList<SyncSet> getSyncedPlaylists(Context context) {
- return getSyncedPlaylists(context, Util.getActiveServer(context));
- }
- public static ArrayList<SyncSet> getSyncedPlaylists(Context context, int instance) {
- String syncFile = getPlaylistSyncFile(context, instance);
- ArrayList<SyncSet> playlists = FileUtil.deserializeCompressed(context, syncFile, ArrayList.class);
- if(playlists == null) {
- playlists = new ArrayList<SyncSet>();
-
- // Try to convert old style into new style
- ArrayList<String> oldPlaylists = FileUtil.deserialize(context, syncFile, ArrayList.class);
- // If exists, time to convert!
- if(oldPlaylists != null) {
- for(String id: oldPlaylists) {
- playlists.add(new SyncSet(id));
- }
-
- FileUtil.serializeCompressed(context, playlists, syncFile);
- }
- }
- return playlists;
- }
- public static void setSyncedPlaylists(Context context, int instance, ArrayList<SyncSet> playlists) {
- FileUtil.serializeCompressed(context, playlists, getPlaylistSyncFile(context, instance));
- }
- public static void addSyncedPlaylist(Context context, String playlistId) {
- String playlistFile = getPlaylistSyncFile(context);
- ArrayList<SyncSet> playlists = getSyncedPlaylists(context);
- SyncSet set = new SyncSet(playlistId);
- if(!playlists.contains(set)) {
- playlists.add(set);
- }
- FileUtil.serializeCompressed(context, playlists, playlistFile);
- syncedPlaylists = playlists;
- }
- public static void removeSyncedPlaylist(Context context, String playlistId) {
- int instance = Util.getActiveServer(context);
- removeSyncedPlaylist(context, playlistId, instance);
- }
- public static void removeSyncedPlaylist(Context context, String playlistId, int instance) {
- String playlistFile = getPlaylistSyncFile(context, instance);
- ArrayList<SyncSet> playlists = getSyncedPlaylists(context, instance);
- SyncSet set = new SyncSet(playlistId);
- if(playlists.contains(set)) {
- playlists.remove(set);
- FileUtil.serializeCompressed(context, playlists, playlistFile);
- syncedPlaylists = playlists;
- }
- }
- public static String getPlaylistSyncFile(Context context) {
- int instance = Util.getActiveServer(context);
- return getPlaylistSyncFile(context, instance);
- }
- public static String getPlaylistSyncFile(Context context, int instance) {
- return "sync-playlist-" + (Util.getRestUrl(context, null, instance, false)).hashCode() + ".ser";
- }
-
- // Podcast sync
- public static boolean isSyncedPodcast(Context context, String podcastId) {
- checkRestURL(context);
- if(syncedPodcasts == null) {
- syncedPodcasts = getSyncedPodcasts(context);
- }
- return syncedPodcasts.contains(new SyncSet(podcastId));
- }
- public static ArrayList<SyncSet> getSyncedPodcasts(Context context) {
- return getSyncedPodcasts(context, Util.getActiveServer(context));
- }
- public static ArrayList<SyncSet> getSyncedPodcasts(Context context, int instance) {
- ArrayList<SyncSet> podcasts = FileUtil.deserialize(context, getPodcastSyncFile(context, instance), ArrayList.class);
- if(podcasts == null) {
- podcasts = new ArrayList<SyncSet>();
- }
- return podcasts;
- }
- public static void addSyncedPodcast(Context context, String podcastId, List<String> synced) {
- String podcastFile = getPodcastSyncFile(context);
- ArrayList<SyncSet> podcasts = getSyncedPodcasts(context);
- SyncSet set = new SyncSet(podcastId, synced);
- if(!podcasts.contains(set)) {
- podcasts.add(set);
- }
- FileUtil.serialize(context, podcasts, podcastFile);
- syncedPodcasts = podcasts;
- }
- public static void removeSyncedPodcast(Context context, String podcastId) {
- removeSyncedPodcast(context, podcastId, Util.getActiveServer(context));
- }
- public static void removeSyncedPodcast(Context context, String podcastId, int instance) {
- String podcastFile = getPodcastSyncFile(context, instance);
- ArrayList<SyncSet> podcasts = getSyncedPodcasts(context, instance);
- SyncSet set = new SyncSet(podcastId);
- if(podcasts.contains(set)) {
- podcasts.remove(set);
- FileUtil.serialize(context, podcasts, podcastFile);
- syncedPodcasts = podcasts;
- }
- }
- public static String getPodcastSyncFile(Context context) {
- int instance = Util.getActiveServer(context);
- return getPodcastSyncFile(context, instance);
- }
- public static String getPodcastSyncFile(Context context, int instance) {
- return "sync-podcast-" + (Util.getRestUrl(context, null, instance, false)).hashCode() + ".ser";
- }
-
- // Starred
- public static ArrayList<String> getSyncedStarred(Context context, int instance) {
- ArrayList<String> list = FileUtil.deserializeCompressed(context, getStarredSyncFile(context, instance), ArrayList.class);
- if(list == null) {
- list = new ArrayList<String>();
- }
- return list;
- }
- public static void setSyncedStarred(ArrayList<String> syncedList, Context context, int instance) {
- FileUtil.serializeCompressed(context, syncedList, SyncUtil.getStarredSyncFile(context, instance));
- }
- public static String getStarredSyncFile(Context context, int instance) {
- return "sync-starred-" + (Util.getRestUrl(context, null, instance, false)).hashCode() + ".ser";
- }
-
- // Most Recently Added
- public static ArrayList<String> getSyncedMostRecent(Context context, int instance) {
- ArrayList<String> list = FileUtil.deserialize(context, getMostRecentSyncFile(context, instance), ArrayList.class);
- if(list == null) {
- list = new ArrayList<String>();
- }
- return list;
- }
- public static void removeMostRecentSyncFiles(Context context) {
- int total = Util.getServerCount(context);
- for(int i = 0; i < total; i++) {
- File file = new File(context.getCacheDir(), getMostRecentSyncFile(context, i));
- file.delete();
- }
- }
- public static String getMostRecentSyncFile(Context context, int instance) {
- return "sync-most_recent-" + (Util.getRestUrl(context, null, instance, false)).hashCode() + ".ser";
- }
-
- public static String joinNames(List<String> names) {
- StringBuilder builder = new StringBuilder();
- for (String val : names) {
- builder.append(val).append(", ");
- }
- builder.setLength(builder.length() - 2);
- return builder.toString();
- }
-
- public static class SyncSet implements Serializable {
- public String id;
- public List<String> synced;
-
- protected SyncSet() {
-
- }
- public SyncSet(String id) {
- this.id = id;
- }
- public SyncSet(String id, List<String> synced) {
- this.id = id;
- this.synced = synced;
- }
-
- @Override
- public boolean equals(Object obj) {
- if(obj instanceof SyncSet) {
- return this.id.equals(((SyncSet)obj).id);
- } else {
- return false;
- }
- }
-
- @Override
- public int hashCode() {
- return id.hashCode();
- }
- }
-}
+package github.daneren2005.dsub.util;
+
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.support.v4.app.NotificationCompat;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.activity.SubsonicFragmentActivity;
+
+/**
+ * Created by Scott on 11/24/13.
+ */
+public final class SyncUtil {
+ private static String TAG = SyncUtil.class.getSimpleName();
+ private static ArrayList<SyncSet> syncedPlaylists;
+ private static ArrayList<SyncSet> syncedPodcasts;
+ private static String url;
+
+ private static void checkRestURL(Context context) {
+ int instance = Util.getActiveServer(context);
+ String newURL = Util.getRestUrl(context, null, instance, false);
+ if(url == null || !url.equals(newURL)) {
+ syncedPlaylists = null;
+ syncedPodcasts = null;
+ url = newURL;
+ }
+ }
+
+ // Playlist sync
+ public static boolean isSyncedPlaylist(Context context, String playlistId) {
+ checkRestURL(context);
+ if(syncedPlaylists == null) {
+ syncedPlaylists = getSyncedPlaylists(context);
+ }
+ return syncedPlaylists.contains(new SyncSet(playlistId));
+ }
+ public static ArrayList<SyncSet> getSyncedPlaylists(Context context) {
+ return getSyncedPlaylists(context, Util.getActiveServer(context));
+ }
+ public static ArrayList<SyncSet> getSyncedPlaylists(Context context, int instance) {
+ String syncFile = getPlaylistSyncFile(context, instance);
+ ArrayList<SyncSet> playlists = FileUtil.deserializeCompressed(context, syncFile, ArrayList.class);
+ if(playlists == null) {
+ playlists = new ArrayList<SyncSet>();
+
+ // Try to convert old style into new style
+ ArrayList<String> oldPlaylists = FileUtil.deserialize(context, syncFile, ArrayList.class);
+ // If exists, time to convert!
+ if(oldPlaylists != null) {
+ for(String id: oldPlaylists) {
+ playlists.add(new SyncSet(id));
+ }
+
+ FileUtil.serializeCompressed(context, playlists, syncFile);
+ }
+ }
+ return playlists;
+ }
+ public static void setSyncedPlaylists(Context context, int instance, ArrayList<SyncSet> playlists) {
+ FileUtil.serializeCompressed(context, playlists, getPlaylistSyncFile(context, instance));
+ }
+ public static void addSyncedPlaylist(Context context, String playlistId) {
+ String playlistFile = getPlaylistSyncFile(context);
+ ArrayList<SyncSet> playlists = getSyncedPlaylists(context);
+ SyncSet set = new SyncSet(playlistId);
+ if(!playlists.contains(set)) {
+ playlists.add(set);
+ }
+ FileUtil.serializeCompressed(context, playlists, playlistFile);
+ syncedPlaylists = playlists;
+ }
+ public static void removeSyncedPlaylist(Context context, String playlistId) {
+ int instance = Util.getActiveServer(context);
+ removeSyncedPlaylist(context, playlistId, instance);
+ }
+ public static void removeSyncedPlaylist(Context context, String playlistId, int instance) {
+ String playlistFile = getPlaylistSyncFile(context, instance);
+ ArrayList<SyncSet> playlists = getSyncedPlaylists(context, instance);
+ SyncSet set = new SyncSet(playlistId);
+ if(playlists.contains(set)) {
+ playlists.remove(set);
+ FileUtil.serializeCompressed(context, playlists, playlistFile);
+ syncedPlaylists = playlists;
+ }
+ }
+ public static String getPlaylistSyncFile(Context context) {
+ int instance = Util.getActiveServer(context);
+ return getPlaylistSyncFile(context, instance);
+ }
+ public static String getPlaylistSyncFile(Context context, int instance) {
+ return "sync-playlist-" + (Util.getRestUrl(context, null, instance, false)).hashCode() + ".ser";
+ }
+
+ // Podcast sync
+ public static boolean isSyncedPodcast(Context context, String podcastId) {
+ checkRestURL(context);
+ if(syncedPodcasts == null) {
+ syncedPodcasts = getSyncedPodcasts(context);
+ }
+ return syncedPodcasts.contains(new SyncSet(podcastId));
+ }
+ public static ArrayList<SyncSet> getSyncedPodcasts(Context context) {
+ return getSyncedPodcasts(context, Util.getActiveServer(context));
+ }
+ public static ArrayList<SyncSet> getSyncedPodcasts(Context context, int instance) {
+ ArrayList<SyncSet> podcasts = FileUtil.deserialize(context, getPodcastSyncFile(context, instance), ArrayList.class);
+ if(podcasts == null) {
+ podcasts = new ArrayList<SyncSet>();
+ }
+ return podcasts;
+ }
+ public static void addSyncedPodcast(Context context, String podcastId, List<String> synced) {
+ String podcastFile = getPodcastSyncFile(context);
+ ArrayList<SyncSet> podcasts = getSyncedPodcasts(context);
+ SyncSet set = new SyncSet(podcastId, synced);
+ if(!podcasts.contains(set)) {
+ podcasts.add(set);
+ }
+ FileUtil.serialize(context, podcasts, podcastFile);
+ syncedPodcasts = podcasts;
+ }
+ public static void removeSyncedPodcast(Context context, String podcastId) {
+ removeSyncedPodcast(context, podcastId, Util.getActiveServer(context));
+ }
+ public static void removeSyncedPodcast(Context context, String podcastId, int instance) {
+ String podcastFile = getPodcastSyncFile(context, instance);
+ ArrayList<SyncSet> podcasts = getSyncedPodcasts(context, instance);
+ SyncSet set = new SyncSet(podcastId);
+ if(podcasts.contains(set)) {
+ podcasts.remove(set);
+ FileUtil.serialize(context, podcasts, podcastFile);
+ syncedPodcasts = podcasts;
+ }
+ }
+ public static String getPodcastSyncFile(Context context) {
+ int instance = Util.getActiveServer(context);
+ return getPodcastSyncFile(context, instance);
+ }
+ public static String getPodcastSyncFile(Context context, int instance) {
+ return "sync-podcast-" + (Util.getRestUrl(context, null, instance, false)).hashCode() + ".ser";
+ }
+
+ // Starred
+ public static ArrayList<String> getSyncedStarred(Context context, int instance) {
+ ArrayList<String> list = FileUtil.deserializeCompressed(context, getStarredSyncFile(context, instance), ArrayList.class);
+ if(list == null) {
+ list = new ArrayList<String>();
+ }
+ return list;
+ }
+ public static void setSyncedStarred(ArrayList<String> syncedList, Context context, int instance) {
+ FileUtil.serializeCompressed(context, syncedList, SyncUtil.getStarredSyncFile(context, instance));
+ }
+ public static String getStarredSyncFile(Context context, int instance) {
+ return "sync-starred-" + (Util.getRestUrl(context, null, instance, false)).hashCode() + ".ser";
+ }
+
+ // Most Recently Added
+ public static ArrayList<String> getSyncedMostRecent(Context context, int instance) {
+ ArrayList<String> list = FileUtil.deserialize(context, getMostRecentSyncFile(context, instance), ArrayList.class);
+ if(list == null) {
+ list = new ArrayList<String>();
+ }
+ return list;
+ }
+ public static void removeMostRecentSyncFiles(Context context) {
+ int total = Util.getServerCount(context);
+ for(int i = 0; i < total; i++) {
+ File file = new File(context.getCacheDir(), getMostRecentSyncFile(context, i));
+ file.delete();
+ }
+ }
+ public static String getMostRecentSyncFile(Context context, int instance) {
+ return "sync-most_recent-" + (Util.getRestUrl(context, null, instance, false)).hashCode() + ".ser";
+ }
+
+ public static String joinNames(List<String> names) {
+ StringBuilder builder = new StringBuilder();
+ for (String val : names) {
+ builder.append(val).append(", ");
+ }
+ builder.setLength(builder.length() - 2);
+ return builder.toString();
+ }
+
+ public static class SyncSet implements Serializable {
+ public String id;
+ public List<String> synced;
+
+ protected SyncSet() {
+
+ }
+ public SyncSet(String id) {
+ this.id = id;
+ }
+ public SyncSet(String id, List<String> synced) {
+ this.id = id;
+ this.synced = synced;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if(obj instanceof SyncSet) {
+ return this.id.equals(((SyncSet)obj).id);
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return id.hashCode();
+ }
+ }
+}
diff --git a/src/github/daneren2005/dsub/util/TabBackgroundTask.java b/app/src/main/java/github/daneren2005/dsub/util/TabBackgroundTask.java
index b0a24b28..759e893a 100644
--- a/src/github/daneren2005/dsub/util/TabBackgroundTask.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/TabBackgroundTask.java
@@ -1,51 +1,51 @@
-package github.daneren2005.dsub.util;
-
-import github.daneren2005.dsub.fragments.SubsonicFragment;
-
-/**
- * @author Sindre Mehus
- * @version $Id$
- */
-public abstract class TabBackgroundTask<T> extends BackgroundTask<T> {
-
- private final SubsonicFragment tabFragment;
-
- public TabBackgroundTask(SubsonicFragment fragment) {
- super(fragment.getActivity());
- tabFragment = fragment;
- }
-
- @Override
- public void execute() {
- tabFragment.setProgressVisible(true);
-
- queue.offer(task = new Task() {
- @Override
- public void onDone(T result) {
- tabFragment.setProgressVisible(false);
- done(result);
- }
-
- @Override
- public void onError(Throwable t) {
- tabFragment.setProgressVisible(false);
- error(t);
- }
- });
- }
-
- @Override
- public boolean isCancelled() {
- return !tabFragment.isAdded() || cancelled.get();
- }
-
- @Override
- public void updateProgress(final String message) {
- getHandler().post(new Runnable() {
- @Override
- public void run() {
- tabFragment.updateProgress(message);
- }
- });
- }
-}
+package github.daneren2005.dsub.util;
+
+import github.daneren2005.dsub.fragments.SubsonicFragment;
+
+/**
+ * @author Sindre Mehus
+ * @version $Id$
+ */
+public abstract class TabBackgroundTask<T> extends BackgroundTask<T> {
+
+ private final SubsonicFragment tabFragment;
+
+ public TabBackgroundTask(SubsonicFragment fragment) {
+ super(fragment.getActivity());
+ tabFragment = fragment;
+ }
+
+ @Override
+ public void execute() {
+ tabFragment.setProgressVisible(true);
+
+ queue.offer(task = new Task() {
+ @Override
+ public void onDone(T result) {
+ tabFragment.setProgressVisible(false);
+ done(result);
+ }
+
+ @Override
+ public void onError(Throwable t) {
+ tabFragment.setProgressVisible(false);
+ error(t);
+ }
+ });
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return !tabFragment.isAdded() || cancelled.get();
+ }
+
+ @Override
+ public void updateProgress(final String message) {
+ getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ tabFragment.updateProgress(message);
+ }
+ });
+ }
+}
diff --git a/src/github/daneren2005/dsub/util/TimeLimitedCache.java b/app/src/main/java/github/daneren2005/dsub/util/TimeLimitedCache.java
index 8b7df783..8b7df783 100644
--- a/src/github/daneren2005/dsub/util/TimeLimitedCache.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/TimeLimitedCache.java
diff --git a/src/github/daneren2005/dsub/util/UserUtil.java b/app/src/main/java/github/daneren2005/dsub/util/UserUtil.java
index c5da93d3..29618424 100644
--- a/src/github/daneren2005/dsub/util/UserUtil.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/UserUtil.java
@@ -1,452 +1,452 @@
-/*
- 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 2014 (C) Scott Jackson
-*/
-
-package github.daneren2005.dsub.util;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.SharedPreferences;
-import android.support.v7.app.ActionBarActivity;
-import android.util.Log;
-import android.view.View;
-import android.widget.ArrayAdapter;
-import android.widget.ListView;
-import android.widget.TextView;
-
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.domain.User;
-import github.daneren2005.dsub.fragments.SubsonicFragment;
-import github.daneren2005.dsub.service.DownloadService;
-import github.daneren2005.dsub.service.MusicService;
-import github.daneren2005.dsub.service.MusicServiceFactory;
-import github.daneren2005.dsub.service.OfflineException;
-import github.daneren2005.dsub.service.ServerTooOldException;
-import github.daneren2005.dsub.adapter.SettingsAdapter;
-
-public final class UserUtil {
- private static final String TAG = UserUtil.class.getSimpleName();
- private static final long MIN_VERIFY_DURATION = 1000L * 60L * 60L;
-
- private static int instance = -1;
- private static User currentUser;
- private static long lastVerifiedTime = 0;
-
-
- public static void refreshCurrentUser(Context context, boolean forceRefresh) {
- refreshCurrentUser(context, forceRefresh, false);
- }
- public static void refreshCurrentUser(Context context, boolean forceRefresh, boolean unAuth) {
- currentUser = null;
- if(unAuth) {
- lastVerifiedTime = 0;
- }
- seedCurrentUser(context, forceRefresh);
- }
-
- public static void seedCurrentUser(Context context) {
- seedCurrentUser(context, false);
- }
- public static void seedCurrentUser(final Context context, final boolean refresh) {
- // Only try to seed if online
- if(Util.isOffline(context)) {
- currentUser = null;
- return;
- }
-
- final int instance = Util.getActiveServer(context);
- if(UserUtil.instance == instance && currentUser != null) {
- return;
- } else {
- UserUtil.instance = instance;
- }
-
- new SilentBackgroundTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- currentUser = MusicServiceFactory.getMusicService(context).getUser(refresh, getCurrentUsername(context, instance), context, null);
-
- // If running, redo cast selector
- DownloadService downloadService = DownloadService.getInstance();
- if(downloadService != null) {
- downloadService.userSettingsChanged();
- }
-
- return null;
- }
-
- @Override
- protected void done(Void result) {
- if(context instanceof ActionBarActivity) {
- ((ActionBarActivity) context).supportInvalidateOptionsMenu();
- }
- }
-
- @Override
- protected void error(Throwable error) {
- // Don't do anything, supposed to be background pull
- Log.e(TAG, "Failed to seed user information");
- }
- }.execute();
- }
-
- public static User getCurrentUser() {
- return currentUser;
- }
-
- public static String getCurrentUsername(Context context, int instance) {
- SharedPreferences prefs = Util.getPreferences(context);
- return prefs.getString(Constants.PREFERENCES_KEY_USERNAME + instance, null);
- }
-
- public static String getCurrentUsername(Context context) {
- return getCurrentUsername(context, Util.getActiveServer(context));
- }
-
- public static boolean isCurrentAdmin() {
- return isCurrentRole(User.ADMIN);
- }
-
- public static boolean canPodcast() {
- return isCurrentRole(User.PODCAST);
- }
- public static boolean canShare() {
- return isCurrentRole(User.SHARE);
- }
- public static boolean canJukebox() {
- return isCurrentRole(User.JUKEBOX);
- }
- public static boolean canScrobble() {
- return isCurrentRole(User.SCROBBLING, true);
- }
-
- public static boolean isCurrentRole(String role) {
- return isCurrentRole(role, false);
- }
- public static boolean isCurrentRole(String role, boolean defaultValue) {
- if(currentUser == null) {
- return defaultValue;
- }
-
- for(User.Setting setting: currentUser.getSettings()) {
- if(setting.getName().equals(role)) {
- return setting.getValue() == true;
- }
- }
-
- return defaultValue;
- }
-
- public static void confirmCredentials(final Activity context, final Runnable onSuccess) {
- final long currentTime = System.currentTimeMillis();
- // If already ran this check within last x time, just go ahead and auth
- if((currentTime - lastVerifiedTime) < MIN_VERIFY_DURATION) {
- onSuccess.run();
- } else {
- View layout = context.getLayoutInflater().inflate(R.layout.confirm_password, null);
- final TextView passwordView = (TextView) layout.findViewById(R.id.password);
-
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
- builder.setTitle(R.string.admin_confirm_password)
- .setView(layout)
- .setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int id) {
- String password = passwordView.getText().toString();
-
- SharedPreferences prefs = Util.getPreferences(context);
- String correctPassword = prefs.getString(Constants.PREFERENCES_KEY_PASSWORD + Util.getActiveServer(context), null);
-
- if(password != null && password.equals(correctPassword)) {
- lastVerifiedTime = currentTime;
- onSuccess.run();
- } else {
- Util.toast(context, R.string.admin_confirm_password_bad);
- }
- }
- })
- .setNegativeButton(R.string.common_cancel, null)
- .setCancelable(true);
-
- AlertDialog dialog = builder.create();
- dialog.show();
- }
- }
-
- public static void changePassword(final Activity context, final User user) {
- View layout = context.getLayoutInflater().inflate(R.layout.change_password, null);
- final TextView passwordView = (TextView) layout.findViewById(R.id.new_password);
-
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
- builder.setTitle(R.string.admin_change_password)
- .setView(layout)
- .setPositiveButton(R.string.common_save, null)
- .setNegativeButton(R.string.common_cancel, null)
- .setCancelable(true);
-
- final AlertDialog dialog = builder.create();
- dialog.show();
-
- dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- final String password = passwordView.getText().toString();
- // Don't allow blank passwords
- if ("".equals(password)) {
- Util.toast(context, R.string.admin_change_password_invalid);
- return;
- }
-
- new SilentBackgroundTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- musicService.changePassword(user.getUsername(), password, context, null);
- return null;
- }
-
- @Override
- protected void done(Void v) {
- Util.toast(context, context.getResources().getString(R.string.admin_change_password_success, user.getUsername()));
- }
-
- @Override
- protected void error(Throwable error) {
- String msg;
- if (error instanceof OfflineException || error instanceof ServerTooOldException) {
- msg = getErrorMessage(error);
- } else {
- msg = context.getResources().getString(R.string.admin_change_password_error, user.getUsername());
- }
-
- Util.toast(context, msg);
- }
- }.execute();
-
- dialog.dismiss();
- }
- });
- }
-
- public static void updateSettings(final Context context, final User user) {
- new SilentBackgroundTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- musicService.updateUser(user, context, null);
- user.setSettings(user.getSettings());
- return null;
- }
-
- @Override
- protected void done(Void v) {
- Util.toast(context, context.getResources().getString(R.string.admin_update_permissions_success, user.getUsername()));
- }
-
- @Override
- protected void error(Throwable error) {
- String msg;
- if (error instanceof OfflineException || error instanceof ServerTooOldException) {
- msg = getErrorMessage(error);
- } else {
- msg = context.getResources().getString(R.string.admin_update_permissions_error, user.getUsername());
- }
-
- Util.toast(context, msg);
- }
- }.execute();
- }
-
- public static void changeEmail(final Activity context, final User user) {
- View layout = context.getLayoutInflater().inflate(R.layout.change_email, null);
- final TextView emailView = (TextView) layout.findViewById(R.id.new_email);
-
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
- builder.setTitle(R.string.admin_change_email)
- .setView(layout)
- .setPositiveButton(R.string.common_save, null)
- .setNegativeButton(R.string.common_cancel, null)
- .setCancelable(true);
-
- final AlertDialog dialog = builder.create();
- dialog.show();
-
- dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- final String email = emailView.getText().toString();
- // Don't allow blank emails
- if ("".equals(email)) {
- Util.toast(context, R.string.admin_change_email_invalid);
- return;
- }
-
- new SilentBackgroundTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- musicService.changeEmail(user.getUsername(), email, context, null);
- user.setEmail(email);
- return null;
- }
-
- @Override
- protected void done(Void v) {
- Util.toast(context, context.getResources().getString(R.string.admin_change_email_success, user.getUsername()));
- }
-
- @Override
- protected void error(Throwable error) {
- String msg;
- if (error instanceof OfflineException || error instanceof ServerTooOldException) {
- msg = getErrorMessage(error);
- } else {
- msg = context.getResources().getString(R.string.admin_change_email_error, user.getUsername());
- }
-
- Util.toast(context, msg);
- }
- }.execute();
-
- dialog.dismiss();
- }
- });
- }
-
- public static void deleteUser(final Context context, final User user, final ArrayAdapter adapter) {
- Util.confirmDialog(context, R.string.common_delete, user.getUsername(), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- new SilentBackgroundTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- musicService.deleteUser(user.getUsername(), context, null);
- return null;
- }
-
- @Override
- protected void done(Void v) {
- if(adapter != null) {
- adapter.remove(user);
- adapter.notifyDataSetChanged();
- }
-
- Util.toast(context, context.getResources().getString(R.string.admin_delete_user_success, user.getUsername()));
- }
-
- @Override
- protected void error(Throwable error) {
- String msg;
- if (error instanceof OfflineException || error instanceof ServerTooOldException) {
- msg = getErrorMessage(error);
- } else {
- msg = context.getResources().getString(R.string.admin_delete_user_error, user.getUsername());
- }
-
- Util.toast(context, msg);
- }
- }.execute();
- }
- });
- }
-
- public static void addNewUser(final Activity context, final SubsonicFragment fragment) {
- final User user = new User();
- for(String role: User.ROLES) {
- if(role.equals(User.SETTINGS) || role.equals(User.STREAM)) {
- user.addSetting(role, true);
- } else {
- user.addSetting(role, false);
- }
- }
-
- View layout = context.getLayoutInflater().inflate(R.layout.create_user, null);
- final TextView usernameView = (TextView) layout.findViewById(R.id.username);
- final TextView emailView = (TextView) layout.findViewById(R.id.email);
- final TextView passwordView = (TextView) layout.findViewById(R.id.password);
- final ListView listView = (ListView) layout.findViewById(R.id.settings_list);
- listView.setAdapter(new SettingsAdapter(context, user, true));
-
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
- builder.setTitle(R.string.menu_add_user)
- .setView(layout)
- .setPositiveButton(R.string.common_save, null)
- .setNegativeButton(R.string.common_cancel, null)
- .setCancelable(true);
-
- final AlertDialog dialog = builder.create();
- dialog.show();
-
- dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- final String username = usernameView.getText().toString();
- // Don't allow blank emails
- if ("".equals(username)) {
- Util.toast(context, R.string.admin_change_username_invalid);
- return;
- }
-
- final String email = emailView.getText().toString();
- // Don't allow blank emails
- if ("".equals(email)) {
- Util.toast(context, R.string.admin_change_email_invalid);
- return;
- }
-
- final String password = passwordView.getText().toString();
- if ("".equals(password)) {
- Util.toast(context, R.string.admin_change_password_invalid);
- return;
- }
-
- user.setUsername(username);
- user.setEmail(email);
- user.setPassword(password);
-
- new SilentBackgroundTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- musicService.createUser(user, context, null);
- return null;
- }
-
- @Override
- protected void done(Void v) {
- fragment.onRefresh();
- Util.toast(context, context.getResources().getString(R.string.admin_create_user_success));
- }
-
- @Override
- protected void error(Throwable error) {
- String msg;
- if (error instanceof OfflineException || error instanceof ServerTooOldException) {
- msg = getErrorMessage(error);
- } else {
- msg = context.getResources().getString(R.string.admin_create_user_error);
- }
-
- Util.toast(context, msg);
- }
- }.execute();
-
- dialog.dismiss();
- }
- });
- }
-}
+/*
+ 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 2014 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.util;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.SharedPreferences;
+import android.support.v7.app.ActionBarActivity;
+import android.util.Log;
+import android.view.View;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.User;
+import github.daneren2005.dsub.fragments.SubsonicFragment;
+import github.daneren2005.dsub.service.DownloadService;
+import github.daneren2005.dsub.service.MusicService;
+import github.daneren2005.dsub.service.MusicServiceFactory;
+import github.daneren2005.dsub.service.OfflineException;
+import github.daneren2005.dsub.service.ServerTooOldException;
+import github.daneren2005.dsub.adapter.SettingsAdapter;
+
+public final class UserUtil {
+ private static final String TAG = UserUtil.class.getSimpleName();
+ private static final long MIN_VERIFY_DURATION = 1000L * 60L * 60L;
+
+ private static int instance = -1;
+ private static User currentUser;
+ private static long lastVerifiedTime = 0;
+
+
+ public static void refreshCurrentUser(Context context, boolean forceRefresh) {
+ refreshCurrentUser(context, forceRefresh, false);
+ }
+ public static void refreshCurrentUser(Context context, boolean forceRefresh, boolean unAuth) {
+ currentUser = null;
+ if(unAuth) {
+ lastVerifiedTime = 0;
+ }
+ seedCurrentUser(context, forceRefresh);
+ }
+
+ public static void seedCurrentUser(Context context) {
+ seedCurrentUser(context, false);
+ }
+ public static void seedCurrentUser(final Context context, final boolean refresh) {
+ // Only try to seed if online
+ if(Util.isOffline(context)) {
+ currentUser = null;
+ return;
+ }
+
+ final int instance = Util.getActiveServer(context);
+ if(UserUtil.instance == instance && currentUser != null) {
+ return;
+ } else {
+ UserUtil.instance = instance;
+ }
+
+ new SilentBackgroundTask<Void>(context) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ currentUser = MusicServiceFactory.getMusicService(context).getUser(refresh, getCurrentUsername(context, instance), context, null);
+
+ // If running, redo cast selector
+ DownloadService downloadService = DownloadService.getInstance();
+ if(downloadService != null) {
+ downloadService.userSettingsChanged();
+ }
+
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ if(context instanceof ActionBarActivity) {
+ ((ActionBarActivity) context).supportInvalidateOptionsMenu();
+ }
+ }
+
+ @Override
+ protected void error(Throwable error) {
+ // Don't do anything, supposed to be background pull
+ Log.e(TAG, "Failed to seed user information");
+ }
+ }.execute();
+ }
+
+ public static User getCurrentUser() {
+ return currentUser;
+ }
+
+ public static String getCurrentUsername(Context context, int instance) {
+ SharedPreferences prefs = Util.getPreferences(context);
+ return prefs.getString(Constants.PREFERENCES_KEY_USERNAME + instance, null);
+ }
+
+ public static String getCurrentUsername(Context context) {
+ return getCurrentUsername(context, Util.getActiveServer(context));
+ }
+
+ public static boolean isCurrentAdmin() {
+ return isCurrentRole(User.ADMIN);
+ }
+
+ public static boolean canPodcast() {
+ return isCurrentRole(User.PODCAST);
+ }
+ public static boolean canShare() {
+ return isCurrentRole(User.SHARE);
+ }
+ public static boolean canJukebox() {
+ return isCurrentRole(User.JUKEBOX);
+ }
+ public static boolean canScrobble() {
+ return isCurrentRole(User.SCROBBLING, true);
+ }
+
+ public static boolean isCurrentRole(String role) {
+ return isCurrentRole(role, false);
+ }
+ public static boolean isCurrentRole(String role, boolean defaultValue) {
+ if(currentUser == null) {
+ return defaultValue;
+ }
+
+ for(User.Setting setting: currentUser.getSettings()) {
+ if(setting.getName().equals(role)) {
+ return setting.getValue() == true;
+ }
+ }
+
+ return defaultValue;
+ }
+
+ public static void confirmCredentials(final Activity context, final Runnable onSuccess) {
+ final long currentTime = System.currentTimeMillis();
+ // If already ran this check within last x time, just go ahead and auth
+ if((currentTime - lastVerifiedTime) < MIN_VERIFY_DURATION) {
+ onSuccess.run();
+ } else {
+ View layout = context.getLayoutInflater().inflate(R.layout.confirm_password, null);
+ final TextView passwordView = (TextView) layout.findViewById(R.id.password);
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.admin_confirm_password)
+ .setView(layout)
+ .setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ String password = passwordView.getText().toString();
+
+ SharedPreferences prefs = Util.getPreferences(context);
+ String correctPassword = prefs.getString(Constants.PREFERENCES_KEY_PASSWORD + Util.getActiveServer(context), null);
+
+ if(password != null && password.equals(correctPassword)) {
+ lastVerifiedTime = currentTime;
+ onSuccess.run();
+ } else {
+ Util.toast(context, R.string.admin_confirm_password_bad);
+ }
+ }
+ })
+ .setNegativeButton(R.string.common_cancel, null)
+ .setCancelable(true);
+
+ AlertDialog dialog = builder.create();
+ dialog.show();
+ }
+ }
+
+ public static void changePassword(final Activity context, final User user) {
+ View layout = context.getLayoutInflater().inflate(R.layout.change_password, null);
+ final TextView passwordView = (TextView) layout.findViewById(R.id.new_password);
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.admin_change_password)
+ .setView(layout)
+ .setPositiveButton(R.string.common_save, null)
+ .setNegativeButton(R.string.common_cancel, null)
+ .setCancelable(true);
+
+ final AlertDialog dialog = builder.create();
+ dialog.show();
+
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ final String password = passwordView.getText().toString();
+ // Don't allow blank passwords
+ if ("".equals(password)) {
+ Util.toast(context, R.string.admin_change_password_invalid);
+ return;
+ }
+
+ new SilentBackgroundTask<Void>(context) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ musicService.changePassword(user.getUsername(), password, context, null);
+ return null;
+ }
+
+ @Override
+ protected void done(Void v) {
+ Util.toast(context, context.getResources().getString(R.string.admin_change_password_success, user.getUsername()));
+ }
+
+ @Override
+ protected void error(Throwable error) {
+ String msg;
+ if (error instanceof OfflineException || error instanceof ServerTooOldException) {
+ msg = getErrorMessage(error);
+ } else {
+ msg = context.getResources().getString(R.string.admin_change_password_error, user.getUsername());
+ }
+
+ Util.toast(context, msg);
+ }
+ }.execute();
+
+ dialog.dismiss();
+ }
+ });
+ }
+
+ public static void updateSettings(final Context context, final User user) {
+ new SilentBackgroundTask<Void>(context) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ musicService.updateUser(user, context, null);
+ user.setSettings(user.getSettings());
+ return null;
+ }
+
+ @Override
+ protected void done(Void v) {
+ Util.toast(context, context.getResources().getString(R.string.admin_update_permissions_success, user.getUsername()));
+ }
+
+ @Override
+ protected void error(Throwable error) {
+ String msg;
+ if (error instanceof OfflineException || error instanceof ServerTooOldException) {
+ msg = getErrorMessage(error);
+ } else {
+ msg = context.getResources().getString(R.string.admin_update_permissions_error, user.getUsername());
+ }
+
+ Util.toast(context, msg);
+ }
+ }.execute();
+ }
+
+ public static void changeEmail(final Activity context, final User user) {
+ View layout = context.getLayoutInflater().inflate(R.layout.change_email, null);
+ final TextView emailView = (TextView) layout.findViewById(R.id.new_email);
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.admin_change_email)
+ .setView(layout)
+ .setPositiveButton(R.string.common_save, null)
+ .setNegativeButton(R.string.common_cancel, null)
+ .setCancelable(true);
+
+ final AlertDialog dialog = builder.create();
+ dialog.show();
+
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ final String email = emailView.getText().toString();
+ // Don't allow blank emails
+ if ("".equals(email)) {
+ Util.toast(context, R.string.admin_change_email_invalid);
+ return;
+ }
+
+ new SilentBackgroundTask<Void>(context) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ musicService.changeEmail(user.getUsername(), email, context, null);
+ user.setEmail(email);
+ return null;
+ }
+
+ @Override
+ protected void done(Void v) {
+ Util.toast(context, context.getResources().getString(R.string.admin_change_email_success, user.getUsername()));
+ }
+
+ @Override
+ protected void error(Throwable error) {
+ String msg;
+ if (error instanceof OfflineException || error instanceof ServerTooOldException) {
+ msg = getErrorMessage(error);
+ } else {
+ msg = context.getResources().getString(R.string.admin_change_email_error, user.getUsername());
+ }
+
+ Util.toast(context, msg);
+ }
+ }.execute();
+
+ dialog.dismiss();
+ }
+ });
+ }
+
+ public static void deleteUser(final Context context, final User user, final ArrayAdapter adapter) {
+ Util.confirmDialog(context, R.string.common_delete, user.getUsername(), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ new SilentBackgroundTask<Void>(context) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ musicService.deleteUser(user.getUsername(), context, null);
+ return null;
+ }
+
+ @Override
+ protected void done(Void v) {
+ if(adapter != null) {
+ adapter.remove(user);
+ adapter.notifyDataSetChanged();
+ }
+
+ Util.toast(context, context.getResources().getString(R.string.admin_delete_user_success, user.getUsername()));
+ }
+
+ @Override
+ protected void error(Throwable error) {
+ String msg;
+ if (error instanceof OfflineException || error instanceof ServerTooOldException) {
+ msg = getErrorMessage(error);
+ } else {
+ msg = context.getResources().getString(R.string.admin_delete_user_error, user.getUsername());
+ }
+
+ Util.toast(context, msg);
+ }
+ }.execute();
+ }
+ });
+ }
+
+ public static void addNewUser(final Activity context, final SubsonicFragment fragment) {
+ final User user = new User();
+ for(String role: User.ROLES) {
+ if(role.equals(User.SETTINGS) || role.equals(User.STREAM)) {
+ user.addSetting(role, true);
+ } else {
+ user.addSetting(role, false);
+ }
+ }
+
+ View layout = context.getLayoutInflater().inflate(R.layout.create_user, null);
+ final TextView usernameView = (TextView) layout.findViewById(R.id.username);
+ final TextView emailView = (TextView) layout.findViewById(R.id.email);
+ final TextView passwordView = (TextView) layout.findViewById(R.id.password);
+ final ListView listView = (ListView) layout.findViewById(R.id.settings_list);
+ listView.setAdapter(new SettingsAdapter(context, user, true));
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.menu_add_user)
+ .setView(layout)
+ .setPositiveButton(R.string.common_save, null)
+ .setNegativeButton(R.string.common_cancel, null)
+ .setCancelable(true);
+
+ final AlertDialog dialog = builder.create();
+ dialog.show();
+
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ final String username = usernameView.getText().toString();
+ // Don't allow blank emails
+ if ("".equals(username)) {
+ Util.toast(context, R.string.admin_change_username_invalid);
+ return;
+ }
+
+ final String email = emailView.getText().toString();
+ // Don't allow blank emails
+ if ("".equals(email)) {
+ Util.toast(context, R.string.admin_change_email_invalid);
+ return;
+ }
+
+ final String password = passwordView.getText().toString();
+ if ("".equals(password)) {
+ Util.toast(context, R.string.admin_change_password_invalid);
+ return;
+ }
+
+ user.setUsername(username);
+ user.setEmail(email);
+ user.setPassword(password);
+
+ new SilentBackgroundTask<Void>(context) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ musicService.createUser(user, context, null);
+ return null;
+ }
+
+ @Override
+ protected void done(Void v) {
+ fragment.onRefresh();
+ Util.toast(context, context.getResources().getString(R.string.admin_create_user_success));
+ }
+
+ @Override
+ protected void error(Throwable error) {
+ String msg;
+ if (error instanceof OfflineException || error instanceof ServerTooOldException) {
+ msg = getErrorMessage(error);
+ } else {
+ msg = context.getResources().getString(R.string.admin_create_user_error);
+ }
+
+ Util.toast(context, msg);
+ }
+ }.execute();
+
+ dialog.dismiss();
+ }
+ });
+ }
+}
diff --git a/src/github/daneren2005/dsub/util/Util.java b/app/src/main/java/github/daneren2005/dsub/util/Util.java
index 75d8d5dd..75d8d5dd 100644
--- a/src/github/daneren2005/dsub/util/Util.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/Util.java
diff --git a/src/github/daneren2005/dsub/util/compat/CastCompat.java b/app/src/main/java/github/daneren2005/dsub/util/compat/CastCompat.java
index ab64bca9..ab64bca9 100644
--- a/src/github/daneren2005/dsub/util/compat/CastCompat.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/compat/CastCompat.java
diff --git a/src/github/daneren2005/dsub/util/compat/RemoteControlClientBase.java b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientBase.java
index 320092e9..320092e9 100644
--- a/src/github/daneren2005/dsub/util/compat/RemoteControlClientBase.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientBase.java
diff --git a/src/github/daneren2005/dsub/util/compat/RemoteControlClientHelper.java b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientHelper.java
index 93075a28..93075a28 100644
--- a/src/github/daneren2005/dsub/util/compat/RemoteControlClientHelper.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientHelper.java
diff --git a/src/github/daneren2005/dsub/util/compat/RemoteControlClientICS.java b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientICS.java
index 50283da6..50283da6 100644
--- a/src/github/daneren2005/dsub/util/compat/RemoteControlClientICS.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientICS.java
diff --git a/src/github/daneren2005/dsub/util/compat/RemoteControlClientJB.java b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientJB.java
index c27df2ba..c27df2ba 100644
--- a/src/github/daneren2005/dsub/util/compat/RemoteControlClientJB.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientJB.java
diff --git a/src/github/daneren2005/dsub/util/tags/Bastp.java b/app/src/main/java/github/daneren2005/dsub/util/tags/Bastp.java
index aa0a2e25..aa0a2e25 100644
--- a/src/github/daneren2005/dsub/util/tags/Bastp.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/tags/Bastp.java
diff --git a/src/github/daneren2005/dsub/util/tags/BastpUtil.java b/app/src/main/java/github/daneren2005/dsub/util/tags/BastpUtil.java
index 7ff517fd..7ff517fd 100644
--- a/src/github/daneren2005/dsub/util/tags/BastpUtil.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/tags/BastpUtil.java
diff --git a/src/github/daneren2005/dsub/util/tags/Common.java b/app/src/main/java/github/daneren2005/dsub/util/tags/Common.java
index 51344d90..51344d90 100644
--- a/src/github/daneren2005/dsub/util/tags/Common.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/tags/Common.java
diff --git a/src/github/daneren2005/dsub/util/tags/FlacFile.java b/app/src/main/java/github/daneren2005/dsub/util/tags/FlacFile.java
index de3584d1..de3584d1 100644
--- a/src/github/daneren2005/dsub/util/tags/FlacFile.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/tags/FlacFile.java
diff --git a/src/github/daneren2005/dsub/util/tags/ID3v2File.java b/app/src/main/java/github/daneren2005/dsub/util/tags/ID3v2File.java
index ea61f36c..ea61f36c 100644
--- a/src/github/daneren2005/dsub/util/tags/ID3v2File.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/tags/ID3v2File.java
diff --git a/src/github/daneren2005/dsub/util/tags/LameHeader.java b/app/src/main/java/github/daneren2005/dsub/util/tags/LameHeader.java
index 720ee87f..720ee87f 100644
--- a/src/github/daneren2005/dsub/util/tags/LameHeader.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/tags/LameHeader.java
diff --git a/src/github/daneren2005/dsub/util/tags/OggFile.java b/app/src/main/java/github/daneren2005/dsub/util/tags/OggFile.java
index d0b31671..d0b31671 100644
--- a/src/github/daneren2005/dsub/util/tags/OggFile.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/tags/OggFile.java
diff --git a/src/github/daneren2005/dsub/view/AlbumCell.java b/app/src/main/java/github/daneren2005/dsub/view/AlbumCell.java
index 110456e7..8707ece7 100644
--- a/src/github/daneren2005/dsub/view/AlbumCell.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/AlbumCell.java
@@ -1,108 +1,108 @@
-/*
- 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 2014 (C) Scott Jackson
-*/
-
-package github.daneren2005.dsub.view;
-
-import android.content.Context;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.RatingBar;
-import android.widget.TextView;
-
-import java.io.File;
-
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.domain.MusicDirectory;
-import github.daneren2005.dsub.util.FileUtil;
-import github.daneren2005.dsub.util.ImageLoader;
-
-public class AlbumCell extends UpdateView {
- private static final String TAG = AlbumCell.class.getSimpleName();
-
- private Context context;
- private MusicDirectory.Entry album;
- private File file;
-
- private View coverArtView;
- private TextView titleView;
- private TextView artistView;
- private boolean showArtist = true;
-
- public AlbumCell(Context context) {
- super(context);
- this.context = context;
- LayoutInflater.from(context).inflate(R.layout.album_cell_item, this, true);
-
- coverArtView = findViewById(R.id.album_coverart);
- titleView = (TextView) findViewById(R.id.album_title);
- artistView = (TextView) findViewById(R.id.album_artist);
-
- ratingBar = (RatingBar) findViewById(R.id.album_rating);
- ratingBar.setFocusable(false);
- starButton = (ImageButton) findViewById(R.id.album_star);
- starButton.setFocusable(false);
- moreButton = (ImageView) findViewById(R.id.album_more);
- moreButton.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- v.showContextMenu();
- }
- });
- }
-
- public void setShowArtist(boolean showArtist) {
- this.showArtist = showArtist;
- }
-
- protected void setObjectImpl(Object obj1, Object obj2) {
- this.album = (MusicDirectory.Entry) obj1;
- titleView.setText(album.getAlbumDisplay());
- String artist = "";
- if(showArtist) {
- artist = album.getArtist();
- if (artist == null) {
- artist = "";
- }
- if (album.getYear() != null) {
- artist += " - " + album.getYear();
- }
- } else if(album.getYear() != null) {
- artist += album.getYear();
- }
- artistView.setText(album.getArtist() == null ? "" : artist);
- imageTask = ((ImageLoader)obj2).loadImage(coverArtView, album, false, true);
- file = null;
- }
-
- @Override
- protected void updateBackground() {
- if(file == null) {
- file = FileUtil.getAlbumDirectory(context, album);
- }
-
- exists = file.exists();
- isStarred = album.isStarred();
- isRated = album.getRating();
- }
-
- public MusicDirectory.Entry getEntry() {
- return album;
- }
-
- public File getFile() {
- return file;
- }
-}
+/*
+ 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 2014 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.view;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.RatingBar;
+import android.widget.TextView;
+
+import java.io.File;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.MusicDirectory;
+import github.daneren2005.dsub.util.FileUtil;
+import github.daneren2005.dsub.util.ImageLoader;
+
+public class AlbumCell extends UpdateView {
+ private static final String TAG = AlbumCell.class.getSimpleName();
+
+ private Context context;
+ private MusicDirectory.Entry album;
+ private File file;
+
+ private View coverArtView;
+ private TextView titleView;
+ private TextView artistView;
+ private boolean showArtist = true;
+
+ public AlbumCell(Context context) {
+ super(context);
+ this.context = context;
+ LayoutInflater.from(context).inflate(R.layout.album_cell_item, this, true);
+
+ coverArtView = findViewById(R.id.album_coverart);
+ titleView = (TextView) findViewById(R.id.album_title);
+ artistView = (TextView) findViewById(R.id.album_artist);
+
+ ratingBar = (RatingBar) findViewById(R.id.album_rating);
+ ratingBar.setFocusable(false);
+ starButton = (ImageButton) findViewById(R.id.album_star);
+ starButton.setFocusable(false);
+ moreButton = (ImageView) findViewById(R.id.album_more);
+ moreButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ v.showContextMenu();
+ }
+ });
+ }
+
+ public void setShowArtist(boolean showArtist) {
+ this.showArtist = showArtist;
+ }
+
+ protected void setObjectImpl(Object obj1, Object obj2) {
+ this.album = (MusicDirectory.Entry) obj1;
+ titleView.setText(album.getAlbumDisplay());
+ String artist = "";
+ if(showArtist) {
+ artist = album.getArtist();
+ if (artist == null) {
+ artist = "";
+ }
+ if (album.getYear() != null) {
+ artist += " - " + album.getYear();
+ }
+ } else if(album.getYear() != null) {
+ artist += album.getYear();
+ }
+ artistView.setText(album.getArtist() == null ? "" : artist);
+ imageTask = ((ImageLoader)obj2).loadImage(coverArtView, album, false, true);
+ file = null;
+ }
+
+ @Override
+ protected void updateBackground() {
+ if(file == null) {
+ file = FileUtil.getAlbumDirectory(context, album);
+ }
+
+ exists = file.exists();
+ isStarred = album.isStarred();
+ isRated = album.getRating();
+ }
+
+ public MusicDirectory.Entry getEntry() {
+ return album;
+ }
+
+ public File getFile() {
+ return file;
+ }
+}
diff --git a/src/github/daneren2005/dsub/view/AlbumView.java b/app/src/main/java/github/daneren2005/dsub/view/AlbumView.java
index 70c1e049..bd54ea1e 100644
--- a/src/github/daneren2005/dsub/view/AlbumView.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/AlbumView.java
@@ -1,107 +1,107 @@
-/*
- 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 github.daneren2005.dsub.view;
-
-import android.content.Context;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.RatingBar;
-import android.widget.TextView;
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.domain.MusicDirectory;
-import github.daneren2005.dsub.util.FileUtil;
-import github.daneren2005.dsub.util.ImageLoader;
-import github.daneren2005.dsub.util.Util;
-import java.io.File;
-import java.util.List;
-
-/**
- * Used to display albums in a {@code ListView}.
- *
- * @author Sindre Mehus
- */
-public class AlbumView extends UpdateView {
- private static final String TAG = AlbumView.class.getSimpleName();
-
- private Context context;
- private MusicDirectory.Entry album;
- private File file;
-
- private TextView titleView;
- private TextView artistView;
- private View coverArtView;
-
- public AlbumView(Context context) {
- super(context);
- this.context = context;
- LayoutInflater.from(context).inflate(R.layout.album_list_item, this, true);
-
- titleView = (TextView) findViewById(R.id.album_title);
- artistView = (TextView) findViewById(R.id.album_artist);
- coverArtView = findViewById(R.id.album_coverart);
- ratingBar = (RatingBar) findViewById(R.id.album_rating);
- starButton = (ImageButton) findViewById(R.id.album_star);
- starButton.setFocusable(false);
-
- moreButton = (ImageView) findViewById(R.id.album_more);
- moreButton.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- v.showContextMenu();
- }
- });
- }
-
- protected void setObjectImpl(Object obj1, Object obj2) {
- this.album = (MusicDirectory.Entry) obj1;
- titleView.setText(album.getAlbumDisplay());
- String artist = album.getArtist();
- if(artist == null) {
- artist = "";
- }
- if(album.getYear() != null) {
- artist += " - " + album.getYear();
- }
- artistView.setText(artist);
- artistView.setVisibility(album.getArtist() == null ? View.GONE : View.VISIBLE);
- imageTask = ((ImageLoader)obj2).loadImage(coverArtView, album, false, true);
- file = null;
- }
-
- @Override
- protected void updateBackground() {
- if(file == null) {
- file = FileUtil.getAlbumDirectory(context, album);
- }
-
- exists = file.exists();
- isStarred = album.isStarred();
- isRated = album.getRating();
- }
-
- public MusicDirectory.Entry getEntry() {
- return album;
- }
-
- public File getFile() {
- return file;
- }
-}
+/*
+ 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 github.daneren2005.dsub.view;
+
+import android.content.Context;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.RatingBar;
+import android.widget.TextView;
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.MusicDirectory;
+import github.daneren2005.dsub.util.FileUtil;
+import github.daneren2005.dsub.util.ImageLoader;
+import github.daneren2005.dsub.util.Util;
+import java.io.File;
+import java.util.List;
+
+/**
+ * Used to display albums in a {@code ListView}.
+ *
+ * @author Sindre Mehus
+ */
+public class AlbumView extends UpdateView {
+ private static final String TAG = AlbumView.class.getSimpleName();
+
+ private Context context;
+ private MusicDirectory.Entry album;
+ private File file;
+
+ private TextView titleView;
+ private TextView artistView;
+ private View coverArtView;
+
+ public AlbumView(Context context) {
+ super(context);
+ this.context = context;
+ LayoutInflater.from(context).inflate(R.layout.album_list_item, this, true);
+
+ titleView = (TextView) findViewById(R.id.album_title);
+ artistView = (TextView) findViewById(R.id.album_artist);
+ coverArtView = findViewById(R.id.album_coverart);
+ ratingBar = (RatingBar) findViewById(R.id.album_rating);
+ starButton = (ImageButton) findViewById(R.id.album_star);
+ starButton.setFocusable(false);
+
+ moreButton = (ImageView) findViewById(R.id.album_more);
+ moreButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ v.showContextMenu();
+ }
+ });
+ }
+
+ protected void setObjectImpl(Object obj1, Object obj2) {
+ this.album = (MusicDirectory.Entry) obj1;
+ titleView.setText(album.getAlbumDisplay());
+ String artist = album.getArtist();
+ if(artist == null) {
+ artist = "";
+ }
+ if(album.getYear() != null) {
+ artist += " - " + album.getYear();
+ }
+ artistView.setText(artist);
+ artistView.setVisibility(album.getArtist() == null ? View.GONE : View.VISIBLE);
+ imageTask = ((ImageLoader)obj2).loadImage(coverArtView, album, false, true);
+ file = null;
+ }
+
+ @Override
+ protected void updateBackground() {
+ if(file == null) {
+ file = FileUtil.getAlbumDirectory(context, album);
+ }
+
+ exists = file.exists();
+ isStarred = album.isStarred();
+ isRated = album.getRating();
+ }
+
+ public MusicDirectory.Entry getEntry() {
+ return album;
+ }
+
+ public File getFile() {
+ return file;
+ }
+}
diff --git a/src/github/daneren2005/dsub/view/ArtistEntryView.java b/app/src/main/java/github/daneren2005/dsub/view/ArtistEntryView.java
index 157b25a9..71bdeb78 100644
--- a/src/github/daneren2005/dsub/view/ArtistEntryView.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/ArtistEntryView.java
@@ -1,79 +1,79 @@
-/*
- 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 github.daneren2005.dsub.view;
-
-import android.content.Context;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.TextView;
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.domain.MusicDirectory;
-import github.daneren2005.dsub.util.FileUtil;
-import github.daneren2005.dsub.util.ImageLoader;
-import github.daneren2005.dsub.util.Util;
-import java.io.File;
-/**
- * Used to display albums in a {@code ListView}.
- *
- * @author Sindre Mehus
- */
-public class ArtistEntryView extends UpdateView {
- private static final String TAG = ArtistEntryView.class.getSimpleName();
-
- private Context context;
- private MusicDirectory.Entry artist;
- private File file;
-
- private TextView titleView;
-
- public ArtistEntryView(Context context) {
- super(context);
- this.context = context;
- LayoutInflater.from(context).inflate(R.layout.basic_list_item, this, true);
-
- titleView = (TextView) findViewById(R.id.item_name);
- starButton = (ImageButton) findViewById(R.id.item_star);
- starButton.setFocusable(false);
- moreButton = (ImageView) findViewById(R.id.item_more);
- moreButton.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- v.showContextMenu();
- }
- });
- }
-
- protected void setObjectImpl(Object obj) {
- this.artist = (MusicDirectory.Entry) obj;
- titleView.setText(artist.getTitle());
- file = FileUtil.getArtistDirectory(context, artist);
- }
-
- @Override
- protected void updateBackground() {
- exists = file.exists();
- isStarred = artist.isStarred();
- }
-
- public File getFile() {
- return file;
- }
-}
+/*
+ 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 github.daneren2005.dsub.view;
+
+import android.content.Context;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.TextView;
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.MusicDirectory;
+import github.daneren2005.dsub.util.FileUtil;
+import github.daneren2005.dsub.util.ImageLoader;
+import github.daneren2005.dsub.util.Util;
+import java.io.File;
+/**
+ * Used to display albums in a {@code ListView}.
+ *
+ * @author Sindre Mehus
+ */
+public class ArtistEntryView extends UpdateView {
+ private static final String TAG = ArtistEntryView.class.getSimpleName();
+
+ private Context context;
+ private MusicDirectory.Entry artist;
+ private File file;
+
+ private TextView titleView;
+
+ public ArtistEntryView(Context context) {
+ super(context);
+ this.context = context;
+ LayoutInflater.from(context).inflate(R.layout.basic_list_item, this, true);
+
+ titleView = (TextView) findViewById(R.id.item_name);
+ starButton = (ImageButton) findViewById(R.id.item_star);
+ starButton.setFocusable(false);
+ moreButton = (ImageView) findViewById(R.id.item_more);
+ moreButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ v.showContextMenu();
+ }
+ });
+ }
+
+ protected void setObjectImpl(Object obj) {
+ this.artist = (MusicDirectory.Entry) obj;
+ titleView.setText(artist.getTitle());
+ file = FileUtil.getArtistDirectory(context, artist);
+ }
+
+ @Override
+ protected void updateBackground() {
+ exists = file.exists();
+ isStarred = artist.isStarred();
+ }
+
+ public File getFile() {
+ return file;
+ }
+}
diff --git a/src/github/daneren2005/dsub/view/ArtistView.java b/app/src/main/java/github/daneren2005/dsub/view/ArtistView.java
index b8a87c20..c255be69 100644
--- a/src/github/daneren2005/dsub/view/ArtistView.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/ArtistView.java
@@ -1,78 +1,78 @@
-/*
- 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 github.daneren2005.dsub.view;
-
-import android.content.Context;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.TextView;
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.domain.Artist;
-import github.daneren2005.dsub.util.FileUtil;
-
-import java.io.File;
-
-/**
- * Used to display albums in a {@code ListView}.
- *
- * @author Sindre Mehus
- */
-public class ArtistView extends UpdateView {
- private static final String TAG = ArtistView.class.getSimpleName();
-
- private Context context;
- private Artist artist;
- private File file;
-
- private TextView titleView;
-
- public ArtistView(Context context) {
- super(context);
- this.context = context;
- LayoutInflater.from(context).inflate(R.layout.basic_list_item, this, true);
-
- titleView = (TextView) findViewById(R.id.item_name);
- starButton = (ImageButton) findViewById(R.id.item_star);
- starButton.setFocusable(false);
- moreButton = (ImageView) findViewById(R.id.item_more);
- moreButton.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- v.showContextMenu();
- }
- });
- }
-
- protected void setObjectImpl(Object obj) {
- this.artist = (Artist) obj;
- titleView.setText(artist.getName());
- file = FileUtil.getArtistDirectory(context, artist);
- }
-
- @Override
- protected void updateBackground() {
- exists = file.exists();
- isStarred = artist.isStarred();
- }
-
- public File getFile() {
- return file;
- }
-}
+/*
+ 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 github.daneren2005.dsub.view;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.TextView;
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.Artist;
+import github.daneren2005.dsub.util.FileUtil;
+
+import java.io.File;
+
+/**
+ * Used to display albums in a {@code ListView}.
+ *
+ * @author Sindre Mehus
+ */
+public class ArtistView extends UpdateView {
+ private static final String TAG = ArtistView.class.getSimpleName();
+
+ private Context context;
+ private Artist artist;
+ private File file;
+
+ private TextView titleView;
+
+ public ArtistView(Context context) {
+ super(context);
+ this.context = context;
+ LayoutInflater.from(context).inflate(R.layout.basic_list_item, this, true);
+
+ titleView = (TextView) findViewById(R.id.item_name);
+ starButton = (ImageButton) findViewById(R.id.item_star);
+ starButton.setFocusable(false);
+ moreButton = (ImageView) findViewById(R.id.item_more);
+ moreButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ v.showContextMenu();
+ }
+ });
+ }
+
+ protected void setObjectImpl(Object obj) {
+ this.artist = (Artist) obj;
+ titleView.setText(artist.getName());
+ file = FileUtil.getArtistDirectory(context, artist);
+ }
+
+ @Override
+ protected void updateBackground() {
+ exists = file.exists();
+ isStarred = artist.isStarred();
+ }
+
+ public File getFile() {
+ return file;
+ }
+}
diff --git a/src/github/daneren2005/dsub/view/AutoRepeatButton.java b/app/src/main/java/github/daneren2005/dsub/view/AutoRepeatButton.java
index cb2d2a51..3c59dd37 100644
--- a/src/github/daneren2005/dsub/view/AutoRepeatButton.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/AutoRepeatButton.java
@@ -1,86 +1,86 @@
-package github.daneren2005.dsub.view;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.View;
-import android.widget.ImageButton;
-
-public class AutoRepeatButton extends ImageButton {
-
- private static final long initialRepeatDelay = 1000;
- private static final long repeatIntervalInMilliseconds = 300;
- private boolean doClick = true;
- private Runnable repeatEvent = null;
-
- private Runnable repeatClickWhileButtonHeldRunnable = new Runnable() {
- @Override
- public void run() {
- doClick = false;
- //Perform the present repetition of the click action provided by the user
- // in setOnClickListener().
- if(repeatEvent != null)
- repeatEvent.run();
-
- //Schedule the next repetitions of the click action, using a faster repeat
- // interval than the initial repeat delay interval.
- postDelayed(repeatClickWhileButtonHeldRunnable, repeatIntervalInMilliseconds);
- }
- };
-
- private void commonConstructorCode() {
- this.setOnTouchListener(new OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- int action = event.getAction();
- if(action == MotionEvent.ACTION_DOWN)
- {
- doClick = true;
- //Just to be sure that we removed all callbacks,
- // which should have occurred in the ACTION_UP
- removeCallbacks(repeatClickWhileButtonHeldRunnable);
-
- //Schedule the start of repetitions after a one half second delay.
- postDelayed(repeatClickWhileButtonHeldRunnable, initialRepeatDelay);
-
- setPressed(true);
- }
- else if(action == MotionEvent.ACTION_UP) {
- //Cancel any repetition in progress.
- removeCallbacks(repeatClickWhileButtonHeldRunnable);
-
- if(doClick || repeatEvent == null) {
- performClick();
- }
-
- setPressed(false);
- }
-
- //Returning true here prevents performClick() from getting called
- // in the usual manner, which would be redundant, given that we are
- // already calling it above.
- return true;
- }
- });
- }
-
- public void setOnRepeatListener(Runnable runnable) {
- repeatEvent = runnable;
- }
-
- public AutoRepeatButton(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- commonConstructorCode();
- }
-
-
- public AutoRepeatButton(Context context, AttributeSet attrs) {
- super(context, attrs);
- commonConstructorCode();
- }
-
- public AutoRepeatButton(Context context) {
- super(context);
- commonConstructorCode();
- }
-}
+package github.daneren2005.dsub.view;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.ImageButton;
+
+public class AutoRepeatButton extends ImageButton {
+
+ private static final long initialRepeatDelay = 1000;
+ private static final long repeatIntervalInMilliseconds = 300;
+ private boolean doClick = true;
+ private Runnable repeatEvent = null;
+
+ private Runnable repeatClickWhileButtonHeldRunnable = new Runnable() {
+ @Override
+ public void run() {
+ doClick = false;
+ //Perform the present repetition of the click action provided by the user
+ // in setOnClickListener().
+ if(repeatEvent != null)
+ repeatEvent.run();
+
+ //Schedule the next repetitions of the click action, using a faster repeat
+ // interval than the initial repeat delay interval.
+ postDelayed(repeatClickWhileButtonHeldRunnable, repeatIntervalInMilliseconds);
+ }
+ };
+
+ private void commonConstructorCode() {
+ this.setOnTouchListener(new OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ int action = event.getAction();
+ if(action == MotionEvent.ACTION_DOWN)
+ {
+ doClick = true;
+ //Just to be sure that we removed all callbacks,
+ // which should have occurred in the ACTION_UP
+ removeCallbacks(repeatClickWhileButtonHeldRunnable);
+
+ //Schedule the start of repetitions after a one half second delay.
+ postDelayed(repeatClickWhileButtonHeldRunnable, initialRepeatDelay);
+
+ setPressed(true);
+ }
+ else if(action == MotionEvent.ACTION_UP) {
+ //Cancel any repetition in progress.
+ removeCallbacks(repeatClickWhileButtonHeldRunnable);
+
+ if(doClick || repeatEvent == null) {
+ performClick();
+ }
+
+ setPressed(false);
+ }
+
+ //Returning true here prevents performClick() from getting called
+ // in the usual manner, which would be redundant, given that we are
+ // already calling it above.
+ return true;
+ }
+ });
+ }
+
+ public void setOnRepeatListener(Runnable runnable) {
+ repeatEvent = runnable;
+ }
+
+ public AutoRepeatButton(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ commonConstructorCode();
+ }
+
+
+ public AutoRepeatButton(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ commonConstructorCode();
+ }
+
+ public AutoRepeatButton(Context context) {
+ super(context);
+ commonConstructorCode();
+ }
+}
diff --git a/src/github/daneren2005/dsub/view/ChangeLog.java b/app/src/main/java/github/daneren2005/dsub/view/ChangeLog.java
index 096583c7..096583c7 100644
--- a/src/github/daneren2005/dsub/view/ChangeLog.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/ChangeLog.java
diff --git a/src/github/daneren2005/dsub/view/ErrorDialog.java b/app/src/main/java/github/daneren2005/dsub/view/ErrorDialog.java
index 0b9d05a0..0b9d05a0 100644
--- a/src/github/daneren2005/dsub/view/ErrorDialog.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/ErrorDialog.java
diff --git a/src/github/daneren2005/dsub/view/FadeOutAnimation.java b/app/src/main/java/github/daneren2005/dsub/view/FadeOutAnimation.java
index 292529e6..817839ef 100644
--- a/src/github/daneren2005/dsub/view/FadeOutAnimation.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/FadeOutAnimation.java
@@ -1,77 +1,77 @@
-/*
- 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 github.daneren2005.dsub.view;
-
-import android.view.View;
-import android.view.animation.AlphaAnimation;
-import android.view.animation.Animation;
-
-/**
- * Fades a view out by changing its alpha value.
- *
- * @author Sindre Mehus
- * @version $Id: Util.java 3203 2012-10-04 09:12:08Z sindre_mehus $
- */
-public class FadeOutAnimation extends AlphaAnimation {
-
- private boolean cancelled;
-
- /**
- * Creates and starts the fade out animation.
- *
- * @param view The view to fade out (or display).
- * @param fadeOut If true, the view is faded out. Otherwise it is immediately made visible.
- * @param durationMillis Fade duration.
- */
- public static void createAndStart(View view, boolean fadeOut, long durationMillis) {
- if (fadeOut) {
- view.clearAnimation();
- view.startAnimation(new FadeOutAnimation(view, durationMillis));
- } else {
- Animation animation = view.getAnimation();
- if (animation instanceof FadeOutAnimation) {
- ((FadeOutAnimation) animation).cancelFadeOut();
- }
- view.clearAnimation();
- view.setVisibility(View.VISIBLE);
- }
- }
-
- FadeOutAnimation(final View view, long durationMillis) {
- super(1.0F, 0.0F);
- setDuration(durationMillis);
- setAnimationListener(new AnimationListener() {
- public void onAnimationStart(Animation animation) {
- }
-
- public void onAnimationRepeat(Animation animation) {
- }
-
- public void onAnimationEnd(Animation animation) {
- if (!cancelled) {
- view.setVisibility(View.INVISIBLE);
- }
- }
- });
- }
-
- private void cancelFadeOut() {
- cancelled = 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 github.daneren2005.dsub.view;
+
+import android.view.View;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+
+/**
+ * Fades a view out by changing its alpha value.
+ *
+ * @author Sindre Mehus
+ * @version $Id: Util.java 3203 2012-10-04 09:12:08Z sindre_mehus $
+ */
+public class FadeOutAnimation extends AlphaAnimation {
+
+ private boolean cancelled;
+
+ /**
+ * Creates and starts the fade out animation.
+ *
+ * @param view The view to fade out (or display).
+ * @param fadeOut If true, the view is faded out. Otherwise it is immediately made visible.
+ * @param durationMillis Fade duration.
+ */
+ public static void createAndStart(View view, boolean fadeOut, long durationMillis) {
+ if (fadeOut) {
+ view.clearAnimation();
+ view.startAnimation(new FadeOutAnimation(view, durationMillis));
+ } else {
+ Animation animation = view.getAnimation();
+ if (animation instanceof FadeOutAnimation) {
+ ((FadeOutAnimation) animation).cancelFadeOut();
+ }
+ view.clearAnimation();
+ view.setVisibility(View.VISIBLE);
+ }
+ }
+
+ FadeOutAnimation(final View view, long durationMillis) {
+ super(1.0F, 0.0F);
+ setDuration(durationMillis);
+ setAnimationListener(new AnimationListener() {
+ public void onAnimationStart(Animation animation) {
+ }
+
+ public void onAnimationRepeat(Animation animation) {
+ }
+
+ public void onAnimationEnd(Animation animation) {
+ if (!cancelled) {
+ view.setVisibility(View.INVISIBLE);
+ }
+ }
+ });
+ }
+
+ private void cancelFadeOut() {
+ cancelled = true;
+ }
+}
diff --git a/src/github/daneren2005/dsub/view/GenreView.java b/app/src/main/java/github/daneren2005/dsub/view/GenreView.java
index 6cd779c5..8dbcf89d 100644
--- a/src/github/daneren2005/dsub/view/GenreView.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/GenreView.java
@@ -1,58 +1,58 @@
-/*
- 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 github.daneren2005.dsub.view;
-
-import android.content.Context;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.TextView;
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.domain.Genre;
-
-public class GenreView extends UpdateView {
- private static final String TAG = GenreView.class.getSimpleName();
-
- private TextView titleView;
- private TextView songsView;
- private TextView albumsView;
-
- public GenreView(Context context) {
- super(context, false);
- LayoutInflater.from(context).inflate(R.layout.genre_list_item, this, true);
-
- titleView = (TextView) findViewById(R.id.genre_name);
- songsView = (TextView) findViewById(R.id.genre_songs);
- albumsView = (TextView) findViewById(R.id.genre_albums);
- }
-
- public void setObjectImpl(Object obj) {
- Genre genre = (Genre) obj;
- titleView.setText(genre.getName());
-
- if(genre.getAlbumCount() != null) {
- songsView.setVisibility(View.VISIBLE);
- albumsView.setVisibility(View.VISIBLE);
- songsView.setText(context.getResources().getString(R.string.select_genre_songs, genre.getSongCount()));
- albumsView.setText(context.getResources().getString(R.string.select_genre_albums, genre.getAlbumCount()));
- } else {
- songsView.setVisibility(View.GONE);
- albumsView.setVisibility(View.GONE);
- }
- }
-}
+/*
+ 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 github.daneren2005.dsub.view;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.TextView;
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.Genre;
+
+public class GenreView extends UpdateView {
+ private static final String TAG = GenreView.class.getSimpleName();
+
+ private TextView titleView;
+ private TextView songsView;
+ private TextView albumsView;
+
+ public GenreView(Context context) {
+ super(context, false);
+ LayoutInflater.from(context).inflate(R.layout.genre_list_item, this, true);
+
+ titleView = (TextView) findViewById(R.id.genre_name);
+ songsView = (TextView) findViewById(R.id.genre_songs);
+ albumsView = (TextView) findViewById(R.id.genre_albums);
+ }
+
+ public void setObjectImpl(Object obj) {
+ Genre genre = (Genre) obj;
+ titleView.setText(genre.getName());
+
+ if(genre.getAlbumCount() != null) {
+ songsView.setVisibility(View.VISIBLE);
+ albumsView.setVisibility(View.VISIBLE);
+ songsView.setText(context.getResources().getString(R.string.select_genre_songs, genre.getSongCount()));
+ albumsView.setText(context.getResources().getString(R.string.select_genre_albums, genre.getAlbumCount()));
+ } else {
+ songsView.setVisibility(View.GONE);
+ albumsView.setVisibility(View.GONE);
+ }
+ }
+}
diff --git a/src/github/daneren2005/dsub/view/HeaderGridView.java b/app/src/main/java/github/daneren2005/dsub/view/HeaderGridView.java
index 8a82f353..8a82f353 100644
--- a/src/github/daneren2005/dsub/view/HeaderGridView.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/HeaderGridView.java
diff --git a/src/github/daneren2005/dsub/view/MyLeadingMarginSpan2.java b/app/src/main/java/github/daneren2005/dsub/view/MyLeadingMarginSpan2.java
index 20281a28..20281a28 100644
--- a/src/github/daneren2005/dsub/view/MyLeadingMarginSpan2.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/MyLeadingMarginSpan2.java
diff --git a/src/github/daneren2005/dsub/view/MyViewFlipper.java b/app/src/main/java/github/daneren2005/dsub/view/MyViewFlipper.java
index 26a3de08..26a3de08 100644
--- a/src/github/daneren2005/dsub/view/MyViewFlipper.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/MyViewFlipper.java
diff --git a/src/github/daneren2005/dsub/view/PlaylistSongView.java b/app/src/main/java/github/daneren2005/dsub/view/PlaylistSongView.java
index 53e9fafc..0264a785 100644
--- a/src/github/daneren2005/dsub/view/PlaylistSongView.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/PlaylistSongView.java
@@ -1,102 +1,102 @@
-/*
- 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 github.daneren2005.dsub.view;
-
-import android.content.Context;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import java.util.List;
-
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.domain.MusicDirectory;
-import github.daneren2005.dsub.domain.Playlist;
-import github.daneren2005.dsub.util.FileUtil;
-import github.daneren2005.dsub.util.SyncUtil;
-import github.daneren2005.dsub.util.Util;
-
-public class PlaylistSongView extends UpdateView {
- private static final String TAG = PlaylistSongView.class.getSimpleName();
-
- private Context context;
- private Playlist playlist;
-
- private TextView titleView;
- private TextView countView;
- private int count = 0;
- private List<MusicDirectory.Entry> songs;
-
- public PlaylistSongView(Context context) {
- super(context, false);
- this.context = context;
- LayoutInflater.from(context).inflate(R.layout.basic_count_item, this, true);
-
- titleView = (TextView) findViewById(R.id.basic_count_name);
- countView = (TextView) findViewById(R.id.basic_count_count);
- }
-
- protected void setObjectImpl(Object obj1, Object obj2) {
- this.playlist = (Playlist) obj1;
- this.songs = (List<MusicDirectory.Entry>) obj2;
- count = 0;
- titleView.setText(playlist.getName());
- // Make sure to hide initially so it's not present briefly before update
- countView.setVisibility(View.GONE);
- }
-
- @Override
- protected void updateBackground() {
- // Make sure to reset when starting count
- count = 0;
-
- // Don't try to lookup playlist for Create New
- if(!"-1".equals(playlist.getId())) {
- MusicDirectory cache = FileUtil.deserialize(context, Util.getCacheName(context, "playlist", playlist.getId()), MusicDirectory.class);
- if(cache != null) {
- // Try to find song instances in the given playlists
- for(MusicDirectory.Entry song: songs) {
- if(cache.getChildren().contains(song)) {
- count++;
- }
- }
- }
- }
- }
-
- @Override
- protected void update() {
- // Update count display with appropriate information
- if(count <= 0) {
- countView.setVisibility(View.GONE);
- } else {
- String displayName;
- if(count < 10) {
- displayName = "0" + count;
- } else {
- displayName = "" + count;
- }
-
- countView.setText(displayName);
- countView.setVisibility(View.VISIBLE);
- }
- }
-}
+/*
+ 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 github.daneren2005.dsub.view;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.util.List;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.MusicDirectory;
+import github.daneren2005.dsub.domain.Playlist;
+import github.daneren2005.dsub.util.FileUtil;
+import github.daneren2005.dsub.util.SyncUtil;
+import github.daneren2005.dsub.util.Util;
+
+public class PlaylistSongView extends UpdateView {
+ private static final String TAG = PlaylistSongView.class.getSimpleName();
+
+ private Context context;
+ private Playlist playlist;
+
+ private TextView titleView;
+ private TextView countView;
+ private int count = 0;
+ private List<MusicDirectory.Entry> songs;
+
+ public PlaylistSongView(Context context) {
+ super(context, false);
+ this.context = context;
+ LayoutInflater.from(context).inflate(R.layout.basic_count_item, this, true);
+
+ titleView = (TextView) findViewById(R.id.basic_count_name);
+ countView = (TextView) findViewById(R.id.basic_count_count);
+ }
+
+ protected void setObjectImpl(Object obj1, Object obj2) {
+ this.playlist = (Playlist) obj1;
+ this.songs = (List<MusicDirectory.Entry>) obj2;
+ count = 0;
+ titleView.setText(playlist.getName());
+ // Make sure to hide initially so it's not present briefly before update
+ countView.setVisibility(View.GONE);
+ }
+
+ @Override
+ protected void updateBackground() {
+ // Make sure to reset when starting count
+ count = 0;
+
+ // Don't try to lookup playlist for Create New
+ if(!"-1".equals(playlist.getId())) {
+ MusicDirectory cache = FileUtil.deserialize(context, Util.getCacheName(context, "playlist", playlist.getId()), MusicDirectory.class);
+ if(cache != null) {
+ // Try to find song instances in the given playlists
+ for(MusicDirectory.Entry song: songs) {
+ if(cache.getChildren().contains(song)) {
+ count++;
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ protected void update() {
+ // Update count display with appropriate information
+ if(count <= 0) {
+ countView.setVisibility(View.GONE);
+ } else {
+ String displayName;
+ if(count < 10) {
+ displayName = "0" + count;
+ } else {
+ displayName = "" + count;
+ }
+
+ countView.setText(displayName);
+ countView.setVisibility(View.VISIBLE);
+ }
+ }
+}
diff --git a/src/github/daneren2005/dsub/view/PlaylistView.java b/app/src/main/java/github/daneren2005/dsub/view/PlaylistView.java
index 8b5e8ca9..25613984 100644
--- a/src/github/daneren2005/dsub/view/PlaylistView.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/PlaylistView.java
@@ -1,69 +1,69 @@
-/*
- 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 github.daneren2005.dsub.view;
-
-import android.content.Context;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.TextView;
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.domain.Playlist;
-import github.daneren2005.dsub.util.SyncUtil;
-
-/**
- * Used to display albums in a {@code ListView}.
- *
- * @author Sindre Mehus
- */
-public class PlaylistView extends UpdateView {
- private static final String TAG = PlaylistView.class.getSimpleName();
-
- private Context context;
- private Playlist playlist;
-
- private TextView titleView;
-
- public PlaylistView(Context context) {
- super(context);
- this.context = context;
- LayoutInflater.from(context).inflate(R.layout.basic_list_item, this, true);
-
- titleView = (TextView) findViewById(R.id.item_name);
- starButton = (ImageButton) findViewById(R.id.item_star);
- starButton.setFocusable(false);
- moreButton = (ImageView) findViewById(R.id.item_more);
- moreButton.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- v.showContextMenu();
- }
- });
- }
-
- protected void setObjectImpl(Object obj) {
- this.playlist = (Playlist) obj;
- titleView.setText(playlist.getName());
- }
-
- @Override
- protected void updateBackground() {
- pinned = SyncUtil.isSyncedPlaylist(context, playlist.getId());
- }
-}
+/*
+ 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 github.daneren2005.dsub.view;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.TextView;
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.Playlist;
+import github.daneren2005.dsub.util.SyncUtil;
+
+/**
+ * Used to display albums in a {@code ListView}.
+ *
+ * @author Sindre Mehus
+ */
+public class PlaylistView extends UpdateView {
+ private static final String TAG = PlaylistView.class.getSimpleName();
+
+ private Context context;
+ private Playlist playlist;
+
+ private TextView titleView;
+
+ public PlaylistView(Context context) {
+ super(context);
+ this.context = context;
+ LayoutInflater.from(context).inflate(R.layout.basic_list_item, this, true);
+
+ titleView = (TextView) findViewById(R.id.item_name);
+ starButton = (ImageButton) findViewById(R.id.item_star);
+ starButton.setFocusable(false);
+ moreButton = (ImageView) findViewById(R.id.item_more);
+ moreButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ v.showContextMenu();
+ }
+ });
+ }
+
+ protected void setObjectImpl(Object obj) {
+ this.playlist = (Playlist) obj;
+ titleView.setText(playlist.getName());
+ }
+
+ @Override
+ protected void updateBackground() {
+ pinned = SyncUtil.isSyncedPlaylist(context, playlist.getId());
+ }
+}
diff --git a/src/github/daneren2005/dsub/view/PodcastChannelView.java b/app/src/main/java/github/daneren2005/dsub/view/PodcastChannelView.java
index 0e773d14..ada8019e 100644
--- a/src/github/daneren2005/dsub/view/PodcastChannelView.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/PodcastChannelView.java
@@ -1,87 +1,87 @@
-/*
- 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 github.daneren2005.dsub.view;
-
-import android.content.Context;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.TextView;
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.domain.PodcastChannel;
-import github.daneren2005.dsub.util.SyncUtil;
-import github.daneren2005.dsub.util.FileUtil;
-import java.io.File;
-
-public class PodcastChannelView extends UpdateView {
- private static final String TAG = PodcastChannelView.class.getSimpleName();
-
- private Context context;
- private PodcastChannel channel;
- private File file;
-
- private TextView titleView;
-
- public PodcastChannelView(Context context) {
- super(context);
- this.context = context;
- LayoutInflater.from(context).inflate(R.layout.basic_list_item, this, true);
-
- titleView = (TextView) findViewById(R.id.item_name);
- starButton = (ImageButton) findViewById(R.id.item_star);
- starButton.setFocusable(false);
- moreButton = (ImageView) findViewById(R.id.item_more);
- moreButton.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- v.showContextMenu();
- }
- });
- }
-
- protected void setObjectImpl(Object obj) {
- channel = (PodcastChannel) obj;
- if(channel.getName() != null) {
- titleView.setText(channel.getName());
- } else {
- titleView.setText(channel.getUrl());
- }
- file = FileUtil.getPodcastDirectory(context, channel);
- }
-
- @Override
- protected void updateBackground() {
- if(SyncUtil.isSyncedPodcast(context, channel.getId())) {
- if(exists) {
- shaded = false;
- exists = false;
- }
- pinned = true;
- } else if(file.exists()) {
- if(pinned) {
- shaded = false;
- pinned = false;
- }
- exists = true;
- } else {
- pinned = false;
- exists = false;
- }
- }
-}
+/*
+ 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 github.daneren2005.dsub.view;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.TextView;
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.PodcastChannel;
+import github.daneren2005.dsub.util.SyncUtil;
+import github.daneren2005.dsub.util.FileUtil;
+import java.io.File;
+
+public class PodcastChannelView extends UpdateView {
+ private static final String TAG = PodcastChannelView.class.getSimpleName();
+
+ private Context context;
+ private PodcastChannel channel;
+ private File file;
+
+ private TextView titleView;
+
+ public PodcastChannelView(Context context) {
+ super(context);
+ this.context = context;
+ LayoutInflater.from(context).inflate(R.layout.basic_list_item, this, true);
+
+ titleView = (TextView) findViewById(R.id.item_name);
+ starButton = (ImageButton) findViewById(R.id.item_star);
+ starButton.setFocusable(false);
+ moreButton = (ImageView) findViewById(R.id.item_more);
+ moreButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ v.showContextMenu();
+ }
+ });
+ }
+
+ protected void setObjectImpl(Object obj) {
+ channel = (PodcastChannel) obj;
+ if(channel.getName() != null) {
+ titleView.setText(channel.getName());
+ } else {
+ titleView.setText(channel.getUrl());
+ }
+ file = FileUtil.getPodcastDirectory(context, channel);
+ }
+
+ @Override
+ protected void updateBackground() {
+ if(SyncUtil.isSyncedPodcast(context, channel.getId())) {
+ if(exists) {
+ shaded = false;
+ exists = false;
+ }
+ pinned = true;
+ } else if(file.exists()) {
+ if(pinned) {
+ shaded = false;
+ pinned = false;
+ }
+ exists = true;
+ } else {
+ pinned = false;
+ exists = false;
+ }
+ }
+}
diff --git a/src/github/daneren2005/dsub/view/RecyclingImageView.java b/app/src/main/java/github/daneren2005/dsub/view/RecyclingImageView.java
index 0c85697f..0c85697f 100644
--- a/src/github/daneren2005/dsub/view/RecyclingImageView.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/RecyclingImageView.java
diff --git a/src/github/daneren2005/dsub/view/SeekBarPreference.java b/app/src/main/java/github/daneren2005/dsub/view/SeekBarPreference.java
index a22d049b..fa8e8b3a 100644
--- a/src/github/daneren2005/dsub/view/SeekBarPreference.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/SeekBarPreference.java
@@ -1,156 +1,156 @@
-/*
- * Copyright (C) 2012 Christopher Eby <kreed@kreed.org>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package github.daneren2005.dsub.view;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.preference.DialogPreference;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-import android.widget.SeekBar;
-import android.widget.TextView;
-
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.util.Constants;
-
-/**
- * SeekBar preference to set the shake force threshold.
- */
-public class SeekBarPreference extends DialogPreference implements SeekBar.OnSeekBarChangeListener {
- private static final String TAG = SeekBarPreference.class.getSimpleName();
- /**
- * The current value.
- */
- private String mValue;
- private int mMin;
- private int mMax;
- private float mStepSize;
- private String mDisplay;
-
- /**
- * Our context (needed for getResources())
- */
- private Context mContext;
-
- /**
- * TextView to display current threshold.
- */
- private TextView mValueText;
-
- public SeekBarPreference(Context context, AttributeSet attrs)
- {
- super(context, attrs);
- mContext = context;
-
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SeekBarPreference);
- mMin = a.getInteger(R.styleable.SeekBarPreference_min, 0);
- mMax = a.getInteger(R.styleable.SeekBarPreference_max, 100);
- mStepSize = a.getFloat(R.styleable.SeekBarPreference_stepSize, 1f);
- mDisplay = a.getString(R.styleable.SeekBarPreference_display);
- if(mDisplay == null) {
- mDisplay = "%.0f";
- }
- }
-
- @Override
- public CharSequence getSummary()
- {
- return getSummary(mValue);
- }
-
- @Override
- protected Object onGetDefaultValue(TypedArray a, int index)
- {
- return a.getString(index);
- }
-
- @Override
- protected void onSetInitialValue(boolean restoreValue, Object defaultValue)
- {
- mValue = restoreValue ? getPersistedString((String) defaultValue) : (String)defaultValue;
- }
-
- /**
- * Create the summary for the given value.
- *
- * @param value The force threshold.
- * @return A string representation of the threshold.
- */
- private String getSummary(String value) {
- try {
- int val = Integer.parseInt(value);
- return String.format(mDisplay, (val + mMin) / mStepSize);
- } catch (Exception e) {
- return "";
- }
- }
-
- @Override
- protected View onCreateDialogView()
- {
- View view = super.onCreateDialogView();
-
- mValueText = (TextView)view.findViewById(R.id.value);
- mValueText.setText(getSummary(mValue));
-
- SeekBar seekBar = (SeekBar)view.findViewById(R.id.seek_bar);
- seekBar.setMax(mMax - mMin);
- try {
- seekBar.setProgress(Integer.parseInt(mValue));
- } catch(Exception e) {
- seekBar.setProgress(0);
- }
- seekBar.setOnSeekBarChangeListener(this);
-
- return view;
- }
-
- @Override
- protected void onDialogClosed(boolean positiveResult)
- {
- if(positiveResult) {
- persistString(mValue);
- notifyChanged();
- }
- }
-
- @Override
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
- {
- if (fromUser) {
- mValue = String.valueOf(progress);
- mValueText.setText(getSummary(mValue));
- }
- }
-
- @Override
- public void onStartTrackingTouch(SeekBar seekBar)
- {
- }
-
- @Override
- public void onStopTrackingTouch(SeekBar seekBar)
- {
- }
-}
+/*
+ * Copyright (C) 2012 Christopher Eby <kreed@kreed.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package github.daneren2005.dsub.view;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.preference.DialogPreference;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.util.Constants;
+
+/**
+ * SeekBar preference to set the shake force threshold.
+ */
+public class SeekBarPreference extends DialogPreference implements SeekBar.OnSeekBarChangeListener {
+ private static final String TAG = SeekBarPreference.class.getSimpleName();
+ /**
+ * The current value.
+ */
+ private String mValue;
+ private int mMin;
+ private int mMax;
+ private float mStepSize;
+ private String mDisplay;
+
+ /**
+ * Our context (needed for getResources())
+ */
+ private Context mContext;
+
+ /**
+ * TextView to display current threshold.
+ */
+ private TextView mValueText;
+
+ public SeekBarPreference(Context context, AttributeSet attrs)
+ {
+ super(context, attrs);
+ mContext = context;
+
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SeekBarPreference);
+ mMin = a.getInteger(R.styleable.SeekBarPreference_min, 0);
+ mMax = a.getInteger(R.styleable.SeekBarPreference_max, 100);
+ mStepSize = a.getFloat(R.styleable.SeekBarPreference_stepSize, 1f);
+ mDisplay = a.getString(R.styleable.SeekBarPreference_display);
+ if(mDisplay == null) {
+ mDisplay = "%.0f";
+ }
+ }
+
+ @Override
+ public CharSequence getSummary()
+ {
+ return getSummary(mValue);
+ }
+
+ @Override
+ protected Object onGetDefaultValue(TypedArray a, int index)
+ {
+ return a.getString(index);
+ }
+
+ @Override
+ protected void onSetInitialValue(boolean restoreValue, Object defaultValue)
+ {
+ mValue = restoreValue ? getPersistedString((String) defaultValue) : (String)defaultValue;
+ }
+
+ /**
+ * Create the summary for the given value.
+ *
+ * @param value The force threshold.
+ * @return A string representation of the threshold.
+ */
+ private String getSummary(String value) {
+ try {
+ int val = Integer.parseInt(value);
+ return String.format(mDisplay, (val + mMin) / mStepSize);
+ } catch (Exception e) {
+ return "";
+ }
+ }
+
+ @Override
+ protected View onCreateDialogView()
+ {
+ View view = super.onCreateDialogView();
+
+ mValueText = (TextView)view.findViewById(R.id.value);
+ mValueText.setText(getSummary(mValue));
+
+ SeekBar seekBar = (SeekBar)view.findViewById(R.id.seek_bar);
+ seekBar.setMax(mMax - mMin);
+ try {
+ seekBar.setProgress(Integer.parseInt(mValue));
+ } catch(Exception e) {
+ seekBar.setProgress(0);
+ }
+ seekBar.setOnSeekBarChangeListener(this);
+
+ return view;
+ }
+
+ @Override
+ protected void onDialogClosed(boolean positiveResult)
+ {
+ if(positiveResult) {
+ persistString(mValue);
+ notifyChanged();
+ }
+ }
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
+ {
+ if (fromUser) {
+ mValue = String.valueOf(progress);
+ mValueText.setText(getSummary(mValue));
+ }
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar)
+ {
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar)
+ {
+ }
+}
diff --git a/src/github/daneren2005/dsub/view/SettingView.java b/app/src/main/java/github/daneren2005/dsub/view/SettingView.java
index 95452408..1c78706e 100644
--- a/src/github/daneren2005/dsub/view/SettingView.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/SettingView.java
@@ -1,102 +1,102 @@
-/*
- 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 2014 (C) Scott Jackson
-*/
-
-package github.daneren2005.dsub.view;
-
-import android.content.Context;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.CheckedTextView;
-
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.domain.User;
-
-import static github.daneren2005.dsub.domain.User.Setting;
-
-public class SettingView extends UpdateView {
- Setting setting;
-
- CheckedTextView view;
-
- public SettingView(Context context) {
- super(context, false);
- this.context = context;
- LayoutInflater.from(context).inflate(android.R.layout.simple_list_item_multiple_choice, this, true);
-
- view = (CheckedTextView) findViewById(android.R.id.text1);
- }
-
- protected void setObjectImpl(Object obj, Object editable) {
- this.setting = (Setting) obj;
-
- // Can't edit non-role parts
- String name = setting.getName();
- if(name.indexOf("Role") == -1) {
- editable = false;
- }
-
- int res = -1;
- if(User.SCROBBLING.equals(name)) {
- res = R.string.admin_scrobblingEnabled;
- } else if(User.ADMIN.equals(name)) {
- res = R.string.admin_role_admin;
- } else if(User.SETTINGS.equals(name)) {
- res = R.string.admin_role_settings;
- } else if(User.DOWNLOAD.equals(name)) {
- res = R.string.admin_role_download;
- } else if(User.UPLOAD.equals(name)) {
- res = R.string.admin_role_upload;
- } else if(User.COVERART.equals(name)) {
- res = R.string.admin_role_coverArt;
- } else if(User.COMMENT.equals(name)) {
- res = R.string.admin_role_comment;
- } else if(User.PODCAST.equals(name)) {
- res = R.string.admin_role_podcast;
- } else if(User.STREAM.equals(name)) {
- res = R.string.admin_role_stream;
- } else if(User.JUKEBOX.equals(name)) {
- res = R.string.admin_role_jukebox;
- } else if(User.SHARE.equals(name)) {
- res = R.string.admin_role_share;
- } else if(User.LASTFM.equals(name)) {
- res = R.string.admin_role_lastfm;
- } else {
- // Last resort to display the raw value
- view.setText(name);
- }
-
- if(res != -1) {
- view.setText(res);
- }
-
- if(setting.getValue()) {
- view.setChecked(setting.getValue());
- } else {
- view.setChecked(false);
- }
-
- if((Boolean) editable) {
- view.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- view.toggle();
- setting.setValue(view.isChecked());
- }
- });
- } else {
- view.setOnClickListener(null);
- }
- }
-}
+/*
+ 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 2014 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.view;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.CheckedTextView;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.User;
+
+import static github.daneren2005.dsub.domain.User.Setting;
+
+public class SettingView extends UpdateView {
+ Setting setting;
+
+ CheckedTextView view;
+
+ public SettingView(Context context) {
+ super(context, false);
+ this.context = context;
+ LayoutInflater.from(context).inflate(android.R.layout.simple_list_item_multiple_choice, this, true);
+
+ view = (CheckedTextView) findViewById(android.R.id.text1);
+ }
+
+ protected void setObjectImpl(Object obj, Object editable) {
+ this.setting = (Setting) obj;
+
+ // Can't edit non-role parts
+ String name = setting.getName();
+ if(name.indexOf("Role") == -1) {
+ editable = false;
+ }
+
+ int res = -1;
+ if(User.SCROBBLING.equals(name)) {
+ res = R.string.admin_scrobblingEnabled;
+ } else if(User.ADMIN.equals(name)) {
+ res = R.string.admin_role_admin;
+ } else if(User.SETTINGS.equals(name)) {
+ res = R.string.admin_role_settings;
+ } else if(User.DOWNLOAD.equals(name)) {
+ res = R.string.admin_role_download;
+ } else if(User.UPLOAD.equals(name)) {
+ res = R.string.admin_role_upload;
+ } else if(User.COVERART.equals(name)) {
+ res = R.string.admin_role_coverArt;
+ } else if(User.COMMENT.equals(name)) {
+ res = R.string.admin_role_comment;
+ } else if(User.PODCAST.equals(name)) {
+ res = R.string.admin_role_podcast;
+ } else if(User.STREAM.equals(name)) {
+ res = R.string.admin_role_stream;
+ } else if(User.JUKEBOX.equals(name)) {
+ res = R.string.admin_role_jukebox;
+ } else if(User.SHARE.equals(name)) {
+ res = R.string.admin_role_share;
+ } else if(User.LASTFM.equals(name)) {
+ res = R.string.admin_role_lastfm;
+ } else {
+ // Last resort to display the raw value
+ view.setText(name);
+ }
+
+ if(res != -1) {
+ view.setText(res);
+ }
+
+ if(setting.getValue()) {
+ view.setChecked(setting.getValue());
+ } else {
+ view.setChecked(false);
+ }
+
+ if((Boolean) editable) {
+ view.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ view.toggle();
+ setting.setValue(view.isChecked());
+ }
+ });
+ } else {
+ view.setOnClickListener(null);
+ }
+ }
+}
diff --git a/src/github/daneren2005/dsub/view/ShareView.java b/app/src/main/java/github/daneren2005/dsub/view/ShareView.java
index 2b2fbee4..bfb5b198 100644
--- a/src/github/daneren2005/dsub/view/ShareView.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/ShareView.java
@@ -1,65 +1,65 @@
-/*
- 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 github.daneren2005.dsub.view;
-
-import android.content.Context;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import java.text.SimpleDateFormat;
-import java.util.Locale;
-
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.domain.Share;
-
-public class ShareView extends UpdateView {
- private static final String TAG = ShareView.class.getSimpleName();
-
- private TextView titleView;
- private TextView descriptionView;
-
- public ShareView(Context context) {
- super(context, false);
- LayoutInflater.from(context).inflate(R.layout.complex_list_item, this, true);
-
- titleView = (TextView) findViewById(R.id.item_name);
- descriptionView = (TextView) findViewById(R.id.item_description);
- starButton = (ImageButton) findViewById(R.id.item_star);
- starButton.setFocusable(false);
- moreButton = (ImageView) findViewById(R.id.item_more);
- moreButton.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- v.showContextMenu();
- }
- });
- }
-
- public void setObjectImpl(Object obj) {
- Share share = (Share) obj;
- titleView.setText(share.getName());
- if(share.getExpires() != null) {
- descriptionView.setText(context.getResources().getString(R.string.share_expires, new SimpleDateFormat("E MMM d, yyyy", Locale.ENGLISH).format(share.getExpires())));
- } else {
- descriptionView.setText(context.getResources().getString(R.string.share_expires_never));
- }
- }
-}
+/*
+ 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 github.daneren2005.dsub.view;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.text.SimpleDateFormat;
+import java.util.Locale;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.Share;
+
+public class ShareView extends UpdateView {
+ private static final String TAG = ShareView.class.getSimpleName();
+
+ private TextView titleView;
+ private TextView descriptionView;
+
+ public ShareView(Context context) {
+ super(context, false);
+ LayoutInflater.from(context).inflate(R.layout.complex_list_item, this, true);
+
+ titleView = (TextView) findViewById(R.id.item_name);
+ descriptionView = (TextView) findViewById(R.id.item_description);
+ starButton = (ImageButton) findViewById(R.id.item_star);
+ starButton.setFocusable(false);
+ moreButton = (ImageView) findViewById(R.id.item_more);
+ moreButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ v.showContextMenu();
+ }
+ });
+ }
+
+ public void setObjectImpl(Object obj) {
+ Share share = (Share) obj;
+ titleView.setText(share.getName());
+ if(share.getExpires() != null) {
+ descriptionView.setText(context.getResources().getString(R.string.share_expires, new SimpleDateFormat("E MMM d, yyyy", Locale.ENGLISH).format(share.getExpires())));
+ } else {
+ descriptionView.setText(context.getResources().getString(R.string.share_expires_never));
+ }
+ }
+}
diff --git a/src/github/daneren2005/dsub/view/SongView.java b/app/src/main/java/github/daneren2005/dsub/view/SongView.java
index 2fbaedc3..2fbaedc3 100644
--- a/src/github/daneren2005/dsub/view/SongView.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/SongView.java
diff --git a/src/github/daneren2005/dsub/view/SquareImageView.java b/app/src/main/java/github/daneren2005/dsub/view/SquareImageView.java
index 44a74e16..66ab7d8d 100644
--- a/src/github/daneren2005/dsub/view/SquareImageView.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/SquareImageView.java
@@ -1,32 +1,32 @@
-/*
- 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 2014 (C) Scott Jackson
-*/
-
-package github.daneren2005.dsub.view;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.ImageView;
-
-public class SquareImageView extends RecyclingImageView {
- public SquareImageView(final Context context, final AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- public void onMeasure(final int widthSpec, final int heightSpec) {
- super.onMeasure(widthSpec, heightSpec);
- setMeasuredDimension(getMeasuredWidth(), getMeasuredWidth());
- }
-}
+/*
+ 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 2014 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.view;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+
+public class SquareImageView extends RecyclingImageView {
+ public SquareImageView(final Context context, final AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public void onMeasure(final int widthSpec, final int heightSpec) {
+ super.onMeasure(widthSpec, heightSpec);
+ setMeasuredDimension(getMeasuredWidth(), getMeasuredWidth());
+ }
+}
diff --git a/src/github/daneren2005/dsub/view/UnscrollableGridView.java b/app/src/main/java/github/daneren2005/dsub/view/UnscrollableGridView.java
index 1e18f032..3047d5d7 100644
--- a/src/github/daneren2005/dsub/view/UnscrollableGridView.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/UnscrollableGridView.java
@@ -1,128 +1,128 @@
-package github.daneren2005.dsub.view;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.os.Build;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-import android.widget.AbsListView;
-import android.widget.GridView;
-import android.widget.ListAdapter;
-
-import java.lang.reflect.Field;
-
-/**
- * Created by Scott on 4/26/2014.
- */
-public class UnscrollableGridView extends GridView {
- private static final String TAG = UnscrollableGridView.class.getSimpleName();
-
- public UnscrollableGridView(Context context) {
- super(context);
- }
-
- public UnscrollableGridView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public UnscrollableGridView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- @Override
- public int getColumnWidth() {
- // This method will be called from onMeasure() too.
- // It's better to use getMeasuredWidth(), as it is safe in this case.
-
- int hSpacing = 20;
- try {
- Field field = GridView.class.getDeclaredField("mHorizontalSpacing");
- field.setAccessible(true);
- hSpacing = field.getInt(this);
- } catch(Exception e) {
-
- }
-
- final int totalHorizontalSpacing = getNumColumnsCompat() > 0 ? (getNumColumnsCompat() - 1) * hSpacing : 0;
- return (getMeasuredWidth() - getPaddingLeft() - getPaddingRight() - totalHorizontalSpacing) / getNumColumnsCompat();
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- // Sets the padding for this view.
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
- final int measuredWidth = getMeasuredWidth();
- final int childWidth = getColumnWidth();
- int childHeight = 0;
-
- // If there's an adapter, use it to calculate the height of this view.
- final ListAdapter adapter = getAdapter();
- final int count;
-
- // There shouldn't be any inherent size (due to padding) if there are no child views.
- if (adapter == null || (count = adapter.getCount()) == 0) {
- setMeasuredDimension(0, 0);
- return;
- }
-
- // Get the first child from the adapter.
- final View child = adapter.getView(0, null, this);
- if (child != null) {
- // Set a default LayoutParams on the child, if it doesn't have one on its own.
- AbsListView.LayoutParams params = (AbsListView.LayoutParams) child.getLayoutParams();
- if (params == null) {
- params = new AbsListView.LayoutParams(AbsListView.LayoutParams.WRAP_CONTENT,
- AbsListView.LayoutParams.WRAP_CONTENT);
- child.setLayoutParams(params);
- }
-
- // Measure the exact width of the child, and the height based on the width.
- // Note: the child takes care of calculating its height.
- int childWidthSpec = MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY);
- int childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
- child.measure(childWidthSpec, childHeightSpec);
- childHeight = child.getMeasuredHeight();
- }
-
- int vSpacing = 10;
- try {
- Field field = GridView.class.getDeclaredField("mVerticalSpacing");
- field.setAccessible(true);
- vSpacing = field.getInt(this);
- } catch(Exception e) {
-
- }
-
- // Number of rows required to 'mTotal' items.
- final int rows = (int) Math.ceil((double) getCount() / getNumColumnsCompat());
- final int childrenHeight = childHeight * rows;
- final int totalVerticalSpacing = rows > 0 ? (rows - 1) * vSpacing : 0;
-
- // Total height of this view.
- final int measuredHeight = Math.abs(childrenHeight + getPaddingTop() + getPaddingBottom() + totalVerticalSpacing);
- setMeasuredDimension(measuredWidth, measuredHeight);
- }
-
- private int getNumColumnsCompat() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- return getNumColumnsCompat11();
- } else {
- int columns = 0;
- int children = getChildCount();
- if (children > 0) {
- int width = getChildAt(0).getMeasuredWidth();
- if (width > 0) {
- columns = getWidth() / width;
- }
- }
- return columns > 0 ? columns : AUTO_FIT;
- }
- }
-
- @TargetApi(Build.VERSION_CODES.HONEYCOMB)
- private int getNumColumnsCompat11() {
- return getNumColumns();
- }
-}
+package github.daneren2005.dsub.view;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.widget.AbsListView;
+import android.widget.GridView;
+import android.widget.ListAdapter;
+
+import java.lang.reflect.Field;
+
+/**
+ * Created by Scott on 4/26/2014.
+ */
+public class UnscrollableGridView extends GridView {
+ private static final String TAG = UnscrollableGridView.class.getSimpleName();
+
+ public UnscrollableGridView(Context context) {
+ super(context);
+ }
+
+ public UnscrollableGridView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public UnscrollableGridView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @Override
+ public int getColumnWidth() {
+ // This method will be called from onMeasure() too.
+ // It's better to use getMeasuredWidth(), as it is safe in this case.
+
+ int hSpacing = 20;
+ try {
+ Field field = GridView.class.getDeclaredField("mHorizontalSpacing");
+ field.setAccessible(true);
+ hSpacing = field.getInt(this);
+ } catch(Exception e) {
+
+ }
+
+ final int totalHorizontalSpacing = getNumColumnsCompat() > 0 ? (getNumColumnsCompat() - 1) * hSpacing : 0;
+ return (getMeasuredWidth() - getPaddingLeft() - getPaddingRight() - totalHorizontalSpacing) / getNumColumnsCompat();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ // Sets the padding for this view.
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ final int measuredWidth = getMeasuredWidth();
+ final int childWidth = getColumnWidth();
+ int childHeight = 0;
+
+ // If there's an adapter, use it to calculate the height of this view.
+ final ListAdapter adapter = getAdapter();
+ final int count;
+
+ // There shouldn't be any inherent size (due to padding) if there are no child views.
+ if (adapter == null || (count = adapter.getCount()) == 0) {
+ setMeasuredDimension(0, 0);
+ return;
+ }
+
+ // Get the first child from the adapter.
+ final View child = adapter.getView(0, null, this);
+ if (child != null) {
+ // Set a default LayoutParams on the child, if it doesn't have one on its own.
+ AbsListView.LayoutParams params = (AbsListView.LayoutParams) child.getLayoutParams();
+ if (params == null) {
+ params = new AbsListView.LayoutParams(AbsListView.LayoutParams.WRAP_CONTENT,
+ AbsListView.LayoutParams.WRAP_CONTENT);
+ child.setLayoutParams(params);
+ }
+
+ // Measure the exact width of the child, and the height based on the width.
+ // Note: the child takes care of calculating its height.
+ int childWidthSpec = MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY);
+ int childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ child.measure(childWidthSpec, childHeightSpec);
+ childHeight = child.getMeasuredHeight();
+ }
+
+ int vSpacing = 10;
+ try {
+ Field field = GridView.class.getDeclaredField("mVerticalSpacing");
+ field.setAccessible(true);
+ vSpacing = field.getInt(this);
+ } catch(Exception e) {
+
+ }
+
+ // Number of rows required to 'mTotal' items.
+ final int rows = (int) Math.ceil((double) getCount() / getNumColumnsCompat());
+ final int childrenHeight = childHeight * rows;
+ final int totalVerticalSpacing = rows > 0 ? (rows - 1) * vSpacing : 0;
+
+ // Total height of this view.
+ final int measuredHeight = Math.abs(childrenHeight + getPaddingTop() + getPaddingBottom() + totalVerticalSpacing);
+ setMeasuredDimension(measuredWidth, measuredHeight);
+ }
+
+ private int getNumColumnsCompat() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ return getNumColumnsCompat11();
+ } else {
+ int columns = 0;
+ int children = getChildCount();
+ if (children > 0) {
+ int width = getChildAt(0).getMeasuredWidth();
+ if (width > 0) {
+ columns = getWidth() / width;
+ }
+ }
+ return columns > 0 ? columns : AUTO_FIT;
+ }
+ }
+
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ private int getNumColumnsCompat11() {
+ return getNumColumns();
+ }
+}
diff --git a/src/github/daneren2005/dsub/view/UpdateView.java b/app/src/main/java/github/daneren2005/dsub/view/UpdateView.java
index c491fc6d..f9c62121 100644
--- a/src/github/daneren2005/dsub/view/UpdateView.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/UpdateView.java
@@ -1,286 +1,286 @@
-/*
- 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 github.daneren2005.dsub.view;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.os.Handler;
-import android.os.Looper;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AbsListView;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.RatingBar;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.WeakHashMap;
-
-import github.daneren2005.dsub.domain.MusicDirectory;
-import github.daneren2005.dsub.util.ImageLoader;
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.util.SilentBackgroundTask;
-
-public class UpdateView extends LinearLayout {
- private static final String TAG = UpdateView.class.getSimpleName();
- private static final WeakHashMap<UpdateView, ?> INSTANCES = new WeakHashMap<UpdateView, Object>();
-
- private static Handler backgroundHandler;
- private static Handler uiHandler;
- private static Runnable updateRunnable;
- private static int activeActivities = 0;
-
- protected Context context;
- protected RatingBar ratingBar;
- protected ImageButton starButton;
- protected ImageView moreButton;
-
- protected boolean exists = false;
- protected boolean pinned = false;
- protected boolean shaded = false;
- protected boolean starred = false;
- protected boolean isStarred = false;
- protected int isRated = 0;
- protected int rating = 0;
- protected SilentBackgroundTask<Void> imageTask = null;
-
- protected final boolean autoUpdate;
-
- public UpdateView(Context context) {
- this(context, true);
- }
- public UpdateView(Context context, boolean autoUpdate) {
- super(context);
- this.context = context;
- this.autoUpdate = autoUpdate;
-
- setLayoutParams(new AbsListView.LayoutParams(
- ViewGroup.LayoutParams.FILL_PARENT,
- ViewGroup.LayoutParams.WRAP_CONTENT));
-
- if(autoUpdate) {
- INSTANCES.put(this, null);
- }
- startUpdater();
- }
-
- @Override
- public void setPressed(boolean pressed) {
-
- }
-
- public void setObject(Object obj) {
- setObjectImpl(obj);
- updateBackground();
- update();
- }
- public void setObject(Object obj1, Object obj2) {
- if(imageTask != null) {
- imageTask.cancel();
- imageTask = null;
- }
-
- setObjectImpl(obj1, obj2);
- backgroundHandler.post(new Runnable() {
- @Override
- public void run() {
- updateBackground();
- uiHandler.post(new Runnable() {
- @Override
- public void run() {
- update();
- }
- });
- }
- });
- }
- protected void setObjectImpl(Object obj) {
-
- }
- protected void setObjectImpl(Object obj1, Object obj2) {
-
- }
-
- private static synchronized void startUpdater() {
- if(uiHandler != null) {
- return;
- }
-
- uiHandler = new Handler();
- // Needed so handler is never null until thread creates it
- backgroundHandler = uiHandler;
- updateRunnable = new Runnable() {
- @Override
- public void run() {
- updateAll();
- }
- };
-
- new Thread(new Runnable() {
- public void run() {
- Looper.prepare();
- backgroundHandler = new Handler(Looper.myLooper());
- uiHandler.post(updateRunnable);
- Looper.loop();
- }
- }, "UpdateView").start();
- }
-
- public static synchronized void triggerUpdate() {
- if(backgroundHandler != null) {
- uiHandler.removeCallbacksAndMessages(null);
- backgroundHandler.removeCallbacksAndMessages(null);
- uiHandler.post(updateRunnable);
- }
- }
-
- private static void updateAll() {
- try {
- // If nothing can see this, stop updating
- if(activeActivities == 0) {
- activeActivities--;
- return;
- }
-
- List<UpdateView> views = new ArrayList<UpdateView>();
- for (UpdateView view : INSTANCES.keySet()) {
- if (view.isShown()) {
- views.add(view);
- }
- }
- if(views.size() > 0) {
- updateAllLive(views);
- } else {
- uiHandler.postDelayed(updateRunnable, 2000L);
- }
- } catch (Throwable x) {
- Log.w(TAG, "Error when updating song views.", x);
- }
- }
- private static void updateAllLive(final List<UpdateView> views) {
- final Runnable runnable = new Runnable() {
- @Override
- public void run() {
- try {
- for(UpdateView view: views) {
- view.update();
- }
- } catch (Throwable x) {
- Log.w(TAG, "Error when updating song views.", x);
- }
- uiHandler.postDelayed(updateRunnable, 1000L);
- }
- };
-
- backgroundHandler.post(new Runnable() {
- @Override
- public void run() {
- try {
- for(UpdateView view: views) {
- view.updateBackground();
- }
- uiHandler.post(runnable);
- } catch (Throwable x) {
- Log.w(TAG, "Error when updating song views.", x);
- }
- }
- });
- }
-
- public static void addActiveActivity() {
- activeActivities++;
-
- if(activeActivities == 0 && uiHandler != null && updateRunnable != null) {
- activeActivities++;
- uiHandler.post(updateRunnable);
- }
- }
- public static void removeActiveActivity() {
- activeActivities--;
- }
-
- public static MusicDirectory.Entry findEntry(MusicDirectory.Entry entry) {
- for(UpdateView view: INSTANCES.keySet()) {
- MusicDirectory.Entry check = null;
- if(view instanceof SongView) {
- check = ((SongView) view).getEntry();
- } else if(view instanceof AlbumCell) {
- check = ((AlbumCell) view).getEntry();
- } else if(view instanceof AlbumView) {
- check = ((AlbumView) view).getEntry();
- }
-
- if(check != null && entry != check && check.getId().equals(entry.getId())) {
- return check;
- }
- }
-
- return null;
- }
-
- protected void updateBackground() {
-
- }
- protected void update() {
- if(moreButton != null) {
- if(exists || pinned) {
- if(!shaded) {
- moreButton.setImageResource(exists ? R.drawable.download_cached : R.drawable.download_pinned);
- shaded = true;
- }
- } else {
- if(shaded) {
- int[] attrs = new int[] {R.attr.download_none};
- TypedArray typedArray = context.obtainStyledAttributes(attrs);
- moreButton.setImageResource(typedArray.getResourceId(0, 0));
- shaded = false;
- }
- }
- }
-
- if(starButton != null) {
- if(isStarred) {
- if(!starred) {
- starButton.setVisibility(View.VISIBLE);
- starred = true;
- }
- } else {
- if(starred) {
- starButton.setVisibility(View.GONE);
- starred = false;
- }
- }
- }
-
- if(ratingBar != null && isRated != rating) {
- if(isRated > 0 && rating == 0) {
- ratingBar.setVisibility(View.VISIBLE);
- } else if(isRated == 0 && rating > 0) {
- ratingBar.setVisibility(View.GONE);
- }
-
- ratingBar.setRating(isRated);
- rating = isRated;
- }
- }
-}
+/*
+ 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 github.daneren2005.dsub.view;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.RatingBar;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.WeakHashMap;
+
+import github.daneren2005.dsub.domain.MusicDirectory;
+import github.daneren2005.dsub.util.ImageLoader;
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.util.SilentBackgroundTask;
+
+public class UpdateView extends LinearLayout {
+ private static final String TAG = UpdateView.class.getSimpleName();
+ private static final WeakHashMap<UpdateView, ?> INSTANCES = new WeakHashMap<UpdateView, Object>();
+
+ private static Handler backgroundHandler;
+ private static Handler uiHandler;
+ private static Runnable updateRunnable;
+ private static int activeActivities = 0;
+
+ protected Context context;
+ protected RatingBar ratingBar;
+ protected ImageButton starButton;
+ protected ImageView moreButton;
+
+ protected boolean exists = false;
+ protected boolean pinned = false;
+ protected boolean shaded = false;
+ protected boolean starred = false;
+ protected boolean isStarred = false;
+ protected int isRated = 0;
+ protected int rating = 0;
+ protected SilentBackgroundTask<Void> imageTask = null;
+
+ protected final boolean autoUpdate;
+
+ public UpdateView(Context context) {
+ this(context, true);
+ }
+ public UpdateView(Context context, boolean autoUpdate) {
+ super(context);
+ this.context = context;
+ this.autoUpdate = autoUpdate;
+
+ setLayoutParams(new AbsListView.LayoutParams(
+ ViewGroup.LayoutParams.FILL_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT));
+
+ if(autoUpdate) {
+ INSTANCES.put(this, null);
+ }
+ startUpdater();
+ }
+
+ @Override
+ public void setPressed(boolean pressed) {
+
+ }
+
+ public void setObject(Object obj) {
+ setObjectImpl(obj);
+ updateBackground();
+ update();
+ }
+ public void setObject(Object obj1, Object obj2) {
+ if(imageTask != null) {
+ imageTask.cancel();
+ imageTask = null;
+ }
+
+ setObjectImpl(obj1, obj2);
+ backgroundHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ updateBackground();
+ uiHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ update();
+ }
+ });
+ }
+ });
+ }
+ protected void setObjectImpl(Object obj) {
+
+ }
+ protected void setObjectImpl(Object obj1, Object obj2) {
+
+ }
+
+ private static synchronized void startUpdater() {
+ if(uiHandler != null) {
+ return;
+ }
+
+ uiHandler = new Handler();
+ // Needed so handler is never null until thread creates it
+ backgroundHandler = uiHandler;
+ updateRunnable = new Runnable() {
+ @Override
+ public void run() {
+ updateAll();
+ }
+ };
+
+ new Thread(new Runnable() {
+ public void run() {
+ Looper.prepare();
+ backgroundHandler = new Handler(Looper.myLooper());
+ uiHandler.post(updateRunnable);
+ Looper.loop();
+ }
+ }, "UpdateView").start();
+ }
+
+ public static synchronized void triggerUpdate() {
+ if(backgroundHandler != null) {
+ uiHandler.removeCallbacksAndMessages(null);
+ backgroundHandler.removeCallbacksAndMessages(null);
+ uiHandler.post(updateRunnable);
+ }
+ }
+
+ private static void updateAll() {
+ try {
+ // If nothing can see this, stop updating
+ if(activeActivities == 0) {
+ activeActivities--;
+ return;
+ }
+
+ List<UpdateView> views = new ArrayList<UpdateView>();
+ for (UpdateView view : INSTANCES.keySet()) {
+ if (view.isShown()) {
+ views.add(view);
+ }
+ }
+ if(views.size() > 0) {
+ updateAllLive(views);
+ } else {
+ uiHandler.postDelayed(updateRunnable, 2000L);
+ }
+ } catch (Throwable x) {
+ Log.w(TAG, "Error when updating song views.", x);
+ }
+ }
+ private static void updateAllLive(final List<UpdateView> views) {
+ final Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ try {
+ for(UpdateView view: views) {
+ view.update();
+ }
+ } catch (Throwable x) {
+ Log.w(TAG, "Error when updating song views.", x);
+ }
+ uiHandler.postDelayed(updateRunnable, 1000L);
+ }
+ };
+
+ backgroundHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ for(UpdateView view: views) {
+ view.updateBackground();
+ }
+ uiHandler.post(runnable);
+ } catch (Throwable x) {
+ Log.w(TAG, "Error when updating song views.", x);
+ }
+ }
+ });
+ }
+
+ public static void addActiveActivity() {
+ activeActivities++;
+
+ if(activeActivities == 0 && uiHandler != null && updateRunnable != null) {
+ activeActivities++;
+ uiHandler.post(updateRunnable);
+ }
+ }
+ public static void removeActiveActivity() {
+ activeActivities--;
+ }
+
+ public static MusicDirectory.Entry findEntry(MusicDirectory.Entry entry) {
+ for(UpdateView view: INSTANCES.keySet()) {
+ MusicDirectory.Entry check = null;
+ if(view instanceof SongView) {
+ check = ((SongView) view).getEntry();
+ } else if(view instanceof AlbumCell) {
+ check = ((AlbumCell) view).getEntry();
+ } else if(view instanceof AlbumView) {
+ check = ((AlbumView) view).getEntry();
+ }
+
+ if(check != null && entry != check && check.getId().equals(entry.getId())) {
+ return check;
+ }
+ }
+
+ return null;
+ }
+
+ protected void updateBackground() {
+
+ }
+ protected void update() {
+ if(moreButton != null) {
+ if(exists || pinned) {
+ if(!shaded) {
+ moreButton.setImageResource(exists ? R.drawable.download_cached : R.drawable.download_pinned);
+ shaded = true;
+ }
+ } else {
+ if(shaded) {
+ int[] attrs = new int[] {R.attr.download_none};
+ TypedArray typedArray = context.obtainStyledAttributes(attrs);
+ moreButton.setImageResource(typedArray.getResourceId(0, 0));
+ shaded = false;
+ }
+ }
+ }
+
+ if(starButton != null) {
+ if(isStarred) {
+ if(!starred) {
+ starButton.setVisibility(View.VISIBLE);
+ starred = true;
+ }
+ } else {
+ if(starred) {
+ starButton.setVisibility(View.GONE);
+ starred = false;
+ }
+ }
+ }
+
+ if(ratingBar != null && isRated != rating) {
+ if(isRated > 0 && rating == 0) {
+ ratingBar.setVisibility(View.VISIBLE);
+ } else if(isRated == 0 && rating > 0) {
+ ratingBar.setVisibility(View.GONE);
+ }
+
+ ratingBar.setRating(isRated);
+ rating = isRated;
+ }
+ }
+}
diff --git a/src/github/daneren2005/dsub/view/UserView.java b/app/src/main/java/github/daneren2005/dsub/view/UserView.java
index 1a0192a1..dec8dbef 100644
--- a/src/github/daneren2005/dsub/view/UserView.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/UserView.java
@@ -1,54 +1,54 @@
-/*
- 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 2014 (C) Scott Jackson
-*/
-
-package github.daneren2005.dsub.view;
-
-import android.content.Context;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.domain.User;
-import github.daneren2005.dsub.util.ImageLoader;
-
-public class UserView extends UpdateView {
- private User user;
-
- private TextView usernameView;
- private ImageView avatarView;
-
- public UserView(Context context) {
- super(context, false);
- this.context = context;
- LayoutInflater.from(context).inflate(R.layout.user_list_item, this, true);
-
- usernameView = (TextView) findViewById(R.id.item_name);
- avatarView = (ImageView) findViewById(R.id.item_avatar);
- moreButton = (ImageView) findViewById(R.id.item_more);
- moreButton.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- v.showContextMenu();
- }
- });
- }
-
- protected void setObjectImpl(Object obj, Object obj2) {
- this.user = (User) obj;
- usernameView.setText(user.getUsername());
- imageTask = ((ImageLoader)obj2).loadAvatar(context, avatarView, user.getUsername());
- }
-}
+/*
+ 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 2014 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.view;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.User;
+import github.daneren2005.dsub.util.ImageLoader;
+
+public class UserView extends UpdateView {
+ private User user;
+
+ private TextView usernameView;
+ private ImageView avatarView;
+
+ public UserView(Context context) {
+ super(context, false);
+ this.context = context;
+ LayoutInflater.from(context).inflate(R.layout.user_list_item, this, true);
+
+ usernameView = (TextView) findViewById(R.id.item_name);
+ avatarView = (ImageView) findViewById(R.id.item_avatar);
+ moreButton = (ImageView) findViewById(R.id.item_more);
+ moreButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ v.showContextMenu();
+ }
+ });
+ }
+
+ protected void setObjectImpl(Object obj, Object obj2) {
+ this.user = (User) obj;
+ usernameView.setText(user.getUsername());
+ imageTask = ((ImageLoader)obj2).loadAvatar(context, avatarView, user.getUsername());
+ }
+}
diff --git a/res/anim/enter_from_left.xml b/app/src/main/res/anim/enter_from_left.xml
index 2d5c1737..3c11332c 100644
--- a/res/anim/enter_from_left.xml
+++ b/app/src/main/res/anim/enter_from_left.xml
@@ -1,12 +1,12 @@
-<?xml version="1.0" encoding="utf-8"?>
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false">
-
- <translate
- android:fromXDelta="-100%" android:toXDelta="0%"
- android:duration="@android:integer/config_shortAnimTime"
- android:interpolator="@android:anim/accelerate_interpolator"/>
-
- <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
- android:duration="@android:integer/config_shortAnimTime" />
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false">
+
+ <translate
+ android:fromXDelta="-100%" android:toXDelta="0%"
+ android:duration="@android:integer/config_shortAnimTime"
+ android:interpolator="@android:anim/accelerate_interpolator"/>
+
+ <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+ android:duration="@android:integer/config_shortAnimTime" />
</set> \ No newline at end of file
diff --git a/res/anim/enter_from_right.xml b/app/src/main/res/anim/enter_from_right.xml
index 60a0ac05..568a0c07 100644
--- a/res/anim/enter_from_right.xml
+++ b/app/src/main/res/anim/enter_from_right.xml
@@ -1,12 +1,12 @@
-<?xml version="1.0" encoding="utf-8"?>
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false">
-
- <translate
- android:fromXDelta="100%" android:toXDelta="0%"
- android:duration="@android:integer/config_shortAnimTime"
- android:interpolator="@android:anim/accelerate_interpolator"/>
-
- <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
- android:duration="@android:integer/config_shortAnimTime" />
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false">
+
+ <translate
+ android:fromXDelta="100%" android:toXDelta="0%"
+ android:duration="@android:integer/config_shortAnimTime"
+ android:interpolator="@android:anim/accelerate_interpolator"/>
+
+ <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+ android:duration="@android:integer/config_shortAnimTime" />
</set> \ No newline at end of file
diff --git a/res/anim/exit_to_left.xml b/app/src/main/res/anim/exit_to_left.xml
index 65ad4290..2cb8febf 100644
--- a/res/anim/exit_to_left.xml
+++ b/app/src/main/res/anim/exit_to_left.xml
@@ -1,12 +1,12 @@
-<?xml version="1.0" encoding="utf-8"?>
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false">
-
- <translate
- android:fromXDelta="0%" android:toXDelta="-100%"
- android:duration="@android:integer/config_shortAnimTime"
- android:interpolator="@android:anim/accelerate_interpolator"/>
-
- <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
- android:duration="@android:integer/config_shortAnimTime" />
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false">
+
+ <translate
+ android:fromXDelta="0%" android:toXDelta="-100%"
+ android:duration="@android:integer/config_shortAnimTime"
+ android:interpolator="@android:anim/accelerate_interpolator"/>
+
+ <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+ android:duration="@android:integer/config_shortAnimTime" />
</set> \ No newline at end of file
diff --git a/res/anim/exit_to_right.xml b/app/src/main/res/anim/exit_to_right.xml
index 893a7ef6..a3fa5bad 100644
--- a/res/anim/exit_to_right.xml
+++ b/app/src/main/res/anim/exit_to_right.xml
@@ -1,12 +1,12 @@
-<?xml version="1.0" encoding="utf-8"?>
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false">
-
- <translate
- android:fromXDelta="0%" android:toXDelta="100%"
- android:duration="@android:integer/config_shortAnimTime"
- android:interpolator="@android:anim/accelerate_interpolator"/>
-
- <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
- android:duration="@android:integer/config_shortAnimTime" />
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false">
+
+ <translate
+ android:fromXDelta="0%" android:toXDelta="100%"
+ android:duration="@android:integer/config_shortAnimTime"
+ android:interpolator="@android:anim/accelerate_interpolator"/>
+
+ <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+ android:duration="@android:integer/config_shortAnimTime" />
</set> \ No newline at end of file
diff --git a/res/anim/fade_in.xml b/app/src/main/res/anim/fade_in.xml
index c41db065..c41db065 100644
--- a/res/anim/fade_in.xml
+++ b/app/src/main/res/anim/fade_in.xml
diff --git a/res/anim/fade_out.xml b/app/src/main/res/anim/fade_out.xml
index d615f2a1..d615f2a1 100644
--- a/res/anim/fade_out.xml
+++ b/app/src/main/res/anim/fade_out.xml
diff --git a/res/anim/push_down_in.xml b/app/src/main/res/anim/push_down_in.xml
index 6ab9a047..6ab9a047 100644
--- a/res/anim/push_down_in.xml
+++ b/app/src/main/res/anim/push_down_in.xml
diff --git a/res/anim/push_down_out.xml b/app/src/main/res/anim/push_down_out.xml
index ce36458a..ce36458a 100644
--- a/res/anim/push_down_out.xml
+++ b/app/src/main/res/anim/push_down_out.xml
diff --git a/res/anim/push_up_in.xml b/app/src/main/res/anim/push_up_in.xml
index 6ef582c4..6ef582c4 100644
--- a/res/anim/push_up_in.xml
+++ b/app/src/main/res/anim/push_up_in.xml
diff --git a/res/anim/push_up_out.xml b/app/src/main/res/anim/push_up_out.xml
index 2b267d59..2b267d59 100644
--- a/res/anim/push_up_out.xml
+++ b/app/src/main/res/anim/push_up_out.xml
diff --git a/res/drawable-hdpi-v11/notification_close.png b/app/src/main/res/drawable-hdpi-v11/notification_close.png
index 254e130f..254e130f 100644
--- a/res/drawable-hdpi-v11/notification_close.png
+++ b/app/src/main/res/drawable-hdpi-v11/notification_close.png
Binary files differ
diff --git a/res/drawable-hdpi-v11/notification_next.png b/app/src/main/res/drawable-hdpi-v11/notification_next.png
index 59239305..59239305 100644
--- a/res/drawable-hdpi-v11/notification_next.png
+++ b/app/src/main/res/drawable-hdpi-v11/notification_next.png
Binary files differ
diff --git a/res/drawable-hdpi-v11/notification_pause.png b/app/src/main/res/drawable-hdpi-v11/notification_pause.png
index cbd61795..cbd61795 100644
--- a/res/drawable-hdpi-v11/notification_pause.png
+++ b/app/src/main/res/drawable-hdpi-v11/notification_pause.png
Binary files differ
diff --git a/res/drawable-hdpi-v11/notification_play.png b/app/src/main/res/drawable-hdpi-v11/notification_play.png
index 78b4d5bf..78b4d5bf 100644
--- a/res/drawable-hdpi-v11/notification_play.png
+++ b/app/src/main/res/drawable-hdpi-v11/notification_play.png
Binary files differ
diff --git a/res/drawable-hdpi-v11/notification_previous.png b/app/src/main/res/drawable-hdpi-v11/notification_previous.png
index 556eaec3..556eaec3 100644
--- a/res/drawable-hdpi-v11/notification_previous.png
+++ b/app/src/main/res/drawable-hdpi-v11/notification_previous.png
Binary files differ
diff --git a/res/drawable-hdpi-v11/stat_notify_download.png b/app/src/main/res/drawable-hdpi-v11/stat_notify_download.png
index 48ca6924..48ca6924 100644
--- a/res/drawable-hdpi-v11/stat_notify_download.png
+++ b/app/src/main/res/drawable-hdpi-v11/stat_notify_download.png
Binary files differ
diff --git a/res/drawable-hdpi-v11/stat_notify_playing.png b/app/src/main/res/drawable-hdpi-v11/stat_notify_playing.png
index 78b4d5bf..78b4d5bf 100644
--- a/res/drawable-hdpi-v11/stat_notify_playing.png
+++ b/app/src/main/res/drawable-hdpi-v11/stat_notify_playing.png
Binary files differ
diff --git a/res/drawable-hdpi-v11/stat_notify_sync.png b/app/src/main/res/drawable-hdpi-v11/stat_notify_sync.png
index f1ff1eb2..f1ff1eb2 100644
--- a/res/drawable-hdpi-v11/stat_notify_sync.png
+++ b/app/src/main/res/drawable-hdpi-v11/stat_notify_sync.png
Binary files differ
diff --git a/res/drawable-hdpi/action_toggle_list_dark.png b/app/src/main/res/drawable-hdpi/action_toggle_list_dark.png
index d0ec1a5d..d0ec1a5d 100644
--- a/res/drawable-hdpi/action_toggle_list_dark.png
+++ b/app/src/main/res/drawable-hdpi/action_toggle_list_dark.png
Binary files differ
diff --git a/res/drawable-hdpi/action_toggle_list_light.png b/app/src/main/res/drawable-hdpi/action_toggle_list_light.png
index 60ec88be..60ec88be 100644
--- a/res/drawable-hdpi/action_toggle_list_light.png
+++ b/app/src/main/res/drawable-hdpi/action_toggle_list_light.png
Binary files differ
diff --git a/res/drawable-hdpi/actionbar_button_normal.9.png b/app/src/main/res/drawable-hdpi/actionbar_button_normal.9.png
index 385f751c..385f751c 100644
--- a/res/drawable-hdpi/actionbar_button_normal.9.png
+++ b/app/src/main/res/drawable-hdpi/actionbar_button_normal.9.png
Binary files differ
diff --git a/res/drawable-hdpi/appwidget_art_default.png b/app/src/main/res/drawable-hdpi/appwidget_art_default.png
index 5bd39cc2..5bd39cc2 100644
--- a/res/drawable-hdpi/appwidget_art_default.png
+++ b/app/src/main/res/drawable-hdpi/appwidget_art_default.png
Binary files differ
diff --git a/res/drawable-hdpi/appwidget_art_unknown.png b/app/src/main/res/drawable-hdpi/appwidget_art_unknown.png
index 5bd39cc2..5bd39cc2 100644
--- a/res/drawable-hdpi/appwidget_art_unknown.png
+++ b/app/src/main/res/drawable-hdpi/appwidget_art_unknown.png
Binary files differ
diff --git a/res/drawable-hdpi/appwidget_bg.9.png b/app/src/main/res/drawable-hdpi/appwidget_bg.9.png
index 6bacc7fe..6bacc7fe 100644
--- a/res/drawable-hdpi/appwidget_bg.9.png
+++ b/app/src/main/res/drawable-hdpi/appwidget_bg.9.png
Binary files differ
diff --git a/res/drawable-hdpi/background.png b/app/src/main/res/drawable-hdpi/background.png
index 07d6a9cc..07d6a9cc 100644
--- a/res/drawable-hdpi/background.png
+++ b/app/src/main/res/drawable-hdpi/background.png
Binary files differ
diff --git a/res/drawable-hdpi/download_cached.png b/app/src/main/res/drawable-hdpi/download_cached.png
index 56bfc0e1..56bfc0e1 100644
--- a/res/drawable-hdpi/download_cached.png
+++ b/app/src/main/res/drawable-hdpi/download_cached.png
Binary files differ
diff --git a/res/drawable-hdpi/download_none_dark.png b/app/src/main/res/drawable-hdpi/download_none_dark.png
index a074c10d..a074c10d 100644
--- a/res/drawable-hdpi/download_none_dark.png
+++ b/app/src/main/res/drawable-hdpi/download_none_dark.png
Binary files differ
diff --git a/res/drawable-hdpi/download_none_light.png b/app/src/main/res/drawable-hdpi/download_none_light.png
index 21544e5f..21544e5f 100644
--- a/res/drawable-hdpi/download_none_light.png
+++ b/app/src/main/res/drawable-hdpi/download_none_light.png
Binary files differ
diff --git a/res/drawable-hdpi/download_pinned.png b/app/src/main/res/drawable-hdpi/download_pinned.png
index 711c7704..711c7704 100644
--- a/res/drawable-hdpi/download_pinned.png
+++ b/app/src/main/res/drawable-hdpi/download_pinned.png
Binary files differ
diff --git a/res/drawable-hdpi/downloading_dark.png b/app/src/main/res/drawable-hdpi/downloading_dark.png
index 3ccb1837..3ccb1837 100644
--- a/res/drawable-hdpi/downloading_dark.png
+++ b/app/src/main/res/drawable-hdpi/downloading_dark.png
Binary files differ
diff --git a/res/drawable-hdpi/downloading_light.png b/app/src/main/res/drawable-hdpi/downloading_light.png
index 07be3016..07be3016 100644
--- a/res/drawable-hdpi/downloading_light.png
+++ b/app/src/main/res/drawable-hdpi/downloading_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_action_add_dark.png b/app/src/main/res/drawable-hdpi/ic_action_add_dark.png
index 81d535d5..81d535d5 100644
--- a/res/drawable-hdpi/ic_action_add_dark.png
+++ b/app/src/main/res/drawable-hdpi/ic_action_add_dark.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_action_add_light.png b/app/src/main/res/drawable-hdpi/ic_action_add_light.png
index 0e4f3347..0e4f3347 100644
--- a/res/drawable-hdpi/ic_action_add_light.png
+++ b/app/src/main/res/drawable-hdpi/ic_action_add_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_action_album.png b/app/src/main/res/drawable-hdpi/ic_action_album.png
index 6058c483..6058c483 100644
--- a/res/drawable-hdpi/ic_action_album.png
+++ b/app/src/main/res/drawable-hdpi/ic_action_album.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_action_artist.png b/app/src/main/res/drawable-hdpi/ic_action_artist.png
index 40c9a7fb..40c9a7fb 100644
--- a/res/drawable-hdpi/ic_action_artist.png
+++ b/app/src/main/res/drawable-hdpi/ic_action_artist.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_action_rating_bad_dark.png b/app/src/main/res/drawable-hdpi/ic_action_rating_bad_dark.png
index 855709e9..855709e9 100644
--- a/res/drawable-hdpi/ic_action_rating_bad_dark.png
+++ b/app/src/main/res/drawable-hdpi/ic_action_rating_bad_dark.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_action_rating_bad_light.png b/app/src/main/res/drawable-hdpi/ic_action_rating_bad_light.png
index 34199d3a..34199d3a 100644
--- a/res/drawable-hdpi/ic_action_rating_bad_light.png
+++ b/app/src/main/res/drawable-hdpi/ic_action_rating_bad_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_action_rating_bad_selected.png b/app/src/main/res/drawable-hdpi/ic_action_rating_bad_selected.png
index c57aba50..c57aba50 100644
--- a/res/drawable-hdpi/ic_action_rating_bad_selected.png
+++ b/app/src/main/res/drawable-hdpi/ic_action_rating_bad_selected.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_action_rating_good_dark.png b/app/src/main/res/drawable-hdpi/ic_action_rating_good_dark.png
index fa91e699..fa91e699 100644
--- a/res/drawable-hdpi/ic_action_rating_good_dark.png
+++ b/app/src/main/res/drawable-hdpi/ic_action_rating_good_dark.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_action_rating_good_light.png b/app/src/main/res/drawable-hdpi/ic_action_rating_good_light.png
index 3427d770..3427d770 100644
--- a/res/drawable-hdpi/ic_action_rating_good_light.png
+++ b/app/src/main/res/drawable-hdpi/ic_action_rating_good_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_action_rating_good_selected.png b/app/src/main/res/drawable-hdpi/ic_action_rating_good_selected.png
index 34d53153..34d53153 100644
--- a/res/drawable-hdpi/ic_action_rating_good_selected.png
+++ b/app/src/main/res/drawable-hdpi/ic_action_rating_good_selected.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_action_song.png b/app/src/main/res/drawable-hdpi/ic_action_song.png
index 95342f76..95342f76 100644
--- a/res/drawable-hdpi/ic_action_song.png
+++ b/app/src/main/res/drawable-hdpi/ic_action_song.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_action_volume_dark.png b/app/src/main/res/drawable-hdpi/ic_action_volume_dark.png
index 62550655..62550655 100644
--- a/res/drawable-hdpi/ic_action_volume_dark.png
+++ b/app/src/main/res/drawable-hdpi/ic_action_volume_dark.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_action_volume_light.png b/app/src/main/res/drawable-hdpi/ic_action_volume_light.png
index 1b1d182c..1b1d182c 100644
--- a/res/drawable-hdpi/ic_action_volume_light.png
+++ b/app/src/main/res/drawable-hdpi/ic_action_volume_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_appwidget_music_next.png b/app/src/main/res/drawable-hdpi/ic_appwidget_music_next.png
index 99d28766..99d28766 100644
--- a/res/drawable-hdpi/ic_appwidget_music_next.png
+++ b/app/src/main/res/drawable-hdpi/ic_appwidget_music_next.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_appwidget_music_pause.png b/app/src/main/res/drawable-hdpi/ic_appwidget_music_pause.png
index a05a8c50..a05a8c50 100644
--- a/res/drawable-hdpi/ic_appwidget_music_pause.png
+++ b/app/src/main/res/drawable-hdpi/ic_appwidget_music_pause.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_appwidget_music_play.png b/app/src/main/res/drawable-hdpi/ic_appwidget_music_play.png
index a754b920..a754b920 100644
--- a/res/drawable-hdpi/ic_appwidget_music_play.png
+++ b/app/src/main/res/drawable-hdpi/ic_appwidget_music_play.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_appwidget_music_previous.png b/app/src/main/res/drawable-hdpi/ic_appwidget_music_previous.png
index 7fb3921b..7fb3921b 100644
--- a/res/drawable-hdpi/ic_appwidget_music_previous.png
+++ b/app/src/main/res/drawable-hdpi/ic_appwidget_music_previous.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_add_person_dark.png b/app/src/main/res/drawable-hdpi/ic_menu_add_person_dark.png
index 971048d5..971048d5 100644
--- a/res/drawable-hdpi/ic_menu_add_person_dark.png
+++ b/app/src/main/res/drawable-hdpi/ic_menu_add_person_dark.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_add_person_light.png b/app/src/main/res/drawable-hdpi/ic_menu_add_person_light.png
index f94446d0..f94446d0 100644
--- a/res/drawable-hdpi/ic_menu_add_person_light.png
+++ b/app/src/main/res/drawable-hdpi/ic_menu_add_person_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_admin_dark.png b/app/src/main/res/drawable-hdpi/ic_menu_admin_dark.png
index 76da5ade..76da5ade 100644
--- a/res/drawable-hdpi/ic_menu_admin_dark.png
+++ b/app/src/main/res/drawable-hdpi/ic_menu_admin_dark.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_admin_light.png b/app/src/main/res/drawable-hdpi/ic_menu_admin_light.png
index 5431889c..5431889c 100644
--- a/res/drawable-hdpi/ic_menu_admin_light.png
+++ b/app/src/main/res/drawable-hdpi/ic_menu_admin_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_bookmark_dark.png b/app/src/main/res/drawable-hdpi/ic_menu_bookmark_dark.png
index e7cd08e4..e7cd08e4 100644
--- a/res/drawable-hdpi/ic_menu_bookmark_dark.png
+++ b/app/src/main/res/drawable-hdpi/ic_menu_bookmark_dark.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_bookmark_light.png b/app/src/main/res/drawable-hdpi/ic_menu_bookmark_light.png
index fdb46da3..fdb46da3 100644
--- a/res/drawable-hdpi/ic_menu_bookmark_light.png
+++ b/app/src/main/res/drawable-hdpi/ic_menu_bookmark_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_bookmark_selected.png b/app/src/main/res/drawable-hdpi/ic_menu_bookmark_selected.png
index 5a33d60c..5a33d60c 100644
--- a/res/drawable-hdpi/ic_menu_bookmark_selected.png
+++ b/app/src/main/res/drawable-hdpi/ic_menu_bookmark_selected.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_chat_dark.png b/app/src/main/res/drawable-hdpi/ic_menu_chat_dark.png
index 75363fce..75363fce 100644
--- a/res/drawable-hdpi/ic_menu_chat_dark.png
+++ b/app/src/main/res/drawable-hdpi/ic_menu_chat_dark.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_chat_light.png b/app/src/main/res/drawable-hdpi/ic_menu_chat_light.png
index e28933e4..e28933e4 100644
--- a/res/drawable-hdpi/ic_menu_chat_light.png
+++ b/app/src/main/res/drawable-hdpi/ic_menu_chat_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_chat_send_dark.png b/app/src/main/res/drawable-hdpi/ic_menu_chat_send_dark.png
index c0e9b372..c0e9b372 100644
--- a/res/drawable-hdpi/ic_menu_chat_send_dark.png
+++ b/app/src/main/res/drawable-hdpi/ic_menu_chat_send_dark.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_chat_send_light.png b/app/src/main/res/drawable-hdpi/ic_menu_chat_send_light.png
index ebcfe9e8..ebcfe9e8 100644
--- a/res/drawable-hdpi/ic_menu_chat_send_light.png
+++ b/app/src/main/res/drawable-hdpi/ic_menu_chat_send_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_download_dark.png b/app/src/main/res/drawable-hdpi/ic_menu_download_dark.png
index 872b73c0..872b73c0 100644
--- a/res/drawable-hdpi/ic_menu_download_dark.png
+++ b/app/src/main/res/drawable-hdpi/ic_menu_download_dark.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_download_light.png b/app/src/main/res/drawable-hdpi/ic_menu_download_light.png
index f8818490..f8818490 100644
--- a/res/drawable-hdpi/ic_menu_download_light.png
+++ b/app/src/main/res/drawable-hdpi/ic_menu_download_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_library_dark.png b/app/src/main/res/drawable-hdpi/ic_menu_library_dark.png
index 717cb3e1..717cb3e1 100644
--- a/res/drawable-hdpi/ic_menu_library_dark.png
+++ b/app/src/main/res/drawable-hdpi/ic_menu_library_dark.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_library_light.png b/app/src/main/res/drawable-hdpi/ic_menu_library_light.png
index 17a45d77..17a45d77 100644
--- a/res/drawable-hdpi/ic_menu_library_light.png
+++ b/app/src/main/res/drawable-hdpi/ic_menu_library_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_password_dark.png b/app/src/main/res/drawable-hdpi/ic_menu_password_dark.png
index 67fa3e84..67fa3e84 100644
--- a/res/drawable-hdpi/ic_menu_password_dark.png
+++ b/app/src/main/res/drawable-hdpi/ic_menu_password_dark.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_password_light.png b/app/src/main/res/drawable-hdpi/ic_menu_password_light.png
index bd99c01f..bd99c01f 100644
--- a/res/drawable-hdpi/ic_menu_password_light.png
+++ b/app/src/main/res/drawable-hdpi/ic_menu_password_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_playlist_dark.png b/app/src/main/res/drawable-hdpi/ic_menu_playlist_dark.png
index 8e3babc7..8e3babc7 100644
--- a/res/drawable-hdpi/ic_menu_playlist_dark.png
+++ b/app/src/main/res/drawable-hdpi/ic_menu_playlist_dark.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_playlist_light.png b/app/src/main/res/drawable-hdpi/ic_menu_playlist_light.png
index 4131dba4..4131dba4 100644
--- a/res/drawable-hdpi/ic_menu_playlist_light.png
+++ b/app/src/main/res/drawable-hdpi/ic_menu_playlist_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_podcast_dark.png b/app/src/main/res/drawable-hdpi/ic_menu_podcast_dark.png
index d1d62d03..d1d62d03 100644
--- a/res/drawable-hdpi/ic_menu_podcast_dark.png
+++ b/app/src/main/res/drawable-hdpi/ic_menu_podcast_dark.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_podcast_light.png b/app/src/main/res/drawable-hdpi/ic_menu_podcast_light.png
index 4ce1b787..4ce1b787 100644
--- a/res/drawable-hdpi/ic_menu_podcast_light.png
+++ b/app/src/main/res/drawable-hdpi/ic_menu_podcast_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_radio_dark.png b/app/src/main/res/drawable-hdpi/ic_menu_radio_dark.png
index a801dce0..a801dce0 100644
--- a/res/drawable-hdpi/ic_menu_radio_dark.png
+++ b/app/src/main/res/drawable-hdpi/ic_menu_radio_dark.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_radio_light.png b/app/src/main/res/drawable-hdpi/ic_menu_radio_light.png
index b723d574..b723d574 100644
--- a/res/drawable-hdpi/ic_menu_radio_light.png
+++ b/app/src/main/res/drawable-hdpi/ic_menu_radio_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_refresh_dark.png b/app/src/main/res/drawable-hdpi/ic_menu_refresh_dark.png
index 2795cfa9..2795cfa9 100644
--- a/res/drawable-hdpi/ic_menu_refresh_dark.png
+++ b/app/src/main/res/drawable-hdpi/ic_menu_refresh_dark.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_refresh_light.png b/app/src/main/res/drawable-hdpi/ic_menu_refresh_light.png
index 86d1b042..86d1b042 100644
--- a/res/drawable-hdpi/ic_menu_refresh_light.png
+++ b/app/src/main/res/drawable-hdpi/ic_menu_refresh_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_remove_dark.png b/app/src/main/res/drawable-hdpi/ic_menu_remove_dark.png
index 878b378a..878b378a 100644
--- a/res/drawable-hdpi/ic_menu_remove_dark.png
+++ b/app/src/main/res/drawable-hdpi/ic_menu_remove_dark.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_remove_light.png b/app/src/main/res/drawable-hdpi/ic_menu_remove_light.png
index ece5ad8d..ece5ad8d 100644
--- a/res/drawable-hdpi/ic_menu_remove_light.png
+++ b/app/src/main/res/drawable-hdpi/ic_menu_remove_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_save_dark.png b/app/src/main/res/drawable-hdpi/ic_menu_save_dark.png
index b80828bf..b80828bf 100644
--- a/res/drawable-hdpi/ic_menu_save_dark.png
+++ b/app/src/main/res/drawable-hdpi/ic_menu_save_dark.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_save_light.png b/app/src/main/res/drawable-hdpi/ic_menu_save_light.png
index a3a5e23a..a3a5e23a 100644
--- a/res/drawable-hdpi/ic_menu_save_light.png
+++ b/app/src/main/res/drawable-hdpi/ic_menu_save_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_search_dark.png b/app/src/main/res/drawable-hdpi/ic_menu_search_dark.png
index ef2b3013..ef2b3013 100644
--- a/res/drawable-hdpi/ic_menu_search_dark.png
+++ b/app/src/main/res/drawable-hdpi/ic_menu_search_dark.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_search_light.png b/app/src/main/res/drawable-hdpi/ic_menu_search_light.png
index 756937df..756937df 100644
--- a/res/drawable-hdpi/ic_menu_search_light.png
+++ b/app/src/main/res/drawable-hdpi/ic_menu_search_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_settings_dark.png b/app/src/main/res/drawable-hdpi/ic_menu_settings_dark.png
index d6dd17ec..d6dd17ec 100644
--- a/res/drawable-hdpi/ic_menu_settings_dark.png
+++ b/app/src/main/res/drawable-hdpi/ic_menu_settings_dark.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_settings_light.png b/app/src/main/res/drawable-hdpi/ic_menu_settings_light.png
index 70c29951..70c29951 100644
--- a/res/drawable-hdpi/ic_menu_settings_light.png
+++ b/app/src/main/res/drawable-hdpi/ic_menu_settings_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_share_dark.png b/app/src/main/res/drawable-hdpi/ic_menu_share_dark.png
index 218aa864..218aa864 100644
--- a/res/drawable-hdpi/ic_menu_share_dark.png
+++ b/app/src/main/res/drawable-hdpi/ic_menu_share_dark.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_share_light.png b/app/src/main/res/drawable-hdpi/ic_menu_share_light.png
index cfd19d43..cfd19d43 100644
--- a/res/drawable-hdpi/ic_menu_share_light.png
+++ b/app/src/main/res/drawable-hdpi/ic_menu_share_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_shuffle_dark.png b/app/src/main/res/drawable-hdpi/ic_menu_shuffle_dark.png
index f77cfed2..f77cfed2 100644
--- a/res/drawable-hdpi/ic_menu_shuffle_dark.png
+++ b/app/src/main/res/drawable-hdpi/ic_menu_shuffle_dark.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_shuffle_light.png b/app/src/main/res/drawable-hdpi/ic_menu_shuffle_light.png
index ded93939..ded93939 100644
--- a/res/drawable-hdpi/ic_menu_shuffle_light.png
+++ b/app/src/main/res/drawable-hdpi/ic_menu_shuffle_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_number_border.png b/app/src/main/res/drawable-hdpi/ic_number_border.png
index d05aa7c2..d05aa7c2 100644
--- a/res/drawable-hdpi/ic_number_border.png
+++ b/app/src/main/res/drawable-hdpi/ic_number_border.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_social_person.png b/app/src/main/res/drawable-hdpi/ic_social_person.png
index 0a0a5ff2..0a0a5ff2 100644
--- a/res/drawable-hdpi/ic_social_person.png
+++ b/app/src/main/res/drawable-hdpi/ic_social_person.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_stat_star.png b/app/src/main/res/drawable-hdpi/ic_stat_star.png
index 67ad40f5..67ad40f5 100644
--- a/res/drawable-hdpi/ic_stat_star.png
+++ b/app/src/main/res/drawable-hdpi/ic_stat_star.png
Binary files differ
diff --git a/res/drawable-hdpi/launch.png b/app/src/main/res/drawable-hdpi/launch.png
index 0c77b9b4..0c77b9b4 100644
--- a/res/drawable-hdpi/launch.png
+++ b/app/src/main/res/drawable-hdpi/launch.png
Binary files differ
diff --git a/res/drawable-hdpi/main_offline_dark.png b/app/src/main/res/drawable-hdpi/main_offline_dark.png
index a594530d..a594530d 100644
--- a/res/drawable-hdpi/main_offline_dark.png
+++ b/app/src/main/res/drawable-hdpi/main_offline_dark.png
Binary files differ
diff --git a/res/drawable-hdpi/main_offline_light.png b/app/src/main/res/drawable-hdpi/main_offline_light.png
index cabca581..cabca581 100644
--- a/res/drawable-hdpi/main_offline_light.png
+++ b/app/src/main/res/drawable-hdpi/main_offline_light.png
Binary files differ
diff --git a/res/drawable-hdpi/main_select_server_dark.png b/app/src/main/res/drawable-hdpi/main_select_server_dark.png
index e3a9dd5d..e3a9dd5d 100644
--- a/res/drawable-hdpi/main_select_server_dark.png
+++ b/app/src/main/res/drawable-hdpi/main_select_server_dark.png
Binary files differ
diff --git a/res/drawable-hdpi/main_select_server_light.png b/app/src/main/res/drawable-hdpi/main_select_server_light.png
index 4606410d..4606410d 100644
--- a/res/drawable-hdpi/main_select_server_light.png
+++ b/app/src/main/res/drawable-hdpi/main_select_server_light.png
Binary files differ
diff --git a/res/drawable-hdpi/media_backward_dark.png b/app/src/main/res/drawable-hdpi/media_backward_dark.png
index b1dde4f5..b1dde4f5 100644
--- a/res/drawable-hdpi/media_backward_dark.png
+++ b/app/src/main/res/drawable-hdpi/media_backward_dark.png
Binary files differ
diff --git a/res/drawable-hdpi/media_backward_light.png b/app/src/main/res/drawable-hdpi/media_backward_light.png
index 3e277267..3e277267 100644
--- a/res/drawable-hdpi/media_backward_light.png
+++ b/app/src/main/res/drawable-hdpi/media_backward_light.png
Binary files differ
diff --git a/res/drawable-hdpi/media_forward_dark.png b/app/src/main/res/drawable-hdpi/media_forward_dark.png
index eb2546c7..eb2546c7 100644
--- a/res/drawable-hdpi/media_forward_dark.png
+++ b/app/src/main/res/drawable-hdpi/media_forward_dark.png
Binary files differ
diff --git a/res/drawable-hdpi/media_forward_light.png b/app/src/main/res/drawable-hdpi/media_forward_light.png
index 185e3c39..185e3c39 100644
--- a/res/drawable-hdpi/media_forward_light.png
+++ b/app/src/main/res/drawable-hdpi/media_forward_light.png
Binary files differ
diff --git a/res/drawable-hdpi/media_pause_dark.png b/app/src/main/res/drawable-hdpi/media_pause_dark.png
index b057588e..b057588e 100644
--- a/res/drawable-hdpi/media_pause_dark.png
+++ b/app/src/main/res/drawable-hdpi/media_pause_dark.png
Binary files differ
diff --git a/res/drawable-hdpi/media_pause_light.png b/app/src/main/res/drawable-hdpi/media_pause_light.png
index e01815e3..e01815e3 100644
--- a/res/drawable-hdpi/media_pause_light.png
+++ b/app/src/main/res/drawable-hdpi/media_pause_light.png
Binary files differ
diff --git a/res/drawable-hdpi/media_repeat_all.png b/app/src/main/res/drawable-hdpi/media_repeat_all.png
index c2255058..c2255058 100644
--- a/res/drawable-hdpi/media_repeat_all.png
+++ b/app/src/main/res/drawable-hdpi/media_repeat_all.png
Binary files differ
diff --git a/res/drawable-hdpi/media_repeat_off.png b/app/src/main/res/drawable-hdpi/media_repeat_off.png
index 10315ab3..10315ab3 100644
--- a/res/drawable-hdpi/media_repeat_off.png
+++ b/app/src/main/res/drawable-hdpi/media_repeat_off.png
Binary files differ
diff --git a/res/drawable-hdpi/media_repeat_off_light.png b/app/src/main/res/drawable-hdpi/media_repeat_off_light.png
index 39408bec..39408bec 100644
--- a/res/drawable-hdpi/media_repeat_off_light.png
+++ b/app/src/main/res/drawable-hdpi/media_repeat_off_light.png
Binary files differ
diff --git a/res/drawable-hdpi/media_repeat_single.png b/app/src/main/res/drawable-hdpi/media_repeat_single.png
index 6d280e7a..6d280e7a 100644
--- a/res/drawable-hdpi/media_repeat_single.png
+++ b/app/src/main/res/drawable-hdpi/media_repeat_single.png
Binary files differ
diff --git a/res/drawable-hdpi/media_start_dark.png b/app/src/main/res/drawable-hdpi/media_start_dark.png
index dbfd337a..dbfd337a 100644
--- a/res/drawable-hdpi/media_start_dark.png
+++ b/app/src/main/res/drawable-hdpi/media_start_dark.png
Binary files differ
diff --git a/res/drawable-hdpi/media_start_light.png b/app/src/main/res/drawable-hdpi/media_start_light.png
index e4310efc..e4310efc 100644
--- a/res/drawable-hdpi/media_start_light.png
+++ b/app/src/main/res/drawable-hdpi/media_start_light.png
Binary files differ
diff --git a/res/drawable-hdpi/media_stop_dark.png b/app/src/main/res/drawable-hdpi/media_stop_dark.png
index 5ceb39f3..5ceb39f3 100644
--- a/res/drawable-hdpi/media_stop_dark.png
+++ b/app/src/main/res/drawable-hdpi/media_stop_dark.png
Binary files differ
diff --git a/res/drawable-hdpi/media_stop_light.png b/app/src/main/res/drawable-hdpi/media_stop_light.png
index 8deca73e..8deca73e 100644
--- a/res/drawable-hdpi/media_stop_light.png
+++ b/app/src/main/res/drawable-hdpi/media_stop_light.png
Binary files differ
diff --git a/res/drawable-hdpi/notification_close.png b/app/src/main/res/drawable-hdpi/notification_close.png
index 916c9a0f..916c9a0f 100644
--- a/res/drawable-hdpi/notification_close.png
+++ b/app/src/main/res/drawable-hdpi/notification_close.png
Binary files differ
diff --git a/res/drawable-hdpi/notification_next.png b/app/src/main/res/drawable-hdpi/notification_next.png
index 078c310f..078c310f 100644
--- a/res/drawable-hdpi/notification_next.png
+++ b/app/src/main/res/drawable-hdpi/notification_next.png
Binary files differ
diff --git a/res/drawable-hdpi/notification_pause.png b/app/src/main/res/drawable-hdpi/notification_pause.png
index 16627e44..16627e44 100644
--- a/res/drawable-hdpi/notification_pause.png
+++ b/app/src/main/res/drawable-hdpi/notification_pause.png
Binary files differ
diff --git a/res/drawable-hdpi/notification_play.png b/app/src/main/res/drawable-hdpi/notification_play.png
index 02f38944..02f38944 100644
--- a/res/drawable-hdpi/notification_play.png
+++ b/app/src/main/res/drawable-hdpi/notification_play.png
Binary files differ
diff --git a/res/drawable-hdpi/notification_previous.png b/app/src/main/res/drawable-hdpi/notification_previous.png
index 9d10abd9..9d10abd9 100644
--- a/res/drawable-hdpi/notification_previous.png
+++ b/app/src/main/res/drawable-hdpi/notification_previous.png
Binary files differ
diff --git a/res/drawable-hdpi/now_playing.png b/app/src/main/res/drawable-hdpi/now_playing.png
index 02f38944..02f38944 100644
--- a/res/drawable-hdpi/now_playing.png
+++ b/app/src/main/res/drawable-hdpi/now_playing.png
Binary files differ
diff --git a/res/drawable-hdpi/stat_notify_download.png b/app/src/main/res/drawable-hdpi/stat_notify_download.png
index aa1b6c92..aa1b6c92 100644
--- a/res/drawable-hdpi/stat_notify_download.png
+++ b/app/src/main/res/drawable-hdpi/stat_notify_download.png
Binary files differ
diff --git a/res/drawable-hdpi/stat_notify_playing.png b/app/src/main/res/drawable-hdpi/stat_notify_playing.png
index 02f38944..02f38944 100644
--- a/res/drawable-hdpi/stat_notify_playing.png
+++ b/app/src/main/res/drawable-hdpi/stat_notify_playing.png
Binary files differ
diff --git a/res/drawable-hdpi/stat_notify_sync.png b/app/src/main/res/drawable-hdpi/stat_notify_sync.png
index 7dbf0e95..7dbf0e95 100644
--- a/res/drawable-hdpi/stat_notify_sync.png
+++ b/app/src/main/res/drawable-hdpi/stat_notify_sync.png
Binary files differ
diff --git a/res/drawable-hdpi/toast_frame.9.png b/app/src/main/res/drawable-hdpi/toast_frame.9.png
index 8f5d8119..8f5d8119 100644
--- a/res/drawable-hdpi/toast_frame.9.png
+++ b/app/src/main/res/drawable-hdpi/toast_frame.9.png
Binary files differ
diff --git a/res/drawable-hdpi/unknown_album.png b/app/src/main/res/drawable-hdpi/unknown_album.png
index 9b7844f4..9b7844f4 100644
--- a/res/drawable-hdpi/unknown_album.png
+++ b/app/src/main/res/drawable-hdpi/unknown_album.png
Binary files differ
diff --git a/res/drawable-hdpi/unknown_album_large.png b/app/src/main/res/drawable-hdpi/unknown_album_large.png
index 42c28c7d..42c28c7d 100644
--- a/res/drawable-hdpi/unknown_album_large.png
+++ b/app/src/main/res/drawable-hdpi/unknown_album_large.png
Binary files differ
diff --git a/res/drawable-large/unknown_album.png b/app/src/main/res/drawable-large/unknown_album.png
index c4e32c61..c4e32c61 100644
--- a/res/drawable-large/unknown_album.png
+++ b/app/src/main/res/drawable-large/unknown_album.png
Binary files differ
diff --git a/res/drawable-mdpi-v11/notification_close.png b/app/src/main/res/drawable-mdpi-v11/notification_close.png
index a056fe61..a056fe61 100644
--- a/res/drawable-mdpi-v11/notification_close.png
+++ b/app/src/main/res/drawable-mdpi-v11/notification_close.png
Binary files differ
diff --git a/res/drawable-mdpi-v11/notification_next.png b/app/src/main/res/drawable-mdpi-v11/notification_next.png
index 7297577f..7297577f 100644
--- a/res/drawable-mdpi-v11/notification_next.png
+++ b/app/src/main/res/drawable-mdpi-v11/notification_next.png
Binary files differ
diff --git a/res/drawable-mdpi-v11/notification_pause.png b/app/src/main/res/drawable-mdpi-v11/notification_pause.png
index 5d3ca3f2..5d3ca3f2 100644
--- a/res/drawable-mdpi-v11/notification_pause.png
+++ b/app/src/main/res/drawable-mdpi-v11/notification_pause.png
Binary files differ
diff --git a/res/drawable-mdpi-v11/notification_play.png b/app/src/main/res/drawable-mdpi-v11/notification_play.png
index 999ce798..999ce798 100644
--- a/res/drawable-mdpi-v11/notification_play.png
+++ b/app/src/main/res/drawable-mdpi-v11/notification_play.png
Binary files differ
diff --git a/res/drawable-mdpi-v11/notification_previous.png b/app/src/main/res/drawable-mdpi-v11/notification_previous.png
index 55a1f326..55a1f326 100644
--- a/res/drawable-mdpi-v11/notification_previous.png
+++ b/app/src/main/res/drawable-mdpi-v11/notification_previous.png
Binary files differ
diff --git a/res/drawable-mdpi-v11/stat_notify_download.png b/app/src/main/res/drawable-mdpi-v11/stat_notify_download.png
index 4164e0fa..4164e0fa 100644
--- a/res/drawable-mdpi-v11/stat_notify_download.png
+++ b/app/src/main/res/drawable-mdpi-v11/stat_notify_download.png
Binary files differ
diff --git a/res/drawable-mdpi-v11/stat_notify_playing.png b/app/src/main/res/drawable-mdpi-v11/stat_notify_playing.png
index 999ce798..999ce798 100644
--- a/res/drawable-mdpi-v11/stat_notify_playing.png
+++ b/app/src/main/res/drawable-mdpi-v11/stat_notify_playing.png
Binary files differ
diff --git a/res/drawable-mdpi-v11/stat_notify_sync.png b/app/src/main/res/drawable-mdpi-v11/stat_notify_sync.png
index 3e3c64c0..3e3c64c0 100644
--- a/res/drawable-mdpi-v11/stat_notify_sync.png
+++ b/app/src/main/res/drawable-mdpi-v11/stat_notify_sync.png
Binary files differ
diff --git a/res/drawable-mdpi/action_toggle_list_dark.png b/app/src/main/res/drawable-mdpi/action_toggle_list_dark.png
index ace7fcee..ace7fcee 100644
--- a/res/drawable-mdpi/action_toggle_list_dark.png
+++ b/app/src/main/res/drawable-mdpi/action_toggle_list_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/action_toggle_list_light.png b/app/src/main/res/drawable-mdpi/action_toggle_list_light.png
index fa6432da..fa6432da 100644
--- a/res/drawable-mdpi/action_toggle_list_light.png
+++ b/app/src/main/res/drawable-mdpi/action_toggle_list_light.png
Binary files differ
diff --git a/res/drawable-mdpi/download_cached.png b/app/src/main/res/drawable-mdpi/download_cached.png
index 2b5d33d1..2b5d33d1 100644
--- a/res/drawable-mdpi/download_cached.png
+++ b/app/src/main/res/drawable-mdpi/download_cached.png
Binary files differ
diff --git a/res/drawable-mdpi/download_none_dark.png b/app/src/main/res/drawable-mdpi/download_none_dark.png
index b6d614fc..b6d614fc 100644
--- a/res/drawable-mdpi/download_none_dark.png
+++ b/app/src/main/res/drawable-mdpi/download_none_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/download_none_light.png b/app/src/main/res/drawable-mdpi/download_none_light.png
index 2485c570..2485c570 100644
--- a/res/drawable-mdpi/download_none_light.png
+++ b/app/src/main/res/drawable-mdpi/download_none_light.png
Binary files differ
diff --git a/res/drawable-mdpi/download_pinned.png b/app/src/main/res/drawable-mdpi/download_pinned.png
index ce3fe064..ce3fe064 100644
--- a/res/drawable-mdpi/download_pinned.png
+++ b/app/src/main/res/drawable-mdpi/download_pinned.png
Binary files differ
diff --git a/res/drawable-mdpi/downloading_dark.png b/app/src/main/res/drawable-mdpi/downloading_dark.png
index ae6c5c9c..ae6c5c9c 100644
--- a/res/drawable-mdpi/downloading_dark.png
+++ b/app/src/main/res/drawable-mdpi/downloading_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/downloading_light.png b/app/src/main/res/drawable-mdpi/downloading_light.png
index abd5b748..abd5b748 100644
--- a/res/drawable-mdpi/downloading_light.png
+++ b/app/src/main/res/drawable-mdpi/downloading_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_action_add_dark.png b/app/src/main/res/drawable-mdpi/ic_action_add_dark.png
index a4c84f0f..a4c84f0f 100644
--- a/res/drawable-mdpi/ic_action_add_dark.png
+++ b/app/src/main/res/drawable-mdpi/ic_action_add_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_action_add_light.png b/app/src/main/res/drawable-mdpi/ic_action_add_light.png
index 86097d84..86097d84 100644
--- a/res/drawable-mdpi/ic_action_add_light.png
+++ b/app/src/main/res/drawable-mdpi/ic_action_add_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_action_album.png b/app/src/main/res/drawable-mdpi/ic_action_album.png
index 02674347..02674347 100644
--- a/res/drawable-mdpi/ic_action_album.png
+++ b/app/src/main/res/drawable-mdpi/ic_action_album.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_action_artist.png b/app/src/main/res/drawable-mdpi/ic_action_artist.png
index c113cf78..c113cf78 100644
--- a/res/drawable-mdpi/ic_action_artist.png
+++ b/app/src/main/res/drawable-mdpi/ic_action_artist.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_action_rating_bad_dark.png b/app/src/main/res/drawable-mdpi/ic_action_rating_bad_dark.png
index 64f3cd1f..64f3cd1f 100644
--- a/res/drawable-mdpi/ic_action_rating_bad_dark.png
+++ b/app/src/main/res/drawable-mdpi/ic_action_rating_bad_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_action_rating_bad_light.png b/app/src/main/res/drawable-mdpi/ic_action_rating_bad_light.png
index d6c8d42a..d6c8d42a 100644
--- a/res/drawable-mdpi/ic_action_rating_bad_light.png
+++ b/app/src/main/res/drawable-mdpi/ic_action_rating_bad_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_action_rating_bad_selected.png b/app/src/main/res/drawable-mdpi/ic_action_rating_bad_selected.png
index 34f5a9de..34f5a9de 100644
--- a/res/drawable-mdpi/ic_action_rating_bad_selected.png
+++ b/app/src/main/res/drawable-mdpi/ic_action_rating_bad_selected.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_action_rating_good_dark.png b/app/src/main/res/drawable-mdpi/ic_action_rating_good_dark.png
index cadfbe1e..cadfbe1e 100644
--- a/res/drawable-mdpi/ic_action_rating_good_dark.png
+++ b/app/src/main/res/drawable-mdpi/ic_action_rating_good_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_action_rating_good_light.png b/app/src/main/res/drawable-mdpi/ic_action_rating_good_light.png
index 75711920..75711920 100644
--- a/res/drawable-mdpi/ic_action_rating_good_light.png
+++ b/app/src/main/res/drawable-mdpi/ic_action_rating_good_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_action_rating_good_selected.png b/app/src/main/res/drawable-mdpi/ic_action_rating_good_selected.png
index 97d279be..97d279be 100644
--- a/res/drawable-mdpi/ic_action_rating_good_selected.png
+++ b/app/src/main/res/drawable-mdpi/ic_action_rating_good_selected.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_action_song.png b/app/src/main/res/drawable-mdpi/ic_action_song.png
index fa9acbde..fa9acbde 100644
--- a/res/drawable-mdpi/ic_action_song.png
+++ b/app/src/main/res/drawable-mdpi/ic_action_song.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_action_volume_dark.png b/app/src/main/res/drawable-mdpi/ic_action_volume_dark.png
index 2b5f1d11..2b5f1d11 100644
--- a/res/drawable-mdpi/ic_action_volume_dark.png
+++ b/app/src/main/res/drawable-mdpi/ic_action_volume_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_action_volume_light.png b/app/src/main/res/drawable-mdpi/ic_action_volume_light.png
index 47071ccf..47071ccf 100644
--- a/res/drawable-mdpi/ic_action_volume_light.png
+++ b/app/src/main/res/drawable-mdpi/ic_action_volume_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_add_person_dark.png b/app/src/main/res/drawable-mdpi/ic_menu_add_person_dark.png
index 7d64f5d3..7d64f5d3 100644
--- a/res/drawable-mdpi/ic_menu_add_person_dark.png
+++ b/app/src/main/res/drawable-mdpi/ic_menu_add_person_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_add_person_light.png b/app/src/main/res/drawable-mdpi/ic_menu_add_person_light.png
index 55c38c26..55c38c26 100644
--- a/res/drawable-mdpi/ic_menu_add_person_light.png
+++ b/app/src/main/res/drawable-mdpi/ic_menu_add_person_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_admin_dark.png b/app/src/main/res/drawable-mdpi/ic_menu_admin_dark.png
index f88f5f15..f88f5f15 100644
--- a/res/drawable-mdpi/ic_menu_admin_dark.png
+++ b/app/src/main/res/drawable-mdpi/ic_menu_admin_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_admin_light.png b/app/src/main/res/drawable-mdpi/ic_menu_admin_light.png
index 35cd14f4..35cd14f4 100644
--- a/res/drawable-mdpi/ic_menu_admin_light.png
+++ b/app/src/main/res/drawable-mdpi/ic_menu_admin_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_bookmark_dark.png b/app/src/main/res/drawable-mdpi/ic_menu_bookmark_dark.png
index 3360f37e..3360f37e 100644
--- a/res/drawable-mdpi/ic_menu_bookmark_dark.png
+++ b/app/src/main/res/drawable-mdpi/ic_menu_bookmark_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_bookmark_light.png b/app/src/main/res/drawable-mdpi/ic_menu_bookmark_light.png
index b4d916fb..b4d916fb 100644
--- a/res/drawable-mdpi/ic_menu_bookmark_light.png
+++ b/app/src/main/res/drawable-mdpi/ic_menu_bookmark_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_bookmark_selected.png b/app/src/main/res/drawable-mdpi/ic_menu_bookmark_selected.png
index efcc1afa..efcc1afa 100644
--- a/res/drawable-mdpi/ic_menu_bookmark_selected.png
+++ b/app/src/main/res/drawable-mdpi/ic_menu_bookmark_selected.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_chat_dark.png b/app/src/main/res/drawable-mdpi/ic_menu_chat_dark.png
index 74d98888..74d98888 100644
--- a/res/drawable-mdpi/ic_menu_chat_dark.png
+++ b/app/src/main/res/drawable-mdpi/ic_menu_chat_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_chat_light.png b/app/src/main/res/drawable-mdpi/ic_menu_chat_light.png
index 468c1220..468c1220 100644
--- a/res/drawable-mdpi/ic_menu_chat_light.png
+++ b/app/src/main/res/drawable-mdpi/ic_menu_chat_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_chat_send_dark.png b/app/src/main/res/drawable-mdpi/ic_menu_chat_send_dark.png
index 91db4a4a..91db4a4a 100644
--- a/res/drawable-mdpi/ic_menu_chat_send_dark.png
+++ b/app/src/main/res/drawable-mdpi/ic_menu_chat_send_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_chat_send_light.png b/app/src/main/res/drawable-mdpi/ic_menu_chat_send_light.png
index f2a3e724..f2a3e724 100644
--- a/res/drawable-mdpi/ic_menu_chat_send_light.png
+++ b/app/src/main/res/drawable-mdpi/ic_menu_chat_send_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_download_dark.png b/app/src/main/res/drawable-mdpi/ic_menu_download_dark.png
index 935bbd45..935bbd45 100644
--- a/res/drawable-mdpi/ic_menu_download_dark.png
+++ b/app/src/main/res/drawable-mdpi/ic_menu_download_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_download_light.png b/app/src/main/res/drawable-mdpi/ic_menu_download_light.png
index cc13d444..cc13d444 100644
--- a/res/drawable-mdpi/ic_menu_download_light.png
+++ b/app/src/main/res/drawable-mdpi/ic_menu_download_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_library_dark.png b/app/src/main/res/drawable-mdpi/ic_menu_library_dark.png
index 0102d7ad..0102d7ad 100644
--- a/res/drawable-mdpi/ic_menu_library_dark.png
+++ b/app/src/main/res/drawable-mdpi/ic_menu_library_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_library_light.png b/app/src/main/res/drawable-mdpi/ic_menu_library_light.png
index a30b4d39..a30b4d39 100644
--- a/res/drawable-mdpi/ic_menu_library_light.png
+++ b/app/src/main/res/drawable-mdpi/ic_menu_library_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_password_dark.png b/app/src/main/res/drawable-mdpi/ic_menu_password_dark.png
index 74d0095a..74d0095a 100644
--- a/res/drawable-mdpi/ic_menu_password_dark.png
+++ b/app/src/main/res/drawable-mdpi/ic_menu_password_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_password_light.png b/app/src/main/res/drawable-mdpi/ic_menu_password_light.png
index 159f7889..159f7889 100644
--- a/res/drawable-mdpi/ic_menu_password_light.png
+++ b/app/src/main/res/drawable-mdpi/ic_menu_password_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_playlist_dark.png b/app/src/main/res/drawable-mdpi/ic_menu_playlist_dark.png
index ebf00427..ebf00427 100644
--- a/res/drawable-mdpi/ic_menu_playlist_dark.png
+++ b/app/src/main/res/drawable-mdpi/ic_menu_playlist_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_playlist_light.png b/app/src/main/res/drawable-mdpi/ic_menu_playlist_light.png
index e248a488..e248a488 100644
--- a/res/drawable-mdpi/ic_menu_playlist_light.png
+++ b/app/src/main/res/drawable-mdpi/ic_menu_playlist_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_podcast_dark.png b/app/src/main/res/drawable-mdpi/ic_menu_podcast_dark.png
index ad69156a..ad69156a 100644
--- a/res/drawable-mdpi/ic_menu_podcast_dark.png
+++ b/app/src/main/res/drawable-mdpi/ic_menu_podcast_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_podcast_light.png b/app/src/main/res/drawable-mdpi/ic_menu_podcast_light.png
index c15cb03f..c15cb03f 100644
--- a/res/drawable-mdpi/ic_menu_podcast_light.png
+++ b/app/src/main/res/drawable-mdpi/ic_menu_podcast_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_radio_dark.png b/app/src/main/res/drawable-mdpi/ic_menu_radio_dark.png
index bab20118..bab20118 100644
--- a/res/drawable-mdpi/ic_menu_radio_dark.png
+++ b/app/src/main/res/drawable-mdpi/ic_menu_radio_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_radio_light.png b/app/src/main/res/drawable-mdpi/ic_menu_radio_light.png
index 72578d54..72578d54 100644
--- a/res/drawable-mdpi/ic_menu_radio_light.png
+++ b/app/src/main/res/drawable-mdpi/ic_menu_radio_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_refresh_dark.png b/app/src/main/res/drawable-mdpi/ic_menu_refresh_dark.png
index 554c07dc..554c07dc 100644
--- a/res/drawable-mdpi/ic_menu_refresh_dark.png
+++ b/app/src/main/res/drawable-mdpi/ic_menu_refresh_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_refresh_light.png b/app/src/main/res/drawable-mdpi/ic_menu_refresh_light.png
index a2d90c16..a2d90c16 100644
--- a/res/drawable-mdpi/ic_menu_refresh_light.png
+++ b/app/src/main/res/drawable-mdpi/ic_menu_refresh_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_remove_dark.png b/app/src/main/res/drawable-mdpi/ic_menu_remove_dark.png
index 5ba24546..5ba24546 100644
--- a/res/drawable-mdpi/ic_menu_remove_dark.png
+++ b/app/src/main/res/drawable-mdpi/ic_menu_remove_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_remove_light.png b/app/src/main/res/drawable-mdpi/ic_menu_remove_light.png
index 93483b6c..93483b6c 100644
--- a/res/drawable-mdpi/ic_menu_remove_light.png
+++ b/app/src/main/res/drawable-mdpi/ic_menu_remove_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_save_dark.png b/app/src/main/res/drawable-mdpi/ic_menu_save_dark.png
index 89aa17cc..89aa17cc 100644
--- a/res/drawable-mdpi/ic_menu_save_dark.png
+++ b/app/src/main/res/drawable-mdpi/ic_menu_save_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_save_light.png b/app/src/main/res/drawable-mdpi/ic_menu_save_light.png
index dcb3a2f6..dcb3a2f6 100644
--- a/res/drawable-mdpi/ic_menu_save_light.png
+++ b/app/src/main/res/drawable-mdpi/ic_menu_save_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_search_dark.png b/app/src/main/res/drawable-mdpi/ic_menu_search_dark.png
index 076085c5..076085c5 100644
--- a/res/drawable-mdpi/ic_menu_search_dark.png
+++ b/app/src/main/res/drawable-mdpi/ic_menu_search_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_search_light.png b/app/src/main/res/drawable-mdpi/ic_menu_search_light.png
index 026c8498..026c8498 100644
--- a/res/drawable-mdpi/ic_menu_search_light.png
+++ b/app/src/main/res/drawable-mdpi/ic_menu_search_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_settings_dark.png b/app/src/main/res/drawable-mdpi/ic_menu_settings_dark.png
index fc2bf8c3..fc2bf8c3 100644
--- a/res/drawable-mdpi/ic_menu_settings_dark.png
+++ b/app/src/main/res/drawable-mdpi/ic_menu_settings_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_settings_light.png b/app/src/main/res/drawable-mdpi/ic_menu_settings_light.png
index 0e65c682..0e65c682 100644
--- a/res/drawable-mdpi/ic_menu_settings_light.png
+++ b/app/src/main/res/drawable-mdpi/ic_menu_settings_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_share_dark.png b/app/src/main/res/drawable-mdpi/ic_menu_share_dark.png
index c37aadba..c37aadba 100644
--- a/res/drawable-mdpi/ic_menu_share_dark.png
+++ b/app/src/main/res/drawable-mdpi/ic_menu_share_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_share_light.png b/app/src/main/res/drawable-mdpi/ic_menu_share_light.png
index 72eeb598..72eeb598 100644
--- a/res/drawable-mdpi/ic_menu_share_light.png
+++ b/app/src/main/res/drawable-mdpi/ic_menu_share_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_shuffle_dark.png b/app/src/main/res/drawable-mdpi/ic_menu_shuffle_dark.png
index 7007fde5..7007fde5 100644
--- a/res/drawable-mdpi/ic_menu_shuffle_dark.png
+++ b/app/src/main/res/drawable-mdpi/ic_menu_shuffle_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_shuffle_light.png b/app/src/main/res/drawable-mdpi/ic_menu_shuffle_light.png
index 4d07c3b4..4d07c3b4 100644
--- a/res/drawable-mdpi/ic_menu_shuffle_light.png
+++ b/app/src/main/res/drawable-mdpi/ic_menu_shuffle_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_number_border.png b/app/src/main/res/drawable-mdpi/ic_number_border.png
index 212fabce..212fabce 100644
--- a/res/drawable-mdpi/ic_number_border.png
+++ b/app/src/main/res/drawable-mdpi/ic_number_border.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_social_person.png b/app/src/main/res/drawable-mdpi/ic_social_person.png
index c09313d8..c09313d8 100644
--- a/res/drawable-mdpi/ic_social_person.png
+++ b/app/src/main/res/drawable-mdpi/ic_social_person.png
Binary files differ
diff --git a/res/drawable-mdpi/launch.png b/app/src/main/res/drawable-mdpi/launch.png
index 88887e94..88887e94 100644
--- a/res/drawable-mdpi/launch.png
+++ b/app/src/main/res/drawable-mdpi/launch.png
Binary files differ
diff --git a/res/drawable-mdpi/main_offline_dark.png b/app/src/main/res/drawable-mdpi/main_offline_dark.png
index 4990fb8e..4990fb8e 100644
--- a/res/drawable-mdpi/main_offline_dark.png
+++ b/app/src/main/res/drawable-mdpi/main_offline_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/main_offline_light.png b/app/src/main/res/drawable-mdpi/main_offline_light.png
index e70ec1c2..e70ec1c2 100644
--- a/res/drawable-mdpi/main_offline_light.png
+++ b/app/src/main/res/drawable-mdpi/main_offline_light.png
Binary files differ
diff --git a/res/drawable-mdpi/main_select_server_dark.png b/app/src/main/res/drawable-mdpi/main_select_server_dark.png
index 119b1573..119b1573 100644
--- a/res/drawable-mdpi/main_select_server_dark.png
+++ b/app/src/main/res/drawable-mdpi/main_select_server_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/main_select_server_light.png b/app/src/main/res/drawable-mdpi/main_select_server_light.png
index 7d8dad34..7d8dad34 100644
--- a/res/drawable-mdpi/main_select_server_light.png
+++ b/app/src/main/res/drawable-mdpi/main_select_server_light.png
Binary files differ
diff --git a/res/drawable-mdpi/media_backward_dark.png b/app/src/main/res/drawable-mdpi/media_backward_dark.png
index 4f2233a1..4f2233a1 100644
--- a/res/drawable-mdpi/media_backward_dark.png
+++ b/app/src/main/res/drawable-mdpi/media_backward_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/media_backward_light.png b/app/src/main/res/drawable-mdpi/media_backward_light.png
index 425f2df7..425f2df7 100644
--- a/res/drawable-mdpi/media_backward_light.png
+++ b/app/src/main/res/drawable-mdpi/media_backward_light.png
Binary files differ
diff --git a/res/drawable-mdpi/media_forward_dark.png b/app/src/main/res/drawable-mdpi/media_forward_dark.png
index 1641c0fa..1641c0fa 100644
--- a/res/drawable-mdpi/media_forward_dark.png
+++ b/app/src/main/res/drawable-mdpi/media_forward_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/media_forward_light.png b/app/src/main/res/drawable-mdpi/media_forward_light.png
index 2e66868f..2e66868f 100644
--- a/res/drawable-mdpi/media_forward_light.png
+++ b/app/src/main/res/drawable-mdpi/media_forward_light.png
Binary files differ
diff --git a/res/drawable-mdpi/media_pause_dark.png b/app/src/main/res/drawable-mdpi/media_pause_dark.png
index 3580dab4..3580dab4 100644
--- a/res/drawable-mdpi/media_pause_dark.png
+++ b/app/src/main/res/drawable-mdpi/media_pause_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/media_pause_light.png b/app/src/main/res/drawable-mdpi/media_pause_light.png
index 7e9ade73..7e9ade73 100644
--- a/res/drawable-mdpi/media_pause_light.png
+++ b/app/src/main/res/drawable-mdpi/media_pause_light.png
Binary files differ
diff --git a/res/drawable-mdpi/media_start_dark.png b/app/src/main/res/drawable-mdpi/media_start_dark.png
index a2f198ae..a2f198ae 100644
--- a/res/drawable-mdpi/media_start_dark.png
+++ b/app/src/main/res/drawable-mdpi/media_start_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/media_start_light.png b/app/src/main/res/drawable-mdpi/media_start_light.png
index d69107ba..d69107ba 100644
--- a/res/drawable-mdpi/media_start_light.png
+++ b/app/src/main/res/drawable-mdpi/media_start_light.png
Binary files differ
diff --git a/res/drawable-mdpi/media_stop_dark.png b/app/src/main/res/drawable-mdpi/media_stop_dark.png
index 944482e6..944482e6 100644
--- a/res/drawable-mdpi/media_stop_dark.png
+++ b/app/src/main/res/drawable-mdpi/media_stop_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/media_stop_light.png b/app/src/main/res/drawable-mdpi/media_stop_light.png
index ff1932a1..ff1932a1 100644
--- a/res/drawable-mdpi/media_stop_light.png
+++ b/app/src/main/res/drawable-mdpi/media_stop_light.png
Binary files differ
diff --git a/res/drawable-mdpi/notification_close.png b/app/src/main/res/drawable-mdpi/notification_close.png
index 2a8f9a36..2a8f9a36 100644
--- a/res/drawable-mdpi/notification_close.png
+++ b/app/src/main/res/drawable-mdpi/notification_close.png
Binary files differ
diff --git a/res/drawable-mdpi/notification_next.png b/app/src/main/res/drawable-mdpi/notification_next.png
index f85d45a5..f85d45a5 100644
--- a/res/drawable-mdpi/notification_next.png
+++ b/app/src/main/res/drawable-mdpi/notification_next.png
Binary files differ
diff --git a/res/drawable-mdpi/notification_pause.png b/app/src/main/res/drawable-mdpi/notification_pause.png
index 06c3cf9d..06c3cf9d 100644
--- a/res/drawable-mdpi/notification_pause.png
+++ b/app/src/main/res/drawable-mdpi/notification_pause.png
Binary files differ
diff --git a/res/drawable-mdpi/notification_play.png b/app/src/main/res/drawable-mdpi/notification_play.png
index 0248c1cc..0248c1cc 100644
--- a/res/drawable-mdpi/notification_play.png
+++ b/app/src/main/res/drawable-mdpi/notification_play.png
Binary files differ
diff --git a/res/drawable-mdpi/notification_previous.png b/app/src/main/res/drawable-mdpi/notification_previous.png
index 167d7d05..167d7d05 100644
--- a/res/drawable-mdpi/notification_previous.png
+++ b/app/src/main/res/drawable-mdpi/notification_previous.png
Binary files differ
diff --git a/res/drawable-mdpi/now_playing.png b/app/src/main/res/drawable-mdpi/now_playing.png
index 0248c1cc..0248c1cc 100644
--- a/res/drawable-mdpi/now_playing.png
+++ b/app/src/main/res/drawable-mdpi/now_playing.png
Binary files differ
diff --git a/res/drawable-mdpi/stat_notify_download.png b/app/src/main/res/drawable-mdpi/stat_notify_download.png
index 4c2a22de..4c2a22de 100644
--- a/res/drawable-mdpi/stat_notify_download.png
+++ b/app/src/main/res/drawable-mdpi/stat_notify_download.png
Binary files differ
diff --git a/res/drawable-mdpi/stat_notify_playing.png b/app/src/main/res/drawable-mdpi/stat_notify_playing.png
index 0248c1cc..0248c1cc 100644
--- a/res/drawable-mdpi/stat_notify_playing.png
+++ b/app/src/main/res/drawable-mdpi/stat_notify_playing.png
Binary files differ
diff --git a/res/drawable-mdpi/stat_notify_sync.png b/app/src/main/res/drawable-mdpi/stat_notify_sync.png
index 35a06857..35a06857 100644
--- a/res/drawable-mdpi/stat_notify_sync.png
+++ b/app/src/main/res/drawable-mdpi/stat_notify_sync.png
Binary files differ
diff --git a/res/drawable-xhdpi-v11/notification_close.png b/app/src/main/res/drawable-xhdpi-v11/notification_close.png
index f1013578..f1013578 100644
--- a/res/drawable-xhdpi-v11/notification_close.png
+++ b/app/src/main/res/drawable-xhdpi-v11/notification_close.png
Binary files differ
diff --git a/res/drawable-xhdpi-v11/notification_next.png b/app/src/main/res/drawable-xhdpi-v11/notification_next.png
index ad070680..ad070680 100644
--- a/res/drawable-xhdpi-v11/notification_next.png
+++ b/app/src/main/res/drawable-xhdpi-v11/notification_next.png
Binary files differ
diff --git a/res/drawable-xhdpi-v11/notification_pause.png b/app/src/main/res/drawable-xhdpi-v11/notification_pause.png
index 709602aa..709602aa 100644
--- a/res/drawable-xhdpi-v11/notification_pause.png
+++ b/app/src/main/res/drawable-xhdpi-v11/notification_pause.png
Binary files differ
diff --git a/res/drawable-xhdpi-v11/notification_play.png b/app/src/main/res/drawable-xhdpi-v11/notification_play.png
index e2bafa6a..e2bafa6a 100644
--- a/res/drawable-xhdpi-v11/notification_play.png
+++ b/app/src/main/res/drawable-xhdpi-v11/notification_play.png
Binary files differ
diff --git a/res/drawable-xhdpi-v11/notification_previous.png b/app/src/main/res/drawable-xhdpi-v11/notification_previous.png
index d22488cb..d22488cb 100644
--- a/res/drawable-xhdpi-v11/notification_previous.png
+++ b/app/src/main/res/drawable-xhdpi-v11/notification_previous.png
Binary files differ
diff --git a/res/drawable-xhdpi-v11/stat_notify_download.png b/app/src/main/res/drawable-xhdpi-v11/stat_notify_download.png
index 96ceb383..96ceb383 100644
--- a/res/drawable-xhdpi-v11/stat_notify_download.png
+++ b/app/src/main/res/drawable-xhdpi-v11/stat_notify_download.png
Binary files differ
diff --git a/res/drawable-xhdpi-v11/stat_notify_playing.png b/app/src/main/res/drawable-xhdpi-v11/stat_notify_playing.png
index e2bafa6a..e2bafa6a 100644
--- a/res/drawable-xhdpi-v11/stat_notify_playing.png
+++ b/app/src/main/res/drawable-xhdpi-v11/stat_notify_playing.png
Binary files differ
diff --git a/res/drawable-xhdpi-v11/stat_notify_sync.png b/app/src/main/res/drawable-xhdpi-v11/stat_notify_sync.png
index b723bf54..b723bf54 100644
--- a/res/drawable-xhdpi-v11/stat_notify_sync.png
+++ b/app/src/main/res/drawable-xhdpi-v11/stat_notify_sync.png
Binary files differ
diff --git a/res/drawable-xhdpi/action_toggle_list_dark.png b/app/src/main/res/drawable-xhdpi/action_toggle_list_dark.png
index 92003c6b..92003c6b 100644
--- a/res/drawable-xhdpi/action_toggle_list_dark.png
+++ b/app/src/main/res/drawable-xhdpi/action_toggle_list_dark.png
Binary files differ
diff --git a/res/drawable-xhdpi/action_toggle_list_light.png b/app/src/main/res/drawable-xhdpi/action_toggle_list_light.png
index a4007ea5..a4007ea5 100644
--- a/res/drawable-xhdpi/action_toggle_list_light.png
+++ b/app/src/main/res/drawable-xhdpi/action_toggle_list_light.png
Binary files differ
diff --git a/res/drawable-xhdpi/download_cached.png b/app/src/main/res/drawable-xhdpi/download_cached.png
index 70de6f04..70de6f04 100644
--- a/res/drawable-xhdpi/download_cached.png
+++ b/app/src/main/res/drawable-xhdpi/download_cached.png
Binary files differ
diff --git a/res/drawable-xhdpi/download_none_dark.png b/app/src/main/res/drawable-xhdpi/download_none_dark.png
index 7be3c2a4..7be3c2a4 100644
--- a/res/drawable-xhdpi/download_none_dark.png
+++ b/app/src/main/res/drawable-xhdpi/download_none_dark.png
Binary files differ
diff --git a/res/drawable-xhdpi/download_none_light.png b/app/src/main/res/drawable-xhdpi/download_none_light.png
index 817651d7..817651d7 100644
--- a/res/drawable-xhdpi/download_none_light.png
+++ b/app/src/main/res/drawable-xhdpi/download_none_light.png
Binary files differ
diff --git a/res/drawable-xhdpi/download_pinned.png b/app/src/main/res/drawable-xhdpi/download_pinned.png
index 79bf92e3..79bf92e3 100644
--- a/res/drawable-xhdpi/download_pinned.png
+++ b/app/src/main/res/drawable-xhdpi/download_pinned.png
Binary files differ
diff --git a/res/drawable-xhdpi/downloading_dark.png b/app/src/main/res/drawable-xhdpi/downloading_dark.png
index 3f14bdf4..3f14bdf4 100644
--- a/res/drawable-xhdpi/downloading_dark.png
+++ b/app/src/main/res/drawable-xhdpi/downloading_dark.png
Binary files differ
diff --git a/res/drawable-xhdpi/downloading_light.png b/app/src/main/res/drawable-xhdpi/downloading_light.png
index 643c15d0..643c15d0 100644
--- a/res/drawable-xhdpi/downloading_light.png
+++ b/app/src/main/res/drawable-xhdpi/downloading_light.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_action_add_dark.png b/app/src/main/res/drawable-xhdpi/ic_action_add_dark.png
index 93eae7c6..93eae7c6 100644
--- a/res/drawable-xhdpi/ic_action_add_dark.png
+++ b/app/src/main/res/drawable-xhdpi/ic_action_add_dark.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_action_add_light.png b/app/src/main/res/drawable-xhdpi/ic_action_add_light.png
index 1ebdb432..1ebdb432 100644
--- a/res/drawable-xhdpi/ic_action_add_light.png
+++ b/app/src/main/res/drawable-xhdpi/ic_action_add_light.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_action_album.png b/app/src/main/res/drawable-xhdpi/ic_action_album.png
index e4b12908..e4b12908 100644
--- a/res/drawable-xhdpi/ic_action_album.png
+++ b/app/src/main/res/drawable-xhdpi/ic_action_album.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_action_artist.png b/app/src/main/res/drawable-xhdpi/ic_action_artist.png
index 2dff43ea..2dff43ea 100644
--- a/res/drawable-xhdpi/ic_action_artist.png
+++ b/app/src/main/res/drawable-xhdpi/ic_action_artist.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_action_rating_bad_dark.png b/app/src/main/res/drawable-xhdpi/ic_action_rating_bad_dark.png
index 1393be0c..1393be0c 100644
--- a/res/drawable-xhdpi/ic_action_rating_bad_dark.png
+++ b/app/src/main/res/drawable-xhdpi/ic_action_rating_bad_dark.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_action_rating_bad_light.png b/app/src/main/res/drawable-xhdpi/ic_action_rating_bad_light.png
index fc1959b6..fc1959b6 100644
--- a/res/drawable-xhdpi/ic_action_rating_bad_light.png
+++ b/app/src/main/res/drawable-xhdpi/ic_action_rating_bad_light.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_action_rating_bad_selected.png b/app/src/main/res/drawable-xhdpi/ic_action_rating_bad_selected.png
index cf7802d5..cf7802d5 100644
--- a/res/drawable-xhdpi/ic_action_rating_bad_selected.png
+++ b/app/src/main/res/drawable-xhdpi/ic_action_rating_bad_selected.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_action_rating_good_dark.png b/app/src/main/res/drawable-xhdpi/ic_action_rating_good_dark.png
index 249ea9ec..249ea9ec 100644
--- a/res/drawable-xhdpi/ic_action_rating_good_dark.png
+++ b/app/src/main/res/drawable-xhdpi/ic_action_rating_good_dark.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_action_rating_good_light.png b/app/src/main/res/drawable-xhdpi/ic_action_rating_good_light.png
index c8a776b0..c8a776b0 100644
--- a/res/drawable-xhdpi/ic_action_rating_good_light.png
+++ b/app/src/main/res/drawable-xhdpi/ic_action_rating_good_light.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_action_rating_good_selected.png b/app/src/main/res/drawable-xhdpi/ic_action_rating_good_selected.png
index c6770221..c6770221 100644
--- a/res/drawable-xhdpi/ic_action_rating_good_selected.png
+++ b/app/src/main/res/drawable-xhdpi/ic_action_rating_good_selected.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_action_song.png b/app/src/main/res/drawable-xhdpi/ic_action_song.png
index 29fd3a2d..29fd3a2d 100644
--- a/res/drawable-xhdpi/ic_action_song.png
+++ b/app/src/main/res/drawable-xhdpi/ic_action_song.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_action_volume_dark.png b/app/src/main/res/drawable-xhdpi/ic_action_volume_dark.png
index 400de38b..400de38b 100644
--- a/res/drawable-xhdpi/ic_action_volume_dark.png
+++ b/app/src/main/res/drawable-xhdpi/ic_action_volume_dark.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_action_volume_light.png b/app/src/main/res/drawable-xhdpi/ic_action_volume_light.png
index 9a1128c1..9a1128c1 100644
--- a/res/drawable-xhdpi/ic_action_volume_light.png
+++ b/app/src/main/res/drawable-xhdpi/ic_action_volume_light.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_add_person_dark.png b/app/src/main/res/drawable-xhdpi/ic_menu_add_person_dark.png
index 30c78e5a..30c78e5a 100644
--- a/res/drawable-xhdpi/ic_menu_add_person_dark.png
+++ b/app/src/main/res/drawable-xhdpi/ic_menu_add_person_dark.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_add_person_light.png b/app/src/main/res/drawable-xhdpi/ic_menu_add_person_light.png
index b3fb3808..b3fb3808 100644
--- a/res/drawable-xhdpi/ic_menu_add_person_light.png
+++ b/app/src/main/res/drawable-xhdpi/ic_menu_add_person_light.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_admin_dark.png b/app/src/main/res/drawable-xhdpi/ic_menu_admin_dark.png
index 09f90c15..09f90c15 100644
--- a/res/drawable-xhdpi/ic_menu_admin_dark.png
+++ b/app/src/main/res/drawable-xhdpi/ic_menu_admin_dark.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_admin_light.png b/app/src/main/res/drawable-xhdpi/ic_menu_admin_light.png
index 4bd3beaf..4bd3beaf 100644
--- a/res/drawable-xhdpi/ic_menu_admin_light.png
+++ b/app/src/main/res/drawable-xhdpi/ic_menu_admin_light.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_bookmark_dark.png b/app/src/main/res/drawable-xhdpi/ic_menu_bookmark_dark.png
index 18f71365..18f71365 100644
--- a/res/drawable-xhdpi/ic_menu_bookmark_dark.png
+++ b/app/src/main/res/drawable-xhdpi/ic_menu_bookmark_dark.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_bookmark_light.png b/app/src/main/res/drawable-xhdpi/ic_menu_bookmark_light.png
index d5776317..d5776317 100644
--- a/res/drawable-xhdpi/ic_menu_bookmark_light.png
+++ b/app/src/main/res/drawable-xhdpi/ic_menu_bookmark_light.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_bookmark_selected.png b/app/src/main/res/drawable-xhdpi/ic_menu_bookmark_selected.png
index 353b7b79..353b7b79 100644
--- a/res/drawable-xhdpi/ic_menu_bookmark_selected.png
+++ b/app/src/main/res/drawable-xhdpi/ic_menu_bookmark_selected.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_chat_dark.png b/app/src/main/res/drawable-xhdpi/ic_menu_chat_dark.png
index 28318219..28318219 100644
--- a/res/drawable-xhdpi/ic_menu_chat_dark.png
+++ b/app/src/main/res/drawable-xhdpi/ic_menu_chat_dark.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_chat_light.png b/app/src/main/res/drawable-xhdpi/ic_menu_chat_light.png
index dcc95dcb..dcc95dcb 100644
--- a/res/drawable-xhdpi/ic_menu_chat_light.png
+++ b/app/src/main/res/drawable-xhdpi/ic_menu_chat_light.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_chat_send_dark.png b/app/src/main/res/drawable-xhdpi/ic_menu_chat_send_dark.png
index c0a5a3eb..c0a5a3eb 100644
--- a/res/drawable-xhdpi/ic_menu_chat_send_dark.png
+++ b/app/src/main/res/drawable-xhdpi/ic_menu_chat_send_dark.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_chat_send_light.png b/app/src/main/res/drawable-xhdpi/ic_menu_chat_send_light.png
index f9c3b9bb..f9c3b9bb 100644
--- a/res/drawable-xhdpi/ic_menu_chat_send_light.png
+++ b/app/src/main/res/drawable-xhdpi/ic_menu_chat_send_light.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_download_dark.png b/app/src/main/res/drawable-xhdpi/ic_menu_download_dark.png
index 6b6c65df..6b6c65df 100644
--- a/res/drawable-xhdpi/ic_menu_download_dark.png
+++ b/app/src/main/res/drawable-xhdpi/ic_menu_download_dark.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_download_light.png b/app/src/main/res/drawable-xhdpi/ic_menu_download_light.png
index c8caf90b..c8caf90b 100644
--- a/res/drawable-xhdpi/ic_menu_download_light.png
+++ b/app/src/main/res/drawable-xhdpi/ic_menu_download_light.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_library_dark.png b/app/src/main/res/drawable-xhdpi/ic_menu_library_dark.png
index b1612f65..b1612f65 100644
--- a/res/drawable-xhdpi/ic_menu_library_dark.png
+++ b/app/src/main/res/drawable-xhdpi/ic_menu_library_dark.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_library_light.png b/app/src/main/res/drawable-xhdpi/ic_menu_library_light.png
index 1f93c8f2..1f93c8f2 100644
--- a/res/drawable-xhdpi/ic_menu_library_light.png
+++ b/app/src/main/res/drawable-xhdpi/ic_menu_library_light.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_password_dark.png b/app/src/main/res/drawable-xhdpi/ic_menu_password_dark.png
index d1fc0a97..d1fc0a97 100644
--- a/res/drawable-xhdpi/ic_menu_password_dark.png
+++ b/app/src/main/res/drawable-xhdpi/ic_menu_password_dark.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_password_light.png b/app/src/main/res/drawable-xhdpi/ic_menu_password_light.png
index 1cbf085c..1cbf085c 100644
--- a/res/drawable-xhdpi/ic_menu_password_light.png
+++ b/app/src/main/res/drawable-xhdpi/ic_menu_password_light.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_playlist_dark.png b/app/src/main/res/drawable-xhdpi/ic_menu_playlist_dark.png
index fd6cd498..fd6cd498 100644
--- a/res/drawable-xhdpi/ic_menu_playlist_dark.png
+++ b/app/src/main/res/drawable-xhdpi/ic_menu_playlist_dark.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_playlist_light.png b/app/src/main/res/drawable-xhdpi/ic_menu_playlist_light.png
index e7e510d0..e7e510d0 100644
--- a/res/drawable-xhdpi/ic_menu_playlist_light.png
+++ b/app/src/main/res/drawable-xhdpi/ic_menu_playlist_light.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_podcast_dark.png b/app/src/main/res/drawable-xhdpi/ic_menu_podcast_dark.png
index 40469b46..40469b46 100644
--- a/res/drawable-xhdpi/ic_menu_podcast_dark.png
+++ b/app/src/main/res/drawable-xhdpi/ic_menu_podcast_dark.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_podcast_light.png b/app/src/main/res/drawable-xhdpi/ic_menu_podcast_light.png
index 3748526a..3748526a 100644
--- a/res/drawable-xhdpi/ic_menu_podcast_light.png
+++ b/app/src/main/res/drawable-xhdpi/ic_menu_podcast_light.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_radio_dark.png b/app/src/main/res/drawable-xhdpi/ic_menu_radio_dark.png
index 3a4114a3..3a4114a3 100644
--- a/res/drawable-xhdpi/ic_menu_radio_dark.png
+++ b/app/src/main/res/drawable-xhdpi/ic_menu_radio_dark.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_radio_light.png b/app/src/main/res/drawable-xhdpi/ic_menu_radio_light.png
index 5bcc9261..5bcc9261 100644
--- a/res/drawable-xhdpi/ic_menu_radio_light.png
+++ b/app/src/main/res/drawable-xhdpi/ic_menu_radio_light.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_refresh_dark.png b/app/src/main/res/drawable-xhdpi/ic_menu_refresh_dark.png
index b6801006..b6801006 100644
--- a/res/drawable-xhdpi/ic_menu_refresh_dark.png
+++ b/app/src/main/res/drawable-xhdpi/ic_menu_refresh_dark.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_refresh_light.png b/app/src/main/res/drawable-xhdpi/ic_menu_refresh_light.png
index 38943f82..38943f82 100644
--- a/res/drawable-xhdpi/ic_menu_refresh_light.png
+++ b/app/src/main/res/drawable-xhdpi/ic_menu_refresh_light.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_remove_dark.png b/app/src/main/res/drawable-xhdpi/ic_menu_remove_dark.png
index 09ce75e2..09ce75e2 100644
--- a/res/drawable-xhdpi/ic_menu_remove_dark.png
+++ b/app/src/main/res/drawable-xhdpi/ic_menu_remove_dark.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_remove_light.png b/app/src/main/res/drawable-xhdpi/ic_menu_remove_light.png
index 94f7c8c1..94f7c8c1 100644
--- a/res/drawable-xhdpi/ic_menu_remove_light.png
+++ b/app/src/main/res/drawable-xhdpi/ic_menu_remove_light.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_save_dark.png b/app/src/main/res/drawable-xhdpi/ic_menu_save_dark.png
index 1612fd0a..1612fd0a 100644
--- a/res/drawable-xhdpi/ic_menu_save_dark.png
+++ b/app/src/main/res/drawable-xhdpi/ic_menu_save_dark.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_save_light.png b/app/src/main/res/drawable-xhdpi/ic_menu_save_light.png
index 5dcd75d7..5dcd75d7 100644
--- a/res/drawable-xhdpi/ic_menu_save_light.png
+++ b/app/src/main/res/drawable-xhdpi/ic_menu_save_light.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_search_dark.png b/app/src/main/res/drawable-xhdpi/ic_menu_search_dark.png
index 1ae3dff0..1ae3dff0 100644
--- a/res/drawable-xhdpi/ic_menu_search_dark.png
+++ b/app/src/main/res/drawable-xhdpi/ic_menu_search_dark.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_search_light.png b/app/src/main/res/drawable-xhdpi/ic_menu_search_light.png
index 705074bd..705074bd 100644
--- a/res/drawable-xhdpi/ic_menu_search_light.png
+++ b/app/src/main/res/drawable-xhdpi/ic_menu_search_light.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_settings_dark.png b/app/src/main/res/drawable-xhdpi/ic_menu_settings_dark.png
index ae917587..ae917587 100644
--- a/res/drawable-xhdpi/ic_menu_settings_dark.png
+++ b/app/src/main/res/drawable-xhdpi/ic_menu_settings_dark.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_settings_light.png b/app/src/main/res/drawable-xhdpi/ic_menu_settings_light.png
index 29f961b2..29f961b2 100644
--- a/res/drawable-xhdpi/ic_menu_settings_light.png
+++ b/app/src/main/res/drawable-xhdpi/ic_menu_settings_light.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_share_dark.png b/app/src/main/res/drawable-xhdpi/ic_menu_share_dark.png
index 41073d1f..41073d1f 100644
--- a/res/drawable-xhdpi/ic_menu_share_dark.png
+++ b/app/src/main/res/drawable-xhdpi/ic_menu_share_dark.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_share_light.png b/app/src/main/res/drawable-xhdpi/ic_menu_share_light.png
index 36f9f55f..36f9f55f 100644
--- a/res/drawable-xhdpi/ic_menu_share_light.png
+++ b/app/src/main/res/drawable-xhdpi/ic_menu_share_light.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_shuffle_dark.png b/app/src/main/res/drawable-xhdpi/ic_menu_shuffle_dark.png
index e3a31a84..e3a31a84 100644
--- a/res/drawable-xhdpi/ic_menu_shuffle_dark.png
+++ b/app/src/main/res/drawable-xhdpi/ic_menu_shuffle_dark.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_shuffle_light.png b/app/src/main/res/drawable-xhdpi/ic_menu_shuffle_light.png
index 14eb942c..14eb942c 100644
--- a/res/drawable-xhdpi/ic_menu_shuffle_light.png
+++ b/app/src/main/res/drawable-xhdpi/ic_menu_shuffle_light.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_number_border.png b/app/src/main/res/drawable-xhdpi/ic_number_border.png
index 1b370fbd..1b370fbd 100644
--- a/res/drawable-xhdpi/ic_number_border.png
+++ b/app/src/main/res/drawable-xhdpi/ic_number_border.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_social_person.png b/app/src/main/res/drawable-xhdpi/ic_social_person.png
index ed333afe..ed333afe 100644
--- a/res/drawable-xhdpi/ic_social_person.png
+++ b/app/src/main/res/drawable-xhdpi/ic_social_person.png
Binary files differ
diff --git a/res/drawable-xhdpi/launch.png b/app/src/main/res/drawable-xhdpi/launch.png
index 0f647dfa..0f647dfa 100644
--- a/res/drawable-xhdpi/launch.png
+++ b/app/src/main/res/drawable-xhdpi/launch.png
Binary files differ
diff --git a/res/drawable-xhdpi/main_offline_dark.png b/app/src/main/res/drawable-xhdpi/main_offline_dark.png
index 231e4715..231e4715 100644
--- a/res/drawable-xhdpi/main_offline_dark.png
+++ b/app/src/main/res/drawable-xhdpi/main_offline_dark.png
Binary files differ
diff --git a/res/drawable-xhdpi/main_offline_light.png b/app/src/main/res/drawable-xhdpi/main_offline_light.png
index 87937fcb..87937fcb 100644
--- a/res/drawable-xhdpi/main_offline_light.png
+++ b/app/src/main/res/drawable-xhdpi/main_offline_light.png
Binary files differ
diff --git a/res/drawable-xhdpi/main_select_server_dark.png b/app/src/main/res/drawable-xhdpi/main_select_server_dark.png
index b84f1851..b84f1851 100644
--- a/res/drawable-xhdpi/main_select_server_dark.png
+++ b/app/src/main/res/drawable-xhdpi/main_select_server_dark.png
Binary files differ
diff --git a/res/drawable-xhdpi/main_select_server_light.png b/app/src/main/res/drawable-xhdpi/main_select_server_light.png
index ee154cc7..ee154cc7 100644
--- a/res/drawable-xhdpi/main_select_server_light.png
+++ b/app/src/main/res/drawable-xhdpi/main_select_server_light.png
Binary files differ
diff --git a/res/drawable-xhdpi/media_backward_dark.png b/app/src/main/res/drawable-xhdpi/media_backward_dark.png
index 3c9921a8..3c9921a8 100644
--- a/res/drawable-xhdpi/media_backward_dark.png
+++ b/app/src/main/res/drawable-xhdpi/media_backward_dark.png
Binary files differ
diff --git a/res/drawable-xhdpi/media_backward_light.png b/app/src/main/res/drawable-xhdpi/media_backward_light.png
index aafd76fa..aafd76fa 100644
--- a/res/drawable-xhdpi/media_backward_light.png
+++ b/app/src/main/res/drawable-xhdpi/media_backward_light.png
Binary files differ
diff --git a/res/drawable-xhdpi/media_forward_dark.png b/app/src/main/res/drawable-xhdpi/media_forward_dark.png
index b082b3a6..b082b3a6 100644
--- a/res/drawable-xhdpi/media_forward_dark.png
+++ b/app/src/main/res/drawable-xhdpi/media_forward_dark.png
Binary files differ
diff --git a/res/drawable-xhdpi/media_forward_light.png b/app/src/main/res/drawable-xhdpi/media_forward_light.png
index 20772843..20772843 100644
--- a/res/drawable-xhdpi/media_forward_light.png
+++ b/app/src/main/res/drawable-xhdpi/media_forward_light.png
Binary files differ
diff --git a/res/drawable-xhdpi/media_pause_dark.png b/app/src/main/res/drawable-xhdpi/media_pause_dark.png
index aafdd4aa..aafdd4aa 100644
--- a/res/drawable-xhdpi/media_pause_dark.png
+++ b/app/src/main/res/drawable-xhdpi/media_pause_dark.png
Binary files differ
diff --git a/res/drawable-xhdpi/media_pause_light.png b/app/src/main/res/drawable-xhdpi/media_pause_light.png
index 2639777d..2639777d 100644
--- a/res/drawable-xhdpi/media_pause_light.png
+++ b/app/src/main/res/drawable-xhdpi/media_pause_light.png
Binary files differ
diff --git a/res/drawable-xhdpi/media_start_dark.png b/app/src/main/res/drawable-xhdpi/media_start_dark.png
index 9e63c90b..9e63c90b 100644
--- a/res/drawable-xhdpi/media_start_dark.png
+++ b/app/src/main/res/drawable-xhdpi/media_start_dark.png
Binary files differ
diff --git a/res/drawable-xhdpi/media_start_light.png b/app/src/main/res/drawable-xhdpi/media_start_light.png
index 2ff8c399..2ff8c399 100644
--- a/res/drawable-xhdpi/media_start_light.png
+++ b/app/src/main/res/drawable-xhdpi/media_start_light.png
Binary files differ
diff --git a/res/drawable-xhdpi/media_stop_dark.png b/app/src/main/res/drawable-xhdpi/media_stop_dark.png
index 9cb32909..9cb32909 100644
--- a/res/drawable-xhdpi/media_stop_dark.png
+++ b/app/src/main/res/drawable-xhdpi/media_stop_dark.png
Binary files differ
diff --git a/res/drawable-xhdpi/media_stop_light.png b/app/src/main/res/drawable-xhdpi/media_stop_light.png
index edf13ccf..edf13ccf 100644
--- a/res/drawable-xhdpi/media_stop_light.png
+++ b/app/src/main/res/drawable-xhdpi/media_stop_light.png
Binary files differ
diff --git a/res/drawable-xhdpi/notification_close.png b/app/src/main/res/drawable-xhdpi/notification_close.png
index 4230842e..4230842e 100644
--- a/res/drawable-xhdpi/notification_close.png
+++ b/app/src/main/res/drawable-xhdpi/notification_close.png
Binary files differ
diff --git a/res/drawable-xhdpi/notification_next.png b/app/src/main/res/drawable-xhdpi/notification_next.png
index 44dbbd12..44dbbd12 100644
--- a/res/drawable-xhdpi/notification_next.png
+++ b/app/src/main/res/drawable-xhdpi/notification_next.png
Binary files differ
diff --git a/res/drawable-xhdpi/notification_pause.png b/app/src/main/res/drawable-xhdpi/notification_pause.png
index e8d8c535..e8d8c535 100644
--- a/res/drawable-xhdpi/notification_pause.png
+++ b/app/src/main/res/drawable-xhdpi/notification_pause.png
Binary files differ
diff --git a/res/drawable-xhdpi/notification_play.png b/app/src/main/res/drawable-xhdpi/notification_play.png
index 532041fa..532041fa 100644
--- a/res/drawable-xhdpi/notification_play.png
+++ b/app/src/main/res/drawable-xhdpi/notification_play.png
Binary files differ
diff --git a/res/drawable-xhdpi/notification_previous.png b/app/src/main/res/drawable-xhdpi/notification_previous.png
index 87ee8d2f..87ee8d2f 100644
--- a/res/drawable-xhdpi/notification_previous.png
+++ b/app/src/main/res/drawable-xhdpi/notification_previous.png
Binary files differ
diff --git a/res/drawable-xhdpi/now_playing.png b/app/src/main/res/drawable-xhdpi/now_playing.png
index 532041fa..532041fa 100644
--- a/res/drawable-xhdpi/now_playing.png
+++ b/app/src/main/res/drawable-xhdpi/now_playing.png
Binary files differ
diff --git a/res/drawable-xhdpi/stat_notify_download.png b/app/src/main/res/drawable-xhdpi/stat_notify_download.png
index bd4cb567..bd4cb567 100644
--- a/res/drawable-xhdpi/stat_notify_download.png
+++ b/app/src/main/res/drawable-xhdpi/stat_notify_download.png
Binary files differ
diff --git a/res/drawable-xhdpi/stat_notify_playing.png b/app/src/main/res/drawable-xhdpi/stat_notify_playing.png
index 532041fa..532041fa 100644
--- a/res/drawable-xhdpi/stat_notify_playing.png
+++ b/app/src/main/res/drawable-xhdpi/stat_notify_playing.png
Binary files differ
diff --git a/res/drawable-xhdpi/stat_notify_sync.png b/app/src/main/res/drawable-xhdpi/stat_notify_sync.png
index 6da882a2..6da882a2 100644
--- a/res/drawable-xhdpi/stat_notify_sync.png
+++ b/app/src/main/res/drawable-xhdpi/stat_notify_sync.png
Binary files differ
diff --git a/res/drawable-xxhdpi-v11/notification_close.png b/app/src/main/res/drawable-xxhdpi-v11/notification_close.png
index c3ac026a..c3ac026a 100644
--- a/res/drawable-xxhdpi-v11/notification_close.png
+++ b/app/src/main/res/drawable-xxhdpi-v11/notification_close.png
Binary files differ
diff --git a/res/drawable-xxhdpi-v11/notification_next.png b/app/src/main/res/drawable-xxhdpi-v11/notification_next.png
index 06911082..06911082 100644
--- a/res/drawable-xxhdpi-v11/notification_next.png
+++ b/app/src/main/res/drawable-xxhdpi-v11/notification_next.png
Binary files differ
diff --git a/res/drawable-xxhdpi-v11/notification_pause.png b/app/src/main/res/drawable-xxhdpi-v11/notification_pause.png
index 1513f9d9..1513f9d9 100644
--- a/res/drawable-xxhdpi-v11/notification_pause.png
+++ b/app/src/main/res/drawable-xxhdpi-v11/notification_pause.png
Binary files differ
diff --git a/res/drawable-xxhdpi-v11/notification_play.png b/app/src/main/res/drawable-xxhdpi-v11/notification_play.png
index 9138a760..9138a760 100644
--- a/res/drawable-xxhdpi-v11/notification_play.png
+++ b/app/src/main/res/drawable-xxhdpi-v11/notification_play.png
Binary files differ
diff --git a/res/drawable-xxhdpi-v11/notification_previous.png b/app/src/main/res/drawable-xxhdpi-v11/notification_previous.png
index b4456c16..b4456c16 100644
--- a/res/drawable-xxhdpi-v11/notification_previous.png
+++ b/app/src/main/res/drawable-xxhdpi-v11/notification_previous.png
Binary files differ
diff --git a/res/drawable-xxhdpi-v11/stat_notify_download.png b/app/src/main/res/drawable-xxhdpi-v11/stat_notify_download.png
index b2dc5651..b2dc5651 100644
--- a/res/drawable-xxhdpi-v11/stat_notify_download.png
+++ b/app/src/main/res/drawable-xxhdpi-v11/stat_notify_download.png
Binary files differ
diff --git a/res/drawable-xxhdpi-v11/stat_notify_playing.png b/app/src/main/res/drawable-xxhdpi-v11/stat_notify_playing.png
index 9138a760..9138a760 100644
--- a/res/drawable-xxhdpi-v11/stat_notify_playing.png
+++ b/app/src/main/res/drawable-xxhdpi-v11/stat_notify_playing.png
Binary files differ
diff --git a/res/drawable-xxhdpi-v11/stat_notify_sync.png b/app/src/main/res/drawable-xxhdpi-v11/stat_notify_sync.png
index 61f6a331..61f6a331 100644
--- a/res/drawable-xxhdpi-v11/stat_notify_sync.png
+++ b/app/src/main/res/drawable-xxhdpi-v11/stat_notify_sync.png
Binary files differ
diff --git a/res/drawable-xxhdpi/action_toggle_list_dark.png b/app/src/main/res/drawable-xxhdpi/action_toggle_list_dark.png
index 598fc312..598fc312 100644
--- a/res/drawable-xxhdpi/action_toggle_list_dark.png
+++ b/app/src/main/res/drawable-xxhdpi/action_toggle_list_dark.png
Binary files differ
diff --git a/res/drawable-xxhdpi/action_toggle_list_light.png b/app/src/main/res/drawable-xxhdpi/action_toggle_list_light.png
index ceb3fade..ceb3fade 100644
--- a/res/drawable-xxhdpi/action_toggle_list_light.png
+++ b/app/src/main/res/drawable-xxhdpi/action_toggle_list_light.png
Binary files differ
diff --git a/res/drawable-xxhdpi/download_cached.png b/app/src/main/res/drawable-xxhdpi/download_cached.png
index 243c570e..243c570e 100644
--- a/res/drawable-xxhdpi/download_cached.png
+++ b/app/src/main/res/drawable-xxhdpi/download_cached.png
Binary files differ
diff --git a/res/drawable-xxhdpi/download_none_dark.png b/app/src/main/res/drawable-xxhdpi/download_none_dark.png
index a0cb8a41..a0cb8a41 100644
--- a/res/drawable-xxhdpi/download_none_dark.png
+++ b/app/src/main/res/drawable-xxhdpi/download_none_dark.png
Binary files differ
diff --git a/res/drawable-xxhdpi/download_none_light.png b/app/src/main/res/drawable-xxhdpi/download_none_light.png
index 7a1639ef..7a1639ef 100644
--- a/res/drawable-xxhdpi/download_none_light.png
+++ b/app/src/main/res/drawable-xxhdpi/download_none_light.png
Binary files differ
diff --git a/res/drawable-xxhdpi/download_pinned.png b/app/src/main/res/drawable-xxhdpi/download_pinned.png
index 354ac23d..354ac23d 100644
--- a/res/drawable-xxhdpi/download_pinned.png
+++ b/app/src/main/res/drawable-xxhdpi/download_pinned.png
Binary files differ
diff --git a/res/drawable-xxhdpi/downloading_dark.png b/app/src/main/res/drawable-xxhdpi/downloading_dark.png
index afc4bf84..afc4bf84 100644
--- a/res/drawable-xxhdpi/downloading_dark.png
+++ b/app/src/main/res/drawable-xxhdpi/downloading_dark.png
Binary files differ
diff --git a/res/drawable-xxhdpi/downloading_light.png b/app/src/main/res/drawable-xxhdpi/downloading_light.png
index ba31a979..ba31a979 100644
--- a/res/drawable-xxhdpi/downloading_light.png
+++ b/app/src/main/res/drawable-xxhdpi/downloading_light.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_action_add_dark.png b/app/src/main/res/drawable-xxhdpi/ic_action_add_dark.png
index 70495672..70495672 100644
--- a/res/drawable-xxhdpi/ic_action_add_dark.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_action_add_dark.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_action_add_light.png b/app/src/main/res/drawable-xxhdpi/ic_action_add_light.png
index 9322b136..9322b136 100644
--- a/res/drawable-xxhdpi/ic_action_add_light.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_action_add_light.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_action_rating_bad_dark.png b/app/src/main/res/drawable-xxhdpi/ic_action_rating_bad_dark.png
index d784b239..d784b239 100644
--- a/res/drawable-xxhdpi/ic_action_rating_bad_dark.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_action_rating_bad_dark.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_action_rating_bad_light.png b/app/src/main/res/drawable-xxhdpi/ic_action_rating_bad_light.png
index a1484d25..a1484d25 100644
--- a/res/drawable-xxhdpi/ic_action_rating_bad_light.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_action_rating_bad_light.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_action_rating_bad_selected.png b/app/src/main/res/drawable-xxhdpi/ic_action_rating_bad_selected.png
index 13218a08..13218a08 100644
--- a/res/drawable-xxhdpi/ic_action_rating_bad_selected.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_action_rating_bad_selected.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_action_rating_good_dark.png b/app/src/main/res/drawable-xxhdpi/ic_action_rating_good_dark.png
index a332a632..a332a632 100644
--- a/res/drawable-xxhdpi/ic_action_rating_good_dark.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_action_rating_good_dark.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_action_rating_good_light.png b/app/src/main/res/drawable-xxhdpi/ic_action_rating_good_light.png
index 2ef75765..2ef75765 100644
--- a/res/drawable-xxhdpi/ic_action_rating_good_light.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_action_rating_good_light.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_action_rating_good_selected.png b/app/src/main/res/drawable-xxhdpi/ic_action_rating_good_selected.png
index bb444806..bb444806 100644
--- a/res/drawable-xxhdpi/ic_action_rating_good_selected.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_action_rating_good_selected.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_action_volume_dark.png b/app/src/main/res/drawable-xxhdpi/ic_action_volume_dark.png
index 7991a65d..7991a65d 100644
--- a/res/drawable-xxhdpi/ic_action_volume_dark.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_action_volume_dark.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_action_volume_light.png b/app/src/main/res/drawable-xxhdpi/ic_action_volume_light.png
index 8dfbf3f5..8dfbf3f5 100644
--- a/res/drawable-xxhdpi/ic_action_volume_light.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_action_volume_light.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_add_person_dark.png b/app/src/main/res/drawable-xxhdpi/ic_menu_add_person_dark.png
index 446985ea..446985ea 100644
--- a/res/drawable-xxhdpi/ic_menu_add_person_dark.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_menu_add_person_dark.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_add_person_light.png b/app/src/main/res/drawable-xxhdpi/ic_menu_add_person_light.png
index 0f1d36bc..0f1d36bc 100644
--- a/res/drawable-xxhdpi/ic_menu_add_person_light.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_menu_add_person_light.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_admin_dark.png b/app/src/main/res/drawable-xxhdpi/ic_menu_admin_dark.png
index 0e57c9ed..0e57c9ed 100644
--- a/res/drawable-xxhdpi/ic_menu_admin_dark.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_menu_admin_dark.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_admin_light.png b/app/src/main/res/drawable-xxhdpi/ic_menu_admin_light.png
index 63ab2f83..63ab2f83 100644
--- a/res/drawable-xxhdpi/ic_menu_admin_light.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_menu_admin_light.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_bookmark_dark.png b/app/src/main/res/drawable-xxhdpi/ic_menu_bookmark_dark.png
index 2523e14c..2523e14c 100644
--- a/res/drawable-xxhdpi/ic_menu_bookmark_dark.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_menu_bookmark_dark.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_bookmark_light.png b/app/src/main/res/drawable-xxhdpi/ic_menu_bookmark_light.png
index 9e8c4591..9e8c4591 100644
--- a/res/drawable-xxhdpi/ic_menu_bookmark_light.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_menu_bookmark_light.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_bookmark_selected.png b/app/src/main/res/drawable-xxhdpi/ic_menu_bookmark_selected.png
index a1890fbb..a1890fbb 100644
--- a/res/drawable-xxhdpi/ic_menu_bookmark_selected.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_menu_bookmark_selected.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_chat_dark.png b/app/src/main/res/drawable-xxhdpi/ic_menu_chat_dark.png
index 60efb47d..60efb47d 100644
--- a/res/drawable-xxhdpi/ic_menu_chat_dark.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_menu_chat_dark.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_chat_light.png b/app/src/main/res/drawable-xxhdpi/ic_menu_chat_light.png
index 02c89560..02c89560 100644
--- a/res/drawable-xxhdpi/ic_menu_chat_light.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_menu_chat_light.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_chat_send_dark.png b/app/src/main/res/drawable-xxhdpi/ic_menu_chat_send_dark.png
index b86ca3d3..b86ca3d3 100644
--- a/res/drawable-xxhdpi/ic_menu_chat_send_dark.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_menu_chat_send_dark.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_chat_send_light.png b/app/src/main/res/drawable-xxhdpi/ic_menu_chat_send_light.png
index 048b8aac..048b8aac 100644
--- a/res/drawable-xxhdpi/ic_menu_chat_send_light.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_menu_chat_send_light.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_download_dark.png b/app/src/main/res/drawable-xxhdpi/ic_menu_download_dark.png
index 03ddef9f..03ddef9f 100644
--- a/res/drawable-xxhdpi/ic_menu_download_dark.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_menu_download_dark.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_download_light.png b/app/src/main/res/drawable-xxhdpi/ic_menu_download_light.png
index c487580c..c487580c 100644
--- a/res/drawable-xxhdpi/ic_menu_download_light.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_menu_download_light.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_library_dark.png b/app/src/main/res/drawable-xxhdpi/ic_menu_library_dark.png
index 02a4f3f2..02a4f3f2 100644
--- a/res/drawable-xxhdpi/ic_menu_library_dark.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_menu_library_dark.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_library_light.png b/app/src/main/res/drawable-xxhdpi/ic_menu_library_light.png
index 52ce8203..52ce8203 100644
--- a/res/drawable-xxhdpi/ic_menu_library_light.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_menu_library_light.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_password_dark.png b/app/src/main/res/drawable-xxhdpi/ic_menu_password_dark.png
index a7cd1a6d..a7cd1a6d 100644
--- a/res/drawable-xxhdpi/ic_menu_password_dark.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_menu_password_dark.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_password_light.png b/app/src/main/res/drawable-xxhdpi/ic_menu_password_light.png
index 5670a209..5670a209 100644
--- a/res/drawable-xxhdpi/ic_menu_password_light.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_menu_password_light.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_playlist_dark.png b/app/src/main/res/drawable-xxhdpi/ic_menu_playlist_dark.png
index 2c955eee..2c955eee 100644
--- a/res/drawable-xxhdpi/ic_menu_playlist_dark.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_menu_playlist_dark.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_playlist_light.png b/app/src/main/res/drawable-xxhdpi/ic_menu_playlist_light.png
index d1877328..d1877328 100644
--- a/res/drawable-xxhdpi/ic_menu_playlist_light.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_menu_playlist_light.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_podcast_dark.png b/app/src/main/res/drawable-xxhdpi/ic_menu_podcast_dark.png
index a748dc60..a748dc60 100644
--- a/res/drawable-xxhdpi/ic_menu_podcast_dark.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_menu_podcast_dark.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_podcast_light.png b/app/src/main/res/drawable-xxhdpi/ic_menu_podcast_light.png
index efa7b037..efa7b037 100644
--- a/res/drawable-xxhdpi/ic_menu_podcast_light.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_menu_podcast_light.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_radio_dark.png b/app/src/main/res/drawable-xxhdpi/ic_menu_radio_dark.png
index 0c63afbe..0c63afbe 100644
--- a/res/drawable-xxhdpi/ic_menu_radio_dark.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_menu_radio_dark.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_radio_light.png b/app/src/main/res/drawable-xxhdpi/ic_menu_radio_light.png
index 133772f8..133772f8 100644
--- a/res/drawable-xxhdpi/ic_menu_radio_light.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_menu_radio_light.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_refresh_dark.png b/app/src/main/res/drawable-xxhdpi/ic_menu_refresh_dark.png
index 0e5616bd..0e5616bd 100644
--- a/res/drawable-xxhdpi/ic_menu_refresh_dark.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_menu_refresh_dark.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_refresh_light.png b/app/src/main/res/drawable-xxhdpi/ic_menu_refresh_light.png
index 7dea70df..7dea70df 100644
--- a/res/drawable-xxhdpi/ic_menu_refresh_light.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_menu_refresh_light.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_remove_dark.png b/app/src/main/res/drawable-xxhdpi/ic_menu_remove_dark.png
index d5952ea0..d5952ea0 100644
--- a/res/drawable-xxhdpi/ic_menu_remove_dark.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_menu_remove_dark.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_remove_light.png b/app/src/main/res/drawable-xxhdpi/ic_menu_remove_light.png
index c814869e..c814869e 100644
--- a/res/drawable-xxhdpi/ic_menu_remove_light.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_menu_remove_light.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_save_dark.png b/app/src/main/res/drawable-xxhdpi/ic_menu_save_dark.png
index acb264ec..acb264ec 100644
--- a/res/drawable-xxhdpi/ic_menu_save_dark.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_menu_save_dark.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_save_light.png b/app/src/main/res/drawable-xxhdpi/ic_menu_save_light.png
index fcd18ccd..fcd18ccd 100644
--- a/res/drawable-xxhdpi/ic_menu_save_light.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_menu_save_light.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_search_dark.png b/app/src/main/res/drawable-xxhdpi/ic_menu_search_dark.png
index 500ac03a..500ac03a 100644
--- a/res/drawable-xxhdpi/ic_menu_search_dark.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_menu_search_dark.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_search_light.png b/app/src/main/res/drawable-xxhdpi/ic_menu_search_light.png
index fa64f9e8..fa64f9e8 100644
--- a/res/drawable-xxhdpi/ic_menu_search_light.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_menu_search_light.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_settings_dark.png b/app/src/main/res/drawable-xxhdpi/ic_menu_settings_dark.png
index ded5dbb5..ded5dbb5 100644
--- a/res/drawable-xxhdpi/ic_menu_settings_dark.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_menu_settings_dark.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_settings_light.png b/app/src/main/res/drawable-xxhdpi/ic_menu_settings_light.png
index cd242306..cd242306 100644
--- a/res/drawable-xxhdpi/ic_menu_settings_light.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_menu_settings_light.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_share_dark.png b/app/src/main/res/drawable-xxhdpi/ic_menu_share_dark.png
index 1fa12609..1fa12609 100644
--- a/res/drawable-xxhdpi/ic_menu_share_dark.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_menu_share_dark.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_share_light.png b/app/src/main/res/drawable-xxhdpi/ic_menu_share_light.png
index 7511340b..7511340b 100644
--- a/res/drawable-xxhdpi/ic_menu_share_light.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_menu_share_light.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_shuffle_dark.png b/app/src/main/res/drawable-xxhdpi/ic_menu_shuffle_dark.png
index b53733df..b53733df 100644
--- a/res/drawable-xxhdpi/ic_menu_shuffle_dark.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_menu_shuffle_dark.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_shuffle_light.png b/app/src/main/res/drawable-xxhdpi/ic_menu_shuffle_light.png
index 4d5dff32..4d5dff32 100644
--- a/res/drawable-xxhdpi/ic_menu_shuffle_light.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_menu_shuffle_light.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_number_border.png b/app/src/main/res/drawable-xxhdpi/ic_number_border.png
index caf4ca23..caf4ca23 100644
--- a/res/drawable-xxhdpi/ic_number_border.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_number_border.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_social_person.png b/app/src/main/res/drawable-xxhdpi/ic_social_person.png
index f81dc6a4..f81dc6a4 100644
--- a/res/drawable-xxhdpi/ic_social_person.png
+++ b/app/src/main/res/drawable-xxhdpi/ic_social_person.png
Binary files differ
diff --git a/res/drawable-xxhdpi/launch.png b/app/src/main/res/drawable-xxhdpi/launch.png
index cae4f99a..cae4f99a 100644
--- a/res/drawable-xxhdpi/launch.png
+++ b/app/src/main/res/drawable-xxhdpi/launch.png
Binary files differ
diff --git a/res/drawable-xxhdpi/main_offline_dark.png b/app/src/main/res/drawable-xxhdpi/main_offline_dark.png
index c415e0a5..c415e0a5 100644
--- a/res/drawable-xxhdpi/main_offline_dark.png
+++ b/app/src/main/res/drawable-xxhdpi/main_offline_dark.png
Binary files differ
diff --git a/res/drawable-xxhdpi/main_offline_light.png b/app/src/main/res/drawable-xxhdpi/main_offline_light.png
index b7e1c380..b7e1c380 100644
--- a/res/drawable-xxhdpi/main_offline_light.png
+++ b/app/src/main/res/drawable-xxhdpi/main_offline_light.png
Binary files differ
diff --git a/res/drawable-xxhdpi/main_select_server_dark.png b/app/src/main/res/drawable-xxhdpi/main_select_server_dark.png
index b85e3a1e..b85e3a1e 100644
--- a/res/drawable-xxhdpi/main_select_server_dark.png
+++ b/app/src/main/res/drawable-xxhdpi/main_select_server_dark.png
Binary files differ
diff --git a/res/drawable-xxhdpi/main_select_server_light.png b/app/src/main/res/drawable-xxhdpi/main_select_server_light.png
index 8fc39eff..8fc39eff 100644
--- a/res/drawable-xxhdpi/main_select_server_light.png
+++ b/app/src/main/res/drawable-xxhdpi/main_select_server_light.png
Binary files differ
diff --git a/res/drawable-xxhdpi/media_backward_dark.png b/app/src/main/res/drawable-xxhdpi/media_backward_dark.png
index 5b6c6148..5b6c6148 100644
--- a/res/drawable-xxhdpi/media_backward_dark.png
+++ b/app/src/main/res/drawable-xxhdpi/media_backward_dark.png
Binary files differ
diff --git a/res/drawable-xxhdpi/media_backward_light.png b/app/src/main/res/drawable-xxhdpi/media_backward_light.png
index 32f7d3bc..32f7d3bc 100644
--- a/res/drawable-xxhdpi/media_backward_light.png
+++ b/app/src/main/res/drawable-xxhdpi/media_backward_light.png
Binary files differ
diff --git a/res/drawable-xxhdpi/media_forward_dark.png b/app/src/main/res/drawable-xxhdpi/media_forward_dark.png
index ca4ee295..ca4ee295 100644
--- a/res/drawable-xxhdpi/media_forward_dark.png
+++ b/app/src/main/res/drawable-xxhdpi/media_forward_dark.png
Binary files differ
diff --git a/res/drawable-xxhdpi/media_forward_light.png b/app/src/main/res/drawable-xxhdpi/media_forward_light.png
index 208e46e9..208e46e9 100644
--- a/res/drawable-xxhdpi/media_forward_light.png
+++ b/app/src/main/res/drawable-xxhdpi/media_forward_light.png
Binary files differ
diff --git a/res/drawable-xxhdpi/media_pause_dark.png b/app/src/main/res/drawable-xxhdpi/media_pause_dark.png
index 4b5aacbc..4b5aacbc 100644
--- a/res/drawable-xxhdpi/media_pause_dark.png
+++ b/app/src/main/res/drawable-xxhdpi/media_pause_dark.png
Binary files differ
diff --git a/res/drawable-xxhdpi/media_pause_light.png b/app/src/main/res/drawable-xxhdpi/media_pause_light.png
index 111f6d00..111f6d00 100644
--- a/res/drawable-xxhdpi/media_pause_light.png
+++ b/app/src/main/res/drawable-xxhdpi/media_pause_light.png
Binary files differ
diff --git a/res/drawable-xxhdpi/media_start_dark.png b/app/src/main/res/drawable-xxhdpi/media_start_dark.png
index 641ad544..641ad544 100644
--- a/res/drawable-xxhdpi/media_start_dark.png
+++ b/app/src/main/res/drawable-xxhdpi/media_start_dark.png
Binary files differ
diff --git a/res/drawable-xxhdpi/media_start_light.png b/app/src/main/res/drawable-xxhdpi/media_start_light.png
index a6286203..a6286203 100644
--- a/res/drawable-xxhdpi/media_start_light.png
+++ b/app/src/main/res/drawable-xxhdpi/media_start_light.png
Binary files differ
diff --git a/res/drawable-xxhdpi/media_stop_dark.png b/app/src/main/res/drawable-xxhdpi/media_stop_dark.png
index 9a9c432a..9a9c432a 100644
--- a/res/drawable-xxhdpi/media_stop_dark.png
+++ b/app/src/main/res/drawable-xxhdpi/media_stop_dark.png
Binary files differ
diff --git a/res/drawable-xxhdpi/media_stop_light.png b/app/src/main/res/drawable-xxhdpi/media_stop_light.png
index 79eb8d95..79eb8d95 100644
--- a/res/drawable-xxhdpi/media_stop_light.png
+++ b/app/src/main/res/drawable-xxhdpi/media_stop_light.png
Binary files differ
diff --git a/res/drawable-xxhdpi/notification_close.png b/app/src/main/res/drawable-xxhdpi/notification_close.png
index 022a6780..022a6780 100644
--- a/res/drawable-xxhdpi/notification_close.png
+++ b/app/src/main/res/drawable-xxhdpi/notification_close.png
Binary files differ
diff --git a/res/drawable-xxhdpi/notification_next.png b/app/src/main/res/drawable-xxhdpi/notification_next.png
index dfe129db..dfe129db 100644
--- a/res/drawable-xxhdpi/notification_next.png
+++ b/app/src/main/res/drawable-xxhdpi/notification_next.png
Binary files differ
diff --git a/res/drawable-xxhdpi/notification_pause.png b/app/src/main/res/drawable-xxhdpi/notification_pause.png
index 9c952207..9c952207 100644
--- a/res/drawable-xxhdpi/notification_pause.png
+++ b/app/src/main/res/drawable-xxhdpi/notification_pause.png
Binary files differ
diff --git a/res/drawable-xxhdpi/notification_play.png b/app/src/main/res/drawable-xxhdpi/notification_play.png
index 4ee0a5eb..4ee0a5eb 100644
--- a/res/drawable-xxhdpi/notification_play.png
+++ b/app/src/main/res/drawable-xxhdpi/notification_play.png
Binary files differ
diff --git a/res/drawable-xxhdpi/notification_previous.png b/app/src/main/res/drawable-xxhdpi/notification_previous.png
index e6908126..e6908126 100644
--- a/res/drawable-xxhdpi/notification_previous.png
+++ b/app/src/main/res/drawable-xxhdpi/notification_previous.png
Binary files differ
diff --git a/res/drawable-xxhdpi/now_playing.png b/app/src/main/res/drawable-xxhdpi/now_playing.png
index 4ee0a5eb..4ee0a5eb 100644
--- a/res/drawable-xxhdpi/now_playing.png
+++ b/app/src/main/res/drawable-xxhdpi/now_playing.png
Binary files differ
diff --git a/res/drawable-xxhdpi/stat_notify_download.png b/app/src/main/res/drawable-xxhdpi/stat_notify_download.png
index 9d9a7f3e..9d9a7f3e 100644
--- a/res/drawable-xxhdpi/stat_notify_download.png
+++ b/app/src/main/res/drawable-xxhdpi/stat_notify_download.png
Binary files differ
diff --git a/res/drawable-xxhdpi/stat_notify_playing.png b/app/src/main/res/drawable-xxhdpi/stat_notify_playing.png
index 4ee0a5eb..4ee0a5eb 100644
--- a/res/drawable-xxhdpi/stat_notify_playing.png
+++ b/app/src/main/res/drawable-xxhdpi/stat_notify_playing.png
Binary files differ
diff --git a/res/drawable-xxhdpi/stat_notify_sync.png b/app/src/main/res/drawable-xxhdpi/stat_notify_sync.png
index 89fe6525..89fe6525 100644
--- a/res/drawable-xxhdpi/stat_notify_sync.png
+++ b/app/src/main/res/drawable-xxhdpi/stat_notify_sync.png
Binary files differ
diff --git a/res/drawable/appwidget4x1_preview.png b/app/src/main/res/drawable/appwidget4x1_preview.png
index eb1fabc6..eb1fabc6 100644
--- a/res/drawable/appwidget4x1_preview.png
+++ b/app/src/main/res/drawable/appwidget4x1_preview.png
Binary files differ
diff --git a/res/drawable/appwidget4x2_preview.png b/app/src/main/res/drawable/appwidget4x2_preview.png
index 704dc09e..704dc09e 100644
--- a/res/drawable/appwidget4x2_preview.png
+++ b/app/src/main/res/drawable/appwidget4x2_preview.png
Binary files differ
diff --git a/res/drawable/appwidget4x3_preview.png b/app/src/main/res/drawable/appwidget4x3_preview.png
index 3437b857..3437b857 100644
--- a/res/drawable/appwidget4x3_preview.png
+++ b/app/src/main/res/drawable/appwidget4x3_preview.png
Binary files differ
diff --git a/res/drawable/appwidget4x4_preview.png b/app/src/main/res/drawable/appwidget4x4_preview.png
index ebc2b7a1..ebc2b7a1 100644
--- a/res/drawable/appwidget4x4_preview.png
+++ b/app/src/main/res/drawable/appwidget4x4_preview.png
Binary files differ
diff --git a/res/layout-land/download.xml b/app/src/main/res/layout-land/download.xml
index c0108597..f3e39a5f 100644
--- a/res/layout-land/download.xml
+++ b/app/src/main/res/layout-land/download.xml
@@ -1,129 +1,129 @@
-<?xml version="1.0" encoding="utf-8"?>
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/download_layout_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/download_layout"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
-
- <LinearLayout android:orientation="horizontal"
- android:layout_width="fill_parent"
- android:layout_height="0dip"
- android:layout_weight="1">
-
- <github.daneren2005.dsub.view.MyViewFlipper
- android:id="@+id/download_playlist_flipper"
- android:layout_width="0dp"
- android:layout_height="fill_parent"
- android:layout_weight="1">
-
- <github.daneren2005.dsub.view.RecyclingImageView
- android:id="@+id/download_album_art_image"
- android:src="@drawable/unknown_album_large"
- android:layout_width="wrap_content"
- android:layout_height="fill_parent"
- android:layout_weight="1"
- android:scaleType="fitStart"/>
-
- <include layout="@layout/download_playlist"/>
-
- </github.daneren2005.dsub.view.MyViewFlipper>
-
- <RelativeLayout android:orientation="vertical"
- android:id="@+id/download_control_layout"
- android:layout_width="0dp"
- android:layout_height="fill_parent"
- android:layout_weight="1"
- android:background="@android:color/transparent">
-
- <LinearLayout
- android:id="@+id/download_other_controls_wrapper"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerHorizontal="true"
- android:layout_above="@+id/download_song_title">
-
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/download_other_controls_layout"
- android:orientation="horizontal"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal">
-
- <ImageButton
- android:id="@+id/download_rating_bad"
- style="@style/DownloadActionImageButton"
- android:src="?attr/rating_bad"/>
-
- <ImageButton
- android:id="@+id/download_star"
- style="@style/DownloadActionImageButton"
- android:src="@android:drawable/star_big_off"/>
-
- <ImageButton
- android:id="@+id/download_bookmark"
- style="@style/DownloadActionImageButton"
- android:src="?attr/bookmark"/>
-
- <ImageButton
- android:id="@+id/download_rating_good"
- style="@style/DownloadActionImageButton"
- android:src="?attr/rating_good"/>
- </LinearLayout>
- </LinearLayout>
-
- <TextView
- android:id="@+id/download_song_title"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_centerHorizontal="true"
- android:layout_marginLeft="12dip"
- android:layout_marginRight="12dip"
- android:singleLine="true"
- android:ellipsize="end"
- android:gravity="center_horizontal"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textColor="?android:textColorPrimary"
- android:layout_above="@+id/download_status"/>
-
- <TextView
- android:id="@+id/download_status"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:gravity="center_horizontal"
- android:layout_marginBottom="8dip"
- android:layout_marginLeft="12dip"
- android:layout_marginRight="12dip"
- android:singleLine="true"
- android:ellipsize="end"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?android:textColorSecondary"
- android:layout_above="@+id/download_media_buttons_wrapper"/>
-
- <LinearLayout
- android:id="@+id/download_media_buttons_wrapper"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_above="@+id/download_slider_wrapper">
-
- <include layout="@layout/download_media_buttons"/>
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/download_slider_wrapper"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true">
-
- <include layout="@layout/download_slider"/>
- </LinearLayout>
-
- </RelativeLayout>
-
- </LinearLayout>
- </LinearLayout>
-</FrameLayout>
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/download_layout_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/download_layout"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <LinearLayout android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1">
+
+ <github.daneren2005.dsub.view.MyViewFlipper
+ android:id="@+id/download_playlist_flipper"
+ android:layout_width="0dp"
+ android:layout_height="fill_parent"
+ android:layout_weight="1">
+
+ <github.daneren2005.dsub.view.RecyclingImageView
+ android:id="@+id/download_album_art_image"
+ android:src="@drawable/unknown_album_large"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_weight="1"
+ android:scaleType="fitStart"/>
+
+ <include layout="@layout/download_playlist"/>
+
+ </github.daneren2005.dsub.view.MyViewFlipper>
+
+ <RelativeLayout android:orientation="vertical"
+ android:id="@+id/download_control_layout"
+ android:layout_width="0dp"
+ android:layout_height="fill_parent"
+ android:layout_weight="1"
+ android:background="@android:color/transparent">
+
+ <LinearLayout
+ android:id="@+id/download_other_controls_wrapper"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:layout_above="@+id/download_song_title">
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/download_other_controls_layout"
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal">
+
+ <ImageButton
+ android:id="@+id/download_rating_bad"
+ style="@style/DownloadActionImageButton"
+ android:src="?attr/rating_bad"/>
+
+ <ImageButton
+ android:id="@+id/download_star"
+ style="@style/DownloadActionImageButton"
+ android:src="@android:drawable/star_big_off"/>
+
+ <ImageButton
+ android:id="@+id/download_bookmark"
+ style="@style/DownloadActionImageButton"
+ android:src="?attr/bookmark"/>
+
+ <ImageButton
+ android:id="@+id/download_rating_good"
+ style="@style/DownloadActionImageButton"
+ android:src="?attr/rating_good"/>
+ </LinearLayout>
+ </LinearLayout>
+
+ <TextView
+ android:id="@+id/download_song_title"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:layout_marginLeft="12dip"
+ android:layout_marginRight="12dip"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:gravity="center_horizontal"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?android:textColorPrimary"
+ android:layout_above="@+id/download_status"/>
+
+ <TextView
+ android:id="@+id/download_status"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:layout_marginBottom="8dip"
+ android:layout_marginLeft="12dip"
+ android:layout_marginRight="12dip"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:textColorSecondary"
+ android:layout_above="@+id/download_media_buttons_wrapper"/>
+
+ <LinearLayout
+ android:id="@+id/download_media_buttons_wrapper"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_above="@+id/download_slider_wrapper">
+
+ <include layout="@layout/download_media_buttons"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/download_slider_wrapper"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true">
+
+ <include layout="@layout/download_slider"/>
+ </LinearLayout>
+
+ </RelativeLayout>
+
+ </LinearLayout>
+ </LinearLayout>
+</FrameLayout>
diff --git a/res/layout-large-land/abstract_fragment_container.xml b/app/src/main/res/layout-large-land/abstract_fragment_container.xml
index 511d63aa..5e3b1561 100644
--- a/res/layout-large-land/abstract_fragment_container.xml
+++ b/app/src/main/res/layout-large-land/abstract_fragment_container.xml
@@ -1,21 +1,21 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="horizontal"
- android:layout_width="match_parent"
- android:layout_height="0px"
- android:layout_weight="1">
-
- <FrameLayout
- android:id="@+id/fragment_container"
- android:layout_width="0px"
- android:layout_height="match_parent"
- android:layout_weight="4"/>
-
- <FrameLayout
- android:id="@+id/fragment_second_container"
- android:layout_width="0px"
- android:layout_height="match_parent"
- android:layout_weight="6"
- android:visibility="gone"/>
-
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="0px"
+ android:layout_weight="1">
+
+ <FrameLayout
+ android:id="@+id/fragment_container"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="4"/>
+
+ <FrameLayout
+ android:id="@+id/fragment_second_container"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="6"
+ android:visibility="gone"/>
+
</LinearLayout> \ No newline at end of file
diff --git a/res/layout-large-land/download.xml b/app/src/main/res/layout-large-land/download.xml
index 139685e0..8b252190 100644
--- a/res/layout-large-land/download.xml
+++ b/app/src/main/res/layout-large-land/download.xml
@@ -1,130 +1,130 @@
-<?xml version="1.0" encoding="utf-8"?>
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/download_layout_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/download_layout"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
-
- <LinearLayout android:orientation="horizontal"
- android:layout_width="fill_parent"
- android:layout_height="0dip"
- android:layout_weight="1">
-
- <github.daneren2005.dsub.view.RecyclingImageView
- android:id="@+id/download_album_art_image"
- android:src="@drawable/unknown_album_large"
- android:layout_width="0dp"
- android:layout_height="fill_parent"
- android:layout_weight="1"
- android:scaleType="fitStart"/>
-
- <RelativeLayout android:orientation="vertical"
- android:id="@+id/download_control_layout"
- android:layout_width="0dp"
- android:layout_height="fill_parent"
- android:layout_weight="1"
- android:background="@android:color/transparent">
-
- <github.daneren2005.dsub.view.MyViewFlipper
- android:id="@+id/download_playlist_flipper"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:layout_above="@+id/download_song_title">
-
- <RelativeLayout
- android:id="@+id/download_other_controls_wrapper"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:orientation="vertical">
-
- <LinearLayout
- android:id="@+id/download_other_controls_layout"
- android:orientation="horizontal"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerHorizontal="true"
- android:layout_alignParentBottom="true">
-
- <ImageButton
- android:id="@+id/download_rating_bad"
- style="@style/DownloadActionImageButton"
- android:src="?attr/rating_bad"/>
-
- <ImageButton
- android:id="@+id/download_star"
- style="@style/DownloadActionImageButton"
- android:src="@android:drawable/star_big_off"/>
-
- <ImageButton
- android:id="@+id/download_bookmark"
- style="@style/DownloadActionImageButton"
- android:src="?attr/bookmark"/>
-
- <ImageButton
- android:id="@+id/download_rating_good"
- style="@style/DownloadActionImageButton"
- android:src="?attr/rating_good"/>
- </LinearLayout>
- </RelativeLayout>
-
- <include layout="@layout/download_playlist"/>
-
- </github.daneren2005.dsub.view.MyViewFlipper>
-
- <TextView
- android:id="@+id/download_song_title"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_centerHorizontal="true"
- android:layout_marginLeft="12dip"
- android:layout_marginRight="12dip"
- android:singleLine="true"
- android:ellipsize="end"
- android:gravity="center_horizontal"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textColor="?android:textColorPrimary"
- android:layout_above="@+id/download_status"/>
-
- <TextView
- android:id="@+id/download_status"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:gravity="center_horizontal"
- android:layout_marginBottom="8dip"
- android:layout_marginLeft="12dip"
- android:layout_marginRight="12dip"
- android:singleLine="true"
- android:ellipsize="end"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?android:textColorSecondary"
- android:layout_above="@+id/download_media_buttons_wrapper"/>
-
- <LinearLayout
- android:id="@+id/download_media_buttons_wrapper"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_above="@+id/download_slider_wrapper">
-
- <include layout="@layout/download_media_buttons"/>
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/download_slider_wrapper"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true">
-
- <include layout="@layout/download_slider"/>
- </LinearLayout>
-
- </RelativeLayout>
-
- </LinearLayout>
- </LinearLayout>
-</FrameLayout>
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/download_layout_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/download_layout"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <LinearLayout android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1">
+
+ <github.daneren2005.dsub.view.RecyclingImageView
+ android:id="@+id/download_album_art_image"
+ android:src="@drawable/unknown_album_large"
+ android:layout_width="0dp"
+ android:layout_height="fill_parent"
+ android:layout_weight="1"
+ android:scaleType="fitStart"/>
+
+ <RelativeLayout android:orientation="vertical"
+ android:id="@+id/download_control_layout"
+ android:layout_width="0dp"
+ android:layout_height="fill_parent"
+ android:layout_weight="1"
+ android:background="@android:color/transparent">
+
+ <github.daneren2005.dsub.view.MyViewFlipper
+ android:id="@+id/download_playlist_flipper"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_above="@+id/download_song_title">
+
+ <RelativeLayout
+ android:id="@+id/download_other_controls_wrapper"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:id="@+id/download_other_controls_layout"
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:layout_alignParentBottom="true">
+
+ <ImageButton
+ android:id="@+id/download_rating_bad"
+ style="@style/DownloadActionImageButton"
+ android:src="?attr/rating_bad"/>
+
+ <ImageButton
+ android:id="@+id/download_star"
+ style="@style/DownloadActionImageButton"
+ android:src="@android:drawable/star_big_off"/>
+
+ <ImageButton
+ android:id="@+id/download_bookmark"
+ style="@style/DownloadActionImageButton"
+ android:src="?attr/bookmark"/>
+
+ <ImageButton
+ android:id="@+id/download_rating_good"
+ style="@style/DownloadActionImageButton"
+ android:src="?attr/rating_good"/>
+ </LinearLayout>
+ </RelativeLayout>
+
+ <include layout="@layout/download_playlist"/>
+
+ </github.daneren2005.dsub.view.MyViewFlipper>
+
+ <TextView
+ android:id="@+id/download_song_title"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:layout_marginLeft="12dip"
+ android:layout_marginRight="12dip"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:gravity="center_horizontal"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?android:textColorPrimary"
+ android:layout_above="@+id/download_status"/>
+
+ <TextView
+ android:id="@+id/download_status"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:layout_marginBottom="8dip"
+ android:layout_marginLeft="12dip"
+ android:layout_marginRight="12dip"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:textColorSecondary"
+ android:layout_above="@+id/download_media_buttons_wrapper"/>
+
+ <LinearLayout
+ android:id="@+id/download_media_buttons_wrapper"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_above="@+id/download_slider_wrapper">
+
+ <include layout="@layout/download_media_buttons"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/download_slider_wrapper"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true">
+
+ <include layout="@layout/download_slider"/>
+ </LinearLayout>
+
+ </RelativeLayout>
+
+ </LinearLayout>
+ </LinearLayout>
+</FrameLayout>
diff --git a/res/layout-port/download.xml b/app/src/main/res/layout-port/download.xml
index 207ab260..96e2c864 100644
--- a/res/layout-port/download.xml
+++ b/app/src/main/res/layout-port/download.xml
@@ -1,120 +1,120 @@
-<?xml version="1.0" encoding="utf-8"?>
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/download_layout_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/download_layout"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
-
- <github.daneren2005.dsub.view.MyViewFlipper
- android:id="@+id/download_playlist_flipper"
- android:layout_width="fill_parent"
- android:layout_height="0dip"
- android:layout_weight="1">
-
- <RelativeLayout
- android:id="@+id/download_album_art_layout"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:layout_weight="1"
- android:background="@android:color/transparent">
-
- <FrameLayout android:orientation="vertical"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentTop="true">
-
- <github.daneren2005.dsub.view.RecyclingImageView
- android:id="@+id/download_album_art_image"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:scaleType="fitCenter"
- android:layout_gravity="center_horizontal|top"/>
-
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/download_overlay_buttons"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:background="@color/overlayColor"
- android:layout_gravity="center_horizontal|bottom">
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerHorizontal="true">
-
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/download_other_controls_layout"
- android:orientation="horizontal"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal">
-
- <ImageButton
- android:id="@+id/download_rating_bad"
- style="@style/DownloadActionImageButton"
- android:src="@drawable/ic_action_rating_bad_dark"/>
-
- <ImageButton
- android:id="@+id/download_star"
- style="@style/DownloadActionImageButton"
- android:src="@android:drawable/star_big_off"/>
-
- <ImageButton
- android:id="@+id/download_bookmark"
- style="@style/DownloadActionImageButton"
- android:src="@drawable/ic_menu_bookmark_dark"/>
-
- <ImageButton
- android:id="@+id/download_rating_good"
- style="@style/DownloadActionImageButton"
- android:src="@drawable/ic_action_rating_good_dark"/>
- </LinearLayout>
- </LinearLayout>
- </RelativeLayout>
- </FrameLayout>
-
- <TextView
- android:id="@+id/download_status"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_centerHorizontal="true"
- android:layout_marginLeft="16dip"
- android:layout_marginRight="16dip"
- android:singleLine="true"
- android:ellipsize="end"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?android:textColorSecondary"/>
-
- <TextView
- android:id="@+id/download_song_title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:layout_above="@+id/download_status"
- android:layout_centerHorizontal="true"
- android:layout_marginLeft="16dip"
- android:layout_marginRight="16dip"
- android:singleLine="true"
- android:textColor="?android:textColorPrimary"
- android:textStyle="bold"
- android:textSize="18sp"
- android:ellipsize="end"/>
-
- </RelativeLayout>
-
- <include layout="@layout/download_playlist"/>
-
- </github.daneren2005.dsub.view.MyViewFlipper>
-
- <include layout="@layout/download_media_buttons"/>
-
- <include layout="@layout/download_slider"/>
- </LinearLayout>
-</FrameLayout>
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/download_layout_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/download_layout"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <github.daneren2005.dsub.view.MyViewFlipper
+ android:id="@+id/download_playlist_flipper"
+ android:layout_width="fill_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1">
+
+ <RelativeLayout
+ android:id="@+id/download_album_art_layout"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1"
+ android:background="@android:color/transparent">
+
+ <FrameLayout android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true">
+
+ <github.daneren2005.dsub.view.RecyclingImageView
+ android:id="@+id/download_album_art_image"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:scaleType="fitCenter"
+ android:layout_gravity="center_horizontal|top"/>
+
+ <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/download_overlay_buttons"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:background="@color/overlayColor"
+ android:layout_gravity="center_horizontal|bottom">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true">
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/download_other_controls_layout"
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal">
+
+ <ImageButton
+ android:id="@+id/download_rating_bad"
+ style="@style/DownloadActionImageButton"
+ android:src="@drawable/ic_action_rating_bad_dark"/>
+
+ <ImageButton
+ android:id="@+id/download_star"
+ style="@style/DownloadActionImageButton"
+ android:src="@android:drawable/star_big_off"/>
+
+ <ImageButton
+ android:id="@+id/download_bookmark"
+ style="@style/DownloadActionImageButton"
+ android:src="@drawable/ic_menu_bookmark_dark"/>
+
+ <ImageButton
+ android:id="@+id/download_rating_good"
+ style="@style/DownloadActionImageButton"
+ android:src="@drawable/ic_action_rating_good_dark"/>
+ </LinearLayout>
+ </LinearLayout>
+ </RelativeLayout>
+ </FrameLayout>
+
+ <TextView
+ android:id="@+id/download_status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_centerHorizontal="true"
+ android:layout_marginLeft="16dip"
+ android:layout_marginRight="16dip"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:textColorSecondary"/>
+
+ <TextView
+ android:id="@+id/download_song_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_above="@+id/download_status"
+ android:layout_centerHorizontal="true"
+ android:layout_marginLeft="16dip"
+ android:layout_marginRight="16dip"
+ android:singleLine="true"
+ android:textColor="?android:textColorPrimary"
+ android:textStyle="bold"
+ android:textSize="18sp"
+ android:ellipsize="end"/>
+
+ </RelativeLayout>
+
+ <include layout="@layout/download_playlist"/>
+
+ </github.daneren2005.dsub.view.MyViewFlipper>
+
+ <include layout="@layout/download_media_buttons"/>
+
+ <include layout="@layout/download_slider"/>
+ </LinearLayout>
+</FrameLayout>
diff --git a/res/layout/abstract_activity.xml b/app/src/main/res/layout/abstract_activity.xml
index 784d753c..be65e437 100644
--- a/res/layout/abstract_activity.xml
+++ b/app/src/main/res/layout/abstract_activity.xml
@@ -1,21 +1,21 @@
-<?xml version="1.0" encoding="utf-8"?>
-<android.support.v4.widget.DrawerLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/drawer_layout"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <!-- The main content view -->
- <FrameLayout
- android:id="@+id/content_frame"
- android:layout_width="match_parent"
- android:layout_height="match_parent"/>
- <!-- The navigation drawer -->
- <ListView android:id="@+id/left_drawer"
- android:layout_width="240dp"
- android:layout_height="match_parent"
- android:layout_gravity="start"
- android:choiceMode="singleChoice"
- android:divider="@android:color/transparent"
- android:dividerHeight="0dp"
- android:background="?android:windowBackground"/>
-</android.support.v4.widget.DrawerLayout>
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.v4.widget.DrawerLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/drawer_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <!-- The main content view -->
+ <FrameLayout
+ android:id="@+id/content_frame"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+ <!-- The navigation drawer -->
+ <ListView android:id="@+id/left_drawer"
+ android:layout_width="240dp"
+ android:layout_height="match_parent"
+ android:layout_gravity="start"
+ android:choiceMode="singleChoice"
+ android:divider="@android:color/transparent"
+ android:dividerHeight="0dp"
+ android:background="?android:windowBackground"/>
+</android.support.v4.widget.DrawerLayout>
diff --git a/res/layout/abstract_fragment_activity.xml b/app/src/main/res/layout/abstract_fragment_activity.xml
index fae09e46..d9c99f2f 100644
--- a/res/layout/abstract_fragment_activity.xml
+++ b/app/src/main/res/layout/abstract_fragment_activity.xml
@@ -1,84 +1,84 @@
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:gravity="center_horizontal"
- android:orientation="vertical" >
-
- <include layout="@layout/abstract_fragment_container" />
-
- <View
- android:layout_width="fill_parent"
- android:layout_height="1px"
- android:background="@color/dividerColor"/>
-
- <LinearLayout
- android:id="@+id/bottom_bar"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- style="@style/BasicButton"
- android:orientation="horizontal">
-
- <github.daneren2005.dsub.view.RecyclingImageView
- android:id="@+id/album_art"
- android:layout_width="50dip"
- android:layout_height="50dip"
- android:layout_gravity="left|center"
- android:scaleType="fitStart"/>
-
- <LinearLayout
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_weight="1"
- android:orientation="vertical"
- android:paddingLeft="8dip">
-
- <TextView
- android:id="@+id/track_name"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:textColor="?android:textColorPrimary"
- android:singleLine="true"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textSize="13sp"
- android:text="@string/search.artists"/>
-
- <TextView
- android:id="@+id/artist_name"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:textColor="?android:textColorSecondary"
- android:singleLine="true"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textSize="12sp"
- android:text="@string/search.albums"/>
- </LinearLayout>
-
- <LinearLayout
- android:layout_height="wrap_content"
- android:layout_width="0dp"
- android:layout_weight="1">
-
- <ImageButton
- style="@style/PlaybackControl.Small"
- android:id="@+id/download_previous"
- android:src="?attr/media_button_backward"
- android:layout_width="0dp"
- android:layout_weight="1"/>
-
- <ImageButton
- style="@style/PlaybackControl.Small"
- android:id="@+id/download_start"
- android:src="?attr/media_button_start"
- android:layout_width="0dp"
- android:layout_weight="1"/>
-
- <ImageButton
- style="@style/PlaybackControl.Small"
- android:id="@+id/download_next"
- android:src="?attr/media_button_forward"
- android:layout_width="0dp"
- android:layout_weight="1"/>
- </LinearLayout>
- </LinearLayout>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center_horizontal"
+ android:orientation="vertical" >
+
+ <include layout="@layout/abstract_fragment_container" />
+
+ <View
+ android:layout_width="fill_parent"
+ android:layout_height="1px"
+ android:background="@color/dividerColor"/>
+
+ <LinearLayout
+ android:id="@+id/bottom_bar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ style="@style/BasicButton"
+ android:orientation="horizontal">
+
+ <github.daneren2005.dsub.view.RecyclingImageView
+ android:id="@+id/album_art"
+ android:layout_width="50dip"
+ android:layout_height="50dip"
+ android:layout_gravity="left|center"
+ android:scaleType="fitStart"/>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:paddingLeft="8dip">
+
+ <TextView
+ android:id="@+id/track_name"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textColor="?android:textColorPrimary"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textSize="13sp"
+ android:text="@string/search.artists"/>
+
+ <TextView
+ android:id="@+id/artist_name"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textColor="?android:textColorSecondary"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textSize="12sp"
+ android:text="@string/search.albums"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_height="wrap_content"
+ android:layout_width="0dp"
+ android:layout_weight="1">
+
+ <ImageButton
+ style="@style/PlaybackControl.Small"
+ android:id="@+id/download_previous"
+ android:src="?attr/media_button_backward"
+ android:layout_width="0dp"
+ android:layout_weight="1"/>
+
+ <ImageButton
+ style="@style/PlaybackControl.Small"
+ android:id="@+id/download_start"
+ android:src="?attr/media_button_start"
+ android:layout_width="0dp"
+ android:layout_weight="1"/>
+
+ <ImageButton
+ style="@style/PlaybackControl.Small"
+ android:id="@+id/download_next"
+ android:src="?attr/media_button_forward"
+ android:layout_width="0dp"
+ android:layout_weight="1"/>
+ </LinearLayout>
+ </LinearLayout>
</LinearLayout> \ No newline at end of file
diff --git a/res/layout/abstract_fragment_container.xml b/app/src/main/res/layout/abstract_fragment_container.xml
index d4a8607f..61e17d1d 100644
--- a/res/layout/abstract_fragment_container.xml
+++ b/app/src/main/res/layout/abstract_fragment_container.xml
@@ -1,6 +1,6 @@
-<?xml version="1.0" encoding="utf-8"?>
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/fragment_container"
- android:layout_width="match_parent"
- android:layout_height="0px"
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/fragment_container"
+ android:layout_width="match_parent"
+ android:layout_height="0px"
android:layout_weight="1"/> \ No newline at end of file
diff --git a/res/layout/abstract_list_fragment.xml b/app/src/main/res/layout/abstract_list_fragment.xml
index 8d586105..618a7341 100644
--- a/res/layout/abstract_list_fragment.xml
+++ b/app/src/main/res/layout/abstract_list_fragment.xml
@@ -1,27 +1,27 @@
-<?xml version="1.0" encoding="utf-8"?>
-<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/refresh_layout"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
-
- <LinearLayout
- android:id="@+id/fragment_list_layout"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical" >
-
- <View
- android:layout_width="fill_parent"
- android:layout_height="1px"
- android:background="@color/dividerColor"/>
-
- <ListView
- android:id="@+id/fragment_list"
- android:layout_width="fill_parent"
- android:layout_height="0dip"
- android:layout_weight="1.0"
- android:fastScrollEnabled="true"/>
-
- <include layout="@layout/tab_progress" />
- </LinearLayout>
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/refresh_layout"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <LinearLayout
+ android:id="@+id/fragment_list_layout"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical" >
+
+ <View
+ android:layout_width="fill_parent"
+ android:layout_height="1px"
+ android:background="@color/dividerColor"/>
+
+ <ListView
+ android:id="@+id/fragment_list"
+ android:layout_width="fill_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1.0"
+ android:fastScrollEnabled="true"/>
+
+ <include layout="@layout/tab_progress" />
+ </LinearLayout>
</android.support.v4.widget.SwipeRefreshLayout> \ No newline at end of file
diff --git a/res/layout/actionbar_spinner.xml b/app/src/main/res/layout/actionbar_spinner.xml
index 22055901..22055901 100644
--- a/res/layout/actionbar_spinner.xml
+++ b/app/src/main/res/layout/actionbar_spinner.xml
diff --git a/res/layout/album_cell_item.xml b/app/src/main/res/layout/album_cell_item.xml
index c1c8aa56..3f708e63 100644
--- a/res/layout/album_cell_item.xml
+++ b/app/src/main/res/layout/album_cell_item.xml
@@ -1,89 +1,89 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <RelativeLayout
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1">
-
- <github.daneren2005.dsub.view.SquareImageView
- android:id="@+id/album_coverart"
- android:layout_width="match_parent"
- android:layout_height="match_parent"/>
-
- <RatingBar
- android:id="@+id/album_rating"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:isIndicator="true"
- android:layout_centerHorizontal="true"
- android:numStars="5"
- style="@android:style/Widget.Holo.RatingBar.Small"
- android:layout_alignParentBottom="true"
- android:visibility="gone"/>
- </RelativeLayout>
-
- <LinearLayout
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:paddingTop="4dp"
- android:paddingLeft="2dp">
-
- <LinearLayout
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:gravity="center_vertical"
- android:orientation="vertical">
-
- <TextView
- android:id="@+id/album_title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:text="@string/search.albums"
- android:textColor="?android:textColorPrimary"/>
-
- <LinearLayout
- android:layout_width="fill_parent"
- android:layout_height="wrap_content">
-
- <TextView
- android:id="@+id/album_artist"
- android:layout_width="0dp"
- android:layout_weight="1"
- android:layout_height="wrap_content"
- android:textSize="12sp"
- android:textColor="?android:textColorSecondary"
- android:singleLine="true"
- android:text="@string/search.artists"/>
-
- <ImageButton
- android:id="@+id/album_star"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="right|center_vertical"
- android:src="@drawable/ic_stat_star"
- android:background="@android:color/transparent"
- android:focusable="false"
- android:visibility="gone"/>
- </LinearLayout>
- </LinearLayout>
-
- <ImageView
- android:id="@+id/album_more"
- android:src="?attr/download_none"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="right|center_vertical"
- android:paddingRight="2dp"
- style="@style/BasicButton"/>
- </LinearLayout>
-
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1">
+
+ <github.daneren2005.dsub.view.SquareImageView
+ android:id="@+id/album_coverart"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
+ <RatingBar
+ android:id="@+id/album_rating"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:isIndicator="true"
+ android:layout_centerHorizontal="true"
+ android:numStars="5"
+ style="@android:style/Widget.Holo.RatingBar.Small"
+ android:layout_alignParentBottom="true"
+ android:visibility="gone"/>
+ </RelativeLayout>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:paddingTop="4dp"
+ android:paddingLeft="2dp">
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:gravity="center_vertical"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/album_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:text="@string/search.albums"
+ android:textColor="?android:textColorPrimary"/>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/album_artist"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:textSize="12sp"
+ android:textColor="?android:textColorSecondary"
+ android:singleLine="true"
+ android:text="@string/search.artists"/>
+
+ <ImageButton
+ android:id="@+id/album_star"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right|center_vertical"
+ android:src="@drawable/ic_stat_star"
+ android:background="@android:color/transparent"
+ android:focusable="false"
+ android:visibility="gone"/>
+ </LinearLayout>
+ </LinearLayout>
+
+ <ImageView
+ android:id="@+id/album_more"
+ android:src="?attr/download_none"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right|center_vertical"
+ android:paddingRight="2dp"
+ style="@style/BasicButton"/>
+ </LinearLayout>
+
</LinearLayout> \ No newline at end of file
diff --git a/res/layout/album_list_item.xml b/app/src/main/res/layout/album_list_item.xml
index 088cdcd3..0ee92edd 100644
--- a/res/layout/album_list_item.xml
+++ b/app/src/main/res/layout/album_list_item.xml
@@ -1,74 +1,74 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@id/drag_handle"
- android:orientation="horizontal"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content">
-
- <RelativeLayout
- android:layout_width="@dimen/AlbumArt.Small"
- android:layout_height="@dimen/AlbumArt.Small">
-
- <github.daneren2005.dsub.view.RecyclingImageView
- android:id="@+id/album_coverart"
- android:layout_width="@dimen/AlbumArt.Small"
- android:layout_height="@dimen/AlbumArt.Small"
- android:layout_gravity="left|center_vertical"/>
-
- <RatingBar
- android:id="@+id/album_rating"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:isIndicator="true"
- android:layout_centerHorizontal="true"
- android:numStars="5"
- style="@android:style/Widget.Holo.RatingBar.Small"
- android:layout_alignParentBottom="true"
- android:visibility="gone"/>
- </RelativeLayout>
-
- <LinearLayout
- android:orientation="vertical"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_gravity="left|center_vertical"
- android:paddingLeft="10dip"
- android:paddingRight="3dip">
-
- <TextView
- android:id="@+id/album_title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:paddingBottom="6dip"/>
-
- <TextView
- android:id="@+id/album_artist"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:singleLine="true"/>
-
- </LinearLayout>
-
- <ImageButton
- android:id="@+id/album_star"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="right|center_vertical"
- android:src="@drawable/ic_stat_star"
- android:background="@android:color/transparent"
- android:focusable="false"
- android:visibility="gone"/>
-
- <ImageView
- android:id="@+id/album_more"
- android:src="?attr/download_none"
- android:layout_width="wrap_content"
- android:layout_height="fill_parent"
- android:layout_gravity="right|center_vertical"
- style="@style/MoreButton"/>
-</LinearLayout>
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@id/drag_handle"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+
+ <RelativeLayout
+ android:layout_width="@dimen/AlbumArt.Small"
+ android:layout_height="@dimen/AlbumArt.Small">
+
+ <github.daneren2005.dsub.view.RecyclingImageView
+ android:id="@+id/album_coverart"
+ android:layout_width="@dimen/AlbumArt.Small"
+ android:layout_height="@dimen/AlbumArt.Small"
+ android:layout_gravity="left|center_vertical"/>
+
+ <RatingBar
+ android:id="@+id/album_rating"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:isIndicator="true"
+ android:layout_centerHorizontal="true"
+ android:numStars="5"
+ style="@android:style/Widget.Holo.RatingBar.Small"
+ android:layout_alignParentBottom="true"
+ android:visibility="gone"/>
+ </RelativeLayout>
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_gravity="left|center_vertical"
+ android:paddingLeft="10dip"
+ android:paddingRight="3dip">
+
+ <TextView
+ android:id="@+id/album_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:paddingBottom="6dip"/>
+
+ <TextView
+ android:id="@+id/album_artist"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:singleLine="true"/>
+
+ </LinearLayout>
+
+ <ImageButton
+ android:id="@+id/album_star"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right|center_vertical"
+ android:src="@drawable/ic_stat_star"
+ android:background="@android:color/transparent"
+ android:focusable="false"
+ android:visibility="gone"/>
+
+ <ImageView
+ android:id="@+id/album_more"
+ android:src="?attr/download_none"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_gravity="right|center_vertical"
+ style="@style/MoreButton"/>
+</LinearLayout>
diff --git a/res/layout/appwidget4x1.xml b/app/src/main/res/layout/appwidget4x1.xml
index 8f52c872..8f52c872 100644
--- a/res/layout/appwidget4x1.xml
+++ b/app/src/main/res/layout/appwidget4x1.xml
diff --git a/res/layout/appwidget4x2.xml b/app/src/main/res/layout/appwidget4x2.xml
index 5763fb01..5763fb01 100644
--- a/res/layout/appwidget4x2.xml
+++ b/app/src/main/res/layout/appwidget4x2.xml
diff --git a/res/layout/appwidget4x3.xml b/app/src/main/res/layout/appwidget4x3.xml
index 539b9f01..539b9f01 100644
--- a/res/layout/appwidget4x3.xml
+++ b/app/src/main/res/layout/appwidget4x3.xml
diff --git a/res/layout/appwidget4x4.xml b/app/src/main/res/layout/appwidget4x4.xml
index 1f2db9e1..1f2db9e1 100644
--- a/res/layout/appwidget4x4.xml
+++ b/app/src/main/res/layout/appwidget4x4.xml
diff --git a/res/layout/basic_count_item.xml b/app/src/main/res/layout/basic_count_item.xml
index 36a0e75d..08d276db 100644
--- a/res/layout/basic_count_item.xml
+++ b/app/src/main/res/layout/basic_count_item.xml
@@ -1,35 +1,35 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:background="@android:color/transparent"
- android:minHeight="50dip">
-
- <TextView
- android:id="@+id/basic_count_name"
- android:layout_width="wrap_content"
- android:layout_height="fill_parent"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:gravity="left|center_vertical"
- android:paddingLeft="6dip"
- android:paddingRight="6dip"
- android:background="@android:color/transparent"
- android:text="Text"/>
-
- <TextView
- android:id="@+id/basic_count_count"
- android:layout_width="32dp"
- android:layout_height="32dp"
- android:layout_marginRight="12dp"
- android:background="@drawable/ic_number_border"
- android:focusable="false"
- android:paddingRight="10dp"
- android:layout_marginLeft="20px"
- android:layout_marginBottom="4px"
- android:text="99"
- android:textAppearance="?android:attr/textAppearanceSmallPopupMenu"
- android:textSize="11sp"
- android:gravity="right|center_vertical"
- android:layout_gravity="center_vertical"
- android:visibility="gone"/>
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:background="@android:color/transparent"
+ android:minHeight="50dip">
+
+ <TextView
+ android:id="@+id/basic_count_name"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:gravity="left|center_vertical"
+ android:paddingLeft="6dip"
+ android:paddingRight="6dip"
+ android:background="@android:color/transparent"
+ android:text="Text"/>
+
+ <TextView
+ android:id="@+id/basic_count_count"
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:layout_marginRight="12dp"
+ android:background="@drawable/ic_number_border"
+ android:focusable="false"
+ android:paddingRight="10dp"
+ android:layout_marginLeft="20px"
+ android:layout_marginBottom="4px"
+ android:text="99"
+ android:textAppearance="?android:attr/textAppearanceSmallPopupMenu"
+ android:textSize="11sp"
+ android:gravity="right|center_vertical"
+ android:layout_gravity="center_vertical"
+ android:visibility="gone"/>
</LinearLayout> \ No newline at end of file
diff --git a/res/layout/basic_list_item.xml b/app/src/main/res/layout/basic_list_item.xml
index f40aef2e..2338f7e0 100644
--- a/res/layout/basic_list_item.xml
+++ b/app/src/main/res/layout/basic_list_item.xml
@@ -1,37 +1,37 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="horizontal"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:background="@android:color/transparent">
-
- <TextView
- android:id="@+id/item_name"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:gravity="left|center_vertical"
- android:paddingLeft="6dip"
- android:paddingRight="6dip"
- android:minHeight="50dip"
- android:background="@android:color/transparent"/>
-
- <ImageButton
- android:id="@+id/item_star"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="right|center_vertical"
- android:src="@drawable/ic_stat_star"
- android:background="@android:color/transparent"
- android:focusable="false"
- android:visibility="gone"/>
-
- <ImageView
- android:id="@+id/item_more"
- android:src="?attr/download_none"
- android:layout_width="wrap_content"
- android:layout_height="fill_parent"
- android:layout_gravity="right|center_vertical"
- style="@style/MoreButton"/>
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:background="@android:color/transparent">
+
+ <TextView
+ android:id="@+id/item_name"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:gravity="left|center_vertical"
+ android:paddingLeft="6dip"
+ android:paddingRight="6dip"
+ android:minHeight="50dip"
+ android:background="@android:color/transparent"/>
+
+ <ImageButton
+ android:id="@+id/item_star"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right|center_vertical"
+ android:src="@drawable/ic_stat_star"
+ android:background="@android:color/transparent"
+ android:focusable="false"
+ android:visibility="gone"/>
+
+ <ImageView
+ android:id="@+id/item_more"
+ android:src="?attr/download_none"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_gravity="right|center_vertical"
+ style="@style/MoreButton"/>
</LinearLayout> \ No newline at end of file
diff --git a/res/layout/change_email.xml b/app/src/main/res/layout/change_email.xml
index 8b74215c..18ffc765 100644
--- a/res/layout/change_email.xml
+++ b/app/src/main/res/layout/change_email.xml
@@ -1,28 +1,28 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="horizontal"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content">
-
- <TextView
- android:id="@+id/new_email_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="4dp"
- android:textSize="20dp"
- android:text="@string/admin.change_email_label" />
- <EditText
- android:id="@+id/new_email"
- android:inputType="textEmailAddress"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_marginLeft="4dp" />
- </LinearLayout>
-
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/new_email_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="4dp"
+ android:textSize="20dp"
+ android:text="@string/admin.change_email_label" />
+ <EditText
+ android:id="@+id/new_email"
+ android:inputType="textEmailAddress"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginLeft="4dp" />
+ </LinearLayout>
+
</LinearLayout> \ No newline at end of file
diff --git a/res/layout/change_password.xml b/app/src/main/res/layout/change_password.xml
index ad3e9cd8..1a382a6b 100644
--- a/res/layout/change_password.xml
+++ b/app/src/main/res/layout/change_password.xml
@@ -1,28 +1,28 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="horizontal"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content">
-
- <TextView
- android:id="@+id/new_password_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="4dp"
- android:textSize="20dp"
- android:text="@string/admin.change_password_label" />
- <EditText
- android:id="@+id/new_password"
- android:inputType="textPassword"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_marginLeft="4dp" />
- </LinearLayout>
-
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/new_password_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="4dp"
+ android:textSize="20dp"
+ android:text="@string/admin.change_password_label" />
+ <EditText
+ android:id="@+id/new_password"
+ android:inputType="textPassword"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginLeft="4dp" />
+ </LinearLayout>
+
</LinearLayout> \ No newline at end of file
diff --git a/res/layout/chat.xml b/app/src/main/res/layout/chat.xml
index 89ad77ac..89ad77ac 100644
--- a/res/layout/chat.xml
+++ b/app/src/main/res/layout/chat.xml
diff --git a/res/layout/chat_item.xml b/app/src/main/res/layout/chat_item.xml
index f31f7988..f31f7988 100644
--- a/res/layout/chat_item.xml
+++ b/app/src/main/res/layout/chat_item.xml
diff --git a/res/layout/chat_item_reverse.xml b/app/src/main/res/layout/chat_item_reverse.xml
index b8102193..b8102193 100644
--- a/res/layout/chat_item_reverse.xml
+++ b/app/src/main/res/layout/chat_item_reverse.xml
diff --git a/res/layout/complex_list_item.xml b/app/src/main/res/layout/complex_list_item.xml
index a36cb2f6..67851eca 100644
--- a/res/layout/complex_list_item.xml
+++ b/app/src/main/res/layout/complex_list_item.xml
@@ -1,49 +1,49 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="horizontal"
- android:layout_width="fill_parent"
- android:layout_height="?android:attr/listPreferredItemHeight"
- android:background="@android:color/transparent">
-
- <LinearLayout android:orientation="vertical"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_gravity="center_vertical"
- android:paddingLeft="6dip"
- android:paddingRight="6dip"
- android:gravity="left|center_vertical">
-
- <TextView
- android:id="@+id/item_name"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:background="@android:color/transparent"/>
-
- <TextView
- android:id="@+id/item_description"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:background="@android:color/transparent"/>
- </LinearLayout>
-
- <ImageButton
- android:id="@+id/item_star"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="right|center_vertical"
- android:src="@drawable/ic_stat_star"
- android:background="@android:color/transparent"
- android:focusable="false"
- android:visibility="gone"/>
-
- <ImageView
- android:id="@+id/item_more"
- android:src="?attr/download_none"
- android:layout_width="wrap_content"
- android:layout_height="fill_parent"
- android:layout_gravity="right|center_vertical"
- style="@style/MoreButton"/>
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="?android:attr/listPreferredItemHeight"
+ android:background="@android:color/transparent">
+
+ <LinearLayout android:orientation="vertical"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_gravity="center_vertical"
+ android:paddingLeft="6dip"
+ android:paddingRight="6dip"
+ android:gravity="left|center_vertical">
+
+ <TextView
+ android:id="@+id/item_name"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:background="@android:color/transparent"/>
+
+ <TextView
+ android:id="@+id/item_description"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:background="@android:color/transparent"/>
+ </LinearLayout>
+
+ <ImageButton
+ android:id="@+id/item_star"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right|center_vertical"
+ android:src="@drawable/ic_stat_star"
+ android:background="@android:color/transparent"
+ android:focusable="false"
+ android:visibility="gone"/>
+
+ <ImageView
+ android:id="@+id/item_more"
+ android:src="?attr/download_none"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_gravity="right|center_vertical"
+ style="@style/MoreButton"/>
</LinearLayout> \ No newline at end of file
diff --git a/res/layout/confirm_password.xml b/app/src/main/res/layout/confirm_password.xml
index 97cbcac3..27ee04ea 100644
--- a/res/layout/confirm_password.xml
+++ b/app/src/main/res/layout/confirm_password.xml
@@ -1,28 +1,28 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="horizontal"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content">
-
- <TextView
- android:id="@+id/password_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="4dp"
- android:textSize="20dp"
- android:text="@string/admin.add_user_password" />
- <EditText
- android:id="@+id/password"
- android:inputType="textPassword"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_marginLeft="4dp" />
- </LinearLayout>
-
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/password_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="4dp"
+ android:textSize="20dp"
+ android:text="@string/admin.add_user_password" />
+ <EditText
+ android:id="@+id/password"
+ android:inputType="textPassword"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginLeft="4dp" />
+ </LinearLayout>
+
</LinearLayout> \ No newline at end of file
diff --git a/res/layout/create_bookmark.xml b/app/src/main/res/layout/create_bookmark.xml
index f72b39d8..f72b39d8 100644
--- a/res/layout/create_bookmark.xml
+++ b/app/src/main/res/layout/create_bookmark.xml
diff --git a/res/layout/create_podcast.xml b/app/src/main/res/layout/create_podcast.xml
index 5a2ec970..5a2ec970 100644
--- a/res/layout/create_podcast.xml
+++ b/app/src/main/res/layout/create_podcast.xml
diff --git a/res/layout/create_user.xml b/app/src/main/res/layout/create_user.xml
index 4d918fc9..eac6e5cb 100644
--- a/res/layout/create_user.xml
+++ b/app/src/main/res/layout/create_user.xml
@@ -1,77 +1,77 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="horizontal"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content">
-
- <TextView
- android:id="@+id/username_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="4dp"
- android:textSize="20dp"
- android:text="@string/admin.add_user_username" />
- <EditText
- android:id="@+id/username"
- android:inputType="text"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_marginLeft="4dp" />
- </LinearLayout>
-
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="horizontal"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content">
-
- <TextView
- android:id="@+id/email_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="4dp"
- android:textSize="20dp"
- android:text="@string/admin.add_user_email" />
- <EditText
- android:id="@+id/email"
- android:inputType="textEmailAddress"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_marginLeft="4dp" />
- </LinearLayout>
-
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="horizontal"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content">
-
- <TextView
- android:id="@+id/password_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="4dp"
- android:textSize="20dp"
- android:text="@string/admin.add_user_password" />
- <EditText
- android:id="@+id/password"
- android:inputType="textPassword"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_marginLeft="4dp" />
- </LinearLayout>
-
- <ListView
- android:id="@+id/settings_list"
- android:layout_width="fill_parent"
- android:layout_height="0dip"
- android:layout_weight="1.0"
- android:fastScrollEnabled="true"/>
-
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/username_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="4dp"
+ android:textSize="20dp"
+ android:text="@string/admin.add_user_username" />
+ <EditText
+ android:id="@+id/username"
+ android:inputType="text"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginLeft="4dp" />
+ </LinearLayout>
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/email_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="4dp"
+ android:textSize="20dp"
+ android:text="@string/admin.add_user_email" />
+ <EditText
+ android:id="@+id/email"
+ android:inputType="textEmailAddress"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginLeft="4dp" />
+ </LinearLayout>
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/password_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="4dp"
+ android:textSize="20dp"
+ android:text="@string/admin.add_user_password" />
+ <EditText
+ android:id="@+id/password"
+ android:inputType="textPassword"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginLeft="4dp" />
+ </LinearLayout>
+
+ <ListView
+ android:id="@+id/settings_list"
+ android:layout_width="fill_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1.0"
+ android:fastScrollEnabled="true"/>
+
</LinearLayout> \ No newline at end of file
diff --git a/res/layout/download_activity.xml b/app/src/main/res/layout/download_activity.xml
index 017e4013..017e4013 100644
--- a/res/layout/download_activity.xml
+++ b/app/src/main/res/layout/download_activity.xml
diff --git a/res/layout/download_media_buttons.xml b/app/src/main/res/layout/download_media_buttons.xml
index 1affb164..1affb164 100644
--- a/res/layout/download_media_buttons.xml
+++ b/app/src/main/res/layout/download_media_buttons.xml
diff --git a/res/layout/download_playlist.xml b/app/src/main/res/layout/download_playlist.xml
index e37981e2..8a73ef3b 100644
--- a/res/layout/download_playlist.xml
+++ b/app/src/main/res/layout/download_playlist.xml
@@ -1,31 +1,31 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:layout_weight="1">
-
- <View
- android:layout_width="fill_parent"
- android:layout_height="1px"
- android:background="@color/dividerColor"/>
-
- <TextView
- android:id="@+id/download_empty"
- android:text="@string/download.empty"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:padding="10dip"/>
-
- <com.mobeta.android.dslv.DragSortListView
- style="@style/DragDropListView"
- android:id="@+id/download_list"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:layout_weight="1"
- android:cacheColorHint="#00000000"
- android:fastScrollEnabled="true"/>
-
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1">
+
+ <View
+ android:layout_width="fill_parent"
+ android:layout_height="1px"
+ android:background="@color/dividerColor"/>
+
+ <TextView
+ android:id="@+id/download_empty"
+ android:text="@string/download.empty"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:padding="10dip"/>
+
+ <com.mobeta.android.dslv.DragSortListView
+ style="@style/DragDropListView"
+ android:id="@+id/download_list"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1"
+ android:cacheColorHint="#00000000"
+ android:fastScrollEnabled="true"/>
+
</LinearLayout> \ No newline at end of file
diff --git a/res/layout/download_slider.xml b/app/src/main/res/layout/download_slider.xml
index add11fbb..bfd4c120 100644
--- a/res/layout/download_slider.xml
+++ b/app/src/main/res/layout/download_slider.xml
@@ -1,43 +1,43 @@
-<?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/download_slider"
- android:layout_height="wrap_content"
- android:layout_width="fill_parent"
- android:background="@android:color/transparent"
- android:paddingBottom="10dip">
-
- <TextView
- android:id="@+id/download_position"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
- android:layout_centerVertical="true"
- android:paddingLeft="8dip"
- android:text="0:00"
- android:textSize="12sp"
- android:textColor="?android:textColorPrimary"
- android:paddingBottom="4dip"/>
-
- <SeekBar
- android:id="@+id/download_progress_bar"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:indeterminate="false"
- android:paddingLeft="55dip"
- android:paddingRight="55dip"
- android:paddingTop="3dip"
- android:paddingBottom="7dip" />
-
- <TextView
- android:id="@+id/download_duration"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_centerVertical="true"
- android:paddingRight="8dip"
- android:text="-:--"
- android:textSize="12sp"
- android:textColor="?android:textColorPrimary"
- android:paddingBottom="4dip"/>
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/download_slider"
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent"
+ android:background="@android:color/transparent"
+ android:paddingBottom="10dip">
+
+ <TextView
+ android:id="@+id/download_position"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_centerVertical="true"
+ android:paddingLeft="8dip"
+ android:text="0:00"
+ android:textSize="12sp"
+ android:textColor="?android:textColorPrimary"
+ android:paddingBottom="4dip"/>
+
+ <SeekBar
+ android:id="@+id/download_progress_bar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:indeterminate="false"
+ android:paddingLeft="55dip"
+ android:paddingRight="55dip"
+ android:paddingTop="3dip"
+ android:paddingBottom="7dip" />
+
+ <TextView
+ android:id="@+id/download_duration"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_centerVertical="true"
+ android:paddingRight="8dip"
+ android:text="-:--"
+ android:textSize="12sp"
+ android:textColor="?android:textColorPrimary"
+ android:paddingBottom="4dip"/>
</RelativeLayout> \ No newline at end of file
diff --git a/res/layout/drawer_list_item.xml b/app/src/main/res/layout/drawer_list_item.xml
index 0f98ec41..5f17c9e9 100644
--- a/res/layout/drawer_list_item.xml
+++ b/app/src/main/res/layout/drawer_list_item.xml
@@ -1,26 +1,26 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="horizontal"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:paddingTop="8dip"
- android:paddingBottom="9dip">
-
- <ImageView
- android:id="@+id/drawer_icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="left|center_vertical"
- android:paddingTop="1dip"
- android:paddingBottom="1dip"
- android:paddingRight="8dip"
- android:paddingLeft="10dip"/>
-
- <TextView
- android:id="@+id/drawer_name"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="26sp"
- android:singleLine="true"
- android:textColor="?android:textColorPrimary"/>
-</LinearLayout>
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="8dip"
+ android:paddingBottom="9dip">
+
+ <ImageView
+ android:id="@+id/drawer_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="left|center_vertical"
+ android:paddingTop="1dip"
+ android:paddingBottom="1dip"
+ android:paddingRight="8dip"
+ android:paddingLeft="10dip"/>
+
+ <TextView
+ android:id="@+id/drawer_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="26sp"
+ android:singleLine="true"
+ android:textColor="?android:textColorPrimary"/>
+</LinearLayout>
diff --git a/res/layout/edit_play_action.xml b/app/src/main/res/layout/edit_play_action.xml
index a1115da6..a1115da6 100644
--- a/res/layout/edit_play_action.xml
+++ b/app/src/main/res/layout/edit_play_action.xml
diff --git a/res/layout/equalizer.xml b/app/src/main/res/layout/equalizer.xml
index 3f501a7a..1c6cc833 100644
--- a/res/layout/equalizer.xml
+++ b/app/src/main/res/layout/equalizer.xml
@@ -1,51 +1,51 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:padding="16dip">
-
- <CheckBox
- android:id="@+id/equalizer_enabled"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/equalizer.enabled"
- android:textAppearance="?android:attr/textAppearanceMedium"/>
-
- <ScrollView
- android:layout_width="fill_parent"
- android:layout_height="wrap_content">
-
- <LinearLayout
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content">
-
- <LinearLayout
- android:id="@+id/special_effects_layout"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"/>
-
- <LinearLayout
- android:id="@+id/equalizer_layout"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"/>
-
- <Button
- android:id="@+id/equalizer_preset"
- android:text="@string/equalizer.preset"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_marginTop="20dip"
- android:paddingLeft="40dip"
- android:paddingRight="40dip"/>
-
- </LinearLayout>
- </ScrollView>
-
-</LinearLayout>
-
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:padding="16dip">
+
+ <CheckBox
+ android:id="@+id/equalizer_enabled"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/equalizer.enabled"
+ android:textAppearance="?android:attr/textAppearanceMedium"/>
+
+ <ScrollView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+
+ <LinearLayout
+ android:id="@+id/special_effects_layout"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"/>
+
+ <LinearLayout
+ android:id="@+id/equalizer_layout"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"/>
+
+ <Button
+ android:id="@+id/equalizer_preset"
+ android:text="@string/equalizer.preset"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_marginTop="20dip"
+ android:paddingLeft="40dip"
+ android:paddingRight="40dip"/>
+
+ </LinearLayout>
+ </ScrollView>
+
+</LinearLayout>
+
diff --git a/res/layout/equalizer_bar.xml b/app/src/main/res/layout/equalizer_bar.xml
index 3a104e9b..6dc91565 100644
--- a/res/layout/equalizer_bar.xml
+++ b/app/src/main/res/layout/equalizer_bar.xml
@@ -1,36 +1,36 @@
-<?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content">
-
- <TextView
- android:id="@+id/equalizer.frequency"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
- android:layout_alignParentLeft="true"
- />
-
- <TextView
- android:id="@+id/equalizer.level"
- android:text="0 dB"
- android:textSize="12sp"
- android:gravity="right"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
- android:layout_alignParentRight="true"
- android:layout_toRightOf="@+id/equalizer.frequency"
- />
-
- <SeekBar
- android:id="@+id/equalizer.bar"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_below="@+id/equalizer.frequency"
- />
-
-
-</RelativeLayout>
-
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/equalizer.frequency"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:layout_alignParentLeft="true"
+ />
+
+ <TextView
+ android:id="@+id/equalizer.level"
+ android:text="0 dB"
+ android:textSize="12sp"
+ android:gravity="right"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:layout_alignParentRight="true"
+ android:layout_toRightOf="@+id/equalizer.frequency"
+ />
+
+ <SeekBar
+ android:id="@+id/equalizer.bar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/equalizer.frequency"
+ />
+
+
+</RelativeLayout>
+
diff --git a/res/layout/genre_list_item.xml b/app/src/main/res/layout/genre_list_item.xml
index 658d9cd6..6affa24c 100644
--- a/res/layout/genre_list_item.xml
+++ b/app/src/main/res/layout/genre_list_item.xml
@@ -1,42 +1,42 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="horizontal"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:background="@android:color/transparent">
-
- <TextView
- android:id="@+id/genre_name"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:gravity="left|center_vertical"
- android:paddingLeft="6dip"
- android:paddingRight="6dip"
- android:minHeight="50dip"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:background="@android:color/transparent"/>
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="fill_parent"
- android:orientation="vertical"
- android:gravity="right|center_vertical"
- android:paddingRight="10dip"
- android:background="@android:color/transparent">
-
- <TextView
- android:id="@+id/genre_songs"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceSmall"/>
-
- <TextView
- android:id="@+id/genre_albums"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceSmall"/>
- </LinearLayout>
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:background="@android:color/transparent">
+
+ <TextView
+ android:id="@+id/genre_name"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:gravity="left|center_vertical"
+ android:paddingLeft="6dip"
+ android:paddingRight="6dip"
+ android:minHeight="50dip"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:background="@android:color/transparent"/>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:orientation="vertical"
+ android:gravity="right|center_vertical"
+ android:paddingRight="10dip"
+ android:background="@android:color/transparent">
+
+ <TextView
+ android:id="@+id/genre_songs"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"/>
+
+ <TextView
+ android:id="@+id/genre_albums"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"/>
+ </LinearLayout>
</LinearLayout> \ No newline at end of file
diff --git a/res/layout/grid_view.xml b/app/src/main/res/layout/grid_view.xml
index 4685d494..599cf92c 100644
--- a/res/layout/grid_view.xml
+++ b/app/src/main/res/layout/grid_view.xml
@@ -1,14 +1,14 @@
-<?xml version="1.0" encoding="utf-8"?>
-<github.daneren2005.dsub.view.HeaderGridView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/gridview"
- android:layout_width="fill_parent"
- android:layout_height="0dip"
- android:layout_weight="1.0"
- android:numColumns="@integer/Grid.Columns"
- android:horizontalSpacing="10dp"
- android:verticalSpacing="10dp"
- android:gravity="center"
- android:stretchMode="columnWidth"
- android:padding="24px"
- android:fastScrollEnabled="true"
+<?xml version="1.0" encoding="utf-8"?>
+<github.daneren2005.dsub.view.HeaderGridView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/gridview"
+ android:layout_width="fill_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1.0"
+ android:numColumns="@integer/Grid.Columns"
+ android:horizontalSpacing="10dp"
+ android:verticalSpacing="10dp"
+ android:gravity="center"
+ android:stretchMode="columnWidth"
+ android:padding="24px"
+ android:fastScrollEnabled="true"
android:scrollbarStyle="outsideOverlay"/> \ No newline at end of file
diff --git a/res/layout/home.xml b/app/src/main/res/layout/home.xml
index 018061fa..e5bf5a70 100644
--- a/res/layout/home.xml
+++ b/app/src/main/res/layout/home.xml
@@ -1,23 +1,23 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/home_layout"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
-
- <View
- android:layout_width="fill_parent"
- android:layout_height="1px"
- android:background="@color/dividerColor"/>
-
- <ListView
- android:id="@+id/main_list"
- android:layout_width="fill_parent"
- android:layout_height="0px"
- android:layout_weight="1"/>
-
- <View android:id="@+id/main_dummy"
- android:layout_width="0px"
- android:layout_height="0px"/>
-</LinearLayout>
-
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/home_layout"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <View
+ android:layout_width="fill_parent"
+ android:layout_height="1px"
+ android:background="@color/dividerColor"/>
+
+ <ListView
+ android:id="@+id/main_list"
+ android:layout_width="fill_parent"
+ android:layout_height="0px"
+ android:layout_weight="1"/>
+
+ <View android:id="@+id/main_dummy"
+ android:layout_width="0px"
+ android:layout_height="0px"/>
+</LinearLayout>
+
diff --git a/res/layout/jukebox_volume.xml b/app/src/main/res/layout/jukebox_volume.xml
index 0c49f634..0c49f634 100644
--- a/res/layout/jukebox_volume.xml
+++ b/app/src/main/res/layout/jukebox_volume.xml
diff --git a/res/layout/lyrics.xml b/app/src/main/res/layout/lyrics.xml
index 69230ac6..747727bd 100644
--- a/res/layout/lyrics.xml
+++ b/app/src/main/res/layout/lyrics.xml
@@ -1,55 +1,55 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
-
- <include layout="@layout/tab_progress"/>
-
- <ScrollView
- android:layout_width="fill_parent"
- android:layout_height="0dip"
- android:layout_weight="1.0">
-
- <LinearLayout
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
- <TextView
- android:id="@+id/lyrics_artist"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:gravity="center_horizontal"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:paddingLeft="10dip"
- android:paddingRight="10dip"
- android:paddingTop="10dip"
- android:paddingBottom="4dip"
- />
-
- <TextView
- android:id="@+id/lyrics_title"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:gravity="center_horizontal"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:paddingLeft="10dip"
- android:paddingRight="10dip"
- />
-
- <TextView
- android:id="@+id/lyrics_text"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:gravity="center_horizontal"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:paddingLeft="10dip"
- android:paddingRight="10dip"
- />
-
- </LinearLayout>
-
- </ScrollView>
-
-</LinearLayout>
-
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <include layout="@layout/tab_progress"/>
+
+ <ScrollView
+ android:layout_width="fill_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1.0">
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <TextView
+ android:id="@+id/lyrics_artist"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:gravity="center_horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="10dip"
+ android:paddingRight="10dip"
+ android:paddingTop="10dip"
+ android:paddingBottom="4dip"
+ />
+
+ <TextView
+ android:id="@+id/lyrics_title"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:gravity="center_horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="10dip"
+ android:paddingRight="10dip"
+ />
+
+ <TextView
+ android:id="@+id/lyrics_text"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:gravity="center_horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="10dip"
+ android:paddingRight="10dip"
+ />
+
+ </LinearLayout>
+
+ </ScrollView>
+
+</LinearLayout>
+
diff --git a/res/layout/main_buttons.xml b/app/src/main/res/layout/main_buttons.xml
index e18589d9..95a60409 100644
--- a/res/layout/main_buttons.xml
+++ b/app/src/main/res/layout/main_buttons.xml
@@ -1,157 +1,157 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content">
-
- <LinearLayout
- android:id="@+id/main_select_server"
- android:orientation="horizontal"
- android:paddingTop="2dip"
- android:paddingBottom="2dip"
- android:paddingLeft="6dp"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:minHeight="?android:attr/listPreferredItemHeight">
-
- <ImageView
- android:src="?attr/select_server"
- android:layout_gravity="center_vertical"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
-
- <LinearLayout
- android:orientation="vertical"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content">
-
- <TextView android:id="@+id/main.select_server_1"
- android:text="@string/main.select_server"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="10dip"
- android:layout_marginTop="6dip"
- android:textAppearance="?android:attr/textAppearanceLarge"/>
-
- <TextView android:id="@+id/main.select_server_2"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="10dip"
- android:textAppearance="?android:attr/textAppearanceSmall"/>
-
- </LinearLayout>
- </LinearLayout>
-
- <TextView
- android:id="@+id/main_offline"
- android:text="@string/main.offline"
- android:drawablePadding="12dip"
- android:drawableLeft="?attr/offline_icon"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:gravity="center_vertical"
- android:paddingLeft="6dp"
- android:paddingBottom="4dp"
- android:minHeight="50dip"/>
-
- <LinearLayout
- android:id="@+id/main_albums"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <TextView
- android:text="@string/main.albums_title"
- style="@style/MainAlbumButtonLabel"
- android:layout_width="0dp"
- android:layout_weight="1"
- android:layout_height="fill_parent"/>
-
- <CheckBox
- android:id="@+id/main_albums_per_folder"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/main.albums_per_folder"
- android:layout_marginRight="6dp"
- android:layout_gravity="right"/>
- </LinearLayout>
-
- <TextView
- android:id="@+id/main_video_section"
- android:text="@string/main.videos"
- style="@style/MainAlbumButtonLabel"
- android:layout_width="0dp"
- android:layout_weight="1"
- android:layout_height="fill_parent"/>
-
- <LinearLayout
- android:id="@+id/main_albums_newest"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:minHeight="46dip">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="fill_parent"
- android:text="@string/main.albums_newest"
- style="@style/MainAlbumButton"/>
-
- <TextView
- android:id="@+id/main_albums_recent_count"
- android:layout_width="32dp"
- android:layout_height="32dp"
- android:layout_marginRight="12dp"
- android:background="@drawable/ic_number_border"
- android:focusable="false"
- android:paddingRight="10dp"
- android:layout_marginLeft="20px"
- android:layout_marginBottom="4px"
- android:text="99"
- android:textAppearance="?android:attr/textAppearanceSmallPopupMenu"
- android:textSize="11sp"
- android:gravity="right|center_vertical"
- android:layout_gravity="center_vertical"
- android:visibility="gone"/>
- </LinearLayout>
-
- <TextView
- android:id="@+id/main_albums_recent"
- android:text="@string/main.albums_recent"
- style="@style/MainAlbumButton"/>
- <TextView
- android:id="@+id/main_albums_frequent"
- android:text="@string/main.albums_frequent"
- style="@style/MainAlbumButton"/>
- <TextView
- android:id="@+id/main_albums_highest"
- android:text="@string/main.albums_highest"
- style="@style/MainAlbumButton"/>
- <TextView
- android:id="@+id/main_albums_starred"
- android:text="@string/main.albums_starred"
- style="@style/MainAlbumButton"/>
- <TextView
- android:id="@+id/main_albums_genres"
- android:text="@string/main.albums_genres"
- style="@style/MainAlbumButton"/>
- <TextView
- android:id="@+id/main_albums_year"
- android:text="@string/main.albums_year"
- style="@style/MainAlbumButton"/>
- <TextView
- android:id="@+id/main_albums_random"
- android:text="@string/main.albums_random"
- style="@style/MainAlbumButton"/>
- <TextView
- android:id="@+id/main_albums_alphabetical"
- android:text="@string/main.albums_alphabetical"
- style="@style/MainAlbumButton"/>
-
- <TextView
- android:id="@+id/main_videos"
- android:text="@string/main.videos"
- style="@style/MainAlbumButton"/>
-
-</LinearLayout>
-
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+
+ <LinearLayout
+ android:id="@+id/main_select_server"
+ android:orientation="horizontal"
+ android:paddingTop="2dip"
+ android:paddingBottom="2dip"
+ android:paddingLeft="6dp"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight">
+
+ <ImageView
+ android:src="?attr/select_server"
+ android:layout_gravity="center_vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <TextView android:id="@+id/main.select_server_1"
+ android:text="@string/main.select_server"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="6dip"
+ android:textAppearance="?android:attr/textAppearanceLarge"/>
+
+ <TextView android:id="@+id/main.select_server_2"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:textAppearance="?android:attr/textAppearanceSmall"/>
+
+ </LinearLayout>
+ </LinearLayout>
+
+ <TextView
+ android:id="@+id/main_offline"
+ android:text="@string/main.offline"
+ android:drawablePadding="12dip"
+ android:drawableLeft="?attr/offline_icon"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:gravity="center_vertical"
+ android:paddingLeft="6dp"
+ android:paddingBottom="4dp"
+ android:minHeight="50dip"/>
+
+ <LinearLayout
+ android:id="@+id/main_albums"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <TextView
+ android:text="@string/main.albums_title"
+ style="@style/MainAlbumButtonLabel"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="fill_parent"/>
+
+ <CheckBox
+ android:id="@+id/main_albums_per_folder"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/main.albums_per_folder"
+ android:layout_marginRight="6dp"
+ android:layout_gravity="right"/>
+ </LinearLayout>
+
+ <TextView
+ android:id="@+id/main_video_section"
+ android:text="@string/main.videos"
+ style="@style/MainAlbumButtonLabel"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="fill_parent"/>
+
+ <LinearLayout
+ android:id="@+id/main_albums_newest"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="46dip">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:text="@string/main.albums_newest"
+ style="@style/MainAlbumButton"/>
+
+ <TextView
+ android:id="@+id/main_albums_recent_count"
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:layout_marginRight="12dp"
+ android:background="@drawable/ic_number_border"
+ android:focusable="false"
+ android:paddingRight="10dp"
+ android:layout_marginLeft="20px"
+ android:layout_marginBottom="4px"
+ android:text="99"
+ android:textAppearance="?android:attr/textAppearanceSmallPopupMenu"
+ android:textSize="11sp"
+ android:gravity="right|center_vertical"
+ android:layout_gravity="center_vertical"
+ android:visibility="gone"/>
+ </LinearLayout>
+
+ <TextView
+ android:id="@+id/main_albums_recent"
+ android:text="@string/main.albums_recent"
+ style="@style/MainAlbumButton"/>
+ <TextView
+ android:id="@+id/main_albums_frequent"
+ android:text="@string/main.albums_frequent"
+ style="@style/MainAlbumButton"/>
+ <TextView
+ android:id="@+id/main_albums_highest"
+ android:text="@string/main.albums_highest"
+ style="@style/MainAlbumButton"/>
+ <TextView
+ android:id="@+id/main_albums_starred"
+ android:text="@string/main.albums_starred"
+ style="@style/MainAlbumButton"/>
+ <TextView
+ android:id="@+id/main_albums_genres"
+ android:text="@string/main.albums_genres"
+ style="@style/MainAlbumButton"/>
+ <TextView
+ android:id="@+id/main_albums_year"
+ android:text="@string/main.albums_year"
+ style="@style/MainAlbumButton"/>
+ <TextView
+ android:id="@+id/main_albums_random"
+ android:text="@string/main.albums_random"
+ style="@style/MainAlbumButton"/>
+ <TextView
+ android:id="@+id/main_albums_alphabetical"
+ android:text="@string/main.albums_alphabetical"
+ style="@style/MainAlbumButton"/>
+
+ <TextView
+ android:id="@+id/main_videos"
+ android:text="@string/main.videos"
+ style="@style/MainAlbumButton"/>
+
+</LinearLayout>
+
diff --git a/res/layout/notification.xml b/app/src/main/res/layout/notification.xml
index 12efa4ec..12efa4ec 100644
--- a/res/layout/notification.xml
+++ b/app/src/main/res/layout/notification.xml
diff --git a/res/layout/notification_expanded.xml b/app/src/main/res/layout/notification_expanded.xml
index aa9fe759..aa9fe759 100644
--- a/res/layout/notification_expanded.xml
+++ b/app/src/main/res/layout/notification_expanded.xml
diff --git a/res/layout/preferences.xml b/app/src/main/res/layout/preferences.xml
index 5caaa804..5caaa804 100644
--- a/res/layout/preferences.xml
+++ b/app/src/main/res/layout/preferences.xml
diff --git a/res/layout/progress.xml b/app/src/main/res/layout/progress.xml
index 4a693cb3..a1904c11 100644
--- a/res/layout/progress.xml
+++ b/app/src/main/res/layout/progress.xml
@@ -1,20 +1,20 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="horizontal"
- android:layout_weight="1"
- android:layout_width="0dip"
- android:layout_height="fill_parent"
- android:padding="10dp">
-
- <ProgressBar
- android:layout_width="wrap_content"
- android:layout_height="fill_parent"
- android:layout_marginRight="10dp"/>
-
- <TextView
- android:id="@+id/progress_message"
- android:text="@string/progress.wait"
- android:layout_width="wrap_content"
- android:layout_height="fill_parent"/>
-
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_weight="1"
+ android:layout_width="0dip"
+ android:layout_height="fill_parent"
+ android:padding="10dp">
+
+ <ProgressBar
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_marginRight="10dp"/>
+
+ <TextView
+ android:id="@+id/progress_message"
+ android:text="@string/progress.wait"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"/>
+
</LinearLayout> \ No newline at end of file
diff --git a/res/layout/rating.xml b/app/src/main/res/layout/rating.xml
index cf38fba1..2753ef68 100644
--- a/res/layout/rating.xml
+++ b/app/src/main/res/layout/rating.xml
@@ -1,15 +1,15 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <RatingBar
- android:id="@+id/rating_bar"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:numStars="5"
- android:stepSize="1"
- android:layout_gravity="center_horizontal"/>
-
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <RatingBar
+ android:id="@+id/rating_bar"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:numStars="5"
+ android:stepSize="1"
+ android:layout_gravity="center_horizontal"/>
+
</LinearLayout> \ No newline at end of file
diff --git a/res/layout/save_playlist.xml b/app/src/main/res/layout/save_playlist.xml
index 43f1827a..8bb21748 100644
--- a/res/layout/save_playlist.xml
+++ b/app/src/main/res/layout/save_playlist.xml
@@ -1,26 +1,26 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/save_playlist_root"
- android:padding="10dip"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
-
- <EditText
- android:id="@+id/save_playlist_name"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:inputType="text"
- android:singleLine="true"/>
-
- <CheckBox
- android:id="@+id/save_playlist_overwrite"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:text="@string/playlist.overwrite"
- android:layout_marginLeft="4dp"
- android:checked="false"
- android:visibility="gone"/>
-
-</LinearLayout>
-
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/save_playlist_root"
+ android:padding="10dip"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <EditText
+ android:id="@+id/save_playlist_name"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:inputType="text"
+ android:singleLine="true"/>
+
+ <CheckBox
+ android:id="@+id/save_playlist_overwrite"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/playlist.overwrite"
+ android:layout_marginLeft="4dp"
+ android:checked="false"
+ android:visibility="gone"/>
+
+</LinearLayout>
+
diff --git a/res/layout/search_buttons.xml b/app/src/main/res/layout/search_buttons.xml
index 3e3acfac..699ad341 100644
--- a/res/layout/search_buttons.xml
+++ b/app/src/main/res/layout/search_buttons.xml
@@ -1,73 +1,73 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content">
-
- <TextView
- android:id="@+id/search_artists"
- android:text="@string/search.artists"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="#EFEFEF"
- android:textStyle="bold"
- android:background="#A5A5A5"
- android:gravity="center_vertical"
- android:paddingLeft="4dp"/>
-
- <TextView
- android:id="@+id/search_albums"
- android:text="@string/search.albums"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="#EFEFEF"
- android:textStyle="bold"
- android:background="#A5A5A5"
- android:gravity="center_vertical"
- android:paddingLeft="4dp"/>
-
- <TextView
- android:id="@+id/search_songs"
- android:text="@string/search.songs"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="#EFEFEF"
- android:textStyle="bold"
- android:background="#A5A5A5"
- android:gravity="center_vertical"
- android:paddingLeft="4dp"/>
-
- <TextView
- android:id="@+id/search_more_artists"
- android:text="@string/search.more"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:gravity="center"
- android:paddingTop="8dp"
- android:paddingBottom="8dp"/>
-
- <TextView
- android:id="@+id/search_more_albums"
- android:text="@string/search.more"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:gravity="center"
- android:paddingTop="8dp"
- android:paddingBottom="8dp"/>
-
- <TextView
- android:id="@+id/search_more_songs"
- android:text="@string/search.more"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:gravity="center"
- android:paddingTop="8dp"
- android:paddingBottom="8dp"/>
-
-</LinearLayout>
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/search_artists"
+ android:text="@string/search.artists"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="#EFEFEF"
+ android:textStyle="bold"
+ android:background="#A5A5A5"
+ android:gravity="center_vertical"
+ android:paddingLeft="4dp"/>
+
+ <TextView
+ android:id="@+id/search_albums"
+ android:text="@string/search.albums"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="#EFEFEF"
+ android:textStyle="bold"
+ android:background="#A5A5A5"
+ android:gravity="center_vertical"
+ android:paddingLeft="4dp"/>
+
+ <TextView
+ android:id="@+id/search_songs"
+ android:text="@string/search.songs"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="#EFEFEF"
+ android:textStyle="bold"
+ android:background="#A5A5A5"
+ android:gravity="center_vertical"
+ android:paddingLeft="4dp"/>
+
+ <TextView
+ android:id="@+id/search_more_artists"
+ android:text="@string/search.more"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:gravity="center"
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp"/>
+
+ <TextView
+ android:id="@+id/search_more_albums"
+ android:text="@string/search.more"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:gravity="center"
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp"/>
+
+ <TextView
+ android:id="@+id/search_more_songs"
+ android:text="@string/search.more"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:gravity="center"
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp"/>
+
+</LinearLayout>
diff --git a/res/layout/seekbar_preference.xml b/app/src/main/res/layout/seekbar_preference.xml
index 4d3bc877..030b608b 100644
--- a/res/layout/seekbar_preference.xml
+++ b/app/src/main/res/layout/seekbar_preference.xml
@@ -1,18 +1,18 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content">
-
- <TextView
- android:id="@+id/value"
- android:padding="5dip"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:gravity="right" />
- <SeekBar
- android:id="@+id/seek_bar"
- android:padding="15dip"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content" />
-</LinearLayout>
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/value"
+ android:padding="5dip"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="right" />
+ <SeekBar
+ android:id="@+id/seek_bar"
+ android:padding="15dip"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+</LinearLayout>
diff --git a/res/layout/select_album.xml b/app/src/main/res/layout/select_album.xml
index 54d47047..bbdf0e54 100644
--- a/res/layout/select_album.xml
+++ b/app/src/main/res/layout/select_album.xml
@@ -1,28 +1,28 @@
-<?xml version="1.0" encoding="utf-8"?>
-<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/refresh_layout"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
-
- <LinearLayout
- android:id="@+id/select_album_layout"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
-
- <View
- android:layout_width="fill_parent"
- android:layout_height="1px"
- android:background="@color/dividerColor"/>
-
- <include layout="@layout/tab_progress"/>
-
- <ListView
- android:id="@+id/select_album_entries"
- android:textFilterEnabled="true"
- android:layout_width="fill_parent"
- android:layout_height="0dip"
- android:layout_weight="1.0"
- android:fastScrollEnabled="true"/>
- </LinearLayout>
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/refresh_layout"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <LinearLayout
+ android:id="@+id/select_album_layout"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <View
+ android:layout_width="fill_parent"
+ android:layout_height="1px"
+ android:background="@color/dividerColor"/>
+
+ <include layout="@layout/tab_progress"/>
+
+ <ListView
+ android:id="@+id/select_album_entries"
+ android:textFilterEnabled="true"
+ android:layout_width="fill_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1.0"
+ android:fastScrollEnabled="true"/>
+ </LinearLayout>
</android.support.v4.widget.SwipeRefreshLayout> \ No newline at end of file
diff --git a/res/layout/select_album_header.xml b/app/src/main/res/layout/select_album_header.xml
index d028a476..d028a476 100644
--- a/res/layout/select_album_header.xml
+++ b/app/src/main/res/layout/select_album_header.xml
diff --git a/res/layout/select_artist_header.xml b/app/src/main/res/layout/select_artist_header.xml
index ba1b3d47..2821ce43 100644
--- a/res/layout/select_artist_header.xml
+++ b/app/src/main/res/layout/select_artist_header.xml
@@ -1,43 +1,43 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content">
- <LinearLayout
- android:id="@+id/select_artist_folder"
- android:orientation="horizontal"
- android:paddingTop="2dip"
- android:paddingBottom="2dip"
- android:paddingLeft="6dp"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:minHeight="?android:attr/listPreferredItemHeight">
-
- <ImageView
- android:src="?attr/select_server"
- android:layout_gravity="center_vertical"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
-
- <LinearLayout
- android:orientation="vertical"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content">
-
- <TextView android:id="@+id/select_artist_folder_1"
- android:text="@string/select_artist.folder"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="10dip"
- android:layout_marginTop="6dip"
- android:textAppearance="?android:attr/textAppearanceLarge"/>
-
- <TextView android:id="@+id/select_artist_folder_2"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="10dip"
- android:textAppearance="?android:attr/textAppearanceSmall"/>
-
- </LinearLayout>
- </LinearLayout>
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+ <LinearLayout
+ android:id="@+id/select_artist_folder"
+ android:orientation="horizontal"
+ android:paddingTop="2dip"
+ android:paddingBottom="2dip"
+ android:paddingLeft="6dp"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight">
+
+ <ImageView
+ android:src="?attr/select_server"
+ android:layout_gravity="center_vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <TextView android:id="@+id/select_artist_folder_1"
+ android:text="@string/select_artist.folder"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="6dip"
+ android:textAppearance="?android:attr/textAppearanceLarge"/>
+
+ <TextView android:id="@+id/select_artist_folder_2"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:textAppearance="?android:attr/textAppearanceSmall"/>
+
+ </LinearLayout>
+ </LinearLayout>
</LinearLayout> \ No newline at end of file
diff --git a/res/layout/shuffle_dialog.xml b/app/src/main/res/layout/shuffle_dialog.xml
index 295f57cb..295f57cb 100644
--- a/res/layout/shuffle_dialog.xml
+++ b/app/src/main/res/layout/shuffle_dialog.xml
diff --git a/res/layout/song_list_item.xml b/app/src/main/res/layout/song_list_item.xml
index 67d460f1..86f77869 100644
--- a/res/layout/song_list_item.xml
+++ b/app/src/main/res/layout/song_list_item.xml
@@ -1,126 +1,126 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@id/drag_handle"
- android:orientation="horizontal"
- android:layout_width="fill_parent"
- android:layout_height="?android:attr/listPreferredItemHeight">
-
- <CheckedTextView
- android:id="@+id/song_check"
- android:layout_width="wrap_content"
- android:layout_height="fill_parent"
- android:gravity="center_vertical"
- android:checkMark="?android:attr/listChoiceIndicatorMultiple"
- android:paddingLeft="3dip"/>
-
- <LinearLayout android:orientation="vertical"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_gravity="center_vertical">
-
- <LinearLayout android:orientation="horizontal"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical">
-
- <TextView
- android:id="@+id/song_title"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_gravity="left|center_vertical"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:drawablePadding="6dip"
- android:paddingLeft="6dip"
- android:paddingRight="6dip"/>
-
- <ImageButton
- android:id="@+id/song_bookmark"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:layout_gravity="right|center_vertical"
- android:src="@drawable/ic_menu_bookmark_selected"
- android:background="@null"
- android:focusable="false"
- android:scaleType="fitCenter"
- android:visibility="gone"/>
-
- <ImageButton
- android:id="@+id/song_star"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="right|center_vertical"
- android:src="@drawable/ic_stat_star"
- android:background="@null"
- android:focusable="false"
- android:visibility="gone"/>
-
- <TextView
- android:id="@+id/song_status"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="right|center_vertical"
- android:drawablePadding="1dip"
- android:paddingRight="2dip"/>
-
- <ImageView
- android:id="@+id/song_status_icon"
- android:layout_width="24dip"
- android:layout_height="24dip"
- android:layout_gravity="center_vertical"
- android:src="?attr/downloading"
- android:visibility="gone"/>
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/song_bottom"
- android:orientation="horizontal"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical">
-
- <TextView
- android:id="@+id/song_artist"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_gravity="left|center_vertical"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:singleLine="true"
- android:ellipsize="middle"
- android:paddingLeft="6dip"/>
-
- <RatingBar
- android:id="@+id/song_rating"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:isIndicator="true"
- android:layout_centerHorizontal="true"
- android:numStars="5"
- style="@android:style/Widget.Holo.RatingBar.Small"
- android:visibility="gone"/>
-
- <TextView
- android:id="@+id/song_duration"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="right|center_vertical"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:singleLine="true"
- android:paddingLeft="3dip"
- android:paddingRight="4dip"/>
-
- </LinearLayout>
- </LinearLayout>
-
- <ImageView
- android:id="@+id/artist_more"
- android:src="?attr/download_none"
- android:layout_width="wrap_content"
- android:layout_height="fill_parent"
- android:layout_gravity="right|center_vertical"
- style="@style/MoreButton"/>
-</LinearLayout>
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@id/drag_handle"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="?android:attr/listPreferredItemHeight">
+
+ <CheckedTextView
+ android:id="@+id/song_check"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:gravity="center_vertical"
+ android:checkMark="?android:attr/listChoiceIndicatorMultiple"
+ android:paddingLeft="3dip"/>
+
+ <LinearLayout android:orientation="vertical"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_gravity="center_vertical">
+
+ <LinearLayout android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical">
+
+ <TextView
+ android:id="@+id/song_title"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_gravity="left|center_vertical"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:drawablePadding="6dip"
+ android:paddingLeft="6dip"
+ android:paddingRight="6dip"/>
+
+ <ImageButton
+ android:id="@+id/song_bookmark"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_gravity="right|center_vertical"
+ android:src="@drawable/ic_menu_bookmark_selected"
+ android:background="@null"
+ android:focusable="false"
+ android:scaleType="fitCenter"
+ android:visibility="gone"/>
+
+ <ImageButton
+ android:id="@+id/song_star"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right|center_vertical"
+ android:src="@drawable/ic_stat_star"
+ android:background="@null"
+ android:focusable="false"
+ android:visibility="gone"/>
+
+ <TextView
+ android:id="@+id/song_status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right|center_vertical"
+ android:drawablePadding="1dip"
+ android:paddingRight="2dip"/>
+
+ <ImageView
+ android:id="@+id/song_status_icon"
+ android:layout_width="24dip"
+ android:layout_height="24dip"
+ android:layout_gravity="center_vertical"
+ android:src="?attr/downloading"
+ android:visibility="gone"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/song_bottom"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical">
+
+ <TextView
+ android:id="@+id/song_artist"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_gravity="left|center_vertical"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:singleLine="true"
+ android:ellipsize="middle"
+ android:paddingLeft="6dip"/>
+
+ <RatingBar
+ android:id="@+id/song_rating"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:isIndicator="true"
+ android:layout_centerHorizontal="true"
+ android:numStars="5"
+ style="@android:style/Widget.Holo.RatingBar.Small"
+ android:visibility="gone"/>
+
+ <TextView
+ android:id="@+id/song_duration"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right|center_vertical"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:singleLine="true"
+ android:paddingLeft="3dip"
+ android:paddingRight="4dip"/>
+
+ </LinearLayout>
+ </LinearLayout>
+
+ <ImageView
+ android:id="@+id/artist_more"
+ android:src="?attr/download_none"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_gravity="right|center_vertical"
+ style="@style/MoreButton"/>
+</LinearLayout>
diff --git a/res/layout/start_timer.xml b/app/src/main/res/layout/start_timer.xml
index 9736a31d..9736a31d 100644
--- a/res/layout/start_timer.xml
+++ b/app/src/main/res/layout/start_timer.xml
diff --git a/res/layout/static_drawer_activity.xml b/app/src/main/res/layout/static_drawer_activity.xml
index db631540..db631540 100644
--- a/res/layout/static_drawer_activity.xml
+++ b/app/src/main/res/layout/static_drawer_activity.xml
diff --git a/res/layout/sync_dialog.xml b/app/src/main/res/layout/sync_dialog.xml
index 5133b753..5133b753 100644
--- a/res/layout/sync_dialog.xml
+++ b/app/src/main/res/layout/sync_dialog.xml
diff --git a/res/layout/tab_progress.xml b/app/src/main/res/layout/tab_progress.xml
index 3ce61a33..f6d326ca 100644
--- a/res/layout/tab_progress.xml
+++ b/app/src/main/res/layout/tab_progress.xml
@@ -1,31 +1,31 @@
-<?xml version="1.0" encoding="utf-8"?>
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/tab_progress"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:clickable="true"
- android:visibility="gone">
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:layout_gravity="center">
-
- <ProgressBar
- android:id="@+id/tab_progress_spinner"
- style="?android:attr/progressBarStyleLarge"
- android:layout_gravity="center_horizontal"
- android:layout_marginRight="6dp"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
-
- <TextView
- android:id="@+id/tab_progress_message"
- android:text="@string/progress.wait"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:textAppearance="?android:attr/textAppearanceMedium"/>
- </LinearLayout>
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/tab_progress"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:clickable="true"
+ android:visibility="gone">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_gravity="center">
+
+ <ProgressBar
+ android:id="@+id/tab_progress_spinner"
+ style="?android:attr/progressBarStyleLarge"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginRight="6dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <TextView
+ android:id="@+id/tab_progress_message"
+ android:text="@string/progress.wait"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:textAppearance="?android:attr/textAppearanceMedium"/>
+ </LinearLayout>
</FrameLayout> \ No newline at end of file
diff --git a/res/layout/unscrollable_grid_view.xml b/app/src/main/res/layout/unscrollable_grid_view.xml
index d63eeac7..96bea5ce 100644
--- a/res/layout/unscrollable_grid_view.xml
+++ b/app/src/main/res/layout/unscrollable_grid_view.xml
@@ -1,11 +1,11 @@
-<?xml version="1.0" encoding="utf-8"?>
-<github.daneren2005.dsub.view.UnscrollableGridView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/gridview"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:numColumns="@integer/Grid.Columns"
- android:horizontalSpacing="10dp"
- android:verticalSpacing="10dp"
- android:gravity="center"
- android:padding="20px"
+<?xml version="1.0" encoding="utf-8"?>
+<github.daneren2005.dsub.view.UnscrollableGridView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/gridview"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:numColumns="@integer/Grid.Columns"
+ android:horizontalSpacing="10dp"
+ android:verticalSpacing="10dp"
+ android:gravity="center"
+ android:padding="20px"
android:stretchMode="columnWidth"/> \ No newline at end of file
diff --git a/res/layout/update_playlist.xml b/app/src/main/res/layout/update_playlist.xml
index 7354ef5c..7354ef5c 100644
--- a/res/layout/update_playlist.xml
+++ b/app/src/main/res/layout/update_playlist.xml
diff --git a/res/layout/update_share.xml b/app/src/main/res/layout/update_share.xml
index 92b7137b..92b7137b 100644
--- a/res/layout/update_share.xml
+++ b/app/src/main/res/layout/update_share.xml
diff --git a/res/layout/user_header.xml b/app/src/main/res/layout/user_header.xml
index 0b303afe..0b303afe 100644
--- a/res/layout/user_header.xml
+++ b/app/src/main/res/layout/user_header.xml
diff --git a/res/layout/user_list_item.xml b/app/src/main/res/layout/user_list_item.xml
index 146c44d5..dc2bdab9 100644
--- a/res/layout/user_list_item.xml
+++ b/app/src/main/res/layout/user_list_item.xml
@@ -1,44 +1,44 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="horizontal"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:background="@android:color/transparent">
-
- <github.daneren2005.dsub.view.RecyclingImageView
- android:id="@+id/item_avatar"
- android:src="@drawable/ic_social_person"
- android:layout_width="@dimen/AlbumArt.Small"
- android:layout_height="@dimen/AlbumArt.Small"
- android:layout_gravity="left|center_vertical"/>
-
- <TextView
- android:id="@+id/item_name"
- android:layout_width="0dip"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:gravity="left|center_vertical"
- android:paddingLeft="12dip"
- android:paddingRight="6dip"
- android:minHeight="50dip"
- android:background="@android:color/transparent"/>
-
- <ImageButton
- android:id="@+id/item_star"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="right|center_vertical"
- android:src="@drawable/ic_stat_star"
- android:background="@android:color/transparent"
- android:focusable="false"
- android:visibility="gone"/>
-
- <ImageView
- android:id="@+id/item_more"
- android:src="?attr/download_none"
- android:layout_width="wrap_content"
- android:layout_height="fill_parent"
- android:layout_gravity="right|center_vertical"
- style="@style/MoreButton"/>
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:background="@android:color/transparent">
+
+ <github.daneren2005.dsub.view.RecyclingImageView
+ android:id="@+id/item_avatar"
+ android:src="@drawable/ic_social_person"
+ android:layout_width="@dimen/AlbumArt.Small"
+ android:layout_height="@dimen/AlbumArt.Small"
+ android:layout_gravity="left|center_vertical"/>
+
+ <TextView
+ android:id="@+id/item_name"
+ android:layout_width="0dip"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:gravity="left|center_vertical"
+ android:paddingLeft="12dip"
+ android:paddingRight="6dip"
+ android:minHeight="50dip"
+ android:background="@android:color/transparent"/>
+
+ <ImageButton
+ android:id="@+id/item_star"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right|center_vertical"
+ android:src="@drawable/ic_stat_star"
+ android:background="@android:color/transparent"
+ android:focusable="false"
+ android:visibility="gone"/>
+
+ <ImageView
+ android:id="@+id/item_more"
+ android:src="?attr/download_none"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_gravity="right|center_vertical"
+ style="@style/MoreButton"/>
</LinearLayout> \ No newline at end of file
diff --git a/res/menu/abstract_top_menu.xml b/app/src/main/res/menu/abstract_top_menu.xml
index 830b167c..22499ae9 100644
--- a/res/menu/abstract_top_menu.xml
+++ b/app/src/main/res/menu/abstract_top_menu.xml
@@ -1,21 +1,21 @@
-<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:compat="http://schemas.android.com/apk/res-auto">
- <item
- android:id="@+id/menu_search"
- android:icon="?attr/search"
- android:title="@string/menu.search"
- compat:showAsAction="always|withText"/>
-
- <group android:id="@+id/not_touchscreen">
- <item
- android:id="@+id/menu_refresh"
- android:icon="?attr/refresh"
- android:title="@string/menu.refresh"
- compat:showAsAction="ifRoom|withText"/>
- </group>
-
- <item
- android:id="@+id/menu_exit"
- android:title="@string/menu.exit"/>
-</menu>
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:compat="http://schemas.android.com/apk/res-auto">
+ <item
+ android:id="@+id/menu_search"
+ android:icon="?attr/search"
+ android:title="@string/menu.search"
+ compat:showAsAction="always|withText"/>
+
+ <group android:id="@+id/not_touchscreen">
+ <item
+ android:id="@+id/menu_refresh"
+ android:icon="?attr/refresh"
+ android:title="@string/menu.refresh"
+ compat:showAsAction="ifRoom|withText"/>
+ </group>
+
+ <item
+ android:id="@+id/menu_exit"
+ android:title="@string/menu.exit"/>
+</menu>
diff --git a/res/menu/admin.xml b/app/src/main/res/menu/admin.xml
index f16e89f7..28c5134b 100644
--- a/res/menu/admin.xml
+++ b/app/src/main/res/menu/admin.xml
@@ -1,12 +1,12 @@
-<menu xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:compat="http://schemas.android.com/apk/res-auto">
- <item
- android:id="@+id/menu_add_user"
- android:title="@string/menu.add_user"
- android:icon="?attr/add_person"
- compat:showAsAction="always|withText"/>
-
- <item
- android:id="@+id/menu_exit"
- android:title="@string/menu.exit"/>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:compat="http://schemas.android.com/apk/res-auto">
+ <item
+ android:id="@+id/menu_add_user"
+ android:title="@string/menu.add_user"
+ android:icon="?attr/add_person"
+ compat:showAsAction="always|withText"/>
+
+ <item
+ android:id="@+id/menu_exit"
+ android:title="@string/menu.exit"/>
</menu> \ No newline at end of file
diff --git a/res/menu/admin_context.xml b/app/src/main/res/menu/admin_context.xml
index 922a24a9..e4c8fdc3 100644
--- a/res/menu/admin_context.xml
+++ b/app/src/main/res/menu/admin_context.xml
@@ -1,16 +1,16 @@
-<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:compat="http://schemas.android.com/apk/res-auto">
-
- <item
- android:id="@+id/admin_change_email"
- android:title="@string/admin.change_email"/>
-
- <item
- android:id="@+id/admin_change_password"
- android:title="@string/admin.change_password"/>
-
- <item
- android:id="@+id/admin_delete_user"
- android:title="@string/admin.delete_user"/>
-</menu>
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:compat="http://schemas.android.com/apk/res-auto">
+
+ <item
+ android:id="@+id/admin_change_email"
+ android:title="@string/admin.change_email"/>
+
+ <item
+ android:id="@+id/admin_change_password"
+ android:title="@string/admin.change_password"/>
+
+ <item
+ android:id="@+id/admin_delete_user"
+ android:title="@string/admin.delete_user"/>
+</menu>
diff --git a/res/menu/admin_context_user.xml b/app/src/main/res/menu/admin_context_user.xml
index cc4c1ab9..d53eee49 100644
--- a/res/menu/admin_context_user.xml
+++ b/app/src/main/res/menu/admin_context_user.xml
@@ -1,8 +1,8 @@
-<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:compat="http://schemas.android.com/apk/res-auto">
-
- <item
- android:id="@+id/admin_change_password"
- android:title="@string/admin.change_password"/>
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:compat="http://schemas.android.com/apk/res-auto">
+
+ <item
+ android:id="@+id/admin_change_password"
+ android:title="@string/admin.change_password"/>
</menu> \ No newline at end of file
diff --git a/res/menu/downloading.xml b/app/src/main/res/menu/downloading.xml
index 670a2803..670a2803 100644
--- a/res/menu/downloading.xml
+++ b/app/src/main/res/menu/downloading.xml
diff --git a/res/menu/drawer_menu.xml b/app/src/main/res/menu/drawer_menu.xml
index 3a3baf29..b3e70cfa 100644
--- a/res/menu/drawer_menu.xml
+++ b/app/src/main/res/menu/drawer_menu.xml
@@ -1,14 +1,14 @@
-<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:compat="http://schemas.android.com/apk/res-auto">
-
- <item
- android:id="@+id/menu_search"
- android:icon="?attr/search"
- android:title="@string/menu.search"
- compat:showAsAction="always|withText"/>
-
- <item
- android:id="@+id/menu_exit"
- android:title="@string/menu.exit"/>
-</menu>
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:compat="http://schemas.android.com/apk/res-auto">
+
+ <item
+ android:id="@+id/menu_search"
+ android:icon="?attr/search"
+ android:title="@string/menu.search"
+ compat:showAsAction="always|withText"/>
+
+ <item
+ android:id="@+id/menu_exit"
+ android:title="@string/menu.exit"/>
+</menu>
diff --git a/res/menu/empty.xml b/app/src/main/res/menu/empty.xml
index eae036cf..eae036cf 100644
--- a/res/menu/empty.xml
+++ b/app/src/main/res/menu/empty.xml
diff --git a/res/menu/main.xml b/app/src/main/res/menu/main.xml
index 549c5fb6..549c5fb6 100644
--- a/res/menu/main.xml
+++ b/app/src/main/res/menu/main.xml
diff --git a/res/menu/nowplaying.xml b/app/src/main/res/menu/nowplaying.xml
index 60255692..60255692 100644
--- a/res/menu/nowplaying.xml
+++ b/app/src/main/res/menu/nowplaying.xml
diff --git a/res/menu/nowplaying_context.xml b/app/src/main/res/menu/nowplaying_context.xml
index c9347353..c9347353 100644
--- a/res/menu/nowplaying_context.xml
+++ b/app/src/main/res/menu/nowplaying_context.xml
diff --git a/res/menu/nowplaying_context_offline.xml b/app/src/main/res/menu/nowplaying_context_offline.xml
index 24b23a8d..24b23a8d 100644
--- a/res/menu/nowplaying_context_offline.xml
+++ b/app/src/main/res/menu/nowplaying_context_offline.xml
diff --git a/res/menu/nowplaying_offline.xml b/app/src/main/res/menu/nowplaying_offline.xml
index bba5ba00..bba5ba00 100644
--- a/res/menu/nowplaying_offline.xml
+++ b/app/src/main/res/menu/nowplaying_offline.xml
diff --git a/res/menu/search.xml b/app/src/main/res/menu/search.xml
index cab9c4f6..cab9c4f6 100644
--- a/res/menu/search.xml
+++ b/app/src/main/res/menu/search.xml
diff --git a/res/menu/select_album.xml b/app/src/main/res/menu/select_album.xml
index 3d2228e8..3d2228e8 100644
--- a/res/menu/select_album.xml
+++ b/app/src/main/res/menu/select_album.xml
diff --git a/res/menu/select_album_context.xml b/app/src/main/res/menu/select_album_context.xml
index 5b2529e7..5b2529e7 100644
--- a/res/menu/select_album_context.xml
+++ b/app/src/main/res/menu/select_album_context.xml
diff --git a/res/menu/select_album_context_offline.xml b/app/src/main/res/menu/select_album_context_offline.xml
index a1805f5b..a1805f5b 100644
--- a/res/menu/select_album_context_offline.xml
+++ b/app/src/main/res/menu/select_album_context_offline.xml
diff --git a/res/menu/select_album_list.xml b/app/src/main/res/menu/select_album_list.xml
index a9196d1c..a9196d1c 100644
--- a/res/menu/select_album_list.xml
+++ b/app/src/main/res/menu/select_album_list.xml
diff --git a/res/menu/select_artist.xml b/app/src/main/res/menu/select_artist.xml
index 603f0a3b..603f0a3b 100644
--- a/res/menu/select_artist.xml
+++ b/app/src/main/res/menu/select_artist.xml
diff --git a/res/menu/select_artist_context.xml b/app/src/main/res/menu/select_artist_context.xml
index debc07c6..debc07c6 100644
--- a/res/menu/select_artist_context.xml
+++ b/app/src/main/res/menu/select_artist_context.xml
diff --git a/res/menu/select_artist_context_offline.xml b/app/src/main/res/menu/select_artist_context_offline.xml
index 17ee97e0..17ee97e0 100644
--- a/res/menu/select_artist_context_offline.xml
+++ b/app/src/main/res/menu/select_artist_context_offline.xml
diff --git a/res/menu/select_bookmark_context.xml b/app/src/main/res/menu/select_bookmark_context.xml
index 2b1b83fd..2b1b83fd 100644
--- a/res/menu/select_bookmark_context.xml
+++ b/app/src/main/res/menu/select_bookmark_context.xml
diff --git a/res/menu/select_playlist_context.xml b/app/src/main/res/menu/select_playlist_context.xml
index 47033d9c..47033d9c 100644
--- a/res/menu/select_playlist_context.xml
+++ b/app/src/main/res/menu/select_playlist_context.xml
diff --git a/res/menu/select_playlist_context_offline.xml b/app/src/main/res/menu/select_playlist_context_offline.xml
index d63aec17..d63aec17 100644
--- a/res/menu/select_playlist_context_offline.xml
+++ b/app/src/main/res/menu/select_playlist_context_offline.xml
diff --git a/res/menu/select_podcast_episode.xml b/app/src/main/res/menu/select_podcast_episode.xml
index 65cb0555..65cb0555 100644
--- a/res/menu/select_podcast_episode.xml
+++ b/app/src/main/res/menu/select_podcast_episode.xml
diff --git a/res/menu/select_podcast_episode_context.xml b/app/src/main/res/menu/select_podcast_episode_context.xml
index bacccda3..bacccda3 100644
--- a/res/menu/select_podcast_episode_context.xml
+++ b/app/src/main/res/menu/select_podcast_episode_context.xml
diff --git a/res/menu/select_podcast_episode_context_offline.xml b/app/src/main/res/menu/select_podcast_episode_context_offline.xml
index 587d01f7..587d01f7 100644
--- a/res/menu/select_podcast_episode_context_offline.xml
+++ b/app/src/main/res/menu/select_podcast_episode_context_offline.xml
diff --git a/res/menu/select_podcast_episode_offline.xml b/app/src/main/res/menu/select_podcast_episode_offline.xml
index 3665d317..3665d317 100644
--- a/res/menu/select_podcast_episode_offline.xml
+++ b/app/src/main/res/menu/select_podcast_episode_offline.xml
diff --git a/res/menu/select_podcasts.xml b/app/src/main/res/menu/select_podcasts.xml
index f30429ce..f30429ce 100644
--- a/res/menu/select_podcasts.xml
+++ b/app/src/main/res/menu/select_podcasts.xml
diff --git a/res/menu/select_podcasts_context.xml b/app/src/main/res/menu/select_podcasts_context.xml
index 5df9d278..5df9d278 100644
--- a/res/menu/select_podcasts_context.xml
+++ b/app/src/main/res/menu/select_podcasts_context.xml
diff --git a/res/menu/select_podcasts_context_offline.xml b/app/src/main/res/menu/select_podcasts_context_offline.xml
index cbc76224..cbc76224 100644
--- a/res/menu/select_podcasts_context_offline.xml
+++ b/app/src/main/res/menu/select_podcasts_context_offline.xml
diff --git a/res/menu/select_share_context.xml b/app/src/main/res/menu/select_share_context.xml
index d015beaf..79eb3d55 100644
--- a/res/menu/select_share_context.xml
+++ b/app/src/main/res/menu/select_share_context.xml
@@ -1,21 +1,21 @@
-<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:compat="http://schemas.android.com/apk/res-auto">
-
- <item
- android:id="@+id/share_menu_info"
- android:title="@string/common.info"/>
-
- <item
- android:id="@+id/share_menu_share"
- android:title="@string/menu.share"/>
-
- <item
- android:id="@+id/share_update_info"
- android:title="@string/playlist.update_info"
- />
-
- <item
- android:id="@+id/share_menu_delete"
- android:title="@string/share.delete"/>
-</menu>
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:compat="http://schemas.android.com/apk/res-auto">
+
+ <item
+ android:id="@+id/share_menu_info"
+ android:title="@string/common.info"/>
+
+ <item
+ android:id="@+id/share_menu_share"
+ android:title="@string/menu.share"/>
+
+ <item
+ android:id="@+id/share_update_info"
+ android:title="@string/playlist.update_info"
+ />
+
+ <item
+ android:id="@+id/share_menu_delete"
+ android:title="@string/share.delete"/>
+</menu>
diff --git a/res/menu/select_song.xml b/app/src/main/res/menu/select_song.xml
index fc4494cb..fc4494cb 100644
--- a/res/menu/select_song.xml
+++ b/app/src/main/res/menu/select_song.xml
diff --git a/res/menu/select_song_context.xml b/app/src/main/res/menu/select_song_context.xml
index d8fc211c..d8fc211c 100644
--- a/res/menu/select_song_context.xml
+++ b/app/src/main/res/menu/select_song_context.xml
diff --git a/res/menu/select_song_context_offline.xml b/app/src/main/res/menu/select_song_context_offline.xml
index 49445876..49445876 100644
--- a/res/menu/select_song_context_offline.xml
+++ b/app/src/main/res/menu/select_song_context_offline.xml
diff --git a/res/menu/select_song_offline.xml b/app/src/main/res/menu/select_song_offline.xml
index c45405fb..c45405fb 100644
--- a/res/menu/select_song_offline.xml
+++ b/app/src/main/res/menu/select_song_offline.xml
diff --git a/res/menu/select_video_context.xml b/app/src/main/res/menu/select_video_context.xml
index 3eda2df7..3eda2df7 100644
--- a/res/menu/select_video_context.xml
+++ b/app/src/main/res/menu/select_video_context.xml
diff --git a/res/menu/select_video_context_offline.xml b/app/src/main/res/menu/select_video_context_offline.xml
index 3fea9f5e..3fea9f5e 100644
--- a/res/menu/select_video_context_offline.xml
+++ b/app/src/main/res/menu/select_video_context_offline.xml
diff --git a/res/menu/similar_artists.xml b/app/src/main/res/menu/similar_artists.xml
index 771555b6..771555b6 100644
--- a/res/menu/similar_artists.xml
+++ b/app/src/main/res/menu/similar_artists.xml
diff --git a/res/menu/tasker_configuration.xml b/app/src/main/res/menu/tasker_configuration.xml
index 07613640..bb49ba27 100644
--- a/res/menu/tasker_configuration.xml
+++ b/app/src/main/res/menu/tasker_configuration.xml
@@ -1,16 +1,16 @@
-<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:compat="http://schemas.android.com/apk/res-auto">
-
- <item
- android:id="@+id/menu_cancel"
- android:icon="?attr/remove"
- android:title="@string/common.cancel"
- compat:showAsAction="always|withText"/>
-
- <item
- android:id="@+id/menu_accept"
- android:icon="?attr/save"
- android:title="@string/common.ok"
- compat:showAsAction="always|withText"/>
-</menu>
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:compat="http://schemas.android.com/apk/res-auto">
+
+ <item
+ android:id="@+id/menu_cancel"
+ android:icon="?attr/remove"
+ android:title="@string/common.cancel"
+ compat:showAsAction="always|withText"/>
+
+ <item
+ android:id="@+id/menu_accept"
+ android:icon="?attr/save"
+ android:title="@string/common.ok"
+ compat:showAsAction="always|withText"/>
+</menu>
diff --git a/res/menu/unstar.xml b/app/src/main/res/menu/unstar.xml
index 4b629cda..502e88ab 100644
--- a/res/menu/unstar.xml
+++ b/app/src/main/res/menu/unstar.xml
@@ -1,7 +1,7 @@
-<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:compat="http://schemas.android.com/apk/res-auto">
- <item
- android:id="@+id/menu_unstar"
- android:title="@string/common.unstar"/>
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:compat="http://schemas.android.com/apk/res-auto">
+ <item
+ android:id="@+id/menu_unstar"
+ android:title="@string/common.unstar"/>
</menu> \ No newline at end of file
diff --git a/res/menu/user.xml b/app/src/main/res/menu/user.xml
index 9c335cec..eed352e5 100644
--- a/res/menu/user.xml
+++ b/app/src/main/res/menu/user.xml
@@ -1,32 +1,32 @@
-<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:compat="http://schemas.android.com/apk/res-auto">
-
- <item
- android:id="@+id/menu_update_permissions"
- android:title="@string/admin.update_permissions"
- android:icon="?attr/save"
- compat:showAsAction="always|withText"/>
-
- <item
- android:id="@+id/menu_change_password"
- android:title="@string/admin.change_password"
- android:icon="?attr/password"
- compat:showAsAction="always|withText"/>
-
- <group android:id="@+id/not_touchscreen">
- <item
- android:id="@+id/menu_refresh"
- android:icon="?attr/refresh"
- android:title="@string/menu.refresh"
- compat:showAsAction="ifRoom|withText"/>
- </group>
-
- <item
- android:id="@+id/menu_change_email"
- android:title="@string/admin.change_email"/>
-
- <item
- android:id="@+id/menu_exit"
- android:title="@string/menu.exit"/>
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:compat="http://schemas.android.com/apk/res-auto">
+
+ <item
+ android:id="@+id/menu_update_permissions"
+ android:title="@string/admin.update_permissions"
+ android:icon="?attr/save"
+ compat:showAsAction="always|withText"/>
+
+ <item
+ android:id="@+id/menu_change_password"
+ android:title="@string/admin.change_password"
+ android:icon="?attr/password"
+ compat:showAsAction="always|withText"/>
+
+ <group android:id="@+id/not_touchscreen">
+ <item
+ android:id="@+id/menu_refresh"
+ android:icon="?attr/refresh"
+ android:title="@string/menu.refresh"
+ compat:showAsAction="ifRoom|withText"/>
+ </group>
+
+ <item
+ android:id="@+id/menu_change_email"
+ android:title="@string/admin.change_email"/>
+
+ <item
+ android:id="@+id/menu_exit"
+ android:title="@string/menu.exit"/>
</menu> \ No newline at end of file
diff --git a/res/menu/user_user.xml b/app/src/main/res/menu/user_user.xml
index f66aa793..83465b3e 100644
--- a/res/menu/user_user.xml
+++ b/app/src/main/res/menu/user_user.xml
@@ -1,14 +1,14 @@
-<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:compat="http://schemas.android.com/apk/res-auto">
-
- <item
- android:id="@+id/menu_change_password"
- android:title="@string/admin.change_password"
- android:icon="?attr/password"
- compat:showAsAction="always|withText"/>
-
- <item
- android:id="@+id/menu_exit"
- android:title="@string/menu.exit"/>
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:compat="http://schemas.android.com/apk/res-auto">
+
+ <item
+ android:id="@+id/menu_change_password"
+ android:title="@string/admin.change_password"
+ android:icon="?attr/password"
+ compat:showAsAction="always|withText"/>
+
+ <item
+ android:id="@+id/menu_exit"
+ android:title="@string/menu.exit"/>
</menu> \ No newline at end of file
diff --git a/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 67b8ad59..62d19b3f 100644
--- a/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -1,557 +1,557 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-
- <string name="common.appname">DSub</string>
- <string name="common.ok">OK</string>
- <string name="common.save">Speichern</string>
- <string name="common.cancel">Abbrechen</string>
- <string name="common.play_now">Jetzt wiedergeben</string>
- <string name="common.play_shuffled">Zufallswiedergabe</string>
- <string name="common.play_next">Als nƤchstes abspielen</string>
- <string name="common.play_last">Als letztes abspielen</string>
- <string name="common.download">Cache</string>
- <string name="common.pin">Permanenter Cache</string>
- <string name="common.delete">Lƶschen</string>
- <string name="common.star">Favorit</string>
- <string name="common.unstar">Kein Favorit</string>
- <string name="common.info">Details</string>
- <string name="common.name">Name</string>
- <string name="common.comment">Kommentar</string>
- <string name="common.public">Ɩffentlich</string>
- <string name="common.play_external">Video abspielen</string>
- <string name="common.stream_external">Video streamen</string>
- <string name="common.confirm">BestƤtigen</string>
- <string name="common.confirm_message">Wollen Sie %2$s %1$s?</string>
- <string name="common.confirm_message_cache">den Cache</string>
- <string name="common.empty">Nicht gefunden</string>
- <string name="common.warning">Warnung</string>
-
- <string name="button_bar.home">Ɯbersicht</string>
- <string name="button_bar.browse">Bibliothek</string>
- <string name="button_bar.search">Suchen</string>
- <string name="button_bar.playlists">Wiedergabeliste</string>
- <string name="button_bar.now_playing">Aktuelle Wiedergabe</string>
- <string name="button_bar.podcasts">Podcasts</string>
- <string name="button_bar.bookmarks">Lesezeichen</string>
- <string name="button_bar.shares">Freigaben</string>
- <string name="button_bar.chat">Chat</string>
- <string name="button_bar.admin">Administration</string>
- <string name="button_bar.downloading">Downloads</string>
-
- <string name="main.welcome_title">Willkommen!</string>
- <string name="main.welcome_text">Willkommen zu DSub! Die App ist aktuell fĆ¼r den Subsonic-Demo-Server konfiguriert. Nachdem Sie Ihren eigenen Server
- aufgesetzt haben (verfĆ¼gbar unter <b>subsonic.org</b>) kƶnne Sie diesen unter <b>Einstellungen</b> konfigurieren.</string>
- <string name="main.about_title">Ɯber DSub</string>
- <string name="main.about_text">Autor: Scott Jackson
- \nEmail: dsub.android@gmail.com
- \nVersion: %1$s
- \nLokal gespeicherte Titel: %2$s
- \nGenutzter Speicher: %3$s von %4$s
- \nVerfĆ¼gbarer Speicher: %5$s von %6$s</string>
- <string name="main.faq_title">FAQ</string>
- <string name="main.faq_text">
- <![CDATA[
- <font color="red">Cache vs Permanenter Cache</font>:
- <br/>Von DSub heruntergelade Titel im Cache kƶnnen automatisch gelƶscht werden, um Platz fĆ¼r neue Titel zu schaffen. Titel im permanenten Cache werden dagegen nie automatisch gelƶscht.
- <p/><font color="red">ChromeCast funktioniert nicht</font>:
- <br/>Bitte stellen Sie sicher, das keine selbstsignierten Zertifikate verwendet werden, da Chromecast diese nicht akzeptiert.
- ]]>
- </string>
- <string name="main.select_server">WƤhle Server</string>
- <string name="main.shuffle">Zufallswiedergabe</string>
- <string name="main.offline">Gehe Offline</string>
- <string name="main.online">Gehe Online</string>
- <string name="main.settings">Einstellungen</string>
- <string name="main.albums_title">Album Liste</string>
- <string name="main.albums_newest">Neue Alben</string>
- <string name="main.albums_recent">Vor kurzem gespielt</string>
- <string name="main.albums_frequent">Am meisten gespielt</string>
- <string name="main.albums_highest">Top bewertet</string>
- <string name="main.albums_starred">Favoriten</string>
- <string name="main.albums_random">Zufall</string>
- <string name="main.albums_genres">Genres</string>
- <string name="main.albums_year">Jahrzehnt</string>
- <string name="main.songs_genres">@string/main.albums_genres</string>
- <string name="main.back_confirm">Zum beenden nochmal zurĆ¼ck drĆ¼cken</string>
- <string name="main.scan_complete">Durchsuchen des Server abgeschlossen</string>
-
- <string name="menu.search">Suche</string>
- <string name="menu.shuffle">Zufall</string>
- <string name="menu.refresh">Aktualisieren</string>
- <string name="menu.play">Abspielen</string>
- <string name="menu.play_last">Ans Ende der Wiedergabeliste</string>
- <string name="menu.exit">Beenden</string>
- <string name="menu.settings">Einstellungen</string>
- <string name="menu.help">Hilfe</string>
- <string name="menu.about">Ɯber</string>
- <string name="menu.add_playlist">Zur Wiedergabeliste hinzufĆ¼gen</string>
- <string name="menu.remove_playlist">Von Wiedergabeliste entfernen</string>
- <string name="menu.deleted_playlist">Lƶsche Wiedergabeliste %s</string>
- <string name="menu.deleted_playlist_error">Lƶschen der Wiedergabeliste %s fehlgeschlagen</string>
- <string name="menu.log">Sende Log</string>
- <string name="menu.set_timer">Setze Timer</string>
- <string name="menu.check_podcasts">PrĆ¼fe auf neue Episoden</string>
- <string name="menu.add_podcast">Podcast hinzufĆ¼gen</string>
- <string name="menu.keep_synced">Synchron halten</string>
- <string name="menu.stop_sync">Synchronisierung stoppen</string>
- <string name="menu.show_all">Zeige alle Medien</string>
- <string name="menu.show_artist">Zeige KĆ¼nstler</string>
- <string name="menu.share">Freigeben</string>
- <string name="menu.delete_cache">Lƶsche Cache</string>
- <string name="menu.cast">An Chromecast senden</string>
- <string name="menu.faq">FAQ</string>
- <string name="menu.add_user">Nutzer hinzufĆ¼gen</string>
- <string name="menu.rescan">Server neu durchsuchen</string>
- <string name="menu.rate">Setze Bewertung</string>
- <string name="menu.top_tracks">Last.FM Top Medien</string>
- <string name="menu.similar_artists">Ƅhnliche KĆ¼nstler</string>
- <string name="menu.show_missing">Zeige fehlende</string>
-
- <string name="playlist.label">Wiedergabelisten</string>
- <string name="playlist.update_info">Aktualisiere Informationen</string>
- <string name="playlist.updated_info">Aktualisiere Wiedergabeliste fĆ¼r %s</string>
- <string name="playlist.updated_info_error">Aktualisierung der Wiedergabeliste %s fehlgeschlagen</string>
- <string name="playlist.overwrite">Wiedergabeliste Ć¼berschreiben</string>
- <string name="playlist.add_to">Zur Wiedergabeliste hinzu</string>
- <string name="playlist.create_new">Neue Wiedergabeliste</string>
- <string name="playlist.delete">Lƶsche Wiedergabeliste</string>
-
- <string name="search.label">Suche</string>
- <string name="search.title">Suche</string>
- <string name="search.search">Zum Suche klicken</string>
- <string name="search.no_match">Nichts gefunden, bitte erneut versuchen</string>
- <string name="search.artists">KĆ¼nstler</string>
- <string name="search.albums">Alben</string>
- <string name="search.songs">Lieder</string>
- <string name="search.more">Zeige mehr</string>
-
- <string name="progress.wait">Bitte warten...</string>
-
- <string name="music_library.label">Medienbibliothek</string>
- <string name="music_library.label_offline">Offline Medien</string>
-
- <string name="select_album.select">Alle auswƤhlen</string>
- <string name="select_album.n_selected">%d Lieder ausgewƤhlt.</string>
- <string name="select_album.n_unselected">%d Lieder deselektiert.</string>
- <string name="select_album.more">Mehr</string>
- <string name="select_album.offline">Offline</string>
- <string name="select_album.searching">Suche...</string>
- <string name="select_album.no_sdcard">Fehler: Keine SD-Karte verfĆ¼gbar.</string>
- <string name="select_album.no_network">Warnung: Kein Netzwerk verfĆ¼gbar.</string>
- <string name="select_album.not_licensed">Server ist nicht lizensiert. Testzeitraum lƤuft ab in %d Tagen.</string>
- <string name="select_album.donate_dialog_message">Erhalte unbegrenzte Downloads durch eine Spende an Subsonic.</string>
- <string name="select_album.donate_dialog_now">Jetzt</string>
- <string name="select_album.donate_dialog_later">SpƤter</string>
- <string name="select_album.donate_dialog_0_trial_days_left">Testzeitraum ist vorbei</string>
-
- <string name="offline.sync_dialog_title">Offline Lieder warten auf Synchronisierung</string>
- <string name="offline.sync_dialog_message">Scrobble %1$d EintrƤge?
- \nƜbermittle %2$d neue Favoriten an den Server?
- </string>
- <string name="offline.sync_dialog_default">Nutze als Standardaktion</string>
- <string name="offline.sync_success">Erfolgreich %1$d Lieder synchronisiert</string>
- <string name="offline.sync_partial">Erfolgreich %1$d von %2$d Liedern synchronisiert</string>
- <string name="offline.sync_error">Synchronisierung der Lieder fehlgeschlagen</string>
-
- <string name="select_genre.blank">leer</string>
- <string name="select_genre.songs">%d Lieder</string>
- <string name="select_genre.albums">%d Alben</string>
-
- <string name="select_podcasts.error">Ein Fehler beim herunterladen dieses Podcast durch den Server. Der Podcast muss zuerst vom Server heruntergeladen werden.</string>
- <string name="select_podcasts.skipped">Der Podcast wurde noch nicht vom Server heruntergeladen. Der Podcast muss zuerst vom Server heruntergalden werden..</string>
- <string name="select_podcasts.initializing">Der Podcastkanal wird vom Server initialisiert. Bitte nach kurzer Wartezeit erneut laden.</string>
- <string name="select_podcasts.server_download">Auf den Server herunterladen</string>
- <string name="select_podcasts.server_delete">Lƶsche vom Server</string>
- <string name="select_podcasts.downloading">Lade %s auf den Server herunter</string>
- <string name="select_podcasts.refreshing">Der Server prĆ¼ft auf neue Podcasts.</string>
- <string name="select_podcasts.deleted">Lƶsche Podcast %s</string>
- <string name="select_podcasts.deleted_error">Fehler beim lƶschen des Podcast %s</string>
- <string name="select_podcasts.add_url">URL:</string>
- <string name="select_podcasts.created_error">Konnte Podcast nicht hinzufĆ¼gen</string>
- <string name="select_podcasts.invalid_podcast_channel">UngĆ¼ltiger Podcastkanal: %s</string>
- <string name="select_podcasts.delete">Lƶsche Podcast</string>
-
- <string name="download.empty">Wiedergabeliste ist leer</string>
- <string name="download.shuffle_loading">Wiedergabeliste wird gemischt...</string>
- <string name="download.playerstate_downloading">Downloade - %s</string>
- <string name="download.playerstate_buffering">Buffere</string>
- <string name="download.playerstate_playing_shuffle">Playing shuffle</string>
- <string name="download.menu_show_album">Zeige Album</string>
- <string name="download.menu_lyrics">Liedtext</string>
- <string name="download.menu_remove">Entferne aus Warteschlange</string>
- <string name="download.menu_remove_all">Alle entfernen</string>
- <string name="download.menu_screen_on">Bildschirm an</string>
- <string name="download.menu_shuffle">Mischen</string>
- <string name="download.menu_toggle">Umschalten</string>
- <string name="download.menu_save">Wiedergabeliste speichern</string>
- <string name="download.menu_shuffle_notification">Wiedergabeliste wurde gemischt</string>
- <string name="download.menu_remove_played_songs">Abgespielte Titel entfernen</string>
- <string name="download.playlist_title">Speichere Wiedergabeliste</string>
- <string name="download.playlist_name">Name der Wiedergabeliste eingeben:</string>
- <string name="download.playlist_saving">Sichere Wiedergabeliste \"%s\"...</string>
- <string name="download.playlist_done">Wiedergabeliste wurde erfolgreich gespeichert.</string>
- <string name="download.playlist_error">Fehler beim speichern der Wiedergabeliste, bitte spƤter erneut probieren.</string>
- <string name="download.repeat_off">Keine Wiederholung</string>
- <string name="download.repeat_all">Wiederhole alle</string>
- <string name="download.repeat_single">Aktuelles Lied wiederholen</string>
- <string name="download.jukebox_on">Fernbedienung aktiviert. Musik wird auf dem Computer abgespielt.</string>
- <string name="download.jukebox_off">Fernbedienung deaktiviert. Musik wird auf dem Telefon abgespielt.</string>
- <string name="download.jukebox_volume">LautstƤrke</string>
- <string name="download.jukebox_server_too_old">Fernbedienung wird nicht unterstĆ¼tzt. Aktualisierung des Subsonic-Servers notwendig.</string>
- <string name="download.jukebox_offline">Fernbedienung im Offline-Modus nicht verfĆ¼gbar.</string>
- <string name="download.jukebox_not_authorized">Fernbedienung ist nicht erlaubt. Bitte aktivieren Sie den Jukebox-Modus unter <b>Nutzer &gt; Einstellungen</b> auf Ihrem Subsonic-Server.</string>
- <string name="download.timer_length">Timer:</string>
- <string name="download.start_timer">Starte Timer</string>
- <string name="download.stop_timer">Stoppe Timer</string>
- <string name="download.need_download">Video muss zuerst heruntergeladen werden</string>
- <string name="download.no_streaming_player">Stream kann nicht wiedergegeben werden.</string>
- <string name="download.playing_out_of">Wiedergabe: %1$d/%2$d</string>
- <string name="download.save_bookmark_title">Setze Lesezeichen</string>
- <string name="download.save_bookmark">Lesezeichen gesetzt</string>
- <string name="download.save_bookmark_failed">Lesezeichen konnte nicht gesetzt werden.</string>
- <string name="download.downloading_title">Lade %1$d Lieder</string>
- <string name="download.downloading_summary">Aktuell: %1$s</string>
- <string name="download.downloading_summary_expanded">Aktuell: %1$s
- \nGeschƤtzte GrĆ¶ĆŸe: %2$s</string>
- <string name="download.failed_to_load">Laden fehlgeschlagen</string>
-
- <string name="sync.new_podcasts">Neuer Podcast verfĆ¼gbar</string>
- <string name="sync.new_playlists">Neu in Wiedergabeliste</string>
- <string name="sync.new_albums">Neues Album verfĆ¼gbar</string>
- <string name="sync.new_starred">Neue Favoriten verfĆ¼gbar</string>
-
- <string name="starring_content_starred">Favorit \"%s\"</string>
- <string name="starring_content_unstarred">Kein Favorit \"%s\"</string>
- <string name="starring_content_error">Aktualisierung von \"%s\" fehlgeschlagen, bitte spƤter erneut probieren.</string>
-
- <string name="playlist_error">Konnte Liste der Wiedergabelisten nicht herunterladen</string>
- <string name="updated_playlist">%1$s Lieder zu \"%2$s\" hinzugefĆ¼gt</string>
- <string name="updated_playlist_error">Aktualisierung \"%s\" fehlgeschlagen, bitte spƤter erneut probieren.</string>
- <string name="removed_playlist">%1$s entfernt aus \"%2$s\"</string>
-
- <string name="bookmark.delete">Lƶsche Lesezeichen</string>
- <string name="bookmark.delete_title">Lƶsche Lesezeichen fĆ¼r</string>
- <string name="bookmark.deleted">Lesezeichen fĆ¼r \"%s\" gelƶscht</string>
- <string name="bookmark.deleted_error">Lƶschen des Lesezeichen fĆ¼r \"%s\" ist fehlgeschlagen</string>
- <string name="bookmark.details_title">Lesezeichendetails</string>
- <string name="bookmark.details">Lied: %1$s
- \nPosition: %2$s
- \nErzeugt: %3$s
- \nZuletzt aktualisiert: %4$s
- \nKommentar: %5$s</string>
- <string name="bookmark.resume_title">Wiedergabe fortsetzen?</string>
- <string name="bookmark.resume">\'%1$s\' fortsetzen bei %2$s</string>
- <string name="bookmark.action_resume">Fortsetzen</string>
- <string name="bookmark.action_start_over">Neu beginnen</string>
-
- <string name="rating.title">Bewerte \"%s\"</string>
- <string name="rating.set_rating">Setze Bewertung fĆ¼r \"%s\"</string>
- <string name="rating.set_rating_failed">Konnte Bewertung fĆ¼r \"%s\" nicht setzen</string>
- <string name="rating.remove_rating">Bewertung fĆ¼r \"%s\" entfernt</string>
- <string name="rating.remove_rating_failed">Konnte Bewertung fĆ¼r \"%s\" nicht entfernen</string>
-
- <string name="song_details.error">Fehler</string>
- <string name="song_details.skipped">Ɯberspringen</string>
- <string name="song_details.downloading">Wird geladen</string>
-
- <string name="lyrics.nomatch">Kein Liedtext gefunden</string>
-
- <string name="error.label">Fehler</string>
-
- <string name="settings.title">DSub Einstellungen</string>
- <string name="settings.test_connection_title">Teste Verbindung</string>
- <string name="settings.servers_add">Server hinzufĆ¼gen</string>
- <string name="settings.servers_remove">Server entfernen</string>
- <string name="settings.servers_title">Server</string>
- <string name="settings.server_unused">ungenutzt</string>
- <string name="settings.server_name">Name</string>
- <string name="settings.server_address">Server Adresse</string>
- <string name="settings.server_local_network_ssid" >Lokale Netzwerk-SSID</string>
- <string name="settings.server_local_network_ssid_hint">Aktuelle SSID: %s</string>
- <string name="settings.server_internal_address">Lokale Netzwerkadresse</string>
- <string name="settings.server_username">Nutzername</string>
- <string name="settings.server_password">Passwort</string>
- <string name="settings.server_open_browser">Im Browser ƶffnen</string>
- <string name="settings.server_sync_summary">Synchronisierung fĆ¼r diesen Server de-/aktivieren</string>
- <string name="settings.server_sync">Synchronisierung aktivieren</string>
- <string name="settings.cache_title">Musik Cache</string>
- <string name="settings.preload_wifi">Im Vorraus zu laden (Wifi)</string>
- <string name="settings.preload_mobile">Im Vorraus zu laden (Mobil)</string>
- <string name="settings.cache_size">GrĆ¶ĆŸe des Cache (MB)</string>
- <string name="settings.cache_location">Position des Cache</string>
- <string name="settings.cache_location_error">UngĆ¼ltiger Pfad zum Cache. Nutze Standard.</string>
- <string name="settings.cache_location_reset">Der gesetzte Cachepfad ist nicht (mehr) beschreibbar. Durch die Aktualisierung des Telefon-OS auf KitKat 4.4 wurde der Zugriff auf die SD-Karte durch Apps eingeschrƤnkt auf spezielle Pfade. Der von DSub verwendete Pfad wurde entsprechend korrigiert. Um den alten Cache zu lƶschen, mĆ¼ssen Sie die SD-Karte an einem Computer anschliessend und den Ordner manuell lƶschen.</string>
- <string name="settings.cache_clear">Lƶsche Cache</string>
- <string name="settings.cache_clear_complete">Cache wurde geleert</string>
- <string name="settings.testing_connection">Teste Verbindung...</string>
- <string name="settings.testing_ok">Verbindung ist OK</string>
- <string name="settings.testing_unlicensed">Verbindung ist OK. Server ist nicht lizensiert.</string>
- <string name="settings.connection_failure">Verbindung fehlgeschlagen.</string>
- <string name="settings.invalid_url">Bitte geben Sie eine gĆ¼ltige URL an.</string>
- <string name="settings.invalid_username">Bitte gĆ¼ltigen Nutzernamen angeben (keine fĆ¼hrenden Leerzeichen).</string>
- <string name="settings.appearance_title">Erscheinung</string>
- <string name="settings.theme_title">Theme</string>
- <string name="settings.theme_light">Hell</string>
- <string name="settings.theme_dark">Dunkel</string>
- <string name="settings.theme_black">Schwarz</string>
- <string name="settings.theme_holo">Holo</string>
- <string name="settings.theme_fullscreen">Vollbildschirm</string>
- <string name="settings.theme_fullscreen_summary">Verstecke soviel der Benutzerschnittstelle wie von Android ermƶglicht.</string>
- <string name="settings.track_title">Titelnummer anzeigen</string>
- <string name="settings.track_summary">Zeige Titelnummer vor dem Titel</string>
- <string name="settings.custom_sort">Sortiere nach Jahr</string>
- <string name="settings.custom_sort_summary">Sortiere Alben nach Jahr, oder alphabetisch.</string>
- <string name="settings.network_title">Netzwerk</string>
- <string name="settings.max_bitrate_wifi">Max Audio Bitrate - Wi-Fi</string>
- <string name="settings.max_bitrate_mobile">Max Audio Bitrate - Mobil</string>
- <string name="settings.max_video_bitrate_wifi">Max Video Bitrate - Wi-Fi</string>
- <string name="settings.max_video_bitrate_mobile">Max Video Bitrate - Mobil</string>
- <string name="settings.max_bitrate_unlimited">Unbegrenzt</string>
- <string name="settings.wifi_required_title">Nur Wi-Fi streaming</string>
- <string name="settings.wifi_required_summary">Medien nur streamen, wenn mit Wi-Fi verbunden</string>
- <string name="settings.network_timeout_title">Netzwerk Timeout</string>
- <string name="settings.network_timeout_10000">10 Sekunden</string>
- <string name="settings.network_timeout_15000">15 Sekunden</string>
- <string name="settings.network_timeout_30000">30 Sekunden</string>
- <string name="settings.network_timeout_45000">45 Sekunden</string>
- <string name="settings.network_timeout_60000">60 Sekunden</string>
- <string name="settings.preload_0">Kein Lied</string>
- <string name="settings.preload_1">1 Lied</string>
- <string name="settings.preload_2">2 Lieder</string>
- <string name="settings.preload_3">3 Lieder</string>
- <string name="settings.preload_5">5 Lieder</string>
- <string name="settings.preload_10">10 Lieder</string>
- <string name="settings.preload_unlimited">unbegrenzt</string>
- <string name="settings.clear_search_history">Lƶsche Suchanfragen</string>
- <string name="settings.search_history_cleared">Suchanfragen gelƶscht</string>
- <string name="settings.other_title">Sonstige Einstellungen</string>
- <string name="settings.scrobble_title">Scrobble zu Last.fm</string>
- <string name="settings.scrobble_summary">Ihr Benutzername und Passwort fĆ¼r Last.fm muss im Subsonic-Server konfiguriert sein</string>
- <string name="settings.hide_media_title">Verstecke vor anderen</string>
- <string name="settings.hide_media_summary">Musikdateien vor anderen Apps verstecken.</string>
- <string name="settings.hide_media_toast">Wird aktiv bei der nƤchsten Mediensuche durch Android.</string>
- <string name="settings.media_button_title">Medientasten</string>
- <string name="settings.media_button_summary">Verwende Medientasten des Telefon, Freisprecheinrichtung und Bluetooth</string>
- <string name="settings.screen_lit_title">Bildschirm aktiv halten</string>
- <string name="settings.screen_lit_summary">Ein aktiver Bildschirm verbessert die Downloadgeschwindigkeit.</string>
- <string name="settings.playlist_title">Wiedergeben</string>
- <string name="settings.playlist_random_size_title">LƤnge der Zufallswiedergabeliste</string>
- <string name="settings.sleep_timer_title">Einschlaftimer</string>
- <string name="settings.sleep_timer_duration_title">Einschlafdauer</string>
- <string name="settings.sleep_timer_off">Aus</string>
- <string name="settings.sleep_timer_on">An</string>
- <string name="settings.sleep_timer_always_on">Immer An</string>
- <string name="settings.temp_loss_title">Verhalten bei Benachrichtigungen</string>
- <string name="settings.temp_loss_pause">Immer pausieren</string>
- <string name="settings.temp_loss_pause_lower">Pausiere und LautstƤrke bei Bedarf veringern</string>
- <string name="settings.temp_loss_lower">LautstƤrke veringern</string>
- <string name="settings.temp_loss_nothing">Nichts machen</string>
- <string name="settings.disconnect_pause_title">Verhalten bei Verbindungsverlust</string>
- <string name="settings.disconnect_pause_both">Immer pausieren</string>
- <string name="settings.disconnect_pause_neither">Nichts machen</string>
- <string name="settings.persistent_title">Dauerhafte Benachrichtigung</string>
- <string name="settings.persistent_summary">Zeige die Benachrichtigung auch nach pausieren der Wiedergabe. Zum entfernen Stop auswƤhlen.</string>
- <string name="settings.gapless_playback">LĆ¼ckenlose Wiedergabe</string>
- <string name="settings.gapless_playback_summary">Das Galaxy S3 scheint Probleme seit der EinfĆ¼hrung der lĆ¼ckenlosen Wiedergabe zu haben. Zur Behebung schalten Sie dies ab.</string>
- <string name="settings.chat_refresh">Chat Aktualisierungsrate (Sekunden)</string>
- <string name="settings.chat_enabled">Chat aktiv</string>
- <string name="settings.chat_enabled_summary">Chat im SeitenmenĆ¼ anzeigen</string>
- <string name="settings.video_title">Video</string>
- <string name="settings.video_player">Video Player</string>
- <string name="settings.video_raw">Raw (benƶtigt Subsonic 4.8+)</string>
- <string name="settings.video_hls">HTTP Live Stream (HLS) (benƶtigt Subsonic 4.8+)</string>
- <string name="settings.video_transcode">Direkte Transkodierung (benƶtigt video -> mp4 oder Ƥhnliche Einstellungen auf dem Server)</string>
- <string name="settings.video_flash">Flash (benƶtigt Plugin)</string>
- <string name="settings.cache_screen_title">Cache/Netzwerk</string>
- <string name="settings.playback_title">Wiedergabe</string>
- <string name="settings.hide_widget_title">Verstecke Widget</string>
- <string name="settings.hide_widget_summary">Verstecke Widget nach dem verlassen der App</string>
- <string name="settings.podcasts_enabled">Podcasts aktiviert</string>
- <string name="settings.podcasts_enabled_summary">Podcast im SeitenmenĆ¼ anzeigen</string>
- <string name="settings.bookmarks_enabled">Lesezeichen aktiviert</string>
- <string name="settings.bookmarks_enabled_summary">Lesezeichen im SeitenmenĆ¼ anzeigen</string>
- <string name="settings.shares_enabled">Freigaben aktiviert</string>
- <string name="settings.shares_enabled_summary">Freigabe im SeitenmenĆ¼ anzeigen</string>
- <string name="settings.sync_title">Synchronisierung</string>
- <string name="settings.sync_enabled">Synchronisierung aktiv</string>
- <string name="settings.sync_enabled_summary">Podcast regelmƤƟig auf Ƅnderungen prĆ¼fen</string>
- <string name="settings.sync_interval">Synchronisierungsintervall</string>
- <string name="settings.sync_interval_15">15 Minutes</string>
- <string name="settings.sync_interval_30">30 Minutes</string>
- <string name="settings.sync_interval_60">1 Stunde</string>
- <string name="settings.sync_interval_120">2 Stunden</string>
- <string name="settings.sync_interval_240">4 Stunden</string>
- <string name="settings.sync_interval_360">6 Stunden</string>
- <string name="settings.sync_interval_720">12 Stunden</string>
- <string name="settings.sync_interval_1440">tƤglich</string>
- <string name="settings.sync_wifi">Nur per Wifi synchronisieren</string>
- <string name="settings.sync_wifi_summary">Nur bei Verwendung von Wifi synchronisieren</string>
- <string name="settings.sync_most_recent">Synchronisiere neu hinzugefĆ¼gte Lieder</string>
- <string name="settings.sync_most_recent_summary">Lade neu hinzugefĆ¼gte Alben automatisch herunter</string>
- <string name="settings.sync_starred">Synchronisiere Favoriten</string>
- <string name="settings.sync_starred_summary">Synchronisiere favorisierte Lieder, Alben und KĆ¼nstler automatisch</string>
- <string name="settings.sync_notification">Benachrichtigung nach Synchronisierung</string>
- <string name="settings.sync_notification_summary">Zeige eine Benachrichtigung nachdem neue Medien synchronisiert wurden.</string>
- <string name="settings.menu_options.title">Optionale MenĆ¼ EintrƤge</string>
- <string name="settings.menu_options.play_next_summary">Zeige \"Als nƤchstes abspielen\" im MenĆ¼</string>
- <string name="settings.menu_options.play_last_summary">Zeige \"Als letztes abspielen\" im MenĆ¼</string>
- <string name="settings.menu_options.star_summary">Zeige Favorit im MenĆ¼</string>
- <string name="settings.menu_options.shared_summary">Zeige \"Freigeben\" im MenĆ¼</string>
- <string name="settings.menu_options.rate_summary">Zeige \"Bewerten\" im MenĆ¼</string>
- <string name="settings.browse_by_tags">Tags nutzen</string>
- <string name="settings.browse_by_tags_summary">Tags statt Ordner verwenden. Benƶtigt Subsonic 4.7+</string>
- <string name="settings.override_system_language">In Englisch anzeigen</string>
- <string name="settings.override_system_language_summary">Verwende Englisch anstatt Deutsch fĆ¼r DSub. Benƶtigt einen Neustart der App.</string>
- <string name="settings.drawer_items_title">SeitenmenĆ¼</string>
- <string name="settings.play_now_after">Jetzt wiedergeben bis zum Listenende</string>
- <string name="settings.play_now_after_summary">\"Jetzt wiedergeben\" im KontextmenĆ¼ spielt das ausgewƤhlte Lied und alle in der Liste nachfolgenden Lieder ab (wie in der Web-Schnittstelle des Subsonic-Server)</string>
- <string name="settings.large_album_art">GroƟe Cover anzeigen</string>
- <string name="settings.large_album_art_summary">Verwende groƟe Cover zur Anzeige der Alben anstatt einer Liste</string>
- <string name="settings.admin_enabled">Administration aktiviert</string>
- <string name="settings.admin_enabled_summary">Administration im SeitenmenĆ¼ anzeigen</string>
- <string name="settings.replay_gain">WiedergabeverstƤrkung</string>
- <string name="settings.replay_gain_summary">Gibt an, ob die WiedergabeverstƤrkung anhand von Tags erfolgen soll.</string>
- <string name="settings.replay_gain_type">Anhand der Tags</string>
- <string name="settings.replay_gain_type.smart">Automatische Erkennung</string>
- <string name="settings.replay_gain_type.album">Album Tag</string>
- <string name="settings.replay_gain_type.track">Track Tag</string>
- <string name="settings.replay_gain_bump">WiedergabevorverstƤrkung</string>
- <string name="settings.replay_gain_untagged">Lieder ohne WiedergabeverstƤrkung</string>
-
- <string name="shuffle.title">Mischen von</string>
- <string name="shuffle.startYear">Startjahr:</string>
- <string name="shuffle.endYear">Endjahr:</string>
- <string name="shuffle.genre">Genre:</string>
- <string name="shuffle.pick_genre">WƤhle ein Genre</string>
-
- <string name="share.info">EigentĆ¼mer: %1$s
- \nBeschreibung: %2$s
- \nURL: %3$s
- \nErzeugt: %4$s
- \nZuletzt besucht: %5$s
- \nAblauf: %6$s
- \nBesuchszƤhler: %7$s
-
- </string>
- <string name="share.expires">Ablauf: %s</string>
- <string name="share.expires_never">nie</string>
- <string name="share.deleted">Lƶsche Freigabe %s</string>
- <string name="share.deleted_error">Lƶschen der Freigabe %s fehlgeschlagen</string>
- <string name="share.no_expiration">Kein Ablauf</string>
- <string name="share.expiration">Ablauf:</string>
- <string name="share.updated_info">Aktualisiere Informationen der Freigabe %s</string>
- <string name="share.updated_info_error">Aktualisierung der Freigabe %s fehlgeschlagen</string>
- <string name="share.via">Teile via</string>
- <string name="share.delete">Lƶsche Freigabe</string>
-
- <string name="admin.add_user_username">Nutzername:</string>
- <string name="admin.add_user_email">Email:</string>
- <string name="admin.add_user_password">Passwort:</string>
- <string name="admin.create_user_success">Neuen Nutzer wurde erfolgreich angelegt</string>
- <string name="admin.create_user_error">Neuer Nutzer konnte nicht erzeugt werden</string>
- <string name="admin.change_username_invalid">Bitte gĆ¼ltigen Nutzernamen angeben</string>
- <string name="admin.update_permissions">Aktualisiere Berechtigungen</string>
- <string name="admin.update_permissions_success">Berechtigungen fĆ¼r %1$s erfolgreich aktualisiert</string>
- <string name="admin.update_permissions_error">Ƅnderung der Berechtigungen fĆ¼r %1$s fehlgeschlagen</string>
- <string name="admin.change_email">Email Ƥndern</string>
- <string name="admin.change_email_success">Email fĆ¼r %1$s wurde geƤndert</string>
- <string name="admin.change_email_error">Konnte Email fĆ¼r %1$s nicht Ƥndern</string>
- <string name="admin.change_email_label">Neue Email:</string>
- <string name="admin.change_email_invalid">Bitte gĆ¼ltige Email angeben</string>
- <string name="admin.change_password">Passwort Ƥndern</string>
- <string name="admin.change_password_success">Passwort fĆ¼r %1$s erfolgreich geƤndert</string>
- <string name="admin.change_password_error">PasswortƤnderung fĆ¼r %1$s ist fehlgeschlagen</string>
- <string name="admin.change_password_label">Neues Passwort:</string>
- <string name="admin.change_password_invalid">Bitte ein gĆ¼ltiges Passwort eingeben</string>
- <string name="admin.delete_user">Nutzer lƶschen</string>
- <string name="admin.delete_user_success">Nutzer %1$s erfolgreich gelƶscht</string>
- <string name="admin.delete_user_error">Nutzer %1$s konnte nicht gelƶscht werden</string>
- <string name="admin.confirm_password">Passwort bestƤtigen</string>
- <string name="admin.confirm_password_bad">Eingegebenes Passwort ist falsch</string>
-
- <string name="admin.scrobblingEnabled">Erlaube Scrobbeln</string>
- <string name="admin.role.admin">Administrator</string>
- <string name="admin.role.settings">Einstellungen Ƥndern</string>
- <string name="admin.role.download">Dateien herunterladen</string>
- <string name="admin.role.upload">Hochladen zum Server</string>
- <string name="admin.role.coverArt">Cover Ƥndern</string>
- <string name="admin.role.comment">Kommentare hinzufĆ¼gen</string>
- <string name="admin.role.podcast">Podcasts verwalten</string>
- <string name="admin.role.stream">Musik streamen</string>
- <string name="admin.role.jukebox">Jukebox kontrollieren</string>
- <string name="admin.role.share">Freigaben verwalten</string>
- <string name="admin.role.lastfm">Last.FM nutzen</string>
-
- <string name="music_service.retry">Ein Netzwerkfehler ist aufgetreten. Versuch %1$d von %2$d.</string>
-
- <string name="background_task.wait">Bitte warten...</string>
- <string name="background_task.loading">Lade.</string>
- <string name="background_task.no_network">Diese Programm benƶtigt Netzwerkzugriff. Bitte schalten Sie Wi-Fi oder Mobiles Netzwerk ein.</string>
- <string name="background_task.network_error">Ein Netzwerkfehler ist aufgetreten. Bitte prĆ¼fen Sie die Serveradresse oder versuchen Sie es spƤter nochmal.</string>
- <string name="background_task.not_found">Quelle wurde nicht gefunden. Bitte prĆ¼fen Sie die Serveradresse.</string>
- <string name="background_task.parse_error">Ein Fehler ist bei der Kommunikation mit dem Server aufgetreten. Bitte prĆ¼fen Sie die Serveradresse und stellen Sie sicher, das Sie der Server mit einem Webbrowser erreichen.</string>
-
- <string name="service.connecting">Kontaktiere Server, bitte warten.</string>
-
- <string name="parser.upgrade_client">Inkompatible Versionen. Aktualisierung von DSub erforderlich.</string>
- <string name="parser.upgrade_server">Inkompatible Versionen. Aktualisierung des Subsonic Server erforderlich.</string>
- <string name="parser.not_authenticated">Benutzername oder/und Passwort falsch.</string>
- <string name="parser.not_authorized">Nicht authorisiert. Bitte prĆ¼fen Sie die Einstellungen im Subsonic-Server.</string>
- <string name="parser.artist_count">%d KĆ¼nstler.</string>
- <string name="parser.server_error">Serverfehler: %s</string>
- <string name="parser.scan_count">%d EintrƤge gefunden</string>
-
- <string name="select_artist.refresh">Aktualisieren</string>
- <string name="select_artist.folder">WƤhle Ordner</string>
- <string name="select_artist.all_folders">Alle Ordner</string>
-
- <string name="equalizer.label">Equalizer</string>
- <string name="equalizer.enabled">aktiv</string>
- <string name="equalizer.preset">WƤhle Vorgabe</string>
- <string name="equalizer.bass_booster">BassverstƤrkung</string>
- <string name="equalizer.voice_booster">SprachverstƤrkung</string>
- <string name="equalizer.db_size">%d dB</string>
- <string name="equalizer.bass_size">%d mille</string>
-
- <string name="widget.initial_text">Zur Musikauswahl berĆ¼hren</string>
- <string name="widget.sdcard_busy">SD-Karte nicht verfĆ¼gbar</string>
- <string name="widget.sdcard_missing">Keine SD-Karte</string>
-
- <string name="changelog_full_title">Ƅnderungen</string>
- <string name="changelog_title">Was ist Neu</string>
- <string name="changelog_ok_button">OK</string>
- <string name="changelog_show_full">Mehrā€¦</string>
-
- <string name="chat.send_a_message">Nachricht senden</string>
-
- <string name="changelog_version_format" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">Version <xliff:g id="version_name">%s</xliff:g></string>
-
- <string name="tasker.start_playing">Starte Wiedergabe</string>
- <string name="tasker.start_playing_shuffled">Starte Zufallswiedergabe</string>
- <string name="tasker.start_playing_title">Tasker -> Starte DSub</string>
- <string name="tasker.edit_shuffle_mode">Starte im Zufallsmodus: </string>
- <string name="tasker.edit_shuffle_start_year">Startjahr:</string>
- <string name="tasker.edit_shuffle_end_year">Endjahr:</string>
- <string name="tasker.edit_shuffle_genre">Genre:</string>
- <string name="tasker.edit_server_offline">Zwischen On-/Offline-Modus wechseln: </string>
- <string name="tasker.edit_do_nothing">Nichts tun</string>
-
- <plurals name="select_album_n_songs">
- <item quantity="zero">Keine Lieder</item>
- <item quantity="one">Ein Lied</item>
- <item quantity="other">%d Lieder</item>
- </plurals>
- <plurals name="select_album_n_songs_downloading">
- <item quantity="one">Ein Lied wird heruntergeladen.</item>
- <item quantity="other">%d Lieder werden heruntergeladen.</item>
- </plurals>
- <plurals name="select_album_n_songs_added">
- <item quantity="one">Ein Lied zur Abspielliste hinzugefĆ¼gt.</item>
- <item quantity="other">%d Lieder zur Abspielliste hinzugefĆ¼gt.</item>
- </plurals>
- <plurals name="select_album_donate_dialog_n_trial_days_left">
- <item quantity="one">Noch ein Tag bis zum Ablauf des Testzeitraums.</item>
- <item quantity="other">%d Tage bis zum Ablauf des Testzeitraums.</item>
- </plurals>
-
-</resources>
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <string name="common.appname">DSub</string>
+ <string name="common.ok">OK</string>
+ <string name="common.save">Speichern</string>
+ <string name="common.cancel">Abbrechen</string>
+ <string name="common.play_now">Jetzt wiedergeben</string>
+ <string name="common.play_shuffled">Zufallswiedergabe</string>
+ <string name="common.play_next">Als nƤchstes abspielen</string>
+ <string name="common.play_last">Als letztes abspielen</string>
+ <string name="common.download">Cache</string>
+ <string name="common.pin">Permanenter Cache</string>
+ <string name="common.delete">Lƶschen</string>
+ <string name="common.star">Favorit</string>
+ <string name="common.unstar">Kein Favorit</string>
+ <string name="common.info">Details</string>
+ <string name="common.name">Name</string>
+ <string name="common.comment">Kommentar</string>
+ <string name="common.public">Ɩffentlich</string>
+ <string name="common.play_external">Video abspielen</string>
+ <string name="common.stream_external">Video streamen</string>
+ <string name="common.confirm">BestƤtigen</string>
+ <string name="common.confirm_message">Wollen Sie %2$s %1$s?</string>
+ <string name="common.confirm_message_cache">den Cache</string>
+ <string name="common.empty">Nicht gefunden</string>
+ <string name="common.warning">Warnung</string>
+
+ <string name="button_bar.home">Ɯbersicht</string>
+ <string name="button_bar.browse">Bibliothek</string>
+ <string name="button_bar.search">Suchen</string>
+ <string name="button_bar.playlists">Wiedergabeliste</string>
+ <string name="button_bar.now_playing">Aktuelle Wiedergabe</string>
+ <string name="button_bar.podcasts">Podcasts</string>
+ <string name="button_bar.bookmarks">Lesezeichen</string>
+ <string name="button_bar.shares">Freigaben</string>
+ <string name="button_bar.chat">Chat</string>
+ <string name="button_bar.admin">Administration</string>
+ <string name="button_bar.downloading">Downloads</string>
+
+ <string name="main.welcome_title">Willkommen!</string>
+ <string name="main.welcome_text">Willkommen zu DSub! Die App ist aktuell fĆ¼r den Subsonic-Demo-Server konfiguriert. Nachdem Sie Ihren eigenen Server
+ aufgesetzt haben (verfĆ¼gbar unter <b>subsonic.org</b>) kƶnne Sie diesen unter <b>Einstellungen</b> konfigurieren.</string>
+ <string name="main.about_title">Ɯber DSub</string>
+ <string name="main.about_text">Autor: Scott Jackson
+ \nEmail: dsub.android@gmail.com
+ \nVersion: %1$s
+ \nLokal gespeicherte Titel: %2$s
+ \nGenutzter Speicher: %3$s von %4$s
+ \nVerfĆ¼gbarer Speicher: %5$s von %6$s</string>
+ <string name="main.faq_title">FAQ</string>
+ <string name="main.faq_text">
+ <![CDATA[
+ <font color="red">Cache vs Permanenter Cache</font>:
+ <br/>Von DSub heruntergelade Titel im Cache kƶnnen automatisch gelƶscht werden, um Platz fĆ¼r neue Titel zu schaffen. Titel im permanenten Cache werden dagegen nie automatisch gelƶscht.
+ <p/><font color="red">ChromeCast funktioniert nicht</font>:
+ <br/>Bitte stellen Sie sicher, das keine selbstsignierten Zertifikate verwendet werden, da Chromecast diese nicht akzeptiert.
+ ]]>
+ </string>
+ <string name="main.select_server">WƤhle Server</string>
+ <string name="main.shuffle">Zufallswiedergabe</string>
+ <string name="main.offline">Gehe Offline</string>
+ <string name="main.online">Gehe Online</string>
+ <string name="main.settings">Einstellungen</string>
+ <string name="main.albums_title">Album Liste</string>
+ <string name="main.albums_newest">Neue Alben</string>
+ <string name="main.albums_recent">Vor kurzem gespielt</string>
+ <string name="main.albums_frequent">Am meisten gespielt</string>
+ <string name="main.albums_highest">Top bewertet</string>
+ <string name="main.albums_starred">Favoriten</string>
+ <string name="main.albums_random">Zufall</string>
+ <string name="main.albums_genres">Genres</string>
+ <string name="main.albums_year">Jahrzehnt</string>
+ <string name="main.songs_genres">@string/main.albums_genres</string>
+ <string name="main.back_confirm">Zum beenden nochmal zurĆ¼ck drĆ¼cken</string>
+ <string name="main.scan_complete">Durchsuchen des Server abgeschlossen</string>
+
+ <string name="menu.search">Suche</string>
+ <string name="menu.shuffle">Zufall</string>
+ <string name="menu.refresh">Aktualisieren</string>
+ <string name="menu.play">Abspielen</string>
+ <string name="menu.play_last">Ans Ende der Wiedergabeliste</string>
+ <string name="menu.exit">Beenden</string>
+ <string name="menu.settings">Einstellungen</string>
+ <string name="menu.help">Hilfe</string>
+ <string name="menu.about">Ɯber</string>
+ <string name="menu.add_playlist">Zur Wiedergabeliste hinzufĆ¼gen</string>
+ <string name="menu.remove_playlist">Von Wiedergabeliste entfernen</string>
+ <string name="menu.deleted_playlist">Lƶsche Wiedergabeliste %s</string>
+ <string name="menu.deleted_playlist_error">Lƶschen der Wiedergabeliste %s fehlgeschlagen</string>
+ <string name="menu.log">Sende Log</string>
+ <string name="menu.set_timer">Setze Timer</string>
+ <string name="menu.check_podcasts">PrĆ¼fe auf neue Episoden</string>
+ <string name="menu.add_podcast">Podcast hinzufĆ¼gen</string>
+ <string name="menu.keep_synced">Synchron halten</string>
+ <string name="menu.stop_sync">Synchronisierung stoppen</string>
+ <string name="menu.show_all">Zeige alle Medien</string>
+ <string name="menu.show_artist">Zeige KĆ¼nstler</string>
+ <string name="menu.share">Freigeben</string>
+ <string name="menu.delete_cache">Lƶsche Cache</string>
+ <string name="menu.cast">An Chromecast senden</string>
+ <string name="menu.faq">FAQ</string>
+ <string name="menu.add_user">Nutzer hinzufĆ¼gen</string>
+ <string name="menu.rescan">Server neu durchsuchen</string>
+ <string name="menu.rate">Setze Bewertung</string>
+ <string name="menu.top_tracks">Last.FM Top Medien</string>
+ <string name="menu.similar_artists">Ƅhnliche KĆ¼nstler</string>
+ <string name="menu.show_missing">Zeige fehlende</string>
+
+ <string name="playlist.label">Wiedergabelisten</string>
+ <string name="playlist.update_info">Aktualisiere Informationen</string>
+ <string name="playlist.updated_info">Aktualisiere Wiedergabeliste fĆ¼r %s</string>
+ <string name="playlist.updated_info_error">Aktualisierung der Wiedergabeliste %s fehlgeschlagen</string>
+ <string name="playlist.overwrite">Wiedergabeliste Ć¼berschreiben</string>
+ <string name="playlist.add_to">Zur Wiedergabeliste hinzu</string>
+ <string name="playlist.create_new">Neue Wiedergabeliste</string>
+ <string name="playlist.delete">Lƶsche Wiedergabeliste</string>
+
+ <string name="search.label">Suche</string>
+ <string name="search.title">Suche</string>
+ <string name="search.search">Zum Suche klicken</string>
+ <string name="search.no_match">Nichts gefunden, bitte erneut versuchen</string>
+ <string name="search.artists">KĆ¼nstler</string>
+ <string name="search.albums">Alben</string>
+ <string name="search.songs">Lieder</string>
+ <string name="search.more">Zeige mehr</string>
+
+ <string name="progress.wait">Bitte warten...</string>
+
+ <string name="music_library.label">Medienbibliothek</string>
+ <string name="music_library.label_offline">Offline Medien</string>
+
+ <string name="select_album.select">Alle auswƤhlen</string>
+ <string name="select_album.n_selected">%d Lieder ausgewƤhlt.</string>
+ <string name="select_album.n_unselected">%d Lieder deselektiert.</string>
+ <string name="select_album.more">Mehr</string>
+ <string name="select_album.offline">Offline</string>
+ <string name="select_album.searching">Suche...</string>
+ <string name="select_album.no_sdcard">Fehler: Keine SD-Karte verfĆ¼gbar.</string>
+ <string name="select_album.no_network">Warnung: Kein Netzwerk verfĆ¼gbar.</string>
+ <string name="select_album.not_licensed">Server ist nicht lizensiert. Testzeitraum lƤuft ab in %d Tagen.</string>
+ <string name="select_album.donate_dialog_message">Erhalte unbegrenzte Downloads durch eine Spende an Subsonic.</string>
+ <string name="select_album.donate_dialog_now">Jetzt</string>
+ <string name="select_album.donate_dialog_later">SpƤter</string>
+ <string name="select_album.donate_dialog_0_trial_days_left">Testzeitraum ist vorbei</string>
+
+ <string name="offline.sync_dialog_title">Offline Lieder warten auf Synchronisierung</string>
+ <string name="offline.sync_dialog_message">Scrobble %1$d EintrƤge?
+ \nƜbermittle %2$d neue Favoriten an den Server?
+ </string>
+ <string name="offline.sync_dialog_default">Nutze als Standardaktion</string>
+ <string name="offline.sync_success">Erfolgreich %1$d Lieder synchronisiert</string>
+ <string name="offline.sync_partial">Erfolgreich %1$d von %2$d Liedern synchronisiert</string>
+ <string name="offline.sync_error">Synchronisierung der Lieder fehlgeschlagen</string>
+
+ <string name="select_genre.blank">leer</string>
+ <string name="select_genre.songs">%d Lieder</string>
+ <string name="select_genre.albums">%d Alben</string>
+
+ <string name="select_podcasts.error">Ein Fehler beim herunterladen dieses Podcast durch den Server. Der Podcast muss zuerst vom Server heruntergeladen werden.</string>
+ <string name="select_podcasts.skipped">Der Podcast wurde noch nicht vom Server heruntergeladen. Der Podcast muss zuerst vom Server heruntergalden werden..</string>
+ <string name="select_podcasts.initializing">Der Podcastkanal wird vom Server initialisiert. Bitte nach kurzer Wartezeit erneut laden.</string>
+ <string name="select_podcasts.server_download">Auf den Server herunterladen</string>
+ <string name="select_podcasts.server_delete">Lƶsche vom Server</string>
+ <string name="select_podcasts.downloading">Lade %s auf den Server herunter</string>
+ <string name="select_podcasts.refreshing">Der Server prĆ¼ft auf neue Podcasts.</string>
+ <string name="select_podcasts.deleted">Lƶsche Podcast %s</string>
+ <string name="select_podcasts.deleted_error">Fehler beim lƶschen des Podcast %s</string>
+ <string name="select_podcasts.add_url">URL:</string>
+ <string name="select_podcasts.created_error">Konnte Podcast nicht hinzufĆ¼gen</string>
+ <string name="select_podcasts.invalid_podcast_channel">UngĆ¼ltiger Podcastkanal: %s</string>
+ <string name="select_podcasts.delete">Lƶsche Podcast</string>
+
+ <string name="download.empty">Wiedergabeliste ist leer</string>
+ <string name="download.shuffle_loading">Wiedergabeliste wird gemischt...</string>
+ <string name="download.playerstate_downloading">Downloade - %s</string>
+ <string name="download.playerstate_buffering">Buffere</string>
+ <string name="download.playerstate_playing_shuffle">Playing shuffle</string>
+ <string name="download.menu_show_album">Zeige Album</string>
+ <string name="download.menu_lyrics">Liedtext</string>
+ <string name="download.menu_remove">Entferne aus Warteschlange</string>
+ <string name="download.menu_remove_all">Alle entfernen</string>
+ <string name="download.menu_screen_on">Bildschirm an</string>
+ <string name="download.menu_shuffle">Mischen</string>
+ <string name="download.menu_toggle">Umschalten</string>
+ <string name="download.menu_save">Wiedergabeliste speichern</string>
+ <string name="download.menu_shuffle_notification">Wiedergabeliste wurde gemischt</string>
+ <string name="download.menu_remove_played_songs">Abgespielte Titel entfernen</string>
+ <string name="download.playlist_title">Speichere Wiedergabeliste</string>
+ <string name="download.playlist_name">Name der Wiedergabeliste eingeben:</string>
+ <string name="download.playlist_saving">Sichere Wiedergabeliste \"%s\"...</string>
+ <string name="download.playlist_done">Wiedergabeliste wurde erfolgreich gespeichert.</string>
+ <string name="download.playlist_error">Fehler beim speichern der Wiedergabeliste, bitte spƤter erneut probieren.</string>
+ <string name="download.repeat_off">Keine Wiederholung</string>
+ <string name="download.repeat_all">Wiederhole alle</string>
+ <string name="download.repeat_single">Aktuelles Lied wiederholen</string>
+ <string name="download.jukebox_on">Fernbedienung aktiviert. Musik wird auf dem Computer abgespielt.</string>
+ <string name="download.jukebox_off">Fernbedienung deaktiviert. Musik wird auf dem Telefon abgespielt.</string>
+ <string name="download.jukebox_volume">LautstƤrke</string>
+ <string name="download.jukebox_server_too_old">Fernbedienung wird nicht unterstĆ¼tzt. Aktualisierung des Subsonic-Servers notwendig.</string>
+ <string name="download.jukebox_offline">Fernbedienung im Offline-Modus nicht verfĆ¼gbar.</string>
+ <string name="download.jukebox_not_authorized">Fernbedienung ist nicht erlaubt. Bitte aktivieren Sie den Jukebox-Modus unter <b>Nutzer &gt; Einstellungen</b> auf Ihrem Subsonic-Server.</string>
+ <string name="download.timer_length">Timer:</string>
+ <string name="download.start_timer">Starte Timer</string>
+ <string name="download.stop_timer">Stoppe Timer</string>
+ <string name="download.need_download">Video muss zuerst heruntergeladen werden</string>
+ <string name="download.no_streaming_player">Stream kann nicht wiedergegeben werden.</string>
+ <string name="download.playing_out_of">Wiedergabe: %1$d/%2$d</string>
+ <string name="download.save_bookmark_title">Setze Lesezeichen</string>
+ <string name="download.save_bookmark">Lesezeichen gesetzt</string>
+ <string name="download.save_bookmark_failed">Lesezeichen konnte nicht gesetzt werden.</string>
+ <string name="download.downloading_title">Lade %1$d Lieder</string>
+ <string name="download.downloading_summary">Aktuell: %1$s</string>
+ <string name="download.downloading_summary_expanded">Aktuell: %1$s
+ \nGeschƤtzte GrĆ¶ĆŸe: %2$s</string>
+ <string name="download.failed_to_load">Laden fehlgeschlagen</string>
+
+ <string name="sync.new_podcasts">Neuer Podcast verfĆ¼gbar</string>
+ <string name="sync.new_playlists">Neu in Wiedergabeliste</string>
+ <string name="sync.new_albums">Neues Album verfĆ¼gbar</string>
+ <string name="sync.new_starred">Neue Favoriten verfĆ¼gbar</string>
+
+ <string name="starring_content_starred">Favorit \"%s\"</string>
+ <string name="starring_content_unstarred">Kein Favorit \"%s\"</string>
+ <string name="starring_content_error">Aktualisierung von \"%s\" fehlgeschlagen, bitte spƤter erneut probieren.</string>
+
+ <string name="playlist_error">Konnte Liste der Wiedergabelisten nicht herunterladen</string>
+ <string name="updated_playlist">%1$s Lieder zu \"%2$s\" hinzugefĆ¼gt</string>
+ <string name="updated_playlist_error">Aktualisierung \"%s\" fehlgeschlagen, bitte spƤter erneut probieren.</string>
+ <string name="removed_playlist">%1$s entfernt aus \"%2$s\"</string>
+
+ <string name="bookmark.delete">Lƶsche Lesezeichen</string>
+ <string name="bookmark.delete_title">Lƶsche Lesezeichen fĆ¼r</string>
+ <string name="bookmark.deleted">Lesezeichen fĆ¼r \"%s\" gelƶscht</string>
+ <string name="bookmark.deleted_error">Lƶschen des Lesezeichen fĆ¼r \"%s\" ist fehlgeschlagen</string>
+ <string name="bookmark.details_title">Lesezeichendetails</string>
+ <string name="bookmark.details">Lied: %1$s
+ \nPosition: %2$s
+ \nErzeugt: %3$s
+ \nZuletzt aktualisiert: %4$s
+ \nKommentar: %5$s</string>
+ <string name="bookmark.resume_title">Wiedergabe fortsetzen?</string>
+ <string name="bookmark.resume">\'%1$s\' fortsetzen bei %2$s</string>
+ <string name="bookmark.action_resume">Fortsetzen</string>
+ <string name="bookmark.action_start_over">Neu beginnen</string>
+
+ <string name="rating.title">Bewerte \"%s\"</string>
+ <string name="rating.set_rating">Setze Bewertung fĆ¼r \"%s\"</string>
+ <string name="rating.set_rating_failed">Konnte Bewertung fĆ¼r \"%s\" nicht setzen</string>
+ <string name="rating.remove_rating">Bewertung fĆ¼r \"%s\" entfernt</string>
+ <string name="rating.remove_rating_failed">Konnte Bewertung fĆ¼r \"%s\" nicht entfernen</string>
+
+ <string name="song_details.error">Fehler</string>
+ <string name="song_details.skipped">Ɯberspringen</string>
+ <string name="song_details.downloading">Wird geladen</string>
+
+ <string name="lyrics.nomatch">Kein Liedtext gefunden</string>
+
+ <string name="error.label">Fehler</string>
+
+ <string name="settings.title">DSub Einstellungen</string>
+ <string name="settings.test_connection_title">Teste Verbindung</string>
+ <string name="settings.servers_add">Server hinzufĆ¼gen</string>
+ <string name="settings.servers_remove">Server entfernen</string>
+ <string name="settings.servers_title">Server</string>
+ <string name="settings.server_unused">ungenutzt</string>
+ <string name="settings.server_name">Name</string>
+ <string name="settings.server_address">Server Adresse</string>
+ <string name="settings.server_local_network_ssid" >Lokale Netzwerk-SSID</string>
+ <string name="settings.server_local_network_ssid_hint">Aktuelle SSID: %s</string>
+ <string name="settings.server_internal_address">Lokale Netzwerkadresse</string>
+ <string name="settings.server_username">Nutzername</string>
+ <string name="settings.server_password">Passwort</string>
+ <string name="settings.server_open_browser">Im Browser ƶffnen</string>
+ <string name="settings.server_sync_summary">Synchronisierung fĆ¼r diesen Server de-/aktivieren</string>
+ <string name="settings.server_sync">Synchronisierung aktivieren</string>
+ <string name="settings.cache_title">Musik Cache</string>
+ <string name="settings.preload_wifi">Im Vorraus zu laden (Wifi)</string>
+ <string name="settings.preload_mobile">Im Vorraus zu laden (Mobil)</string>
+ <string name="settings.cache_size">GrĆ¶ĆŸe des Cache (MB)</string>
+ <string name="settings.cache_location">Position des Cache</string>
+ <string name="settings.cache_location_error">UngĆ¼ltiger Pfad zum Cache. Nutze Standard.</string>
+ <string name="settings.cache_location_reset">Der gesetzte Cachepfad ist nicht (mehr) beschreibbar. Durch die Aktualisierung des Telefon-OS auf KitKat 4.4 wurde der Zugriff auf die SD-Karte durch Apps eingeschrƤnkt auf spezielle Pfade. Der von DSub verwendete Pfad wurde entsprechend korrigiert. Um den alten Cache zu lƶschen, mĆ¼ssen Sie die SD-Karte an einem Computer anschliessend und den Ordner manuell lƶschen.</string>
+ <string name="settings.cache_clear">Lƶsche Cache</string>
+ <string name="settings.cache_clear_complete">Cache wurde geleert</string>
+ <string name="settings.testing_connection">Teste Verbindung...</string>
+ <string name="settings.testing_ok">Verbindung ist OK</string>
+ <string name="settings.testing_unlicensed">Verbindung ist OK. Server ist nicht lizensiert.</string>
+ <string name="settings.connection_failure">Verbindung fehlgeschlagen.</string>
+ <string name="settings.invalid_url">Bitte geben Sie eine gĆ¼ltige URL an.</string>
+ <string name="settings.invalid_username">Bitte gĆ¼ltigen Nutzernamen angeben (keine fĆ¼hrenden Leerzeichen).</string>
+ <string name="settings.appearance_title">Erscheinung</string>
+ <string name="settings.theme_title">Theme</string>
+ <string name="settings.theme_light">Hell</string>
+ <string name="settings.theme_dark">Dunkel</string>
+ <string name="settings.theme_black">Schwarz</string>
+ <string name="settings.theme_holo">Holo</string>
+ <string name="settings.theme_fullscreen">Vollbildschirm</string>
+ <string name="settings.theme_fullscreen_summary">Verstecke soviel der Benutzerschnittstelle wie von Android ermƶglicht.</string>
+ <string name="settings.track_title">Titelnummer anzeigen</string>
+ <string name="settings.track_summary">Zeige Titelnummer vor dem Titel</string>
+ <string name="settings.custom_sort">Sortiere nach Jahr</string>
+ <string name="settings.custom_sort_summary">Sortiere Alben nach Jahr, oder alphabetisch.</string>
+ <string name="settings.network_title">Netzwerk</string>
+ <string name="settings.max_bitrate_wifi">Max Audio Bitrate - Wi-Fi</string>
+ <string name="settings.max_bitrate_mobile">Max Audio Bitrate - Mobil</string>
+ <string name="settings.max_video_bitrate_wifi">Max Video Bitrate - Wi-Fi</string>
+ <string name="settings.max_video_bitrate_mobile">Max Video Bitrate - Mobil</string>
+ <string name="settings.max_bitrate_unlimited">Unbegrenzt</string>
+ <string name="settings.wifi_required_title">Nur Wi-Fi streaming</string>
+ <string name="settings.wifi_required_summary">Medien nur streamen, wenn mit Wi-Fi verbunden</string>
+ <string name="settings.network_timeout_title">Netzwerk Timeout</string>
+ <string name="settings.network_timeout_10000">10 Sekunden</string>
+ <string name="settings.network_timeout_15000">15 Sekunden</string>
+ <string name="settings.network_timeout_30000">30 Sekunden</string>
+ <string name="settings.network_timeout_45000">45 Sekunden</string>
+ <string name="settings.network_timeout_60000">60 Sekunden</string>
+ <string name="settings.preload_0">Kein Lied</string>
+ <string name="settings.preload_1">1 Lied</string>
+ <string name="settings.preload_2">2 Lieder</string>
+ <string name="settings.preload_3">3 Lieder</string>
+ <string name="settings.preload_5">5 Lieder</string>
+ <string name="settings.preload_10">10 Lieder</string>
+ <string name="settings.preload_unlimited">unbegrenzt</string>
+ <string name="settings.clear_search_history">Lƶsche Suchanfragen</string>
+ <string name="settings.search_history_cleared">Suchanfragen gelƶscht</string>
+ <string name="settings.other_title">Sonstige Einstellungen</string>
+ <string name="settings.scrobble_title">Scrobble zu Last.fm</string>
+ <string name="settings.scrobble_summary">Ihr Benutzername und Passwort fĆ¼r Last.fm muss im Subsonic-Server konfiguriert sein</string>
+ <string name="settings.hide_media_title">Verstecke vor anderen</string>
+ <string name="settings.hide_media_summary">Musikdateien vor anderen Apps verstecken.</string>
+ <string name="settings.hide_media_toast">Wird aktiv bei der nƤchsten Mediensuche durch Android.</string>
+ <string name="settings.media_button_title">Medientasten</string>
+ <string name="settings.media_button_summary">Verwende Medientasten des Telefon, Freisprecheinrichtung und Bluetooth</string>
+ <string name="settings.screen_lit_title">Bildschirm aktiv halten</string>
+ <string name="settings.screen_lit_summary">Ein aktiver Bildschirm verbessert die Downloadgeschwindigkeit.</string>
+ <string name="settings.playlist_title">Wiedergeben</string>
+ <string name="settings.playlist_random_size_title">LƤnge der Zufallswiedergabeliste</string>
+ <string name="settings.sleep_timer_title">Einschlaftimer</string>
+ <string name="settings.sleep_timer_duration_title">Einschlafdauer</string>
+ <string name="settings.sleep_timer_off">Aus</string>
+ <string name="settings.sleep_timer_on">An</string>
+ <string name="settings.sleep_timer_always_on">Immer An</string>
+ <string name="settings.temp_loss_title">Verhalten bei Benachrichtigungen</string>
+ <string name="settings.temp_loss_pause">Immer pausieren</string>
+ <string name="settings.temp_loss_pause_lower">Pausiere und LautstƤrke bei Bedarf veringern</string>
+ <string name="settings.temp_loss_lower">LautstƤrke veringern</string>
+ <string name="settings.temp_loss_nothing">Nichts machen</string>
+ <string name="settings.disconnect_pause_title">Verhalten bei Verbindungsverlust</string>
+ <string name="settings.disconnect_pause_both">Immer pausieren</string>
+ <string name="settings.disconnect_pause_neither">Nichts machen</string>
+ <string name="settings.persistent_title">Dauerhafte Benachrichtigung</string>
+ <string name="settings.persistent_summary">Zeige die Benachrichtigung auch nach pausieren der Wiedergabe. Zum entfernen Stop auswƤhlen.</string>
+ <string name="settings.gapless_playback">LĆ¼ckenlose Wiedergabe</string>
+ <string name="settings.gapless_playback_summary">Das Galaxy S3 scheint Probleme seit der EinfĆ¼hrung der lĆ¼ckenlosen Wiedergabe zu haben. Zur Behebung schalten Sie dies ab.</string>
+ <string name="settings.chat_refresh">Chat Aktualisierungsrate (Sekunden)</string>
+ <string name="settings.chat_enabled">Chat aktiv</string>
+ <string name="settings.chat_enabled_summary">Chat im SeitenmenĆ¼ anzeigen</string>
+ <string name="settings.video_title">Video</string>
+ <string name="settings.video_player">Video Player</string>
+ <string name="settings.video_raw">Raw (benƶtigt Subsonic 4.8+)</string>
+ <string name="settings.video_hls">HTTP Live Stream (HLS) (benƶtigt Subsonic 4.8+)</string>
+ <string name="settings.video_transcode">Direkte Transkodierung (benƶtigt video -> mp4 oder Ƥhnliche Einstellungen auf dem Server)</string>
+ <string name="settings.video_flash">Flash (benƶtigt Plugin)</string>
+ <string name="settings.cache_screen_title">Cache/Netzwerk</string>
+ <string name="settings.playback_title">Wiedergabe</string>
+ <string name="settings.hide_widget_title">Verstecke Widget</string>
+ <string name="settings.hide_widget_summary">Verstecke Widget nach dem verlassen der App</string>
+ <string name="settings.podcasts_enabled">Podcasts aktiviert</string>
+ <string name="settings.podcasts_enabled_summary">Podcast im SeitenmenĆ¼ anzeigen</string>
+ <string name="settings.bookmarks_enabled">Lesezeichen aktiviert</string>
+ <string name="settings.bookmarks_enabled_summary">Lesezeichen im SeitenmenĆ¼ anzeigen</string>
+ <string name="settings.shares_enabled">Freigaben aktiviert</string>
+ <string name="settings.shares_enabled_summary">Freigabe im SeitenmenĆ¼ anzeigen</string>
+ <string name="settings.sync_title">Synchronisierung</string>
+ <string name="settings.sync_enabled">Synchronisierung aktiv</string>
+ <string name="settings.sync_enabled_summary">Podcast regelmƤƟig auf Ƅnderungen prĆ¼fen</string>
+ <string name="settings.sync_interval">Synchronisierungsintervall</string>
+ <string name="settings.sync_interval_15">15 Minutes</string>
+ <string name="settings.sync_interval_30">30 Minutes</string>
+ <string name="settings.sync_interval_60">1 Stunde</string>
+ <string name="settings.sync_interval_120">2 Stunden</string>
+ <string name="settings.sync_interval_240">4 Stunden</string>
+ <string name="settings.sync_interval_360">6 Stunden</string>
+ <string name="settings.sync_interval_720">12 Stunden</string>
+ <string name="settings.sync_interval_1440">tƤglich</string>
+ <string name="settings.sync_wifi">Nur per Wifi synchronisieren</string>
+ <string name="settings.sync_wifi_summary">Nur bei Verwendung von Wifi synchronisieren</string>
+ <string name="settings.sync_most_recent">Synchronisiere neu hinzugefĆ¼gte Lieder</string>
+ <string name="settings.sync_most_recent_summary">Lade neu hinzugefĆ¼gte Alben automatisch herunter</string>
+ <string name="settings.sync_starred">Synchronisiere Favoriten</string>
+ <string name="settings.sync_starred_summary">Synchronisiere favorisierte Lieder, Alben und KĆ¼nstler automatisch</string>
+ <string name="settings.sync_notification">Benachrichtigung nach Synchronisierung</string>
+ <string name="settings.sync_notification_summary">Zeige eine Benachrichtigung nachdem neue Medien synchronisiert wurden.</string>
+ <string name="settings.menu_options.title">Optionale MenĆ¼ EintrƤge</string>
+ <string name="settings.menu_options.play_next_summary">Zeige \"Als nƤchstes abspielen\" im MenĆ¼</string>
+ <string name="settings.menu_options.play_last_summary">Zeige \"Als letztes abspielen\" im MenĆ¼</string>
+ <string name="settings.menu_options.star_summary">Zeige Favorit im MenĆ¼</string>
+ <string name="settings.menu_options.shared_summary">Zeige \"Freigeben\" im MenĆ¼</string>
+ <string name="settings.menu_options.rate_summary">Zeige \"Bewerten\" im MenĆ¼</string>
+ <string name="settings.browse_by_tags">Tags nutzen</string>
+ <string name="settings.browse_by_tags_summary">Tags statt Ordner verwenden. Benƶtigt Subsonic 4.7+</string>
+ <string name="settings.override_system_language">In Englisch anzeigen</string>
+ <string name="settings.override_system_language_summary">Verwende Englisch anstatt Deutsch fĆ¼r DSub. Benƶtigt einen Neustart der App.</string>
+ <string name="settings.drawer_items_title">SeitenmenĆ¼</string>
+ <string name="settings.play_now_after">Jetzt wiedergeben bis zum Listenende</string>
+ <string name="settings.play_now_after_summary">\"Jetzt wiedergeben\" im KontextmenĆ¼ spielt das ausgewƤhlte Lied und alle in der Liste nachfolgenden Lieder ab (wie in der Web-Schnittstelle des Subsonic-Server)</string>
+ <string name="settings.large_album_art">GroƟe Cover anzeigen</string>
+ <string name="settings.large_album_art_summary">Verwende groƟe Cover zur Anzeige der Alben anstatt einer Liste</string>
+ <string name="settings.admin_enabled">Administration aktiviert</string>
+ <string name="settings.admin_enabled_summary">Administration im SeitenmenĆ¼ anzeigen</string>
+ <string name="settings.replay_gain">WiedergabeverstƤrkung</string>
+ <string name="settings.replay_gain_summary">Gibt an, ob die WiedergabeverstƤrkung anhand von Tags erfolgen soll.</string>
+ <string name="settings.replay_gain_type">Anhand der Tags</string>
+ <string name="settings.replay_gain_type.smart">Automatische Erkennung</string>
+ <string name="settings.replay_gain_type.album">Album Tag</string>
+ <string name="settings.replay_gain_type.track">Track Tag</string>
+ <string name="settings.replay_gain_bump">WiedergabevorverstƤrkung</string>
+ <string name="settings.replay_gain_untagged">Lieder ohne WiedergabeverstƤrkung</string>
+
+ <string name="shuffle.title">Mischen von</string>
+ <string name="shuffle.startYear">Startjahr:</string>
+ <string name="shuffle.endYear">Endjahr:</string>
+ <string name="shuffle.genre">Genre:</string>
+ <string name="shuffle.pick_genre">WƤhle ein Genre</string>
+
+ <string name="share.info">EigentĆ¼mer: %1$s
+ \nBeschreibung: %2$s
+ \nURL: %3$s
+ \nErzeugt: %4$s
+ \nZuletzt besucht: %5$s
+ \nAblauf: %6$s
+ \nBesuchszƤhler: %7$s
+
+ </string>
+ <string name="share.expires">Ablauf: %s</string>
+ <string name="share.expires_never">nie</string>
+ <string name="share.deleted">Lƶsche Freigabe %s</string>
+ <string name="share.deleted_error">Lƶschen der Freigabe %s fehlgeschlagen</string>
+ <string name="share.no_expiration">Kein Ablauf</string>
+ <string name="share.expiration">Ablauf:</string>
+ <string name="share.updated_info">Aktualisiere Informationen der Freigabe %s</string>
+ <string name="share.updated_info_error">Aktualisierung der Freigabe %s fehlgeschlagen</string>
+ <string name="share.via">Teile via</string>
+ <string name="share.delete">Lƶsche Freigabe</string>
+
+ <string name="admin.add_user_username">Nutzername:</string>
+ <string name="admin.add_user_email">Email:</string>
+ <string name="admin.add_user_password">Passwort:</string>
+ <string name="admin.create_user_success">Neuen Nutzer wurde erfolgreich angelegt</string>
+ <string name="admin.create_user_error">Neuer Nutzer konnte nicht erzeugt werden</string>
+ <string name="admin.change_username_invalid">Bitte gĆ¼ltigen Nutzernamen angeben</string>
+ <string name="admin.update_permissions">Aktualisiere Berechtigungen</string>
+ <string name="admin.update_permissions_success">Berechtigungen fĆ¼r %1$s erfolgreich aktualisiert</string>
+ <string name="admin.update_permissions_error">Ƅnderung der Berechtigungen fĆ¼r %1$s fehlgeschlagen</string>
+ <string name="admin.change_email">Email Ƥndern</string>
+ <string name="admin.change_email_success">Email fĆ¼r %1$s wurde geƤndert</string>
+ <string name="admin.change_email_error">Konnte Email fĆ¼r %1$s nicht Ƥndern</string>
+ <string name="admin.change_email_label">Neue Email:</string>
+ <string name="admin.change_email_invalid">Bitte gĆ¼ltige Email angeben</string>
+ <string name="admin.change_password">Passwort Ƥndern</string>
+ <string name="admin.change_password_success">Passwort fĆ¼r %1$s erfolgreich geƤndert</string>
+ <string name="admin.change_password_error">PasswortƤnderung fĆ¼r %1$s ist fehlgeschlagen</string>
+ <string name="admin.change_password_label">Neues Passwort:</string>
+ <string name="admin.change_password_invalid">Bitte ein gĆ¼ltiges Passwort eingeben</string>
+ <string name="admin.delete_user">Nutzer lƶschen</string>
+ <string name="admin.delete_user_success">Nutzer %1$s erfolgreich gelƶscht</string>
+ <string name="admin.delete_user_error">Nutzer %1$s konnte nicht gelƶscht werden</string>
+ <string name="admin.confirm_password">Passwort bestƤtigen</string>
+ <string name="admin.confirm_password_bad">Eingegebenes Passwort ist falsch</string>
+
+ <string name="admin.scrobblingEnabled">Erlaube Scrobbeln</string>
+ <string name="admin.role.admin">Administrator</string>
+ <string name="admin.role.settings">Einstellungen Ƥndern</string>
+ <string name="admin.role.download">Dateien herunterladen</string>
+ <string name="admin.role.upload">Hochladen zum Server</string>
+ <string name="admin.role.coverArt">Cover Ƥndern</string>
+ <string name="admin.role.comment">Kommentare hinzufĆ¼gen</string>
+ <string name="admin.role.podcast">Podcasts verwalten</string>
+ <string name="admin.role.stream">Musik streamen</string>
+ <string name="admin.role.jukebox">Jukebox kontrollieren</string>
+ <string name="admin.role.share">Freigaben verwalten</string>
+ <string name="admin.role.lastfm">Last.FM nutzen</string>
+
+ <string name="music_service.retry">Ein Netzwerkfehler ist aufgetreten. Versuch %1$d von %2$d.</string>
+
+ <string name="background_task.wait">Bitte warten...</string>
+ <string name="background_task.loading">Lade.</string>
+ <string name="background_task.no_network">Diese Programm benƶtigt Netzwerkzugriff. Bitte schalten Sie Wi-Fi oder Mobiles Netzwerk ein.</string>
+ <string name="background_task.network_error">Ein Netzwerkfehler ist aufgetreten. Bitte prĆ¼fen Sie die Serveradresse oder versuchen Sie es spƤter nochmal.</string>
+ <string name="background_task.not_found">Quelle wurde nicht gefunden. Bitte prĆ¼fen Sie die Serveradresse.</string>
+ <string name="background_task.parse_error">Ein Fehler ist bei der Kommunikation mit dem Server aufgetreten. Bitte prĆ¼fen Sie die Serveradresse und stellen Sie sicher, das Sie der Server mit einem Webbrowser erreichen.</string>
+
+ <string name="service.connecting">Kontaktiere Server, bitte warten.</string>
+
+ <string name="parser.upgrade_client">Inkompatible Versionen. Aktualisierung von DSub erforderlich.</string>
+ <string name="parser.upgrade_server">Inkompatible Versionen. Aktualisierung des Subsonic Server erforderlich.</string>
+ <string name="parser.not_authenticated">Benutzername oder/und Passwort falsch.</string>
+ <string name="parser.not_authorized">Nicht authorisiert. Bitte prĆ¼fen Sie die Einstellungen im Subsonic-Server.</string>
+ <string name="parser.artist_count">%d KĆ¼nstler.</string>
+ <string name="parser.server_error">Serverfehler: %s</string>
+ <string name="parser.scan_count">%d EintrƤge gefunden</string>
+
+ <string name="select_artist.refresh">Aktualisieren</string>
+ <string name="select_artist.folder">WƤhle Ordner</string>
+ <string name="select_artist.all_folders">Alle Ordner</string>
+
+ <string name="equalizer.label">Equalizer</string>
+ <string name="equalizer.enabled">aktiv</string>
+ <string name="equalizer.preset">WƤhle Vorgabe</string>
+ <string name="equalizer.bass_booster">BassverstƤrkung</string>
+ <string name="equalizer.voice_booster">SprachverstƤrkung</string>
+ <string name="equalizer.db_size">%d dB</string>
+ <string name="equalizer.bass_size">%d mille</string>
+
+ <string name="widget.initial_text">Zur Musikauswahl berĆ¼hren</string>
+ <string name="widget.sdcard_busy">SD-Karte nicht verfĆ¼gbar</string>
+ <string name="widget.sdcard_missing">Keine SD-Karte</string>
+
+ <string name="changelog_full_title">Ƅnderungen</string>
+ <string name="changelog_title">Was ist Neu</string>
+ <string name="changelog_ok_button">OK</string>
+ <string name="changelog_show_full">Mehrā€¦</string>
+
+ <string name="chat.send_a_message">Nachricht senden</string>
+
+ <string name="changelog_version_format" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">Version <xliff:g id="version_name">%s</xliff:g></string>
+
+ <string name="tasker.start_playing">Starte Wiedergabe</string>
+ <string name="tasker.start_playing_shuffled">Starte Zufallswiedergabe</string>
+ <string name="tasker.start_playing_title">Tasker -> Starte DSub</string>
+ <string name="tasker.edit_shuffle_mode">Starte im Zufallsmodus: </string>
+ <string name="tasker.edit_shuffle_start_year">Startjahr:</string>
+ <string name="tasker.edit_shuffle_end_year">Endjahr:</string>
+ <string name="tasker.edit_shuffle_genre">Genre:</string>
+ <string name="tasker.edit_server_offline">Zwischen On-/Offline-Modus wechseln: </string>
+ <string name="tasker.edit_do_nothing">Nichts tun</string>
+
+ <plurals name="select_album_n_songs">
+ <item quantity="zero">Keine Lieder</item>
+ <item quantity="one">Ein Lied</item>
+ <item quantity="other">%d Lieder</item>
+ </plurals>
+ <plurals name="select_album_n_songs_downloading">
+ <item quantity="one">Ein Lied wird heruntergeladen.</item>
+ <item quantity="other">%d Lieder werden heruntergeladen.</item>
+ </plurals>
+ <plurals name="select_album_n_songs_added">
+ <item quantity="one">Ein Lied zur Abspielliste hinzugefĆ¼gt.</item>
+ <item quantity="other">%d Lieder zur Abspielliste hinzugefĆ¼gt.</item>
+ </plurals>
+ <plurals name="select_album_donate_dialog_n_trial_days_left">
+ <item quantity="one">Noch ein Tag bis zum Ablauf des Testzeitraums.</item>
+ <item quantity="other">%d Tage bis zum Ablauf des Testzeitraums.</item>
+ </plurals>
+
+</resources>
diff --git a/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index cd255e13..cd255e13 100644
--- a/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
diff --git a/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 0bcb2407..759e3fd8 100644
--- a/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -1,570 +1,570 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-
- <string name="common.appname">DSub</string>
- <string name="common.ok">OK</string>
- <string name="common.save">Sauver</string>
- <string name="common.cancel">Annuler</string>
- <string name="common.play_now">Jouer</string>
- <string name="common.play_shuffled">Jouer au hasard</string>
- <string name="common.play_next">Suivant</string>
- <string name="common.play_last">PrƩcƩdent</string>
- <string name="common.download">Mettre en cache</string>
- <string name="common.pin">Mettre en cache Permanent</string>
- <string name="common.delete">Supprimer</string>
- <string name="common.star">Favori</string>
- <string name="common.unstar">Supp. favori</string>
- <string name="common.info">DĆ©tails</string>
- <string name="common.name">Nom</string>
- <string name="common.comment">Commentaire</string>
- <string name="common.public">Publique</string>
- <string name="common.play_external">Jouer Video</string>
- <string name="common.stream_external">Stream Video</string>
- <string name="common.confirm">Confirmer</string>
- <string name="common.confirm_message">Voulez-vous %1$s %2$s ?</string>
- <string name="common.confirm_message_cache">cache</string>
- <string name="common.empty">Aucune donnƩe</string>
- <string name="common.warning">Avertissement</string>
-
- <string name="button_bar.home">Accueil</string>
- <string name="button_bar.browse">BibliothĆØque</string>
- <string name="button_bar.search">Recherche</string>
- <string name="button_bar.playlists">Playlists</string>
- <string name="button_bar.now_playing">Lecture en cours</string>
- <string name="button_bar.podcasts">Podcasts</string>
- <string name="button_bar.bookmarks">Favoris</string>
- <string name="button_bar.shares">Partages</string>
- <string name="button_bar.chat">Chat</string>
- <string name="button_bar.admin">Admin</string>
- <string name="button_bar.downloading">TƩlƩchargement</string>
-
- <string name="main.welcome_title">Bienvenue !</string>
- <string name="main.welcome_text">Bienvenue dans DSub ! L\'application est actuellement configurĆ©e pour se connecter au serveur de dĆ©mo Subsonic (<b>demo.subsonic.org</b>). Vous pouvez configurer votre propre serveur dans les paramĆØtres. Choisir <b>ParamĆØtres</b> et mettre Ć  jour la configuration pour vous y connecter.</string>
- <string name="main.about_title">A propos de DSub</string>
- <string name="main.about_text">Auteur : Scott Jackson
- \nEmail : dsub.android@gmail.com
- \nVersion : %1$s
- \nFichiers en cache : %2$s
- \nEspace utilisƩ : %3$s de %4$s
- \nEspace dispo. : %5$s de %6$s</string>
- <string name="main.faq_title">FAQ</string>
- <string name="main.faq_text">
- <![CDATA[
- <font color="red">Cache vs Cache permanent</font> :
- <br/>Lorsque des titres sont tĆ©lĆ©chargĆ©s par DSub, ils peuvent ĆŖtre supprimĆ©s pour libĆ©rer de l\'espace pour de nouveaux tĆ©lĆ©chargements. Le cache permanent premet, lui, de ne jamais supprimer automatiquement la musique tĆ©lĆ©chargĆ©e.
- <p/><font color="red">ChromeCast a ƩchouƩ</font> :
- <br/>Assurez-vous de ne pas utiliser un certificat auto-signƩ, Chromecast les rejette systƩmatiquement.
- ]]>
- </string>
- <string name="main.select_server">Choisir un serveur</string>
- <string name="main.shuffle">Jouer au hasard</string>
- <string name="main.offline">DĆ©connecter</string>
- <string name="main.online">Connecter</string>
- <string name="main.settings">ParamĆØtres</string>
- <string name="main.albums_title">Albums</string>
- <string name="main.albums_newest">AjoutƩs rƩcemments</string>
- <string name="main.albums_recent">JouƩs rƩcemment</string>
- <string name="main.albums_frequent">Les plus jouƩs</string>
- <string name="main.albums_highest">Les mieux notƩs</string>
- <string name="main.albums_starred">Favoris</string>
- <string name="main.albums_random">Au hasard</string>
- <string name="main.albums_genres">Par genres</string>
- <string name="main.albums_year">Par dƩcennies</string>
- <string name="main.songs_genres">@string/main.albums_genres</string>
- <string name="main.back_confirm">Presser retour Ć  nouveau pour quitter</string>
- <string name="main.scan_complete">Completed scan of Server</string>
-
- <string name="menu.search">Recherche</string>
- <string name="menu.shuffle">Hasard</string>
- <string name="menu.refresh">Recharger</string>
- <string name="menu.play">Jouer</string>
- <string name="menu.exit">Quitter</string>
- <string name="menu.play_last">Jouer dernier</string>
-
- <string name="menu.settings">ParamĆØtres</string>
- <string name="menu.help">Aide</string>
- <string name="menu.about">A propos</string>
- <string name="menu.add_playlist">Ajouter Ć  la playlist</string>
- <string name="menu.remove_playlist">Supprimer de la playlist</string>
- <string name="menu.deleted_playlist">Supprimer la playlist %s</string>
- <string name="menu.deleted_playlist_error">Echec de la suppression de la playlist %s</string>
- <string name="menu.log">Envoyer le journal</string>
- <string name="menu.set_timer">Ajuster le minuteur</string>
- <string name="menu.check_podcasts">VĆ©rifier les nouveaux podcasts</string>
- <string name="menu.add_podcast">Ajouter une chaƮne</string>
- <string name="menu.keep_synced">Synchronisation automatique</string>
- <string name="menu.stop_sync">ArrĆŖter la synchro.</string>
- <string name="menu.show_all">Afficher tous les media</string>
- <string name="menu.show_artist">Afficher l\'artiste</string>
- <string name="menu.share">Partager</string>
- <string name="menu.delete_cache">Supprimer du cache</string>
- <string name="menu.cast">Diffuser vers appareil</string>
- <string name="menu.faq">FAQ</string>
- <string name="menu.add_user">Ajouter utilisateur</string>
- <string name="menu.rescan">Relire le server</string>
- <string name="menu.rate">Noter</string>
-
- <string name="playlist.label">Playlists</string>
- <string name="playlist.update_info">Mise Ć  jour informations</string>
- <string name="playlist.updated_info">Informations de la playlist %s mises Ć  jour</string>
- <string name="playlist.updated_info_error">Echec de la mise Ć  jour des informations de la playlist %s</string>
- <string name="playlist.overwrite">Remplacer la playlist existante</string>
- <string name="playlist.add_to">Ajouter Ć  la playlist</string>
- <string name="playlist.create_new">CrƩer une nouvelle</string>
- <string name="playlist.delete">Supprimer la playlist</string>
-
- <string name="search.label">Recherche</string>
- <string name="search.title">Recherche</string>
- <string name="search.search">Cliquer pour rechercher</string>
- <string name="search.no_match">Aucun rƩsultat, recommencer</string>
- <string name="search.artists">Artistes</string>
- <string name="search.albums">Albums</string>
- <string name="search.songs">Chansons</string>
- <string name="search.more">Afficher plus</string>
-
- <string name="progress.wait">Patientezā€¦</string>
-
- <string name="music_library.label">BibliothĆØque</string>
- <string name="music_library.label_offline">MƩdia mode dƩconnectƩ</string>
-
- <string name="select_album.select">Tout sƩlectionner</string>
- <string name="select_album.n_selected">%d pistes sƩlectionnƩes.</string>
- <string name="select_album.n_unselected">%d pistes dƩsƩlectionnƩes.</string>
- <string name="select_album.more">Plus</string>
- <string name="select_album.offline">DƩconnectƩ</string>
- <string name="select_album.searching">Recherche en cours...</string>
- <string name="select_album.no_sdcard">Erreur : Aucune carte SD card disponible.</string>
- <string name="select_album.no_network">ProblĆØme : Aucun rĆ©seau disponible.</string>
- <string name="select_album.not_licensed">Serveur sans licence valide. %d jours restant.</string>
- <string name="select_album.donate_dialog_message">TƩlƩchargement illimitƩ en supportant Subsonic.</string>
- <string name="select_album.donate_dialog_now">Maintenant</string>
- <string name="select_album.donate_dialog_later">Plus tard</string>
- <string name="select_album.donate_dialog_0_trial_days_left">La pƩriode d\'essai est terminƩe</string>
-
- <string name="offline.sync_dialog_title">Chanson dƩconnectƩes en attente de synchro</string>
- <string name="offline.sync_dialog_message">GƩrer %1$d prƩfƩrences en mode dƩconnectƩ ?
- \nGƩrer %2$d favoris en mode dƩconnectƩ ?
- </string>
- <string name="offline.sync_dialog_default">Utiliser cette action par dƩfaut</string>
- <string name="offline.sync_success">%1$d chansons synchronisĆ©es avec succĆØs</string>
- <string name="offline.sync_partial">%1$d chansons sur %2$d synchronisĆ©es avec succĆØs</string>
- <string name="offline.sync_error">Echec de la synchro. des chansons</string>
-
- <string name="select_genre.blank">Vide</string>
- <string name="select_genre.songs">%d chansons</string>
- <string name="select_genre.albums">%d albums</string>
-
- <string name="select_podcasts.error">Une erreur est survenue avec ce podcast pendant le chargement. Le serveur doit d\'abord le tƩlƩcharger.</string>
- <string name="select_podcasts.skipped">Ce podcast n\'a pas ƩtƩ chargƩ sur le serveur. Le serveur doit d\'abord le tƩlƩcharger.</string>
- <string name="select_podcasts.initializing">Le chargement du podcast a commencer sur le serveur. Recharger SVP dans quelques instants.</string>
- <string name="select_podcasts.server_download">TƩlƩcharger sur le serveur</string>
- <string name="select_podcasts.server_delete">Supprimer du serveur</string>
- <string name="select_podcasts.downloading">TƩlƩchargement %s sur le serveur</string>
- <string name="select_podcasts.refreshing">Le serveur recherche les mises Ć  jour de podcasts</string>
- <string name="select_podcasts.deleted">Podcast supprimƩ %s</string>
- <string name="select_podcasts.deleted_error">Erreur lors de la suppression du podcast %s</string>
- <string name="select_podcasts.add_url">URL :</string>
- <string name="select_podcasts.created_error">Erreur lors de l\'ajout du podcast</string>
- <string name="select_podcasts.invalid_podcast_channel">Podcast invalide : %s</string>
- <string name="select_podcasts.delete">Supprimer podcast</string>
-
- <string name="download.empty">La playlist est vide</string>
- <string name="download.shuffle_loading">Chargement en cours liste au hasard...</string>
- <string name="download.playerstate_downloading">Chargement - %s</string>
- <string name="download.playerstate_buffering">Mise en mƩmoire tampon</string>
- <string name="download.playerstate_playing_shuffle">Lecture au hasard</string>
- <string name="download.menu_show_album">Afficher l\'album</string>
- <string name="download.menu_lyrics">Paroles</string>
- <string name="download.menu_remove">Enlever de la queue</string>
- <string name="download.menu_remove_all">Enlever tout</string>
- <string name="download.menu_screen_on">Ecran actif</string>
- <string name="download.menu_shuffle">Hasard</string>
- <string name="download.menu_toggle">Basculer</string>
- <string name="download.menu_save">Enregistrer la playlist</string>
- <string name="download.menu_shuffle_notification">La playlist a ƩtƩ mƩlangƩe</string>
- <string name="download.menu_remove_played_songs">Supprimer les titres dƩjƠ jouƩ</string>
- <string name="download.playlist_title">Enregistrer la playlist</string>
- <string name="download.playlist_name">Nom de la playlist :</string>
- <string name="download.playlist_saving">Enregistrement playlist \&quot;%s\&quot;...</string>
- <string name="download.playlist_done">La playlist a bien ƩtƩ enregistrƩe.</string>
- <string name="download.playlist_error">Erreur Ơ l\'enregistrement de la playlist, rƩessayer plus tard.</string>
- <string name="download.repeat_off">RƩpƩter inactif</string>
- <string name="download.repeat_all">RƩpƩter tout</string>
- <string name="download.repeat_single">RƩpƩter titre</string>
- <string name="download.jukebox_on">TƩlƩcommande activƩe. La musique est diffusƩe sur l\'ordinateur.</string>
- <string name="download.jukebox_off">TƩlƩcommande dƩsactivƩe. La musique est diffusƩe sur le mobile.</string>
- <string name="download.jukebox_volume">Volume distant</string>
- <string name="download.jukebox_server_too_old">TƩlƩcommande non supportƩe. Mettre Ơ jour le serveur Subsonic.</string>
- <string name="download.jukebox_offline">La tƩlƩcommande n\'est pas disponible en mode dƩconnectƩ.</string>
- <string name="download.jukebox_not_authorized">Mode tƩlƩcommande non autorisƩe. Activer le mode jukebox.<b>Users &gt; Settings</b> on your Subsonic server.</string>
- <string name="download.timer_length">Minuteur :</string>
- <string name="download.start_timer">DĆ©marrer le minuteur</string>
- <string name="download.stop_timer">ArrĆŖter le minuteur</string>
- <string name="download.need_download">La vidĆ©o doit d\'abord ĆŖtre tĆ©lĆ©chargĆ©e</string>
- <string name="download.no_streaming_player">Aucun lecteur ne peut afficher ce flux</string>
- <string name="download.playing_out_of">Lecture : %1$d/%2$d</string>
- <string name="download.save_bookmark_title">CrƩer un favori</string>
- <string name="download.save_bookmark">Favori crƩƩ</string>
- <string name="download.downloading_title">Chargement des titres %1$d</string>
- <string name="download.downloading_summary">En cours : %1$s</string>
- <string name="download.downloading_summary_expanded">En cours : %1$s
- \nTaille estimƩe : %2$s</string>
- <string name="download.failed_to_load">Echec du chargement</string>
- <string name="download.save_bookmark_failed">Echec de la crƩation du favori</string>
-
- <string name="sync.new_podcasts">Nouveaux podcasts disponibles</string>
- <string name="sync.new_playlists">Nouveaux titres dans les playlists</string>
- <string name="sync.new_albums">Nouveaux albums disponibles</string>
- <string name="sync.new_starred">Nouveaux titres notƩs disponibles</string>
-
- <string name="starring_content_starred">NotƩ \&quot;%s\&quot;</string>
- <string name="starring_content_unstarred">DƩvaluƩs \&quot;%s\&quot;</string>
- <string name="starring_content_error">Echec de la mise Ć  jour \&quot;%s\&quot;, RĆ©essayer plus tard.</string>
-
- <string name="playlist_error">Echec de la rƩcupƩration des playlists</string>
- <string name="updated_playlist">Titre %1$s ajoutƩ Ơ \&quot;%2$s\&quot;</string>
- <string name="updated_playlist_error">Echec de la mise Ơ jour \&quot;%s\&quot;, rƩessayer plus tard.</string>
- <string name="removed_playlist">Titre %1$s retirƩ de \&quot;%2$s\&quot;</string>
-
- <string name="bookmark.delete">Supprimer le favori</string>
- <string name="bookmark.delete_title">Dupprimer le favori pour</string>
- <string name="bookmark.deleted">Favori pour \&quot;%s\&quot; supprimƩ</string>
- <string name="bookmark.deleted_error">Echec de la suppression du favori pour \&quot;%s\&quot;</string>
- <string name="bookmark.details_title">DĆ©tails du favori</string>
- <string name="bookmark.details">Titre : %1$s
- \nPosition : %2$s
- \nCrƩƩ le : %3$s
- \nMis Ć  jour : %4$s
- \nCommentaire : %5$s</string>
- <string name="bookmark.resume_title">Reprendre la lecture ?</string>
- <string name="bookmark.resume">Reprendre la lecture de \'%1$s\' depuis %2$s</string>
- <string name="bookmark.action_resume">Reprendre</string>
- <string name="bookmark.action_start_over">Start Over</string>
-
- <string name="rating.title">Noter \"%s\"</string>
- <string name="rating.set_rating">Note attribuƩe Ơ \"%s\"</string>
- <string name="rating.set_rating_failed">Echec de l\'attribution de la note Ć  \"%s\"</string>
- <string name="rating.remove_rating">Note supprimƩe pour \"%s\"</string>
- <string name="rating.remove_rating_failed">Echec de la suppression de la note pour \"%s\"</string>
-
- <string name="song_details.error">Erreur</string>
- <string name="song_details.skipped">IgnorƩ</string>
- <string name="song_details.downloading">Chargement</string>
-
- <string name="lyrics.nomatch">Aucune paroles trouvƩes</string>
-
- <string name="error.label">Erreur</string>
-
- <string name="settings.title">ParamĆØtres DSub</string>
- <string name="settings.test_connection_title">Test de connexion</string>
- <string name="settings.servers_add">Ajouter un serveur</string>
- <string name="settings.servers_remove">Supprimer le serveur</string>
- <string name="settings.servers_title">Serveurs</string>
- <string name="settings.server_unused">InutilisƩ</string>
- <string name="settings.server_name">Nom</string>
- <string name="settings.server_address">Adresse du serveur</string>
- <string name="settings.server_local_network_ssid" >SSID du rƩseau local</string>
- <string name="settings.server_local_network_ssid_hint">SSID Actuel : %s</string>
- <string name="settings.server_internal_address">Adresse sur le rƩseau local</string>
- <string name="settings.server_username">Nom d\'utilisateur</string>
- <string name="settings.server_password">Mot de passe</string>
- <string name="settings.server_open_browser">Ouvrir dans le navigateur</string>
- <string name="settings.cache_title">MĆ©moire tampon pour la musique</string>
- <string name="settings.preload_wifi">Titres Ơ prƩcharger (Wifi)</string>
- <string name="settings.preload_mobile">Titre Ơ prƩcharger (Mobile)</string>
- <string name="settings.cache_size">Taille de la mƩmoire tampon (MB)</string>
- <string name="settings.cache_location">Emplacement de la mƩmoire tampon</string>
- <string name="settings.cache_location_error">Emplacement invalide pour la mƩmoire tampon. Utilisation emplacement par dƩfaut.</string>
- <string name="settings.cache_location_reset">L\'emplacement choisi pour la mĆ©moire tampon ne peut plus ĆŖtre utilisĆ©. S\'il y a eu une mise Ć  jour rĆ©cente vers Android 4.4 KiktKat, la faƧon dont les application Ć©crivent sur la carte SD Ć  changĆ©e afin que ces derniĆØres ne puissent Ć©crire que dans un emplacement spĆ©cifique. L\'emplacement Ć  utiliser pour DSub a dĆ©jĆ  Ć©tĆ© mis Ć  jour. Afin de supprimer toutes les donnĆ©es obsolĆØtes, il faut vider l\'ancien emplacement en utilisant un ordinateur.</string>
- <string name="settings.cache_clear">Vider la mƩmoire tampon</string>
- <string name="settings.cache_clear_complete">MƩmoire tampon vidƩe</string>
- <string name="settings.testing_connection">Test de connexion...</string>
- <string name="settings.testing_ok">Connexion OK</string>
- <string name="settings.testing_unlicensed">Connexion OK. Pas de licence.</string>
- <string name="settings.connection_failure">Echec de la connexion.</string>
- <string name="settings.invalid_url">Saisir une URL valide.</string>
- <string name="settings.invalid_username">Saisir un nom d\'utilisateur valide (espaces interdits).</string>
- <string name="settings.appearance_title">Apparence</string>
- <string name="settings.theme_title">ThĆØme</string>
- <string name="settings.theme_light">Light</string>
- <string name="settings.theme_dark">Dark</string>
- <string name="settings.theme_black">Black</string>
- <string name="settings.theme_holo">Holo</string>
- <string name="settings.theme_fullscreen">Plein Ć©cran</string>
- <string name="settings.theme_fullscreen_summary">Cacher autant d\'ƩlƩment graphique que possible</string>
- <string name="settings.track_title">Afficher nĀ° piste</string>
- <string name="settings.track_summary">Afficher le nĀ° de piste devant les titres</string>
- <string name="settings.custom_sort">Trier par annƩes</string>
- <string name="settings.custom_sort_summary">Trier les albums par annƩe, ou par ordre alphabƩtique</string>
- <string name="settings.network_title">RĆ©seau</string>
- <string name="settings.max_bitrate_wifi">DĆ©bit audio max (Wifi)</string>
- <string name="settings.max_bitrate_mobile">DĆ©bit audio max (Mobile)</string>
- <string name="settings.max_bitrate_32">32 Kbps</string>
- <string name="settings.max_bitrate_64">64 Kbps</string>
- <string name="settings.max_bitrate_80">80 Kbps</string>
- <string name="settings.max_bitrate_96">96 Kbps</string>
- <string name="settings.max_bitrate_112">112 Kbps</string>
- <string name="settings.max_bitrate_128">128 Kbps</string>
- <string name="settings.max_bitrate_160">160 Kbps</string>
- <string name="settings.max_bitrate_192">192 Kbps</string>
- <string name="settings.max_bitrate_256">256 Kbps</string>
- <string name="settings.max_bitrate_320">320 Kbps</string>
- <string name="settings.max_video_bitrate_wifi">DƩbit vidƩo max (Wifi)</string>
- <string name="settings.max_video_bitrate_mobile">DƩbit vidƩo max (Mobile)</string>
- <string name="settings.max_video_bitrate_200">200 Kbps</string>
- <string name="settings.max_video_bitrate_300">300 Kbps</string>
- <string name="settings.max_video_bitrate_400">400 Kbps</string>
- <string name="settings.max_video_bitrate_500">500 Kbps</string>
- <string name="settings.max_video_bitrate_700">700 Kbps</string>
- <string name="settings.max_video_bitrate_1000">1000 Kbps</string>
- <string name="settings.max_video_bitrate_1500">1500 Kbps</string>
- <string name="settings.max_video_bitrate_2000">2000 Kbps</string>
- <string name="settings.max_video_bitrate_3000">3000 Kbps</string>
- <string name="settings.max_video_bitrate_5000">5000 Kbps</string>
- <string name="settings.max_bitrate_unlimited">IllimitƩ</string>
- <string name="settings.wifi_required_title">Streaming en Wifi uniquement</string>
- <string name="settings.wifi_required_summary">Ne lire les mƩdia qu\'avec une connexion Wifi</string>
- <string name="settings.network_timeout_title">DƩlai d\'attente rƩseau (timeout)</string>
- <string name="settings.network_timeout_10000">10 secondes</string>
- <string name="settings.network_timeout_15000">15 secondes</string>
- <string name="settings.network_timeout_30000">30 secondes</string>
- <string name="settings.network_timeout_45000">45 secondes</string>
- <string name="settings.network_timeout_60000">60 secondes</string>
- <string name="settings.preload_0">0 titre</string>
- <string name="settings.preload_1">1 titre</string>
- <string name="settings.preload_2">2 titres</string>
- <string name="settings.preload_3">3 titres</string>
- <string name="settings.preload_5">5 titres</string>
- <string name="settings.preload_10">10 titres</string>
- <string name="settings.preload_unlimited">IllimitƩ</string>
- <string name="settings.clear_search_history">Effacer l\'historique de recherche</string>
- <string name="settings.search_history_cleared">Rechercher dans l\'historique effacƩ</string>
- <string name="settings.other_title">Autres paramĆØtres</string>
- <string name="settings.scrobble_title">Publier vers Last.fm</string>
- <string name="settings.scrobble_summary">Penser Ơ paramƩtrer votre compte Last.fm sur le serveur Subsonic</string>
- <string name="settings.hide_media_title">Invisible pour les autres</string>
- <string name="settings.hide_media_summary">Rendre les fichiers de musique indisponibles pour les autres applications.</string>
- <string name="settings.hide_media_toast">Prendra effet au prochain scan de musique d\'Android.</string>
- <string name="settings.media_button_title">Boutons physique</string>
- <string name="settings.media_button_summary">RĆ©pondre aux boutons du mobile, du casque filaire ou bluetooth</string>
- <string name="settings.screen_lit_title">Garder l\'Ʃcran allumƩ</string>
- <string name="settings.screen_lit_summary">Garder l\'Ʃcran allumƩ augmente la vitesse de tƩlƩchargement.</string>
- <string name="settings.playlist_title">Lecture</string>
- <string name="settings.playlist_random_size_title">Taille de la liste de lecture alƩatoire</string>
- <string name="settings.sleep_timer_title">Temporisateur</string>
- <string name="settings.sleep_timer_duration_title">DurƩe temporisation</string>
- <string name="settings.sleep_timer_off">Eteindre</string>
- <string name="settings.sleep_timer_on">Allumer</string>
- <string name="settings.sleep_timer_always_on">Toujours en fonctionnement</string>
- <string name="settings.temp_loss_title">Perte temporaire de focus</string>
- <string name="settings.temp_loss_pause">Toujours mettre en pause</string>
- <string name="settings.temp_loss_pause_lower">Pause, baisser le volume si demandƩ</string>
- <string name="settings.temp_loss_lower">Toujours baisser le volume</string>
- <string name="settings.temp_loss_nothing">Ne rien faire</string>
- <string name="settings.disconnect_pause_title">Pause Ơ la dƩconnexion</string>
- <string name="settings.disconnect_pause_both">Pause</string>
- <string name="settings.disconnect_pause_neither">Ne rien faire</string>
- <string name="settings.persistent_title">Notification persistente</string>
- <string name="settings.persistent_summary">Afficher la notification mĆŖme aprĆØs la mise en pause. Appuyer sur stop pour l\'effacer.</string>
- <string name="settings.gapless_playback">Lecture sans saut</string>
- <string name="settings.gapless_playback_summary">Si vous rencontrez des problĆØmes lors de la lecture, dĆ©sactiver ceci pourrait aider.</string>
- <string name="settings.chat_refresh">FrƩquence de rafraƮchissement du chat (Secs)</string>
- <string name="settings.chat_enabled">Chat autorisƩ</string>
- <string name="settings.chat_enabled_summary">Afficher ou non la zone de chat</string>
- <string name="settings.video_title">Video</string>
- <string name="settings.video_player">Lecteur vidƩo</string>
- <string name="settings.video_raw">Raw (Necessite Subsonic 4.8+)</string>
- <string name="settings.video_hls">HTTP Live Stream (HLS) (Necessite Subsonic 4.8+)</string>
- <string name="settings.video_transcode">Direct Transcode (Necessite video -&gt; mp4 ou paramƩtrage identique sur le serveur)</string>
- <string name="settings.video_flash">Flash Necessite le Plugin)</string>
- <string name="settings.cache_screen_title">Tampon/RĆ©seau</string>
- <string name="settings.playback_title">Lecture</string>
- <string name="settings.hide_widget_title">Cacher le Widget</string>
- <string name="settings.hide_widget_summary">Cacher le widget aprĆØs avoir quittĆ© l\'application</string>
- <string name="settings.podcasts_enabled">Podcasts autorisƩs</string>
- <string name="settings.podcasts_enabled_summary">Afficher ou non l\'accĆØs aux podcasts</string>
- <string name="settings.bookmarks_enabled">Favoris autorisƩs</string>
- <string name="settings.bookmarks_enabled_summary">Afficher ou non l\'accĆØs aux favoris</string>
- <string name="settings.shares_enabled">Partages autorisƩs</string>
- <string name="settings.shares_enabled_summary">Afficher ou non l\'accĆØs aux partages</string>
- <string name="settings.sync_title">Sync</string>
- <string name="settings.sync_enabled">Sync autorisƩe</string>
- <string name="settings.sync_enabled_summary">Controler ou non pƩriodiquement si les playlists et podcasts ont ƩtƩ mis Ơ jour</string>
- <string name="settings.sync_interval">DĆ©lai de synchro</string>
- <string name="settings.sync_interval_15">15 Minutes</string>
- <string name="settings.sync_interval_30">30 Minutes</string>
- <string name="settings.sync_interval_60">1 Heure</string>
- <string name="settings.sync_interval_120">2 Heures</string>
- <string name="settings.sync_interval_240">4 Heures</string>
- <string name="settings.sync_interval_360">6 Heures</string>
- <string name="settings.sync_interval_720">12 Heures</string>
- <string name="settings.sync_interval_1440">Quotidiennement</string>
- <string name="settings.sync_wifi">Sync en Wifi uniquement</string>
- <string name="settings.sync_wifi_summary">Synchroniser uniquement quand la connexion Wifi est active</string>
- <string name="settings.sync_most_recent">Sync ajouts rƩcents</string>
- <string name="settings.sync_most_recent_summary">Charger automatiquement les nouveaux albums</string>
- <string name="settings.sync_starred">Sync notƩs</string>
- <string name="settings.sync_starred_summary">Charger automatiquement les titres, albums et artistes bien notƩs</string>
- <string name="settings.sync_notification">Afficher des notifications de synchro</string>
- <string name="settings.sync_notification_summary">Afficher une notification dĆØs qu\'un mĆ©dia a Ć©tĆ© synchronisĆ©</string>
- <string name="settings.server_sync_summary">Synchron autorisƩe ou non sur ce serveur</string>
- <string name="settings.server_sync">Synchro autorisƩe</string>
- <string name="settings.menu_options.title">Options de menu optionnelles</string>
- <string name="settings.menu_options.play_next_summary">Afficher Lire suivant dans les menus</string>
- <string name="settings.menu_options.play_last_summary">Afficher Lire dernier dans les menus</string>
- <string name="settings.menu_options.star_summary">Afficher Noter dans les menus</string>
- <string name="settings.menu_options.shared_summary">Afficher Partager dans les menus</string>
- <string name="settings.menu_options.rate_summary">Montrer les notes dans les menus</string>
- <string name="settings.browse_by_tags">Naviguer via les tags</string>
- <string name="settings.browse_by_tags_summary">Naviguer via les tags plutƓt que via l\'arborescence de fichier. NƩcessite Subsonic 4.7+</string>
- <string name="settings.override_system_language">Ne pas utiliser la langue du systĆØme</string>
- <string name="settings.override_system_language_summary">Afficher DSub en Anglais mĆŖme si une traduction existe pour la langue systĆØme. Peut nĆ©cessiter un vidage du cache de l\'application.</string>
- <string name="settings.drawer_items_title">EntrƩes de menu</string>
- <string name="settings.play_now_after">Jouer maintenant - Plus tard</string>
- <string name="settings.play_now_after_summary">Play Now context menu for a song plays everything after selected item (like the Subsonic web GUI)</string>
- <string name="settings.large_album_art">Pochettes larges</string>
- <string name="settings.large_album_art_summary">Afficher les pochettes en grand plutƓt qu\'en liste.</string>
- <string name="settings.admin_enabled">Administration</string>
- <string name="settings.admin_enabled_summary">Afficher ou non l\'accĆØs aux outils d\'administration</string>
-
- <string name="shuffle.title">Shuffle By</string>
- <string name="shuffle.startYear">AnnƩe dƩbut :</string>
- <string name="shuffle.endYear">AnnƩe fin :</string>
- <string name="shuffle.genre">Genre :</string>
- <string name="shuffle.pick_genre">Choisir un genre</string>
-
- <string name="share.info">PropriƩtaire : %1$s
- \nDescription: %2$s
- \nURL: %3$s
- \nCrƩation : %4$s
- \nDerniĆØre visite : %5$s
- \nExpiration : %6$s
- \nNombre de visites : %7$s
-
- </string>
- <string name="share.expires">Expiration : %s</string>
- <string name="share.expires_never">N\'expire jamais</string>
- <string name="share.deleted">Supprimer le partage %s</string>
- <string name="share.deleted_error">Echec de la suppression du partage %s</string>
- <string name="share.no_expiration">Pas d\'expiration</string>
- <string name="share.expiration">Expiration :</string>
- <string name="share.updated_info">Informations de partage mises Ć  jour pour %s</string>
- <string name="share.updated_info_error">Echec de la mise Ć  jour des informations de partage pour %s</string>
- <string name="share.via">Partager via</string>
- <string name="share.delete">Supprimer le partage</string>
-
- <string name="admin.add_user_username">Nom d\'utilisateur :</string>
- <string name="admin.add_user_email">Email :</string>
- <string name="admin.add_user_password">Mot de passe :</string>
- <string name="admin.create_user_success">Nouvel utilisateur crƩƩ</string>
- <string name="admin.create_user_error">Erreur Ơ la crƩation de l\'utilisateur</string>
- <string name="admin.change_username_invalid">Saisir un nom d\'utilisateur valide</string>
- <string name="admin.update_permissions">Mettre Ć  jour les autorisations</string>
- <string name="admin.update_permissions_success">Autorisation mises Ć  jour pour %1$s</string>
- <string name="admin.update_permissions_error">Echec lors de lamise Ć  jour des autorisations de %1$s</string>
- <string name="admin.change_email">Modifier Email</string>
- <string name="admin.change_email_success">Email remplacƩ pour %1$s</string>
- <string name="admin.change_email_error">Echec lors du remplacement de l\'Email de %1$s</string>
- <string name="admin.change_email_label">Nouvel Email :</string>
- <string name="admin.change_email_invalid">Saisir un Email valide</string>
- <string name="admin.change_password">Modifier le mot de passe</string>
- <string name="admin.change_password_success">Mot de passe modifiƩ pour %1$s</string>
- <string name="admin.change_password_error">Echec du remplacement du mot de passe pour %1$s</string>
- <string name="admin.change_password_label">Nouveau mot de passe :</string>
- <string name="admin.change_password_invalid">Saisir un mot de passe valide</string>
- <string name="admin.delete_user">Supprimer l\'utilisateur</string>
- <string name="admin.delete_user_success">Suppression effectuƩe %1$s</string>
- <string name="admin.delete_user_error">Echec de la suppression %1$s</string>
- <string name="admin.confirm_password">Confirmer le mot de passe</string>
- <string name="admin.confirm_password_bad">Mot de passe saisi erronƩ</string>
-
- <string name="admin.scrobblingEnabled">Diffusion autorisƩe</string>
- <string name="admin.role.admin">Administrateur</string>
- <string name="admin.role.settings">Modifier les paramĆØtres</string>
- <string name="admin.role.download">TƩlƩcharger les fichiers</string>
- <string name="admin.role.upload">TƩlƩverser sur le serveur</string>
- <string name="admin.role.coverArt">Modifier les pochettes</string>
- <string name="admin.role.comment">Ajouter des commentaires</string>
- <string name="admin.role.podcast">GĆ©rer les podcasts</string>
- <string name="admin.role.stream">Ecouter de la musique</string>
- <string name="admin.role.jukebox">TƩlƩcommander la lecture (jukebox)</string>
- <string name="admin.role.share">GĆ©rer les partages</string>
- <string name="admin.role.lastfm">Utiliser Last.FM</string>
-
- <string name="music_service.retry">Erreur rƩseau. Nouvelle tentative %1$d de %2$d.</string>
-
- <string name="background_task.wait">Patienter...</string>
- <string name="background_task.loading">Chargement.</string>
- <string name="background_task.no_network">Cette application nĆ©cessite un accĆØs rĆ©seau. Activer les connexion Wifi ou mobile.</string>
- <string name="background_task.network_error">Une erreur rƩseau est survenue. Merci de vƩrifier l\'adresse du serveur ou rƩessayer plus tard.</string>
- <string name="background_task.not_found">Ressource non trouvƩe. VƩrifier l\'adresse du serveur.</string>
- <string name="background_task.parse_error">Erreur de communication avec le serveur.VĆ©rifier l\'adresse du serveur et que la connexion via un navigateur fonctionne.</string>
-
- <string name="service.connecting">Interrogation du serveur, veuillez patienter.</string>
-
- <string name="parser.upgrade_client"> Versions incompatible. Mettre Ć  jour DSub.</string>
- <string name="parser.upgrade_server">Versions incompatibles. Mettre Ć  jour le serveur Subsonic.</string>
- <string name="parser.not_authenticated">Mauvais nom d\'utilisateur ou mot de passe.</string>
- <string name="parser.not_authorized">Non autorisƩ. VƩrifier les droit de l\'utilisateur sur le serveur Subsonic.</string>
- <string name="parser.artist_count">%d artistes rƩcupƩrƩs.</string>
- <string name="parser.server_error">Erreur serveur : %s</string>
- <string name="parser.scan_count">%d entrƩes trouvƩes</string>
-
- <string name="select_artist.refresh">Recharger</string>
- <string name="select_artist.folder">SĆ©lectionner un dossier</string>
- <string name="select_artist.all_folders">Tous les dossier</string>
-
- <string name="equalizer.label">Equaliseur</string>
- <string name="equalizer.enabled">ActivƩ</string>
- <string name="equalizer.preset">Selectioner un prƩrƩglage</string>
- <string name="equalizer.bass_booster">Bass Booster</string>
- <string name="equalizer.voice_booster">Voice Booster</string>
- <string name="equalizer.db_size">%d dB</string>
- <string name="equalizer.bass_size">%d mille</string>
-
- <string name="widget.4x1">DSub (4x1)</string>
- <string name="widget.4x2">DSub (4x2)</string>
- <string name="widget.4x3">DSub (4x3)</string>
- <string name="widget.4x4">DSub (4x4)</string>
- <string name="widget.initial_text">Toucher pour choisir de la musique</string>
- <string name="widget.sdcard_busy">Carte SD indisponible</string>
- <string name="widget.sdcard_missing">Pas de carte SD</string>
-
- <string name="util.bytes_format.gigabyte">0.00 GB</string>
- <string name="util.bytes_format.megabyte">0.00 MB</string>
- <string name="util.bytes_format.kilobyte">0 KB</string>
- <string name="util.bytes_format.byte">0 B</string>
-
- <string name="changelog_full_title">Change Log</string>
- <string name="changelog_title">What\'s New</string>
- <string name="changelog_ok_button">OK</string>
- <string name="changelog_show_full">Plusā€¦</string>
-
- <string name="chat.send_a_message">Envoyer un message</string>
-
- <string name="changelog_version_format" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">Version <xliff:g id="version_name">%s</xliff:g></string>
-
- <string name="tasker.start_playing">Commencer la lecture</string>
- <string name="tasker.start_playing_title">Tasker -> DĆ©marrer DSub</string>
- <string name="tasker.edit_shuffle_mode">DƩmarrer en mode lecture alƩatoire : </string>
- <string name="tasker.start_playing_shuffled">DƩmarrer la lecture en mode lecture alƩatoire</string>
-
- <plurals name="select_album_n_songs">
- <item quantity="zero">Aucun titre</item>
- <item quantity="one">Un titre</item>
- <item quantity="other">%d titres</item>
- </plurals>
- <plurals name="select_album_n_songs_downloading">
- <item quantity="one">TƩlƩchargement programmƩ pour un titre.</item>
- <item quantity="other">TƩlƩchargement programmƩ pour %d titres.</item>
- </plurals>
- <plurals name="select_album_n_songs_added">
- <item quantity="one">Un titre a ƩtƩ ajoutƩ Ơ la liste de lecture en cours.</item>
- <item quantity="other">%d titres ont ƩtƩ ajoutƩs Ơ la liste de lecture en cours.</item>
- </plurals>
- <plurals name="select_album_donate_dialog_n_trial_days_left">
- <item quantity="one">Dernier jour avant la fin de pƩriode d\'essai.</item>
- <item quantity="other">%d jours avant la fin de pƩriode d\'essai.</item>
- </plurals>
-
-</resources>
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <string name="common.appname">DSub</string>
+ <string name="common.ok">OK</string>
+ <string name="common.save">Sauver</string>
+ <string name="common.cancel">Annuler</string>
+ <string name="common.play_now">Jouer</string>
+ <string name="common.play_shuffled">Jouer au hasard</string>
+ <string name="common.play_next">Suivant</string>
+ <string name="common.play_last">PrƩcƩdent</string>
+ <string name="common.download">Mettre en cache</string>
+ <string name="common.pin">Mettre en cache Permanent</string>
+ <string name="common.delete">Supprimer</string>
+ <string name="common.star">Favori</string>
+ <string name="common.unstar">Supp. favori</string>
+ <string name="common.info">DĆ©tails</string>
+ <string name="common.name">Nom</string>
+ <string name="common.comment">Commentaire</string>
+ <string name="common.public">Publique</string>
+ <string name="common.play_external">Jouer Video</string>
+ <string name="common.stream_external">Stream Video</string>
+ <string name="common.confirm">Confirmer</string>
+ <string name="common.confirm_message">Voulez-vous %1$s %2$s ?</string>
+ <string name="common.confirm_message_cache">cache</string>
+ <string name="common.empty">Aucune donnƩe</string>
+ <string name="common.warning">Avertissement</string>
+
+ <string name="button_bar.home">Accueil</string>
+ <string name="button_bar.browse">BibliothĆØque</string>
+ <string name="button_bar.search">Recherche</string>
+ <string name="button_bar.playlists">Playlists</string>
+ <string name="button_bar.now_playing">Lecture en cours</string>
+ <string name="button_bar.podcasts">Podcasts</string>
+ <string name="button_bar.bookmarks">Favoris</string>
+ <string name="button_bar.shares">Partages</string>
+ <string name="button_bar.chat">Chat</string>
+ <string name="button_bar.admin">Admin</string>
+ <string name="button_bar.downloading">TƩlƩchargement</string>
+
+ <string name="main.welcome_title">Bienvenue !</string>
+ <string name="main.welcome_text">Bienvenue dans DSub ! L\'application est actuellement configurĆ©e pour se connecter au serveur de dĆ©mo Subsonic (<b>demo.subsonic.org</b>). Vous pouvez configurer votre propre serveur dans les paramĆØtres. Choisir <b>ParamĆØtres</b> et mettre Ć  jour la configuration pour vous y connecter.</string>
+ <string name="main.about_title">A propos de DSub</string>
+ <string name="main.about_text">Auteur : Scott Jackson
+ \nEmail : dsub.android@gmail.com
+ \nVersion : %1$s
+ \nFichiers en cache : %2$s
+ \nEspace utilisƩ : %3$s de %4$s
+ \nEspace dispo. : %5$s de %6$s</string>
+ <string name="main.faq_title">FAQ</string>
+ <string name="main.faq_text">
+ <![CDATA[
+ <font color="red">Cache vs Cache permanent</font> :
+ <br/>Lorsque des titres sont tĆ©lĆ©chargĆ©s par DSub, ils peuvent ĆŖtre supprimĆ©s pour libĆ©rer de l\'espace pour de nouveaux tĆ©lĆ©chargements. Le cache permanent premet, lui, de ne jamais supprimer automatiquement la musique tĆ©lĆ©chargĆ©e.
+ <p/><font color="red">ChromeCast a ƩchouƩ</font> :
+ <br/>Assurez-vous de ne pas utiliser un certificat auto-signƩ, Chromecast les rejette systƩmatiquement.
+ ]]>
+ </string>
+ <string name="main.select_server">Choisir un serveur</string>
+ <string name="main.shuffle">Jouer au hasard</string>
+ <string name="main.offline">DĆ©connecter</string>
+ <string name="main.online">Connecter</string>
+ <string name="main.settings">ParamĆØtres</string>
+ <string name="main.albums_title">Albums</string>
+ <string name="main.albums_newest">AjoutƩs rƩcemments</string>
+ <string name="main.albums_recent">JouƩs rƩcemment</string>
+ <string name="main.albums_frequent">Les plus jouƩs</string>
+ <string name="main.albums_highest">Les mieux notƩs</string>
+ <string name="main.albums_starred">Favoris</string>
+ <string name="main.albums_random">Au hasard</string>
+ <string name="main.albums_genres">Par genres</string>
+ <string name="main.albums_year">Par dƩcennies</string>
+ <string name="main.songs_genres">@string/main.albums_genres</string>
+ <string name="main.back_confirm">Presser retour Ć  nouveau pour quitter</string>
+ <string name="main.scan_complete">Completed scan of Server</string>
+
+ <string name="menu.search">Recherche</string>
+ <string name="menu.shuffle">Hasard</string>
+ <string name="menu.refresh">Recharger</string>
+ <string name="menu.play">Jouer</string>
+ <string name="menu.exit">Quitter</string>
+ <string name="menu.play_last">Jouer dernier</string>
+
+ <string name="menu.settings">ParamĆØtres</string>
+ <string name="menu.help">Aide</string>
+ <string name="menu.about">A propos</string>
+ <string name="menu.add_playlist">Ajouter Ć  la playlist</string>
+ <string name="menu.remove_playlist">Supprimer de la playlist</string>
+ <string name="menu.deleted_playlist">Supprimer la playlist %s</string>
+ <string name="menu.deleted_playlist_error">Echec de la suppression de la playlist %s</string>
+ <string name="menu.log">Envoyer le journal</string>
+ <string name="menu.set_timer">Ajuster le minuteur</string>
+ <string name="menu.check_podcasts">VĆ©rifier les nouveaux podcasts</string>
+ <string name="menu.add_podcast">Ajouter une chaƮne</string>
+ <string name="menu.keep_synced">Synchronisation automatique</string>
+ <string name="menu.stop_sync">ArrĆŖter la synchro.</string>
+ <string name="menu.show_all">Afficher tous les media</string>
+ <string name="menu.show_artist">Afficher l\'artiste</string>
+ <string name="menu.share">Partager</string>
+ <string name="menu.delete_cache">Supprimer du cache</string>
+ <string name="menu.cast">Diffuser vers appareil</string>
+ <string name="menu.faq">FAQ</string>
+ <string name="menu.add_user">Ajouter utilisateur</string>
+ <string name="menu.rescan">Relire le server</string>
+ <string name="menu.rate">Noter</string>
+
+ <string name="playlist.label">Playlists</string>
+ <string name="playlist.update_info">Mise Ć  jour informations</string>
+ <string name="playlist.updated_info">Informations de la playlist %s mises Ć  jour</string>
+ <string name="playlist.updated_info_error">Echec de la mise Ć  jour des informations de la playlist %s</string>
+ <string name="playlist.overwrite">Remplacer la playlist existante</string>
+ <string name="playlist.add_to">Ajouter Ć  la playlist</string>
+ <string name="playlist.create_new">CrƩer une nouvelle</string>
+ <string name="playlist.delete">Supprimer la playlist</string>
+
+ <string name="search.label">Recherche</string>
+ <string name="search.title">Recherche</string>
+ <string name="search.search">Cliquer pour rechercher</string>
+ <string name="search.no_match">Aucun rƩsultat, recommencer</string>
+ <string name="search.artists">Artistes</string>
+ <string name="search.albums">Albums</string>
+ <string name="search.songs">Chansons</string>
+ <string name="search.more">Afficher plus</string>
+
+ <string name="progress.wait">Patientezā€¦</string>
+
+ <string name="music_library.label">BibliothĆØque</string>
+ <string name="music_library.label_offline">MƩdia mode dƩconnectƩ</string>
+
+ <string name="select_album.select">Tout sƩlectionner</string>
+ <string name="select_album.n_selected">%d pistes sƩlectionnƩes.</string>
+ <string name="select_album.n_unselected">%d pistes dƩsƩlectionnƩes.</string>
+ <string name="select_album.more">Plus</string>
+ <string name="select_album.offline">DƩconnectƩ</string>
+ <string name="select_album.searching">Recherche en cours...</string>
+ <string name="select_album.no_sdcard">Erreur : Aucune carte SD card disponible.</string>
+ <string name="select_album.no_network">ProblĆØme : Aucun rĆ©seau disponible.</string>
+ <string name="select_album.not_licensed">Serveur sans licence valide. %d jours restant.</string>
+ <string name="select_album.donate_dialog_message">TƩlƩchargement illimitƩ en supportant Subsonic.</string>
+ <string name="select_album.donate_dialog_now">Maintenant</string>
+ <string name="select_album.donate_dialog_later">Plus tard</string>
+ <string name="select_album.donate_dialog_0_trial_days_left">La pƩriode d\'essai est terminƩe</string>
+
+ <string name="offline.sync_dialog_title">Chanson dƩconnectƩes en attente de synchro</string>
+ <string name="offline.sync_dialog_message">GƩrer %1$d prƩfƩrences en mode dƩconnectƩ ?
+ \nGƩrer %2$d favoris en mode dƩconnectƩ ?
+ </string>
+ <string name="offline.sync_dialog_default">Utiliser cette action par dƩfaut</string>
+ <string name="offline.sync_success">%1$d chansons synchronisĆ©es avec succĆØs</string>
+ <string name="offline.sync_partial">%1$d chansons sur %2$d synchronisĆ©es avec succĆØs</string>
+ <string name="offline.sync_error">Echec de la synchro. des chansons</string>
+
+ <string name="select_genre.blank">Vide</string>
+ <string name="select_genre.songs">%d chansons</string>
+ <string name="select_genre.albums">%d albums</string>
+
+ <string name="select_podcasts.error">Une erreur est survenue avec ce podcast pendant le chargement. Le serveur doit d\'abord le tƩlƩcharger.</string>
+ <string name="select_podcasts.skipped">Ce podcast n\'a pas ƩtƩ chargƩ sur le serveur. Le serveur doit d\'abord le tƩlƩcharger.</string>
+ <string name="select_podcasts.initializing">Le chargement du podcast a commencer sur le serveur. Recharger SVP dans quelques instants.</string>
+ <string name="select_podcasts.server_download">TƩlƩcharger sur le serveur</string>
+ <string name="select_podcasts.server_delete">Supprimer du serveur</string>
+ <string name="select_podcasts.downloading">TƩlƩchargement %s sur le serveur</string>
+ <string name="select_podcasts.refreshing">Le serveur recherche les mises Ć  jour de podcasts</string>
+ <string name="select_podcasts.deleted">Podcast supprimƩ %s</string>
+ <string name="select_podcasts.deleted_error">Erreur lors de la suppression du podcast %s</string>
+ <string name="select_podcasts.add_url">URL :</string>
+ <string name="select_podcasts.created_error">Erreur lors de l\'ajout du podcast</string>
+ <string name="select_podcasts.invalid_podcast_channel">Podcast invalide : %s</string>
+ <string name="select_podcasts.delete">Supprimer podcast</string>
+
+ <string name="download.empty">La playlist est vide</string>
+ <string name="download.shuffle_loading">Chargement en cours liste au hasard...</string>
+ <string name="download.playerstate_downloading">Chargement - %s</string>
+ <string name="download.playerstate_buffering">Mise en mƩmoire tampon</string>
+ <string name="download.playerstate_playing_shuffle">Lecture au hasard</string>
+ <string name="download.menu_show_album">Afficher l\'album</string>
+ <string name="download.menu_lyrics">Paroles</string>
+ <string name="download.menu_remove">Enlever de la queue</string>
+ <string name="download.menu_remove_all">Enlever tout</string>
+ <string name="download.menu_screen_on">Ecran actif</string>
+ <string name="download.menu_shuffle">Hasard</string>
+ <string name="download.menu_toggle">Basculer</string>
+ <string name="download.menu_save">Enregistrer la playlist</string>
+ <string name="download.menu_shuffle_notification">La playlist a ƩtƩ mƩlangƩe</string>
+ <string name="download.menu_remove_played_songs">Supprimer les titres dƩjƠ jouƩ</string>
+ <string name="download.playlist_title">Enregistrer la playlist</string>
+ <string name="download.playlist_name">Nom de la playlist :</string>
+ <string name="download.playlist_saving">Enregistrement playlist \&quot;%s\&quot;...</string>
+ <string name="download.playlist_done">La playlist a bien ƩtƩ enregistrƩe.</string>
+ <string name="download.playlist_error">Erreur Ơ l\'enregistrement de la playlist, rƩessayer plus tard.</string>
+ <string name="download.repeat_off">RƩpƩter inactif</string>
+ <string name="download.repeat_all">RƩpƩter tout</string>
+ <string name="download.repeat_single">RƩpƩter titre</string>
+ <string name="download.jukebox_on">TƩlƩcommande activƩe. La musique est diffusƩe sur l\'ordinateur.</string>
+ <string name="download.jukebox_off">TƩlƩcommande dƩsactivƩe. La musique est diffusƩe sur le mobile.</string>
+ <string name="download.jukebox_volume">Volume distant</string>
+ <string name="download.jukebox_server_too_old">TƩlƩcommande non supportƩe. Mettre Ơ jour le serveur Subsonic.</string>
+ <string name="download.jukebox_offline">La tƩlƩcommande n\'est pas disponible en mode dƩconnectƩ.</string>
+ <string name="download.jukebox_not_authorized">Mode tƩlƩcommande non autorisƩe. Activer le mode jukebox.<b>Users &gt; Settings</b> on your Subsonic server.</string>
+ <string name="download.timer_length">Minuteur :</string>
+ <string name="download.start_timer">DĆ©marrer le minuteur</string>
+ <string name="download.stop_timer">ArrĆŖter le minuteur</string>
+ <string name="download.need_download">La vidĆ©o doit d\'abord ĆŖtre tĆ©lĆ©chargĆ©e</string>
+ <string name="download.no_streaming_player">Aucun lecteur ne peut afficher ce flux</string>
+ <string name="download.playing_out_of">Lecture : %1$d/%2$d</string>
+ <string name="download.save_bookmark_title">CrƩer un favori</string>
+ <string name="download.save_bookmark">Favori crƩƩ</string>
+ <string name="download.downloading_title">Chargement des titres %1$d</string>
+ <string name="download.downloading_summary">En cours : %1$s</string>
+ <string name="download.downloading_summary_expanded">En cours : %1$s
+ \nTaille estimƩe : %2$s</string>
+ <string name="download.failed_to_load">Echec du chargement</string>
+ <string name="download.save_bookmark_failed">Echec de la crƩation du favori</string>
+
+ <string name="sync.new_podcasts">Nouveaux podcasts disponibles</string>
+ <string name="sync.new_playlists">Nouveaux titres dans les playlists</string>
+ <string name="sync.new_albums">Nouveaux albums disponibles</string>
+ <string name="sync.new_starred">Nouveaux titres notƩs disponibles</string>
+
+ <string name="starring_content_starred">NotƩ \&quot;%s\&quot;</string>
+ <string name="starring_content_unstarred">DƩvaluƩs \&quot;%s\&quot;</string>
+ <string name="starring_content_error">Echec de la mise Ć  jour \&quot;%s\&quot;, RĆ©essayer plus tard.</string>
+
+ <string name="playlist_error">Echec de la rƩcupƩration des playlists</string>
+ <string name="updated_playlist">Titre %1$s ajoutƩ Ơ \&quot;%2$s\&quot;</string>
+ <string name="updated_playlist_error">Echec de la mise Ơ jour \&quot;%s\&quot;, rƩessayer plus tard.</string>
+ <string name="removed_playlist">Titre %1$s retirƩ de \&quot;%2$s\&quot;</string>
+
+ <string name="bookmark.delete">Supprimer le favori</string>
+ <string name="bookmark.delete_title">Dupprimer le favori pour</string>
+ <string name="bookmark.deleted">Favori pour \&quot;%s\&quot; supprimƩ</string>
+ <string name="bookmark.deleted_error">Echec de la suppression du favori pour \&quot;%s\&quot;</string>
+ <string name="bookmark.details_title">DĆ©tails du favori</string>
+ <string name="bookmark.details">Titre : %1$s
+ \nPosition : %2$s
+ \nCrƩƩ le : %3$s
+ \nMis Ć  jour : %4$s
+ \nCommentaire : %5$s</string>
+ <string name="bookmark.resume_title">Reprendre la lecture ?</string>
+ <string name="bookmark.resume">Reprendre la lecture de \'%1$s\' depuis %2$s</string>
+ <string name="bookmark.action_resume">Reprendre</string>
+ <string name="bookmark.action_start_over">Start Over</string>
+
+ <string name="rating.title">Noter \"%s\"</string>
+ <string name="rating.set_rating">Note attribuƩe Ơ \"%s\"</string>
+ <string name="rating.set_rating_failed">Echec de l\'attribution de la note Ć  \"%s\"</string>
+ <string name="rating.remove_rating">Note supprimƩe pour \"%s\"</string>
+ <string name="rating.remove_rating_failed">Echec de la suppression de la note pour \"%s\"</string>
+
+ <string name="song_details.error">Erreur</string>
+ <string name="song_details.skipped">IgnorƩ</string>
+ <string name="song_details.downloading">Chargement</string>
+
+ <string name="lyrics.nomatch">Aucune paroles trouvƩes</string>
+
+ <string name="error.label">Erreur</string>
+
+ <string name="settings.title">ParamĆØtres DSub</string>
+ <string name="settings.test_connection_title">Test de connexion</string>
+ <string name="settings.servers_add">Ajouter un serveur</string>
+ <string name="settings.servers_remove">Supprimer le serveur</string>
+ <string name="settings.servers_title">Serveurs</string>
+ <string name="settings.server_unused">InutilisƩ</string>
+ <string name="settings.server_name">Nom</string>
+ <string name="settings.server_address">Adresse du serveur</string>
+ <string name="settings.server_local_network_ssid" >SSID du rƩseau local</string>
+ <string name="settings.server_local_network_ssid_hint">SSID Actuel : %s</string>
+ <string name="settings.server_internal_address">Adresse sur le rƩseau local</string>
+ <string name="settings.server_username">Nom d\'utilisateur</string>
+ <string name="settings.server_password">Mot de passe</string>
+ <string name="settings.server_open_browser">Ouvrir dans le navigateur</string>
+ <string name="settings.cache_title">MĆ©moire tampon pour la musique</string>
+ <string name="settings.preload_wifi">Titres Ơ prƩcharger (Wifi)</string>
+ <string name="settings.preload_mobile">Titre Ơ prƩcharger (Mobile)</string>
+ <string name="settings.cache_size">Taille de la mƩmoire tampon (MB)</string>
+ <string name="settings.cache_location">Emplacement de la mƩmoire tampon</string>
+ <string name="settings.cache_location_error">Emplacement invalide pour la mƩmoire tampon. Utilisation emplacement par dƩfaut.</string>
+ <string name="settings.cache_location_reset">L\'emplacement choisi pour la mĆ©moire tampon ne peut plus ĆŖtre utilisĆ©. S\'il y a eu une mise Ć  jour rĆ©cente vers Android 4.4 KiktKat, la faƧon dont les application Ć©crivent sur la carte SD Ć  changĆ©e afin que ces derniĆØres ne puissent Ć©crire que dans un emplacement spĆ©cifique. L\'emplacement Ć  utiliser pour DSub a dĆ©jĆ  Ć©tĆ© mis Ć  jour. Afin de supprimer toutes les donnĆ©es obsolĆØtes, il faut vider l\'ancien emplacement en utilisant un ordinateur.</string>
+ <string name="settings.cache_clear">Vider la mƩmoire tampon</string>
+ <string name="settings.cache_clear_complete">MƩmoire tampon vidƩe</string>
+ <string name="settings.testing_connection">Test de connexion...</string>
+ <string name="settings.testing_ok">Connexion OK</string>
+ <string name="settings.testing_unlicensed">Connexion OK. Pas de licence.</string>
+ <string name="settings.connection_failure">Echec de la connexion.</string>
+ <string name="settings.invalid_url">Saisir une URL valide.</string>
+ <string name="settings.invalid_username">Saisir un nom d\'utilisateur valide (espaces interdits).</string>
+ <string name="settings.appearance_title">Apparence</string>
+ <string name="settings.theme_title">ThĆØme</string>
+ <string name="settings.theme_light">Light</string>
+ <string name="settings.theme_dark">Dark</string>
+ <string name="settings.theme_black">Black</string>
+ <string name="settings.theme_holo">Holo</string>
+ <string name="settings.theme_fullscreen">Plein Ć©cran</string>
+ <string name="settings.theme_fullscreen_summary">Cacher autant d\'ƩlƩment graphique que possible</string>
+ <string name="settings.track_title">Afficher nĀ° piste</string>
+ <string name="settings.track_summary">Afficher le nĀ° de piste devant les titres</string>
+ <string name="settings.custom_sort">Trier par annƩes</string>
+ <string name="settings.custom_sort_summary">Trier les albums par annƩe, ou par ordre alphabƩtique</string>
+ <string name="settings.network_title">RĆ©seau</string>
+ <string name="settings.max_bitrate_wifi">DĆ©bit audio max (Wifi)</string>
+ <string name="settings.max_bitrate_mobile">DĆ©bit audio max (Mobile)</string>
+ <string name="settings.max_bitrate_32">32 Kbps</string>
+ <string name="settings.max_bitrate_64">64 Kbps</string>
+ <string name="settings.max_bitrate_80">80 Kbps</string>
+ <string name="settings.max_bitrate_96">96 Kbps</string>
+ <string name="settings.max_bitrate_112">112 Kbps</string>
+ <string name="settings.max_bitrate_128">128 Kbps</string>
+ <string name="settings.max_bitrate_160">160 Kbps</string>
+ <string name="settings.max_bitrate_192">192 Kbps</string>
+ <string name="settings.max_bitrate_256">256 Kbps</string>
+ <string name="settings.max_bitrate_320">320 Kbps</string>
+ <string name="settings.max_video_bitrate_wifi">DƩbit vidƩo max (Wifi)</string>
+ <string name="settings.max_video_bitrate_mobile">DƩbit vidƩo max (Mobile)</string>
+ <string name="settings.max_video_bitrate_200">200 Kbps</string>
+ <string name="settings.max_video_bitrate_300">300 Kbps</string>
+ <string name="settings.max_video_bitrate_400">400 Kbps</string>
+ <string name="settings.max_video_bitrate_500">500 Kbps</string>
+ <string name="settings.max_video_bitrate_700">700 Kbps</string>
+ <string name="settings.max_video_bitrate_1000">1000 Kbps</string>
+ <string name="settings.max_video_bitrate_1500">1500 Kbps</string>
+ <string name="settings.max_video_bitrate_2000">2000 Kbps</string>
+ <string name="settings.max_video_bitrate_3000">3000 Kbps</string>
+ <string name="settings.max_video_bitrate_5000">5000 Kbps</string>
+ <string name="settings.max_bitrate_unlimited">IllimitƩ</string>
+ <string name="settings.wifi_required_title">Streaming en Wifi uniquement</string>
+ <string name="settings.wifi_required_summary">Ne lire les mƩdia qu\'avec une connexion Wifi</string>
+ <string name="settings.network_timeout_title">DƩlai d\'attente rƩseau (timeout)</string>
+ <string name="settings.network_timeout_10000">10 secondes</string>
+ <string name="settings.network_timeout_15000">15 secondes</string>
+ <string name="settings.network_timeout_30000">30 secondes</string>
+ <string name="settings.network_timeout_45000">45 secondes</string>
+ <string name="settings.network_timeout_60000">60 secondes</string>
+ <string name="settings.preload_0">0 titre</string>
+ <string name="settings.preload_1">1 titre</string>
+ <string name="settings.preload_2">2 titres</string>
+ <string name="settings.preload_3">3 titres</string>
+ <string name="settings.preload_5">5 titres</string>
+ <string name="settings.preload_10">10 titres</string>
+ <string name="settings.preload_unlimited">IllimitƩ</string>
+ <string name="settings.clear_search_history">Effacer l\'historique de recherche</string>
+ <string name="settings.search_history_cleared">Rechercher dans l\'historique effacƩ</string>
+ <string name="settings.other_title">Autres paramĆØtres</string>
+ <string name="settings.scrobble_title">Publier vers Last.fm</string>
+ <string name="settings.scrobble_summary">Penser Ơ paramƩtrer votre compte Last.fm sur le serveur Subsonic</string>
+ <string name="settings.hide_media_title">Invisible pour les autres</string>
+ <string name="settings.hide_media_summary">Rendre les fichiers de musique indisponibles pour les autres applications.</string>
+ <string name="settings.hide_media_toast">Prendra effet au prochain scan de musique d\'Android.</string>
+ <string name="settings.media_button_title">Boutons physique</string>
+ <string name="settings.media_button_summary">RĆ©pondre aux boutons du mobile, du casque filaire ou bluetooth</string>
+ <string name="settings.screen_lit_title">Garder l\'Ʃcran allumƩ</string>
+ <string name="settings.screen_lit_summary">Garder l\'Ʃcran allumƩ augmente la vitesse de tƩlƩchargement.</string>
+ <string name="settings.playlist_title">Lecture</string>
+ <string name="settings.playlist_random_size_title">Taille de la liste de lecture alƩatoire</string>
+ <string name="settings.sleep_timer_title">Temporisateur</string>
+ <string name="settings.sleep_timer_duration_title">DurƩe temporisation</string>
+ <string name="settings.sleep_timer_off">Eteindre</string>
+ <string name="settings.sleep_timer_on">Allumer</string>
+ <string name="settings.sleep_timer_always_on">Toujours en fonctionnement</string>
+ <string name="settings.temp_loss_title">Perte temporaire de focus</string>
+ <string name="settings.temp_loss_pause">Toujours mettre en pause</string>
+ <string name="settings.temp_loss_pause_lower">Pause, baisser le volume si demandƩ</string>
+ <string name="settings.temp_loss_lower">Toujours baisser le volume</string>
+ <string name="settings.temp_loss_nothing">Ne rien faire</string>
+ <string name="settings.disconnect_pause_title">Pause Ơ la dƩconnexion</string>
+ <string name="settings.disconnect_pause_both">Pause</string>
+ <string name="settings.disconnect_pause_neither">Ne rien faire</string>
+ <string name="settings.persistent_title">Notification persistente</string>
+ <string name="settings.persistent_summary">Afficher la notification mĆŖme aprĆØs la mise en pause. Appuyer sur stop pour l\'effacer.</string>
+ <string name="settings.gapless_playback">Lecture sans saut</string>
+ <string name="settings.gapless_playback_summary">Si vous rencontrez des problĆØmes lors de la lecture, dĆ©sactiver ceci pourrait aider.</string>
+ <string name="settings.chat_refresh">FrƩquence de rafraƮchissement du chat (Secs)</string>
+ <string name="settings.chat_enabled">Chat autorisƩ</string>
+ <string name="settings.chat_enabled_summary">Afficher ou non la zone de chat</string>
+ <string name="settings.video_title">Video</string>
+ <string name="settings.video_player">Lecteur vidƩo</string>
+ <string name="settings.video_raw">Raw (Necessite Subsonic 4.8+)</string>
+ <string name="settings.video_hls">HTTP Live Stream (HLS) (Necessite Subsonic 4.8+)</string>
+ <string name="settings.video_transcode">Direct Transcode (Necessite video -&gt; mp4 ou paramƩtrage identique sur le serveur)</string>
+ <string name="settings.video_flash">Flash Necessite le Plugin)</string>
+ <string name="settings.cache_screen_title">Tampon/RĆ©seau</string>
+ <string name="settings.playback_title">Lecture</string>
+ <string name="settings.hide_widget_title">Cacher le Widget</string>
+ <string name="settings.hide_widget_summary">Cacher le widget aprĆØs avoir quittĆ© l\'application</string>
+ <string name="settings.podcasts_enabled">Podcasts autorisƩs</string>
+ <string name="settings.podcasts_enabled_summary">Afficher ou non l\'accĆØs aux podcasts</string>
+ <string name="settings.bookmarks_enabled">Favoris autorisƩs</string>
+ <string name="settings.bookmarks_enabled_summary">Afficher ou non l\'accĆØs aux favoris</string>
+ <string name="settings.shares_enabled">Partages autorisƩs</string>
+ <string name="settings.shares_enabled_summary">Afficher ou non l\'accĆØs aux partages</string>
+ <string name="settings.sync_title">Sync</string>
+ <string name="settings.sync_enabled">Sync autorisƩe</string>
+ <string name="settings.sync_enabled_summary">Controler ou non pƩriodiquement si les playlists et podcasts ont ƩtƩ mis Ơ jour</string>
+ <string name="settings.sync_interval">DĆ©lai de synchro</string>
+ <string name="settings.sync_interval_15">15 Minutes</string>
+ <string name="settings.sync_interval_30">30 Minutes</string>
+ <string name="settings.sync_interval_60">1 Heure</string>
+ <string name="settings.sync_interval_120">2 Heures</string>
+ <string name="settings.sync_interval_240">4 Heures</string>
+ <string name="settings.sync_interval_360">6 Heures</string>
+ <string name="settings.sync_interval_720">12 Heures</string>
+ <string name="settings.sync_interval_1440">Quotidiennement</string>
+ <string name="settings.sync_wifi">Sync en Wifi uniquement</string>
+ <string name="settings.sync_wifi_summary">Synchroniser uniquement quand la connexion Wifi est active</string>
+ <string name="settings.sync_most_recent">Sync ajouts rƩcents</string>
+ <string name="settings.sync_most_recent_summary">Charger automatiquement les nouveaux albums</string>
+ <string name="settings.sync_starred">Sync notƩs</string>
+ <string name="settings.sync_starred_summary">Charger automatiquement les titres, albums et artistes bien notƩs</string>
+ <string name="settings.sync_notification">Afficher des notifications de synchro</string>
+ <string name="settings.sync_notification_summary">Afficher une notification dĆØs qu\'un mĆ©dia a Ć©tĆ© synchronisĆ©</string>
+ <string name="settings.server_sync_summary">Synchron autorisƩe ou non sur ce serveur</string>
+ <string name="settings.server_sync">Synchro autorisƩe</string>
+ <string name="settings.menu_options.title">Options de menu optionnelles</string>
+ <string name="settings.menu_options.play_next_summary">Afficher Lire suivant dans les menus</string>
+ <string name="settings.menu_options.play_last_summary">Afficher Lire dernier dans les menus</string>
+ <string name="settings.menu_options.star_summary">Afficher Noter dans les menus</string>
+ <string name="settings.menu_options.shared_summary">Afficher Partager dans les menus</string>
+ <string name="settings.menu_options.rate_summary">Montrer les notes dans les menus</string>
+ <string name="settings.browse_by_tags">Naviguer via les tags</string>
+ <string name="settings.browse_by_tags_summary">Naviguer via les tags plutƓt que via l\'arborescence de fichier. NƩcessite Subsonic 4.7+</string>
+ <string name="settings.override_system_language">Ne pas utiliser la langue du systĆØme</string>
+ <string name="settings.override_system_language_summary">Afficher DSub en Anglais mĆŖme si une traduction existe pour la langue systĆØme. Peut nĆ©cessiter un vidage du cache de l\'application.</string>
+ <string name="settings.drawer_items_title">EntrƩes de menu</string>
+ <string name="settings.play_now_after">Jouer maintenant - Plus tard</string>
+ <string name="settings.play_now_after_summary">Play Now context menu for a song plays everything after selected item (like the Subsonic web GUI)</string>
+ <string name="settings.large_album_art">Pochettes larges</string>
+ <string name="settings.large_album_art_summary">Afficher les pochettes en grand plutƓt qu\'en liste.</string>
+ <string name="settings.admin_enabled">Administration</string>
+ <string name="settings.admin_enabled_summary">Afficher ou non l\'accĆØs aux outils d\'administration</string>
+
+ <string name="shuffle.title">Shuffle By</string>
+ <string name="shuffle.startYear">AnnƩe dƩbut :</string>
+ <string name="shuffle.endYear">AnnƩe fin :</string>
+ <string name="shuffle.genre">Genre :</string>
+ <string name="shuffle.pick_genre">Choisir un genre</string>
+
+ <string name="share.info">PropriƩtaire : %1$s
+ \nDescription: %2$s
+ \nURL: %3$s
+ \nCrƩation : %4$s
+ \nDerniĆØre visite : %5$s
+ \nExpiration : %6$s
+ \nNombre de visites : %7$s
+
+ </string>
+ <string name="share.expires">Expiration : %s</string>
+ <string name="share.expires_never">N\'expire jamais</string>
+ <string name="share.deleted">Supprimer le partage %s</string>
+ <string name="share.deleted_error">Echec de la suppression du partage %s</string>
+ <string name="share.no_expiration">Pas d\'expiration</string>
+ <string name="share.expiration">Expiration :</string>
+ <string name="share.updated_info">Informations de partage mises Ć  jour pour %s</string>
+ <string name="share.updated_info_error">Echec de la mise Ć  jour des informations de partage pour %s</string>
+ <string name="share.via">Partager via</string>
+ <string name="share.delete">Supprimer le partage</string>
+
+ <string name="admin.add_user_username">Nom d\'utilisateur :</string>
+ <string name="admin.add_user_email">Email :</string>
+ <string name="admin.add_user_password">Mot de passe :</string>
+ <string name="admin.create_user_success">Nouvel utilisateur crƩƩ</string>
+ <string name="admin.create_user_error">Erreur Ơ la crƩation de l\'utilisateur</string>
+ <string name="admin.change_username_invalid">Saisir un nom d\'utilisateur valide</string>
+ <string name="admin.update_permissions">Mettre Ć  jour les autorisations</string>
+ <string name="admin.update_permissions_success">Autorisation mises Ć  jour pour %1$s</string>
+ <string name="admin.update_permissions_error">Echec lors de lamise Ć  jour des autorisations de %1$s</string>
+ <string name="admin.change_email">Modifier Email</string>
+ <string name="admin.change_email_success">Email remplacƩ pour %1$s</string>
+ <string name="admin.change_email_error">Echec lors du remplacement de l\'Email de %1$s</string>
+ <string name="admin.change_email_label">Nouvel Email :</string>
+ <string name="admin.change_email_invalid">Saisir un Email valide</string>
+ <string name="admin.change_password">Modifier le mot de passe</string>
+ <string name="admin.change_password_success">Mot de passe modifiƩ pour %1$s</string>
+ <string name="admin.change_password_error">Echec du remplacement du mot de passe pour %1$s</string>
+ <string name="admin.change_password_label">Nouveau mot de passe :</string>
+ <string name="admin.change_password_invalid">Saisir un mot de passe valide</string>
+ <string name="admin.delete_user">Supprimer l\'utilisateur</string>
+ <string name="admin.delete_user_success">Suppression effectuƩe %1$s</string>
+ <string name="admin.delete_user_error">Echec de la suppression %1$s</string>
+ <string name="admin.confirm_password">Confirmer le mot de passe</string>
+ <string name="admin.confirm_password_bad">Mot de passe saisi erronƩ</string>
+
+ <string name="admin.scrobblingEnabled">Diffusion autorisƩe</string>
+ <string name="admin.role.admin">Administrateur</string>
+ <string name="admin.role.settings">Modifier les paramĆØtres</string>
+ <string name="admin.role.download">TƩlƩcharger les fichiers</string>
+ <string name="admin.role.upload">TƩlƩverser sur le serveur</string>
+ <string name="admin.role.coverArt">Modifier les pochettes</string>
+ <string name="admin.role.comment">Ajouter des commentaires</string>
+ <string name="admin.role.podcast">GĆ©rer les podcasts</string>
+ <string name="admin.role.stream">Ecouter de la musique</string>
+ <string name="admin.role.jukebox">TƩlƩcommander la lecture (jukebox)</string>
+ <string name="admin.role.share">GĆ©rer les partages</string>
+ <string name="admin.role.lastfm">Utiliser Last.FM</string>
+
+ <string name="music_service.retry">Erreur rƩseau. Nouvelle tentative %1$d de %2$d.</string>
+
+ <string name="background_task.wait">Patienter...</string>
+ <string name="background_task.loading">Chargement.</string>
+ <string name="background_task.no_network">Cette application nĆ©cessite un accĆØs rĆ©seau. Activer les connexion Wifi ou mobile.</string>
+ <string name="background_task.network_error">Une erreur rƩseau est survenue. Merci de vƩrifier l\'adresse du serveur ou rƩessayer plus tard.</string>
+ <string name="background_task.not_found">Ressource non trouvƩe. VƩrifier l\'adresse du serveur.</string>
+ <string name="background_task.parse_error">Erreur de communication avec le serveur.VĆ©rifier l\'adresse du serveur et que la connexion via un navigateur fonctionne.</string>
+
+ <string name="service.connecting">Interrogation du serveur, veuillez patienter.</string>
+
+ <string name="parser.upgrade_client"> Versions incompatible. Mettre Ć  jour DSub.</string>
+ <string name="parser.upgrade_server">Versions incompatibles. Mettre Ć  jour le serveur Subsonic.</string>
+ <string name="parser.not_authenticated">Mauvais nom d\'utilisateur ou mot de passe.</string>
+ <string name="parser.not_authorized">Non autorisƩ. VƩrifier les droit de l\'utilisateur sur le serveur Subsonic.</string>
+ <string name="parser.artist_count">%d artistes rƩcupƩrƩs.</string>
+ <string name="parser.server_error">Erreur serveur : %s</string>
+ <string name="parser.scan_count">%d entrƩes trouvƩes</string>
+
+ <string name="select_artist.refresh">Recharger</string>
+ <string name="select_artist.folder">SĆ©lectionner un dossier</string>
+ <string name="select_artist.all_folders">Tous les dossier</string>
+
+ <string name="equalizer.label">Equaliseur</string>
+ <string name="equalizer.enabled">ActivƩ</string>
+ <string name="equalizer.preset">Selectioner un prƩrƩglage</string>
+ <string name="equalizer.bass_booster">Bass Booster</string>
+ <string name="equalizer.voice_booster">Voice Booster</string>
+ <string name="equalizer.db_size">%d dB</string>
+ <string name="equalizer.bass_size">%d mille</string>
+
+ <string name="widget.4x1">DSub (4x1)</string>
+ <string name="widget.4x2">DSub (4x2)</string>
+ <string name="widget.4x3">DSub (4x3)</string>
+ <string name="widget.4x4">DSub (4x4)</string>
+ <string name="widget.initial_text">Toucher pour choisir de la musique</string>
+ <string name="widget.sdcard_busy">Carte SD indisponible</string>
+ <string name="widget.sdcard_missing">Pas de carte SD</string>
+
+ <string name="util.bytes_format.gigabyte">0.00 GB</string>
+ <string name="util.bytes_format.megabyte">0.00 MB</string>
+ <string name="util.bytes_format.kilobyte">0 KB</string>
+ <string name="util.bytes_format.byte">0 B</string>
+
+ <string name="changelog_full_title">Change Log</string>
+ <string name="changelog_title">What\'s New</string>
+ <string name="changelog_ok_button">OK</string>
+ <string name="changelog_show_full">Plusā€¦</string>
+
+ <string name="chat.send_a_message">Envoyer un message</string>
+
+ <string name="changelog_version_format" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">Version <xliff:g id="version_name">%s</xliff:g></string>
+
+ <string name="tasker.start_playing">Commencer la lecture</string>
+ <string name="tasker.start_playing_title">Tasker -> DĆ©marrer DSub</string>
+ <string name="tasker.edit_shuffle_mode">DƩmarrer en mode lecture alƩatoire : </string>
+ <string name="tasker.start_playing_shuffled">DƩmarrer la lecture en mode lecture alƩatoire</string>
+
+ <plurals name="select_album_n_songs">
+ <item quantity="zero">Aucun titre</item>
+ <item quantity="one">Un titre</item>
+ <item quantity="other">%d titres</item>
+ </plurals>
+ <plurals name="select_album_n_songs_downloading">
+ <item quantity="one">TƩlƩchargement programmƩ pour un titre.</item>
+ <item quantity="other">TƩlƩchargement programmƩ pour %d titres.</item>
+ </plurals>
+ <plurals name="select_album_n_songs_added">
+ <item quantity="one">Un titre a ƩtƩ ajoutƩ Ơ la liste de lecture en cours.</item>
+ <item quantity="other">%d titres ont ƩtƩ ajoutƩs Ơ la liste de lecture en cours.</item>
+ </plurals>
+ <plurals name="select_album_donate_dialog_n_trial_days_left">
+ <item quantity="one">Dernier jour avant la fin de pƩriode d\'essai.</item>
+ <item quantity="other">%d jours avant la fin de pƩriode d\'essai.</item>
+ </plurals>
+
+</resources>
diff --git a/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml
index 3c7780dc..6222989a 100644
--- a/res/values-hu/strings.xml
+++ b/app/src/main/res/values-hu/strings.xml
@@ -1,602 +1,602 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-
- <string name="common.appname">DSub</string>
- <string name="common.ok">OK</string>
- <string name="common.save">MentƩs</string>
- <string name="common.cancel">MĆ©gse</string>
- <string name="common.play_now">LejƔtszƔs</string>
- <string name="common.play_shuffled">LejƔtszƔs kevert sorrendben</string>
- <string name="common.play_next">SorbaĆ”llĆ­tĆ”s kƶvetkezőnek</string>
- <string name="common.play_last">SorbaĆ”llĆ­tĆ”s utolsĆ³nak</string>
- <string name="common.download">LetƶltĆ©s gyorsĆ­tĆ³tĆ”rba</string>
- <string name="common.pin">LetƶltĆ©s tĆ”rolĆ”sra (megőrzĆ©s)</string>
- <string name="common.delete">TƶrlƩs</string>
- <string name="common.star">CsillagozƔs</string>
- <string name="common.unstar">CsillagozƔs ki</string>
- <string name="common.info">RĆ©szletek</string>
- <string name="common.name">NĆ©v</string>
- <string name="common.comment">MegjegyzƩs</string>
- <string name="common.public">NyilvƔnos</string>
- <string name="common.play_external">VideĆ³ lejĆ”tszĆ”sa</string>
- <string name="common.stream_external">VideĆ³ streamelĆ©se</string>
- <string name="common.confirm">JĆ³vĆ”hagyĆ”s</string>
- <string name="common.confirm_message">Biztos benne? %1$s -> \"%2$s\"</string>
- <string name="common.confirm_message_cache">cache</string>
- <string name="common.empty">Nem talĆ”lhatĆ³!</string>
- <string name="common.warning">Figyelem!</string>
-
- <string name="button_bar.home">Főoldal</string>
- <string name="button_bar.browse">MƩdiatƔr</string>
- <string name="button_bar.search">KeresƩs</string>
- <string name="button_bar.playlists">LejƔtszƔsi listƔk</string>
- <string name="button_bar.now_playing">VĆ”rĆ³lista</string>
- <string name="button_bar.podcasts">Podcastok</string>
- <string name="button_bar.bookmarks">Kƶnyvjelzők</string>
- <string name="button_bar.shares">MegosztƔsok</string>
- <string name="button_bar.chat">CsevegƩs (Chat)</string>
- <string name="button_bar.admin">Admin</string>
- <string name="button_bar.downloading">LetƶltƩsek</string>
-
- <string name="main.welcome_title">Ɯdvƶzlet!</string>
- <string name="main.welcome_text">Ɯdvƶzli a DSub! Az alkalmazĆ”s mĆ©g nincs beĆ”llĆ­tva. MiutĆ”n konfigurĆ”lta sajĆ”t kiszolgĆ”lĆ³jĆ”t
- (elĆ©rhető: <b>subsonic.org</b>), hĆŗzza balrĆ³l jobbra az oldalsĆ”vot, lĆ©pjen be a <b>BeĆ”llĆ­tĆ”sok</b> menĆ¼pontba Ć©s adja meg a kapcsolĆ³dĆ”si adatokat!</string>
- <string name="main.about_title">DSub informĆ”ciĆ³k</string>
- <string name="main.about_text">Fejlesztő: Scott Jackson
- \nEmail: dsub.android@gmail.com
- \nVerziĆ³: %1$s
- \nGyorsĆ­tĆ³tĆ”razott fĆ”jlok: %2$s
- \nFelhasznĆ”lt tĆ”rolĆ³: %3$s/%4$s
- \nFelhasznĆ”lhatĆ³ tĆ”rolĆ³: %5$s/%6$s</string>
- <string name="main.faq_title">GYIK</string>
- <string name="main.faq_text">
- <![CDATA[
- <font color="red">GyorsĆ­tĆ³tĆ”razĆ”s vs TĆ”rolĆ”s</font>:
- <br/>MĆ­g a normĆ”l mĆ³don gyorsĆ­tĆ³tĆ”razott dalok tƶrlődhetnek amikor Ćŗjak kerĆ¼lnek letƶltĆ©sre, addig a \"LetƶltĆ©s tĆ”rolĆ”sra (megőrzĆ©s)\" menĆ¼pont segĆ­tsĆ©gĆ©vel letƶltƶtt dalok soha nem tƶrlődnek automatikusan.
- <p/><font color="red">Ha a ChromeCast sikertelen</font>:
- <br/>PrĆ³bĆ”lja meg bejelƶlni: BeĆ”llĆ­tĆ”sok -> LejĆ”tszĆ”s -> Eszkƶz hasznĆ”lata proxykĆ©nt. Ez egy kerĆ¼lő megoldĆ”s arra, ha a ChromeCast elutasĆ­tja a sajĆ”t alƔƭrĆ”sĆŗ tanĆŗsĆ­tvĆ”nyt.
- <p/><font color="red">A MĆ©diatĆ”r első szintje tulajdonkĆ©ppen az előadĆ³k csoportja</font>:
- <br/>A BeĆ”llĆ­tĆ”sok menĆ¼ben tƶrƶlje az "ElőadĆ³k első szintje" jelƶlĆ©st. Ez teszi lehetővĆ©, hogy a mappĆ”k teljes első szintjĆ©nek megjelenĆ­tĆ©se előadĆ³i csoportonkĆ©nt Ć©s ne előadĆ³nkĆ©nt legyen kezelve.
- ]]>
- </string>
- <string name="main.select_server">KiszolgĆ”lĆ³ kivĆ”lasztĆ”sa</string>
- <string name="main.shuffle">LejƔtszƔs kevert sorrendben</string>
- <string name="main.offline">Offline mĆ³d</string>
- <string name="main.online">Online mĆ³d</string>
- <string name="main.settings">BeƔllƭtƔsok</string>
- <string name="main.albums_title">Albumok</string>
- <string name="main.albums_per_folder">MappƔnkƩnt</string>
- <string name="main.albums_newest">UtoljƔra hozzƔadottak</string>
- <string name="main.albums_recent">UtoljƔra lejƔtszottak</string>
- <string name="main.albums_frequent">Legtƶbbszƶr lejƔtszottak</string>
- <string name="main.albums_highest">Legjobbra ƩrtƩkeltek</string>
- <string name="main.albums_starred">Csillagozottak</string>
- <string name="main.albums_random">VĆ©letlenszerű kivĆ”lasztĆ”s</string>
- <string name="main.albums_genres">Műfajok</string>
- <string name="main.albums_year">Ɖvtizedek</string>
- <string name="main.albums_alphabetical">Betűrendben</string>
- <string name="main.videos">VideĆ³k</string>
- <string name="main.songs_genres">@string/main.albums_genres</string>
- <string name="main.back_confirm">Nyomja meg mƩg egyszer a kilƩpƩshez!</string>
- <string name="main.scan_complete">A mĆ©diatĆ”r frissĆ­tĆ©se befejeződƶtt a kiszolgĆ”lĆ³n!</string>
-
- <string name="menu.search">KeresƩs</string>
- <string name="menu.shuffle">LejƔtszƔs kevert sorrendben</string>
- <string name="menu.refresh">FrissƭtƩs</string>
- <string name="menu.play">LejƔtszƔs</string>
- <string name="menu.play_last">SorbaĆ”llĆ­tĆ”s utolsĆ³nak</string>
- <string name="menu.exit">KilƩpƩs</string>
- <string name="menu.settings">BeƔllƭtƔsok</string>
- <string name="menu.help">SĆŗgĆ³</string>
- <string name="menu.about">NĆ©vjegy</string>
- <string name="menu.add_playlist">HozzƔadƔs lejƔtszƔsi listƔhoz</string>
- <string name="menu.remove_playlist">EltĆ”volĆ­tĆ”s a lejĆ”tszĆ”si listĆ”bĆ³l</string>
- <string name="menu.deleted_playlist">\"%s\" lejƔtszƔsi lista tƶrƶlve</string>
- <string name="menu.deleted_playlist_error">\"%s\" lejƔtszƔsi lista tƶrlƩse sikertelen!</string>
- <string name="menu.log">Log kĆ¼ldĆ©se</string>
- <string name="menu.set_timer">IdőzĆ­tő beĆ”llĆ­tĆ”sa</string>
- <string name="menu.check_podcasts">ƚj epizĆ³dok ellenőrzĆ©se</string>
- <string name="menu.add_podcast">Csatorna hozzƔadƔsa</string>
- <string name="menu.keep_synced">Tartsa szinkronizƔlva</string>
- <string name="menu.stop_sync">SzinkronizƔlƔs megƔllƭtƔsa</string>
- <string name="menu.show_all">Ɩsszes mĆ©dia megjelenĆ­tĆ©se</string>
- <string name="menu.show_artist">UgrĆ”s az előadĆ³hoz</string>
- <string name="menu.share">MegosztƔs</string>
- <string name="menu.delete_cache">GyorsĆ­tĆ³tĆ”r tƶrlĆ©se</string>
- <string name="menu.cast">TovƔbbƭtƔs eszkƶzhƶz</string>
- <string name="menu.faq">FAQ</string>
- <string name="menu.add_user">FelhasznĆ”lĆ³ hozzĆ”adĆ”sa</string>
- <string name="menu.rescan">MĆ©diatĆ”r frissĆ­tĆ©se a kiszolgĆ”lĆ³n</string>
- <string name="menu.rate">ƉrtĆ©kelĆ©s</string>
- <string name="menu.top_tracks">Last.fm legjobb dalok</string>
- <string name="menu.similar_artists">HasonlĆ³ előadĆ³k</string>
- <string name="menu.show_missing">HiĆ”nyzĆ³ megjelenĆ­tĆ©se</string>
- <string name="menu.start_radio">RĆ”diĆ³ indĆ­tĆ”sa</string>
- <string name="menu.first_level_artist">ElőadĆ³k első szintje</string>
-
- <string name="playlist.label">LejƔtszƔsi listƔk</string>
- <string name="playlist.update_info">SzerkesztƩs</string>
- <string name="playlist.updated_info">\"%s\" lejĆ”tszĆ”si lista mĆ³dosĆ­tva</string>
- <string name="playlist.updated_info_error">\"%s\" lejĆ”tszĆ”si lista mĆ³dosĆ­tĆ”sa sikertelen!</string>
- <string name="playlist.overwrite">LĆ©tező lejĆ”tszĆ”si lista felĆ¼lĆ­rĆ”sa</string>
- <string name="playlist.add_to">HozzƔadƔs lejƔtszƔsi listƔhoz</string>
- <string name="playlist.create_new">ƚj lejĆ”tszĆ”si lista</string>
- <string name="playlist.delete">LejƔtszƔsi lista tƶrlƩse</string>
-
- <string name="search.label">KeresƩs</string>
- <string name="search.title">KeresƩs</string>
- <string name="search.search">Ɖrintse meg a keresĆ©shez</string>
- <string name="search.no_match">Nincs talĆ”lat, prĆ³bĆ”lja Ćŗjra!</string>
- <string name="search.artists">ElőadĆ³k</string>
- <string name="search.albums">Albumok</string>
- <string name="search.songs">Dalok</string>
- <string name="search.more">TovƔbbiak</string>
-
- <string name="progress.wait">KƩrem vƔrjon...</string>
-
- <string name="music_library.label">MƩdiatƔr</string>
- <string name="music_library.label_offline">Kapcsolat nĆ©lkĆ¼li mĆ©diĆ”k</string>
-
- <string name="select_album.select">Ɩsszes jelƶlĆ©se be/ki</string>
- <string name="select_album.n_selected">%d dal kijelƶlve.</string>
- <string name="select_album.n_unselected">%d dal visszavonva.</string>
- <string name="select_album.more">TovƔbbiak</string>
- <string name="select_album.offline">Offline</string>
- <string name="select_album.searching">KeresƩs...</string>
- <string name="select_album.no_sdcard">Hiba: SD kƔrtya nem Ɣll rendelkezƩsre!</string>
- <string name="select_album.no_network">Figyelem: HĆ”lĆ³zat nem Ć”ll rendelkezĆ©sre!</string>
- <string name="select_album.no_room">Figyelem: MƔr csak %s hely Ɣll rendelkezƩsre!</string>
- <string name="select_album.not_licensed">A kiszolgĆ”lĆ³nak nincs licence! %d prĆ³banap van hĆ”tra!</string>
- <string name="select_album.donate_dialog_message">KorlƔtlan letƶltƩshez juthat a Subsonic tƔmogatƔsƔval!</string>
- <string name="select_album.donate_dialog_now">Most</string>
- <string name="select_album.donate_dialog_later">KĆ©sőbb</string>
- <string name="select_album.donate_dialog_0_trial_days_left">A prĆ³baidőszak lejĆ”rt!</string>
-
- <string name="offline.sync_dialog_title">Offline dalok vƔrnak a szinkronizƔlƔs befejezƩsƩre</string>
- <string name="offline.sync_dialog_message">%1$d scrobble folyamat offline mĆ³dban?
- \n%2$d csillagozĆ”s folyamat offline mĆ³dban?
- </string>
- <string name="offline.sync_dialog_default">A művelet hasznĆ”lata alapĆ©rtelmezettkĆ©nt</string>
- <string name="offline.sync_success">%1$d dal sikeresen szinkronizƔlva</string>
- <string name="offline.sync_partial">%1$d/%2$d dal sikeresen szinkronizƔlva</string>
- <string name="offline.sync_error">A dalok szinkronizƔlƔsa sikertelen!</string>
-
- <string name="select_genre.blank">Ɯres</string>
- <string name="select_genre.songs">%d dal</string>
- <string name="select_genre.albums">%d album</string>
-
- <string name="select_podcasts.error">A podcast hibĆ”t jelzett a kiszolgĆ”lĆ³ra tƶrtĆ©nő letƶltĆ©s kƶzben! A kiszolgĆ”lĆ³nak kell letƶltenie előszƶr!</string>
- <string name="select_podcasts.skipped">Ez a podcast nem lett letƶltve a kiszolgĆ”lĆ³ra! A kiszolgĆ”lĆ³nak kell letƶltenie előszƶr!</string>
- <string name="select_podcasts.initializing">A podcast csatorna inicializĆ”lĆ”sa a kiszolgĆ”lĆ³n. KĆ©rjĆ¼k, tƶltse be Ćŗjra nĆ©hĆ”ny pillanat mĆŗlva!</string>
- <string name="select_podcasts.server_download">LetƶltĆ©s a kiszolgĆ”lĆ³ra</string>
- <string name="select_podcasts.server_delete">TƶrlĆ©s a kiszolgĆ”lĆ³rĆ³l</string>
- <string name="select_podcasts.downloading">\"%s\" letƶltĆ©se a kiszolgĆ”lĆ³ra</string>
- <string name="select_podcasts.refreshing">A kiszolgĆ”lĆ³ ellenőrzi az Ćŗj podcastokat...</string>
- <string name="select_podcasts.deleted">\"%s\" podcast tƶrƶlve</string>
- <string name="select_podcasts.deleted_error">\"%s\" podcast tƶrlƩse sikertelen!</string>
- <string name="select_podcasts.add_url">URL:</string>
- <string name="select_podcasts.created_error">Podcast hozzƔadƔsa sikertelen!</string>
- <string name="select_podcasts.invalid_podcast_channel">ƉrvĆ©nytelen podcast csatorna: \"%s\"</string>
- <string name="select_podcasts.delete">Podcast tƶrlƩse</string>
-
- <string name="download.empty">A vĆ”rĆ³lista Ć¼res!</string>
- <string name="download.shuffle_loading">Kevert sorrendű lista betƶltĆ©se...</string>
- <string name="download.playerstate_downloading">LetƶltƩs - \"%s\"</string>
- <string name="download.playerstate_buffering">PufferelƩs</string>
- <string name="download.playerstate_playing_shuffle">Sorrend keverƩse</string>
- <string name="download.menu_show_album">UgrƔs az albumhoz</string>
- <string name="download.menu_lyrics">Dalszƶveg</string>
- <string name="download.menu_remove">EltĆ”volĆ­tĆ”s a vĆ”rĆ³listĆ”rĆ³l</string>
- <string name="download.menu_remove_all">Ɩsszes eltĆ”volĆ­tĆ”sa</string>
- <string name="download.menu_screen_on">Kijelző be</string>
- <string name="download.menu_shuffle">Sorrend keverƩse</string>
- <string name="download.menu_toggle">VƔltƔs</string>
- <string name="download.menu_save">MentƩs lejƔtszƔsi listƔba</string>
- <string name="download.menu_shuffle_notification">LejƔtszƔs kevert sorrendben</string>
- <string name="download.menu_remove_played_songs">LejƔtszottak eltƔvolƭtƔsa</string>
- <string name="download.playlist_title">MentƩs lejƔtszƔsi listƔba</string>
- <string name="download.playlist_name">LejƔtszƔsi lista neve:</string>
- <string name="download.playlist_saving">\"%s\" lejƔtszƔsi lista mentƩse...</string>
- <string name="download.playlist_done">LejƔtszƔsi lista mentƩse sikeres</string>
- <string name="download.playlist_error">LejĆ”tszĆ”si lista mentĆ©se sikertelen, prĆ³bĆ”lja kĆ©sőbb!</string>
- <string name="download.repeat_off">IsmƩtlƩs ki</string>
- <string name="download.repeat_all">Ɩsszes ismĆ©tlĆ©se</string>
- <string name="download.repeat_single">Dal ismƩtlƩse</string>
- <string name="download.jukebox_on">TĆ”vvezĆ©rlĆ©s bekapcsolĆ”sa. A zenelejĆ”tszĆ”s a szĆ”mĆ­tĆ³gĆ©pen tƶrtĆ©nik.</string>
- <string name="download.jukebox_off">TƔvvezƩrlƩs kikapcsolƔsa. A zenelejƔtszƔs az eszkƶzƶn tƶrtƩnik.</string>
- <string name="download.jukebox_volume">Hangerő tĆ”vvezĆ©rlĆ©se</string>
- <string name="download.jukebox_server_too_old">A tĆ”vvezĆ©rlĆ©s nem tĆ”mogatott. KĆ©rjĆ¼k, frissĆ­tse a Subsonic kiszolgĆ”lĆ³t!</string>
- <string name="download.jukebox_offline">A tĆ”vvezĆ©rlĆ©s nem lehetsĆ©ges offline mĆ³dban!</string>
- <string name="download.jukebox_not_authorized">A tĆ”vvezĆ©rlĆ©s nem lehetsĆ©ges! EngedĆ©lyezze a Jukebox mĆ³dot a <b>Users &gt; Settings</b> menĆ¼ben a Subsonic kiszolgĆ”lĆ³n!</string>
- <string name="download.timer_length">Időhossz:</string>
- <string name="download.start_timer">IdőzĆ­tő indĆ­tĆ”sa</string>
- <string name="download.stop_timer">IdőzĆ­tő megĆ”llĆ­tĆ”sa</string>
- <string name="download.need_download">A videĆ³t előszƶr le kell tƶlteni!</string>
- <string name="download.no_streaming_player">Nincs megfelelő lejĆ”tszĆ³ a stream megjelenĆ­tĆ©sĆ©hez!</string>
- <string name="download.playing_out_of">LejƔtszƔs: %1$d/%2$d</string>
- <string name="download.save_bookmark_title">Kƶnyvjelző lĆ©trehozĆ”sa</string>
- <string name="download.save_bookmark">Kƶnyvjelző lĆ©trehozva</string>
- <string name="download.save_bookmark_failed">Kƶnyvjelző lĆ©trehozĆ”sa sikertelen!</string>
- <string name="download.downloading_title">%1$d dal letƶltƩse</string>
- <string name="download.downloading_summary">AktuƔlis: %1$s</string>
- <string name="download.downloading_summary_expanded">AktuƔlis: %1$s
- \nBecsĆ¼lt mĆ©ret: %2$s</string>
- <string name="download.failed_to_load">A beolvasƔs sikertelen!</string>
- <string name="download.restore_play_queue">FolytatƔs onnan, ahol egy mƔsik eszkƶzƶn abbahagyta.</string>
-
- <string name="sync.new_podcasts">ƚj podcastok: \"%s\"</string>
- <string name="sync.new_playlists">ƚj lejĆ”tszĆ”si listĆ”k: \"%s\"</string>
- <string name="sync.new_albums">ƚj albumok: \"%s\"</string>
- <string name="sync.new_starred">ƚj csillagozott dalok</string>
-
- <string name="starring_content_starred">\"%s\" csillagozƔs be</string>
- <string name="starring_content_unstarred">\"%s\" csillagozƔs ki</string>
- <string name="starring_content_error">Nem sikerĆ¼lt frissĆ­teni \"%s\", prĆ³bĆ”lja kĆ©sőbb!</string>
-
- <string name="playlist_error">Nem sikerĆ¼lt elĆ©rni a lejĆ”tszĆ”si lista adatait!</string>
- <string name="updated_playlist">%1$s dal hozzƔadva: \"%2$s\"</string>
- <string name="updated_playlist_error">Nem sikerĆ¼lt frissĆ­teni \"%s\", prĆ³bĆ”lja kĆ©sőbb!</string>
- <string name="removed_playlist">%1$s dal eltƔvolƭtva: \"%2$s\"</string>
-
- <string name="bookmark.delete">Kƶnyvjelző tƶrlĆ©se</string>
- <string name="bookmark.delete_title">Kƶnyvjelző tƶrlĆ©se</string>
- <string name="bookmark.deleted">\"%s\" kƶnyvjelző tƶrƶlve</string>
- <string name="bookmark.deleted_error">\"%s\" kƶnyvjelző tƶrlĆ©se sikertelen!</string>
- <string name="bookmark.details_title">Kƶnyvjelző rĆ©szletei</string>
- <string name="bookmark.details">Dal: %1$s
- \nPozĆ­ciĆ³: %2$s
- \nLĆ©trehozva: %3$s
- \nUtolsĆ³ mĆ³dosĆ­tĆ”s: %4$s
- \nMegjegyzƩs: %5$s</string>
- <string name="bookmark.resume_title">Folytatja a lejƔtszƔst?</string>
- <string name="bookmark.resume">\"%1$s\" folytatƔsa innen: \"%2$s\"</string>
- <string name="bookmark.action_resume">FolytatƔs</string>
- <string name="bookmark.action_start_over">KezdƩs</string>
-
- <string name="rating.title">\"%s\" ƩrtƩkelve</string>
- <string name="rating.set_rating">\"%s\" ƩrtƩkelve</string>
- <string name="rating.set_rating_failed">\"%s\" ƩrtƩkelƩse sikertelen!</string>
- <string name="rating.remove_rating">\"%s\" ƩrtƩkelƩse visszavonva</string>
- <string name="rating.remove_rating_failed">\"%s\" ƩrtƩkelƩsƩnek visszavonƔsa sikertelen!</string>
-
- <string name="song_details.error">Hiba</string>
- <string name="song_details.skipped">ƁtlƩpve</string>
- <string name="song_details.downloading">LetƶltƩs</string>
-
- <string name="lyrics.nomatch">Dalszƶveg nem talĆ”lhatĆ³!</string>
-
- <string name="error.label">Hiba</string>
-
- <string name="settings.title">BeƔllƭtƔsok</string>
- <string name="settings.test_connection_title">Kapcsolat tesztelƩse</string>
- <string name="settings.servers_add">KiszolgĆ”lĆ³ hozzĆ”adĆ”sa</string>
- <string name="settings.servers_remove">KiszolgĆ”lĆ³ eltĆ”volĆ­tĆ”sa</string>
- <string name="settings.servers_title">KiszolgĆ”lĆ³k</string>
- <string name="settings.server_unused">Nem hasznƔlt</string>
- <string name="settings.server_name">NĆ©v</string>
- <string name="settings.server_address">KiszolgĆ”lĆ³ cĆ­me</string>
- <string name="settings.server_local_network_ssid" >Helyi hĆ”lĆ³zati SSID</string>
- <string name="settings.server_local_network_ssid_hint">AktuƔlis SSID: %s</string>
- <string name="settings.server_internal_address">Belső hĆ”lĆ³zati cĆ­m</string>
- <string name="settings.server_username">FelhasznĆ”lĆ³nĆ©v</string>
- <string name="settings.server_password">JelszĆ³</string>
- <string name="settings.server_open_browser">MegnyitĆ”s bƶngĆ©szőben</string>
- <string name="settings.server_sync_summary">FĆ¼ggetlenĆ¼l attĆ³l, hogy a szinkronizĆ”lĆ”s engedĆ©lyezett-e ezen a kiszolgĆ”lĆ³n.</string>
- <string name="settings.server_sync">SzinkronizƔlƔs engedƩlyezve</string>
- <string name="settings.cache_title">Zene gyorsĆ­tĆ³tĆ”r (Cache)</string>
- <string name="settings.preload_wifi">Dalok előolvasĆ”sa (Wi-Fi)</string>
- <string name="settings.preload_mobile">Dalok előolvasĆ”sa (MobilhĆ”lĆ³zat)</string>
- <string name="settings.cache_size">GyorsĆ­tĆ³tĆ”r mĆ©rete (MB)</string>
- <string name="settings.cache_location">GyorsĆ­tĆ³tĆ”r helye</string>
- <string name="settings.cache_location_error">HibĆ”s gyorsĆ­tĆ³tĆ”r hely! Az alapĆ©rtelmezett hasznĆ”lata.</string>
- <string name="settings.cache_location_reset">A beĆ”llĆ­tott gyorsĆ­tĆ³tĆ”r-hely mĆ”r nem Ć­rhatĆ³! Ha a kƶzelmĆŗltban frissĆ­tette telefonja Android rendszerĆ©t 4.4.x KitKat verziĆ³ra, abban az SD kĆ”rtya kezelĆ©se megvĆ”ltozott, Ć©s az alkalmazĆ”sok csak egy speciĆ”lis helyre tudnak Ć­rni. A Dsub mĆ”r automatikusan Ć”tĆ”llt a megfelelő helyre. Ahhoz, hogy a rĆ©gi adatokat tƶrƶlni tudja, csatlakoztassa az SD kĆ”rtyĆ”t a szĆ”mĆ­tĆ³gĆ©pĆ©hez, Ć©s tƶrƶlje a rĆ©gi mappĆ”t!</string>
- <string name="settings.cache_clear">GyorsĆ­tĆ³tĆ”r tƶrlĆ©se</string>
- <string name="settings.cache_clear_complete">GyorsĆ­tĆ³tĆ”r tƶrlĆ©se kĆ©sz!</string>
- <string name="settings.testing_connection">Kapcsolat tesztelƩse...</string>
- <string name="settings.testing_ok">Kapcsolat OK!</string>
- <string name="settings.testing_unlicensed">Kapcsolat OK! A kiszolgĆ”lĆ³nak nincs licence!</string>
- <string name="settings.connection_failure">KapcsolĆ³dĆ”si hiba!</string>
- <string name="settings.invalid_url">Adjon meg egy ƩrvƩnyes URL-t!</string>
- <string name="settings.invalid_username">Adjon meg egy Ć©rvĆ©nyes felhasznĆ”lĆ³nevet (szĆ³kƶzt nem tartalmazhat)!</string>
- <string name="settings.appearance_title">MegjelenƩs</string>
- <string name="settings.theme_title">TƩmƔk</string>
- <string name="settings.theme_light">VilƔgos</string>
- <string name="settings.theme_dark">SƶtƩt</string>
- <string name="settings.theme_black">Fekete</string>
- <string name="settings.theme_holo">Holo</string>
- <string name="settings.theme_fullscreen">Teljes kĆ©pernyős</string>
- <string name="settings.theme_fullscreen_summary">Teljes kĆ©pernyős Ć¼zemmĆ³d (Ć©rtesĆ­tĆ©si sĆ”v elrejtĆ©se).</string>
- <string name="settings.track_title">DalsorszƔm megjelenƭtƩse</string>
- <string name="settings.track_summary">DalsorszĆ”m megjelenĆ­tĆ©se a dal cĆ­me előtt, ha lĆ©tezik.</string>
- <string name="settings.custom_sort">EgyƩni rendezƩs</string>
- <string name="settings.custom_sort_summary">A kiszolgĆ”lĆ³ alapĆ©rtelmezett rendezĆ©sĆ©nek felĆ¼lbĆ­rĆ”lĆ”sa, rendezĆ©s a lemez sorszĆ”ma Ć©s a kiadĆ”s Ć©ve alapjĆ”n.</string>
- <string name="settings.network_title">HĆ”lĆ³zat</string>
- <string name="settings.max_bitrate_wifi">Max. audiĆ³ bitrĆ”ta - Wi-Fi</string>
- <string name="settings.max_bitrate_mobile">Max. audiĆ³ bitrĆ”ta - MobilhĆ”lĆ³zat</string>
- <string name="settings.max_bitrate_32">32 Kbps</string>
- <string name="settings.max_bitrate_64">64 Kbps</string>
- <string name="settings.max_bitrate_80">80 Kbps</string>
- <string name="settings.max_bitrate_96">96 Kbps</string>
- <string name="settings.max_bitrate_112">112 Kbps</string>
- <string name="settings.max_bitrate_128">128 Kbps</string>
- <string name="settings.max_bitrate_160">160 Kbps</string>
- <string name="settings.max_bitrate_192">192 Kbps</string>
- <string name="settings.max_bitrate_256">256 Kbps</string>
- <string name="settings.max_bitrate_320">320 Kbps</string>
- <string name="settings.max_video_bitrate_wifi">Max. videĆ³ bitrĆ”ta - Wi-Fi</string>
- <string name="settings.max_video_bitrate_mobile">Max. videĆ³ bitrĆ”ta - MobilhĆ”lĆ³zat</string>
- <string name="settings.max_video_bitrate_200">200 Kbps</string>
- <string name="settings.max_video_bitrate_300">300 Kbps</string>
- <string name="settings.max_video_bitrate_400">400 Kbps</string>
- <string name="settings.max_video_bitrate_500">500 Kbps</string>
- <string name="settings.max_video_bitrate_700">700 Kbps</string>
- <string name="settings.max_video_bitrate_1000">1000 Kbps</string>
- <string name="settings.max_video_bitrate_1500">1500 Kbps</string>
- <string name="settings.max_video_bitrate_2000">2000 Kbps</string>
- <string name="settings.max_video_bitrate_3000">3000 Kbps</string>
- <string name="settings.max_video_bitrate_5000">5000 Kbps</string>
- <string name="settings.max_bitrate_unlimited">KorlƔtlan</string>
- <string name="settings.wifi_required_title">StreamelƩs csak Wi-Fivel</string>
- <string name="settings.wifi_required_summary">StreamelĆ©s csak Wi-Fi hĆ”lĆ³zaton keresztĆ¼l.</string>
- <string name="settings.network_timeout_title">HĆ”lĆ³zati időtĆŗllĆ©pĆ©s</string>
- <string name="settings.network_timeout_10000">10 mƔsodperc</string>
- <string name="settings.network_timeout_15000">15 mƔsodperc</string>
- <string name="settings.network_timeout_30000">30 mƔsodperc</string>
- <string name="settings.network_timeout_45000">45 mƔsodperc</string>
- <string name="settings.network_timeout_60000">60 mƔsodperc</string>
- <string name="settings.preload_0">0 dal</string>
- <string name="settings.preload_1">1 dal</string>
- <string name="settings.preload_2">2 dal</string>
- <string name="settings.preload_3">3 dal</string>
- <string name="settings.preload_5">5 dal</string>
- <string name="settings.preload_10">10 dal</string>
- <string name="settings.preload_unlimited">KorlƔtlan</string>
- <string name="settings.clear_search_history">KeresĆ©si előzmĆ©nyek tƶrlĆ©se</string>
- <string name="settings.search_history_cleared">KeresĆ©si előzmĆ©nyek tƶrƶlve</string>
- <string name="settings.other_title">EgyƩb beƔllƭtƔsok</string>
- <string name="settings.scrobble_title">TovƔbbƭtƔs Last.fm-re (Scrobbling)</string>
- <string name="settings.scrobble_summary">A Last.fm felhasznĆ”lĆ³nevet Ć©s jelszĆ³t be kell Ć”llĆ­tani a Subsonic kiszolgĆ”lĆ³n!</string>
- <string name="settings.hide_media_title">ElrejtƩs</string>
- <string name="settings.hide_media_summary">ZenefĆ”jlok elrejtĆ©se egyĆ©b alkalmazĆ”sok elől.</string>
- <string name="settings.hide_media_toast">A kƶvetkező alkalomtĆ³l lĆ©p Ć©letbe, amikor az Android zenefĆ”jlokat keres az eszkƶzƶn.</string>
- <string name="settings.media_button_title">MĆ©dia vezĆ©rlőgombok</string>
- <string name="settings.media_button_summary">A lejĆ”tszĆ³ irĆ”nyĆ­tĆ”sa a bluetooth eszkƶz vagy a fĆ¼lhallgatĆ³ vezĆ©rlőgombjaival.</string>
- <string name="settings.screen_lit_title">KĆ©pernyő Ć©brentartĆ”sa</string>
- <string name="settings.screen_lit_summary">KĆ©pernyő Ć©brentartĆ”sa a letƶltĆ©s alatt a magasabb letƶltĆ©si sebessĆ©g Ć©rdekĆ©ben.</string>
- <string name="settings.playlist_title">LejƔtszƔsi listƔk</string>
- <string name="settings.playlist_random_size_title">VĆ©letlenszerű lejĆ”tszĆ”si lista mĆ©rete</string>
- <string name="settings.sleep_timer_title">AlvĆ³ Ć¼zemmĆ³d időzĆ­tő</string>
- <string name="settings.sleep_timer_duration_title">AlvĆ³ Ć¼zemmĆ³d időtartam</string>
- <string name="settings.sleep_timer_off">Ki</string>
- <string name="settings.sleep_timer_on">Be</string>
- <string name="settings.sleep_timer_always_on">Mindig be</string>
- <string name="settings.temp_loss_title">KĆ¼lső esemĆ©ny bekƶvetkeztekor</string>
- <string name="settings.temp_loss_pause">MegƔllƭtƔs minden esetben</string>
- <string name="settings.temp_loss_pause_lower">MegĆ”llĆ­tĆ”s, kĆ©rĆ©sre alacsonyabb hangerő</string>
- <string name="settings.temp_loss_lower">Alacsonyabb hangerő</string>
- <string name="settings.temp_loss_nothing">Ne csinƔljon semmit</string>
- <string name="settings.disconnect_pause_title">MegƔllƭtƔs kapcsolatbontƔs esetƩn</string>
- <string name="settings.disconnect_pause_both">MegƔllƭtƔs minden esetben</string>
- <string name="settings.disconnect_pause_neither">Ne csinƔljon semmit</string>
- <string name="settings.persistent_title">ƁllandĆ³ kijelzĆ©s</string>
- <string name="settings.persistent_summary">KijelzƩs az ƩrtesƭtƩsi sƔvon a lejƔtszƔs megƔllƭtƔsa utƔn is. Nyomja meg a bezƔrƔs gombot a tƶrlƩsƩhez!</string>
- <string name="settings.gapless_playback">EgybefĆ¼ggő lejĆ”tszĆ”s (Gapless)</string>
- <string name="settings.gapless_playback_summary">Ha lefagyĆ”sokat/furcsasĆ”gokat tapasztal az egybefĆ¼ggő lejĆ”tszĆ”s (Gapless) engedĆ©lyezĆ©se utĆ”n, a problĆ©ma megoldĆ”sĆ”hoz kapcsolja ki a funkciĆ³t!</string>
- <string name="settings.chat_refresh">CsevegƩs frissƭtƩsi gyakorisƔga (mp.)</string>
- <string name="settings.chat_enabled">CsevegƩs (Chat) engedƩlyezƩse</string>
- <string name="settings.chat_enabled_summary">CsevegĆ©s (Chat) menĆ¼pont megjelenĆ­tĆ©se az elhĆŗzhatĆ³ oldalsĆ”von.</string>
- <string name="settings.video_title">VideĆ³</string>
- <string name="settings.video_player">VideĆ³-lejĆ”tszĆ³</string>
- <string name="settings.video_raw">Nyers (Subsonic 4.8-tĆ³l)</string>
- <string name="settings.video_hls">HTTP Live Stream (HLS) (Subsonic 4.8-tĆ³l)</string>
- <string name="settings.video_transcode">Kƶzvetlen transzkĆ³dolĆ”s (video -> mp4, vagy hasonlĆ³ beĆ”llĆ­tĆ”s szĆ¼ksĆ©ges a kiszolgĆ”lĆ³n!)</string>
- <string name="settings.video_flash">Flash (Plugin szĆ¼ksĆ©ges!)</string>
- <string name="settings.cache_screen_title">GyorsĆ­tĆ³tĆ”r/HĆ”lĆ³zat</string>
- <string name="settings.playback_title">LejƔtszƔs</string>
- <string name="settings.hide_widget_title">Widget elrejtƩse</string>
- <string name="settings.hide_widget_summary">Widget elrejtƩse kilƩpƩs utƔn.</string>
- <string name="settings.podcasts_enabled">Podcastok engedƩlyezƩse</string>
- <string name="settings.podcasts_enabled_summary">Podcastok menĆ¼pont megjelenĆ­tĆ©se az elhĆŗzhatĆ³ oldalsĆ”von.</string>
- <string name="settings.bookmarks_enabled">Kƶnyvjelzők engedĆ©lyezĆ©se</string>
- <string name="settings.bookmarks_enabled_summary">Kƶnyvjelzők menĆ¼pont megjelenĆ­tĆ©se az elhĆŗzhatĆ³ oldalsĆ”von.</string>
- <string name="settings.shares_enabled">MegosztƔsok engedƩlyezƩse</string>
- <string name="settings.shares_enabled_summary">MegosztĆ”sok menĆ¼pont megjelenĆ­tĆ©se az elhĆŗzhatĆ³ oldalsĆ”von.</string>
- <string name="settings.sync_title">SzinkronizƔlƔs</string>
- <string name="settings.sync_enabled">SzinkronizƔlƔs engedƩlyezƩse</string>
- <string name="settings.sync_enabled_summary">LejĆ”tszĆ”si listĆ”k Ć©s podcastok vĆ”ltozĆ”sainak rendszeres ellenőrzĆ©se.</string>
- <string name="settings.sync_interval">SzinkronizƔlƔs gyakorisƔga</string>
- <string name="settings.sync_interval_15">15 perc</string>
- <string name="settings.sync_interval_30">30 perc</string>
- <string name="settings.sync_interval_60">1 Ć³ra</string>
- <string name="settings.sync_interval_120">2 Ć³ra</string>
- <string name="settings.sync_interval_240">4 Ć³ra</string>
- <string name="settings.sync_interval_360">6 Ć³ra</string>
- <string name="settings.sync_interval_720">12 Ć³ra</string>
- <string name="settings.sync_interval_1440">Naponta</string>
- <string name="settings.sync_wifi">SzinkronizƔlƔs csak Wi-Fivel</string>
- <string name="settings.sync_wifi_summary">SzinkronizĆ”lĆ”s csak Wi-Fi hĆ”lĆ³zaton keresztĆ¼l.</string>
- <string name="settings.sync_most_recent">UtoljƔra hozzƔadottak szinkronizƔlƔsa</string>
- <string name="settings.sync_most_recent_summary">Az utoljĆ”ra hozzĆ”adott albumok automatikus gyorsĆ­tĆ³tĆ”razĆ”sa.</string>
- <string name="settings.sync_starred">Csillagozottak szinkronizƔlƔsa</string>
- <string name="settings.sync_starred_summary">A csillagozott dalok/albumok/előadĆ³k automatikus gyorsĆ­tĆ³tĆ”razĆ”sa.</string>
- <string name="settings.sync_notification">SzinkronizƔlƔsi ƩrtesƭtƩsek</string>
- <string name="settings.sync_notification_summary">ƉrtesĆ­tĆ©s megjelenĆ­tĆ©se, ha Ćŗj mĆ©dia kerĆ¼lt szinkronizĆ”lĆ”sra.</string>
- <string name="settings.menu_options.title">OpcionĆ”lis menĆ¼beĆ”llĆ­tĆ”sok</string>
- <string name="settings.menu_options.play_next_summary">SorbaĆ”llĆ­tĆ”s kƶvetkezőnek opciĆ³ megjelenĆ­tĆ©se a menĆ¼ben.</string>
- <string name="settings.menu_options.play_last_summary">SorbaĆ”llĆ­tĆ”s utolsĆ³nak opciĆ³ megjelenĆ­tĆ©se a menĆ¼ben.</string>
- <string name="settings.menu_options.star_summary">CsillagozĆ”s opciĆ³ megjelenĆ­tĆ©se a menĆ¼ben.</string>
- <string name="settings.menu_options.shared_summary">MegosztĆ”s opciĆ³ megjelenĆ­tĆ©se a menĆ¼ben.</string>
- <string name="settings.menu_options.rate_summary">ƉrtĆ©kelĆ©s opciĆ³ megjelenĆ­tĆ©se a menĆ¼ben.</string>
- <string name="settings.browse_by_tags">BƶngƩszƩs ID3 Tag hasznƔlatƔval</string>
- <string name="settings.browse_by_tags_summary">ID3 Tag mĆ³dszer hasznĆ”lata a fĆ”jlredszer alapĆŗ mĆ³d helyett. Subsonic 4.7+ verziĆ³ felett!</string>
- <string name="settings.disable_exit_prompt">KilĆ©pĆ©s megerősĆ­tĆ©sĆ©nek tiltĆ”sa</string>
- <string name="settings.disable_exit_prompt_summary">A főoldalon a vissza gomb megnyomĆ”sakor azonnali kilĆ©pĆ©s az alkalmazĆ”sbĆ³l.</string>
- <string name="settings.override_system_language">A rendszer nyelvĆ©nek felĆ¼lbĆ­rĆ”lĆ”sa</string>
- <string name="settings.override_system_language_summary">A Dsub megjelenĆ­tĆ©se angol nyelven abban az esetben is, ha rendelkezik fordĆ­tĆ”ssal. Az alkalmazĆ”st tƶrƶlni kell a memĆ³riĆ”bĆ³l, mert a beĆ”llĆ­tĆ”s csak ĆŗjraindĆ­tĆ”s utĆ”n lĆ©p Ć©rvĆ©nybe!</string>
- <string name="settings.drawer_items_title">OldalsƔv elemei</string>
- <string name="settings.play_now_after">LejƔtszƔs utƔna</string>
- <string name="settings.play_now_after_summary">Egy helyi menĆ¼, amivel lehetővĆ© vĆ”lik minden dal lejĆ”tszĆ”sa a kijelƶlt elem utĆ”n (mint a Subsonic webes felĆ¼letĆ©n)</string>
- <string name="settings.large_album_art">Nagy mĆ©retű albumborĆ­tĆ³k</string>
- <string name="settings.large_album_art_summary">Albumok megjelenĆ­tĆ©se rĆ”csnĆ©zetben Ć©s nagy mĆ©retű albumborĆ­tĆ³val a listanĆ©zet helyett.</string>
- <string name="settings.admin_enabled">Admin engedƩlyezƩse</string>
- <string name="settings.admin_enabled_summary">Admin menĆ¼pont megjelenĆ­tĆ©se az elhĆŗzhatĆ³ oldalsĆ”von.</string>
- <string name="settings.replay_gain">Hangerő-kiegyenlĆ­tĆ©s (Replay Gain)</string>
- <string name="settings.replay_gain_summary">Hangerő kiegyenlĆ­tĆ©se (normalizĆ”lĆ”sa) a dal, vagy az album hangerőszint Ć©rtĆ©kei (tags) alapjĆ”n.</string>
- <string name="settings.replay_gain_type">Hangerőszint meghatĆ”rozĆ”sa</string>
- <string name="settings.replay_gain_type.smart">Intelligens mĆ³don</string>
- <string name="settings.replay_gain_type.album">Album Ć©rtĆ©keiből</string>
- <string name="settings.replay_gain_type.track">Dal Ć©rtĆ©keiből</string>
- <string name="settings.replay_gain_bump">Hangerő-kiegyenlĆ­tĆ©s előerősĆ­tĆ©se</string>
- <string name="settings.replay_gain_untagged">Dalok hangerő-kiegyenlĆ­tĆ©s nĆ©lkĆ¼l</string>
- <string name="settings.casting">Casting (Tartalmak Ć”tkĆ¼ldĆ©se)</string>
- <string name="settings.casting_proxy">Eszkƶz hasznƔlata proxykƩnt</string>
- <string name="settings.casting_proxy_summary">StreamelĆ©s az eszkƶzƶn (mint egy proxyn) keresztĆ¼l. Ez megoldĆ”st hozhat nĆ©hĆ”ny esetben, pl. sajĆ”t alƔƭrĆ”sĆŗ tanĆŗsĆ­tvĆ”ny hasznĆ”latakor.</string>
- <string name="settings.rename_duplicates">DuplikƔlt dalok ƔtnevezƩse</string>
- <string name="settings.rename_duplicates_summary">DuplikĆ”lt dalok Ć”tnevezĆ©se az eredeti fĆ”jlnĆ©vre, Ć­gy megkĆ¼lƶnbƶztethetővĆ© vĆ”lnak.</string>
-
- <string name="shuffle.title">Sorrend keverƩse</string>
- <string name="shuffle.startYear">Kezdő Ć©v:</string>
- <string name="shuffle.endYear">Befejező Ć©v:</string>
- <string name="shuffle.genre">Műfaj:</string>
- <string name="shuffle.pick_genre">Műfaj kivĆ”lasztĆ”sa</string>
-
- <string name="share.info">Tulajdonos: %1$s
- \nLeƭrƔs: %2$s
- \nURL: %3$s
- \nLĆ©trehozva: %4$s
- \nUtolsĆ³ lĆ”togatĆ”s: %5$s
- \nLejĆ”rati idő: %6$s
- \nLƔtogatƔsok szƔma: %7$s
-
- </string>
- <string name="share.expires">LejĆ”rati idő: %s</string>
- <string name="share.expires_never">Nincs lejĆ”rati idő</string>
- <string name="share.deleted">\"%s\" megosztƔs tƶrƶlve</string>
- <string name="share.deleted_error">\"%s\" megosztƔs tƶrlƩse sikertelen!</string>
- <string name="share.no_expiration">Nincs lejĆ”rati idő</string>
- <string name="share.expiration">LejĆ”rati idő:</string>
- <string name="share.updated_info">\"%s\" megosztĆ”s informĆ”ciĆ³i frissĆ­tve</string>
- <string name="share.updated_info_error">\"%s\" megosztĆ”s informĆ”ciĆ³inak frissĆ­tĆ©se sikertelen!</string>
- <string name="share.via">MegosztƔs ezzel</string>
- <string name="share.delete">MegosztƔs tƶrlƩse</string>
-
- <string name="admin.add_user_username">FelhasznĆ”lĆ³nĆ©v:</string>
- <string name="admin.add_user_email">Email:</string>
- <string name="admin.add_user_password">JelszĆ³:</string>
- <string name="admin.create_user_success">A felhasznĆ”lĆ³ lĆ©trehozva</string>
- <string name="admin.create_user_error">A felhasznĆ”lĆ³ lĆ©trehozĆ”sa sikertelen!</string>
- <string name="admin.change_username_invalid">Adjon meg egy Ć©rvĆ©nyes felhasznĆ”lĆ³nevet!</string>
- <string name="admin.update_permissions">JogosultsĆ”gok mĆ³dosĆ­tĆ”sa</string>
- <string name="admin.update_permissions_success">\"%1$s\" jogosultsĆ”gainak mĆ³dosĆ­tĆ”sa sikerĆ¼lt</string>
- <string name="admin.update_permissions_error">\"%1$s\" jogosultsĆ”gainak mĆ³dosĆ­tĆ”sa sikertelen!</string>
- <string name="admin.change_email">Email csere</string>
- <string name="admin.change_email_success">\"%1$s\" email cĆ­mĆ©nek mĆ³dosĆ­tĆ”sa sikerĆ¼lt</string>
- <string name="admin.change_email_error">\"%1$s\" email cĆ­mĆ©nek mĆ³dosĆ­tĆ”sa sikertelen!</string>
- <string name="admin.change_email_label">ƚj email:</string>
- <string name="admin.change_email_invalid">Adjon meg egy ƩrvƩnyes email cƭmet!</string>
- <string name="admin.change_password">JelszĆ³ csere</string>
- <string name="admin.change_password_success">\"%1$s\" jelszavĆ”nak mĆ³dosĆ­tĆ”sa sikerĆ¼lt</string>
- <string name="admin.change_password_error">\"%1$s\" jelszavĆ”nak mĆ³dosĆ­tĆ”sa sikertelen!</string>
- <string name="admin.change_password_label">ƚj jelszĆ³:</string>
- <string name="admin.change_password_invalid">Adjon meg egy Ć©rvĆ©nyes jelszĆ³t!</string>
- <string name="admin.delete_user">FelhasznĆ”lĆ³ tƶrlĆ©se</string>
- <string name="admin.delete_user_success">\"%1$s\" felhasznĆ”lĆ³ lĆ©trehozva</string>
- <string name="admin.delete_user_error">\"%1$s\" felhasznĆ”lĆ³ tƶrlĆ©se sikertelen!</string>
- <string name="admin.confirm_password">JelszĆ³ megerősĆ­tĆ©se</string>
- <string name="admin.confirm_password_bad">A beĆ­rt jelszĆ³ nem egyezik!</string>
-
- <string name="admin.scrobblingEnabled">Scrobbling hasznƔlata</string>
- <string name="admin.role.admin">AdminisztrƔtor</string>
- <string name="admin.role.settings">BeĆ”llĆ­tĆ”sok mĆ³dosĆ­tĆ”sa</string>
- <string name="admin.role.download">Eredeti fƔjlok letƶltƩse</string>
- <string name="admin.role.upload">FeltƶltĆ©s a kiszolgĆ”lĆ³ra</string>
- <string name="admin.role.coverArt">AlbumborĆ­tĆ³ cserĆ©je</string>
- <string name="admin.role.comment">MegjegyzƩsek hozzƔadƔsa</string>
- <string name="admin.role.podcast">Podcastok kezelƩse</string>
- <string name="admin.role.stream">Zene streamelƩse</string>
- <string name="admin.role.jukebox">Jukebox vezƩrlƩse</string>
- <string name="admin.role.share">MegosztƔsok kezelƩse</string>
- <string name="admin.role.lastfm">Last.fm funkciĆ³ hasznĆ”lata</string>
-
- <string name="music_service.retry">HĆ”lĆ³zati hiba tƶrtĆ©nt! ƚjraprĆ³bĆ”lkozĆ”s %1$d/%2$d.</string>
-
- <string name="background_task.wait">KƩrem vƔrjon...</string>
- <string name="background_task.loading">BetƶltƩs...</string>
- <string name="background_task.no_network">Az alkalmazĆ”s hĆ”lĆ³zati hozzĆ”fĆ©rĆ©st igĆ©nyel. KĆ©rjĆ¼k, kapcsolja be a Wi-Fi-t vagy a mobilhĆ”lĆ³zatot!</string>
- <string name="background_task.network_error">HĆ”lĆ³zati hiba tƶrtĆ©nt! KĆ©rjĆ¼k, ellenőrizze a kiszolgĆ”lĆ³ cĆ­mĆ©t, vagy prĆ³bĆ”lja kĆ©sőbb!</string>
- <string name="background_task.not_found">Az erőforrĆ”s nem talĆ”lhatĆ³! KĆ©rjĆ¼k, ellenőrizze a kiszolgĆ”lĆ³ cĆ­mĆ©t!</string>
- <string name="background_task.parse_error">Hiba tƶrtĆ©nt a kiszolgĆ”lĆ³val tƶrtĆ©nő kommunikĆ”ciĆ³ban. KĆ©rjĆ¼k, ellenőrizze a kiszolgĆ”lĆ³ cĆ­mĆ©t, Ć©s prĆ³bĆ”ljon meg web bƶngĆ©szővel kapcsolĆ³dni a kiszolgĆ”lĆ³hoz!</string>
-
- <string name="service.connecting">KapcsolĆ³dĆ”s a kiszolgĆ”lĆ³hoz, kĆ©rem vĆ”rjon...</string>
-
- <string name="parser.upgrade_client">Nem kompatibilis verziĆ³. KĆ©rjĆ¼k, frissĆ­tse a DSub Android alkalmazĆ”st!</string>
- <string name="parser.upgrade_server">Nem kompatibilis verziĆ³. KĆ©rjĆ¼k, frissĆ­tse a Subsonic kiszolgĆ”lĆ³t!</string>
- <string name="parser.not_authenticated">HibĆ”s felhasznĆ”lĆ³nĆ©v vagy jelszĆ³!</string>
- <string name="parser.not_authorized">Nincs engedĆ©lyezve! Ellenőrizze a felhasznĆ”lĆ³ jogosultsĆ”gait a Subsonic kiszolgĆ”lĆ³n!</string>
- <string name="parser.artist_count">%d előadĆ³ talĆ”lhatĆ³ a mĆ©diatĆ”rban.</string>
- <string name="parser.server_error">KiszolgĆ”lĆ³ hiba: %s</string>
- <string name="parser.scan_count">%d tƩtel ƔtvizsgƔlva.</string>
-
- <string name="select_artist.refresh">FrissƭtƩs</string>
- <string name="select_artist.folder">Mappa kivƔlasztƔsa</string>
- <string name="select_artist.all_folders">Ɩsszes mappa</string>
-
- <string name="equalizer.label">Equalizer</string>
- <string name="equalizer.enabled">EngedƩlyezve</string>
- <string name="equalizer.preset">Profil kivƔlasztƔsa</string>
- <string name="equalizer.bass_booster">Basszus fokozƔs</string>
- <string name="equalizer.voice_booster">BeszƩdhang fokozƔs</string>
- <string name="equalizer.db_size">%d dB</string>
- <string name="equalizer.bass_size">%d ezer</string>
-
- <string name="widget.4x1">DSub (4x1)</string>
- <string name="widget.4x2">DSub (4x2)</string>
- <string name="widget.4x3">DSub (4x3)</string>
- <string name="widget.4x4">DSub (4x4)</string>
- <string name="widget.initial_text">Ɖrintse meg a zene kivĆ”lasztĆ”sĆ”hoz!</string>
- <string name="widget.sdcard_busy">Az SD kĆ”rtya nem elĆ©rhető!</string>
- <string name="widget.sdcard_missing">Nincs SD kƔrtya!</string>
-
- <string name="util.bytes_format.gigabyte">0.00 GB</string>
- <string name="util.bytes_format.megabyte">0.00 MB</string>
- <string name="util.bytes_format.kilobyte">0 KB</string>
- <string name="util.bytes_format.byte">0 B</string>
-
- <string name="changelog_full_title">ƚjdonsĆ”gok</string>
- <string name="changelog_title">ƚjdonsĆ”gok</string>
- <string name="changelog_ok_button">OK</string>
- <string name="changelog_show_full">TovĆ”bbiakā€¦</string>
-
- <string name="chat.send_a_message">Ɯzenet kĆ¼ldĆ©se</string>
-
- <string name="changelog_version_format" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">Version <xliff:g id="version_name">%s</xliff:g></string>
-
- <string name="tasker.start_playing">LejƔtszƔs indƭtƔsa</string>
- <string name="tasker.start_playing_shuffled">LejƔtszƔs indƭtƔsa kevert sorrendben</string>
- <string name="tasker.start_playing_title">Tasker -> DSub indƭtƔsa</string>
- <string name="tasker.edit_shuffle_mode">IndƭtƔs kevert sorrendben: </string>
- <string name="tasker.edit_shuffle_start_year">Kevert sorrend kezdő Ć©v:</string>
- <string name="tasker.edit_shuffle_end_year">Kevert sorrend utolsĆ³ Ć©v:</string>
- <string name="tasker.edit_shuffle_genre">Kevert sorrend műfaja:</string>
- <string name="tasker.edit_server_offline">Offline kapcsolĆ³: </string>
- <string name="tasker.edit_do_nothing">Ne csinƔljon semmit</string>
-
- <plurals name="select_album_n_songs">
- <item quantity="zero">Nincsenek dalok</item>
- <item quantity="one">1 dal</item>
- <item quantity="other">%d dal</item>
- </plurals>
- <plurals name="select_album_n_songs_downloading">
- <item quantity="one">1 dal kijelƶlve letƶltƩsre.</item>
- <item quantity="other">%d dal kijelƶlve letƶltƩsre.</item>
- </plurals>
- <plurals name="select_album_n_songs_added">
- <item quantity="one">1 dal hozzĆ”adva a vĆ”rĆ³listĆ”hoz.</item>
- <item quantity="other">%d dal hozzĆ”adva a vĆ”rĆ³listĆ”hoz.</item>
- </plurals>
- <plurals name="select_album_donate_dialog_n_trial_days_left">
- <item quantity="one">1 nap van hĆ”tra a prĆ³baidőszakbĆ³l.</item>
- <item quantity="other">%d nap van hĆ”tra a prĆ³baidőszakbĆ³l.</item>
- </plurals>
-
-</resources>
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <string name="common.appname">DSub</string>
+ <string name="common.ok">OK</string>
+ <string name="common.save">MentƩs</string>
+ <string name="common.cancel">MĆ©gse</string>
+ <string name="common.play_now">LejƔtszƔs</string>
+ <string name="common.play_shuffled">LejƔtszƔs kevert sorrendben</string>
+ <string name="common.play_next">SorbaĆ”llĆ­tĆ”s kƶvetkezőnek</string>
+ <string name="common.play_last">SorbaĆ”llĆ­tĆ”s utolsĆ³nak</string>
+ <string name="common.download">LetƶltĆ©s gyorsĆ­tĆ³tĆ”rba</string>
+ <string name="common.pin">LetƶltĆ©s tĆ”rolĆ”sra (megőrzĆ©s)</string>
+ <string name="common.delete">TƶrlƩs</string>
+ <string name="common.star">CsillagozƔs</string>
+ <string name="common.unstar">CsillagozƔs ki</string>
+ <string name="common.info">RĆ©szletek</string>
+ <string name="common.name">NĆ©v</string>
+ <string name="common.comment">MegjegyzƩs</string>
+ <string name="common.public">NyilvƔnos</string>
+ <string name="common.play_external">VideĆ³ lejĆ”tszĆ”sa</string>
+ <string name="common.stream_external">VideĆ³ streamelĆ©se</string>
+ <string name="common.confirm">JĆ³vĆ”hagyĆ”s</string>
+ <string name="common.confirm_message">Biztos benne? %1$s -> \"%2$s\"</string>
+ <string name="common.confirm_message_cache">cache</string>
+ <string name="common.empty">Nem talĆ”lhatĆ³!</string>
+ <string name="common.warning">Figyelem!</string>
+
+ <string name="button_bar.home">Főoldal</string>
+ <string name="button_bar.browse">MƩdiatƔr</string>
+ <string name="button_bar.search">KeresƩs</string>
+ <string name="button_bar.playlists">LejƔtszƔsi listƔk</string>
+ <string name="button_bar.now_playing">VĆ”rĆ³lista</string>
+ <string name="button_bar.podcasts">Podcastok</string>
+ <string name="button_bar.bookmarks">Kƶnyvjelzők</string>
+ <string name="button_bar.shares">MegosztƔsok</string>
+ <string name="button_bar.chat">CsevegƩs (Chat)</string>
+ <string name="button_bar.admin">Admin</string>
+ <string name="button_bar.downloading">LetƶltƩsek</string>
+
+ <string name="main.welcome_title">Ɯdvƶzlet!</string>
+ <string name="main.welcome_text">Ɯdvƶzli a DSub! Az alkalmazĆ”s mĆ©g nincs beĆ”llĆ­tva. MiutĆ”n konfigurĆ”lta sajĆ”t kiszolgĆ”lĆ³jĆ”t
+ (elĆ©rhető: <b>subsonic.org</b>), hĆŗzza balrĆ³l jobbra az oldalsĆ”vot, lĆ©pjen be a <b>BeĆ”llĆ­tĆ”sok</b> menĆ¼pontba Ć©s adja meg a kapcsolĆ³dĆ”si adatokat!</string>
+ <string name="main.about_title">DSub informĆ”ciĆ³k</string>
+ <string name="main.about_text">Fejlesztő: Scott Jackson
+ \nEmail: dsub.android@gmail.com
+ \nVerziĆ³: %1$s
+ \nGyorsĆ­tĆ³tĆ”razott fĆ”jlok: %2$s
+ \nFelhasznĆ”lt tĆ”rolĆ³: %3$s/%4$s
+ \nFelhasznĆ”lhatĆ³ tĆ”rolĆ³: %5$s/%6$s</string>
+ <string name="main.faq_title">GYIK</string>
+ <string name="main.faq_text">
+ <![CDATA[
+ <font color="red">GyorsĆ­tĆ³tĆ”razĆ”s vs TĆ”rolĆ”s</font>:
+ <br/>MĆ­g a normĆ”l mĆ³don gyorsĆ­tĆ³tĆ”razott dalok tƶrlődhetnek amikor Ćŗjak kerĆ¼lnek letƶltĆ©sre, addig a \"LetƶltĆ©s tĆ”rolĆ”sra (megőrzĆ©s)\" menĆ¼pont segĆ­tsĆ©gĆ©vel letƶltƶtt dalok soha nem tƶrlődnek automatikusan.
+ <p/><font color="red">Ha a ChromeCast sikertelen</font>:
+ <br/>PrĆ³bĆ”lja meg bejelƶlni: BeĆ”llĆ­tĆ”sok -> LejĆ”tszĆ”s -> Eszkƶz hasznĆ”lata proxykĆ©nt. Ez egy kerĆ¼lő megoldĆ”s arra, ha a ChromeCast elutasĆ­tja a sajĆ”t alƔƭrĆ”sĆŗ tanĆŗsĆ­tvĆ”nyt.
+ <p/><font color="red">A MĆ©diatĆ”r első szintje tulajdonkĆ©ppen az előadĆ³k csoportja</font>:
+ <br/>A BeĆ”llĆ­tĆ”sok menĆ¼ben tƶrƶlje az "ElőadĆ³k első szintje" jelƶlĆ©st. Ez teszi lehetővĆ©, hogy a mappĆ”k teljes első szintjĆ©nek megjelenĆ­tĆ©se előadĆ³i csoportonkĆ©nt Ć©s ne előadĆ³nkĆ©nt legyen kezelve.
+ ]]>
+ </string>
+ <string name="main.select_server">KiszolgĆ”lĆ³ kivĆ”lasztĆ”sa</string>
+ <string name="main.shuffle">LejƔtszƔs kevert sorrendben</string>
+ <string name="main.offline">Offline mĆ³d</string>
+ <string name="main.online">Online mĆ³d</string>
+ <string name="main.settings">BeƔllƭtƔsok</string>
+ <string name="main.albums_title">Albumok</string>
+ <string name="main.albums_per_folder">MappƔnkƩnt</string>
+ <string name="main.albums_newest">UtoljƔra hozzƔadottak</string>
+ <string name="main.albums_recent">UtoljƔra lejƔtszottak</string>
+ <string name="main.albums_frequent">Legtƶbbszƶr lejƔtszottak</string>
+ <string name="main.albums_highest">Legjobbra ƩrtƩkeltek</string>
+ <string name="main.albums_starred">Csillagozottak</string>
+ <string name="main.albums_random">VĆ©letlenszerű kivĆ”lasztĆ”s</string>
+ <string name="main.albums_genres">Műfajok</string>
+ <string name="main.albums_year">Ɖvtizedek</string>
+ <string name="main.albums_alphabetical">Betűrendben</string>
+ <string name="main.videos">VideĆ³k</string>
+ <string name="main.songs_genres">@string/main.albums_genres</string>
+ <string name="main.back_confirm">Nyomja meg mƩg egyszer a kilƩpƩshez!</string>
+ <string name="main.scan_complete">A mĆ©diatĆ”r frissĆ­tĆ©se befejeződƶtt a kiszolgĆ”lĆ³n!</string>
+
+ <string name="menu.search">KeresƩs</string>
+ <string name="menu.shuffle">LejƔtszƔs kevert sorrendben</string>
+ <string name="menu.refresh">FrissƭtƩs</string>
+ <string name="menu.play">LejƔtszƔs</string>
+ <string name="menu.play_last">SorbaĆ”llĆ­tĆ”s utolsĆ³nak</string>
+ <string name="menu.exit">KilƩpƩs</string>
+ <string name="menu.settings">BeƔllƭtƔsok</string>
+ <string name="menu.help">SĆŗgĆ³</string>
+ <string name="menu.about">NĆ©vjegy</string>
+ <string name="menu.add_playlist">HozzƔadƔs lejƔtszƔsi listƔhoz</string>
+ <string name="menu.remove_playlist">EltĆ”volĆ­tĆ”s a lejĆ”tszĆ”si listĆ”bĆ³l</string>
+ <string name="menu.deleted_playlist">\"%s\" lejƔtszƔsi lista tƶrƶlve</string>
+ <string name="menu.deleted_playlist_error">\"%s\" lejƔtszƔsi lista tƶrlƩse sikertelen!</string>
+ <string name="menu.log">Log kĆ¼ldĆ©se</string>
+ <string name="menu.set_timer">IdőzĆ­tő beĆ”llĆ­tĆ”sa</string>
+ <string name="menu.check_podcasts">ƚj epizĆ³dok ellenőrzĆ©se</string>
+ <string name="menu.add_podcast">Csatorna hozzƔadƔsa</string>
+ <string name="menu.keep_synced">Tartsa szinkronizƔlva</string>
+ <string name="menu.stop_sync">SzinkronizƔlƔs megƔllƭtƔsa</string>
+ <string name="menu.show_all">Ɩsszes mĆ©dia megjelenĆ­tĆ©se</string>
+ <string name="menu.show_artist">UgrĆ”s az előadĆ³hoz</string>
+ <string name="menu.share">MegosztƔs</string>
+ <string name="menu.delete_cache">GyorsĆ­tĆ³tĆ”r tƶrlĆ©se</string>
+ <string name="menu.cast">TovƔbbƭtƔs eszkƶzhƶz</string>
+ <string name="menu.faq">FAQ</string>
+ <string name="menu.add_user">FelhasznĆ”lĆ³ hozzĆ”adĆ”sa</string>
+ <string name="menu.rescan">MĆ©diatĆ”r frissĆ­tĆ©se a kiszolgĆ”lĆ³n</string>
+ <string name="menu.rate">ƉrtĆ©kelĆ©s</string>
+ <string name="menu.top_tracks">Last.fm legjobb dalok</string>
+ <string name="menu.similar_artists">HasonlĆ³ előadĆ³k</string>
+ <string name="menu.show_missing">HiĆ”nyzĆ³ megjelenĆ­tĆ©se</string>
+ <string name="menu.start_radio">RĆ”diĆ³ indĆ­tĆ”sa</string>
+ <string name="menu.first_level_artist">ElőadĆ³k első szintje</string>
+
+ <string name="playlist.label">LejƔtszƔsi listƔk</string>
+ <string name="playlist.update_info">SzerkesztƩs</string>
+ <string name="playlist.updated_info">\"%s\" lejĆ”tszĆ”si lista mĆ³dosĆ­tva</string>
+ <string name="playlist.updated_info_error">\"%s\" lejĆ”tszĆ”si lista mĆ³dosĆ­tĆ”sa sikertelen!</string>
+ <string name="playlist.overwrite">LĆ©tező lejĆ”tszĆ”si lista felĆ¼lĆ­rĆ”sa</string>
+ <string name="playlist.add_to">HozzƔadƔs lejƔtszƔsi listƔhoz</string>
+ <string name="playlist.create_new">ƚj lejĆ”tszĆ”si lista</string>
+ <string name="playlist.delete">LejƔtszƔsi lista tƶrlƩse</string>
+
+ <string name="search.label">KeresƩs</string>
+ <string name="search.title">KeresƩs</string>
+ <string name="search.search">Ɖrintse meg a keresĆ©shez</string>
+ <string name="search.no_match">Nincs talĆ”lat, prĆ³bĆ”lja Ćŗjra!</string>
+ <string name="search.artists">ElőadĆ³k</string>
+ <string name="search.albums">Albumok</string>
+ <string name="search.songs">Dalok</string>
+ <string name="search.more">TovƔbbiak</string>
+
+ <string name="progress.wait">KƩrem vƔrjon...</string>
+
+ <string name="music_library.label">MƩdiatƔr</string>
+ <string name="music_library.label_offline">Kapcsolat nĆ©lkĆ¼li mĆ©diĆ”k</string>
+
+ <string name="select_album.select">Ɩsszes jelƶlĆ©se be/ki</string>
+ <string name="select_album.n_selected">%d dal kijelƶlve.</string>
+ <string name="select_album.n_unselected">%d dal visszavonva.</string>
+ <string name="select_album.more">TovƔbbiak</string>
+ <string name="select_album.offline">Offline</string>
+ <string name="select_album.searching">KeresƩs...</string>
+ <string name="select_album.no_sdcard">Hiba: SD kƔrtya nem Ɣll rendelkezƩsre!</string>
+ <string name="select_album.no_network">Figyelem: HĆ”lĆ³zat nem Ć”ll rendelkezĆ©sre!</string>
+ <string name="select_album.no_room">Figyelem: MƔr csak %s hely Ɣll rendelkezƩsre!</string>
+ <string name="select_album.not_licensed">A kiszolgĆ”lĆ³nak nincs licence! %d prĆ³banap van hĆ”tra!</string>
+ <string name="select_album.donate_dialog_message">KorlƔtlan letƶltƩshez juthat a Subsonic tƔmogatƔsƔval!</string>
+ <string name="select_album.donate_dialog_now">Most</string>
+ <string name="select_album.donate_dialog_later">KĆ©sőbb</string>
+ <string name="select_album.donate_dialog_0_trial_days_left">A prĆ³baidőszak lejĆ”rt!</string>
+
+ <string name="offline.sync_dialog_title">Offline dalok vƔrnak a szinkronizƔlƔs befejezƩsƩre</string>
+ <string name="offline.sync_dialog_message">%1$d scrobble folyamat offline mĆ³dban?
+ \n%2$d csillagozĆ”s folyamat offline mĆ³dban?
+ </string>
+ <string name="offline.sync_dialog_default">A művelet hasznĆ”lata alapĆ©rtelmezettkĆ©nt</string>
+ <string name="offline.sync_success">%1$d dal sikeresen szinkronizƔlva</string>
+ <string name="offline.sync_partial">%1$d/%2$d dal sikeresen szinkronizƔlva</string>
+ <string name="offline.sync_error">A dalok szinkronizƔlƔsa sikertelen!</string>
+
+ <string name="select_genre.blank">Ɯres</string>
+ <string name="select_genre.songs">%d dal</string>
+ <string name="select_genre.albums">%d album</string>
+
+ <string name="select_podcasts.error">A podcast hibĆ”t jelzett a kiszolgĆ”lĆ³ra tƶrtĆ©nő letƶltĆ©s kƶzben! A kiszolgĆ”lĆ³nak kell letƶltenie előszƶr!</string>
+ <string name="select_podcasts.skipped">Ez a podcast nem lett letƶltve a kiszolgĆ”lĆ³ra! A kiszolgĆ”lĆ³nak kell letƶltenie előszƶr!</string>
+ <string name="select_podcasts.initializing">A podcast csatorna inicializĆ”lĆ”sa a kiszolgĆ”lĆ³n. KĆ©rjĆ¼k, tƶltse be Ćŗjra nĆ©hĆ”ny pillanat mĆŗlva!</string>
+ <string name="select_podcasts.server_download">LetƶltĆ©s a kiszolgĆ”lĆ³ra</string>
+ <string name="select_podcasts.server_delete">TƶrlĆ©s a kiszolgĆ”lĆ³rĆ³l</string>
+ <string name="select_podcasts.downloading">\"%s\" letƶltĆ©se a kiszolgĆ”lĆ³ra</string>
+ <string name="select_podcasts.refreshing">A kiszolgĆ”lĆ³ ellenőrzi az Ćŗj podcastokat...</string>
+ <string name="select_podcasts.deleted">\"%s\" podcast tƶrƶlve</string>
+ <string name="select_podcasts.deleted_error">\"%s\" podcast tƶrlƩse sikertelen!</string>
+ <string name="select_podcasts.add_url">URL:</string>
+ <string name="select_podcasts.created_error">Podcast hozzƔadƔsa sikertelen!</string>
+ <string name="select_podcasts.invalid_podcast_channel">ƉrvĆ©nytelen podcast csatorna: \"%s\"</string>
+ <string name="select_podcasts.delete">Podcast tƶrlƩse</string>
+
+ <string name="download.empty">A vĆ”rĆ³lista Ć¼res!</string>
+ <string name="download.shuffle_loading">Kevert sorrendű lista betƶltĆ©se...</string>
+ <string name="download.playerstate_downloading">LetƶltƩs - \"%s\"</string>
+ <string name="download.playerstate_buffering">PufferelƩs</string>
+ <string name="download.playerstate_playing_shuffle">Sorrend keverƩse</string>
+ <string name="download.menu_show_album">UgrƔs az albumhoz</string>
+ <string name="download.menu_lyrics">Dalszƶveg</string>
+ <string name="download.menu_remove">EltĆ”volĆ­tĆ”s a vĆ”rĆ³listĆ”rĆ³l</string>
+ <string name="download.menu_remove_all">Ɩsszes eltĆ”volĆ­tĆ”sa</string>
+ <string name="download.menu_screen_on">Kijelző be</string>
+ <string name="download.menu_shuffle">Sorrend keverƩse</string>
+ <string name="download.menu_toggle">VƔltƔs</string>
+ <string name="download.menu_save">MentƩs lejƔtszƔsi listƔba</string>
+ <string name="download.menu_shuffle_notification">LejƔtszƔs kevert sorrendben</string>
+ <string name="download.menu_remove_played_songs">LejƔtszottak eltƔvolƭtƔsa</string>
+ <string name="download.playlist_title">MentƩs lejƔtszƔsi listƔba</string>
+ <string name="download.playlist_name">LejƔtszƔsi lista neve:</string>
+ <string name="download.playlist_saving">\"%s\" lejƔtszƔsi lista mentƩse...</string>
+ <string name="download.playlist_done">LejƔtszƔsi lista mentƩse sikeres</string>
+ <string name="download.playlist_error">LejĆ”tszĆ”si lista mentĆ©se sikertelen, prĆ³bĆ”lja kĆ©sőbb!</string>
+ <string name="download.repeat_off">IsmƩtlƩs ki</string>
+ <string name="download.repeat_all">Ɩsszes ismĆ©tlĆ©se</string>
+ <string name="download.repeat_single">Dal ismƩtlƩse</string>
+ <string name="download.jukebox_on">TĆ”vvezĆ©rlĆ©s bekapcsolĆ”sa. A zenelejĆ”tszĆ”s a szĆ”mĆ­tĆ³gĆ©pen tƶrtĆ©nik.</string>
+ <string name="download.jukebox_off">TƔvvezƩrlƩs kikapcsolƔsa. A zenelejƔtszƔs az eszkƶzƶn tƶrtƩnik.</string>
+ <string name="download.jukebox_volume">Hangerő tĆ”vvezĆ©rlĆ©se</string>
+ <string name="download.jukebox_server_too_old">A tĆ”vvezĆ©rlĆ©s nem tĆ”mogatott. KĆ©rjĆ¼k, frissĆ­tse a Subsonic kiszolgĆ”lĆ³t!</string>
+ <string name="download.jukebox_offline">A tĆ”vvezĆ©rlĆ©s nem lehetsĆ©ges offline mĆ³dban!</string>
+ <string name="download.jukebox_not_authorized">A tĆ”vvezĆ©rlĆ©s nem lehetsĆ©ges! EngedĆ©lyezze a Jukebox mĆ³dot a <b>Users &gt; Settings</b> menĆ¼ben a Subsonic kiszolgĆ”lĆ³n!</string>
+ <string name="download.timer_length">Időhossz:</string>
+ <string name="download.start_timer">IdőzĆ­tő indĆ­tĆ”sa</string>
+ <string name="download.stop_timer">IdőzĆ­tő megĆ”llĆ­tĆ”sa</string>
+ <string name="download.need_download">A videĆ³t előszƶr le kell tƶlteni!</string>
+ <string name="download.no_streaming_player">Nincs megfelelő lejĆ”tszĆ³ a stream megjelenĆ­tĆ©sĆ©hez!</string>
+ <string name="download.playing_out_of">LejƔtszƔs: %1$d/%2$d</string>
+ <string name="download.save_bookmark_title">Kƶnyvjelző lĆ©trehozĆ”sa</string>
+ <string name="download.save_bookmark">Kƶnyvjelző lĆ©trehozva</string>
+ <string name="download.save_bookmark_failed">Kƶnyvjelző lĆ©trehozĆ”sa sikertelen!</string>
+ <string name="download.downloading_title">%1$d dal letƶltƩse</string>
+ <string name="download.downloading_summary">AktuƔlis: %1$s</string>
+ <string name="download.downloading_summary_expanded">AktuƔlis: %1$s
+ \nBecsĆ¼lt mĆ©ret: %2$s</string>
+ <string name="download.failed_to_load">A beolvasƔs sikertelen!</string>
+ <string name="download.restore_play_queue">FolytatƔs onnan, ahol egy mƔsik eszkƶzƶn abbahagyta.</string>
+
+ <string name="sync.new_podcasts">ƚj podcastok: \"%s\"</string>
+ <string name="sync.new_playlists">ƚj lejĆ”tszĆ”si listĆ”k: \"%s\"</string>
+ <string name="sync.new_albums">ƚj albumok: \"%s\"</string>
+ <string name="sync.new_starred">ƚj csillagozott dalok</string>
+
+ <string name="starring_content_starred">\"%s\" csillagozƔs be</string>
+ <string name="starring_content_unstarred">\"%s\" csillagozƔs ki</string>
+ <string name="starring_content_error">Nem sikerĆ¼lt frissĆ­teni \"%s\", prĆ³bĆ”lja kĆ©sőbb!</string>
+
+ <string name="playlist_error">Nem sikerĆ¼lt elĆ©rni a lejĆ”tszĆ”si lista adatait!</string>
+ <string name="updated_playlist">%1$s dal hozzƔadva: \"%2$s\"</string>
+ <string name="updated_playlist_error">Nem sikerĆ¼lt frissĆ­teni \"%s\", prĆ³bĆ”lja kĆ©sőbb!</string>
+ <string name="removed_playlist">%1$s dal eltƔvolƭtva: \"%2$s\"</string>
+
+ <string name="bookmark.delete">Kƶnyvjelző tƶrlĆ©se</string>
+ <string name="bookmark.delete_title">Kƶnyvjelző tƶrlĆ©se</string>
+ <string name="bookmark.deleted">\"%s\" kƶnyvjelző tƶrƶlve</string>
+ <string name="bookmark.deleted_error">\"%s\" kƶnyvjelző tƶrlĆ©se sikertelen!</string>
+ <string name="bookmark.details_title">Kƶnyvjelző rĆ©szletei</string>
+ <string name="bookmark.details">Dal: %1$s
+ \nPozĆ­ciĆ³: %2$s
+ \nLĆ©trehozva: %3$s
+ \nUtolsĆ³ mĆ³dosĆ­tĆ”s: %4$s
+ \nMegjegyzƩs: %5$s</string>
+ <string name="bookmark.resume_title">Folytatja a lejƔtszƔst?</string>
+ <string name="bookmark.resume">\"%1$s\" folytatƔsa innen: \"%2$s\"</string>
+ <string name="bookmark.action_resume">FolytatƔs</string>
+ <string name="bookmark.action_start_over">KezdƩs</string>
+
+ <string name="rating.title">\"%s\" ƩrtƩkelve</string>
+ <string name="rating.set_rating">\"%s\" ƩrtƩkelve</string>
+ <string name="rating.set_rating_failed">\"%s\" ƩrtƩkelƩse sikertelen!</string>
+ <string name="rating.remove_rating">\"%s\" ƩrtƩkelƩse visszavonva</string>
+ <string name="rating.remove_rating_failed">\"%s\" ƩrtƩkelƩsƩnek visszavonƔsa sikertelen!</string>
+
+ <string name="song_details.error">Hiba</string>
+ <string name="song_details.skipped">ƁtlƩpve</string>
+ <string name="song_details.downloading">LetƶltƩs</string>
+
+ <string name="lyrics.nomatch">Dalszƶveg nem talĆ”lhatĆ³!</string>
+
+ <string name="error.label">Hiba</string>
+
+ <string name="settings.title">BeƔllƭtƔsok</string>
+ <string name="settings.test_connection_title">Kapcsolat tesztelƩse</string>
+ <string name="settings.servers_add">KiszolgĆ”lĆ³ hozzĆ”adĆ”sa</string>
+ <string name="settings.servers_remove">KiszolgĆ”lĆ³ eltĆ”volĆ­tĆ”sa</string>
+ <string name="settings.servers_title">KiszolgĆ”lĆ³k</string>
+ <string name="settings.server_unused">Nem hasznƔlt</string>
+ <string name="settings.server_name">NĆ©v</string>
+ <string name="settings.server_address">KiszolgĆ”lĆ³ cĆ­me</string>
+ <string name="settings.server_local_network_ssid" >Helyi hĆ”lĆ³zati SSID</string>
+ <string name="settings.server_local_network_ssid_hint">AktuƔlis SSID: %s</string>
+ <string name="settings.server_internal_address">Belső hĆ”lĆ³zati cĆ­m</string>
+ <string name="settings.server_username">FelhasznĆ”lĆ³nĆ©v</string>
+ <string name="settings.server_password">JelszĆ³</string>
+ <string name="settings.server_open_browser">MegnyitĆ”s bƶngĆ©szőben</string>
+ <string name="settings.server_sync_summary">FĆ¼ggetlenĆ¼l attĆ³l, hogy a szinkronizĆ”lĆ”s engedĆ©lyezett-e ezen a kiszolgĆ”lĆ³n.</string>
+ <string name="settings.server_sync">SzinkronizƔlƔs engedƩlyezve</string>
+ <string name="settings.cache_title">Zene gyorsĆ­tĆ³tĆ”r (Cache)</string>
+ <string name="settings.preload_wifi">Dalok előolvasĆ”sa (Wi-Fi)</string>
+ <string name="settings.preload_mobile">Dalok előolvasĆ”sa (MobilhĆ”lĆ³zat)</string>
+ <string name="settings.cache_size">GyorsĆ­tĆ³tĆ”r mĆ©rete (MB)</string>
+ <string name="settings.cache_location">GyorsĆ­tĆ³tĆ”r helye</string>
+ <string name="settings.cache_location_error">HibĆ”s gyorsĆ­tĆ³tĆ”r hely! Az alapĆ©rtelmezett hasznĆ”lata.</string>
+ <string name="settings.cache_location_reset">A beĆ”llĆ­tott gyorsĆ­tĆ³tĆ”r-hely mĆ”r nem Ć­rhatĆ³! Ha a kƶzelmĆŗltban frissĆ­tette telefonja Android rendszerĆ©t 4.4.x KitKat verziĆ³ra, abban az SD kĆ”rtya kezelĆ©se megvĆ”ltozott, Ć©s az alkalmazĆ”sok csak egy speciĆ”lis helyre tudnak Ć­rni. A Dsub mĆ”r automatikusan Ć”tĆ”llt a megfelelő helyre. Ahhoz, hogy a rĆ©gi adatokat tƶrƶlni tudja, csatlakoztassa az SD kĆ”rtyĆ”t a szĆ”mĆ­tĆ³gĆ©pĆ©hez, Ć©s tƶrƶlje a rĆ©gi mappĆ”t!</string>
+ <string name="settings.cache_clear">GyorsĆ­tĆ³tĆ”r tƶrlĆ©se</string>
+ <string name="settings.cache_clear_complete">GyorsĆ­tĆ³tĆ”r tƶrlĆ©se kĆ©sz!</string>
+ <string name="settings.testing_connection">Kapcsolat tesztelƩse...</string>
+ <string name="settings.testing_ok">Kapcsolat OK!</string>
+ <string name="settings.testing_unlicensed">Kapcsolat OK! A kiszolgĆ”lĆ³nak nincs licence!</string>
+ <string name="settings.connection_failure">KapcsolĆ³dĆ”si hiba!</string>
+ <string name="settings.invalid_url">Adjon meg egy ƩrvƩnyes URL-t!</string>
+ <string name="settings.invalid_username">Adjon meg egy Ć©rvĆ©nyes felhasznĆ”lĆ³nevet (szĆ³kƶzt nem tartalmazhat)!</string>
+ <string name="settings.appearance_title">MegjelenƩs</string>
+ <string name="settings.theme_title">TƩmƔk</string>
+ <string name="settings.theme_light">VilƔgos</string>
+ <string name="settings.theme_dark">SƶtƩt</string>
+ <string name="settings.theme_black">Fekete</string>
+ <string name="settings.theme_holo">Holo</string>
+ <string name="settings.theme_fullscreen">Teljes kĆ©pernyős</string>
+ <string name="settings.theme_fullscreen_summary">Teljes kĆ©pernyős Ć¼zemmĆ³d (Ć©rtesĆ­tĆ©si sĆ”v elrejtĆ©se).</string>
+ <string name="settings.track_title">DalsorszƔm megjelenƭtƩse</string>
+ <string name="settings.track_summary">DalsorszĆ”m megjelenĆ­tĆ©se a dal cĆ­me előtt, ha lĆ©tezik.</string>
+ <string name="settings.custom_sort">EgyƩni rendezƩs</string>
+ <string name="settings.custom_sort_summary">A kiszolgĆ”lĆ³ alapĆ©rtelmezett rendezĆ©sĆ©nek felĆ¼lbĆ­rĆ”lĆ”sa, rendezĆ©s a lemez sorszĆ”ma Ć©s a kiadĆ”s Ć©ve alapjĆ”n.</string>
+ <string name="settings.network_title">HĆ”lĆ³zat</string>
+ <string name="settings.max_bitrate_wifi">Max. audiĆ³ bitrĆ”ta - Wi-Fi</string>
+ <string name="settings.max_bitrate_mobile">Max. audiĆ³ bitrĆ”ta - MobilhĆ”lĆ³zat</string>
+ <string name="settings.max_bitrate_32">32 Kbps</string>
+ <string name="settings.max_bitrate_64">64 Kbps</string>
+ <string name="settings.max_bitrate_80">80 Kbps</string>
+ <string name="settings.max_bitrate_96">96 Kbps</string>
+ <string name="settings.max_bitrate_112">112 Kbps</string>
+ <string name="settings.max_bitrate_128">128 Kbps</string>
+ <string name="settings.max_bitrate_160">160 Kbps</string>
+ <string name="settings.max_bitrate_192">192 Kbps</string>
+ <string name="settings.max_bitrate_256">256 Kbps</string>
+ <string name="settings.max_bitrate_320">320 Kbps</string>
+ <string name="settings.max_video_bitrate_wifi">Max. videĆ³ bitrĆ”ta - Wi-Fi</string>
+ <string name="settings.max_video_bitrate_mobile">Max. videĆ³ bitrĆ”ta - MobilhĆ”lĆ³zat</string>
+ <string name="settings.max_video_bitrate_200">200 Kbps</string>
+ <string name="settings.max_video_bitrate_300">300 Kbps</string>
+ <string name="settings.max_video_bitrate_400">400 Kbps</string>
+ <string name="settings.max_video_bitrate_500">500 Kbps</string>
+ <string name="settings.max_video_bitrate_700">700 Kbps</string>
+ <string name="settings.max_video_bitrate_1000">1000 Kbps</string>
+ <string name="settings.max_video_bitrate_1500">1500 Kbps</string>
+ <string name="settings.max_video_bitrate_2000">2000 Kbps</string>
+ <string name="settings.max_video_bitrate_3000">3000 Kbps</string>
+ <string name="settings.max_video_bitrate_5000">5000 Kbps</string>
+ <string name="settings.max_bitrate_unlimited">KorlƔtlan</string>
+ <string name="settings.wifi_required_title">StreamelƩs csak Wi-Fivel</string>
+ <string name="settings.wifi_required_summary">StreamelĆ©s csak Wi-Fi hĆ”lĆ³zaton keresztĆ¼l.</string>
+ <string name="settings.network_timeout_title">HĆ”lĆ³zati időtĆŗllĆ©pĆ©s</string>
+ <string name="settings.network_timeout_10000">10 mƔsodperc</string>
+ <string name="settings.network_timeout_15000">15 mƔsodperc</string>
+ <string name="settings.network_timeout_30000">30 mƔsodperc</string>
+ <string name="settings.network_timeout_45000">45 mƔsodperc</string>
+ <string name="settings.network_timeout_60000">60 mƔsodperc</string>
+ <string name="settings.preload_0">0 dal</string>
+ <string name="settings.preload_1">1 dal</string>
+ <string name="settings.preload_2">2 dal</string>
+ <string name="settings.preload_3">3 dal</string>
+ <string name="settings.preload_5">5 dal</string>
+ <string name="settings.preload_10">10 dal</string>
+ <string name="settings.preload_unlimited">KorlƔtlan</string>
+ <string name="settings.clear_search_history">KeresĆ©si előzmĆ©nyek tƶrlĆ©se</string>
+ <string name="settings.search_history_cleared">KeresĆ©si előzmĆ©nyek tƶrƶlve</string>
+ <string name="settings.other_title">EgyƩb beƔllƭtƔsok</string>
+ <string name="settings.scrobble_title">TovƔbbƭtƔs Last.fm-re (Scrobbling)</string>
+ <string name="settings.scrobble_summary">A Last.fm felhasznĆ”lĆ³nevet Ć©s jelszĆ³t be kell Ć”llĆ­tani a Subsonic kiszolgĆ”lĆ³n!</string>
+ <string name="settings.hide_media_title">ElrejtƩs</string>
+ <string name="settings.hide_media_summary">ZenefĆ”jlok elrejtĆ©se egyĆ©b alkalmazĆ”sok elől.</string>
+ <string name="settings.hide_media_toast">A kƶvetkező alkalomtĆ³l lĆ©p Ć©letbe, amikor az Android zenefĆ”jlokat keres az eszkƶzƶn.</string>
+ <string name="settings.media_button_title">MĆ©dia vezĆ©rlőgombok</string>
+ <string name="settings.media_button_summary">A lejĆ”tszĆ³ irĆ”nyĆ­tĆ”sa a bluetooth eszkƶz vagy a fĆ¼lhallgatĆ³ vezĆ©rlőgombjaival.</string>
+ <string name="settings.screen_lit_title">KĆ©pernyő Ć©brentartĆ”sa</string>
+ <string name="settings.screen_lit_summary">KĆ©pernyő Ć©brentartĆ”sa a letƶltĆ©s alatt a magasabb letƶltĆ©si sebessĆ©g Ć©rdekĆ©ben.</string>
+ <string name="settings.playlist_title">LejƔtszƔsi listƔk</string>
+ <string name="settings.playlist_random_size_title">VĆ©letlenszerű lejĆ”tszĆ”si lista mĆ©rete</string>
+ <string name="settings.sleep_timer_title">AlvĆ³ Ć¼zemmĆ³d időzĆ­tő</string>
+ <string name="settings.sleep_timer_duration_title">AlvĆ³ Ć¼zemmĆ³d időtartam</string>
+ <string name="settings.sleep_timer_off">Ki</string>
+ <string name="settings.sleep_timer_on">Be</string>
+ <string name="settings.sleep_timer_always_on">Mindig be</string>
+ <string name="settings.temp_loss_title">KĆ¼lső esemĆ©ny bekƶvetkeztekor</string>
+ <string name="settings.temp_loss_pause">MegƔllƭtƔs minden esetben</string>
+ <string name="settings.temp_loss_pause_lower">MegĆ”llĆ­tĆ”s, kĆ©rĆ©sre alacsonyabb hangerő</string>
+ <string name="settings.temp_loss_lower">Alacsonyabb hangerő</string>
+ <string name="settings.temp_loss_nothing">Ne csinƔljon semmit</string>
+ <string name="settings.disconnect_pause_title">MegƔllƭtƔs kapcsolatbontƔs esetƩn</string>
+ <string name="settings.disconnect_pause_both">MegƔllƭtƔs minden esetben</string>
+ <string name="settings.disconnect_pause_neither">Ne csinƔljon semmit</string>
+ <string name="settings.persistent_title">ƁllandĆ³ kijelzĆ©s</string>
+ <string name="settings.persistent_summary">KijelzƩs az ƩrtesƭtƩsi sƔvon a lejƔtszƔs megƔllƭtƔsa utƔn is. Nyomja meg a bezƔrƔs gombot a tƶrlƩsƩhez!</string>
+ <string name="settings.gapless_playback">EgybefĆ¼ggő lejĆ”tszĆ”s (Gapless)</string>
+ <string name="settings.gapless_playback_summary">Ha lefagyĆ”sokat/furcsasĆ”gokat tapasztal az egybefĆ¼ggő lejĆ”tszĆ”s (Gapless) engedĆ©lyezĆ©se utĆ”n, a problĆ©ma megoldĆ”sĆ”hoz kapcsolja ki a funkciĆ³t!</string>
+ <string name="settings.chat_refresh">CsevegƩs frissƭtƩsi gyakorisƔga (mp.)</string>
+ <string name="settings.chat_enabled">CsevegƩs (Chat) engedƩlyezƩse</string>
+ <string name="settings.chat_enabled_summary">CsevegĆ©s (Chat) menĆ¼pont megjelenĆ­tĆ©se az elhĆŗzhatĆ³ oldalsĆ”von.</string>
+ <string name="settings.video_title">VideĆ³</string>
+ <string name="settings.video_player">VideĆ³-lejĆ”tszĆ³</string>
+ <string name="settings.video_raw">Nyers (Subsonic 4.8-tĆ³l)</string>
+ <string name="settings.video_hls">HTTP Live Stream (HLS) (Subsonic 4.8-tĆ³l)</string>
+ <string name="settings.video_transcode">Kƶzvetlen transzkĆ³dolĆ”s (video -> mp4, vagy hasonlĆ³ beĆ”llĆ­tĆ”s szĆ¼ksĆ©ges a kiszolgĆ”lĆ³n!)</string>
+ <string name="settings.video_flash">Flash (Plugin szĆ¼ksĆ©ges!)</string>
+ <string name="settings.cache_screen_title">GyorsĆ­tĆ³tĆ”r/HĆ”lĆ³zat</string>
+ <string name="settings.playback_title">LejƔtszƔs</string>
+ <string name="settings.hide_widget_title">Widget elrejtƩse</string>
+ <string name="settings.hide_widget_summary">Widget elrejtƩse kilƩpƩs utƔn.</string>
+ <string name="settings.podcasts_enabled">Podcastok engedƩlyezƩse</string>
+ <string name="settings.podcasts_enabled_summary">Podcastok menĆ¼pont megjelenĆ­tĆ©se az elhĆŗzhatĆ³ oldalsĆ”von.</string>
+ <string name="settings.bookmarks_enabled">Kƶnyvjelzők engedĆ©lyezĆ©se</string>
+ <string name="settings.bookmarks_enabled_summary">Kƶnyvjelzők menĆ¼pont megjelenĆ­tĆ©se az elhĆŗzhatĆ³ oldalsĆ”von.</string>
+ <string name="settings.shares_enabled">MegosztƔsok engedƩlyezƩse</string>
+ <string name="settings.shares_enabled_summary">MegosztĆ”sok menĆ¼pont megjelenĆ­tĆ©se az elhĆŗzhatĆ³ oldalsĆ”von.</string>
+ <string name="settings.sync_title">SzinkronizƔlƔs</string>
+ <string name="settings.sync_enabled">SzinkronizƔlƔs engedƩlyezƩse</string>
+ <string name="settings.sync_enabled_summary">LejĆ”tszĆ”si listĆ”k Ć©s podcastok vĆ”ltozĆ”sainak rendszeres ellenőrzĆ©se.</string>
+ <string name="settings.sync_interval">SzinkronizƔlƔs gyakorisƔga</string>
+ <string name="settings.sync_interval_15">15 perc</string>
+ <string name="settings.sync_interval_30">30 perc</string>
+ <string name="settings.sync_interval_60">1 Ć³ra</string>
+ <string name="settings.sync_interval_120">2 Ć³ra</string>
+ <string name="settings.sync_interval_240">4 Ć³ra</string>
+ <string name="settings.sync_interval_360">6 Ć³ra</string>
+ <string name="settings.sync_interval_720">12 Ć³ra</string>
+ <string name="settings.sync_interval_1440">Naponta</string>
+ <string name="settings.sync_wifi">SzinkronizƔlƔs csak Wi-Fivel</string>
+ <string name="settings.sync_wifi_summary">SzinkronizĆ”lĆ”s csak Wi-Fi hĆ”lĆ³zaton keresztĆ¼l.</string>
+ <string name="settings.sync_most_recent">UtoljƔra hozzƔadottak szinkronizƔlƔsa</string>
+ <string name="settings.sync_most_recent_summary">Az utoljĆ”ra hozzĆ”adott albumok automatikus gyorsĆ­tĆ³tĆ”razĆ”sa.</string>
+ <string name="settings.sync_starred">Csillagozottak szinkronizƔlƔsa</string>
+ <string name="settings.sync_starred_summary">A csillagozott dalok/albumok/előadĆ³k automatikus gyorsĆ­tĆ³tĆ”razĆ”sa.</string>
+ <string name="settings.sync_notification">SzinkronizƔlƔsi ƩrtesƭtƩsek</string>
+ <string name="settings.sync_notification_summary">ƉrtesĆ­tĆ©s megjelenĆ­tĆ©se, ha Ćŗj mĆ©dia kerĆ¼lt szinkronizĆ”lĆ”sra.</string>
+ <string name="settings.menu_options.title">OpcionĆ”lis menĆ¼beĆ”llĆ­tĆ”sok</string>
+ <string name="settings.menu_options.play_next_summary">SorbaĆ”llĆ­tĆ”s kƶvetkezőnek opciĆ³ megjelenĆ­tĆ©se a menĆ¼ben.</string>
+ <string name="settings.menu_options.play_last_summary">SorbaĆ”llĆ­tĆ”s utolsĆ³nak opciĆ³ megjelenĆ­tĆ©se a menĆ¼ben.</string>
+ <string name="settings.menu_options.star_summary">CsillagozĆ”s opciĆ³ megjelenĆ­tĆ©se a menĆ¼ben.</string>
+ <string name="settings.menu_options.shared_summary">MegosztĆ”s opciĆ³ megjelenĆ­tĆ©se a menĆ¼ben.</string>
+ <string name="settings.menu_options.rate_summary">ƉrtĆ©kelĆ©s opciĆ³ megjelenĆ­tĆ©se a menĆ¼ben.</string>
+ <string name="settings.browse_by_tags">BƶngƩszƩs ID3 Tag hasznƔlatƔval</string>
+ <string name="settings.browse_by_tags_summary">ID3 Tag mĆ³dszer hasznĆ”lata a fĆ”jlredszer alapĆŗ mĆ³d helyett. Subsonic 4.7+ verziĆ³ felett!</string>
+ <string name="settings.disable_exit_prompt">KilĆ©pĆ©s megerősĆ­tĆ©sĆ©nek tiltĆ”sa</string>
+ <string name="settings.disable_exit_prompt_summary">A főoldalon a vissza gomb megnyomĆ”sakor azonnali kilĆ©pĆ©s az alkalmazĆ”sbĆ³l.</string>
+ <string name="settings.override_system_language">A rendszer nyelvĆ©nek felĆ¼lbĆ­rĆ”lĆ”sa</string>
+ <string name="settings.override_system_language_summary">A Dsub megjelenĆ­tĆ©se angol nyelven abban az esetben is, ha rendelkezik fordĆ­tĆ”ssal. Az alkalmazĆ”st tƶrƶlni kell a memĆ³riĆ”bĆ³l, mert a beĆ”llĆ­tĆ”s csak ĆŗjraindĆ­tĆ”s utĆ”n lĆ©p Ć©rvĆ©nybe!</string>
+ <string name="settings.drawer_items_title">OldalsƔv elemei</string>
+ <string name="settings.play_now_after">LejƔtszƔs utƔna</string>
+ <string name="settings.play_now_after_summary">Egy helyi menĆ¼, amivel lehetővĆ© vĆ”lik minden dal lejĆ”tszĆ”sa a kijelƶlt elem utĆ”n (mint a Subsonic webes felĆ¼letĆ©n)</string>
+ <string name="settings.large_album_art">Nagy mĆ©retű albumborĆ­tĆ³k</string>
+ <string name="settings.large_album_art_summary">Albumok megjelenĆ­tĆ©se rĆ”csnĆ©zetben Ć©s nagy mĆ©retű albumborĆ­tĆ³val a listanĆ©zet helyett.</string>
+ <string name="settings.admin_enabled">Admin engedƩlyezƩse</string>
+ <string name="settings.admin_enabled_summary">Admin menĆ¼pont megjelenĆ­tĆ©se az elhĆŗzhatĆ³ oldalsĆ”von.</string>
+ <string name="settings.replay_gain">Hangerő-kiegyenlĆ­tĆ©s (Replay Gain)</string>
+ <string name="settings.replay_gain_summary">Hangerő kiegyenlĆ­tĆ©se (normalizĆ”lĆ”sa) a dal, vagy az album hangerőszint Ć©rtĆ©kei (tags) alapjĆ”n.</string>
+ <string name="settings.replay_gain_type">Hangerőszint meghatĆ”rozĆ”sa</string>
+ <string name="settings.replay_gain_type.smart">Intelligens mĆ³don</string>
+ <string name="settings.replay_gain_type.album">Album Ć©rtĆ©keiből</string>
+ <string name="settings.replay_gain_type.track">Dal Ć©rtĆ©keiből</string>
+ <string name="settings.replay_gain_bump">Hangerő-kiegyenlĆ­tĆ©s előerősĆ­tĆ©se</string>
+ <string name="settings.replay_gain_untagged">Dalok hangerő-kiegyenlĆ­tĆ©s nĆ©lkĆ¼l</string>
+ <string name="settings.casting">Casting (Tartalmak Ć”tkĆ¼ldĆ©se)</string>
+ <string name="settings.casting_proxy">Eszkƶz hasznƔlata proxykƩnt</string>
+ <string name="settings.casting_proxy_summary">StreamelĆ©s az eszkƶzƶn (mint egy proxyn) keresztĆ¼l. Ez megoldĆ”st hozhat nĆ©hĆ”ny esetben, pl. sajĆ”t alƔƭrĆ”sĆŗ tanĆŗsĆ­tvĆ”ny hasznĆ”latakor.</string>
+ <string name="settings.rename_duplicates">DuplikƔlt dalok ƔtnevezƩse</string>
+ <string name="settings.rename_duplicates_summary">DuplikĆ”lt dalok Ć”tnevezĆ©se az eredeti fĆ”jlnĆ©vre, Ć­gy megkĆ¼lƶnbƶztethetővĆ© vĆ”lnak.</string>
+
+ <string name="shuffle.title">Sorrend keverƩse</string>
+ <string name="shuffle.startYear">Kezdő Ć©v:</string>
+ <string name="shuffle.endYear">Befejező Ć©v:</string>
+ <string name="shuffle.genre">Műfaj:</string>
+ <string name="shuffle.pick_genre">Műfaj kivĆ”lasztĆ”sa</string>
+
+ <string name="share.info">Tulajdonos: %1$s
+ \nLeƭrƔs: %2$s
+ \nURL: %3$s
+ \nLĆ©trehozva: %4$s
+ \nUtolsĆ³ lĆ”togatĆ”s: %5$s
+ \nLejĆ”rati idő: %6$s
+ \nLƔtogatƔsok szƔma: %7$s
+
+ </string>
+ <string name="share.expires">LejĆ”rati idő: %s</string>
+ <string name="share.expires_never">Nincs lejĆ”rati idő</string>
+ <string name="share.deleted">\"%s\" megosztƔs tƶrƶlve</string>
+ <string name="share.deleted_error">\"%s\" megosztƔs tƶrlƩse sikertelen!</string>
+ <string name="share.no_expiration">Nincs lejĆ”rati idő</string>
+ <string name="share.expiration">LejĆ”rati idő:</string>
+ <string name="share.updated_info">\"%s\" megosztĆ”s informĆ”ciĆ³i frissĆ­tve</string>
+ <string name="share.updated_info_error">\"%s\" megosztĆ”s informĆ”ciĆ³inak frissĆ­tĆ©se sikertelen!</string>
+ <string name="share.via">MegosztƔs ezzel</string>
+ <string name="share.delete">MegosztƔs tƶrlƩse</string>
+
+ <string name="admin.add_user_username">FelhasznĆ”lĆ³nĆ©v:</string>
+ <string name="admin.add_user_email">Email:</string>
+ <string name="admin.add_user_password">JelszĆ³:</string>
+ <string name="admin.create_user_success">A felhasznĆ”lĆ³ lĆ©trehozva</string>
+ <string name="admin.create_user_error">A felhasznĆ”lĆ³ lĆ©trehozĆ”sa sikertelen!</string>
+ <string name="admin.change_username_invalid">Adjon meg egy Ć©rvĆ©nyes felhasznĆ”lĆ³nevet!</string>
+ <string name="admin.update_permissions">JogosultsĆ”gok mĆ³dosĆ­tĆ”sa</string>
+ <string name="admin.update_permissions_success">\"%1$s\" jogosultsĆ”gainak mĆ³dosĆ­tĆ”sa sikerĆ¼lt</string>
+ <string name="admin.update_permissions_error">\"%1$s\" jogosultsĆ”gainak mĆ³dosĆ­tĆ”sa sikertelen!</string>
+ <string name="admin.change_email">Email csere</string>
+ <string name="admin.change_email_success">\"%1$s\" email cĆ­mĆ©nek mĆ³dosĆ­tĆ”sa sikerĆ¼lt</string>
+ <string name="admin.change_email_error">\"%1$s\" email cĆ­mĆ©nek mĆ³dosĆ­tĆ”sa sikertelen!</string>
+ <string name="admin.change_email_label">ƚj email:</string>
+ <string name="admin.change_email_invalid">Adjon meg egy ƩrvƩnyes email cƭmet!</string>
+ <string name="admin.change_password">JelszĆ³ csere</string>
+ <string name="admin.change_password_success">\"%1$s\" jelszavĆ”nak mĆ³dosĆ­tĆ”sa sikerĆ¼lt</string>
+ <string name="admin.change_password_error">\"%1$s\" jelszavĆ”nak mĆ³dosĆ­tĆ”sa sikertelen!</string>
+ <string name="admin.change_password_label">ƚj jelszĆ³:</string>
+ <string name="admin.change_password_invalid">Adjon meg egy Ć©rvĆ©nyes jelszĆ³t!</string>
+ <string name="admin.delete_user">FelhasznĆ”lĆ³ tƶrlĆ©se</string>
+ <string name="admin.delete_user_success">\"%1$s\" felhasznĆ”lĆ³ lĆ©trehozva</string>
+ <string name="admin.delete_user_error">\"%1$s\" felhasznĆ”lĆ³ tƶrlĆ©se sikertelen!</string>
+ <string name="admin.confirm_password">JelszĆ³ megerősĆ­tĆ©se</string>
+ <string name="admin.confirm_password_bad">A beĆ­rt jelszĆ³ nem egyezik!</string>
+
+ <string name="admin.scrobblingEnabled">Scrobbling hasznƔlata</string>
+ <string name="admin.role.admin">AdminisztrƔtor</string>
+ <string name="admin.role.settings">BeĆ”llĆ­tĆ”sok mĆ³dosĆ­tĆ”sa</string>
+ <string name="admin.role.download">Eredeti fƔjlok letƶltƩse</string>
+ <string name="admin.role.upload">FeltƶltĆ©s a kiszolgĆ”lĆ³ra</string>
+ <string name="admin.role.coverArt">AlbumborĆ­tĆ³ cserĆ©je</string>
+ <string name="admin.role.comment">MegjegyzƩsek hozzƔadƔsa</string>
+ <string name="admin.role.podcast">Podcastok kezelƩse</string>
+ <string name="admin.role.stream">Zene streamelƩse</string>
+ <string name="admin.role.jukebox">Jukebox vezƩrlƩse</string>
+ <string name="admin.role.share">MegosztƔsok kezelƩse</string>
+ <string name="admin.role.lastfm">Last.fm funkciĆ³ hasznĆ”lata</string>
+
+ <string name="music_service.retry">HĆ”lĆ³zati hiba tƶrtĆ©nt! ƚjraprĆ³bĆ”lkozĆ”s %1$d/%2$d.</string>
+
+ <string name="background_task.wait">KƩrem vƔrjon...</string>
+ <string name="background_task.loading">BetƶltƩs...</string>
+ <string name="background_task.no_network">Az alkalmazĆ”s hĆ”lĆ³zati hozzĆ”fĆ©rĆ©st igĆ©nyel. KĆ©rjĆ¼k, kapcsolja be a Wi-Fi-t vagy a mobilhĆ”lĆ³zatot!</string>
+ <string name="background_task.network_error">HĆ”lĆ³zati hiba tƶrtĆ©nt! KĆ©rjĆ¼k, ellenőrizze a kiszolgĆ”lĆ³ cĆ­mĆ©t, vagy prĆ³bĆ”lja kĆ©sőbb!</string>
+ <string name="background_task.not_found">Az erőforrĆ”s nem talĆ”lhatĆ³! KĆ©rjĆ¼k, ellenőrizze a kiszolgĆ”lĆ³ cĆ­mĆ©t!</string>
+ <string name="background_task.parse_error">Hiba tƶrtĆ©nt a kiszolgĆ”lĆ³val tƶrtĆ©nő kommunikĆ”ciĆ³ban. KĆ©rjĆ¼k, ellenőrizze a kiszolgĆ”lĆ³ cĆ­mĆ©t, Ć©s prĆ³bĆ”ljon meg web bƶngĆ©szővel kapcsolĆ³dni a kiszolgĆ”lĆ³hoz!</string>
+
+ <string name="service.connecting">KapcsolĆ³dĆ”s a kiszolgĆ”lĆ³hoz, kĆ©rem vĆ”rjon...</string>
+
+ <string name="parser.upgrade_client">Nem kompatibilis verziĆ³. KĆ©rjĆ¼k, frissĆ­tse a DSub Android alkalmazĆ”st!</string>
+ <string name="parser.upgrade_server">Nem kompatibilis verziĆ³. KĆ©rjĆ¼k, frissĆ­tse a Subsonic kiszolgĆ”lĆ³t!</string>
+ <string name="parser.not_authenticated">HibĆ”s felhasznĆ”lĆ³nĆ©v vagy jelszĆ³!</string>
+ <string name="parser.not_authorized">Nincs engedĆ©lyezve! Ellenőrizze a felhasznĆ”lĆ³ jogosultsĆ”gait a Subsonic kiszolgĆ”lĆ³n!</string>
+ <string name="parser.artist_count">%d előadĆ³ talĆ”lhatĆ³ a mĆ©diatĆ”rban.</string>
+ <string name="parser.server_error">KiszolgĆ”lĆ³ hiba: %s</string>
+ <string name="parser.scan_count">%d tƩtel ƔtvizsgƔlva.</string>
+
+ <string name="select_artist.refresh">FrissƭtƩs</string>
+ <string name="select_artist.folder">Mappa kivƔlasztƔsa</string>
+ <string name="select_artist.all_folders">Ɩsszes mappa</string>
+
+ <string name="equalizer.label">Equalizer</string>
+ <string name="equalizer.enabled">EngedƩlyezve</string>
+ <string name="equalizer.preset">Profil kivƔlasztƔsa</string>
+ <string name="equalizer.bass_booster">Basszus fokozƔs</string>
+ <string name="equalizer.voice_booster">BeszƩdhang fokozƔs</string>
+ <string name="equalizer.db_size">%d dB</string>
+ <string name="equalizer.bass_size">%d ezer</string>
+
+ <string name="widget.4x1">DSub (4x1)</string>
+ <string name="widget.4x2">DSub (4x2)</string>
+ <string name="widget.4x3">DSub (4x3)</string>
+ <string name="widget.4x4">DSub (4x4)</string>
+ <string name="widget.initial_text">Ɖrintse meg a zene kivĆ”lasztĆ”sĆ”hoz!</string>
+ <string name="widget.sdcard_busy">Az SD kĆ”rtya nem elĆ©rhető!</string>
+ <string name="widget.sdcard_missing">Nincs SD kƔrtya!</string>
+
+ <string name="util.bytes_format.gigabyte">0.00 GB</string>
+ <string name="util.bytes_format.megabyte">0.00 MB</string>
+ <string name="util.bytes_format.kilobyte">0 KB</string>
+ <string name="util.bytes_format.byte">0 B</string>
+
+ <string name="changelog_full_title">ƚjdonsĆ”gok</string>
+ <string name="changelog_title">ƚjdonsĆ”gok</string>
+ <string name="changelog_ok_button">OK</string>
+ <string name="changelog_show_full">TovĆ”bbiakā€¦</string>
+
+ <string name="chat.send_a_message">Ɯzenet kĆ¼ldĆ©se</string>
+
+ <string name="changelog_version_format" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">Version <xliff:g id="version_name">%s</xliff:g></string>
+
+ <string name="tasker.start_playing">LejƔtszƔs indƭtƔsa</string>
+ <string name="tasker.start_playing_shuffled">LejƔtszƔs indƭtƔsa kevert sorrendben</string>
+ <string name="tasker.start_playing_title">Tasker -> DSub indƭtƔsa</string>
+ <string name="tasker.edit_shuffle_mode">IndƭtƔs kevert sorrendben: </string>
+ <string name="tasker.edit_shuffle_start_year">Kevert sorrend kezdő Ć©v:</string>
+ <string name="tasker.edit_shuffle_end_year">Kevert sorrend utolsĆ³ Ć©v:</string>
+ <string name="tasker.edit_shuffle_genre">Kevert sorrend műfaja:</string>
+ <string name="tasker.edit_server_offline">Offline kapcsolĆ³: </string>
+ <string name="tasker.edit_do_nothing">Ne csinƔljon semmit</string>
+
+ <plurals name="select_album_n_songs">
+ <item quantity="zero">Nincsenek dalok</item>
+ <item quantity="one">1 dal</item>
+ <item quantity="other">%d dal</item>
+ </plurals>
+ <plurals name="select_album_n_songs_downloading">
+ <item quantity="one">1 dal kijelƶlve letƶltƩsre.</item>
+ <item quantity="other">%d dal kijelƶlve letƶltƩsre.</item>
+ </plurals>
+ <plurals name="select_album_n_songs_added">
+ <item quantity="one">1 dal hozzĆ”adva a vĆ”rĆ³listĆ”hoz.</item>
+ <item quantity="other">%d dal hozzĆ”adva a vĆ”rĆ³listĆ”hoz.</item>
+ </plurals>
+ <plurals name="select_album_donate_dialog_n_trial_days_left">
+ <item quantity="one">1 nap van hĆ”tra a prĆ³baidőszakbĆ³l.</item>
+ <item quantity="other">%d nap van hĆ”tra a prĆ³baidőszakbĆ³l.</item>
+ </plurals>
+
+</resources>
diff --git a/res/values-land/integers.xml b/app/src/main/res/values-land/integers.xml
index 93883e2a..40071f39 100644
--- a/res/values-land/integers.xml
+++ b/app/src/main/res/values-land/integers.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
- <integer name="Grid.Columns">3</integer>
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <integer name="Grid.Columns">3</integer>
</resources> \ No newline at end of file
diff --git a/res/values-large/dimens.xml b/app/src/main/res/values-large/dimens.xml
index f68b5459..b08dda86 100644
--- a/res/values-large/dimens.xml
+++ b/app/src/main/res/values-large/dimens.xml
@@ -1,7 +1,7 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
- <dimen name="Button">64dip</dimen>
- <dimen name="Button.Small">54dip</dimen>
- <dimen name="AlbumArt.Small">96dip</dimen>
- <dimen name="AlbumArt.Header">210dip</dimen>
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <dimen name="Button">64dip</dimen>
+ <dimen name="Button.Small">54dip</dimen>
+ <dimen name="AlbumArt.Small">96dip</dimen>
+ <dimen name="AlbumArt.Header">210dip</dimen>
</resources> \ No newline at end of file
diff --git a/res/values-large/integers.xml b/app/src/main/res/values-large/integers.xml
index be82d55a..914ec84a 100644
--- a/res/values-large/integers.xml
+++ b/app/src/main/res/values-large/integers.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
- <integer name="Grid.Columns">3</integer>
- <integer name="TextDescriptionLength">10</integer>
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <integer name="Grid.Columns">3</integer>
+ <integer name="TextDescriptionLength">10</integer>
</resources> \ No newline at end of file
diff --git a/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 03f10808..cfae3ea0 100644
--- a/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -1,312 +1,312 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-
- <string name="common.appname">DSub</string>
- <string name="common.ok">OK</string>
- <string name="common.save">Š”Š¾Ń…Ń€Š°Š½Šøть</string>
- <string name="common.cancel">ŠžŃ‚Š¼ŠµŠ½Š°</string>
- <string name="common.play_now">Š’Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŃŃ‚Šø сŠµŠ¹Ń‡Š°Ń</string>
- <string name="common.play_shuffled">Š”Š»ŃƒŃ‡Š°Š¹Š½Š¾Šµ Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½ŠøŠµ</string>
- <string name="common.play_next">Š’Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŃŃ‚Šø сŠ»ŠµŠ“ующŠøŠ¼</string>
- <string name="common.play_last">Š’Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŃŃ‚Šø ŠæŠ¾ŃŠ»ŠµŠ“Š½ŠøŠ¼</string>
- <string name="common.download">Š”ŠŗŠ°Ń‡Š°Ń‚ŃŒ</string>
- <string name="common.pin">ŠšŠµŃˆŠøрŠ¾Š²Š°Ń‚ŃŒ</string>
- <string name="common.delete">Š£Š“Š°Š»Šøть</string>
- <string name="common.star">Š”Š¾Š±Š°Š²Šøть Š² Š·Š°ŠŗŠ»Š°Š“ŠŗŠø</string>
- <string name="common.unstar">Š£Š“Š°Š»Šøть ŠøŠ· Š·Š°ŠŗŠ»Š°Š“Š¾Šŗ</string>
- <string name="common.info">Š˜Š½Ń„Š¾Ń€Š¼Š°Ń†Šøя</string>
- <string name="common.name">ŠŠ°Š·Š²Š°Š½ŠøŠµ</string>
- <string name="common.comment">ŠšŠ¾Š¼Š¼ŠµŠ½Ń‚Š°Ń€ŠøŠ¹</string>
- <string name="common.public">ŠžŠ±Ń‰ŠµŠ“Š¾ŃŃ‚ŃƒŠæŠ½Ń‹Š¹</string>
- <string name="common.play_external">Š’Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŃŃ‚Šø Š²Š¾ Š²Š½ŠµŃˆŠ½ŠµŠ¼ ŠæŠ»ŠµŠµŃ€Šµ</string>
- <string name="common.stream_external">Š’Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŃŃ‚Šø ŠæŠ¾Ń‚Š¾Šŗ Š²Š¾ Š²Š½ŠµŃˆŠ½ŠµŠ¼ ŠæŠ»ŠµŠµŃ€Šµ</string>
- <string name="common.confirm">ŠŸŠ¾Š“тŠ²ŠµŃ€Š¶Š“ŠµŠ½ŠøŠµ</string>
-
- <string name="button_bar.home">Š”Š¾Š¼Š¾Š¹</string>
- <string name="button_bar.browse">ŠœŠµŠ“ŠøŠ°Ń‚ŠµŠŗŠ°</string>
- <string name="button_bar.search">ŠŸŠ¾ŠøсŠŗ</string>
- <string name="button_bar.playlists">Š”ŠæŠøсŠŗŠø</string>
- <string name="button_bar.now_playing">ŠŸŠ»ŠµŠµŃ€</string>
-
- <string name="main.welcome_title">Š—Š“рŠ°Š²ŃŃ‚Š²ŃƒŠ¹Ń‚Šµ!</string>
- <string name="main.welcome_text">Š”Š¾Š±Ń€Š¾ ŠæŠ¾Š¶Š°Š»Š¾Š²Š°Ń‚ŃŒ Š² DSub! Š­Ń‚Š¾ ŠæрŠøŠ»Š¾Š¶ŠµŠ½ŠøŠµ Š½Š°ŃŃ‚Ń€Š¾ŠµŠ½Š¾ Š½Š° рŠ°Š±Š¾Ń‚Ńƒ с Š“ŠµŠ¼Š¾ сŠµŃ€Š²ŠµŃ€Š¾Š¼ Subsonic. ŠŸŠ¾ŃŠ»Šµ Š½Š°ŃŃ‚Ń€Š¾Š¹ŠŗŠø Š’Š°ŃˆŠµŠ³Š¾ ŠæŠµŃ€ŃŠ¾Š½Š°Š»ŃŒŠ½Š¾Š³Š¾ сŠµŃ€Š²ŠµŃ€Š° (Š“Š¾ŃŃ‚ŃƒŠæŠµŠ½ Š½Š° <b>subsonic.org</b>), ŠæŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, ŠæŠµŃ€ŠµŠ¹Š“ŠøтŠµ Š² <b>ŠŠ°ŃŃ‚Ń€Š¾Š¹ŠŗŠø</b> Šø ŠøŠ·Š¼ŠµŠ½ŠøтŠµ ŠæŠ°Ń€Š°Š¼ŠµŃ‚ры Š“Š»Ń ŠæŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠµŠ½Šøя.</string>
-
- <string name="main.about_title">Šž ŠæрŠ¾Š³Ń€Š°Š¼Š¼Šµ DSub</string>
- <string name="main.about_text">ŠŠ²Ń‚Š¾Ń€: Scott Jackson
- \nEmail: dsub.android@gmail.com
- \nŠ’ŠµŃ€ŃŠøя: %1$s
- \nFiles Cached: %2$s
- \nŠ˜ŃŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Š½Š¾ Š¼ŠµŃŃ‚Š°: %3$s ŠøŠ· %4$s
- \nŠ”Š¾ŃŃ‚ŃƒŠæŠ½Š¾ Š¼ŠµŃŃ‚Š°: %5$s ŠøŠ· %6$s</string>
- <string name="main.select_server">Š’Ń‹Š±Ń€Š°Ń‚ŃŒ сŠµŃ€Š²ŠµŃ€</string>
- <string name="main.shuffle">Š”Š»ŃƒŃ‡Š°Š¹Š½Š¾Šµ Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½ŠøŠµ</string>
- <string name="main.offline">ŠžŃ‚ŠŗŠ»ŃŽŃ‡Šøться</string>
- <string name="main.online">ŠŸŠ¾Š“ŠŗŠ»ŃŽŃ‡Šøться</string>
- <string name="main.settings">ŠŠ°ŃŃ‚Ń€Š¾Š¹ŠŗŠø</string>
- <string name="main.albums_title">ŠŠ»ŃŒŠ±Š¾Š¼Ń‹</string>
- <string name="main.albums_newest">ŠŠµŠ“Š°Š²Š½Š¾ Š“Š¾Š±Š°Š²Š»ŠµŠ½Š½Ń‹Šµ</string>
- <string name="main.albums_recent">ŠŠµŠ“Š°Š²Š½Š¾ ŠæрŠ¾ŃŠ»ŃƒŃˆŠ°Š½Š½Ń‹Šµ</string>
- <string name="main.albums_frequent">Š§Š°ŃŃ‚Š¾ ŠæрŠ¾ŃŠ»ŃƒŃˆŠøŠ²Š°ŠµŠ¼Ń‹Šµ</string>
- <string name="main.albums_highest">ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Ń‹Š¹ рŠµŠ¹Ń‚ŠøŠ½Š³</string>
- <string name="main.albums_starred">Š—Š°ŠŗŠ»Š°Š“ŠŗŠø</string>
- <string name="main.albums_random">Š”Š»ŃƒŃ‡Š°Š¹Š½Ń‹Šµ</string>
-
- <string name="menu.search">ŠŸŠ¾ŠøсŠŗ</string>
- <string name="menu.shuffle">ŠŸŠµŃ€ŠµŠ¼ŠµŃˆŠ°Ń‚ŃŒ</string>
- <string name="menu.refresh">ŠžŠ±Š½Š¾Š²Šøть</string>
- <string name="menu.play">Š’Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŃŃ‚Šø</string>
- <string name="menu.play_last">Š’Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŃŃ‚Šø ŠæŠ¾ŃŠ»ŠµŠ“Š½ŠøŠ¼</string>
- <string name="menu.exit">Š’Ń‹Ń…Š¾Š“</string>
- <string name="menu.settings">ŠŠ°ŃŃ‚Ń€Š¾Š¹ŠŗŠø</string>
- <string name="menu.help">ŠŸŠ¾Š¼Š¾Ń‰ŃŒ</string>
- <string name="menu.about">Šž ŠæрŠ¾Š³Ń€Š°Š¼Š¼Šµ</string>
- <string name="menu.add_playlist">Š”Š¾Š±Š°Š²Šøть Š² сŠæŠøсŠ¾Šŗ</string>
- <string name="menu.remove_playlist">Š£Š“Š°Š»Šøть ŠøŠ· сŠæŠøсŠŗŠ°</string>
- <string name="menu.deleted_playlist">Š”ŠæŠøсŠ¾Šŗ Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½Šøя %s уŠ“Š°Š»ŠµŠ½</string>
- <string name="menu.deleted_playlist_error">ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ уŠ“Š°Š»Šøть сŠæŠøсŠ¾Šŗ %s</string>
- <string name="menu.log">ŠžŃ‚ŠæрŠ°Š²Šøть Š¶ŃƒŃ€Š½Š°Š» сŠ¾Š±Ń‹Ń‚ŠøŠ¹</string>
- <string name="menu.set_timer">Š£ŃŃ‚Š°Š½Š¾Š²Šøть тŠ°Š¹Š¼ŠµŃ€</string>
- <string name="menu.delete_cache">Š£Š“Š°Š»Šøть Šŗэш</string>
-
- <string name="playlist.label">Š”ŠæŠøсŠŗŠø</string>
- <string name="playlist.update_info">Š˜Š·Š¼ŠµŠ½Šøть ŠøŠ½Ń„Š¾Ń€Š¼Š°Ń†Šøю</string>
- <string name="playlist.updated_info">Š˜Š½Ń„Š¾Ń€Š¼Š°Ń†Šøя Š“Š»Ń сŠæŠøсŠŗŠ° Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½Šøя %s Š¾Š±Š½Š¾Š²Š»ŠµŠ½Š°</string>
- <string name="playlist.updated_info_error">ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š¾Š±Š½Š¾Š²Šøть ŠøŠ½Ń„Š¾Ń€Š¼Š°Ń†Šøю Š“Š»Ń сŠæŠøсŠŗŠ° Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½Šøя %s</string>
-
- <string name="search.label">ŠŸŠ¾ŠøсŠŗ</string>
- <string name="search.title">ŠŸŠ¾ŠøсŠŗ</string>
- <string name="search.search">ŠŠ°Š¶Š¼ŠøтŠµ Š“Š»Ń ŠæŠ¾ŠøсŠŗŠ°</string>
- <string name="search.no_match">ŠŠøчŠµŠ³Š¾ Š½Šµ Š½Š°Š¹Š“ŠµŠ½Š¾, ŠæŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, ŠæŠ¾ŠæрŠ¾Š±ŃƒŠ¹Ń‚Šµ сŠ½Š¾Š²Š°</string>
- <string name="search.artists">Š˜ŃŠæŠ¾Š»Š½ŠøтŠµŠ»Šø</string>
- <string name="search.albums">ŠŠ»ŃŒŠ±Š¾Š¼Ń‹</string>
- <string name="search.songs">ŠšŠ¾Š¼ŠæŠ¾Š·ŠøцŠøŠø</string>
- <string name="search.more">ŠŸŠ¾ŠŗŠ°Š·Š°Ń‚ŃŒ ŠµŃ‰Šµ</string>
-
- <string name="progress.wait">ŠŸŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, ŠæŠ¾Š“Š¾Š¶Š“ŠøтŠµ...</string>
-
- <string name="music_library.label">ŠœŠµŠ“ŠøŠ°Ń‚ŠµŠŗŠ°</string>
- <string name="music_library.label_offline">ŠžŃ„Ń„Š»Š°Š¹Š½ Š¼ŠµŠ“ŠøŠ°</string>
-
- <string name="select_album.select">Š’Ń‹Š±Ń€Š°Ń‚ŃŒ Š²ŃŠµ</string>
- <string name="select_album.n_selected">%d ŠŗŠ¾Š¼ŠæŠ¾Š·ŠøцŠøŠ¹ Š²Ń‹Š±Ń€Š°Š½Š¾.</string>
- <string name="select_album.n_unselected">Š’Ń‹Š±Š¾Ń€ сŠ½ŃŃ‚ с %d ŠŗŠ¾Š¼ŠæŠ¾Š·ŠøцŠøŠ¹.</string>
- <string name="select_album.more">Š•Ń‰Šµ</string>
- <string name="select_album.offline">ŠžŃ„Ń„Š»Š°Š¹Š½</string>
- <string name="select_album.searching">Š’Ń‹ŠæŠ¾Š»Š½ŃŠµŃ‚ся ŠæŠ¾ŠøсŠŗ...</string>
- <string name="select_album.no_sdcard">ŠžŃˆŠøŠ±ŠŗŠ°: SD ŠŗŠ°Ń€Ń‚Š° Š½ŠµŠ“Š¾ŃŃ‚ŃƒŠæŠ½Š°</string>
- <string name="select_album.no_network">Š’Š½ŠøŠ¼Š°Š½ŠøŠµ: сŠµŃ‚ŃŒ Š½ŠµŠ“Š¾ŃŃ‚ŃƒŠæŠ½Š°.</string>
- <string name="select_album.not_licensed">Š”ŠµŃ€Š²ŠµŃ€ Š½Šµ Š»ŠøцŠµŠ½Š·ŠøрŠ¾Š²Š°Š½. %d Š“Š½ŠµŠ¹ Š“Š¾ Š¾ŠŗŠ¾Š½Ń‡Š°Š½Šøя ŠæрŠ¾Š±Š½Š¾Š³Š¾ ŠæŠµŃ€ŠøŠ¾Š“Š°.</string>
- <string name="select_album.donate_dialog_message">ŠžŃŃƒŃ‰ŠµŃŃ‚Š²ŠøтŠµ ŠæŠ¾Š¶ŠµŃ€Ń‚Š²Š¾Š²Š°Š½ŠøŠµ Š“Š»Ń Subsonic Šø ŠæŠ¾Š»ŃƒŃ‡ŠøтŠµ Š²Š¾Š·Š¼Š¾Š¶Š½Š¾ŃŃ‚ŃŒ Š½ŠµŠ¾Š³Ń€Š°Š½ŠøчŠµŠ½Š½Š¾Š³Š¾ сŠŗŠ°Ń‡ŠøŠ²Š°Š½Šøя.</string>
- <string name="select_album.donate_dialog_now">Š”ŠµŠ¹Ń‡Š°Ń</string>
- <string name="select_album.donate_dialog_later">ŠŸŠ¾Š·Š¶Šµ</string>
- <string name="select_album.donate_dialog_0_trial_days_left">ŠŸŃ€Š¾Š±Š½Ń‹Š¹ ŠæŠµŃ€ŠøŠ¾Š“ Š·Š°ŠŗŠ¾Š½Ń‡ŠøŠ»ŃŃ</string>
-
- <string name="download.empty">Š”ŠæŠøсŠ¾Šŗ Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½Šøя Šæуст</string>
- <string name="download.shuffle_loading">Š—Š°Š³Ń€ŃƒŠ¶Š°ŠµŃ‚ся сŠ»ŃƒŃ‡Š°Š¹Š½Ń‹Š¹ сŠæŠøсŠ¾Šŗ...</string>
- <string name="download.playerstate_downloading">Š—Š°Š³Ń€ŃƒŠ·ŠŗŠ° - %s</string>
- <string name="download.playerstate_buffering">Š‘ŃƒŃ„ŠµŃ€ŠøŠ·Š°Ń†Šøя</string>
- <string name="download.playerstate_playing_shuffle">Š’Š¾ŃŠæрŠ¾ŠøŠ·Š²Š¾Š“Šøтся сŠ»ŃƒŃ‡Š°Š¹Š½Š¾</string>
- <string name="download.menu_show_album">ŠŸŠ¾ŠŗŠ°Š·Š°Ń‚ŃŒ Š°Š»ŃŒŠ±Š¾Š¼</string>
- <string name="download.menu_lyrics">Š¢ŠµŠŗст</string>
- <string name="download.menu_remove">Š£Š±Ń€Š°Ń‚ŃŒ ŠøŠ· Š¾Ń‡ŠµŃ€ŠµŠ“Šø</string>
- <string name="download.menu_remove_all">ŠžŃ‡ŠøстŠøть</string>
- <string name="download.menu_screen_on">Š’ŠŗŠ»ŃŽŃ‡Šøть ŠæŠ¾Š“сŠ²ŠµŃ‚Šŗу</string>
- <string name="download.menu_shuffle">ŠŸŠµŃ€ŠµŠ¼ŠµŃˆŠ°Ń‚ŃŒ</string>
- <string name="download.menu_toggle">ŠŸŠµŃ€ŠµŠŗŠ»ŃŽŃ‡Š°Ń‚ŠµŠ»ŃŒ</string>
- <string name="download.menu_save">Š”Š¾Ń…Ń€Š°Š½Šøть сŠæŠøсŠ¾Šŗ</string>
- <string name="download.menu_shuffle_notification">Š”ŠæŠøсŠ¾Šŗ Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½Šøя Š±Ń‹Š» ŠæŠµŃ€ŠµŠ¼ŠµŃˆŠ°Š½</string>
- <string name="download.playlist_title">Š”Š¾Ń…Ń€Š°Š½ŠµŠ½ŠøŠµ сŠæŠøсŠŗŠ° Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½Šøя</string>
- <string name="download.playlist_name">Š’Š²ŠµŠ“ŠøтŠµ Š½Š°Š·Š²Š°Š½ŠøŠµ:</string>
- <string name="download.playlist_saving">Š”Š¾Ń…Ń€Š°Š½ŠµŠ½ŠøŠµ сŠæŠøсŠŗŠ° Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½Šøя \"%s\"...</string>
- <string name="download.playlist_done">Š”ŠæŠøсŠ¾Šŗ Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½Šøя сŠ¾Ń…Ń€Š°Š½ŠµŠ½</string>
- <string name="download.playlist_error">ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ сŠ¾Ń…Ń€Š°Š½Šøть сŠæŠøсŠ¾Šŗ Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½Šøя, ŠæŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, ŠæŠ¾ŠæрŠ¾Š±ŃƒŠ¹Ń‚Šµ ŠæŠ¾Š·Š¶Šµ.</string>
- <string name="download.repeat_off">ŠŸŠ¾Š²Ń‚Š¾Ń€ŠµŠ½ŠøŠµ Š¾Ń‚ŠŗŠ»ŃŽŃ‡ŠµŠ½Š¾</string>
- <string name="download.repeat_all">ŠŸŠ¾Š²Ń‚Š¾Ń€ŃŃ‚ŃŒ Š²ŃŠµ</string>
- <string name="download.repeat_single">ŠŸŠ¾Š²Ń‚Š¾Ń€ŃŃ‚ŃŒ ŠŗŠ¾Š¼ŠæŠ¾Š·ŠøцŠøю</string>
- <string name="download.jukebox_on">Š£Š“Š°Š»ŠµŠ½Š½Š¾Šµ уŠæрŠ°Š²Š»ŠµŠ½ŠøŠµ Š²ŠŗŠ»ŃŽŃ‡ŠµŠ½Š¾. ŠœŃƒŠ·Ń‹ŠŗŠ° Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²Š¾Š“Šøтся Š½Š° ŠŗŠ¾Š¼ŠæьютŠµŃ€Šµ.</string>
- <string name="download.jukebox_off">Š£Š“Š°Š»ŠµŠ½Š½Š¾Šµ уŠæрŠ°Š²Š»ŠµŠ½ŠøŠµ Š¾Ń‚ŠŗŠ»ŃŽŃ‡ŠµŠ½Š¾. ŠœŃƒŠ·Ń‹ŠŗŠ° Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²Š¾Š“Šøтся Š½Š° устрŠ¾Š¹ŃŃ‚Š²Šµ.</string>
- <string name="download.jukebox_volume">Š£Š“Š°Š»ŠµŠ½Š½Š¾Šµ уŠæрŠ°Š²Š»ŠµŠ½ŠøŠµ Š³Ń€Š¾Š¼ŠŗŠ¾ŃŃ‚ŃŒŃŽ</string>
- <string name="download.jukebox_server_too_old">Š£Š“Š°Š»ŠµŠ½Š½Š¾Šµ уŠæрŠ°Š²Š»ŠµŠ½ŠøŠµ Š½Šµ ŠæŠ¾Š“Š“ŠµŃ€Š¶ŠøŠ²Š°ŠµŃ‚ся. ŠŸŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, Š¾Š±Š½Š¾Š²ŠøтŠµ Š’Š°Ńˆ сŠµŃ€Š²ŠµŃ€ Subsonic.</string>
- <string name="download.jukebox_offline">Š£Š“Š°Š»ŠµŠ½Š½Š¾Šµ уŠæрŠ°Š²Š»ŠµŠ½ŠøŠµ Š½Šµ ŠæŠ¾Š“Š“ŠµŃ€Š¶ŠøŠ²Š°ŠµŃ‚ся Š² Š¾Ń„Ń„Š»Š°Š¹Š½ рŠµŠ¶ŠøŠ¼Šµ.</string>
- <string name="download.jukebox_not_authorized">Š£Š“Š°Š»ŠµŠ½Š½Š¾Šµ уŠæрŠ°Š²Š»ŠµŠ½ŠøŠµ Š·Š°ŠæрŠµŃ‰ŠµŠ½Š¾. ŠŸŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, Š°ŠŗтŠøŠ²ŠøруŠ¹Ń‚Šµ рŠµŠ¶ŠøŠ¼ jukebox Š² рŠ°Š·Š“ŠµŠ»Šµ <b>ŠŠ°ŃŃ‚Ń€Š¾Š¹ŠŗŠø &gt; ŠŸŃ€Š¾ŠøŠ³Ń€Ń‹Š²Š°Ń‚ŠµŠ»Šø</b> Š½Š° Š²Š°ŃˆŠµŠ¼ сŠµŃ€Š²ŠµŃ€Šµ Subsonic.</string>
- <string name="download.timer_length">Š”Š»ŠøтŠµŠ»ŃŒŠ½Š¾ŃŃ‚ŃŒ</string>
- <string name="download.start_timer">Š—Š°ŠæустŠøть тŠ°Š¹Š¼ŠµŃ€</string>
- <string name="download.stop_timer">ŠžŃŃ‚Š°Š½Š¾Š²Šøть тŠ°Š¹Š¼ŠµŃ€</string>
- <string name="download.need_download">ŠŠµŠ¾Š±Ń…Š¾Š“ŠøŠ¼Š¾ сŠ½Š°Ń‡Š°Š»Š° сŠŗŠ°Ń‡Š°Ń‚ŃŒ Š²ŠøŠ“ŠµŠ¾</string>
- <string name="download.no_streaming_player">ŠŠµŃ‚ ŠæŠ»ŠµŠµŃ€Š° Š“Š»Ń Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½Šøя ŠæŠ¾Ń‚Š¾ŠŗŠ°</string>
-
- <string name="starring_content_starred">\"%s\" Š“Š¾Š±Š°Š²Š»ŠµŠ½Š¾ Š² Š·Š°ŠŗŠ»Š°Š“ŠŗŠø</string>
- <string name="starring_content_unstarred">\"%s\" уŠ“Š°Š»ŠµŠ½Š¾ ŠøŠ· Š·Š°ŠŗŠ»Š°Š“Š¾Šŗ</string>
- <string name="starring_content_error">ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š¾Š±Š½Š¾Š²Šøть \"%s\", ŠæŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, ŠæŠ¾ŠæрŠ¾Š±ŃƒŠ¹Ń‚Šµ ŠæŠ¾Š·Š¶Šµ.</string>
-
- <string name="playlist_error">ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæрŠ¾Ń‡ŠøтŠ°Ń‚ŃŒ сŠæŠøсŠŗŠø Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½Šøя</string>
- <string name="updated_playlist">Š”Š¾Š±Š°Š²Š»ŠµŠ½Š¾ %1$s ŠŗŠ¾Š¼ŠæŠ¾Š·ŠøцŠøŠ¹ Š² \"%2$s\"</string>
- <string name="updated_playlist_error">ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š¾Š±Š½Š¾Š²Šøть \"%s\", ŠæŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, ŠæŠ¾ŠæрŠ¾Š±ŃƒŠ¹Ń‚Šµ ŠæŠ¾Š·Š¶Šµ.</string>
- <string name="removed_playlist">Š£Š“Š°Š»ŠµŠ½Š¾ %1$s ŠøŠ· \"%2$s\" ŠŗŠ¾Š¼ŠæŠ¾Š·ŠøцŠøŠ¹</string>
-
- <string name="lyrics.nomatch">Š¢ŠµŠŗст Š½Šµ Š½Š°Š¹Š“ŠµŠ½</string>
-
- <string name="error.label">ŠžŃˆŠøŠ±ŠŗŠ°</string>
-
- <string name="settings.title">ŠŠ°ŃŃ‚Ń€Š¾Š¹ŠŗŠø DSub</string>
- <string name="settings.test_connection_title">ŠŸŃ€Š¾Š²ŠµŃ€Šøть сŠ¾ŠµŠ“ŠøŠ½ŠµŠ½ŠøŠµ</string>
- <string name="settings.servers_title">Š”ŠµŃ€Š²ŠµŃ€Ń‹</string>
- <string name="settings.server_name">ŠŠ°Š·Š²Š°Š½ŠøŠµ</string>
- <string name="settings.server_address">ŠŠ“рŠµŃ сŠµŃ€Š²ŠµŃ€Š°</string>
- <string name="settings.server_username">Š˜Š¼Ń ŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŠµŠ»Ń</string>
- <string name="settings.server_password">ŠŸŠ°Ń€Š¾Š»ŃŒ</string>
- <string name="settings.cache_title">ŠšŃŃˆ Š¼ŃƒŠ·Ń‹ŠŗŠø</string>
- <string name="settings.cache_size">Š Š°Š·Š¼ŠµŃ€ ŠŗэшŠ° (ŠœŠ±)</string>
- <string name="settings.cache_location">ŠŸŃƒŃ‚ŃŒ ŠŗэшŠ°</string>
- <string name="settings.cache_location_error">ŠŠµŠŗŠ¾Ń€Ń€ŠµŠŗтŠ½Ń‹Š¹ Šæуть. Š˜ŃŠæŠ¾Š»ŃŒŠ·ŃƒŠµŠ¼ Šæуть ŠæŠ¾ уŠ¼Š¾Š»Ń‡Š°Š½Šøю.</string>
- <string name="settings.testing_connection">ŠŸŃ€Š¾Š²ŠµŃ€ŠŗŠ° сŠ¾ŠµŠ“ŠøŠ½ŠµŠ½Šøя...</string>
- <string name="settings.testing_ok">ŠŸŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠµŠ½ŠøŠµ ŠæрŠ¾ŃˆŠ»Š¾ усŠæŠµŃˆŠ½Š¾!</string>
- <string name="settings.testing_unlicensed">ŠŸŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠµŠ½ŠøŠµ ŠæрŠ¾ŃˆŠ»Š¾ усŠæŠµŃˆŠ½Š¾. Š”ŠµŃ€Š²ŠµŃ€ Š½ŠµŠ»ŠøцŠµŠ½Š·ŠøрŠ¾Š²Š°Š½.</string>
- <string name="settings.connection_failure">ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæŠ¾Š“ŠŗŠ»ŃŽŃ‡Šøться.</string>
- <string name="settings.invalid_url">ŠŸŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, уŠŗŠ°Š¶ŠøтŠµ ŠæрŠ°Š²ŠøŠ»ŃŒŠ½Ń‹Š¹ Š°Š“рŠµŃ</string>
- <string name="settings.invalid_username">ŠŸŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, уŠŗŠ°Š¶ŠøтŠµ ŠæрŠ°Š²ŠøŠ»ŃŒŠ½Š¾Šµ ŠøŠ¼Ń ŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŠµŠ»Ń (Š½Šµ Š“Š¾Š»Š¶Š½Š¾ Š±Ń‹Ń‚ŃŒ ŠæрŠ¾Š±ŠµŠ»Š¾Š² Š² ŠŗŠ¾Š½Ń†Šµ)</string>
- <string name="settings.appearance_title">Š’Š½ŠµŃˆŠ½ŠøŠ¹ Š²ŠøŠ“</string>
- <string name="settings.theme_title">Š¢ŠµŠ¼Š°</string>
- <string name="settings.theme_light">Š”Š²ŠµŃ‚Š»Š°Ń</string>
- <string name="settings.theme_dark">Š¢ŠµŠ¼Š½Š°Ń</string>
- <string name="settings.theme_holo">Holo</string>
- <string name="settings.network_title">Š”ŠµŃ‚ŃŒ</string>
- <string name="settings.max_bitrate_wifi">ŠœŠ°Šŗс. Š±ŠøтрŠµŠ¹Ń‚ Š°ŃƒŠ“ŠøŠ¾ ŠæŠ¾ Wi-Fi</string>
- <string name="settings.max_bitrate_mobile">ŠœŠ°Šŗс. Š±ŠøтрŠµŠ¹Ń‚ Š²ŠøŠ“ŠµŠ¾ ŠæŠ¾ сŠµŃ‚Šø</string>
- <string name="settings.max_bitrate_32">32 Kbps</string>
- <string name="settings.max_bitrate_64">64 Kbps</string>
- <string name="settings.max_bitrate_80">80 Kbps</string>
- <string name="settings.max_bitrate_96">96 Kbps</string>
- <string name="settings.max_bitrate_112">112 Kbps</string>
- <string name="settings.max_bitrate_128">128 Kbps</string>
- <string name="settings.max_bitrate_160">160 Kbps</string>
- <string name="settings.max_bitrate_192">192 Kbps</string>
- <string name="settings.max_bitrate_256">256 Kbps</string>
- <string name="settings.max_bitrate_320">320 Kbps</string>
- <string name="settings.max_video_bitrate_wifi">ŠœŠ°Šŗс. Š±ŠøтрŠµŠ¹Ń‚ Š²ŠøŠ“ŠµŠ¾ ŠæŠ¾ Wi-Fi</string>
- <string name="settings.max_video_bitrate_mobile">ŠœŠ°Šŗс. Š±ŠøтрŠµŠ¹Ń‚ Š²ŠøŠ“ŠµŠ¾ ŠæŠ¾ сŠµŃ‚Šø</string>
- <string name="settings.max_video_bitrate_200">200 Kbps</string>
- <string name="settings.max_video_bitrate_300">300 Kbps</string>
- <string name="settings.max_video_bitrate_400">400 Kbps</string>
- <string name="settings.max_video_bitrate_500">500 Kbps</string>
- <string name="settings.max_video_bitrate_700">700 Kbps</string>
- <string name="settings.max_video_bitrate_1000">1000 Kbps</string>
- <string name="settings.max_video_bitrate_1500">1500 Kbps</string>
- <string name="settings.max_video_bitrate_2000">2000 Kbps</string>
- <string name="settings.max_video_bitrate_3000">3000 Kbps</string>
- <string name="settings.max_video_bitrate_5000">5000 Kbps</string>
- <string name="settings.max_bitrate_unlimited">ŠŠµŠ¾Š³Ń€Š°Š½ŠøчŠµŠ½</string>
- <string name="settings.wifi_required_title">ŠŸŠ¾Ń‚Š¾Šŗ ŠæŠ¾ Wi-Fi</string>
- <string name="settings.wifi_required_summary">ŠŸŠ¾Ń‚Š¾ŠŗŠ¾Šµ Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½ŠøŠµ Š±ŃƒŠ“ŠµŃ‚ рŠ°Š±Š¾Ń‚Š°Ń‚ŃŒ тŠ¾Š»ŃŒŠŗŠ¾ ŠæрŠø ŠæŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠµŠ½ŠøŠø чŠµŃ€ŠµŠ· Wi-Fi</string>
- <string name="settings.network_timeout_title">Š¢Š°Š¹Š¼Š°ŃƒŃ‚ сŠµŃ‚Šø</string>
- <string name="settings.network_timeout_10000">10 сŠµŠŗуŠ½Š“</string>
- <string name="settings.network_timeout_15000">15 сŠµŠŗуŠ½Š“</string>
- <string name="settings.network_timeout_30000">30 сŠµŠŗуŠ½Š“</string>
- <string name="settings.network_timeout_45000">45 сŠµŠŗуŠ½Š“</string>
- <string name="settings.network_timeout_60000">60 сŠµŠŗуŠ½Š“</string>
- <string name="settings.preload_0">0 ŠŗŠ¾Š¼ŠæŠ¾Š·ŠøцŠøя</string>
- <string name="settings.preload_1">1 ŠŗŠ¾Š¼ŠæŠ¾Š·ŠøцŠøя</string>
- <string name="settings.preload_2">2 ŠŗŠ¾Š¼ŠæŠ¾Š·ŠøцŠøŠø</string>
- <string name="settings.preload_3">3 ŠŗŠ¾Š¼ŠæŠ¾Š·ŠøцŠøŠø</string>
- <string name="settings.preload_5">5 ŠŗŠ¾Š¼ŠæŠ¾Š·ŠøцŠøŠ¹</string>
- <string name="settings.preload_10">10 ŠŗŠ¾Š¼ŠæŠ¾Š·ŠøцŠøŠ¹</string>
- <string name="settings.preload_unlimited">ŠŠµŠ¾Š³Ń€Š°Š½ŠøчŠµŠ½Š¾</string>
- <string name="settings.clear_search_history">ŠžŃ‡ŠøстŠøть ŠøстŠ¾Ń€Šøю ŠæŠ¾ŠøсŠŗŠ°</string>
- <string name="settings.search_history_cleared">Š˜ŃŃ‚Š¾Ń€Šøя ŠæŠ¾ŠøсŠŗŠ° Š¾Ń‡ŠøщŠµŠ½Š°</string>
- <string name="settings.other_title">Š”Ń€ŃƒŠ³ŠøŠµ Š½Š°ŃŃ‚Ń€Š¾Š¹ŠŗŠø</string>
- <string name="settings.scrobble_title">Š”ŠŗрŠ¾Š±Š±Š»ŠøŠ½Š³ Š½Š° Last.fm</string>
- <string name="settings.scrobble_summary">ŠŠµ Š·Š°Š±ŃƒŠ“ьтŠµ устŠ°Š½Š¾Š²Šøть Š»Š¾Š³ŠøŠ½ Šø ŠæŠ°Ń€Š¾Š»ŃŒ Š¾Ń‚ Last.fm Š½Š° сŠµŃ€Š²ŠµŃ€Šµ DSub</string>
- <string name="settings.hide_media_title">ŠŸŃ€ŃŃ‚Š°Ń‚ŃŒ Š¾Ń‚ Š“руŠ³Šøх</string>
- <string name="settings.hide_media_summary">ŠŸŃ€ŃŃ‚Š°Ń‚ŃŒ Š¼ŃƒŠ·Ń‹ŠŗŠ°Š»ŃŒŠ½Ń‹Šµ фŠ°Š¹Š»Ń‹ Š¾Ń‚ Š“руŠ³Šøх ŠæрŠøŠ»Š¾Š¶ŠµŠ½ŠøŠ¹</string>
- <string name="settings.hide_media_toast">Š˜Š·Š¼ŠµŠ½ŠµŠ½Šøя Š²ŃŃ‚ŃƒŠæят Š² сŠøŠ»Ńƒ ŠæрŠø сŠ»ŠµŠ“ующŠµŠ¼ ŠæŠ¾ŠøсŠŗŠµ Š¼ŃƒŠ·Ń‹ŠŗŠø Š½Š° Š’Š°ŃˆŠµŠ¼ устрŠ¾Š¹ŃŃ‚Š²Šµ.</string>
- <string name="settings.media_button_title">ŠšŠ½Š¾ŠæŠŗŠø уŠæрŠ°Š²Š»ŠµŠ½Šøя</string>
- <string name="settings.media_button_summary">Š Š°Š·Ń€ŠµŃˆŠøть уŠæрŠ°Š²Š»ŠµŠ½ŠøŠµ ŠŗŠ½Š¾ŠæŠŗŠ°Š¼Šø Š¼ŃƒŠ»ŃŒŃ‚ŠøŠ¼ŠµŠ“ŠøŠ° Š½Š° устрŠ¾Š¹ŃŃ‚Š²Šµ Šø Š³Š°Ń€Š½ŠøтурŠµ</string>
- <string name="settings.screen_lit_title">Š”ŠµŃ€Š¶Š°Ń‚ŃŒ эŠŗрŠ°Š½ Š²ŠŗŠ»ŃŽŃ‡ŠµŠ½Š½Ń‹Š¼</string>
- <string name="settings.screen_lit_summary">ŠžŃŃ‚Š°Š²Šøть эŠŗрŠ°Š½ Š²ŠŗŠ»ŃŽŃ‡ŠµŠ½Š½Ń‹Š¼ Š“Š»Ń ŠæŠ¾Š²Ń‹ŃˆŠµŠ½Šøя сŠŗŠ¾Ń€Š¾ŃŃ‚Šø ŠæрŠø сŠŗŠ°Ń‡ŠøŠ²Š°Š½ŠøŠø.</string>
- <string name="settings.playlist_title">Š”ŠæŠøсŠŗŠø Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½Šøя</string>
- <string name="settings.playlist_random_size_title">Š Š°Š·Š¼ŠµŃ€ сŠ»ŃƒŃ‡Š°Š¹Š½Š¾Š³Š¾ сŠæŠøсŠŗŠ°</string>
- <string name="settings.sleep_timer_title">Š¢Š°Š¹Š¼ŠµŃ€ сŠ½Š°</string>
- <string name="settings.sleep_timer_duration_title">ŠŸŃ€Š¾Š“Š¾Š»Š¶ŠøтŠµŠ»ŃŒŠ½Š¾ŃŃ‚ŃŒ тŠ°Š¹Š¼ŠµŃ€Š° сŠ½Š°</string>
- <string name="settings.sleep_timer_off">Š’Ń‹ŠŗŠ»ŃŽŃ‡ŠµŠ½</string>
- <string name="settings.sleep_timer_on">Š’ŠŗŠ»ŃŽŃ‡ŠµŠ½</string>
- <string name="settings.sleep_timer_always_on">Š’сŠµŠ³Š“Š° Š²ŠŗŠ»ŃŽŃ‡ŠµŠ½</string>
- <string name="settings.temp_loss_title">Š’Ń€ŠµŠ¼ŠµŠ½Š½Š°Ń ŠæŠ¾Ń‚ŠµŃ€Ń сŠ²ŃŠ·Šø</string>
- <string name="settings.temp_loss_pause">Š’сŠµŠ³Š“Š° Š¾ŃŃ‚Š°Š½Š°Š²Š»ŠøŠ²Š°Ń‚ŃŒ</string>
- <string name="settings.temp_loss_pause_lower">ŠžŃŃ‚Š°Š½Š°Š²Š»ŠøŠ²Š°Ń‚ŃŒ, ŠæŠ¾Š½ŠøŠ¶Š°Ń‚ŃŒ Š³Ń€Š¾Š¼ŠŗŠ¾ŃŃ‚ŃŒ, ŠµŃŠ»Šø трŠµŠ±ŃƒŠµŃ‚ся</string>
- <string name="settings.temp_loss_lower">Š’сŠµŠ³Š“Š° ŠæŠ¾Š½ŠøŠ¶Š°Ń‚ŃŒ Š³Ń€Š¾Š¼ŠŗŠ¾ŃŃ‚ŃŒ</string>
- <string name="settings.temp_loss_nothing">ŠŠøчŠµŠ³Š¾ Š½Šµ Š“ŠµŠ»Š°Ń‚ŃŒ</string>
-
- <string name="shuffle.startYear">Š“Š¾Š“ Š½Š°Ń‡Š°Š»Š°:</string>
- <string name="shuffle.endYear">Š“Š¾Š“ Š¾ŠŗŠ¾Š½Ń‡Š°Š½Šøя:</string>
- <string name="shuffle.genre">Š–Š°Š½Ń€:</string>
-
- <string name="music_service.retry">ŠžŃˆŠøŠ±ŠŗŠ° ŠæŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠµŠ½Šøя. ŠŸŠ¾ŠæытŠŗŠ° %1$d ŠøŠ· %2$d.</string>
-
- <string name="background_task.wait">ŠŸŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, ŠæŠ¾Š“Š¾Š¶Š“ŠøтŠµ...</string>
- <string name="background_task.loading">Š—Š°Š³Ń€ŃƒŠ·ŠŗŠ°</string>
- <string name="background_task.no_network">Š­Ń‚Š° ŠæрŠ¾Š³Ń€Š°Š¼Š¼Š° трŠµŠ±ŃƒŠµŃ‚ Š“Š¾ŃŃ‚ŃƒŠæ Šŗ сŠµŃ‚Šø. ŠŸŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, Š²ŠŗŠ»ŃŽŃ‡ŠøтŠµ Wi-Fi ŠøŠ»Šø Š¼Š¾Š±ŠøŠ»ŃŒŠ½Ń‹Š¹ ŠøŠ½Ń‚ŠµŃ€Š½ŠµŃ‚</string>
- <string name="background_task.network_error">ŠžŃˆŠøŠ±ŠŗŠ° сŠµŃ‚Šø. ŠŸŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, ŠæрŠ¾Š²ŠµŃ€ŃŒŃ‚Šµ Š°Š“рŠµŃ сŠµŃ€Š²ŠµŃ€Š° Šø ŠæŠ¾ŠæрŠ¾Š±ŃƒŠ¹Ń‚Šµ сŠ½Š¾Š²Š°</string>
- <string name="background_task.not_found">Š ŠµŃŃƒŃ€Ń Š½Šµ Š½Š°Š¹Š“ŠµŠ½. ŠŸŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, ŠæрŠ¾Š²ŠµŃ€ŃŒŃ‚Šµ Š°Š“рŠµŃ сŠµŃ€Š²ŠµŃ€Š°</string>
- <string name="background_task.parse_error">ŠŠµŠøŠ·Š²ŠµŃŃ‚Š½Ń‹Š¹ Š¾Ń‚Š²ŠµŃ‚. ŠŸŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, ŠæрŠ¾Š²ŠµŃ€ŃŒŃ‚Šµ Š°Š“рŠµŃ сŠµŃ€Š²ŠµŃ€Š°</string>
-
- <string name="service.connecting">ŠŸŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠµŠ½ŠøŠµ Šŗ сŠµŃ€Š²ŠµŃ€Ńƒ. ŠŸŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, ŠæŠ¾Š“Š¾Š¶Š“ŠøтŠµ.</string>
-
- <string name="parser.upgrade_client">ŠŠµŃŠ¾Š²Š¼ŠµŃŃ‚ŠøŠ¼Ń‹Šµ Š²ŠµŃ€ŃŠøŠø. ŠŸŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, Š¾Š±Š½Š¾Š²ŠøтŠµ ŠæрŠøŠ»Š¾Š¶ŠµŠ½ŠøŠµ DSub Š“Š»Ń Android.</string>
- <string name="parser.upgrade_server">ŠŠµŃŠ¾Š²Š¼ŠµŃŃ‚ŠøŠ¼Ń‹Šµ Š²ŠµŃ€ŃŠøŠø. ŠŸŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, Š¾Š±Š½Š¾Š²ŠøтŠµ сŠµŃ€Š²ŠµŃ€ Subsonic.</string>
- <string name="parser.not_authenticated">ŠŠµŠæрŠ°Š²ŠøŠ»ŃŒŠ½Š¾Šµ ŠøŠ¼Ń ŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŠµŠ»Ń ŠøŠ»Šø ŠæŠ°Ń€Š¾Š»ŃŒ.</string>
- <string name="parser.not_authorized">ŠŠµ Š°Š²Ń‚Š¾Ń€ŠøŠ·ŠøрŠ¾Š²Š°Š½. ŠŸŃ€Š¾Š²ŠµŃ€ŃŒŃ‚Šµ ŠæрŠ°Š²Š° ŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŠµŠ»Ń Š½Š° сŠµŃ€Š²ŠµŃ€Šµ Subsonic.</string>
- <string name="parser.artist_count">ŠŸŠ¾Š»ŃƒŃ‡ŠµŠ½Š¾ %d ŠøсŠæŠ¾Š»Š½ŠøтŠµŠ»ŠµŠ¹.</string>
-
- <string name="select_artist.refresh">ŠžŠ±Š½Š¾Š²Šøть</string>
- <string name="select_artist.folder">Š’Ń‹Š±Ń€Š°Ń‚ŃŒ ŠæŠ°ŠæŠŗу</string>
- <string name="select_artist.all_folders">Š’сŠµ ŠæŠ°ŠæŠŗŠø</string>
-
- <string name="equalizer.label">Š­ŠŗŠ²Š°Š»Š°Š¹Š·ŠµŃ€</string>
- <string name="equalizer.enabled">Š’ŠŗŠ»ŃŽŃ‡ŠµŠ½</string>
- <string name="equalizer.preset">Š“Š¾Ń‚Š¾Š²Ń‹Šµ Š½Š°ŃŃ‚Ń€Š¾Š¹ŠŗŠø</string>
-
- <string name="widget.initial_text">ŠšŠ¾ŃŠ½ŠøтŠµŃŃŒ Š“Š»Ń Š²Ń‹Š±Š¾Ń€Š° Š¼ŃƒŠ·Ń‹ŠŗŠø</string>
- <string name="widget.sdcard_busy">SD ŠŗŠ°Ń€Ń‚Š° Š½ŠµŠ“Š¾ŃŃ‚ŃƒŠæŠ½Š°</string>
- <string name="widget.sdcard_missing">ŠŠµŃ‚ SD ŠŗŠ°Ń€Ń‚Ń‹</string>
-
- <string name="util.bytes_format.gigabyte">0.00 Š“Š‘</string>
- <string name="util.bytes_format.megabyte">0.00 ŠœŠ‘</string>
- <string name="util.bytes_format.kilobyte">0 ŠšŠ‘</string>
- <string name="util.bytes_format.byte">0 Š‘</string>
-
- <string name="button_bar.chat">Š§Š°Ń‚</string>
- <string name="main.back_confirm">ŠŠ°Š¶Š¼ŠøтŠµ "Š½Š°Š·Š°Š“" ŠµŃ‰Šµ рŠ°Š· Š“Š»Ń Š²Ń‹Ń…Š¾Š“Š°</string>
- <string name="download.playing_out_of">Š’Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½ŠøŠµ: %1$d/%2$d</string>
- <string name="settings.persistent_title">ŠŸŠ¾ŃŃ‚Š¾ŃŠ½Š½Š¾Šµ уŠ²ŠµŠ“Š¾Š¼Š»ŠµŠ½ŠøŠµ</string>
- <string name="settings.persistent_summary">ŠŸŠ¾ŠŗŠ°Š·Ń‹Š²Š°Ń‚ŃŒ уŠ²ŠµŠ“Š¾Š¼Š»ŠµŠ½ŠøŠµ Š“Š°Š¶Šµ Š²Š¾ Š²Ń€ŠµŠ¼Ń ŠæŠ°ŃƒŠ·Ń‹. ŠžŃŃ‚Š°Š½Š¾Š²ŠŗŠ° Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½Šøя уŠ±ŠµŃ€ŠµŃ‚ этŠ¾ уŠ²ŠµŠ“Š¾Š¼Š»ŠµŠ½ŠøŠµ.</string>
- <string name="settings.gapless_playback">ŠŠµŠæрŠµŃ€Ń‹Š²Š½Š¾Šµ Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½ŠøŠµ</string>
- <string name="settings.gapless_playback_summary">Galaxy S3 Š¼Š¾Š¶ŠµŃ‚ Š·Š°Š²ŠøсŠ°Ń‚ŃŒ ŠøŠ»Šø ŠøсŠæытыŠ²Š°Ń‚ŃŒ ŠæрŠ¾Ń‡ŠøŠµ труŠ“Š½Š¾ŃŃ‚Šø с Š¼Š¾Š¼ŠµŠ½Ń‚Š° Š½Š°Ń‡Š°Š»Š° Š½ŠµŠæрŠµŃ€Ń‹Š²Š½Š¾Š³Š¾ Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½Šøя. Š’Ń‹ŠŗŠ»ŃŽŃ‡ŠøтŠµ эту фуŠ½ŠŗцŠøю Š“Š»Ń ŠøсŠæрŠ°Š²Š»ŠµŠ½Šøя Š“Š°Š½Š½Š¾Š¹ ŠæрŠ¾Š±Š»ŠµŠ¼Ń‹.</string>
- <string name="settings.chat_refresh">Š§Š°ŃŃ‚Š¾Ń‚Š° Š¾Š±Š½Š¾Š²Š»ŠµŠ½Šøя чŠ°Ń‚Š° (сŠµŠŗ)</string>
- <string name="settings.chat_enabled">Š§Š°Ń‚ Š°ŠŗтŠøŠ²ŠµŠ½</string>
- <string name="settings.chat_enabled_summary">ŠŸŠ¾ŠŗŠ°Š·Ń‹Š²Š°Ń‚ŃŒ ŠøŠ»Šø Š½ŠµŃ‚ Š²ŠŗŠ»Š°Š“Šŗу чŠ°Ń‚Š°</string>
- <string name="changelog_full_title">Š–ŃƒŃ€Š½Š°Š» ŠøŠ·Š¼ŠµŠ½ŠµŠ½ŠøŠ¹</string>
- <string name="changelog_title">Š§Ń‚Š¾ Š½Š¾Š²Š¾Š³Š¾</string>
- <string name="changelog_ok_button">OK</string>
- <string name="changelog_show_full">Š•Ń‰Šµā€¦</string>
- <string name="chat.send_a_message">ŠžŃ‚ŠæрŠ°Š²Šøть сŠ¾Š¾Š±Ń‰ŠµŠ½ŠøŠµ</string>
-
-
- <plurals name="select_album_n_songs">
- <item quantity="zero">ŠŠµŃ‚ ŠŗŠ¾Š¼ŠæŠ¾Š·ŠøцŠøŠ¹</item>
- <item quantity="one">1 ŠŗŠ¾Š¼ŠæŠ¾Š·ŠøцŠøя</item>
- <item quantity="other">%d ŠŗŠ¾Š¼ŠæŠ¾Š·ŠøцŠøŠ¹</item>
- </plurals>
- <plurals name="select_album_n_songs_downloading">
- <item quantity="one">1 ŠŗŠ¾Š¼ŠæŠ¾Š·ŠøцŠøя Š·Š°ŠæŠ»Š°Š½ŠøрŠ¾Š²Š°Š½Š° Š“Š»Ń сŠŗŠ°Ń‡ŠøŠ²Š°Š½Šøя</item>
- <item quantity="other">%d ŠŗŠ¾Š¼ŠæŠ¾Š·ŠøцŠøŠ¹ Š·Š°ŠæŠ»Š°Š½ŠøрŠ¾Š²Š°Š½Š¾ Š“Š»Ń сŠŗŠ°Ń‡ŠøŠ²Š°Š½Šøя</item>
- </plurals>
- <plurals name="select_album_n_songs_added">
- <item quantity="one">1 ŠŗŠ¾Š¼ŠæŠ¾Š·ŠøцŠøя Š“Š¾Š±Š°Š²Š»ŠµŠ½Š° Š² Š¾Ń‡ŠµŃ€ŠµŠ“ь Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½Šøя</item>
- <item quantity="other">%d ŠŗŠ¾Š¼ŠæŠ¾Š·ŠøцŠøŠ¹ Š“Š¾Š±Š°Š²Š»ŠµŠ½Š¾ Š² Š¾Ń‡ŠµŃ€ŠµŠ“ь Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½Šøя</item>
- </plurals>
- <plurals name="select_album_donate_dialog_n_trial_days_left">
- <item quantity="one">1 Š“ŠµŠ½ŃŒ Š“Š¾ ŠŗŠ¾Š½Ń†Š° ŠæрŠ¾Š±Š½Š¾Š³Š¾ ŠæŠµŃ€ŠøŠ¾Š“Š°</item>
- <item quantity="other">%d Š“Š½ŠµŠ¹ Š“Š¾ ŠŗŠ¾Š½Ń†Š° ŠæрŠ¾Š±Š½Š¾Š³Š¾ ŠæŠµŃ€ŠøŠ¾Š“Š°</item>
- </plurals>
-
-</resources>
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <string name="common.appname">DSub</string>
+ <string name="common.ok">OK</string>
+ <string name="common.save">Š”Š¾Ń…Ń€Š°Š½Šøть</string>
+ <string name="common.cancel">ŠžŃ‚Š¼ŠµŠ½Š°</string>
+ <string name="common.play_now">Š’Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŃŃ‚Šø сŠµŠ¹Ń‡Š°Ń</string>
+ <string name="common.play_shuffled">Š”Š»ŃƒŃ‡Š°Š¹Š½Š¾Šµ Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½ŠøŠµ</string>
+ <string name="common.play_next">Š’Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŃŃ‚Šø сŠ»ŠµŠ“ующŠøŠ¼</string>
+ <string name="common.play_last">Š’Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŃŃ‚Šø ŠæŠ¾ŃŠ»ŠµŠ“Š½ŠøŠ¼</string>
+ <string name="common.download">Š”ŠŗŠ°Ń‡Š°Ń‚ŃŒ</string>
+ <string name="common.pin">ŠšŠµŃˆŠøрŠ¾Š²Š°Ń‚ŃŒ</string>
+ <string name="common.delete">Š£Š“Š°Š»Šøть</string>
+ <string name="common.star">Š”Š¾Š±Š°Š²Šøть Š² Š·Š°ŠŗŠ»Š°Š“ŠŗŠø</string>
+ <string name="common.unstar">Š£Š“Š°Š»Šøть ŠøŠ· Š·Š°ŠŗŠ»Š°Š“Š¾Šŗ</string>
+ <string name="common.info">Š˜Š½Ń„Š¾Ń€Š¼Š°Ń†Šøя</string>
+ <string name="common.name">ŠŠ°Š·Š²Š°Š½ŠøŠµ</string>
+ <string name="common.comment">ŠšŠ¾Š¼Š¼ŠµŠ½Ń‚Š°Ń€ŠøŠ¹</string>
+ <string name="common.public">ŠžŠ±Ń‰ŠµŠ“Š¾ŃŃ‚ŃƒŠæŠ½Ń‹Š¹</string>
+ <string name="common.play_external">Š’Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŃŃ‚Šø Š²Š¾ Š²Š½ŠµŃˆŠ½ŠµŠ¼ ŠæŠ»ŠµŠµŃ€Šµ</string>
+ <string name="common.stream_external">Š’Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŃŃ‚Šø ŠæŠ¾Ń‚Š¾Šŗ Š²Š¾ Š²Š½ŠµŃˆŠ½ŠµŠ¼ ŠæŠ»ŠµŠµŃ€Šµ</string>
+ <string name="common.confirm">ŠŸŠ¾Š“тŠ²ŠµŃ€Š¶Š“ŠµŠ½ŠøŠµ</string>
+
+ <string name="button_bar.home">Š”Š¾Š¼Š¾Š¹</string>
+ <string name="button_bar.browse">ŠœŠµŠ“ŠøŠ°Ń‚ŠµŠŗŠ°</string>
+ <string name="button_bar.search">ŠŸŠ¾ŠøсŠŗ</string>
+ <string name="button_bar.playlists">Š”ŠæŠøсŠŗŠø</string>
+ <string name="button_bar.now_playing">ŠŸŠ»ŠµŠµŃ€</string>
+
+ <string name="main.welcome_title">Š—Š“рŠ°Š²ŃŃ‚Š²ŃƒŠ¹Ń‚Šµ!</string>
+ <string name="main.welcome_text">Š”Š¾Š±Ń€Š¾ ŠæŠ¾Š¶Š°Š»Š¾Š²Š°Ń‚ŃŒ Š² DSub! Š­Ń‚Š¾ ŠæрŠøŠ»Š¾Š¶ŠµŠ½ŠøŠµ Š½Š°ŃŃ‚Ń€Š¾ŠµŠ½Š¾ Š½Š° рŠ°Š±Š¾Ń‚Ńƒ с Š“ŠµŠ¼Š¾ сŠµŃ€Š²ŠµŃ€Š¾Š¼ Subsonic. ŠŸŠ¾ŃŠ»Šµ Š½Š°ŃŃ‚Ń€Š¾Š¹ŠŗŠø Š’Š°ŃˆŠµŠ³Š¾ ŠæŠµŃ€ŃŠ¾Š½Š°Š»ŃŒŠ½Š¾Š³Š¾ сŠµŃ€Š²ŠµŃ€Š° (Š“Š¾ŃŃ‚ŃƒŠæŠµŠ½ Š½Š° <b>subsonic.org</b>), ŠæŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, ŠæŠµŃ€ŠµŠ¹Š“ŠøтŠµ Š² <b>ŠŠ°ŃŃ‚Ń€Š¾Š¹ŠŗŠø</b> Šø ŠøŠ·Š¼ŠµŠ½ŠøтŠµ ŠæŠ°Ń€Š°Š¼ŠµŃ‚ры Š“Š»Ń ŠæŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠµŠ½Šøя.</string>
+
+ <string name="main.about_title">Šž ŠæрŠ¾Š³Ń€Š°Š¼Š¼Šµ DSub</string>
+ <string name="main.about_text">ŠŠ²Ń‚Š¾Ń€: Scott Jackson
+ \nEmail: dsub.android@gmail.com
+ \nŠ’ŠµŃ€ŃŠøя: %1$s
+ \nFiles Cached: %2$s
+ \nŠ˜ŃŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Š½Š¾ Š¼ŠµŃŃ‚Š°: %3$s ŠøŠ· %4$s
+ \nŠ”Š¾ŃŃ‚ŃƒŠæŠ½Š¾ Š¼ŠµŃŃ‚Š°: %5$s ŠøŠ· %6$s</string>
+ <string name="main.select_server">Š’Ń‹Š±Ń€Š°Ń‚ŃŒ сŠµŃ€Š²ŠµŃ€</string>
+ <string name="main.shuffle">Š”Š»ŃƒŃ‡Š°Š¹Š½Š¾Šµ Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½ŠøŠµ</string>
+ <string name="main.offline">ŠžŃ‚ŠŗŠ»ŃŽŃ‡Šøться</string>
+ <string name="main.online">ŠŸŠ¾Š“ŠŗŠ»ŃŽŃ‡Šøться</string>
+ <string name="main.settings">ŠŠ°ŃŃ‚Ń€Š¾Š¹ŠŗŠø</string>
+ <string name="main.albums_title">ŠŠ»ŃŒŠ±Š¾Š¼Ń‹</string>
+ <string name="main.albums_newest">ŠŠµŠ“Š°Š²Š½Š¾ Š“Š¾Š±Š°Š²Š»ŠµŠ½Š½Ń‹Šµ</string>
+ <string name="main.albums_recent">ŠŠµŠ“Š°Š²Š½Š¾ ŠæрŠ¾ŃŠ»ŃƒŃˆŠ°Š½Š½Ń‹Šµ</string>
+ <string name="main.albums_frequent">Š§Š°ŃŃ‚Š¾ ŠæрŠ¾ŃŠ»ŃƒŃˆŠøŠ²Š°ŠµŠ¼Ń‹Šµ</string>
+ <string name="main.albums_highest">ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Ń‹Š¹ рŠµŠ¹Ń‚ŠøŠ½Š³</string>
+ <string name="main.albums_starred">Š—Š°ŠŗŠ»Š°Š“ŠŗŠø</string>
+ <string name="main.albums_random">Š”Š»ŃƒŃ‡Š°Š¹Š½Ń‹Šµ</string>
+
+ <string name="menu.search">ŠŸŠ¾ŠøсŠŗ</string>
+ <string name="menu.shuffle">ŠŸŠµŃ€ŠµŠ¼ŠµŃˆŠ°Ń‚ŃŒ</string>
+ <string name="menu.refresh">ŠžŠ±Š½Š¾Š²Šøть</string>
+ <string name="menu.play">Š’Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŃŃ‚Šø</string>
+ <string name="menu.play_last">Š’Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŃŃ‚Šø ŠæŠ¾ŃŠ»ŠµŠ“Š½ŠøŠ¼</string>
+ <string name="menu.exit">Š’Ń‹Ń…Š¾Š“</string>
+ <string name="menu.settings">ŠŠ°ŃŃ‚Ń€Š¾Š¹ŠŗŠø</string>
+ <string name="menu.help">ŠŸŠ¾Š¼Š¾Ń‰ŃŒ</string>
+ <string name="menu.about">Šž ŠæрŠ¾Š³Ń€Š°Š¼Š¼Šµ</string>
+ <string name="menu.add_playlist">Š”Š¾Š±Š°Š²Šøть Š² сŠæŠøсŠ¾Šŗ</string>
+ <string name="menu.remove_playlist">Š£Š“Š°Š»Šøть ŠøŠ· сŠæŠøсŠŗŠ°</string>
+ <string name="menu.deleted_playlist">Š”ŠæŠøсŠ¾Šŗ Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½Šøя %s уŠ“Š°Š»ŠµŠ½</string>
+ <string name="menu.deleted_playlist_error">ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ уŠ“Š°Š»Šøть сŠæŠøсŠ¾Šŗ %s</string>
+ <string name="menu.log">ŠžŃ‚ŠæрŠ°Š²Šøть Š¶ŃƒŃ€Š½Š°Š» сŠ¾Š±Ń‹Ń‚ŠøŠ¹</string>
+ <string name="menu.set_timer">Š£ŃŃ‚Š°Š½Š¾Š²Šøть тŠ°Š¹Š¼ŠµŃ€</string>
+ <string name="menu.delete_cache">Š£Š“Š°Š»Šøть Šŗэш</string>
+
+ <string name="playlist.label">Š”ŠæŠøсŠŗŠø</string>
+ <string name="playlist.update_info">Š˜Š·Š¼ŠµŠ½Šøть ŠøŠ½Ń„Š¾Ń€Š¼Š°Ń†Šøю</string>
+ <string name="playlist.updated_info">Š˜Š½Ń„Š¾Ń€Š¼Š°Ń†Šøя Š“Š»Ń сŠæŠøсŠŗŠ° Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½Šøя %s Š¾Š±Š½Š¾Š²Š»ŠµŠ½Š°</string>
+ <string name="playlist.updated_info_error">ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š¾Š±Š½Š¾Š²Šøть ŠøŠ½Ń„Š¾Ń€Š¼Š°Ń†Šøю Š“Š»Ń сŠæŠøсŠŗŠ° Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½Šøя %s</string>
+
+ <string name="search.label">ŠŸŠ¾ŠøсŠŗ</string>
+ <string name="search.title">ŠŸŠ¾ŠøсŠŗ</string>
+ <string name="search.search">ŠŠ°Š¶Š¼ŠøтŠµ Š“Š»Ń ŠæŠ¾ŠøсŠŗŠ°</string>
+ <string name="search.no_match">ŠŠøчŠµŠ³Š¾ Š½Šµ Š½Š°Š¹Š“ŠµŠ½Š¾, ŠæŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, ŠæŠ¾ŠæрŠ¾Š±ŃƒŠ¹Ń‚Šµ сŠ½Š¾Š²Š°</string>
+ <string name="search.artists">Š˜ŃŠæŠ¾Š»Š½ŠøтŠµŠ»Šø</string>
+ <string name="search.albums">ŠŠ»ŃŒŠ±Š¾Š¼Ń‹</string>
+ <string name="search.songs">ŠšŠ¾Š¼ŠæŠ¾Š·ŠøцŠøŠø</string>
+ <string name="search.more">ŠŸŠ¾ŠŗŠ°Š·Š°Ń‚ŃŒ ŠµŃ‰Šµ</string>
+
+ <string name="progress.wait">ŠŸŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, ŠæŠ¾Š“Š¾Š¶Š“ŠøтŠµ...</string>
+
+ <string name="music_library.label">ŠœŠµŠ“ŠøŠ°Ń‚ŠµŠŗŠ°</string>
+ <string name="music_library.label_offline">ŠžŃ„Ń„Š»Š°Š¹Š½ Š¼ŠµŠ“ŠøŠ°</string>
+
+ <string name="select_album.select">Š’Ń‹Š±Ń€Š°Ń‚ŃŒ Š²ŃŠµ</string>
+ <string name="select_album.n_selected">%d ŠŗŠ¾Š¼ŠæŠ¾Š·ŠøцŠøŠ¹ Š²Ń‹Š±Ń€Š°Š½Š¾.</string>
+ <string name="select_album.n_unselected">Š’Ń‹Š±Š¾Ń€ сŠ½ŃŃ‚ с %d ŠŗŠ¾Š¼ŠæŠ¾Š·ŠøцŠøŠ¹.</string>
+ <string name="select_album.more">Š•Ń‰Šµ</string>
+ <string name="select_album.offline">ŠžŃ„Ń„Š»Š°Š¹Š½</string>
+ <string name="select_album.searching">Š’Ń‹ŠæŠ¾Š»Š½ŃŠµŃ‚ся ŠæŠ¾ŠøсŠŗ...</string>
+ <string name="select_album.no_sdcard">ŠžŃˆŠøŠ±ŠŗŠ°: SD ŠŗŠ°Ń€Ń‚Š° Š½ŠµŠ“Š¾ŃŃ‚ŃƒŠæŠ½Š°</string>
+ <string name="select_album.no_network">Š’Š½ŠøŠ¼Š°Š½ŠøŠµ: сŠµŃ‚ŃŒ Š½ŠµŠ“Š¾ŃŃ‚ŃƒŠæŠ½Š°.</string>
+ <string name="select_album.not_licensed">Š”ŠµŃ€Š²ŠµŃ€ Š½Šµ Š»ŠøцŠµŠ½Š·ŠøрŠ¾Š²Š°Š½. %d Š“Š½ŠµŠ¹ Š“Š¾ Š¾ŠŗŠ¾Š½Ń‡Š°Š½Šøя ŠæрŠ¾Š±Š½Š¾Š³Š¾ ŠæŠµŃ€ŠøŠ¾Š“Š°.</string>
+ <string name="select_album.donate_dialog_message">ŠžŃŃƒŃ‰ŠµŃŃ‚Š²ŠøтŠµ ŠæŠ¾Š¶ŠµŃ€Ń‚Š²Š¾Š²Š°Š½ŠøŠµ Š“Š»Ń Subsonic Šø ŠæŠ¾Š»ŃƒŃ‡ŠøтŠµ Š²Š¾Š·Š¼Š¾Š¶Š½Š¾ŃŃ‚ŃŒ Š½ŠµŠ¾Š³Ń€Š°Š½ŠøчŠµŠ½Š½Š¾Š³Š¾ сŠŗŠ°Ń‡ŠøŠ²Š°Š½Šøя.</string>
+ <string name="select_album.donate_dialog_now">Š”ŠµŠ¹Ń‡Š°Ń</string>
+ <string name="select_album.donate_dialog_later">ŠŸŠ¾Š·Š¶Šµ</string>
+ <string name="select_album.donate_dialog_0_trial_days_left">ŠŸŃ€Š¾Š±Š½Ń‹Š¹ ŠæŠµŃ€ŠøŠ¾Š“ Š·Š°ŠŗŠ¾Š½Ń‡ŠøŠ»ŃŃ</string>
+
+ <string name="download.empty">Š”ŠæŠøсŠ¾Šŗ Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½Šøя Šæуст</string>
+ <string name="download.shuffle_loading">Š—Š°Š³Ń€ŃƒŠ¶Š°ŠµŃ‚ся сŠ»ŃƒŃ‡Š°Š¹Š½Ń‹Š¹ сŠæŠøсŠ¾Šŗ...</string>
+ <string name="download.playerstate_downloading">Š—Š°Š³Ń€ŃƒŠ·ŠŗŠ° - %s</string>
+ <string name="download.playerstate_buffering">Š‘ŃƒŃ„ŠµŃ€ŠøŠ·Š°Ń†Šøя</string>
+ <string name="download.playerstate_playing_shuffle">Š’Š¾ŃŠæрŠ¾ŠøŠ·Š²Š¾Š“Šøтся сŠ»ŃƒŃ‡Š°Š¹Š½Š¾</string>
+ <string name="download.menu_show_album">ŠŸŠ¾ŠŗŠ°Š·Š°Ń‚ŃŒ Š°Š»ŃŒŠ±Š¾Š¼</string>
+ <string name="download.menu_lyrics">Š¢ŠµŠŗст</string>
+ <string name="download.menu_remove">Š£Š±Ń€Š°Ń‚ŃŒ ŠøŠ· Š¾Ń‡ŠµŃ€ŠµŠ“Šø</string>
+ <string name="download.menu_remove_all">ŠžŃ‡ŠøстŠøть</string>
+ <string name="download.menu_screen_on">Š’ŠŗŠ»ŃŽŃ‡Šøть ŠæŠ¾Š“сŠ²ŠµŃ‚Šŗу</string>
+ <string name="download.menu_shuffle">ŠŸŠµŃ€ŠµŠ¼ŠµŃˆŠ°Ń‚ŃŒ</string>
+ <string name="download.menu_toggle">ŠŸŠµŃ€ŠµŠŗŠ»ŃŽŃ‡Š°Ń‚ŠµŠ»ŃŒ</string>
+ <string name="download.menu_save">Š”Š¾Ń…Ń€Š°Š½Šøть сŠæŠøсŠ¾Šŗ</string>
+ <string name="download.menu_shuffle_notification">Š”ŠæŠøсŠ¾Šŗ Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½Šøя Š±Ń‹Š» ŠæŠµŃ€ŠµŠ¼ŠµŃˆŠ°Š½</string>
+ <string name="download.playlist_title">Š”Š¾Ń…Ń€Š°Š½ŠµŠ½ŠøŠµ сŠæŠøсŠŗŠ° Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½Šøя</string>
+ <string name="download.playlist_name">Š’Š²ŠµŠ“ŠøтŠµ Š½Š°Š·Š²Š°Š½ŠøŠµ:</string>
+ <string name="download.playlist_saving">Š”Š¾Ń…Ń€Š°Š½ŠµŠ½ŠøŠµ сŠæŠøсŠŗŠ° Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½Šøя \"%s\"...</string>
+ <string name="download.playlist_done">Š”ŠæŠøсŠ¾Šŗ Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½Šøя сŠ¾Ń…Ń€Š°Š½ŠµŠ½</string>
+ <string name="download.playlist_error">ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ сŠ¾Ń…Ń€Š°Š½Šøть сŠæŠøсŠ¾Šŗ Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½Šøя, ŠæŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, ŠæŠ¾ŠæрŠ¾Š±ŃƒŠ¹Ń‚Šµ ŠæŠ¾Š·Š¶Šµ.</string>
+ <string name="download.repeat_off">ŠŸŠ¾Š²Ń‚Š¾Ń€ŠµŠ½ŠøŠµ Š¾Ń‚ŠŗŠ»ŃŽŃ‡ŠµŠ½Š¾</string>
+ <string name="download.repeat_all">ŠŸŠ¾Š²Ń‚Š¾Ń€ŃŃ‚ŃŒ Š²ŃŠµ</string>
+ <string name="download.repeat_single">ŠŸŠ¾Š²Ń‚Š¾Ń€ŃŃ‚ŃŒ ŠŗŠ¾Š¼ŠæŠ¾Š·ŠøцŠøю</string>
+ <string name="download.jukebox_on">Š£Š“Š°Š»ŠµŠ½Š½Š¾Šµ уŠæрŠ°Š²Š»ŠµŠ½ŠøŠµ Š²ŠŗŠ»ŃŽŃ‡ŠµŠ½Š¾. ŠœŃƒŠ·Ń‹ŠŗŠ° Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²Š¾Š“Šøтся Š½Š° ŠŗŠ¾Š¼ŠæьютŠµŃ€Šµ.</string>
+ <string name="download.jukebox_off">Š£Š“Š°Š»ŠµŠ½Š½Š¾Šµ уŠæрŠ°Š²Š»ŠµŠ½ŠøŠµ Š¾Ń‚ŠŗŠ»ŃŽŃ‡ŠµŠ½Š¾. ŠœŃƒŠ·Ń‹ŠŗŠ° Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²Š¾Š“Šøтся Š½Š° устрŠ¾Š¹ŃŃ‚Š²Šµ.</string>
+ <string name="download.jukebox_volume">Š£Š“Š°Š»ŠµŠ½Š½Š¾Šµ уŠæрŠ°Š²Š»ŠµŠ½ŠøŠµ Š³Ń€Š¾Š¼ŠŗŠ¾ŃŃ‚ŃŒŃŽ</string>
+ <string name="download.jukebox_server_too_old">Š£Š“Š°Š»ŠµŠ½Š½Š¾Šµ уŠæрŠ°Š²Š»ŠµŠ½ŠøŠµ Š½Šµ ŠæŠ¾Š“Š“ŠµŃ€Š¶ŠøŠ²Š°ŠµŃ‚ся. ŠŸŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, Š¾Š±Š½Š¾Š²ŠøтŠµ Š’Š°Ńˆ сŠµŃ€Š²ŠµŃ€ Subsonic.</string>
+ <string name="download.jukebox_offline">Š£Š“Š°Š»ŠµŠ½Š½Š¾Šµ уŠæрŠ°Š²Š»ŠµŠ½ŠøŠµ Š½Šµ ŠæŠ¾Š“Š“ŠµŃ€Š¶ŠøŠ²Š°ŠµŃ‚ся Š² Š¾Ń„Ń„Š»Š°Š¹Š½ рŠµŠ¶ŠøŠ¼Šµ.</string>
+ <string name="download.jukebox_not_authorized">Š£Š“Š°Š»ŠµŠ½Š½Š¾Šµ уŠæрŠ°Š²Š»ŠµŠ½ŠøŠµ Š·Š°ŠæрŠµŃ‰ŠµŠ½Š¾. ŠŸŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, Š°ŠŗтŠøŠ²ŠøруŠ¹Ń‚Šµ рŠµŠ¶ŠøŠ¼ jukebox Š² рŠ°Š·Š“ŠµŠ»Šµ <b>ŠŠ°ŃŃ‚Ń€Š¾Š¹ŠŗŠø &gt; ŠŸŃ€Š¾ŠøŠ³Ń€Ń‹Š²Š°Ń‚ŠµŠ»Šø</b> Š½Š° Š²Š°ŃˆŠµŠ¼ сŠµŃ€Š²ŠµŃ€Šµ Subsonic.</string>
+ <string name="download.timer_length">Š”Š»ŠøтŠµŠ»ŃŒŠ½Š¾ŃŃ‚ŃŒ</string>
+ <string name="download.start_timer">Š—Š°ŠæустŠøть тŠ°Š¹Š¼ŠµŃ€</string>
+ <string name="download.stop_timer">ŠžŃŃ‚Š°Š½Š¾Š²Šøть тŠ°Š¹Š¼ŠµŃ€</string>
+ <string name="download.need_download">ŠŠµŠ¾Š±Ń…Š¾Š“ŠøŠ¼Š¾ сŠ½Š°Ń‡Š°Š»Š° сŠŗŠ°Ń‡Š°Ń‚ŃŒ Š²ŠøŠ“ŠµŠ¾</string>
+ <string name="download.no_streaming_player">ŠŠµŃ‚ ŠæŠ»ŠµŠµŃ€Š° Š“Š»Ń Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½Šøя ŠæŠ¾Ń‚Š¾ŠŗŠ°</string>
+
+ <string name="starring_content_starred">\"%s\" Š“Š¾Š±Š°Š²Š»ŠµŠ½Š¾ Š² Š·Š°ŠŗŠ»Š°Š“ŠŗŠø</string>
+ <string name="starring_content_unstarred">\"%s\" уŠ“Š°Š»ŠµŠ½Š¾ ŠøŠ· Š·Š°ŠŗŠ»Š°Š“Š¾Šŗ</string>
+ <string name="starring_content_error">ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š¾Š±Š½Š¾Š²Šøть \"%s\", ŠæŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, ŠæŠ¾ŠæрŠ¾Š±ŃƒŠ¹Ń‚Šµ ŠæŠ¾Š·Š¶Šµ.</string>
+
+ <string name="playlist_error">ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæрŠ¾Ń‡ŠøтŠ°Ń‚ŃŒ сŠæŠøсŠŗŠø Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½Šøя</string>
+ <string name="updated_playlist">Š”Š¾Š±Š°Š²Š»ŠµŠ½Š¾ %1$s ŠŗŠ¾Š¼ŠæŠ¾Š·ŠøцŠøŠ¹ Š² \"%2$s\"</string>
+ <string name="updated_playlist_error">ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š¾Š±Š½Š¾Š²Šøть \"%s\", ŠæŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, ŠæŠ¾ŠæрŠ¾Š±ŃƒŠ¹Ń‚Šµ ŠæŠ¾Š·Š¶Šµ.</string>
+ <string name="removed_playlist">Š£Š“Š°Š»ŠµŠ½Š¾ %1$s ŠøŠ· \"%2$s\" ŠŗŠ¾Š¼ŠæŠ¾Š·ŠøцŠøŠ¹</string>
+
+ <string name="lyrics.nomatch">Š¢ŠµŠŗст Š½Šµ Š½Š°Š¹Š“ŠµŠ½</string>
+
+ <string name="error.label">ŠžŃˆŠøŠ±ŠŗŠ°</string>
+
+ <string name="settings.title">ŠŠ°ŃŃ‚Ń€Š¾Š¹ŠŗŠø DSub</string>
+ <string name="settings.test_connection_title">ŠŸŃ€Š¾Š²ŠµŃ€Šøть сŠ¾ŠµŠ“ŠøŠ½ŠµŠ½ŠøŠµ</string>
+ <string name="settings.servers_title">Š”ŠµŃ€Š²ŠµŃ€Ń‹</string>
+ <string name="settings.server_name">ŠŠ°Š·Š²Š°Š½ŠøŠµ</string>
+ <string name="settings.server_address">ŠŠ“рŠµŃ сŠµŃ€Š²ŠµŃ€Š°</string>
+ <string name="settings.server_username">Š˜Š¼Ń ŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŠµŠ»Ń</string>
+ <string name="settings.server_password">ŠŸŠ°Ń€Š¾Š»ŃŒ</string>
+ <string name="settings.cache_title">ŠšŃŃˆ Š¼ŃƒŠ·Ń‹ŠŗŠø</string>
+ <string name="settings.cache_size">Š Š°Š·Š¼ŠµŃ€ ŠŗэшŠ° (ŠœŠ±)</string>
+ <string name="settings.cache_location">ŠŸŃƒŃ‚ŃŒ ŠŗэшŠ°</string>
+ <string name="settings.cache_location_error">ŠŠµŠŗŠ¾Ń€Ń€ŠµŠŗтŠ½Ń‹Š¹ Šæуть. Š˜ŃŠæŠ¾Š»ŃŒŠ·ŃƒŠµŠ¼ Šæуть ŠæŠ¾ уŠ¼Š¾Š»Ń‡Š°Š½Šøю.</string>
+ <string name="settings.testing_connection">ŠŸŃ€Š¾Š²ŠµŃ€ŠŗŠ° сŠ¾ŠµŠ“ŠøŠ½ŠµŠ½Šøя...</string>
+ <string name="settings.testing_ok">ŠŸŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠµŠ½ŠøŠµ ŠæрŠ¾ŃˆŠ»Š¾ усŠæŠµŃˆŠ½Š¾!</string>
+ <string name="settings.testing_unlicensed">ŠŸŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠµŠ½ŠøŠµ ŠæрŠ¾ŃˆŠ»Š¾ усŠæŠµŃˆŠ½Š¾. Š”ŠµŃ€Š²ŠµŃ€ Š½ŠµŠ»ŠøцŠµŠ½Š·ŠøрŠ¾Š²Š°Š½.</string>
+ <string name="settings.connection_failure">ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæŠ¾Š“ŠŗŠ»ŃŽŃ‡Šøться.</string>
+ <string name="settings.invalid_url">ŠŸŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, уŠŗŠ°Š¶ŠøтŠµ ŠæрŠ°Š²ŠøŠ»ŃŒŠ½Ń‹Š¹ Š°Š“рŠµŃ</string>
+ <string name="settings.invalid_username">ŠŸŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, уŠŗŠ°Š¶ŠøтŠµ ŠæрŠ°Š²ŠøŠ»ŃŒŠ½Š¾Šµ ŠøŠ¼Ń ŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŠµŠ»Ń (Š½Šµ Š“Š¾Š»Š¶Š½Š¾ Š±Ń‹Ń‚ŃŒ ŠæрŠ¾Š±ŠµŠ»Š¾Š² Š² ŠŗŠ¾Š½Ń†Šµ)</string>
+ <string name="settings.appearance_title">Š’Š½ŠµŃˆŠ½ŠøŠ¹ Š²ŠøŠ“</string>
+ <string name="settings.theme_title">Š¢ŠµŠ¼Š°</string>
+ <string name="settings.theme_light">Š”Š²ŠµŃ‚Š»Š°Ń</string>
+ <string name="settings.theme_dark">Š¢ŠµŠ¼Š½Š°Ń</string>
+ <string name="settings.theme_holo">Holo</string>
+ <string name="settings.network_title">Š”ŠµŃ‚ŃŒ</string>
+ <string name="settings.max_bitrate_wifi">ŠœŠ°Šŗс. Š±ŠøтрŠµŠ¹Ń‚ Š°ŃƒŠ“ŠøŠ¾ ŠæŠ¾ Wi-Fi</string>
+ <string name="settings.max_bitrate_mobile">ŠœŠ°Šŗс. Š±ŠøтрŠµŠ¹Ń‚ Š²ŠøŠ“ŠµŠ¾ ŠæŠ¾ сŠµŃ‚Šø</string>
+ <string name="settings.max_bitrate_32">32 Kbps</string>
+ <string name="settings.max_bitrate_64">64 Kbps</string>
+ <string name="settings.max_bitrate_80">80 Kbps</string>
+ <string name="settings.max_bitrate_96">96 Kbps</string>
+ <string name="settings.max_bitrate_112">112 Kbps</string>
+ <string name="settings.max_bitrate_128">128 Kbps</string>
+ <string name="settings.max_bitrate_160">160 Kbps</string>
+ <string name="settings.max_bitrate_192">192 Kbps</string>
+ <string name="settings.max_bitrate_256">256 Kbps</string>
+ <string name="settings.max_bitrate_320">320 Kbps</string>
+ <string name="settings.max_video_bitrate_wifi">ŠœŠ°Šŗс. Š±ŠøтрŠµŠ¹Ń‚ Š²ŠøŠ“ŠµŠ¾ ŠæŠ¾ Wi-Fi</string>
+ <string name="settings.max_video_bitrate_mobile">ŠœŠ°Šŗс. Š±ŠøтрŠµŠ¹Ń‚ Š²ŠøŠ“ŠµŠ¾ ŠæŠ¾ сŠµŃ‚Šø</string>
+ <string name="settings.max_video_bitrate_200">200 Kbps</string>
+ <string name="settings.max_video_bitrate_300">300 Kbps</string>
+ <string name="settings.max_video_bitrate_400">400 Kbps</string>
+ <string name="settings.max_video_bitrate_500">500 Kbps</string>
+ <string name="settings.max_video_bitrate_700">700 Kbps</string>
+ <string name="settings.max_video_bitrate_1000">1000 Kbps</string>
+ <string name="settings.max_video_bitrate_1500">1500 Kbps</string>
+ <string name="settings.max_video_bitrate_2000">2000 Kbps</string>
+ <string name="settings.max_video_bitrate_3000">3000 Kbps</string>
+ <string name="settings.max_video_bitrate_5000">5000 Kbps</string>
+ <string name="settings.max_bitrate_unlimited">ŠŠµŠ¾Š³Ń€Š°Š½ŠøчŠµŠ½</string>
+ <string name="settings.wifi_required_title">ŠŸŠ¾Ń‚Š¾Šŗ ŠæŠ¾ Wi-Fi</string>
+ <string name="settings.wifi_required_summary">ŠŸŠ¾Ń‚Š¾ŠŗŠ¾Šµ Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½ŠøŠµ Š±ŃƒŠ“ŠµŃ‚ рŠ°Š±Š¾Ń‚Š°Ń‚ŃŒ тŠ¾Š»ŃŒŠŗŠ¾ ŠæрŠø ŠæŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠµŠ½ŠøŠø чŠµŃ€ŠµŠ· Wi-Fi</string>
+ <string name="settings.network_timeout_title">Š¢Š°Š¹Š¼Š°ŃƒŃ‚ сŠµŃ‚Šø</string>
+ <string name="settings.network_timeout_10000">10 сŠµŠŗуŠ½Š“</string>
+ <string name="settings.network_timeout_15000">15 сŠµŠŗуŠ½Š“</string>
+ <string name="settings.network_timeout_30000">30 сŠµŠŗуŠ½Š“</string>
+ <string name="settings.network_timeout_45000">45 сŠµŠŗуŠ½Š“</string>
+ <string name="settings.network_timeout_60000">60 сŠµŠŗуŠ½Š“</string>
+ <string name="settings.preload_0">0 ŠŗŠ¾Š¼ŠæŠ¾Š·ŠøцŠøя</string>
+ <string name="settings.preload_1">1 ŠŗŠ¾Š¼ŠæŠ¾Š·ŠøцŠøя</string>
+ <string name="settings.preload_2">2 ŠŗŠ¾Š¼ŠæŠ¾Š·ŠøцŠøŠø</string>
+ <string name="settings.preload_3">3 ŠŗŠ¾Š¼ŠæŠ¾Š·ŠøцŠøŠø</string>
+ <string name="settings.preload_5">5 ŠŗŠ¾Š¼ŠæŠ¾Š·ŠøцŠøŠ¹</string>
+ <string name="settings.preload_10">10 ŠŗŠ¾Š¼ŠæŠ¾Š·ŠøцŠøŠ¹</string>
+ <string name="settings.preload_unlimited">ŠŠµŠ¾Š³Ń€Š°Š½ŠøчŠµŠ½Š¾</string>
+ <string name="settings.clear_search_history">ŠžŃ‡ŠøстŠøть ŠøстŠ¾Ń€Šøю ŠæŠ¾ŠøсŠŗŠ°</string>
+ <string name="settings.search_history_cleared">Š˜ŃŃ‚Š¾Ń€Šøя ŠæŠ¾ŠøсŠŗŠ° Š¾Ń‡ŠøщŠµŠ½Š°</string>
+ <string name="settings.other_title">Š”Ń€ŃƒŠ³ŠøŠµ Š½Š°ŃŃ‚Ń€Š¾Š¹ŠŗŠø</string>
+ <string name="settings.scrobble_title">Š”ŠŗрŠ¾Š±Š±Š»ŠøŠ½Š³ Š½Š° Last.fm</string>
+ <string name="settings.scrobble_summary">ŠŠµ Š·Š°Š±ŃƒŠ“ьтŠµ устŠ°Š½Š¾Š²Šøть Š»Š¾Š³ŠøŠ½ Šø ŠæŠ°Ń€Š¾Š»ŃŒ Š¾Ń‚ Last.fm Š½Š° сŠµŃ€Š²ŠµŃ€Šµ DSub</string>
+ <string name="settings.hide_media_title">ŠŸŃ€ŃŃ‚Š°Ń‚ŃŒ Š¾Ń‚ Š“руŠ³Šøх</string>
+ <string name="settings.hide_media_summary">ŠŸŃ€ŃŃ‚Š°Ń‚ŃŒ Š¼ŃƒŠ·Ń‹ŠŗŠ°Š»ŃŒŠ½Ń‹Šµ фŠ°Š¹Š»Ń‹ Š¾Ń‚ Š“руŠ³Šøх ŠæрŠøŠ»Š¾Š¶ŠµŠ½ŠøŠ¹</string>
+ <string name="settings.hide_media_toast">Š˜Š·Š¼ŠµŠ½ŠµŠ½Šøя Š²ŃŃ‚ŃƒŠæят Š² сŠøŠ»Ńƒ ŠæрŠø сŠ»ŠµŠ“ующŠµŠ¼ ŠæŠ¾ŠøсŠŗŠµ Š¼ŃƒŠ·Ń‹ŠŗŠø Š½Š° Š’Š°ŃˆŠµŠ¼ устрŠ¾Š¹ŃŃ‚Š²Šµ.</string>
+ <string name="settings.media_button_title">ŠšŠ½Š¾ŠæŠŗŠø уŠæрŠ°Š²Š»ŠµŠ½Šøя</string>
+ <string name="settings.media_button_summary">Š Š°Š·Ń€ŠµŃˆŠøть уŠæрŠ°Š²Š»ŠµŠ½ŠøŠµ ŠŗŠ½Š¾ŠæŠŗŠ°Š¼Šø Š¼ŃƒŠ»ŃŒŃ‚ŠøŠ¼ŠµŠ“ŠøŠ° Š½Š° устрŠ¾Š¹ŃŃ‚Š²Šµ Šø Š³Š°Ń€Š½ŠøтурŠµ</string>
+ <string name="settings.screen_lit_title">Š”ŠµŃ€Š¶Š°Ń‚ŃŒ эŠŗрŠ°Š½ Š²ŠŗŠ»ŃŽŃ‡ŠµŠ½Š½Ń‹Š¼</string>
+ <string name="settings.screen_lit_summary">ŠžŃŃ‚Š°Š²Šøть эŠŗрŠ°Š½ Š²ŠŗŠ»ŃŽŃ‡ŠµŠ½Š½Ń‹Š¼ Š“Š»Ń ŠæŠ¾Š²Ń‹ŃˆŠµŠ½Šøя сŠŗŠ¾Ń€Š¾ŃŃ‚Šø ŠæрŠø сŠŗŠ°Ń‡ŠøŠ²Š°Š½ŠøŠø.</string>
+ <string name="settings.playlist_title">Š”ŠæŠøсŠŗŠø Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½Šøя</string>
+ <string name="settings.playlist_random_size_title">Š Š°Š·Š¼ŠµŃ€ сŠ»ŃƒŃ‡Š°Š¹Š½Š¾Š³Š¾ сŠæŠøсŠŗŠ°</string>
+ <string name="settings.sleep_timer_title">Š¢Š°Š¹Š¼ŠµŃ€ сŠ½Š°</string>
+ <string name="settings.sleep_timer_duration_title">ŠŸŃ€Š¾Š“Š¾Š»Š¶ŠøтŠµŠ»ŃŒŠ½Š¾ŃŃ‚ŃŒ тŠ°Š¹Š¼ŠµŃ€Š° сŠ½Š°</string>
+ <string name="settings.sleep_timer_off">Š’Ń‹ŠŗŠ»ŃŽŃ‡ŠµŠ½</string>
+ <string name="settings.sleep_timer_on">Š’ŠŗŠ»ŃŽŃ‡ŠµŠ½</string>
+ <string name="settings.sleep_timer_always_on">Š’сŠµŠ³Š“Š° Š²ŠŗŠ»ŃŽŃ‡ŠµŠ½</string>
+ <string name="settings.temp_loss_title">Š’Ń€ŠµŠ¼ŠµŠ½Š½Š°Ń ŠæŠ¾Ń‚ŠµŃ€Ń сŠ²ŃŠ·Šø</string>
+ <string name="settings.temp_loss_pause">Š’сŠµŠ³Š“Š° Š¾ŃŃ‚Š°Š½Š°Š²Š»ŠøŠ²Š°Ń‚ŃŒ</string>
+ <string name="settings.temp_loss_pause_lower">ŠžŃŃ‚Š°Š½Š°Š²Š»ŠøŠ²Š°Ń‚ŃŒ, ŠæŠ¾Š½ŠøŠ¶Š°Ń‚ŃŒ Š³Ń€Š¾Š¼ŠŗŠ¾ŃŃ‚ŃŒ, ŠµŃŠ»Šø трŠµŠ±ŃƒŠµŃ‚ся</string>
+ <string name="settings.temp_loss_lower">Š’сŠµŠ³Š“Š° ŠæŠ¾Š½ŠøŠ¶Š°Ń‚ŃŒ Š³Ń€Š¾Š¼ŠŗŠ¾ŃŃ‚ŃŒ</string>
+ <string name="settings.temp_loss_nothing">ŠŠøчŠµŠ³Š¾ Š½Šµ Š“ŠµŠ»Š°Ń‚ŃŒ</string>
+
+ <string name="shuffle.startYear">Š“Š¾Š“ Š½Š°Ń‡Š°Š»Š°:</string>
+ <string name="shuffle.endYear">Š“Š¾Š“ Š¾ŠŗŠ¾Š½Ń‡Š°Š½Šøя:</string>
+ <string name="shuffle.genre">Š–Š°Š½Ń€:</string>
+
+ <string name="music_service.retry">ŠžŃˆŠøŠ±ŠŗŠ° ŠæŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠµŠ½Šøя. ŠŸŠ¾ŠæытŠŗŠ° %1$d ŠøŠ· %2$d.</string>
+
+ <string name="background_task.wait">ŠŸŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, ŠæŠ¾Š“Š¾Š¶Š“ŠøтŠµ...</string>
+ <string name="background_task.loading">Š—Š°Š³Ń€ŃƒŠ·ŠŗŠ°</string>
+ <string name="background_task.no_network">Š­Ń‚Š° ŠæрŠ¾Š³Ń€Š°Š¼Š¼Š° трŠµŠ±ŃƒŠµŃ‚ Š“Š¾ŃŃ‚ŃƒŠæ Šŗ сŠµŃ‚Šø. ŠŸŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, Š²ŠŗŠ»ŃŽŃ‡ŠøтŠµ Wi-Fi ŠøŠ»Šø Š¼Š¾Š±ŠøŠ»ŃŒŠ½Ń‹Š¹ ŠøŠ½Ń‚ŠµŃ€Š½ŠµŃ‚</string>
+ <string name="background_task.network_error">ŠžŃˆŠøŠ±ŠŗŠ° сŠµŃ‚Šø. ŠŸŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, ŠæрŠ¾Š²ŠµŃ€ŃŒŃ‚Šµ Š°Š“рŠµŃ сŠµŃ€Š²ŠµŃ€Š° Šø ŠæŠ¾ŠæрŠ¾Š±ŃƒŠ¹Ń‚Šµ сŠ½Š¾Š²Š°</string>
+ <string name="background_task.not_found">Š ŠµŃŃƒŃ€Ń Š½Šµ Š½Š°Š¹Š“ŠµŠ½. ŠŸŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, ŠæрŠ¾Š²ŠµŃ€ŃŒŃ‚Šµ Š°Š“рŠµŃ сŠµŃ€Š²ŠµŃ€Š°</string>
+ <string name="background_task.parse_error">ŠŠµŠøŠ·Š²ŠµŃŃ‚Š½Ń‹Š¹ Š¾Ń‚Š²ŠµŃ‚. ŠŸŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, ŠæрŠ¾Š²ŠµŃ€ŃŒŃ‚Šµ Š°Š“рŠµŃ сŠµŃ€Š²ŠµŃ€Š°</string>
+
+ <string name="service.connecting">ŠŸŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠµŠ½ŠøŠµ Šŗ сŠµŃ€Š²ŠµŃ€Ńƒ. ŠŸŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, ŠæŠ¾Š“Š¾Š¶Š“ŠøтŠµ.</string>
+
+ <string name="parser.upgrade_client">ŠŠµŃŠ¾Š²Š¼ŠµŃŃ‚ŠøŠ¼Ń‹Šµ Š²ŠµŃ€ŃŠøŠø. ŠŸŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, Š¾Š±Š½Š¾Š²ŠøтŠµ ŠæрŠøŠ»Š¾Š¶ŠµŠ½ŠøŠµ DSub Š“Š»Ń Android.</string>
+ <string name="parser.upgrade_server">ŠŠµŃŠ¾Š²Š¼ŠµŃŃ‚ŠøŠ¼Ń‹Šµ Š²ŠµŃ€ŃŠøŠø. ŠŸŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, Š¾Š±Š½Š¾Š²ŠøтŠµ сŠµŃ€Š²ŠµŃ€ Subsonic.</string>
+ <string name="parser.not_authenticated">ŠŠµŠæрŠ°Š²ŠøŠ»ŃŒŠ½Š¾Šµ ŠøŠ¼Ń ŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŠµŠ»Ń ŠøŠ»Šø ŠæŠ°Ń€Š¾Š»ŃŒ.</string>
+ <string name="parser.not_authorized">ŠŠµ Š°Š²Ń‚Š¾Ń€ŠøŠ·ŠøрŠ¾Š²Š°Š½. ŠŸŃ€Š¾Š²ŠµŃ€ŃŒŃ‚Šµ ŠæрŠ°Š²Š° ŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŠµŠ»Ń Š½Š° сŠµŃ€Š²ŠµŃ€Šµ Subsonic.</string>
+ <string name="parser.artist_count">ŠŸŠ¾Š»ŃƒŃ‡ŠµŠ½Š¾ %d ŠøсŠæŠ¾Š»Š½ŠøтŠµŠ»ŠµŠ¹.</string>
+
+ <string name="select_artist.refresh">ŠžŠ±Š½Š¾Š²Šøть</string>
+ <string name="select_artist.folder">Š’Ń‹Š±Ń€Š°Ń‚ŃŒ ŠæŠ°ŠæŠŗу</string>
+ <string name="select_artist.all_folders">Š’сŠµ ŠæŠ°ŠæŠŗŠø</string>
+
+ <string name="equalizer.label">Š­ŠŗŠ²Š°Š»Š°Š¹Š·ŠµŃ€</string>
+ <string name="equalizer.enabled">Š’ŠŗŠ»ŃŽŃ‡ŠµŠ½</string>
+ <string name="equalizer.preset">Š“Š¾Ń‚Š¾Š²Ń‹Šµ Š½Š°ŃŃ‚Ń€Š¾Š¹ŠŗŠø</string>
+
+ <string name="widget.initial_text">ŠšŠ¾ŃŠ½ŠøтŠµŃŃŒ Š“Š»Ń Š²Ń‹Š±Š¾Ń€Š° Š¼ŃƒŠ·Ń‹ŠŗŠø</string>
+ <string name="widget.sdcard_busy">SD ŠŗŠ°Ń€Ń‚Š° Š½ŠµŠ“Š¾ŃŃ‚ŃƒŠæŠ½Š°</string>
+ <string name="widget.sdcard_missing">ŠŠµŃ‚ SD ŠŗŠ°Ń€Ń‚Ń‹</string>
+
+ <string name="util.bytes_format.gigabyte">0.00 Š“Š‘</string>
+ <string name="util.bytes_format.megabyte">0.00 ŠœŠ‘</string>
+ <string name="util.bytes_format.kilobyte">0 ŠšŠ‘</string>
+ <string name="util.bytes_format.byte">0 Š‘</string>
+
+ <string name="button_bar.chat">Š§Š°Ń‚</string>
+ <string name="main.back_confirm">ŠŠ°Š¶Š¼ŠøтŠµ "Š½Š°Š·Š°Š“" ŠµŃ‰Šµ рŠ°Š· Š“Š»Ń Š²Ń‹Ń…Š¾Š“Š°</string>
+ <string name="download.playing_out_of">Š’Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½ŠøŠµ: %1$d/%2$d</string>
+ <string name="settings.persistent_title">ŠŸŠ¾ŃŃ‚Š¾ŃŠ½Š½Š¾Šµ уŠ²ŠµŠ“Š¾Š¼Š»ŠµŠ½ŠøŠµ</string>
+ <string name="settings.persistent_summary">ŠŸŠ¾ŠŗŠ°Š·Ń‹Š²Š°Ń‚ŃŒ уŠ²ŠµŠ“Š¾Š¼Š»ŠµŠ½ŠøŠµ Š“Š°Š¶Šµ Š²Š¾ Š²Ń€ŠµŠ¼Ń ŠæŠ°ŃƒŠ·Ń‹. ŠžŃŃ‚Š°Š½Š¾Š²ŠŗŠ° Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½Šøя уŠ±ŠµŃ€ŠµŃ‚ этŠ¾ уŠ²ŠµŠ“Š¾Š¼Š»ŠµŠ½ŠøŠµ.</string>
+ <string name="settings.gapless_playback">ŠŠµŠæрŠµŃ€Ń‹Š²Š½Š¾Šµ Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½ŠøŠµ</string>
+ <string name="settings.gapless_playback_summary">Galaxy S3 Š¼Š¾Š¶ŠµŃ‚ Š·Š°Š²ŠøсŠ°Ń‚ŃŒ ŠøŠ»Šø ŠøсŠæытыŠ²Š°Ń‚ŃŒ ŠæрŠ¾Ń‡ŠøŠµ труŠ“Š½Š¾ŃŃ‚Šø с Š¼Š¾Š¼ŠµŠ½Ń‚Š° Š½Š°Ń‡Š°Š»Š° Š½ŠµŠæрŠµŃ€Ń‹Š²Š½Š¾Š³Š¾ Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½Šøя. Š’Ń‹ŠŗŠ»ŃŽŃ‡ŠøтŠµ эту фуŠ½ŠŗцŠøю Š“Š»Ń ŠøсŠæрŠ°Š²Š»ŠµŠ½Šøя Š“Š°Š½Š½Š¾Š¹ ŠæрŠ¾Š±Š»ŠµŠ¼Ń‹.</string>
+ <string name="settings.chat_refresh">Š§Š°ŃŃ‚Š¾Ń‚Š° Š¾Š±Š½Š¾Š²Š»ŠµŠ½Šøя чŠ°Ń‚Š° (сŠµŠŗ)</string>
+ <string name="settings.chat_enabled">Š§Š°Ń‚ Š°ŠŗтŠøŠ²ŠµŠ½</string>
+ <string name="settings.chat_enabled_summary">ŠŸŠ¾ŠŗŠ°Š·Ń‹Š²Š°Ń‚ŃŒ ŠøŠ»Šø Š½ŠµŃ‚ Š²ŠŗŠ»Š°Š“Šŗу чŠ°Ń‚Š°</string>
+ <string name="changelog_full_title">Š–ŃƒŃ€Š½Š°Š» ŠøŠ·Š¼ŠµŠ½ŠµŠ½ŠøŠ¹</string>
+ <string name="changelog_title">Š§Ń‚Š¾ Š½Š¾Š²Š¾Š³Š¾</string>
+ <string name="changelog_ok_button">OK</string>
+ <string name="changelog_show_full">Š•Ń‰Šµā€¦</string>
+ <string name="chat.send_a_message">ŠžŃ‚ŠæрŠ°Š²Šøть сŠ¾Š¾Š±Ń‰ŠµŠ½ŠøŠµ</string>
+
+
+ <plurals name="select_album_n_songs">
+ <item quantity="zero">ŠŠµŃ‚ ŠŗŠ¾Š¼ŠæŠ¾Š·ŠøцŠøŠ¹</item>
+ <item quantity="one">1 ŠŗŠ¾Š¼ŠæŠ¾Š·ŠøцŠøя</item>
+ <item quantity="other">%d ŠŗŠ¾Š¼ŠæŠ¾Š·ŠøцŠøŠ¹</item>
+ </plurals>
+ <plurals name="select_album_n_songs_downloading">
+ <item quantity="one">1 ŠŗŠ¾Š¼ŠæŠ¾Š·ŠøцŠøя Š·Š°ŠæŠ»Š°Š½ŠøрŠ¾Š²Š°Š½Š° Š“Š»Ń сŠŗŠ°Ń‡ŠøŠ²Š°Š½Šøя</item>
+ <item quantity="other">%d ŠŗŠ¾Š¼ŠæŠ¾Š·ŠøцŠøŠ¹ Š·Š°ŠæŠ»Š°Š½ŠøрŠ¾Š²Š°Š½Š¾ Š“Š»Ń сŠŗŠ°Ń‡ŠøŠ²Š°Š½Šøя</item>
+ </plurals>
+ <plurals name="select_album_n_songs_added">
+ <item quantity="one">1 ŠŗŠ¾Š¼ŠæŠ¾Š·ŠøцŠøя Š“Š¾Š±Š°Š²Š»ŠµŠ½Š° Š² Š¾Ń‡ŠµŃ€ŠµŠ“ь Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½Šøя</item>
+ <item quantity="other">%d ŠŗŠ¾Š¼ŠæŠ¾Š·ŠøцŠøŠ¹ Š“Š¾Š±Š°Š²Š»ŠµŠ½Š¾ Š² Š¾Ń‡ŠµŃ€ŠµŠ“ь Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½Šøя</item>
+ </plurals>
+ <plurals name="select_album_donate_dialog_n_trial_days_left">
+ <item quantity="one">1 Š“ŠµŠ½ŃŒ Š“Š¾ ŠŗŠ¾Š½Ń†Š° ŠæрŠ¾Š±Š½Š¾Š³Š¾ ŠæŠµŃ€ŠøŠ¾Š“Š°</item>
+ <item quantity="other">%d Š“Š½ŠµŠ¹ Š“Š¾ ŠŗŠ¾Š½Ń†Š° ŠæрŠ¾Š±Š½Š¾Š³Š¾ ŠæŠµŃ€ŠøŠ¾Š“Š°</item>
+ </plurals>
+
+</resources>
diff --git a/res/values-v11/colors.xml b/app/src/main/res/values-v11/colors.xml
index f5a422bb..f5a422bb 100644
--- a/res/values-v11/colors.xml
+++ b/app/src/main/res/values-v11/colors.xml
diff --git a/res/values-v11/styles.xml b/app/src/main/res/values-v11/styles.xml
index 04ab9a85..9a7cb2b2 100644
--- a/res/values-v11/styles.xml
+++ b/app/src/main/res/values-v11/styles.xml
@@ -1,6 +1,6 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
- <style name="BasicButton">
- <item name="android:background">?android:selectableItemBackground</item>
- </style>
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <style name="BasicButton">
+ <item name="android:background">?android:selectableItemBackground</item>
+ </style>
</resources> \ No newline at end of file
diff --git a/res/values-v16/themes.xml b/app/src/main/res/values-v16/themes.xml
index 0161388b..013ac0aa 100644
--- a/res/values-v16/themes.xml
+++ b/app/src/main/res/values-v16/themes.xml
@@ -1,15 +1,15 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
- <style name="DSub.TextViewStyle" parent="android:Widget.TextView">
- <item name="android:fontFamily">sans-serif-light</item>
- </style>
-
- <style name="DSub.TextViewStyle.Bold" parent="android:Widget.TextView">
- <item name="android:fontFamily">sans-serif</item>
- <item name="android:textStyle">bold</item>
- </style>
-
- <style name="DSub.ButtonStyle" parent="android:Widget.Holo.Button">
- <item name="android:fontFamily">sans-serif-light</item>
- </style>
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <style name="DSub.TextViewStyle" parent="android:Widget.TextView">
+ <item name="android:fontFamily">sans-serif-light</item>
+ </style>
+
+ <style name="DSub.TextViewStyle.Bold" parent="android:Widget.TextView">
+ <item name="android:fontFamily">sans-serif</item>
+ <item name="android:textStyle">bold</item>
+ </style>
+
+ <style name="DSub.ButtonStyle" parent="android:Widget.Holo.Button">
+ <item name="android:fontFamily">sans-serif-light</item>
+ </style>
</resources> \ No newline at end of file
diff --git a/res/values/arrays.xml b/app/src/main/res/values/arrays.xml
index 37b15d12..37b15d12 100644
--- a/res/values/arrays.xml
+++ b/app/src/main/res/values/arrays.xml
diff --git a/res/values/attrs.xml b/app/src/main/res/values/attrs.xml
index 9667117c..9667117c 100644
--- a/res/values/attrs.xml
+++ b/app/src/main/res/values/attrs.xml
diff --git a/res/values/colors.xml b/app/src/main/res/values/colors.xml
index b1422ad6..b1422ad6 100644
--- a/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
diff --git a/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index d5be260f..be3e843d 100644
--- a/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -1,7 +1,7 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
- <dimen name="Button">54dip</dimen>
- <dimen name="Button.Small">46dip</dimen>
- <dimen name="AlbumArt.Small">78dip</dimen>
- <dimen name="AlbumArt.Header">120dip</dimen>
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <dimen name="Button">54dip</dimen>
+ <dimen name="Button.Small">46dip</dimen>
+ <dimen name="AlbumArt.Small">78dip</dimen>
+ <dimen name="AlbumArt.Header">120dip</dimen>
</resources> \ No newline at end of file
diff --git a/res/values/ids.xml b/app/src/main/res/values/ids.xml
index edb3bbec..edb3bbec 100644
--- a/res/values/ids.xml
+++ b/app/src/main/res/values/ids.xml
diff --git a/res/values/integers.xml b/app/src/main/res/values/integers.xml
index de6c6067..57371a11 100644
--- a/res/values/integers.xml
+++ b/app/src/main/res/values/integers.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
- <integer name="Grid.Columns">2</integer>
- <integer name="TextDescriptionLength">5</integer>
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <integer name="Grid.Columns">2</integer>
+ <integer name="TextDescriptionLength">5</integer>
</resources> \ No newline at end of file
diff --git a/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 352d90c2..b897cad6 100644
--- a/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,607 +1,607 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-
- <string name="common.appname">DSub</string>
- <string name="common.ok">OK</string>
- <string name="common.save">Save</string>
- <string name="common.cancel">Cancel</string>
- <string name="common.play_now">Play Now</string>
- <string name="common.play_shuffled">Play Shuffled</string>
- <string name="common.play_next">Play Next</string>
- <string name="common.play_last">Play Last</string>
- <string name="common.download">Cache</string>
- <string name="common.pin">Permanent Cache</string>
- <string name="common.delete">Delete</string>
- <string name="common.star">Star</string>
- <string name="common.unstar">Unstar</string>
- <string name="common.info">Details</string>
- <string name="common.name">Name</string>
- <string name="common.comment">Comment</string>
- <string name="common.public">Public</string>
- <string name="common.play_external">Play Video</string>
- <string name="common.stream_external">Stream Video</string>
- <string name="common.confirm">Confirm</string>
- <string name="common.confirm_message">Do you want to %1$s %2$s?</string>
- <string name="common.confirm_message_cache">cache</string>
- <string name="common.empty">No data</string>
- <string name="common.warning">Warning</string>
-
- <string name="button_bar.home">Home</string>
- <string name="button_bar.browse">Library</string>
- <string name="button_bar.search">Search</string>
- <string name="button_bar.playlists">Playlists</string>
- <string name="button_bar.now_playing">Now Playing</string>
- <string name="button_bar.podcasts">Podcasts</string>
- <string name="button_bar.bookmarks">Bookmarks</string>
- <string name="button_bar.shares">Shares</string>
- <string name="button_bar.chat">Chat</string>
- <string name="button_bar.admin">Admin</string>
- <string name="button_bar.downloading">Downloading</string>
-
- <string name="main.welcome_title">Welcome!</string>
- <string name="main.welcome_text">Welcome to DSub! The app is currently configured to use the Subsonic demo server. After you\'ve
- set up your personal server (available from <b>subsonic.org</b>), please go to <b>Settings</b> and change the configuration to connect to it.</string>
- <string name="main.about_title">About DSub</string>
- <string name="main.about_text">Author: Scott Jackson
- \nEmail: dsub.android@gmail.com
- \nVersion: %1$s
- \nFiles Cached: %2$s
- \nUsed Space: %3$s of %4$s
- \nAvailable Space: %5$s of %6$s</string>
- <string name="main.faq_title">FAQ</string>
- <string name="main.faq_text">
- <![CDATA[
- <font color="red">Cache vs Permanent Cache</font>:
- <br/>When songs are downloaded by DSub, they can be deleted to make room for new downloads. Permanently cached music on the other hand will never be automatically deleted.
- <p/><font color="red">ChromeCast fails</font>:
- <br/>Try setting the option Settings -> Playback -> Use device proxy. It is a work around for Chromecast not accepting self-signed certificates.
- <p/><font color="red">First level in Library are actually groups of artists</font>:
- <br/>In the option menu, deselect "First level artists". This will make it so that the entire first level of directories shown will be treated like groups of artists instead of the artists themselves.
- ]]>
- </string>
- <string name="main.select_server">Select server</string>
- <string name="main.shuffle">Shuffle play</string>
- <string name="main.offline">Go Offline</string>
- <string name="main.online">Go Online</string>
- <string name="main.settings">Settings</string>
- <string name="main.albums_title">Album Lists</string>
- <string name="main.albums_per_folder">Per Folder</string>
- <string name="main.albums_newest">Recently added</string>
- <string name="main.albums_recent">Recently played</string>
- <string name="main.albums_frequent">Most played</string>
- <string name="main.albums_highest">Top rated</string>
- <string name="main.albums_starred">Starred</string>
- <string name="main.albums_random">Random</string>
- <string name="main.albums_genres">Genres</string>
- <string name="main.albums_year">Decades</string>
- <string name="main.albums_alphabetical">Alphabetically</string>
- <string name="main.videos">Videos</string>
- <string name="main.songs_genres">@string/main.albums_genres</string>
- <string name="main.back_confirm">Press back again to exit</string>
- <string name="main.scan_complete">Completed scan of Server</string>
-
- <string name="menu.search">Search</string>
- <string name="menu.shuffle">Shuffle</string>
- <string name="menu.refresh">Refresh</string>
- <string name="menu.play">Play</string>
- <string name="menu.play_last">Play Last</string>
- <string name="menu.exit">Exit</string>
- <string name="menu.settings">Settings</string>
- <string name="menu.help">Help</string>
- <string name="menu.about">About</string>
- <string name="menu.add_playlist">Add To Playlist</string>
- <string name="menu.remove_playlist">Remove From Playlist</string>
- <string name="menu.deleted_playlist">Deleted playlist %s</string>
- <string name="menu.deleted_playlist_error">Failed to delete playlist %s</string>
- <string name="menu.log">Send Log</string>
- <string name="menu.set_timer">Set Timer</string>
- <string name="menu.check_podcasts">Check For New Episodes</string>
- <string name="menu.add_podcast">Add Channel</string>
- <string name="menu.keep_synced">Keep Synced</string>
- <string name="menu.stop_sync">Stop syncing</string>
- <string name="menu.show_all">Show all media</string>
- <string name="menu.show_artist">Show Artist</string>
- <string name="menu.share">Share</string>
- <string name="menu.delete_cache">Delete Cache</string>
- <string name="menu.cast">Cast To Device</string>
- <string name="menu.faq">FAQ</string>
- <string name="menu.add_user">Add User</string>
- <string name="menu.rescan">Rescan Server</string>
- <string name="menu.rate">Set Rating</string>
- <string name="menu.top_tracks">Last.FM Top Tracks</string>
- <string name="menu.similar_artists">Similar Artists</string>
- <string name="menu.show_missing">Show missing</string>
- <string name="menu.start_radio">Start Radio</string>
- <string name="menu.first_level_artist">First level artists</string>
-
- <string name="playlist.label">Playlists</string>
- <string name="playlist.update_info">Update Information</string>
- <string name="playlist.updated_info">Updated playlist information for %s</string>
- <string name="playlist.updated_info_error">Failed to update playlist information for %s</string>
- <string name="playlist.overwrite">Overwrite existing playlist</string>
- <string name="playlist.add_to">Add to Playlist</string>
- <string name="playlist.create_new">Create New</string>
- <string name="playlist.delete">Delete Playlist</string>
-
- <string name="search.label">Search</string>
- <string name="search.title">Search</string>
- <string name="search.search">Click to search</string>
- <string name="search.no_match">No matches, please try again</string>
- <string name="search.artists">Artists</string>
- <string name="search.albums">Albums</string>
- <string name="search.songs">Songs</string>
- <string name="search.more">Show more</string>
-
- <string name="progress.wait">Please wait...</string>
- <string name="progress.artist_info">Loading Artist Bio</string>
-
- <string name="music_library.label">Media library</string>
- <string name="music_library.label_offline">Offline media</string>
-
- <string name="select_album.select">Select all</string>
- <string name="select_album.n_selected">%d tracks selected.</string>
- <string name="select_album.n_unselected">%d tracks unselected.</string>
- <string name="select_album.more">More</string>
- <string name="select_album.offline">Offline</string>
- <string name="select_album.searching">Searching...</string>
- <string name="select_album.no_sdcard">Error: No SD card available.</string>
- <string name="select_album.no_network">Warning: No network available.</string>
- <string name="select_album.no_room">Warning: you only have %s left</string>
- <string name="select_album.not_licensed">Server not licensed. %d trial days left.</string>
- <string name="select_album.donate_dialog_message">Get unlimited downloads by donating to Subsonic.</string>
- <string name="select_album.donate_dialog_now">Now</string>
- <string name="select_album.donate_dialog_later">Later</string>
- <string name="select_album.donate_dialog_0_trial_days_left">Trial period is over</string>
-
- <string name="offline.sync_dialog_title">Offline songs waiting to be synced</string>
- <string name="offline.sync_dialog_message">Process %1$d offline scrobbles?
- \nProcess %2$d offline stars?
- </string>
- <string name="offline.sync_dialog_default">Use action as default</string>
- <string name="offline.sync_success">Successfully synced %1$d songs</string>
- <string name="offline.sync_partial">Successfully synced %1$d of %2$d songs</string>
- <string name="offline.sync_error">Failed to sync songs</string>
-
- <string name="select_genre.blank">Blank</string>
- <string name="select_genre.songs">%d songs</string>
- <string name="select_genre.albums">%d albums</string>
-
- <string name="select_podcasts.error">This podcast had an error while downloading on the server. The server must download it first.</string>
- <string name="select_podcasts.skipped">This podcast has not been downloaded on the server. The server must download it first.</string>
- <string name="select_podcasts.initializing">This podcast channel is being initialized on the server. Please reload after a moment.</string>
- <string name="select_podcasts.server_download">Download on server</string>
- <string name="select_podcasts.server_delete">Delete from server</string>
- <string name="select_podcasts.downloading">Now downloading %s on the server</string>
- <string name="select_podcasts.refreshing">The server is checking for new podcasts now</string>
- <string name="select_podcasts.deleted">Deleted podcast %s</string>
- <string name="select_podcasts.deleted_error">Failed to delete podcast %s</string>
- <string name="select_podcasts.add_url">URL:</string>
- <string name="select_podcasts.created_error">Failed to add podcast</string>
- <string name="select_podcasts.invalid_podcast_channel">Invalid podcast channel: %s</string>
- <string name="select_podcasts.delete">Delete podcast</string>
-
- <string name="download.empty">Playlist is empty</string>
- <string name="download.shuffle_loading">Shuffle list is loading...</string>
- <string name="download.playerstate_downloading">Downloading - %s</string>
- <string name="download.playerstate_buffering">Buffering</string>
- <string name="download.playerstate_playing_shuffle">Playing shuffle</string>
- <string name="download.menu_show_album">Show Album</string>
- <string name="download.menu_lyrics">Lyrics</string>
- <string name="download.menu_remove">Remove from queue</string>
- <string name="download.menu_remove_all">Remove all</string>
- <string name="download.menu_screen_on">Screen on</string>
- <string name="download.menu_shuffle">Shuffle</string>
- <string name="download.menu_toggle">Toggle</string>
- <string name="download.menu_save">Save playlist</string>
- <string name="download.menu_shuffle_notification">Playlist was shuffled</string>
- <string name="download.menu_remove_played_songs">Remove played songs</string>
- <string name="download.playlist_title">Save playlist</string>
- <string name="download.playlist_name">Enter the playlist name:</string>
- <string name="download.playlist_saving">Saving playlist \"%s\"...</string>
- <string name="download.playlist_done">Playlist was successfully saved.</string>
- <string name="download.playlist_error">Failed to save playlist, please try later.</string>
- <string name="download.repeat_off">Repeat off</string>
- <string name="download.repeat_all">Repeat all</string>
- <string name="download.repeat_single">Repeat song</string>
- <string name="download.jukebox_on">Turned on remote control. Music is played on the computer.</string>
- <string name="download.jukebox_off">Turned off remote control. Music is played on the phone.</string>
- <string name="download.jukebox_volume">Remote volume</string>
- <string name="download.jukebox_server_too_old">Remote control is not supported. Please upgrade your Subsonic server.</string>
- <string name="download.jukebox_offline">Remote control is not available in offline mode.</string>
- <string name="download.jukebox_not_authorized">Remote control is not allowed. Please enable jukebox mode in <b>Users &gt; Settings</b> on your Subsonic server.</string>
- <string name="download.timer_length">Timer:</string>
- <string name="download.start_timer">Start Timer</string>
- <string name="download.stop_timer">Stop Timer</string>
- <string name="download.need_download">Video needs to be downloaded first</string>
- <string name="download.no_streaming_player">No player can play this stream</string>
- <string name="download.playing_out_of">Playing: %1$d/%2$d</string>
- <string name="download.save_bookmark_title">Create bookmark</string>
- <string name="download.save_bookmark">Bookmark created</string>
- <string name="download.save_bookmark_failed">Failed to create bookmark</string>
- <string name="download.downloading_title">Downloading %1$d songs</string>
- <string name="download.downloading_summary">Current: %1$s</string>
- <string name="download.downloading_summary_expanded">Current: %1$s
- \nEstimated Size: %2$s</string>
- <string name="download.failed_to_load">Failed to load</string>
- <string name="download.restore_play_queue">continue from where you left off on another device at</string>
-
- <string name="sync.new_podcasts">New podcasts available</string>
- <string name="sync.new_playlists">New songs in playlists</string>
- <string name="sync.new_albums">New albums available</string>
- <string name="sync.new_starred">New starred songs available</string>
-
- <string name="starring_content_starred">Starred \"%s\"</string>
- <string name="starring_content_unstarred">Unstarred \"%s\"</string>
- <string name="starring_content_error">Failed to update \"%s\", please try later.</string>
-
- <string name="playlist_error">Failed to grab list of playlists</string>
- <string name="updated_playlist">Added %1$s songs to \"%2$s\"</string>
- <string name="updated_playlist_error">Failed to update \"%s\", please try later.</string>
- <string name="removed_playlist">Removed %1$s songs from \"%2$s\"</string>
-
- <string name="bookmark.delete">Delete bookmark</string>
- <string name="bookmark.delete_title">Delete the bookmark for</string>
- <string name="bookmark.deleted">Deleted the bookmark for \"%s\"</string>
- <string name="bookmark.deleted_error">Failed to delete the bookmark for \"%s\"</string>
- <string name="bookmark.details_title">Bookmark Details</string>
- <string name="bookmark.details">Song: %1$s
- \nPosition: %2$s
- \nCreated: %3$s
- \nLast Updated: %4$s
- \nComment: %5$s</string>
- <string name="bookmark.resume_title">Resume playing?</string>
- <string name="bookmark.resume">Resume playing \'%1$s\' from %2$s</string>
- <string name="bookmark.action_resume">Resume</string>
- <string name="bookmark.action_start_over">Start Over</string>
-
- <string name="rating.title">Rate \"%s\"</string>
- <string name="rating.set_rating">Rating set for \"%s\"</string>
- <string name="rating.set_rating_failed">Failed to set rating for \"%s\"</string>
- <string name="rating.remove_rating">Rating removed for \"%s\"</string>
- <string name="rating.remove_rating_failed">Failed to remove rating for \"%s\"</string>
-
- <string name="song_details.error">Error</string>
- <string name="song_details.skipped">Skipped</string>
- <string name="song_details.downloading">Downloading</string>
-
- <string name="lyrics.nomatch">No lyrics found</string>
-
- <string name="error.label">Error</string>
-
- <string name="settings.title">Settings</string>
- <string name="settings.test_connection_title">Test connection</string>
- <string name="settings.servers_add">Add Server</string>
- <string name="settings.servers_remove">Remove Server</string>
- <string name="settings.servers_title">Servers</string>
- <string name="settings.server_unused">Unused</string>
- <string name="settings.server_name">Name</string>
- <string name="settings.server_address">Server address</string>
- <string name="settings.server_local_network_ssid" >Local network SSID</string>
- <string name="settings.server_local_network_ssid_hint">Current SSID: %s</string>
- <string name="settings.server_internal_address">Local network address</string>
- <string name="settings.server_username">Username</string>
- <string name="settings.server_password">Password</string>
- <string name="settings.server_open_browser">Open in browser</string>
- <string name="settings.server_sync_summary">Whether or not syncing is enabled for this server</string>
- <string name="settings.server_sync">Sync Enabled</string>
- <string name="settings.cache_title">Music cache</string>
- <string name="settings.preload_wifi">Songs to preload (Wifi)</string>
- <string name="settings.preload_mobile">Songs to preload (Mobile)</string>
- <string name="settings.cache_size">Cache size</string>
- <string name="settings.cache_location">Cache location</string>
- <string name="settings.cache_location_error">Invalid cache location. Using default.</string>
- <string name="settings.cache_location_reset">The cache location you have set is no longer writable. If you recently upgraded your phone OS to KitKat 4.4, then the way apps write to the SD Card has changed so that they can only write to a specific location. The location that DSub uses has already been automatically changed to the correct location. In order to delete all of the old app data, you will need to mount the SD Card on your computer and delete the old folder manually</string>
- <string name="settings.cache_clear">Clear Cache</string>
- <string name="settings.cache_clear_complete">Finished clearing cache</string>
- <string name="settings.testing_connection">Testing connection...</string>
- <string name="settings.testing_ok">Connection is OK</string>
- <string name="settings.testing_unlicensed">Connection is OK. Server unlicensed.</string>
- <string name="settings.connection_failure">Connection failure.</string>
- <string name="settings.invalid_url">Please specify a valid URL.</string>
- <string name="settings.invalid_username">Please specify a valid username (no trailing spaces).</string>
- <string name="settings.appearance_title">Appearance</string>
- <string name="settings.theme_title">Theme</string>
- <string name="settings.theme_light">Light</string>
- <string name="settings.theme_dark">Dark</string>
- <string name="settings.theme_black">Black</string>
- <string name="settings.theme_holo">Holo</string>
- <string name="settings.theme_fullscreen">Fullscreen</string>
- <string name="settings.theme_fullscreen_summary">Hide as many UI elements as Android will allow</string>
- <string name="settings.track_title">Display Track #</string>
- <string name="settings.track_summary">Display Track # in front of songs if one exists</string>
- <string name="settings.custom_sort">Sort By Year</string>
- <string name="settings.custom_sort_summary">Sort albums by year, or by alphabetical</string>
- <string name="settings.open_to_tab">Open To Tab</string>
- <string name="settings.open_to_tab_summary">Open directly to this tab</string>
- <string name="settings.network_title">Network</string>
- <string name="settings.max_bitrate_wifi">Max Audio bitrate - Wi-Fi</string>
- <string name="settings.max_bitrate_mobile">Max Audio bitrate - Mobile</string>
- <string name="settings.max_bitrate_32">32 Kbps</string>
- <string name="settings.max_bitrate_64">64 Kbps</string>
- <string name="settings.max_bitrate_80">80 Kbps</string>
- <string name="settings.max_bitrate_96">96 Kbps</string>
- <string name="settings.max_bitrate_112">112 Kbps</string>
- <string name="settings.max_bitrate_128">128 Kbps</string>
- <string name="settings.max_bitrate_160">160 Kbps</string>
- <string name="settings.max_bitrate_192">192 Kbps</string>
- <string name="settings.max_bitrate_256">256 Kbps</string>
- <string name="settings.max_bitrate_320">320 Kbps</string>
- <string name="settings.max_video_bitrate_wifi">Max Video bitrate - Wi-Fi</string>
- <string name="settings.max_video_bitrate_mobile">Max Video bitrate - Mobile</string>
- <string name="settings.max_video_bitrate_200">200 Kbps</string>
- <string name="settings.max_video_bitrate_300">300 Kbps</string>
- <string name="settings.max_video_bitrate_400">400 Kbps</string>
- <string name="settings.max_video_bitrate_500">500 Kbps</string>
- <string name="settings.max_video_bitrate_700">700 Kbps</string>
- <string name="settings.max_video_bitrate_1000">1000 Kbps</string>
- <string name="settings.max_video_bitrate_1500">1500 Kbps</string>
- <string name="settings.max_video_bitrate_2000">2000 Kbps</string>
- <string name="settings.max_video_bitrate_3000">3000 Kbps</string>
- <string name="settings.max_video_bitrate_5000">5000 Kbps</string>
- <string name="settings.max_bitrate_unlimited">Unlimited</string>
- <string name="settings.wifi_required_title">Wi-Fi streaming only</string>
- <string name="settings.wifi_required_summary">Only stream media if connected to Wi-Fi</string>
- <string name="settings.network_timeout_title">Network Timeout</string>
- <string name="settings.network_timeout_10000">10 seconds</string>
- <string name="settings.network_timeout_15000">15 seconds</string>
- <string name="settings.network_timeout_30000">30 seconds</string>
- <string name="settings.network_timeout_45000">45 seconds</string>
- <string name="settings.network_timeout_60000">60 seconds</string>
- <string name="settings.preload_0">0 song</string>
- <string name="settings.preload_1">1 song</string>
- <string name="settings.preload_2">2 songs</string>
- <string name="settings.preload_3">3 songs</string>
- <string name="settings.preload_5">5 songs</string>
- <string name="settings.preload_10">10 songs</string>
- <string name="settings.preload_unlimited">Unlimited</string>
- <string name="settings.clear_search_history">Clear search history</string>
- <string name="settings.search_history_cleared">Search history cleared</string>
- <string name="settings.other_title">Other 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.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>
- <string name="settings.media_button_title">Media buttons</string>
- <string name="settings.media_button_summary">Respond to phone, headset and Bluetooth media buttons</string>
- <string name="settings.screen_lit_title">Keep screen on</string>
- <string name="settings.screen_lit_summary">Keeping the screen on while downloading improves download speed.</string>
- <string name="settings.playlist_title">Play</string>
- <string name="settings.playlist_random_size_title">Shuffle Playlist Size</string>
- <string name="settings.sleep_timer_title">Sleep Timer</string>
- <string name="settings.sleep_timer_duration_title">Sleep Timer Duration</string>
- <string name="settings.sleep_timer_off">Off</string>
- <string name="settings.sleep_timer_on">On</string>
- <string name="settings.sleep_timer_always_on">Always On</string>
- <string name="settings.temp_loss_title">Temporary Loss of Focus</string>
- <string name="settings.temp_loss_pause">Always Pause</string>
- <string name="settings.temp_loss_pause_lower">Pause, lower volume when requested</string>
- <string name="settings.temp_loss_lower">Always lower volume</string>
- <string name="settings.temp_loss_nothing">Do Nothing</string>
- <string name="settings.disconnect_pause_title">Pause on Disconnect</string>
- <string name="settings.disconnect_pause_both">Pause</string>
- <string name="settings.disconnect_pause_neither">Do Nothing</string>
- <string name="settings.persistent_title">Persistent Notification</string>
- <string name="settings.persistent_summary">Show the notification even after pausing. Press the stop button to clear it away.</string>
- <string name="settings.gapless_playback">Gapless Playback</string>
- <string name="settings.gapless_playback_summary">If you are seeing strange bugs during playback, turning this off may help.</string>
- <string name="settings.chat_refresh">Chat Refresh Rate (Secs)</string>
- <string name="settings.chat_enabled">Chat Enabled</string>
- <string name="settings.chat_enabled_summary">Whether or not to display the chat listing in the pull out drawer</string>
- <string name="settings.video_title">Video</string>
- <string name="settings.video_player">Video Player</string>
- <string name="settings.video_raw">Raw (Requires Subsonic 4.8+)</string>
- <string name="settings.video_hls">HTTP Live Stream (HLS) (Requires Subsonic 4.8+)</string>
- <string name="settings.video_transcode">Direct Transcode (Requires video -> mp4 or similar setup on Server)</string>
- <string name="settings.video_flash">Flash (Requires Plugin)</string>
- <string name="settings.cache_screen_title">Cache/Network</string>
- <string name="settings.playback_title">Playback</string>
- <string name="settings.hide_widget_title">Hide Widget</string>
- <string name="settings.hide_widget_summary">Hide widget after exiting app</string>
- <string name="settings.podcasts_enabled">Podcasts Enabled</string>
- <string name="settings.podcasts_enabled_summary">Whether or not to display the podcast listing in the pull out drawer</string>
- <string name="settings.bookmarks_enabled">Bookmarks Enabled</string>
- <string name="settings.bookmarks_enabled_summary">Whether or not to display the bookmarks listing in the pull out drawer</string>
- <string name="settings.shares_enabled">Shares Enabled</string>
- <string name="settings.shares_enabled_summary">Whether or not to display the shares listing in the pull out drawer</string>
- <string name="settings.sync_title">Sync</string>
- <string name="settings.sync_enabled">Sync Enabled</string>
- <string name="settings.sync_enabled_summary">Whether or not playlists or podcasts are periodically checked for changes</string>
- <string name="settings.sync_interval">Sync Interval</string>
- <string name="settings.sync_interval_15">15 Minutes</string>
- <string name="settings.sync_interval_30">30 Minutes</string>
- <string name="settings.sync_interval_60">1 Hour</string>
- <string name="settings.sync_interval_120">2 Hours</string>
- <string name="settings.sync_interval_240">4 Hours</string>
- <string name="settings.sync_interval_360">6 Hours</string>
- <string name="settings.sync_interval_720">12 Hours</string>
- <string name="settings.sync_interval_1440">Daily</string>
- <string name="settings.sync_wifi">Sync on Wifi only</string>
- <string name="settings.sync_wifi_summary">Only sync while on wifi</string>
- <string name="settings.sync_most_recent">Sync Recently Added</string>
- <string name="settings.sync_most_recent_summary">Automatically cache newly added albums</string>
- <string name="settings.sync_starred">Sync Starred</string>
- <string name="settings.sync_starred_summary">Automatically cache songs, albums, and artists which are starred</string>
- <string name="settings.sync_notification">Show Sync Notification</string>
- <string name="settings.sync_notification_summary">Show a notification after new media has been synced</string>
- <string name="settings.menu_options.title">Optional Menu Options</string>
- <string name="settings.menu_options.play_next_summary">Show Play next in menus</string>
- <string name="settings.menu_options.play_last_summary">Show Play last in menus</string>
- <string name="settings.menu_options.star_summary">Show Star in menus</string>
- <string name="settings.menu_options.shared_summary">Show Share in menus</string>
- <string name="settings.menu_options.rate_summary">Show Rating in menus</string>
- <string name="settings.browse_by_tags">Browse By Tags</string>
- <string name="settings.browse_by_tags_summary">Browse by tags instead of by folder structure. Requires Subsonic 4.7+</string>
- <string name="settings.disable_exit_prompt">Disable Exit Prompt</string>
- <string name="settings.disable_exit_prompt_summary">Exit the app immediately upon pressing back from the home screen</string>
- <string name="settings.override_system_language">Override System Language</string>
- <string name="settings.override_system_language_summary">Display app in english even if the system language is something DSub has a translation for. May need to clear the app from memory for changes to take affect.</string>
- <string name="settings.drawer_items_title">Drawer Tabs</string>
- <string name="settings.play_now_after">Play Now - After</string>
- <string name="settings.play_now_after_summary">Play Now context menu for a song plays everything after selected item (like the Subsonic web GUI)</string>
- <string name="settings.large_album_art">Large Album Art</string>
- <string name="settings.large_album_art_summary">Display albums with large album art instead of in a list</string>
- <string name="settings.admin_enabled">Admin Enabled</string>
- <string name="settings.admin_enabled_summary">Whether or not to display the admin listing in the pull out drawer</string>
- <string name="settings.replay_gain">Replay Gain</string>
- <string name="settings.replay_gain_summary">Whether or not to scale playback volume by track and album replay gain tags</string>
- <string name="settings.replay_gain_type">Read from tags</string>
- <string name="settings.replay_gain_type.smart">Smart detection</string>
- <string name="settings.replay_gain_type.album">Album tags</string>
- <string name="settings.replay_gain_type.track">Track tags</string>
- <string name="settings.replay_gain_bump">Replay Gain Pre-amp</string>
- <string name="settings.replay_gain_untagged">Songs without Replay Gain</string>
- <string name="settings.casting">Casting</string>
- <string name="settings.casting_proxy">Use device proxy</string>
- <string name="settings.casting_proxy_summary">Stream everything through the device as a proxy. This gets around issues such as using self-signed certificates.</string>
- <string name="settings.rename_duplicates">Rename duplicate tracks</string>
- <string name="settings.rename_duplicates_summary">Rename duplicate tracks to the original filename so you can tell them apart.</string>
- <string name="settings.start_on_headphones">Start on headphones</string>
- <string name="settings.start_on_headphones_summary">Start when headphones are plugged in. This requires the use of a service which starts on boot up to check for the headphone plug event even when DSub is not running.</string>
-
- <string name="shuffle.title">Shuffle By</string>
- <string name="shuffle.startYear">Start Year:</string>
- <string name="shuffle.endYear">End Year:</string>
- <string name="shuffle.genre">Genre:</string>
- <string name="shuffle.pick_genre">Pick a genre</string>
-
- <string name="share.info">Owner: %1$s
- \nDescription: %2$s
- \nURL: %3$s
- \nCreation: %4$s
- \nLast Visited: %5$s
- \nExpiration: %6$s
- \nVisit Count: %7$s
-
- </string>
- <string name="share.expires">Expires: %s</string>
- <string name="share.expires_never">Never Expires</string>
- <string name="share.deleted">Deleted share %s</string>
- <string name="share.deleted_error">Failed to delete share %s</string>
- <string name="share.no_expiration">No expiration</string>
- <string name="share.expiration">Expires:</string>
- <string name="share.updated_info">Updated share information for %s</string>
- <string name="share.updated_info_error">Failed to update share information for %s</string>
- <string name="share.via">Share via</string>
- <string name="share.delete">Delete Share</string>
-
- <string name="admin.add_user_username">Username:</string>
- <string name="admin.add_user_email">Email:</string>
- <string name="admin.add_user_password">Password:</string>
- <string name="admin.create_user_success">Successfully created new user</string>
- <string name="admin.create_user_error">Failed to create new user</string>
- <string name="admin.change_username_invalid">Enter a valid username</string>
- <string name="admin.update_permissions">Update Permissions</string>
- <string name="admin.update_permissions_success">Successfully updated permission for %1$s</string>
- <string name="admin.update_permissions_error">Failed to update permissions for %1$s</string>
- <string name="admin.change_email">Change Email</string>
- <string name="admin.change_email_success">Successfully changed email for %1$s</string>
- <string name="admin.change_email_error">Failed to change email for %1$s</string>
- <string name="admin.change_email_label">New Email:</string>
- <string name="admin.change_email_invalid">Enter a valid email</string>
- <string name="admin.change_password">Change Password</string>
- <string name="admin.change_password_success">Successfully changed password for %1$s</string>
- <string name="admin.change_password_error">Failed to change password for %1$s</string>
- <string name="admin.change_password_label">New Password:</string>
- <string name="admin.change_password_invalid">Enter a valid password</string>
- <string name="admin.delete_user">Delete User</string>
- <string name="admin.delete_user_success">Successfully deleted %1$s</string>
- <string name="admin.delete_user_error">Failed to delete %1$s</string>
- <string name="admin.confirm_password">Confirm Password</string>
- <string name="admin.confirm_password_bad">Entered password is wrong</string>
-
- <string name="admin.scrobblingEnabled">Scrobbling allowed</string>
- <string name="admin.role.admin">Administrator</string>
- <string name="admin.role.settings">Change settings</string>
- <string name="admin.role.download">Download original files</string>
- <string name="admin.role.upload">Upload to server</string>
- <string name="admin.role.coverArt">Change cover art</string>
- <string name="admin.role.comment">Add comments</string>
- <string name="admin.role.podcast">Manage podcasts</string>
- <string name="admin.role.stream">Stream music</string>
- <string name="admin.role.jukebox">Control jukebox</string>
- <string name="admin.role.share">Manage shares</string>
- <string name="admin.role.lastfm">Use Last.FM feature</string>
-
- <string name="music_service.retry">A network error occurred. Retrying %1$d of %2$d.</string>
-
- <string name="background_task.wait">Please wait...</string>
- <string name="background_task.loading">Loading.</string>
- <string name="background_task.no_network">This program requires network access. Please turn on Wi-Fi or mobile network.</string>
- <string name="background_task.network_error">A network error occurred. Please check the server address or try again later.</string>
- <string name="background_task.not_found">Resource not found. Please check the server address.</string>
- <string name="background_task.parse_error">A problem occurred communicating with the server. Please check the server address and verify that you can connect using a regular browser on your device.</string>
-
- <string name="service.connecting">Contacting server, please wait.</string>
-
- <string name="parser.upgrade_client">Incompatible versions. Please upgrade DSub.</string>
- <string name="parser.upgrade_server">Incompatible versions. Please upgrade Subsonic server.</string>
- <string name="parser.not_authenticated">Wrong username or password.</string>
- <string name="parser.not_authorized">Not authorized. Check user permissions in Subsonic server.</string>
- <string name="parser.artist_count">Got %d artists.</string>
- <string name="parser.server_error">Server error: %s</string>
- <string name="parser.scan_count">Scanned %d entries</string>
-
- <string name="select_artist.refresh">Refresh</string>
- <string name="select_artist.folder">Select folder</string>
- <string name="select_artist.all_folders">All folders</string>
-
- <string name="equalizer.label">Equalizer</string>
- <string name="equalizer.enabled">Enabled</string>
- <string name="equalizer.preset">Select preset</string>
- <string name="equalizer.bass_booster">Bass Booster</string>
- <string name="equalizer.voice_booster">Voice Booster</string>
- <string name="equalizer.db_size">%d dB</string>
- <string name="equalizer.bass_size">%d mille</string>
-
- <string name="widget.4x1">DSub (4x1)</string>
- <string name="widget.4x2">DSub (4x2)</string>
- <string name="widget.4x3">DSub (4x3)</string>
- <string name="widget.4x4">DSub (4x4)</string>
- <string name="widget.initial_text">Touch to select music</string>
- <string name="widget.sdcard_busy">SD card unavailable</string>
- <string name="widget.sdcard_missing">No SD card</string>
-
- <string name="util.bytes_format.gigabyte">0.00 GB</string>
- <string name="util.bytes_format.megabyte">0.00 MB</string>
- <string name="util.bytes_format.kilobyte">0 KB</string>
- <string name="util.bytes_format.byte">0 B</string>
-
- <string name="changelog_full_title">Change Log</string>
- <string name="changelog_title">What\'s New</string>
- <string name="changelog_ok_button">OK</string>
- <string name="changelog_show_full">Moreā€¦</string>
-
- <string name="chat.send_a_message">Send a message</string>
-
- <string name="changelog_version_format" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">Version <xliff:g id="version_name">%s</xliff:g></string>
-
- <string name="tasker.start_playing">Start playing</string>
- <string name="tasker.start_playing_shuffled">Start playing in Shuffle Mode</string>
- <string name="tasker.start_playing_title">Tasker -> Start DSub</string>
- <string name="tasker.edit_shuffle_mode">Start in shuffle mode: </string>
- <string name="tasker.edit_shuffle_start_year">Shuffle start year:</string>
- <string name="tasker.edit_shuffle_end_year">Shuffle end year:</string>
- <string name="tasker.edit_shuffle_genre">Shuffle from genre:</string>
- <string name="tasker.edit_server_offline">Toggle offline: </string>
- <string name="tasker.edit_do_nothing">Do Nothing</string>
-
- <plurals name="select_album_n_songs">
- <item quantity="zero">No songs</item>
- <item quantity="one">One song</item>
- <item quantity="other">%d songs</item>
- </plurals>
- <plurals name="select_album_n_songs_downloading">
- <item quantity="one">One song scheduled for download.</item>
- <item quantity="other">%d songs scheduled for download.</item>
- </plurals>
- <plurals name="select_album_n_songs_added">
- <item quantity="one">One song added to play queue.</item>
- <item quantity="other">%d songs added to play queue.</item>
- </plurals>
- <plurals name="select_album_donate_dialog_n_trial_days_left">
- <item quantity="one">One day left of trial period</item>
- <item quantity="other">%d days left of trial period</item>
- </plurals>
-
-</resources>
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <string name="common.appname">DSub</string>
+ <string name="common.ok">OK</string>
+ <string name="common.save">Save</string>
+ <string name="common.cancel">Cancel</string>
+ <string name="common.play_now">Play Now</string>
+ <string name="common.play_shuffled">Play Shuffled</string>
+ <string name="common.play_next">Play Next</string>
+ <string name="common.play_last">Play Last</string>
+ <string name="common.download">Cache</string>
+ <string name="common.pin">Permanent Cache</string>
+ <string name="common.delete">Delete</string>
+ <string name="common.star">Star</string>
+ <string name="common.unstar">Unstar</string>
+ <string name="common.info">Details</string>
+ <string name="common.name">Name</string>
+ <string name="common.comment">Comment</string>
+ <string name="common.public">Public</string>
+ <string name="common.play_external">Play Video</string>
+ <string name="common.stream_external">Stream Video</string>
+ <string name="common.confirm">Confirm</string>
+ <string name="common.confirm_message">Do you want to %1$s %2$s?</string>
+ <string name="common.confirm_message_cache">cache</string>
+ <string name="common.empty">No data</string>
+ <string name="common.warning">Warning</string>
+
+ <string name="button_bar.home">Home</string>
+ <string name="button_bar.browse">Library</string>
+ <string name="button_bar.search">Search</string>
+ <string name="button_bar.playlists">Playlists</string>
+ <string name="button_bar.now_playing">Now Playing</string>
+ <string name="button_bar.podcasts">Podcasts</string>
+ <string name="button_bar.bookmarks">Bookmarks</string>
+ <string name="button_bar.shares">Shares</string>
+ <string name="button_bar.chat">Chat</string>
+ <string name="button_bar.admin">Admin</string>
+ <string name="button_bar.downloading">Downloading</string>
+
+ <string name="main.welcome_title">Welcome!</string>
+ <string name="main.welcome_text">Welcome to DSub! The app is currently configured to use the Subsonic demo server. After you\'ve
+ set up your personal server (available from <b>subsonic.org</b>), please go to <b>Settings</b> and change the configuration to connect to it.</string>
+ <string name="main.about_title">About DSub</string>
+ <string name="main.about_text">Author: Scott Jackson
+ \nEmail: dsub.android@gmail.com
+ \nVersion: %1$s
+ \nFiles Cached: %2$s
+ \nUsed Space: %3$s of %4$s
+ \nAvailable Space: %5$s of %6$s</string>
+ <string name="main.faq_title">FAQ</string>
+ <string name="main.faq_text">
+ <![CDATA[
+ <font color="red">Cache vs Permanent Cache</font>:
+ <br/>When songs are downloaded by DSub, they can be deleted to make room for new downloads. Permanently cached music on the other hand will never be automatically deleted.
+ <p/><font color="red">ChromeCast fails</font>:
+ <br/>Try setting the option Settings -> Playback -> Use device proxy. It is a work around for Chromecast not accepting self-signed certificates.
+ <p/><font color="red">First level in Library are actually groups of artists</font>:
+ <br/>In the option menu, deselect "First level artists". This will make it so that the entire first level of directories shown will be treated like groups of artists instead of the artists themselves.
+ ]]>
+ </string>
+ <string name="main.select_server">Select server</string>
+ <string name="main.shuffle">Shuffle play</string>
+ <string name="main.offline">Go Offline</string>
+ <string name="main.online">Go Online</string>
+ <string name="main.settings">Settings</string>
+ <string name="main.albums_title">Album Lists</string>
+ <string name="main.albums_per_folder">Per Folder</string>
+ <string name="main.albums_newest">Recently added</string>
+ <string name="main.albums_recent">Recently played</string>
+ <string name="main.albums_frequent">Most played</string>
+ <string name="main.albums_highest">Top rated</string>
+ <string name="main.albums_starred">Starred</string>
+ <string name="main.albums_random">Random</string>
+ <string name="main.albums_genres">Genres</string>
+ <string name="main.albums_year">Decades</string>
+ <string name="main.albums_alphabetical">Alphabetically</string>
+ <string name="main.videos">Videos</string>
+ <string name="main.songs_genres">@string/main.albums_genres</string>
+ <string name="main.back_confirm">Press back again to exit</string>
+ <string name="main.scan_complete">Completed scan of Server</string>
+
+ <string name="menu.search">Search</string>
+ <string name="menu.shuffle">Shuffle</string>
+ <string name="menu.refresh">Refresh</string>
+ <string name="menu.play">Play</string>
+ <string name="menu.play_last">Play Last</string>
+ <string name="menu.exit">Exit</string>
+ <string name="menu.settings">Settings</string>
+ <string name="menu.help">Help</string>
+ <string name="menu.about">About</string>
+ <string name="menu.add_playlist">Add To Playlist</string>
+ <string name="menu.remove_playlist">Remove From Playlist</string>
+ <string name="menu.deleted_playlist">Deleted playlist %s</string>
+ <string name="menu.deleted_playlist_error">Failed to delete playlist %s</string>
+ <string name="menu.log">Send Log</string>
+ <string name="menu.set_timer">Set Timer</string>
+ <string name="menu.check_podcasts">Check For New Episodes</string>
+ <string name="menu.add_podcast">Add Channel</string>
+ <string name="menu.keep_synced">Keep Synced</string>
+ <string name="menu.stop_sync">Stop syncing</string>
+ <string name="menu.show_all">Show all media</string>
+ <string name="menu.show_artist">Show Artist</string>
+ <string name="menu.share">Share</string>
+ <string name="menu.delete_cache">Delete Cache</string>
+ <string name="menu.cast">Cast To Device</string>
+ <string name="menu.faq">FAQ</string>
+ <string name="menu.add_user">Add User</string>
+ <string name="menu.rescan">Rescan Server</string>
+ <string name="menu.rate">Set Rating</string>
+ <string name="menu.top_tracks">Last.FM Top Tracks</string>
+ <string name="menu.similar_artists">Similar Artists</string>
+ <string name="menu.show_missing">Show missing</string>
+ <string name="menu.start_radio">Start Radio</string>
+ <string name="menu.first_level_artist">First level artists</string>
+
+ <string name="playlist.label">Playlists</string>
+ <string name="playlist.update_info">Update Information</string>
+ <string name="playlist.updated_info">Updated playlist information for %s</string>
+ <string name="playlist.updated_info_error">Failed to update playlist information for %s</string>
+ <string name="playlist.overwrite">Overwrite existing playlist</string>
+ <string name="playlist.add_to">Add to Playlist</string>
+ <string name="playlist.create_new">Create New</string>
+ <string name="playlist.delete">Delete Playlist</string>
+
+ <string name="search.label">Search</string>
+ <string name="search.title">Search</string>
+ <string name="search.search">Click to search</string>
+ <string name="search.no_match">No matches, please try again</string>
+ <string name="search.artists">Artists</string>
+ <string name="search.albums">Albums</string>
+ <string name="search.songs">Songs</string>
+ <string name="search.more">Show more</string>
+
+ <string name="progress.wait">Please wait...</string>
+ <string name="progress.artist_info">Loading Artist Bio</string>
+
+ <string name="music_library.label">Media library</string>
+ <string name="music_library.label_offline">Offline media</string>
+
+ <string name="select_album.select">Select all</string>
+ <string name="select_album.n_selected">%d tracks selected.</string>
+ <string name="select_album.n_unselected">%d tracks unselected.</string>
+ <string name="select_album.more">More</string>
+ <string name="select_album.offline">Offline</string>
+ <string name="select_album.searching">Searching...</string>
+ <string name="select_album.no_sdcard">Error: No SD card available.</string>
+ <string name="select_album.no_network">Warning: No network available.</string>
+ <string name="select_album.no_room">Warning: you only have %s left</string>
+ <string name="select_album.not_licensed">Server not licensed. %d trial days left.</string>
+ <string name="select_album.donate_dialog_message">Get unlimited downloads by donating to Subsonic.</string>
+ <string name="select_album.donate_dialog_now">Now</string>
+ <string name="select_album.donate_dialog_later">Later</string>
+ <string name="select_album.donate_dialog_0_trial_days_left">Trial period is over</string>
+
+ <string name="offline.sync_dialog_title">Offline songs waiting to be synced</string>
+ <string name="offline.sync_dialog_message">Process %1$d offline scrobbles?
+ \nProcess %2$d offline stars?
+ </string>
+ <string name="offline.sync_dialog_default">Use action as default</string>
+ <string name="offline.sync_success">Successfully synced %1$d songs</string>
+ <string name="offline.sync_partial">Successfully synced %1$d of %2$d songs</string>
+ <string name="offline.sync_error">Failed to sync songs</string>
+
+ <string name="select_genre.blank">Blank</string>
+ <string name="select_genre.songs">%d songs</string>
+ <string name="select_genre.albums">%d albums</string>
+
+ <string name="select_podcasts.error">This podcast had an error while downloading on the server. The server must download it first.</string>
+ <string name="select_podcasts.skipped">This podcast has not been downloaded on the server. The server must download it first.</string>
+ <string name="select_podcasts.initializing">This podcast channel is being initialized on the server. Please reload after a moment.</string>
+ <string name="select_podcasts.server_download">Download on server</string>
+ <string name="select_podcasts.server_delete">Delete from server</string>
+ <string name="select_podcasts.downloading">Now downloading %s on the server</string>
+ <string name="select_podcasts.refreshing">The server is checking for new podcasts now</string>
+ <string name="select_podcasts.deleted">Deleted podcast %s</string>
+ <string name="select_podcasts.deleted_error">Failed to delete podcast %s</string>
+ <string name="select_podcasts.add_url">URL:</string>
+ <string name="select_podcasts.created_error">Failed to add podcast</string>
+ <string name="select_podcasts.invalid_podcast_channel">Invalid podcast channel: %s</string>
+ <string name="select_podcasts.delete">Delete podcast</string>
+
+ <string name="download.empty">Playlist is empty</string>
+ <string name="download.shuffle_loading">Shuffle list is loading...</string>
+ <string name="download.playerstate_downloading">Downloading - %s</string>
+ <string name="download.playerstate_buffering">Buffering</string>
+ <string name="download.playerstate_playing_shuffle">Playing shuffle</string>
+ <string name="download.menu_show_album">Show Album</string>
+ <string name="download.menu_lyrics">Lyrics</string>
+ <string name="download.menu_remove">Remove from queue</string>
+ <string name="download.menu_remove_all">Remove all</string>
+ <string name="download.menu_screen_on">Screen on</string>
+ <string name="download.menu_shuffle">Shuffle</string>
+ <string name="download.menu_toggle">Toggle</string>
+ <string name="download.menu_save">Save playlist</string>
+ <string name="download.menu_shuffle_notification">Playlist was shuffled</string>
+ <string name="download.menu_remove_played_songs">Remove played songs</string>
+ <string name="download.playlist_title">Save playlist</string>
+ <string name="download.playlist_name">Enter the playlist name:</string>
+ <string name="download.playlist_saving">Saving playlist \"%s\"...</string>
+ <string name="download.playlist_done">Playlist was successfully saved.</string>
+ <string name="download.playlist_error">Failed to save playlist, please try later.</string>
+ <string name="download.repeat_off">Repeat off</string>
+ <string name="download.repeat_all">Repeat all</string>
+ <string name="download.repeat_single">Repeat song</string>
+ <string name="download.jukebox_on">Turned on remote control. Music is played on the computer.</string>
+ <string name="download.jukebox_off">Turned off remote control. Music is played on the phone.</string>
+ <string name="download.jukebox_volume">Remote volume</string>
+ <string name="download.jukebox_server_too_old">Remote control is not supported. Please upgrade your Subsonic server.</string>
+ <string name="download.jukebox_offline">Remote control is not available in offline mode.</string>
+ <string name="download.jukebox_not_authorized">Remote control is not allowed. Please enable jukebox mode in <b>Users &gt; Settings</b> on your Subsonic server.</string>
+ <string name="download.timer_length">Timer:</string>
+ <string name="download.start_timer">Start Timer</string>
+ <string name="download.stop_timer">Stop Timer</string>
+ <string name="download.need_download">Video needs to be downloaded first</string>
+ <string name="download.no_streaming_player">No player can play this stream</string>
+ <string name="download.playing_out_of">Playing: %1$d/%2$d</string>
+ <string name="download.save_bookmark_title">Create bookmark</string>
+ <string name="download.save_bookmark">Bookmark created</string>
+ <string name="download.save_bookmark_failed">Failed to create bookmark</string>
+ <string name="download.downloading_title">Downloading %1$d songs</string>
+ <string name="download.downloading_summary">Current: %1$s</string>
+ <string name="download.downloading_summary_expanded">Current: %1$s
+ \nEstimated Size: %2$s</string>
+ <string name="download.failed_to_load">Failed to load</string>
+ <string name="download.restore_play_queue">continue from where you left off on another device at</string>
+
+ <string name="sync.new_podcasts">New podcasts available</string>
+ <string name="sync.new_playlists">New songs in playlists</string>
+ <string name="sync.new_albums">New albums available</string>
+ <string name="sync.new_starred">New starred songs available</string>
+
+ <string name="starring_content_starred">Starred \"%s\"</string>
+ <string name="starring_content_unstarred">Unstarred \"%s\"</string>
+ <string name="starring_content_error">Failed to update \"%s\", please try later.</string>
+
+ <string name="playlist_error">Failed to grab list of playlists</string>
+ <string name="updated_playlist">Added %1$s songs to \"%2$s\"</string>
+ <string name="updated_playlist_error">Failed to update \"%s\", please try later.</string>
+ <string name="removed_playlist">Removed %1$s songs from \"%2$s\"</string>
+
+ <string name="bookmark.delete">Delete bookmark</string>
+ <string name="bookmark.delete_title">Delete the bookmark for</string>
+ <string name="bookmark.deleted">Deleted the bookmark for \"%s\"</string>
+ <string name="bookmark.deleted_error">Failed to delete the bookmark for \"%s\"</string>
+ <string name="bookmark.details_title">Bookmark Details</string>
+ <string name="bookmark.details">Song: %1$s
+ \nPosition: %2$s
+ \nCreated: %3$s
+ \nLast Updated: %4$s
+ \nComment: %5$s</string>
+ <string name="bookmark.resume_title">Resume playing?</string>
+ <string name="bookmark.resume">Resume playing \'%1$s\' from %2$s</string>
+ <string name="bookmark.action_resume">Resume</string>
+ <string name="bookmark.action_start_over">Start Over</string>
+
+ <string name="rating.title">Rate \"%s\"</string>
+ <string name="rating.set_rating">Rating set for \"%s\"</string>
+ <string name="rating.set_rating_failed">Failed to set rating for \"%s\"</string>
+ <string name="rating.remove_rating">Rating removed for \"%s\"</string>
+ <string name="rating.remove_rating_failed">Failed to remove rating for \"%s\"</string>
+
+ <string name="song_details.error">Error</string>
+ <string name="song_details.skipped">Skipped</string>
+ <string name="song_details.downloading">Downloading</string>
+
+ <string name="lyrics.nomatch">No lyrics found</string>
+
+ <string name="error.label">Error</string>
+
+ <string name="settings.title">Settings</string>
+ <string name="settings.test_connection_title">Test connection</string>
+ <string name="settings.servers_add">Add Server</string>
+ <string name="settings.servers_remove">Remove Server</string>
+ <string name="settings.servers_title">Servers</string>
+ <string name="settings.server_unused">Unused</string>
+ <string name="settings.server_name">Name</string>
+ <string name="settings.server_address">Server address</string>
+ <string name="settings.server_local_network_ssid" >Local network SSID</string>
+ <string name="settings.server_local_network_ssid_hint">Current SSID: %s</string>
+ <string name="settings.server_internal_address">Local network address</string>
+ <string name="settings.server_username">Username</string>
+ <string name="settings.server_password">Password</string>
+ <string name="settings.server_open_browser">Open in browser</string>
+ <string name="settings.server_sync_summary">Whether or not syncing is enabled for this server</string>
+ <string name="settings.server_sync">Sync Enabled</string>
+ <string name="settings.cache_title">Music cache</string>
+ <string name="settings.preload_wifi">Songs to preload (Wifi)</string>
+ <string name="settings.preload_mobile">Songs to preload (Mobile)</string>
+ <string name="settings.cache_size">Cache size</string>
+ <string name="settings.cache_location">Cache location</string>
+ <string name="settings.cache_location_error">Invalid cache location. Using default.</string>
+ <string name="settings.cache_location_reset">The cache location you have set is no longer writable. If you recently upgraded your phone OS to KitKat 4.4, then the way apps write to the SD Card has changed so that they can only write to a specific location. The location that DSub uses has already been automatically changed to the correct location. In order to delete all of the old app data, you will need to mount the SD Card on your computer and delete the old folder manually</string>
+ <string name="settings.cache_clear">Clear Cache</string>
+ <string name="settings.cache_clear_complete">Finished clearing cache</string>
+ <string name="settings.testing_connection">Testing connection...</string>
+ <string name="settings.testing_ok">Connection is OK</string>
+ <string name="settings.testing_unlicensed">Connection is OK. Server unlicensed.</string>
+ <string name="settings.connection_failure">Connection failure.</string>
+ <string name="settings.invalid_url">Please specify a valid URL.</string>
+ <string name="settings.invalid_username">Please specify a valid username (no trailing spaces).</string>
+ <string name="settings.appearance_title">Appearance</string>
+ <string name="settings.theme_title">Theme</string>
+ <string name="settings.theme_light">Light</string>
+ <string name="settings.theme_dark">Dark</string>
+ <string name="settings.theme_black">Black</string>
+ <string name="settings.theme_holo">Holo</string>
+ <string name="settings.theme_fullscreen">Fullscreen</string>
+ <string name="settings.theme_fullscreen_summary">Hide as many UI elements as Android will allow</string>
+ <string name="settings.track_title">Display Track #</string>
+ <string name="settings.track_summary">Display Track # in front of songs if one exists</string>
+ <string name="settings.custom_sort">Sort By Year</string>
+ <string name="settings.custom_sort_summary">Sort albums by year, or by alphabetical</string>
+ <string name="settings.open_to_tab">Open To Tab</string>
+ <string name="settings.open_to_tab_summary">Open directly to this tab</string>
+ <string name="settings.network_title">Network</string>
+ <string name="settings.max_bitrate_wifi">Max Audio bitrate - Wi-Fi</string>
+ <string name="settings.max_bitrate_mobile">Max Audio bitrate - Mobile</string>
+ <string name="settings.max_bitrate_32">32 Kbps</string>
+ <string name="settings.max_bitrate_64">64 Kbps</string>
+ <string name="settings.max_bitrate_80">80 Kbps</string>
+ <string name="settings.max_bitrate_96">96 Kbps</string>
+ <string name="settings.max_bitrate_112">112 Kbps</string>
+ <string name="settings.max_bitrate_128">128 Kbps</string>
+ <string name="settings.max_bitrate_160">160 Kbps</string>
+ <string name="settings.max_bitrate_192">192 Kbps</string>
+ <string name="settings.max_bitrate_256">256 Kbps</string>
+ <string name="settings.max_bitrate_320">320 Kbps</string>
+ <string name="settings.max_video_bitrate_wifi">Max Video bitrate - Wi-Fi</string>
+ <string name="settings.max_video_bitrate_mobile">Max Video bitrate - Mobile</string>
+ <string name="settings.max_video_bitrate_200">200 Kbps</string>
+ <string name="settings.max_video_bitrate_300">300 Kbps</string>
+ <string name="settings.max_video_bitrate_400">400 Kbps</string>
+ <string name="settings.max_video_bitrate_500">500 Kbps</string>
+ <string name="settings.max_video_bitrate_700">700 Kbps</string>
+ <string name="settings.max_video_bitrate_1000">1000 Kbps</string>
+ <string name="settings.max_video_bitrate_1500">1500 Kbps</string>
+ <string name="settings.max_video_bitrate_2000">2000 Kbps</string>
+ <string name="settings.max_video_bitrate_3000">3000 Kbps</string>
+ <string name="settings.max_video_bitrate_5000">5000 Kbps</string>
+ <string name="settings.max_bitrate_unlimited">Unlimited</string>
+ <string name="settings.wifi_required_title">Wi-Fi streaming only</string>
+ <string name="settings.wifi_required_summary">Only stream media if connected to Wi-Fi</string>
+ <string name="settings.network_timeout_title">Network Timeout</string>
+ <string name="settings.network_timeout_10000">10 seconds</string>
+ <string name="settings.network_timeout_15000">15 seconds</string>
+ <string name="settings.network_timeout_30000">30 seconds</string>
+ <string name="settings.network_timeout_45000">45 seconds</string>
+ <string name="settings.network_timeout_60000">60 seconds</string>
+ <string name="settings.preload_0">0 song</string>
+ <string name="settings.preload_1">1 song</string>
+ <string name="settings.preload_2">2 songs</string>
+ <string name="settings.preload_3">3 songs</string>
+ <string name="settings.preload_5">5 songs</string>
+ <string name="settings.preload_10">10 songs</string>
+ <string name="settings.preload_unlimited">Unlimited</string>
+ <string name="settings.clear_search_history">Clear search history</string>
+ <string name="settings.search_history_cleared">Search history cleared</string>
+ <string name="settings.other_title">Other 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.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>
+ <string name="settings.media_button_title">Media buttons</string>
+ <string name="settings.media_button_summary">Respond to phone, headset and Bluetooth media buttons</string>
+ <string name="settings.screen_lit_title">Keep screen on</string>
+ <string name="settings.screen_lit_summary">Keeping the screen on while downloading improves download speed.</string>
+ <string name="settings.playlist_title">Play</string>
+ <string name="settings.playlist_random_size_title">Shuffle Playlist Size</string>
+ <string name="settings.sleep_timer_title">Sleep Timer</string>
+ <string name="settings.sleep_timer_duration_title">Sleep Timer Duration</string>
+ <string name="settings.sleep_timer_off">Off</string>
+ <string name="settings.sleep_timer_on">On</string>
+ <string name="settings.sleep_timer_always_on">Always On</string>
+ <string name="settings.temp_loss_title">Temporary Loss of Focus</string>
+ <string name="settings.temp_loss_pause">Always Pause</string>
+ <string name="settings.temp_loss_pause_lower">Pause, lower volume when requested</string>
+ <string name="settings.temp_loss_lower">Always lower volume</string>
+ <string name="settings.temp_loss_nothing">Do Nothing</string>
+ <string name="settings.disconnect_pause_title">Pause on Disconnect</string>
+ <string name="settings.disconnect_pause_both">Pause</string>
+ <string name="settings.disconnect_pause_neither">Do Nothing</string>
+ <string name="settings.persistent_title">Persistent Notification</string>
+ <string name="settings.persistent_summary">Show the notification even after pausing. Press the stop button to clear it away.</string>
+ <string name="settings.gapless_playback">Gapless Playback</string>
+ <string name="settings.gapless_playback_summary">If you are seeing strange bugs during playback, turning this off may help.</string>
+ <string name="settings.chat_refresh">Chat Refresh Rate (Secs)</string>
+ <string name="settings.chat_enabled">Chat Enabled</string>
+ <string name="settings.chat_enabled_summary">Whether or not to display the chat listing in the pull out drawer</string>
+ <string name="settings.video_title">Video</string>
+ <string name="settings.video_player">Video Player</string>
+ <string name="settings.video_raw">Raw (Requires Subsonic 4.8+)</string>
+ <string name="settings.video_hls">HTTP Live Stream (HLS) (Requires Subsonic 4.8+)</string>
+ <string name="settings.video_transcode">Direct Transcode (Requires video -> mp4 or similar setup on Server)</string>
+ <string name="settings.video_flash">Flash (Requires Plugin)</string>
+ <string name="settings.cache_screen_title">Cache/Network</string>
+ <string name="settings.playback_title">Playback</string>
+ <string name="settings.hide_widget_title">Hide Widget</string>
+ <string name="settings.hide_widget_summary">Hide widget after exiting app</string>
+ <string name="settings.podcasts_enabled">Podcasts Enabled</string>
+ <string name="settings.podcasts_enabled_summary">Whether or not to display the podcast listing in the pull out drawer</string>
+ <string name="settings.bookmarks_enabled">Bookmarks Enabled</string>
+ <string name="settings.bookmarks_enabled_summary">Whether or not to display the bookmarks listing in the pull out drawer</string>
+ <string name="settings.shares_enabled">Shares Enabled</string>
+ <string name="settings.shares_enabled_summary">Whether or not to display the shares listing in the pull out drawer</string>
+ <string name="settings.sync_title">Sync</string>
+ <string name="settings.sync_enabled">Sync Enabled</string>
+ <string name="settings.sync_enabled_summary">Whether or not playlists or podcasts are periodically checked for changes</string>
+ <string name="settings.sync_interval">Sync Interval</string>
+ <string name="settings.sync_interval_15">15 Minutes</string>
+ <string name="settings.sync_interval_30">30 Minutes</string>
+ <string name="settings.sync_interval_60">1 Hour</string>
+ <string name="settings.sync_interval_120">2 Hours</string>
+ <string name="settings.sync_interval_240">4 Hours</string>
+ <string name="settings.sync_interval_360">6 Hours</string>
+ <string name="settings.sync_interval_720">12 Hours</string>
+ <string name="settings.sync_interval_1440">Daily</string>
+ <string name="settings.sync_wifi">Sync on Wifi only</string>
+ <string name="settings.sync_wifi_summary">Only sync while on wifi</string>
+ <string name="settings.sync_most_recent">Sync Recently Added</string>
+ <string name="settings.sync_most_recent_summary">Automatically cache newly added albums</string>
+ <string name="settings.sync_starred">Sync Starred</string>
+ <string name="settings.sync_starred_summary">Automatically cache songs, albums, and artists which are starred</string>
+ <string name="settings.sync_notification">Show Sync Notification</string>
+ <string name="settings.sync_notification_summary">Show a notification after new media has been synced</string>
+ <string name="settings.menu_options.title">Optional Menu Options</string>
+ <string name="settings.menu_options.play_next_summary">Show Play next in menus</string>
+ <string name="settings.menu_options.play_last_summary">Show Play last in menus</string>
+ <string name="settings.menu_options.star_summary">Show Star in menus</string>
+ <string name="settings.menu_options.shared_summary">Show Share in menus</string>
+ <string name="settings.menu_options.rate_summary">Show Rating in menus</string>
+ <string name="settings.browse_by_tags">Browse By Tags</string>
+ <string name="settings.browse_by_tags_summary">Browse by tags instead of by folder structure. Requires Subsonic 4.7+</string>
+ <string name="settings.disable_exit_prompt">Disable Exit Prompt</string>
+ <string name="settings.disable_exit_prompt_summary">Exit the app immediately upon pressing back from the home screen</string>
+ <string name="settings.override_system_language">Override System Language</string>
+ <string name="settings.override_system_language_summary">Display app in english even if the system language is something DSub has a translation for. May need to clear the app from memory for changes to take affect.</string>
+ <string name="settings.drawer_items_title">Drawer Tabs</string>
+ <string name="settings.play_now_after">Play Now - After</string>
+ <string name="settings.play_now_after_summary">Play Now context menu for a song plays everything after selected item (like the Subsonic web GUI)</string>
+ <string name="settings.large_album_art">Large Album Art</string>
+ <string name="settings.large_album_art_summary">Display albums with large album art instead of in a list</string>
+ <string name="settings.admin_enabled">Admin Enabled</string>
+ <string name="settings.admin_enabled_summary">Whether or not to display the admin listing in the pull out drawer</string>
+ <string name="settings.replay_gain">Replay Gain</string>
+ <string name="settings.replay_gain_summary">Whether or not to scale playback volume by track and album replay gain tags</string>
+ <string name="settings.replay_gain_type">Read from tags</string>
+ <string name="settings.replay_gain_type.smart">Smart detection</string>
+ <string name="settings.replay_gain_type.album">Album tags</string>
+ <string name="settings.replay_gain_type.track">Track tags</string>
+ <string name="settings.replay_gain_bump">Replay Gain Pre-amp</string>
+ <string name="settings.replay_gain_untagged">Songs without Replay Gain</string>
+ <string name="settings.casting">Casting</string>
+ <string name="settings.casting_proxy">Use device proxy</string>
+ <string name="settings.casting_proxy_summary">Stream everything through the device as a proxy. This gets around issues such as using self-signed certificates.</string>
+ <string name="settings.rename_duplicates">Rename duplicate tracks</string>
+ <string name="settings.rename_duplicates_summary">Rename duplicate tracks to the original filename so you can tell them apart.</string>
+ <string name="settings.start_on_headphones">Start on headphones</string>
+ <string name="settings.start_on_headphones_summary">Start when headphones are plugged in. This requires the use of a service which starts on boot up to check for the headphone plug event even when DSub is not running.</string>
+
+ <string name="shuffle.title">Shuffle By</string>
+ <string name="shuffle.startYear">Start Year:</string>
+ <string name="shuffle.endYear">End Year:</string>
+ <string name="shuffle.genre">Genre:</string>
+ <string name="shuffle.pick_genre">Pick a genre</string>
+
+ <string name="share.info">Owner: %1$s
+ \nDescription: %2$s
+ \nURL: %3$s
+ \nCreation: %4$s
+ \nLast Visited: %5$s
+ \nExpiration: %6$s
+ \nVisit Count: %7$s
+
+ </string>
+ <string name="share.expires">Expires: %s</string>
+ <string name="share.expires_never">Never Expires</string>
+ <string name="share.deleted">Deleted share %s</string>
+ <string name="share.deleted_error">Failed to delete share %s</string>
+ <string name="share.no_expiration">No expiration</string>
+ <string name="share.expiration">Expires:</string>
+ <string name="share.updated_info">Updated share information for %s</string>
+ <string name="share.updated_info_error">Failed to update share information for %s</string>
+ <string name="share.via">Share via</string>
+ <string name="share.delete">Delete Share</string>
+
+ <string name="admin.add_user_username">Username:</string>
+ <string name="admin.add_user_email">Email:</string>
+ <string name="admin.add_user_password">Password:</string>
+ <string name="admin.create_user_success">Successfully created new user</string>
+ <string name="admin.create_user_error">Failed to create new user</string>
+ <string name="admin.change_username_invalid">Enter a valid username</string>
+ <string name="admin.update_permissions">Update Permissions</string>
+ <string name="admin.update_permissions_success">Successfully updated permission for %1$s</string>
+ <string name="admin.update_permissions_error">Failed to update permissions for %1$s</string>
+ <string name="admin.change_email">Change Email</string>
+ <string name="admin.change_email_success">Successfully changed email for %1$s</string>
+ <string name="admin.change_email_error">Failed to change email for %1$s</string>
+ <string name="admin.change_email_label">New Email:</string>
+ <string name="admin.change_email_invalid">Enter a valid email</string>
+ <string name="admin.change_password">Change Password</string>
+ <string name="admin.change_password_success">Successfully changed password for %1$s</string>
+ <string name="admin.change_password_error">Failed to change password for %1$s</string>
+ <string name="admin.change_password_label">New Password:</string>
+ <string name="admin.change_password_invalid">Enter a valid password</string>
+ <string name="admin.delete_user">Delete User</string>
+ <string name="admin.delete_user_success">Successfully deleted %1$s</string>
+ <string name="admin.delete_user_error">Failed to delete %1$s</string>
+ <string name="admin.confirm_password">Confirm Password</string>
+ <string name="admin.confirm_password_bad">Entered password is wrong</string>
+
+ <string name="admin.scrobblingEnabled">Scrobbling allowed</string>
+ <string name="admin.role.admin">Administrator</string>
+ <string name="admin.role.settings">Change settings</string>
+ <string name="admin.role.download">Download original files</string>
+ <string name="admin.role.upload">Upload to server</string>
+ <string name="admin.role.coverArt">Change cover art</string>
+ <string name="admin.role.comment">Add comments</string>
+ <string name="admin.role.podcast">Manage podcasts</string>
+ <string name="admin.role.stream">Stream music</string>
+ <string name="admin.role.jukebox">Control jukebox</string>
+ <string name="admin.role.share">Manage shares</string>
+ <string name="admin.role.lastfm">Use Last.FM feature</string>
+
+ <string name="music_service.retry">A network error occurred. Retrying %1$d of %2$d.</string>
+
+ <string name="background_task.wait">Please wait...</string>
+ <string name="background_task.loading">Loading.</string>
+ <string name="background_task.no_network">This program requires network access. Please turn on Wi-Fi or mobile network.</string>
+ <string name="background_task.network_error">A network error occurred. Please check the server address or try again later.</string>
+ <string name="background_task.not_found">Resource not found. Please check the server address.</string>
+ <string name="background_task.parse_error">A problem occurred communicating with the server. Please check the server address and verify that you can connect using a regular browser on your device.</string>
+
+ <string name="service.connecting">Contacting server, please wait.</string>
+
+ <string name="parser.upgrade_client">Incompatible versions. Please upgrade DSub.</string>
+ <string name="parser.upgrade_server">Incompatible versions. Please upgrade Subsonic server.</string>
+ <string name="parser.not_authenticated">Wrong username or password.</string>
+ <string name="parser.not_authorized">Not authorized. Check user permissions in Subsonic server.</string>
+ <string name="parser.artist_count">Got %d artists.</string>
+ <string name="parser.server_error">Server error: %s</string>
+ <string name="parser.scan_count">Scanned %d entries</string>
+
+ <string name="select_artist.refresh">Refresh</string>
+ <string name="select_artist.folder">Select folder</string>
+ <string name="select_artist.all_folders">All folders</string>
+
+ <string name="equalizer.label">Equalizer</string>
+ <string name="equalizer.enabled">Enabled</string>
+ <string name="equalizer.preset">Select preset</string>
+ <string name="equalizer.bass_booster">Bass Booster</string>
+ <string name="equalizer.voice_booster">Voice Booster</string>
+ <string name="equalizer.db_size">%d dB</string>
+ <string name="equalizer.bass_size">%d mille</string>
+
+ <string name="widget.4x1">DSub (4x1)</string>
+ <string name="widget.4x2">DSub (4x2)</string>
+ <string name="widget.4x3">DSub (4x3)</string>
+ <string name="widget.4x4">DSub (4x4)</string>
+ <string name="widget.initial_text">Touch to select music</string>
+ <string name="widget.sdcard_busy">SD card unavailable</string>
+ <string name="widget.sdcard_missing">No SD card</string>
+
+ <string name="util.bytes_format.gigabyte">0.00 GB</string>
+ <string name="util.bytes_format.megabyte">0.00 MB</string>
+ <string name="util.bytes_format.kilobyte">0 KB</string>
+ <string name="util.bytes_format.byte">0 B</string>
+
+ <string name="changelog_full_title">Change Log</string>
+ <string name="changelog_title">What\'s New</string>
+ <string name="changelog_ok_button">OK</string>
+ <string name="changelog_show_full">Moreā€¦</string>
+
+ <string name="chat.send_a_message">Send a message</string>
+
+ <string name="changelog_version_format" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">Version <xliff:g id="version_name">%s</xliff:g></string>
+
+ <string name="tasker.start_playing">Start playing</string>
+ <string name="tasker.start_playing_shuffled">Start playing in Shuffle Mode</string>
+ <string name="tasker.start_playing_title">Tasker -> Start DSub</string>
+ <string name="tasker.edit_shuffle_mode">Start in shuffle mode: </string>
+ <string name="tasker.edit_shuffle_start_year">Shuffle start year:</string>
+ <string name="tasker.edit_shuffle_end_year">Shuffle end year:</string>
+ <string name="tasker.edit_shuffle_genre">Shuffle from genre:</string>
+ <string name="tasker.edit_server_offline">Toggle offline: </string>
+ <string name="tasker.edit_do_nothing">Do Nothing</string>
+
+ <plurals name="select_album_n_songs">
+ <item quantity="zero">No songs</item>
+ <item quantity="one">One song</item>
+ <item quantity="other">%d songs</item>
+ </plurals>
+ <plurals name="select_album_n_songs_downloading">
+ <item quantity="one">One song scheduled for download.</item>
+ <item quantity="other">%d songs scheduled for download.</item>
+ </plurals>
+ <plurals name="select_album_n_songs_added">
+ <item quantity="one">One song added to play queue.</item>
+ <item quantity="other">%d songs added to play queue.</item>
+ </plurals>
+ <plurals name="select_album_donate_dialog_n_trial_days_left">
+ <item quantity="one">One day left of trial period</item>
+ <item quantity="other">%d days left of trial period</item>
+ </plurals>
+
+</resources>
diff --git a/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 43271afd..43271afd 100644
--- a/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
diff --git a/res/values/themes.xml b/app/src/main/res/values/themes.xml
index 78a2c34d..78a2c34d 100644
--- a/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
diff --git a/res/xml/appwidget4x1.xml b/app/src/main/res/xml/appwidget4x1.xml
index f3ab5d56..f3ab5d56 100644
--- a/res/xml/appwidget4x1.xml
+++ b/app/src/main/res/xml/appwidget4x1.xml
diff --git a/res/xml/appwidget4x2.xml b/app/src/main/res/xml/appwidget4x2.xml
index d687d952..d687d952 100644
--- a/res/xml/appwidget4x2.xml
+++ b/app/src/main/res/xml/appwidget4x2.xml
diff --git a/res/xml/appwidget4x3.xml b/app/src/main/res/xml/appwidget4x3.xml
index 4d1b4e08..4d1b4e08 100644
--- a/res/xml/appwidget4x3.xml
+++ b/app/src/main/res/xml/appwidget4x3.xml
diff --git a/res/xml/appwidget4x4.xml b/app/src/main/res/xml/appwidget4x4.xml
index 74a8ed0c..74a8ed0c 100644
--- a/res/xml/appwidget4x4.xml
+++ b/app/src/main/res/xml/appwidget4x4.xml
diff --git a/res/xml/authenticator.xml b/app/src/main/res/xml/authenticator.xml
index ce62b117..3055240b 100644
--- a/res/xml/authenticator.xml
+++ b/app/src/main/res/xml/authenticator.xml
@@ -1,7 +1,7 @@
-<?xml version="1.0" encoding="utf-8"?>
-<account-authenticator
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:accountType="subsonic.org"
- android:icon="@drawable/launch"
- android:smallIcon="@drawable/launch"
+<?xml version="1.0" encoding="utf-8"?>
+<account-authenticator
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:accountType="subsonic.org"
+ android:icon="@drawable/launch"
+ android:smallIcon="@drawable/launch"
android:label="@string/common.appname"/> \ No newline at end of file
diff --git a/res/xml/changelog.xml b/app/src/main/res/xml/changelog.xml
index 005ddf44..005ddf44 100644
--- a/res/xml/changelog.xml
+++ b/app/src/main/res/xml/changelog.xml
diff --git a/res/xml/mostrecent_syncadapter.xml b/app/src/main/res/xml/mostrecent_syncadapter.xml
index 9e76182a..0195edeb 100644
--- a/res/xml/mostrecent_syncadapter.xml
+++ b/app/src/main/res/xml/mostrecent_syncadapter.xml
@@ -1,8 +1,8 @@
-<?xml version="1.0" encoding="utf-8"?>
-<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
- android:contentAuthority="github.daneren2005.dsub.mostrecent.provider"
- android:accountType="subsonic.org"
- android:userVisible="true"
- android:supportsUploading="false"
- android:allowParallelSyncs="false"
+<?xml version="1.0" encoding="utf-8"?>
+<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
+ android:contentAuthority="github.daneren2005.dsub.mostrecent.provider"
+ android:accountType="subsonic.org"
+ android:userVisible="true"
+ android:supportsUploading="false"
+ android:allowParallelSyncs="false"
android:isAlwaysSyncable="true"/> \ No newline at end of file
diff --git a/res/xml/playlists_syncadapter.xml b/app/src/main/res/xml/playlists_syncadapter.xml
index 418f3f49..6c56557b 100644
--- a/res/xml/playlists_syncadapter.xml
+++ b/app/src/main/res/xml/playlists_syncadapter.xml
@@ -1,8 +1,8 @@
-<?xml version="1.0" encoding="utf-8"?>
-<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
- android:contentAuthority="github.daneren2005.dsub.playlists.provider"
- android:accountType="subsonic.org"
- android:userVisible="true"
- android:supportsUploading="false"
- android:allowParallelSyncs="false"
+<?xml version="1.0" encoding="utf-8"?>
+<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
+ android:contentAuthority="github.daneren2005.dsub.playlists.provider"
+ android:accountType="subsonic.org"
+ android:userVisible="true"
+ android:supportsUploading="false"
+ android:allowParallelSyncs="false"
android:isAlwaysSyncable="true"/> \ No newline at end of file
diff --git a/res/xml/podcasts_syncadapter.xml b/app/src/main/res/xml/podcasts_syncadapter.xml
index 21f421f6..52340ae4 100644
--- a/res/xml/podcasts_syncadapter.xml
+++ b/app/src/main/res/xml/podcasts_syncadapter.xml
@@ -1,8 +1,8 @@
-<?xml version="1.0" encoding="utf-8"?>
-<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
- android:contentAuthority="github.daneren2005.dsub.podcasts.provider"
- android:accountType="subsonic.org"
- android:userVisible="true"
- android:supportsUploading="false"
- android:allowParallelSyncs="false"
+<?xml version="1.0" encoding="utf-8"?>
+<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
+ android:contentAuthority="github.daneren2005.dsub.podcasts.provider"
+ android:accountType="subsonic.org"
+ android:userVisible="true"
+ android:supportsUploading="false"
+ android:allowParallelSyncs="false"
android:isAlwaysSyncable="true"/> \ No newline at end of file
diff --git a/res/xml/searchable.xml b/app/src/main/res/xml/searchable.xml
index 35ff18f3..35ff18f3 100644
--- a/res/xml/searchable.xml
+++ b/app/src/main/res/xml/searchable.xml
diff --git a/res/xml/settings.xml b/app/src/main/res/xml/settings.xml
index b1cbdd8c..b1cbdd8c 100644
--- a/res/xml/settings.xml
+++ b/app/src/main/res/xml/settings.xml
diff --git a/res/xml/starred_syncadapter.xml b/app/src/main/res/xml/starred_syncadapter.xml
index 4d065e93..d1d0e1e2 100644
--- a/res/xml/starred_syncadapter.xml
+++ b/app/src/main/res/xml/starred_syncadapter.xml
@@ -1,8 +1,8 @@
-<?xml version="1.0" encoding="utf-8"?>
-<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
- android:contentAuthority="github.daneren2005.dsub.starred.provider"
- android:accountType="subsonic.org"
- android:userVisible="true"
- android:supportsUploading="false"
- android:allowParallelSyncs="false"
+<?xml version="1.0" encoding="utf-8"?>
+<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
+ android:contentAuthority="github.daneren2005.dsub.starred.provider"
+ android:accountType="subsonic.org"
+ android:userVisible="true"
+ android:supportsUploading="false"
+ android:allowParallelSyncs="false"
android:isAlwaysSyncable="true"/> \ No newline at end of file
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 00000000..5f7c1799
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,19 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:1.1.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
diff --git a/build.properties b/build.properties
deleted file mode 100644
index e69de29b..00000000
--- a/build.properties
+++ /dev/null
diff --git a/build.xml b/build.xml
deleted file mode 100644
index 6d0d026f..00000000
--- a/build.xml
+++ /dev/null
@@ -1,92 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project name="subsonic-android" default="help">
-
- <!-- The local.properties file is created and updated by the 'android' tool.
- It contains the path to the SDK. It should *NOT* be checked into
- Version Control Systems. -->
- <property file="local.properties" />
-
- <!-- The ant.properties file can be created by you. It is only edited by the
- 'android' tool to add properties to it.
- This is the place to change some Ant specific build properties.
- Here are some properties you may want to change/update:
-
- source.dir
- The name of the source directory. Default is 'src'.
- out.dir
- The name of the output directory. Default is 'bin'.
-
- For other overridable properties, look at the beginning of the rules
- files in the SDK, at tools/ant/build.xml
-
- Properties related to the SDK location or the project target should
- be updated using the 'android' tool with the 'update' action.
-
- This file is an integral part of the build system for your
- application and should be checked into Version Control Systems.
-
- -->
- <property file="ant.properties" />
-
- <!-- if sdk.dir was not set from one of the property file, then
- get it from the ANDROID_HOME env var.
- This must be done before we load project.properties since
- the proguard config can use sdk.dir -->
- <property environment="env" />
- <condition property="sdk.dir" value="${env.ANDROID_HOME}">
- <isset property="env.ANDROID_HOME" />
- </condition>
-
- <!-- The project.properties file is created and updated by the 'android'
- tool, as well as ADT.
-
- This contains project specific properties such as project target, and library
- dependencies. Lower level build properties are stored in ant.properties
- (or in .classpath for Eclipse projects).
-
- This file is an integral part of the build system for your
- application and should be checked into Version Control Systems. -->
- <loadproperties srcFile="project.properties" />
-
- <!-- quick check on sdk.dir -->
- <fail
- message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
- unless="sdk.dir"
- />
-
- <!--
- Import per project custom build rules if present at the root of the project.
- This is the place to put custom intermediary targets such as:
- -pre-build
- -pre-compile
- -post-compile (This is typically used for code obfuscation.
- Compiled code location: ${out.classes.absolute.dir}
- If this is not done in place, override ${out.dex.input.absolute.dir})
- -post-package
- -post-build
- -pre-clean
- -->
- <import file="custom_rules.xml" optional="true" />
-
- <!-- Import the actual build file.
-
- To customize existing targets, there are two options:
- - Customize only one target:
- - copy/paste the target into this file, *before* the
- <import> task.
- - customize it to your needs.
- - Customize the whole content of build.xml
- - copy/paste the content of the rules files (minus the top node)
- into this file, replacing the <import> task.
- - customize to your needs.
-
- ***********************
- ****** IMPORTANT ******
- ***********************
- In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
- in order to avoid having your file be overridden by tools such as "android update project"
- -->
- <!-- version-tag: 1 -->
- <import file="${sdk.dir}/tools/ant/build.xml" />
-
-</project>
diff --git a/default.properties b/default.properties
deleted file mode 100644
index e69de29b..00000000
--- a/default.properties
+++ /dev/null
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 00000000..1d3591c8
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,18 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+# Default value: -Xmx10248m -XX:MaxPermSize=256m
+# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true \ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 00000000..8c0fb64a
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000..0c71e760
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Apr 10 15:27:10 PDT 2013
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/gradlew b/gradlew
new file mode 100644
index 00000000..91a7e269
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 00000000..8a0b282a
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/local.properties b/local.properties
new file mode 100644
index 00000000..90a868a9
--- /dev/null
+++ b/local.properties
@@ -0,0 +1,10 @@
+## This file is automatically generated by Android Studio.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file should *NOT* be checked into Version Control Systems,
+# as it contains information specific to your local configuration.
+#
+# Location of the SDK. This is only used by Gradle.
+# For customization when using a Version Control System, please read the
+# header note.
+sdk.dir=C\:\\Program Files (x86)\\Android\\android-sdk \ No newline at end of file
diff --git a/project.properties b/project.properties
deleted file mode 100644
index cfa8bb97..00000000
--- a/project.properties
+++ /dev/null
@@ -1,16 +0,0 @@
-# This file is automatically generated by Android Tools.
-# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-#
-# This file must be checked in Version Control Systems.
-#
-# To customize properties used by the Ant build system use,
-# "ant.properties", and override values to adapt the script to your
-# project structure.
-
-# Project target.
-target=android-21
-android.library.reference.1=DragSortListView/library
-android.library.reference.2=../../../../Program Files (x86)/Android/android-sdk/extras/android/support/v7/appcompat
-android.library.reference.3=../../../../Program Files (x86)/Android/android-sdk/extras/android/support/v7/mediarouter
-android.library.reference.4=../../../../Program Files (x86)/Android/android-sdk/extras/google/google_play_services/libproject/google-play-services_lib
-android.library.reference.5=ServerProxy \ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 00000000..c2f4d6b6
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,3 @@
+include ':app', ':Server Proxy', ':DragSort ListView'
+project(':Server Proxy').projectDir = new File('ServerProxy')
+project(':DragSort ListView').projectDir = new File('DragSortListView/library') \ No newline at end of file