aboutsummaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/.gitignore1
-rw-r--r--app/app.iml121
-rw-r--r--app/build.gradle13
-rw-r--r--app/src/androidTest/java/github/daneren2005/dsub/activity/DownloadActivityTest.java32
-rw-r--r--app/src/androidTest/java/github/daneren2005/dsub/domain/BookmarkTest.java2
-rw-r--r--app/src/main/AndroidManifest.xml2
-rw-r--r--app/src/main/java/github/daneren2005/dsub/activity/DownloadActivity.java62
-rw-r--r--app/src/main/java/github/daneren2005/dsub/activity/EditPlayActionActivity.java2
-rw-r--r--app/src/main/java/github/daneren2005/dsub/activity/SettingsActivity.java55
-rw-r--r--app/src/main/java/github/daneren2005/dsub/activity/SubsonicActivity.java470
-rw-r--r--app/src/main/java/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java431
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/AlbumGridAdapter.java73
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/AlbumListAdapter.java4
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/ArtistAdapter.java197
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/BasicListAdapter.java48
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/BookmarkAdapter.java68
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/DetailsAdapter.java62
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/DownloadFileAdapter.java37
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/DrawerAdapter.java126
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/EntryAdapter.java82
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/EntryGridAdapter.java145
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/EntryInfiniteGridAdapter.java146
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/GenreAdapter.java80
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/MainAdapter.java112
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/MergeAdapter.java290
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/PlaylistAdapter.java93
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/PodcastChannelAdapter.java79
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/SackOfViewsAdapter.java181
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/SearchAdapter.java129
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/SectionAdapter.java463
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/SettingsAdapter.java77
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/ShareAdapter.java75
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/UserAdapter.java34
-rw-r--r--app/src/main/java/github/daneren2005/dsub/domain/Playlist.java15
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/AdminFragment.java27
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/DownloadFragment.java111
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/EqualizerFragment.java1
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/LyricsFragment.java1
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/MainFragment.java396
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/NowPlayingFragment.java894
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SearchFragment.java247
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectArtistFragment.java187
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectBookmarkFragment.java76
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java683
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectGenreFragment.java52
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectListFragment.java163
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectPlaylistFragment.java140
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectPodcastsFragment.java111
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectRecyclerFragment.java197
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectShareFragment.java54
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectVideoFragment.java36
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectYearFragment.java55
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SimilarArtistFragment.java48
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java780
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/UserFragment.java106
-rw-r--r--app/src/main/java/github/daneren2005/dsub/provider/DSubWidgetProvider.java5
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/CachedMusicService.java18
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/DownloadService.java139
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/MusicService.java4
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/OfflineMusicService.java56
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java6
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/parser/PlaylistsParser.java3
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/sync/MostRecentSyncAdapter.java6
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/Constants.java2
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/DrawableTint.java90
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/FileUtil.java45
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/ImageLoader.java52
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/MenuUtil.java55
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/Notifications.java3
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/UserUtil.java20
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/Util.java97
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/AlbumCell.java108
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/AlbumListCountView.java130
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/AlbumView.java77
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/ArtistEntryView.java15
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/ArtistView.java13
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/BasicHeaderView.java40
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/BasicListView.java44
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/ChangeLog.java2
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/ErrorDialog.java2
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/GenreView.java5
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/GridSpacingDecoration.java99
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/HeaderGridView.java836
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/PlaylistSongView.java16
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/PlaylistView.java32
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/PodcastChannelView.java11
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/SettingView.java61
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/ShareView.java5
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/SongView.java150
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/UnscrollableGridView.java128
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/UpdateView.java105
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/UpdateView2.java47
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/UserView.java10
-rw-r--r--app/src/main/res/drawable-hdpi-v11/notification_close.pngbin384 -> 0 bytes
-rw-r--r--app/src/main/res/drawable-hdpi-v11/notification_next.pngbin525 -> 0 bytes
-rw-r--r--app/src/main/res/drawable-hdpi-v11/notification_pause.pngbin210 -> 0 bytes
-rw-r--r--app/src/main/res/drawable-hdpi-v11/notification_play.pngbin385 -> 0 bytes
-rw-r--r--app/src/main/res/drawable-hdpi-v11/notification_previous.pngbin541 -> 0 bytes
-rw-r--r--app/src/main/res/drawable-hdpi-v11/stat_notify_download.pngbin300 -> 0 bytes
-rw-r--r--app/src/main/res/drawable-hdpi-v11/stat_notify_playing.pngbin385 -> 0 bytes
-rw-r--r--app/src/main/res/drawable-hdpi-v11/stat_notify_sync.pngbin819 -> 0 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_action_rating_bad_dark.pngbin754 -> 658 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_action_rating_bad_light.pngbin892 -> 747 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_action_rating_bad_selected.pngbin965 -> 559 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_action_rating_good_dark.pngbin744 -> 644 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_action_rating_good_light.pngbin873 -> 758 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_action_rating_good_selected.pngbin921 -> 550 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_menu_bookmark_dark.pngbin1087 -> 458 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_menu_bookmark_light.pngbin1292 -> 506 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_menu_bookmark_selected.pngbin1374 -> 419 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_stat_star.pngbin826 -> 0 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_toggle_star.pngbin0 -> 904 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_toggle_star_outline_dark.pngbin0 -> 985 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_toggle_star_outline_light.pngbin0 -> 1202 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/launch.pngbin7496 -> 5904 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/notification_close.pngbin501 -> 384 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/notification_next.pngbin651 -> 525 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/notification_pause.pngbin459 -> 210 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/notification_play.pngbin599 -> 385 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/notification_previous.pngbin633 -> 541 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/stat_notify_download.pngbin350 -> 300 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/stat_notify_playing.pngbin599 -> 385 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/stat_notify_sync.pngbin894 -> 819 bytes
-rw-r--r--app/src/main/res/drawable-mdpi-v11/notification_close.pngbin241 -> 0 bytes
-rw-r--r--app/src/main/res/drawable-mdpi-v11/notification_next.pngbin341 -> 0 bytes
-rw-r--r--app/src/main/res/drawable-mdpi-v11/notification_pause.pngbin156 -> 0 bytes
-rw-r--r--app/src/main/res/drawable-mdpi-v11/notification_play.pngbin280 -> 0 bytes
-rw-r--r--app/src/main/res/drawable-mdpi-v11/notification_previous.pngbin355 -> 0 bytes
-rw-r--r--app/src/main/res/drawable-mdpi-v11/stat_notify_download.pngbin234 -> 0 bytes
-rw-r--r--app/src/main/res/drawable-mdpi-v11/stat_notify_playing.pngbin280 -> 0 bytes
-rw-r--r--app/src/main/res/drawable-mdpi-v11/stat_notify_sync.pngbin623 -> 0 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_action_rating_bad_dark.pngbin460 -> 434 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_action_rating_bad_light.pngbin543 -> 483 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_action_rating_bad_selected.pngbin584 -> 345 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_action_rating_good_dark.pngbin456 -> 397 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_action_rating_good_light.pngbin541 -> 453 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_action_rating_good_selected.pngbin581 -> 326 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_menu_bookmark_dark.pngbin658 -> 266 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_menu_bookmark_light.pngbin782 -> 295 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_menu_bookmark_selected.pngbin849 -> 253 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_toggle_star.pngbin0 -> 612 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_toggle_star_outline_dark.pngbin0 -> 622 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_toggle_star_outline_light.pngbin0 -> 767 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/launch.pngbin4077 -> 3591 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/notification_close.pngbin337 -> 241 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/notification_next.pngbin460 -> 341 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/notification_pause.pngbin361 -> 156 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/notification_play.pngbin417 -> 280 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/notification_previous.pngbin476 -> 355 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/stat_notify_download.pngbin272 -> 234 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/stat_notify_playing.pngbin417 -> 280 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/stat_notify_sync.pngbin575 -> 623 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi-v11/notification_close.pngbin491 -> 0 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi-v11/notification_next.pngbin731 -> 0 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi-v11/notification_pause.pngbin257 -> 0 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi-v11/notification_play.pngbin493 -> 0 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi-v11/notification_previous.pngbin750 -> 0 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi-v11/stat_notify_download.pngbin379 -> 0 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi-v11/stat_notify_playing.pngbin493 -> 0 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi-v11/stat_notify_sync.pngbin1205 -> 0 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_action_rating_bad_dark.pngbin961 -> 820 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_action_rating_bad_light.pngbin1141 -> 942 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_action_rating_bad_selected.pngbin1197 -> 655 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_action_rating_good_dark.pngbin946 -> 792 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_action_rating_good_light.pngbin1129 -> 919 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_action_rating_good_selected.pngbin1176 -> 648 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_menu_bookmark_dark.pngbin1442 -> 558 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_menu_bookmark_light.pngbin1665 -> 616 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_menu_bookmark_selected.pngbin1780 -> 511 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_toggle_star.pngbin0 -> 1260 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_toggle_star_outline_dark.pngbin0 -> 1309 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_toggle_star_outline_light.pngbin0 -> 1548 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/launch.pngbin10916 -> 8222 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/notification_close.pngbin538 -> 491 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/notification_next.pngbin886 -> 731 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/notification_pause.pngbin529 -> 257 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/notification_play.pngbin753 -> 493 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/notification_previous.pngbin891 -> 750 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/stat_notify_download.pngbin404 -> 379 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/stat_notify_playing.pngbin753 -> 493 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/stat_notify_sync.pngbin1058 -> 1205 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi-v11/notification_close.pngbin712 -> 0 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi-v11/notification_next.pngbin1105 -> 0 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi-v11/notification_pause.pngbin358 -> 0 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi-v11/notification_play.pngbin781 -> 0 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi-v11/notification_previous.pngbin1143 -> 0 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi-v11/stat_notify_download.pngbin531 -> 0 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi-v11/stat_notify_playing.pngbin781 -> 0 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi-v11/stat_notify_sync.pngbin2198 -> 0 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_action_rating_bad_dark.pngbin1540 -> 1202 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_action_rating_bad_light.pngbin1822 -> 1311 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_action_rating_bad_selected.pngbin1953 -> 1018 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_action_rating_good_dark.pngbin1582 -> 1197 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_action_rating_good_light.pngbin1835 -> 1372 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_action_rating_good_selected.pngbin1915 -> 1085 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_menu_bookmark_dark.pngbin2194 -> 881 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_menu_bookmark_light.pngbin2474 -> 1012 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_menu_bookmark_selected.pngbin2635 -> 778 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_toggle_star.pngbin0 -> 1930 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_toggle_star_outline_dark.pngbin0 -> 2088 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_toggle_star_outline_light.pngbin0 -> 2459 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/launch.pngbin20218 -> 13950 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/notification_close.pngbin1081 -> 712 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/notification_next.pngbin1292 -> 1105 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/notification_pause.pngbin724 -> 358 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/notification_play.pngbin1125 -> 781 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/notification_previous.pngbin1261 -> 1143 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/stat_notify_download.pngbin558 -> 531 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/stat_notify_playing.pngbin1125 -> 781 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/stat_notify_sync.pngbin1932 -> 2198 bytes
-rw-r--r--app/src/main/res/drawable-xxxhdpi/ic_action_rating_bad_dark.pngbin0 -> 1652 bytes
-rw-r--r--app/src/main/res/drawable-xxxhdpi/ic_action_rating_bad_light.pngbin0 -> 1859 bytes
-rw-r--r--app/src/main/res/drawable-xxxhdpi/ic_action_rating_bad_selected.pngbin0 -> 1558 bytes
-rw-r--r--app/src/main/res/drawable-xxxhdpi/ic_action_rating_good_dark.pngbin0 -> 1802 bytes
-rw-r--r--app/src/main/res/drawable-xxxhdpi/ic_action_rating_good_light.pngbin0 -> 2066 bytes
-rw-r--r--app/src/main/res/drawable-xxxhdpi/ic_action_rating_good_selected.pngbin0 -> 1695 bytes
-rw-r--r--app/src/main/res/drawable-xxxhdpi/ic_menu_bookmark_dark.pngbin0 -> 1296 bytes
-rw-r--r--app/src/main/res/drawable-xxxhdpi/ic_menu_bookmark_light.pngbin0 -> 1394 bytes
-rw-r--r--app/src/main/res/drawable-xxxhdpi/ic_menu_bookmark_selected.pngbin0 -> 1255 bytes
-rw-r--r--app/src/main/res/drawable-xxxhdpi/ic_toggle_star.pngbin0 -> 2798 bytes
-rw-r--r--app/src/main/res/drawable-xxxhdpi/ic_toggle_star_outline_dark.pngbin0 -> 3135 bytes
-rw-r--r--app/src/main/res/drawable-xxxhdpi/ic_toggle_star_outline_light.pngbin0 -> 3684 bytes
-rw-r--r--app/src/main/res/drawable/drawer_header.jpgbin0 -> 35612 bytes
-rw-r--r--app/src/main/res/layout-large-land/abstract_fragment_container.xml4
-rw-r--r--app/src/main/res/layout/abstract_activity.xml19
-rw-r--r--app/src/main/res/layout/abstract_fragment_activity.xml201
-rw-r--r--app/src/main/res/layout/abstract_fragment_container.xml4
-rw-r--r--app/src/main/res/layout/abstract_recycler_fragment.xml (renamed from app/src/main/res/layout/abstract_list_fragment.xml)11
-rw-r--r--app/src/main/res/layout/actionbar_spinner.xml21
-rw-r--r--app/src/main/res/layout/album_cell_item.xml11
-rw-r--r--app/src/main/res/layout/album_list_header.xml29
-rw-r--r--app/src/main/res/layout/album_list_item.xml17
-rw-r--r--app/src/main/res/layout/appwidget4x1.xml8
-rw-r--r--app/src/main/res/layout/appwidget4x2.xml8
-rw-r--r--app/src/main/res/layout/appwidget4x3.xml8
-rw-r--r--app/src/main/res/layout/appwidget4x4.xml8
-rw-r--r--app/src/main/res/layout/basic_choice_item.xml27
-rw-r--r--app/src/main/res/layout/basic_count_item.xml6
-rw-r--r--app/src/main/res/layout/basic_header.xml13
-rw-r--r--app/src/main/res/layout/basic_list_item.xml11
-rw-r--r--app/src/main/res/layout/change_email.xml3
-rw-r--r--app/src/main/res/layout/change_password.xml3
-rw-r--r--app/src/main/res/layout/chat_item.xml11
-rw-r--r--app/src/main/res/layout/chat_item_reverse.xml11
-rw-r--r--app/src/main/res/layout/complex_list_item.xml20
-rw-r--r--app/src/main/res/layout/confirm_password.xml3
-rw-r--r--app/src/main/res/layout/create_bookmark.xml3
-rw-r--r--app/src/main/res/layout/create_podcast.xml3
-rw-r--r--app/src/main/res/layout/create_user.xml14
-rw-r--r--app/src/main/res/layout/details_item.xml28
-rw-r--r--app/src/main/res/layout/download_activity.xml4
-rw-r--r--app/src/main/res/layout/download_playlist.xml34
-rw-r--r--app/src/main/res/layout/drawer_header.xml58
-rw-r--r--app/src/main/res/layout/drawer_list_item.xml26
-rw-r--r--app/src/main/res/layout/edit_play_action.xml15
-rw-r--r--app/src/main/res/layout/equalizer_bar.xml55
-rw-r--r--app/src/main/res/layout/genre_list_item.xml11
-rw-r--r--app/src/main/res/layout/grid_view.xml14
-rw-r--r--app/src/main/res/layout/home.xml2
-rw-r--r--app/src/main/res/layout/jukebox_volume.xml46
-rw-r--r--app/src/main/res/layout/lyrics.xml101
-rw-r--r--app/src/main/res/layout/main_buttons.xml157
-rw-r--r--app/src/main/res/layout/notification.xml6
-rw-r--r--app/src/main/res/layout/notification_expanded.xml8
-rw-r--r--app/src/main/res/layout/playlist_cell_item.xml39
-rw-r--r--app/src/main/res/layout/playlist_list_item.xml34
-rw-r--r--app/src/main/res/layout/progress.xml30
-rw-r--r--app/src/main/res/layout/save_playlist.xml25
-rw-r--r--app/src/main/res/layout/search_buttons.xml73
-rw-r--r--app/src/main/res/layout/seekbar_preference.xml3
-rw-r--r--app/src/main/res/layout/select_album.xml7
-rw-r--r--app/src/main/res/layout/select_album_header.xml21
-rw-r--r--app/src/main/res/layout/select_artist_header.xml15
-rw-r--r--app/src/main/res/layout/settings_activity.xml21
-rw-r--r--app/src/main/res/layout/shuffle_dialog.xml9
-rw-r--r--app/src/main/res/layout/song_list_item.xml49
-rw-r--r--app/src/main/res/layout/start_timer.xml3
-rw-r--r--app/src/main/res/layout/sync_dialog.xml3
-rw-r--r--app/src/main/res/layout/tab_progress.xml3
-rw-r--r--app/src/main/res/layout/unscrollable_grid_view.xml11
-rw-r--r--app/src/main/res/layout/update_playlist.xml9
-rw-r--r--app/src/main/res/layout/update_share.xml9
-rw-r--r--app/src/main/res/layout/user_header.xml12
-rw-r--r--app/src/main/res/layout/user_list_item.xml11
-rw-r--r--app/src/main/res/menu/abstract_top_menu.xml2
-rw-r--r--app/src/main/res/menu/drawer_navigation.xml51
-rw-r--r--app/src/main/res/menu/main.xml4
-rw-r--r--app/src/main/res/menu/multiselect_media.xml47
-rw-r--r--app/src/main/res/menu/multiselect_media_offline.xml31
-rw-r--r--app/src/main/res/menu/nowplaying_context.xml4
-rw-r--r--app/src/main/res/menu/nowplaying_context_offline.xml4
-rw-r--r--app/src/main/res/menu/search.xml2
-rw-r--r--app/src/main/res/menu/select_album.xml2
-rw-r--r--app/src/main/res/menu/select_album_context.xml40
-rw-r--r--app/src/main/res/menu/select_album_context_offline.xml30
-rw-r--r--app/src/main/res/menu/select_album_list.xml2
-rw-r--r--app/src/main/res/menu/select_artist.xml4
-rw-r--r--app/src/main/res/menu/select_artist_context.xml40
-rw-r--r--app/src/main/res/menu/select_artist_context_offline.xml30
-rw-r--r--app/src/main/res/menu/select_bookmark_context.xml12
-rw-r--r--app/src/main/res/menu/select_playlist_context.xml15
-rw-r--r--app/src/main/res/menu/select_playlist_context_offline.xml14
-rw-r--r--app/src/main/res/menu/select_podcast_episode_context.xml12
-rw-r--r--app/src/main/res/menu/select_podcast_episode_context_offline.xml8
-rw-r--r--app/src/main/res/menu/select_podcasts.xml2
-rw-r--r--app/src/main/res/menu/select_song.xml10
-rw-r--r--app/src/main/res/menu/select_song_context.xml31
-rw-r--r--app/src/main/res/menu/select_song_context_offline.xml21
-rw-r--r--app/src/main/res/menu/select_song_offline.xml2
-rw-r--r--app/src/main/res/menu/similar_artists.xml2
-rw-r--r--app/src/main/res/values-de/strings.xml83
-rw-r--r--app/src/main/res/values-es/strings.xml15
-rw-r--r--app/src/main/res/values-fr/strings.xml13
-rw-r--r--app/src/main/res/values-hu/strings.xml13
-rw-r--r--app/src/main/res/values-large/dimens.xml2
-rw-r--r--app/src/main/res/values-ru/strings.xml11
-rw-r--r--app/src/main/res/values-v11/colors.xml5
-rw-r--r--app/src/main/res/values-v11/styles.xml6
-rw-r--r--app/src/main/res/values-v16/themes.xml15
-rw-r--r--app/src/main/res/values-v21/themes.xml10
-rw-r--r--app/src/main/res/values/arrays.xml39
-rw-r--r--app/src/main/res/values/attrs.xml20
-rw-r--r--app/src/main/res/values/colors.xml21
-rw-r--r--app/src/main/res/values/dimens.xml3
-rw-r--r--app/src/main/res/values/strings.xml68
-rw-r--r--app/src/main/res/values/styles.xml60
-rw-r--r--app/src/main/res/values/themes.xml129
327 files changed, 6399 insertions, 6674 deletions
diff --git a/app/.gitignore b/app/.gitignore
index 796b96d1..e8fa30f8 100644
--- a/app/.gitignore
+++ b/app/.gitignore
@@ -1 +1,2 @@
/build
+*.iml
diff --git a/app/app.iml b/app/app.iml
deleted file mode 100644
index 28b7ee49..00000000
--- a/app/app.iml
+++ /dev/null
@@ -1,121 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<module external.linked.project.id=":app" 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="SOURCE_GEN_TASK_NAME" value="generateDebugSources" />
- <option name="ASSEMBLE_TEST_TASK_NAME" value="assembleDebugAndroidTest" />
- <option name="COMPILE_JAVA_TEST_TASK_NAME" value="compileDebugAndroidTestSources" />
- <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/exploded-aar/com.android.support/appcompat-v7/22.1.1/jars" />
- <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/mediarouter-v7/22.1.1/jars" />
- <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v4/22.1.1/jars" />
- <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.google.android.gms/play-services-base/7.0.0/jars" />
- <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.google.android.gms/play-services-cast/7.0.0/jars" />
- <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="jetty-continuation-8.1.16.v20140903" level="project" />
- <orderEntry type="library" exported="" name="javax.servlet-3.0.0.v201112011016" level="project" />
- <orderEntry type="library" exported="" name="jetty-server-8.1.16.v20140903" 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="jetty-util-8.1.16.v20140903" level="project" />
- <orderEntry type="library" exported="" name="appcompat-v7-22.1.1" level="project" />
- <orderEntry type="library" exported="" name="seamless-swing-1.1.0" level="project" />
- <orderEntry type="library" exported="" name="seamless-xml-1.1.0" level="project" />
- <orderEntry type="library" exported="" name="jetty-io-8.1.16.v20140903" level="project" />
- <orderEntry type="library" exported="" name="CWAC-EndlessAdapter" level="project" />
- <orderEntry type="library" exported="" name="jetty-security-8.1.16.v20140903" 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-servlet-8.1.16.v20140903" level="project" />
- <orderEntry type="library" exported="" name="CWAC-AdapterWrapper" level="project" />
- <orderEntry type="library" exported="" name="jetty-client-8.1.16.v20140903" level="project" />
- <orderEntry type="library" exported="" name="jetty-http-8.1.16.v20140903" level="project" />
- <orderEntry type="module" module-name="DragSort ListView" exported="" />
- <orderEntry type="module" module-name="Server Proxy" exported="" />
- </component>
-</module> \ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index 0eeaec1d..c09f0f91 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -6,7 +6,7 @@ android {
defaultConfig {
applicationId "github.daneren2005.dsub"
- minSdkVersion 9
+ minSdkVersion 14
targetSdkVersion 19
}
buildTypes {
@@ -32,13 +32,16 @@ android {
}
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.android.support:support-v4:22.2.+'
+ compile 'com.android.support:appcompat-v7:22.2.+'
+ compile 'com.android.support:mediarouter-v7:22.2.+'
+ compile 'com.android.support:recyclerview-v7:22.2.+'
+ compile 'com.android.support:design:22.2.+'
compile 'com.google.android.gms:play-services-cast:7.0.0'
+ compile 'com.sothree.slidinguppanel:library:3.0.0'
+ compile 'de.hdodenhof:circleimageview:1.2.1'
compile group: 'org.fourthline.cling', name: 'cling-core', version:'2.0.1'
compile group: 'org.fourthline.cling', name: 'cling-support', version:'2.0.1'
compile group: 'org.eclipse.jetty', name: 'jetty-server', version:'8.1.16.v20140903'
diff --git a/app/src/androidTest/java/github/daneren2005/dsub/activity/DownloadActivityTest.java b/app/src/androidTest/java/github/daneren2005/dsub/activity/DownloadActivityTest.java
deleted file mode 100644
index ce859181..00000000
--- a/app/src/androidTest/java/github/daneren2005/dsub/activity/DownloadActivityTest.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package github.daneren2005.dsub.activity;
-
-import github.daneren2005.dsub.R;
-import android.test.*;
-import android.view.View;
-
-public class DownloadActivityTest extends
- ActivityInstrumentationTestCase2<DownloadActivity> {
-
- private DownloadActivity activity;
-
- public DownloadActivityTest() {
- super(DownloadActivity.class);
- }
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- activity = getActivity();
- }
-
- /**
- * Test the main layout.
- */
- public void testLayout() {
- View view = activity.findViewById(R.layout.download_activity);
- assertNotNull(view);
- assertNotNull(view.findViewById(R.layout.download_activity));
- assertNotNull(activity.findViewById(R.id.fragment_container));
- }
-
-}
diff --git a/app/src/androidTest/java/github/daneren2005/dsub/domain/BookmarkTest.java b/app/src/androidTest/java/github/daneren2005/dsub/domain/BookmarkTest.java
index 814f658a..30663543 100644
--- a/app/src/androidTest/java/github/daneren2005/dsub/domain/BookmarkTest.java
+++ b/app/src/androidTest/java/github/daneren2005/dsub/domain/BookmarkTest.java
@@ -15,7 +15,7 @@ public class BookmarkTest extends TestCase {
*/
public void testSetCreated() throws ParseException {
Bookmark bookmark = new Bookmark();
- bookmark.setCreated(null);
+ bookmark.setCreated((String) null);
assertEquals(null, bookmark.getCreated());
bookmark.setCreated("");
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 38d80ae8..2b3a5fbc 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -31,7 +31,7 @@
<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"/>
+ <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="19"/>
<supports-screens android:anyDensity="true" android:xlargeScreens="true" android:largeScreens="true" android:normalScreens="true" android:smallScreens="true"/>
diff --git a/app/src/main/java/github/daneren2005/dsub/activity/DownloadActivity.java b/app/src/main/java/github/daneren2005/dsub/activity/DownloadActivity.java
deleted file mode 100644
index e13a8b8c..00000000
--- a/app/src/main/java/github/daneren2005/dsub/activity/DownloadActivity.java
+++ /dev/null
@@ -1,62 +0,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.activity;
-
-import github.daneren2005.dsub.R;
-import android.os.Bundle;
-import android.view.MotionEvent;
-import github.daneren2005.dsub.fragments.NowPlayingFragment;
-
-import android.widget.EditText;
-
-import github.daneren2005.dsub.util.Constants;
-
-public class DownloadActivity extends SubsonicActivity {
- private static final String TAG = DownloadActivity.class.getSimpleName();
- private EditText playlistNameView;
-
- /**
- * Called when the activity is first created.
- */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.download_activity);
-
- if (findViewById(R.id.fragment_container) != null && savedInstanceState == null) {
- currentFragment = new NowPlayingFragment();
- if(getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD_VIEW)) {
- Bundle args = new Bundle();
- args.putBoolean(Constants.INTENT_EXTRA_NAME_DOWNLOAD_VIEW, true);
- currentFragment.setArguments(args);
- }
- currentFragment.setPrimaryFragment(true);
- getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, currentFragment, currentFragment.getSupportTag() + "").commit();
- }
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent me) {
- if(currentFragment != null && currentFragment.getGestureDetector() != null) {
- return currentFragment.getGestureDetector().onTouchEvent(me);
- } else {
- return false;
- }
- }
-}
diff --git a/app/src/main/java/github/daneren2005/dsub/activity/EditPlayActionActivity.java b/app/src/main/java/github/daneren2005/dsub/activity/EditPlayActionActivity.java
index e1f2cad3..0396f8a4 100644
--- a/app/src/main/java/github/daneren2005/dsub/activity/EditPlayActionActivity.java
+++ b/app/src/main/java/github/daneren2005/dsub/activity/EditPlayActionActivity.java
@@ -16,7 +16,7 @@
package github.daneren2005.dsub.activity;
import android.app.Activity;
-import android.app.AlertDialog;
+import android.support.v7.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
diff --git a/app/src/main/java/github/daneren2005/dsub/activity/SettingsActivity.java b/app/src/main/java/github/daneren2005/dsub/activity/SettingsActivity.java
index d5ac60d3..595529b7 100644
--- a/app/src/main/java/github/daneren2005/dsub/activity/SettingsActivity.java
+++ b/app/src/main/java/github/daneren2005/dsub/activity/SettingsActivity.java
@@ -19,61 +19,25 @@
package github.daneren2005.dsub.activity;
import android.annotation.TargetApi;
-import android.accounts.Account;
-import android.content.ContentResolver;
-import android.content.Context;
-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.preference.CheckBoxPreference;
-import android.preference.EditTextPreference;
-import android.preference.ListPreference;
-import android.preference.Preference;
-import android.preference.PreferenceCategory;
-import android.preference.PreferenceScreen;
-import android.support.v7.app.ActionBarActivity;
-import android.text.InputType;
-import android.util.Log;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.FrameLayout;
+import android.support.v7.widget.Toolbar;
import github.daneren2005.dsub.R;
import github.daneren2005.dsub.fragments.PreferenceCompatFragment;
import github.daneren2005.dsub.fragments.SettingsFragment;
-import github.daneren2005.dsub.service.DownloadService;
-import github.daneren2005.dsub.service.MusicService;
-import github.daneren2005.dsub.service.MusicServiceFactory;
import github.daneren2005.dsub.util.Constants;
-import github.daneren2005.dsub.util.LoadingTask;
-import github.daneren2005.dsub.util.SyncUtil;
-import github.daneren2005.dsub.view.ErrorDialog;
-import github.daneren2005.dsub.util.FileUtil;
-import github.daneren2005.dsub.util.Util;
-
-import java.io.File;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.net.URL;
-import java.text.DecimalFormat;
-import java.util.LinkedHashMap;
-import java.util.Map;
public class SettingsActivity extends SubsonicActivity {
- private static final String TAG = SettingsActivity.class.getSimpleName();
+ private static final String TAG = SettingsActivity.class.getSimpleName();
private PreferenceCompatFragment fragment;
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.download_activity);
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ lastSelectedPosition = R.id.drawer_settings;
+ setContentView(R.layout.settings_activity);
if (savedInstanceState == null) {
fragment = new SettingsFragment();
@@ -87,5 +51,8 @@ public class SettingsActivity extends SubsonicActivity {
currentFragment.setPrimaryFragment(true);
getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, currentFragment, currentFragment.getSupportTag() + "").commit();
}
- }
+
+ Toolbar mainToolbar = (Toolbar) findViewById(R.id.main_toolbar);
+ setSupportActionBar(mainToolbar);
+ }
}
diff --git a/app/src/main/java/github/daneren2005/dsub/activity/SubsonicActivity.java b/app/src/main/java/github/daneren2005/dsub/activity/SubsonicActivity.java
index 4651eb0b..92ecf930 100644
--- a/app/src/main/java/github/daneren2005/dsub/activity/SubsonicActivity.java
+++ b/app/src/main/java/github/daneren2005/dsub/activity/SubsonicActivity.java
@@ -25,17 +25,19 @@ 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.os.Handler;
+import android.support.design.widget.NavigationView;
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.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -51,14 +53,13 @@ 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.ImageView;
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;
@@ -67,21 +68,23 @@ 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.DrawableTint;
import github.daneren2005.dsub.util.ImageLoader;
+import github.daneren2005.dsub.util.SilentBackgroundTask;
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 {
+public class SubsonicActivity extends AppCompatActivity 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 static final int MENU_GROUP_SERVER = 10;
+ private static final int MENU_ITEM_SERVER_BASE = 100;
+
+ private final List<Runnable> afterServiceAvailable = new ArrayList<>();
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>();
@@ -90,16 +93,22 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
protected View secondaryContainer;
protected boolean tv = false;
protected boolean touchscreen = true;
+ protected Handler handler = new Handler();
Spinner actionBarSpinner;
ArrayAdapter<CharSequence> spinnerAdapter;
ViewGroup rootView;
DrawerLayout drawer;
ActionBarDrawerToggle drawerToggle;
- DrawerAdapter drawerAdapter;
- ListView drawerList;
- TextView lastSelectedView = null;
+ NavigationView drawerList;
+ View drawerHeader;
+ ImageView drawerUserAvatar;
+ ImageView drawerHeaderToggle;
+ TextView drawerServerName;
+ TextView drawerUserName;
int lastSelectedPosition = 0;
+ boolean showingTabs = true;
boolean drawerOpen = false;
+ SharedPreferences.OnSharedPreferenceChangeListener preferencesListener;
@Override
protected void onCreate(Bundle bundle) {
@@ -118,26 +127,49 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
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);
}
+
+ if(preferencesListener == null) {
+ preferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ // When changing drawer settings change visibility
+ switch(key) {
+ case Constants.PREFERENCES_KEY_PODCASTS_ENABLED:
+ setDrawerItemVisible(R.id.drawer_podcasts, false);
+ break;
+ case Constants.PREFERENCES_KEY_BOOKMARKS_ENABLED:
+ setDrawerItemVisible(R.id.drawer_bookmarks, false);
+ break;
+ case Constants.PREFERENCES_KEY_SHARED_ENABLED:
+ setDrawerItemVisible(R.id.drawer_shares, false);
+ break;
+ case Constants.PREFERENCES_KEY_CHAT_ENABLED:
+ setDrawerItemVisible(R.id.drawer_chat, false);
+ break;
+ case Constants.PREFERENCES_KEY_ADMIN_ENABLED:
+ setDrawerItemVisible(R.id.drawer_admin, false);
+ break;
+ }
+ }
+ };
+ Util.getPreferences(this).registerOnSharedPreferenceChangeListener(preferencesListener);
+ }
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
+
+ if(spinnerAdapter == null) {
+ createCustomActionBarView();
+ }
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setHomeButtonEnabled(true);
+
// Sync the toggle state after onRestoreInstanceState has occurred.
if(drawerToggle != null) {
drawerToggle.syncState();
@@ -150,6 +182,17 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
}
}
+ protected void createCustomActionBarView() {
+ View customActionbar = getLayoutInflater().inflate(R.layout.actionbar_spinner, null);
+ actionBarSpinner = (Spinner)customActionbar.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(customActionbar);
+ }
+
@Override
protected void onResume() {
super.onResume();
@@ -159,9 +202,10 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
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);
+ DrawableTint.wipeTintCache();
}
-
- populateDrawer();
+
+ populateTabs();
UpdateView.addActiveActivity();
}
@@ -176,6 +220,7 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
protected void onDestroy() {
super.onDestroy();
destroyed = true;
+ Util.getPreferences(this).unregisterOnSharedPreferenceChangeListener(preferencesListener);
}
@Override
@@ -185,19 +230,6 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
}
@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);
@@ -210,34 +242,101 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
LayoutInflater layoutInflater = getLayoutInflater();
layoutInflater.inflate(viewId, rootView);
}
-
- drawerList = (ListView) findViewById(R.id.left_drawer);
- drawerList.setOnItemClickListener(new ListView.OnItemClickListener() {
+
+ drawerList = (NavigationView) findViewById(R.id.left_drawer);
+ drawerList.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
+ @Override
+ public boolean onNavigationItemSelected(final MenuItem menuItem) {
+ if(showingTabs) {
+ // Settings are on a different selectable track
+ if (menuItem.getItemId() != R.id.drawer_settings && menuItem.getItemId() != R.id.drawer_admin) {
+ menuItem.setChecked(true);
+ lastSelectedPosition = menuItem.getItemId();
+ }
+
+ switch (menuItem.getItemId()) {
+ case R.id.drawer_home:
+ drawerItemSelected("Home");
+ return true;
+ case R.id.drawer_library:
+ drawerItemSelected("Artist");
+ return true;
+ case R.id.drawer_playlists:
+ drawerItemSelected("Playlist");
+ return true;
+ case R.id.drawer_podcasts:
+ drawerItemSelected("Podcast");
+ return true;
+ case R.id.drawer_bookmarks:
+ drawerItemSelected("Bookmark");
+ return true;
+ case R.id.drawer_shares:
+ drawerItemSelected("Share");
+ return true;
+ case R.id.drawer_chat:
+ drawerItemSelected("Chat");
+ return true;
+ case R.id.drawer_admin:
+ if (UserUtil.isCurrentAdmin()) {
+ UserUtil.confirmCredentials(SubsonicActivity.this, new Runnable() {
+ @Override
+ public void run() {
+ drawerItemSelected("Admin");
+ menuItem.setChecked(true);
+ lastSelectedPosition = menuItem.getItemId();
+ }
+ });
+ } else {
+ drawerItemSelected("Admin");
+ menuItem.setChecked(true);
+ lastSelectedPosition = menuItem.getItemId();
+ }
+ return true;
+ case R.id.drawer_downloading:
+ drawerItemSelected("Download");
+ return true;
+ case R.id.drawer_settings:
+ startActivity(new Intent(SubsonicActivity.this, SettingsActivity.class));
+ drawer.closeDrawers();
+ return true;
+ }
+ } else {
+ int activeServer = menuItem.getItemId() - MENU_ITEM_SERVER_BASE;
+ SubsonicActivity.this.setActiveServer(activeServer);
+ populateTabs();
+ return true;
+ }
+
+ return false;
+ }
+ });
+ populateTabs();
+
+ drawerHeader = drawerList.inflateHeaderView(R.layout.drawer_header);
+ drawerHeader.setOnClickListener(new View.OnClickListener() {
@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);
- }
- });
+ public void onClick(View v) {
+ if(showingTabs) {
+ populateServers();
} else {
- drawerItemSelected(actualPosition, view);
+ populateTabs();
}
}
});
+ drawerHeaderToggle = (ImageView) drawerHeader.findViewById(R.id.header_select_image);
+ drawerServerName = (TextView) drawerHeader.findViewById(R.id.header_server_name);
+ drawerUserName = (TextView) drawerHeader.findViewById(R.id.header_user_name);
+ drawerUserAvatar = (ImageView) drawerHeader.findViewById(R.id.header_user_avatar);
+ updateDrawerHeader();
if(!isTv()) {
drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
- drawerToggle = new ActionBarDrawerToggle(this, drawer, R.string.common_appname, R.string.common_appname) {
+ // Pass in toolbar if it exists
+ Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar);
+ drawerToggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.common_appname, R.string.common_appname) {
@Override
public void onDrawerClosed(View view) {
setTitle(currentFragment.getTitle());
@@ -246,23 +345,19 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
drawerOpen = false;
supportInvalidateOptionsMenu();
+ if(!showingTabs) {
+ populateTabs();
+ }
}
@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);
- }
+ boolean downloadingVisible = downloadService != null && !downloadService.getBackgroundDownloads().isEmpty();
+ if(lastSelectedPosition == R.id.drawer_downloading) {
+ downloadingVisible = true;
}
+ setDrawerItemVisible(R.id.drawer_downloading, downloadingVisible);
getSupportActionBar().setTitle(R.string.common_appname);
getSupportActionBar().setDisplayShowCustomEnabled(false);
@@ -299,7 +394,7 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
primaryContainer = findViewById(R.id.fragment_container);
}
}
-
+
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
@@ -363,6 +458,12 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
}
lastSelectedPosition = savedInstanceState.getInt(Constants.FRAGMENT_POSITION);
+ if(lastSelectedPosition != 0) {
+ MenuItem item = drawerList.getMenu().findItem(lastSelectedPosition);
+ if(item != null) {
+ item.setChecked(true);
+ }
+ }
recreateSpinner();
}
@@ -370,10 +471,11 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
public void onNewIntent(Intent intent) {
super.onNewIntent(intent);
}
-
+
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater menuInflater = getMenuInflater();
+ SubsonicFragment currentFragment = getCurrentFragment();
if(drawerOpen) {
menuInflater.inflate(R.menu.drawer_menu, menu);
} else if(currentFragment != null) {
@@ -399,7 +501,7 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
return true;
}
- return currentFragment.onOptionsItemSelected(item);
+ return getCurrentFragment().onOptionsItemSelected(item);
}
@Override
@@ -415,7 +517,7 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
}
return super.onKeyDown(keyCode, event);
}
-
+
@Override
public void setTitle(CharSequence title) {
if(title != null && !title.equals(getSupportActionBar().getTitle())) {
@@ -426,7 +528,7 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
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;
@@ -439,99 +541,73 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
@Override
public void onNothingSelected(AdapterView<?> parent) {
-
+
}
-
- private void populateDrawer() {
+
+ private void populateTabs() {
+ drawerList.getMenu().clear();
+ drawerList.inflateMenu(R.menu.drawer_navigation);
+
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(!podcastsEnabled) {
+ setDrawerItemVisible(R.id.drawer_podcasts, false);
+ }
+ if(!bookmarksEnabled) {
+ setDrawerItemVisible(R.id.drawer_bookmarks, false);
+ }
+ if(!sharedEnabled) {
+ setDrawerItemVisible(R.id.drawer_shares, false);
+ }
+ if(!chatEnabled) {
+ setDrawerItemVisible(R.id.drawer_chat, false);
+ }
+ if(!adminEnabled) {
+ setDrawerItemVisible(R.id.drawer_admin, false);
+ }
- 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);
- }
+ if(lastSelectedPosition != 0) {
+ MenuItem item = drawerList.getMenu().findItem(lastSelectedPosition);
+ if(item != null) {
+ item.setChecked(true);
}
}
+ showingTabs = true;
}
-
- private void drawerItemSelected(int position, View view) {
- startFragmentActivity(drawerItemsDescriptions[position]);
-
- if(lastSelectedView != view) {
- if(lastSelectedView != null) {
- lastSelectedView.setTextAppearance(this, R.style.DSub_TextViewStyle);
+ private void populateServers() {
+ drawerList.getMenu().clear();
+
+ int serverCount = Util.getServerCount(this);
+ int activeServer = Util.getActiveServer(this);
+ for(int i = 1; i <= serverCount; i++) {
+ MenuItem item = drawerList.getMenu().add(MENU_GROUP_SERVER, MENU_ITEM_SERVER_BASE + i, MENU_ITEM_SERVER_BASE + i, Util.getServerName(this, i));
+ if(activeServer == i) {
+ item.setChecked(true);
}
-
- lastSelectedView = (TextView) view.findViewById(R.id.drawer_name);
- lastSelectedView.setTextAppearance(this, R.style.DSub_TextViewStyle_Bold);
- lastSelectedPosition = position;
+ }
+ drawerList.getMenu().setGroupCheckable(MENU_GROUP_SERVER, true, true);
+
+ showingTabs = false;
+ }
+ private void setDrawerItemVisible(int id, boolean visible) {
+ MenuItem item = drawerList.getMenu().findItem(id);
+ if(item != null) {
+ item.setVisible(visible);
}
}
+ protected void drawerItemSelected(String fragmentType) {
+ if(currentFragment != null) {
+ currentFragment.stopActionMode();
+ }
+ startFragmentActivity(fragmentType);
+ }
+
public void startFragmentActivity(String fragmentType) {
Intent intent = new Intent();
intent.setClass(SubsonicActivity.this, SubsonicFragmentActivity.class);
@@ -539,6 +615,9 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
if(!"".equals(fragmentType)) {
intent.putExtra(Constants.INTENT_EXTRA_FRAGMENT_TYPE, fragmentType);
}
+ if(lastSelectedPosition != 0) {
+ intent.putExtra(Constants.FRAGMENT_POSITION, lastSelectedPosition);
+ }
startActivity(intent);
finish();
}
@@ -575,6 +654,10 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
}
}
+ protected SubsonicFragment getCurrentFragment() {
+ return this.currentFragment;
+ }
+
public void replaceFragment(SubsonicFragment fragment, int tag) {
replaceFragment(fragment, tag, false);
}
@@ -628,11 +711,11 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
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();
}
@@ -656,7 +739,7 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
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);
@@ -682,7 +765,7 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
secondaryContainer.startAnimation(AnimationUtils.loadAnimation(this, R.anim.exit_to_right));
secondaryContainer.setVisibility(View.GONE);
}
-
+
trans.commit();
}
recreateSpinner();
@@ -695,16 +778,19 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
}
currentFragment.invalidate();
- populateDrawer();
+ populateTabs();
}
-
+
supportInvalidateOptionsMenu();
}
-
+
protected void recreateSpinner() {
if(currentFragment == null || currentFragment.getTitle() == null) {
return;
}
+ if(spinnerAdapter == null) {
+ createCustomActionBarView();
+ }
if(backStack.size() > 0) {
spinnerAdapter.clear();
@@ -727,6 +813,7 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
getSupportActionBar().setDisplayShowCustomEnabled(true);
}
} else if(!isTv()) {
+ getSupportActionBar().setTitle(currentFragment.getTitle());
getSupportActionBar().setDisplayShowCustomEnabled(false);
}
}
@@ -735,6 +822,7 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
Intent intent = new Intent(this, ((Object) this).getClass());
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.putExtras(getIntent());
+ intent.putExtra(Constants.FRAGMENT_POSITION, lastSelectedPosition);
Util.startActivityWithoutTransition(this, intent);
}
@@ -745,7 +833,7 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
theme = theme.substring(0, theme.indexOf("_fullscreen"));
Util.setTheme(this, theme);
}
-
+
Util.applyTheme(this, theme);
}
private void applyFullscreen() {
@@ -754,8 +842,8 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
// 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;
+ 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) {
@@ -786,20 +874,35 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
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;
+ break;
}
Log.w(TAG, "DownloadService not running. Attempting to start it.");
startService(new Intent(this, DownloadService.class));
Util.sleepQuietly(50L);
}
- return DownloadService.getInstance();
+
+ final DownloadService downloadService = DownloadService.getInstance();
+ if(downloadService != null && afterServiceAvailable.size() > 0) {
+ for(Runnable runnable: afterServiceAvailable) {
+ handler.post(runnable);
+ }
+ afterServiceAvailable.clear();
+ }
+ return downloadService;
+ }
+ public void runWhenServiceAvailable(Runnable runnable) {
+ if(getDownloadService() != null) {
+ runnable.run();
+ } else {
+ afterServiceAvailable.add(runnable);
+ }
}
-
+
public static String getThemeName() {
return theme;
}
@@ -811,6 +914,49 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
return touchscreen;
}
+ public void openNowPlaying() {
+
+ }
+ public void closeNowPlaying() {
+
+ }
+
+ public void setActiveServer(int instance) {
+ if (Util.getActiveServer(this) != instance) {
+ final DownloadService service = getDownloadService();
+ if (service != null) {
+ new SilentBackgroundTask<Void>(this) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ service.clearIncomplete();
+ return null;
+ }
+ }.execute();
+
+ }
+ Util.setActiveServer(this, instance);
+ invalidate();
+ UserUtil.refreshCurrentUser(this, false, true);
+ updateDrawerHeader();
+ }
+ }
+ public void updateDrawerHeader() {
+ if(Util.isOffline(this)) {
+ drawerServerName.setText(R.string.select_album_offline);
+ drawerUserName.setText("");
+ drawerUserAvatar.setVisibility(View.GONE);
+ drawerHeader.setClickable(false);
+ drawerHeaderToggle.setVisibility(View.GONE);
+ } else {
+ drawerServerName.setText(Util.getServerName(this));
+ drawerUserName.setText(UserUtil.getCurrentUsername(this));
+ drawerUserAvatar.setVisibility(View.VISIBLE);
+ getImageLoader().loadAvatar(this, drawerUserAvatar, UserUtil.getCurrentUsername(this));
+ drawerHeader.setClickable(true);
+ drawerHeaderToggle.setVisibility(View.VISIBLE);
+ }
+ }
+
private void setUncaughtExceptionHandler() {
Thread.UncaughtExceptionHandler handler = Thread.getDefaultUncaughtExceptionHandler();
if (!(handler instanceof SubsonicActivity.SubsonicUncaughtExceptionHandler)) {
diff --git a/app/src/main/java/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java b/app/src/main/java/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java
index 6614e09d..778c4982 100644
--- a/app/src/main/java/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java
+++ b/app/src/main/java/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java
@@ -30,16 +30,22 @@ import android.content.res.TypedArray;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
+import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
+import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.ImageButton;
+import android.widget.ImageView;
import android.widget.TextView;
+import com.sothree.slidinguppanel.SlidingUpPanelLayout;
+
import java.io.File;
-import java.text.SimpleDateFormat;
import java.util.Date;
+import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@@ -53,6 +59,7 @@ 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.NowPlayingFragment;
import github.daneren2005.dsub.fragments.SearchFragment;
import github.daneren2005.dsub.fragments.SelectArtistFragment;
import github.daneren2005.dsub.fragments.SelectBookmarkFragment;
@@ -66,7 +73,6 @@ 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;
@@ -77,15 +83,21 @@ import github.daneren2005.dsub.view.ChangeLog;
/**
* Created by Scott on 10/14/13.
*/
-public class SubsonicFragmentActivity extends SubsonicActivity {
+public class SubsonicFragmentActivity extends SubsonicActivity implements DownloadService.OnSongChangedListener {
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 SlidingUpPanelLayout slideUpPanel;
+ private SlidingUpPanelLayout.PanelSlideListener panelSlideListener;
+ private NowPlayingFragment nowPlayingFragment;
+ private SubsonicFragment secondaryFragment;
+ private Toolbar mainToolbar;
+ private Toolbar nowPlayingToolbar;
+
private View bottomBar;
- private View coverArtView;
+ private ImageView coverArtView;
private TextView trackView;
private TextView artistView;
private ImageButton startButton;
@@ -95,6 +107,24 @@ public class SubsonicFragmentActivity extends SubsonicActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
+ if(savedInstanceState == null) {
+ String fragmentType = getIntent().getStringExtra(Constants.INTENT_EXTRA_FRAGMENT_TYPE);
+ boolean firstRun = false;
+ if (fragmentType == null) {
+ fragmentType = Util.openToTab(this);
+ if (fragmentType != null) {
+ firstRun = true;
+ }
+ }
+
+ if ("".equals(fragmentType) || fragmentType == null || firstRun) {
+ // Initial startup stuff
+ if (!sessionInitialized) {
+ loadSession();
+ }
+ }
+ }
+
super.onCreate(savedInstanceState);
if (getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_EXIT)) {
stopService(new Intent(this, DownloadService.class));
@@ -102,75 +132,153 @@ public class SubsonicFragmentActivity extends SubsonicActivity {
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);
- }
+ lastSelectedPosition = R.id.drawer_downloading;
}
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;
+
+ switch(fragmentType) {
+ case "Home":
+ lastSelectedPosition = R.id.drawer_home;
+ break;
+ case "Artist":
+ lastSelectedPosition = R.id.drawer_library;
+ break;
+ case "Playlist":
+ lastSelectedPosition = R.id.drawer_playlists;
+ break;
+ case "Podcast":
+ lastSelectedPosition = R.id.drawer_podcasts;
+ break;
+ case "Bookmark":
+ lastSelectedPosition = R.id.drawer_bookmarks;
+ break;
+ case "Share":
+ lastSelectedPosition = R.id.drawer_shares;
+ break;
+ case "Chat":
+ lastSelectedPosition = R.id.drawer_chat;
+ break;
+ }
+ } else {
+ lastSelectedPosition = R.id.drawer_home;
}
- }
- currentFragment = getNewFragment(fragmentType);
-
- if("".equals(fragmentType) || fragmentType == null || firstRun) {
- // Initial startup stuff
- if(!sessionInitialized) {
- loadSession();
+
+ MenuItem item = drawerList.getMenu().findItem(lastSelectedPosition);
+ if(item != null) {
+ item.setChecked(true);
}
}
-
+ currentFragment = getNewFragment(fragmentType);
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);
+ slideUpPanel = (SlidingUpPanelLayout) findViewById(R.id.slide_up_panel);
+ panelSlideListener = new SlidingUpPanelLayout.PanelSlideListener() {
+ @Override
+ public void onPanelSlide(View panel, float slideOffset) {
+
}
- });
- coverArtView = bottomBar.findViewById(R.id.album_art);
+
+ @Override
+ public void onPanelCollapsed(View panel) {
+ bottomBar.setVisibility(View.VISIBLE);
+ nowPlayingToolbar.setVisibility(View.GONE);
+ nowPlayingFragment.setPrimaryFragment(false);
+ setSupportActionBar(mainToolbar);
+
+ if(getSupportActionBar().getCustomView() == null) {
+ createCustomActionBarView();
+ }
+ recreateSpinner();
+ if(drawerToggle != null && backStack.size() > 0) {
+ drawerToggle.setDrawerIndicatorEnabled(false);
+ } else {
+ drawerToggle.setDrawerIndicatorEnabled(true);
+ }
+ }
+
+ @Override
+ public void onPanelExpanded(View panel) {
+ currentFragment.stopActionMode();
+
+ // Disable custom view before switching
+ getSupportActionBar().setDisplayShowCustomEnabled(false);
+
+ bottomBar.setVisibility(View.GONE);
+ nowPlayingToolbar.setVisibility(View.VISIBLE);
+ setSupportActionBar(nowPlayingToolbar);
+ nowPlayingFragment.setPrimaryFragment(true);
+
+ drawerToggle.setDrawerIndicatorEnabled(false);
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ }
+
+ @Override
+ public void onPanelAnchored(View panel) {
+
+ }
+
+ @Override
+ public void onPanelHidden(View panel) {
+
+ }
+ };
+ slideUpPanel.setPanelSlideListener(panelSlideListener);
+
+ if(getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD)) {
+ // Post this later so it actually runs
+ handler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ openNowPlaying();
+ }
+ }, 200);
+ }
+
+ bottomBar = findViewById(R.id.bottom_bar);
+ mainToolbar = (Toolbar) findViewById(R.id.main_toolbar);
+ nowPlayingToolbar = (Toolbar) findViewById(R.id.now_playing_toolbar);
+ coverArtView = (ImageView) bottomBar.findViewById(R.id.album_art);
trackView = (TextView) bottomBar.findViewById(R.id.track_name);
artistView = (TextView) bottomBar.findViewById(R.id.artist_name);
+ setSupportActionBar(mainToolbar);
+
+ if (findViewById(R.id.fragment_container) != null && savedInstanceState == null) {
+ nowPlayingFragment = new NowPlayingFragment();
+ FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
+ trans.add(R.id.now_playing_fragment_container, nowPlayingFragment, nowPlayingFragment.getTag() + "");
+ trans.commit();
+ }
+
ImageButton previousButton = (ImageButton) findViewById(R.id.download_previous);
previousButton.setOnClickListener(new View.OnClickListener() {
@Override
@@ -185,11 +293,6 @@ public class SubsonicFragmentActivity extends SubsonicActivity {
getDownloadService().previous();
return null;
}
-
- @Override
- protected void done(Void result) {
- update();
- }
}.execute();
}
});
@@ -210,11 +313,6 @@ public class SubsonicFragmentActivity extends SubsonicActivity {
return null;
}
-
- @Override
- protected void done(Void result) {
- update();
- }
}.execute();
}
});
@@ -233,11 +331,6 @@ public class SubsonicFragmentActivity extends SubsonicActivity {
getDownloadService().next();
return null;
}
-
- @Override
- protected void done(Void result) {
- update();
- }
}.execute();
}
});
@@ -262,7 +355,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity {
}
}
}
-
+
@Override
public void onNewIntent(Intent intent) {
super.onNewIntent(intent);
@@ -276,7 +369,6 @@ public class SubsonicFragmentActivity extends SubsonicActivity {
if (query != null) {
((SearchFragment)currentFragment).search(query, autoplay);
} else {
- ((SearchFragment)currentFragment).populateList();
if (requestsearch) {
onSearchRequested();
}
@@ -300,19 +392,6 @@ public class SubsonicFragmentActivity extends SubsonicActivity {
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();
@@ -329,29 +408,46 @@ public class SubsonicFragmentActivity extends SubsonicActivity {
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);
+ runWhenServiceAvailable(new Runnable() {
+ @Override
+ public void run() {
+ getDownloadService().addOnSongChangedListener(SubsonicFragmentActivity.this, true);
+ }
+ });
}
@Override
public void onPause() {
super.onPause();
- executorService.shutdown();
+ DownloadService downloadService = getDownloadService();
+ if(downloadService != null) {
+ downloadService.removeOnSongChangeListener(this);
+ }
}
@Override
+ public void onSaveInstanceState(Bundle savedInstanceState) {
+ super.onSaveInstanceState(savedInstanceState);
+ savedInstanceState.putString(Constants.MAIN_NOW_PLAYING, nowPlayingFragment.getTag());
+ savedInstanceState.putInt(Constants.MAIN_SLIDE_PANEL_STATE, slideUpPanel.getPanelState().hashCode());
+ }
+ @Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
+
+ String id = savedInstanceState.getString(Constants.MAIN_NOW_PLAYING);
+ FragmentManager fm = getSupportFragmentManager();
+ nowPlayingFragment = (NowPlayingFragment) fm.findFragmentByTag(id);
if(drawerToggle != null && backStack.size() > 0) {
drawerToggle.setDrawerIndicatorEnabled(false);
}
+
+ if(savedInstanceState.getInt(Constants.MAIN_SLIDE_PANEL_STATE, -1) == SlidingUpPanelLayout.PanelState.EXPANDED.hashCode()) {
+ panelSlideListener.onPanelExpanded(null);
+ }
}
@Override
@@ -369,7 +465,9 @@ public class SubsonicFragmentActivity extends SubsonicActivity {
@Override
public void onBackPressed() {
- if(onBackPressedSupport()) {
+ if(slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED && secondaryFragment == null) {
+ slideUpPanel.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
+ } else if(onBackPressedSupport()) {
if(!Util.disableExitPrompt(this) && lastBackPressTime < (System.currentTimeMillis() - 4000)) {
lastBackPressTime = System.currentTimeMillis();
Util.toast(this, R.string.main_back_confirm);
@@ -380,37 +478,99 @@ public class SubsonicFragmentActivity extends SubsonicActivity {
}
@Override
+ public boolean onBackPressedSupport() {
+ if(slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED) {
+ removeCurrent();
+ return false;
+ } else {
+ return super.onBackPressedSupport();
+ }
+ }
+
+ @Override
+ protected SubsonicFragment getCurrentFragment() {
+ if(slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED) {
+ return nowPlayingFragment;
+ } else {
+ return super.getCurrentFragment();
+ }
+ }
+
+ @Override
public void replaceFragment(SubsonicFragment fragment, int tag, boolean replaceCurrent) {
- super.replaceFragment(fragment, tag, replaceCurrent);
- if(drawerToggle != null) {
- drawerToggle.setDrawerIndicatorEnabled(false);
+ if(slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED) {
+ secondaryFragment = fragment;
+ nowPlayingFragment.setPrimaryFragment(false);
+ secondaryFragment.setPrimaryFragment(true);
+ supportInvalidateOptionsMenu();
+
+ 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(nowPlayingFragment);
+ trans.add(R.id.now_playing_fragment_container, secondaryFragment, tag + "");
+ trans.commit();
+ } else {
+ 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);
+ if(slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED && secondaryFragment != 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(secondaryFragment);
+ trans.show(nowPlayingFragment);
+ trans.commit();
+
+ secondaryFragment = null;
+ nowPlayingFragment.setPrimaryFragment(true);
+ supportInvalidateOptionsMenu();
+ } else {
+ super.removeCurrent();
+ if (drawerToggle != null && backStack.isEmpty()) {
+ drawerToggle.setDrawerIndicatorEnabled(true);
+ }
}
}
-
+
+ @Override
+ public void setTitle(CharSequence title) {
+ if(slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED) {
+ getSupportActionBar().setTitle(title);
+ } else {
+ super.setTitle(title);
+ }
+ }
+
+ @Override
+ protected void drawerItemSelected(String fragmentType) {
+ super.drawerItemSelected(fragmentType);
+
+ if(slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED) {
+ slideUpPanel.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
+ }
+ }
+
@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();
@@ -426,7 +586,16 @@ public class SubsonicFragmentActivity extends SubsonicActivity {
drawerToggle.setDrawerIndicatorEnabled(true);
}
}
-
+
+ @Override
+ public void openNowPlaying() {
+ slideUpPanel.setPanelState(SlidingUpPanelLayout.PanelState.EXPANDED);
+ }
+ @Override
+ public void closeNowPlaying() {
+ slideUpPanel.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
+ }
+
private SubsonicFragment getNewFragment(String fragmentType) {
if("Artist".equals(fragmentType)) {
return new SelectArtistFragment();
@@ -447,38 +616,6 @@ public class SubsonicFragmentActivity extends SubsonicActivity {
} 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() {
@@ -502,7 +639,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity {
if(ServerInfo.canSavePlayQueue(this) && !Util.isOffline(this)) {
loadRemotePlayQueue();
}
-
+
sessionInitialized = true;
}
private void loadSettings() {
@@ -551,7 +688,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity {
return true;
}
}
-
+
private void loadBookmarks() {
final Context context = this;
new SilentBackgroundTask<Void>(context) {
@@ -562,7 +699,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity {
return null;
}
-
+
@Override
public void error(Throwable error) {
Log.e(TAG, "Failed to get bookmarks", error);
@@ -683,4 +820,54 @@ public class SubsonicFragmentActivity extends SubsonicActivity {
}
}
}
+
+ public Toolbar getActiveToolbar() {
+ return slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED ? nowPlayingToolbar : mainToolbar;
+ }
+
+ @Override
+ public void onSongChanged(DownloadFile currentPlaying, int currentPlayingIndex) {
+ this.currentPlaying = currentPlaying;
+
+ MusicDirectory.Entry song = null;
+ if (currentPlaying != null) {
+ song = currentPlaying.getSong();
+ trackView.setText(song.getTitle());
+ artistView.setText(song.getArtist());
+ } else {
+ trackView.setText(R.string.main_title);
+ artistView.setText(R.string.main_artist);
+ }
+
+ if (coverArtView != null) {
+ int height = coverArtView.getHeight();
+ if (height <= 0) {
+ int[] attrs = new int[]{R.attr.actionBarSize};
+ TypedArray typedArray = this.obtainStyledAttributes(attrs);
+ height = typedArray.getDimensionPixelSize(0, 0);
+ typedArray.recycle();
+ }
+ getImageLoader().loadImage(coverArtView, song, false, height, false);
+ }
+ }
+
+ @Override
+ public void onSongsChanged(List<DownloadFile> songs, DownloadFile currentPlaying, int currentPlayingIndex) {
+ if(this.currentPlaying != currentPlaying || currentPlaying == null) {
+ onSongChanged(currentPlaying, currentPlayingIndex);
+ }
+ }
+
+ @Override
+ public void onSongProgress(DownloadFile currentPlaying, int millisPlayed, Integer duration, boolean isSeekable) {
+
+ }
+
+ @Override
+ public void onStateUpdate(DownloadFile downloadFile, PlayerState playerState) {
+ int[] attrs = new int[]{(playerState == PlayerState.STARTED) ? R.attr.actionbar_pause : R.attr.actionbar_start};
+ TypedArray typedArray = this.obtainStyledAttributes(attrs);
+ startButton.setImageResource(typedArray.getResourceId(0, 0));
+ typedArray.recycle();
+ }
}
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/AlbumGridAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/AlbumGridAdapter.java
deleted file mode 100644
index eb187569..00000000
--- a/app/src/main/java/github/daneren2005/dsub/adapter/AlbumGridAdapter.java
+++ /dev/null
@@ -1,73 +0,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 2014 (C) Scott Jackson
-*/
-
-package github.daneren2005.dsub.adapter;
-
-import android.content.Context;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-
-import java.util.List;
-
-import github.daneren2005.dsub.domain.MusicDirectory;
-import github.daneren2005.dsub.util.ImageLoader;
-import github.daneren2005.dsub.view.AlbumCell;
-
-public class AlbumGridAdapter extends ArrayAdapter<MusicDirectory.Entry> {
- private final static String TAG = AlbumGridAdapter.class.getSimpleName();
- private final Context activity;
- private final ImageLoader imageLoader;
- private List<MusicDirectory.Entry> entries;
- private boolean showArtist;
-
- public AlbumGridAdapter(Context activity, ImageLoader imageLoader, List<MusicDirectory.Entry> entries, boolean showArtist) {
- super(activity, android.R.layout.simple_list_item_1, entries);
- this.entries = entries;
- this.activity = activity;
- this.imageLoader = imageLoader;
-
- // Always show artist if they aren't all the same
- if(!showArtist) {
- String artist = null;
- for(MusicDirectory.Entry entry: entries) {
- if(artist == null) {
- artist = entry.getArtist();
- }
-
- if(artist != null && !artist.equals(entry.getArtist())) {
- showArtist = true;
- }
- }
- }
- this.showArtist = showArtist;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- MusicDirectory.Entry entry = getItem(position);
-
- AlbumCell view;
- if(convertView instanceof AlbumCell) {
- view = (AlbumCell) convertView;
- } else {
- view = new AlbumCell(activity);
- }
-
- view.setShowArtist(showArtist);
- view.setObject(entry, imageLoader);
- return view;
- }
-}
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/AlbumListAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/AlbumListAdapter.java
index 7151362c..47f82259 100644
--- a/app/src/main/java/github/daneren2005/dsub/adapter/AlbumListAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/AlbumListAdapter.java
@@ -72,11 +72,11 @@ public class AlbumListAdapter extends EndlessAdapter implements SectionIndexer {
MusicService service = MusicServiceFactory.getMusicService(context);
MusicDirectory result;
if(("genres".equals(type) && ServerInfo.checkServerVersion(context, "1.10.0")) || "years".equals(type)) {
- result = service.getAlbumList(type, extra, size, offset, context, null);
+ result = service.getAlbumList(type, extra, size, offset, false, context, null);
} else if("genres".equals(type) || "genres-songs".equals(type)) {
result = service.getSongsByGenre(extra, size, offset, context, null);
} else {
- result = service.getAlbumList(type, size, offset, context, null);
+ result = service.getAlbumList(type, size, offset, shouldIndex, context, null);
}
entries = result.getChildren();
return entries.size() > 0;
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/ArtistAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/ArtistAdapter.java
index 4d469faf..7461af69 100644
--- a/app/src/main/java/github/daneren2005/dsub/adapter/ArtistAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/ArtistAdapter.java
@@ -1,97 +1,128 @@
/*
- This file is part of Subsonic.
+ 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 2015 (C) Scott Jackson
+*/
- 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.adapter;
import android.content.Context;
-import github.daneren2005.dsub.R;
-import java.util.List;
+import android.support.v7.widget.PopupMenu;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.SectionIndexer;
+import android.widget.TextView;
+
+import java.util.List;
+
+import github.daneren2005.dsub.R;
import github.daneren2005.dsub.domain.Artist;
+import github.daneren2005.dsub.domain.MusicFolder;
+import github.daneren2005.dsub.util.Util;
import github.daneren2005.dsub.view.ArtistView;
+import github.daneren2005.dsub.view.UpdateView;
+
+public class ArtistAdapter extends SectionAdapter<Artist> {
+ public static int VIEW_TYPE_ARTIST = 4;
+
+ private List<MusicFolder> musicFolders;
+ private OnMusicFolderChanged onMusicFolderChanged;
+
+ public ArtistAdapter(Context context, List<Artist> artists, OnItemClickedListener listener) {
+ this(context, artists, null, listener, null);
+ }
+
+ public ArtistAdapter(Context context, List<Artist> artists, List<MusicFolder> musicFolders, OnItemClickedListener onItemClickedListener, OnMusicFolderChanged onMusicFolderChanged) {
+ super(context, artists);
+ this.musicFolders = musicFolders;
+ this.onItemClickedListener = onItemClickedListener;
+ this.onMusicFolderChanged = onMusicFolderChanged;
+
+ if(musicFolders != null) {
+ this.singleSectionHeader = true;
+ }
+ }
+
+ @Override
+ public UpdateView.UpdateViewHolder onCreateHeaderHolder(ViewGroup parent) {
+ final View header = LayoutInflater.from(context).inflate(R.layout.select_artist_header, parent, false);
+ header.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ PopupMenu popup = new PopupMenu(context, header.findViewById(R.id.select_artist_folder_2));
+
+ popup.getMenu().add(R.string.select_artist_all_folders);
+ for (MusicFolder musicFolder : musicFolders) {
+ popup.getMenu().add(musicFolder.getName());
+ }
+
+ popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ for (MusicFolder musicFolder : musicFolders) {
+ if(item.getTitle().equals(musicFolder.getName())) {
+ if(onMusicFolderChanged != null) {
+ onMusicFolderChanged.onMusicFolderChanged(musicFolder);
+ }
+ return true;
+ }
+ }
+
+ if(onMusicFolderChanged != null) {
+ onMusicFolderChanged.onMusicFolderChanged(null);
+ }
+ return true;
+ }
+ });
+ popup.show();
+ }
+ });
+
+ return new UpdateView.UpdateViewHolder(header, false);
+ }
+ @Override
+ public void onBindHeaderHolder(UpdateView.UpdateViewHolder holder, String header) {
+ TextView folderName = (TextView) holder.getView().findViewById(R.id.select_artist_folder_2);
-import java.util.ArrayList;
-import java.util.LinkedHashSet;
-import java.util.Set;
-
-/**
- * @author Sindre Mehus
- */
-public class ArtistAdapter extends ArrayAdapter<Artist> implements SectionIndexer {
-
- private final Context activity;
-
- // Both arrays are indexed by section ID.
- private final Object[] sections;
- private final Integer[] positions;
-
- public ArtistAdapter(Context activity, List<Artist> artists) {
- super(activity, R.layout.basic_list_item, artists);
- this.activity = activity;
-
- Set<String> sectionSet = new LinkedHashSet<String>(30);
- List<Integer> positionList = new ArrayList<Integer>(30);
- for (int i = 0; i < artists.size(); i++) {
- Artist artist = artists.get(i);
- String index = artist.getIndex();
- if (!sectionSet.contains(index)) {
- sectionSet.add(index);
- positionList.add(i);
- }
- }
- sections = sectionSet.toArray(new Object[sectionSet.size()]);
- positions = positionList.toArray(new Integer[positionList.size()]);
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- Artist entry = getItem(position);
- ArtistView view;
- if (convertView != null && convertView instanceof ArtistView) {
- view = (ArtistView) convertView;
+ String musicFolderId = Util.getSelectedMusicFolderId(context);
+ if(musicFolderId != null) {
+ for (MusicFolder musicFolder : musicFolders) {
+ if (musicFolder.getId().equals(musicFolderId)) {
+ folderName.setText(musicFolder.getName());
+ break;
+ }
+ }
} else {
- view = new ArtistView(activity);
+ folderName.setText(R.string.select_artist_all_folders);
}
- view.setObject(entry);
- return view;
- }
-
+ }
+
+ @Override
+ public UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) {
+ return new UpdateView.UpdateViewHolder(new ArtistView(context));
+ }
+
@Override
- public Object[] getSections() {
- return sections;
- }
-
- @Override
- public int getPositionForSection(int section) {
- section = Math.min(section, positions.length - 1);
- return positions[section];
- }
-
- @Override
- public int getSectionForPosition(int pos) {
- for (int i = 0; i < sections.length - 1; i++) {
- if (pos < positions[i + 1]) {
- return i;
- }
- }
- return sections.length - 1;
- }
+ public void onBindViewHolder(UpdateView.UpdateViewHolder holder, Artist item, int viewType) {
+ holder.getUpdateView().setObject(item);
+ }
+
+ @Override
+ public int getItemViewType(Artist item) {
+ return VIEW_TYPE_ARTIST;
+ }
+
+ public interface OnMusicFolderChanged {
+ void onMusicFolderChanged(MusicFolder musicFolder);
+ }
}
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/BasicListAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/BasicListAdapter.java
new file mode 100644
index 00000000..dfea91bf
--- /dev/null
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/BasicListAdapter.java
@@ -0,0 +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 2015 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.adapter;
+
+import android.content.Context;
+import android.view.ViewGroup;
+
+import java.util.List;
+
+import github.daneren2005.dsub.view.BasicListView;
+import github.daneren2005.dsub.view.UpdateView;
+
+public class BasicListAdapter extends SectionAdapter<String> {
+ public static int VIEW_TYPE_LINE = 1;
+
+ public BasicListAdapter(Context context, List<String> strings, OnItemClickedListener listener) {
+ super(context, strings);
+ this.onItemClickedListener = listener;
+ }
+
+ @Override
+ public UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) {
+ return new UpdateView.UpdateViewHolder(new BasicListView(context));
+ }
+
+ @Override
+ public void onBindViewHolder(UpdateView.UpdateViewHolder holder, String item, int viewType) {
+ holder.getUpdateView().setObject(item);
+ }
+
+ @Override
+ public int getItemViewType(String item) {
+ return VIEW_TYPE_LINE;
+ }
+}
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/BookmarkAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/BookmarkAdapter.java
index 26d3e16a..8e960de4 100644
--- a/app/src/main/java/github/daneren2005/dsub/adapter/BookmarkAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/BookmarkAdapter.java
@@ -1,20 +1,16 @@
/*
- This file is part of Subsonic.
-
+ 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
+ Copyright 2015 (C) Scott Jackson
*/
package github.daneren2005.dsub.adapter;
@@ -22,9 +18,10 @@ package github.daneren2005.dsub.adapter;
import android.content.Context;
import java.util.List;
-import android.view.View;
+
+import android.view.Menu;
+import android.view.MenuInflater;
import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
import android.widget.TextView;
import github.daneren2005.dsub.R;
@@ -32,33 +29,48 @@ import github.daneren2005.dsub.domain.Bookmark;
import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.util.Util;
import github.daneren2005.dsub.view.SongView;
+import github.daneren2005.dsub.view.UpdateView;
-public class BookmarkAdapter extends ArrayAdapter<MusicDirectory.Entry> {
+public class BookmarkAdapter extends SectionAdapter<MusicDirectory.Entry> {
private final static String TAG = BookmarkAdapter.class.getSimpleName();
- private Context activity;
- public BookmarkAdapter(Context activity, List<MusicDirectory.Entry> bookmarks) {
- super(activity, android.R.layout.simple_list_item_1, bookmarks);
- this.activity = activity;
+ public BookmarkAdapter(Context activity, List<MusicDirectory.Entry> bookmarks, OnItemClickedListener listener) {
+ super(activity, bookmarks);
+ this.onItemClickedListener = listener;
+ checkable = true;
}
-
- public View getView(int position, View convertView, ViewGroup parent) {
- MusicDirectory.Entry entry = getItem(position);
- Bookmark bookmark = entry.getBookmark();
- SongView view;
- if (convertView != null) {
- view = (SongView) convertView;
- } else {
- view = new SongView(activity);
- }
- view.setObject(entry, false);
-
+ @Override
+ public UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) {
+ return new UpdateView.UpdateViewHolder(new SongView(context));
+ }
+
+ @Override
+ public void onBindViewHolder(UpdateView.UpdateViewHolder holder, MusicDirectory.Entry item, int viewType) {
+ SongView songView = (SongView) holder.getUpdateView();
+ Bookmark bookmark = item.getBookmark();
+
+ songView.setObject(item, true);
+
// Add current position to duration
- TextView durationTextView = (TextView) view.findViewById(R.id.song_duration);
+ TextView durationTextView = (TextView) songView.findViewById(R.id.song_duration);
String duration = durationTextView.getText().toString();
durationTextView.setText(Util.formatDuration(bookmark.getPosition() / 1000) + " / " + duration);
-
- return view;
+ }
+
+ @Override
+ public int getItemViewType(MusicDirectory.Entry item) {
+ return EntryGridAdapter.VIEW_TYPE_SONG;
+ }
+
+ @Override
+ public void onCreateActionModeMenu(Menu menu, MenuInflater menuInflater) {
+ if(Util.isOffline(context)) {
+ menuInflater.inflate(R.menu.multiselect_media_offline, menu);
+ } else {
+ menuInflater.inflate(R.menu.multiselect_media, menu);
+ }
+
+ menu.removeItem(R.id.menu_remove_playlist);
}
}
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/DetailsAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/DetailsAdapter.java
new file mode 100644
index 00000000..efafe27a
--- /dev/null
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/DetailsAdapter.java
@@ -0,0 +1,62 @@
+/*
+ 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 2015 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.adapter;
+
+import android.content.Context;
+import android.text.SpannableString;
+import android.text.method.LinkMovementMethod;
+import android.text.util.Linkify;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.TextView;
+
+import java.util.List;
+
+import github.daneren2005.dsub.R;
+
+public class DetailsAdapter extends ArrayAdapter<String> {
+ private List<String> headers;
+ private List<String> details;
+
+ public DetailsAdapter(Context context, int layout, List<String> headers, List<String> details) {
+ super(context, layout, headers);
+
+ this.headers = headers;
+ this.details = details;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent){
+ View view;
+ if(convertView == null) {
+ view = LayoutInflater.from(getContext()).inflate(R.layout.details_item, null);
+ } else {
+ view = convertView;
+ }
+
+ TextView nameView = (TextView) view.findViewById(R.id.detail_name);
+ TextView detailsView = (TextView) view.findViewById(R.id.detail_value);
+
+ nameView.setText(headers.get(position));
+
+ detailsView.setText(details.get(position));
+ Linkify.addLinks(detailsView, Linkify.ALL);
+
+ return view;
+ }
+}
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/DownloadFileAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/DownloadFileAdapter.java
index be9b4cb9..7e926d51 100644
--- a/app/src/main/java/github/daneren2005/dsub/adapter/DownloadFileAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/DownloadFileAdapter.java
@@ -24,26 +24,33 @@ import java.util.List;
import github.daneren2005.dsub.service.DownloadFile;
import github.daneren2005.dsub.view.SongView;
+import github.daneren2005.dsub.view.UpdateView;
-public class DownloadFileAdapter extends ArrayAdapter<DownloadFile> {
- Context context;
+public class DownloadFileAdapter extends SectionAdapter<DownloadFile> {
+ public static int VIEW_TYPE_DOWNLOAD_FILE = 1;
public DownloadFileAdapter(Context context, List<DownloadFile> entries) {
- super(context, android.R.layout.simple_list_item_1, entries);
- this.context = context;
+ super(context, entries);
+ }
+ public DownloadFileAdapter(Context context, List<DownloadFile> entries, OnItemClickedListener onItemClickedListener) {
+ super(context, entries);
+ this.onItemClickedListener = onItemClickedListener;
+ }
+
+ @Override
+ public UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) {
+ return new UpdateView.UpdateViewHolder(new SongView(context));
+ }
+
+ @Override
+ public void onBindViewHolder(UpdateView.UpdateViewHolder holder, DownloadFile item, int viewType) {
+ SongView songView = (SongView) holder.getUpdateView();
+ songView.setObject(item.getSong(), false);
+ songView.setDownloadFile(item);
}
@Override
- public View getView(int position, View convertView, ViewGroup parent) {
- SongView view;
- if (convertView != null && convertView instanceof SongView) {
- view = (SongView) convertView;
- } else {
- view = new SongView(context);
- }
- DownloadFile downloadFile = getItem(position);
- view.setObject(downloadFile.getSong(), false);
- view.setDownloadFile(downloadFile);
- return view;
+ public int getItemViewType(DownloadFile item) {
+ return VIEW_TYPE_DOWNLOAD_FILE;
}
}
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/DrawerAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/DrawerAdapter.java
deleted file mode 100644
index b0a4a33d..00000000
--- a/app/src/main/java/github/daneren2005/dsub/adapter/DrawerAdapter.java
+++ /dev/null
@@ -1,126 +0,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.adapter;
-
-import android.content.Context;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import java.util.List;
-
-import github.daneren2005.dsub.R;
-
-/**
- * Created by Scott on 11/8/13.
- */
-public class DrawerAdapter extends ArrayAdapter<String> {
- private static String TAG = DrawerAdapter.class.getSimpleName();
- private Context context;
- private List<String> items;
- private List<Integer> icons;
- private List<Boolean> visible;
- private int selectedPosition = -1;
-
- public DrawerAdapter(Context context, List<String> items, List<Integer> icons, List<Boolean> visible) {
- super(context, R.layout.drawer_list_item, items);
-
- this.context = context;
- this.items = items;
- this.icons = icons;
- this.visible = visible;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- position = getActualPosition(position);
- String item = items.get(position);
- Integer icon = icons.get(position);
-
- if(convertView == null) {
- convertView = LayoutInflater.from(context).inflate(R.layout.drawer_list_item, null);
- }
-
- TextView textView = (TextView) convertView.findViewById(R.id.drawer_name);
- textView.setText(item);
-
- if(selectedPosition == position) {
- textView.setTextAppearance(context, R.style.DSub_TextViewStyle_Bold);
- selectedPosition = -1;
- }
-
- ImageView iconView = (ImageView) convertView.findViewById(R.id.drawer_icon);
- iconView.setImageResource(icon);
-
- return convertView;
- }
-
- @Override
- public int getCount() {
- int count = 0;
- for(int i = 0; i < visible.size(); i++) {
- if(visible.get(i)) {
- count++;
- }
- }
-
- return count;
- }
-
- public int getActualPosition(int position) {
- for(int i = 0; i <= position; i++) {
- if(!visible.get(i)) {
- position++;
- }
- }
-
- return position;
- }
- public int getAdapterPosition(int position) {
- if(!visible.get(position)) {
- visible.set(position, true);
- notifyDataSetChanged();
- }
-
- for(int i = position; i >= 0; i--) {
- if(!visible.get(i)) {
- position--;
- }
- }
-
- return position;
- }
-
- public void setItemVisible(int position, boolean visible) {
- if(this.visible.get(position) != visible) {
- this.visible.set(position, visible);
- notifyDataSetInvalidated();
- }
- }
- public void setDownloadVisible(boolean visible) {
- setItemVisible(items.size() - 2, visible);
- }
-
- public void setSelectedPosition(int position) {
- selectedPosition = position;
- }
-}
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/EntryAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/EntryAdapter.java
deleted file mode 100644
index 9e506e5a..00000000
--- a/app/src/main/java/github/daneren2005/dsub/adapter/EntryAdapter.java
+++ /dev/null
@@ -1,82 +0,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 2010 (C) Sindre Mehus
- */
-package github.daneren2005.dsub.adapter;
-
-import android.content.Context;
-
-import java.util.List;
-
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import github.daneren2005.dsub.domain.MusicDirectory;
-import github.daneren2005.dsub.util.ImageLoader;
-import github.daneren2005.dsub.view.AlbumView;
-import github.daneren2005.dsub.view.ArtistEntryView;
-import github.daneren2005.dsub.view.SongView;
-
-/**
- * @author Sindre Mehus
- */
-public class EntryAdapter extends ArrayAdapter<MusicDirectory.Entry> {
- private final static String TAG = EntryAdapter.class.getSimpleName();
- private final Context activity;
- private final ImageLoader imageLoader;
- private final boolean checkable;
- private List<MusicDirectory.Entry> entries;
-
- public EntryAdapter(Context activity, ImageLoader imageLoader, List<MusicDirectory.Entry> entries, boolean checkable) {
- super(activity, android.R.layout.simple_list_item_1, entries);
- this.entries = entries;
- this.activity = activity;
- this.imageLoader = imageLoader;
- this.checkable = checkable;
- }
-
- public void removeAt(int position) {
- entries.remove(position);
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- MusicDirectory.Entry entry = getItem(position);
-
- if (entry.isDirectory()) {
- if(entry.isAlbum()) {
- AlbumView view;
- view = new AlbumView(activity);
- view.setObject(entry, imageLoader);
- return view;
- } else {
- ArtistEntryView view = new ArtistEntryView(activity);
- view.setObject(entry);
- return view;
- }
- } else {
- SongView view;
- if (convertView != null && convertView instanceof SongView) {
- view = (SongView) convertView;
- } else {
- view = new SongView(activity);
- }
- view.setObject(entry, checkable);
- return view;
- }
- }
-}
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/EntryGridAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/EntryGridAdapter.java
new file mode 100644
index 00000000..78ef13ed
--- /dev/null
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/EntryGridAdapter.java
@@ -0,0 +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 2015 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.adapter;
+
+import android.content.Context;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.MusicDirectory;
+import github.daneren2005.dsub.domain.MusicDirectory.Entry;
+import github.daneren2005.dsub.util.ImageLoader;
+import github.daneren2005.dsub.util.Util;
+import github.daneren2005.dsub.view.AlbumView;
+import github.daneren2005.dsub.view.SongView;
+import github.daneren2005.dsub.view.UpdateView;
+import github.daneren2005.dsub.view.UpdateView.UpdateViewHolder;
+
+public class EntryGridAdapter extends SectionAdapter<Entry> {
+ private static String TAG = EntryGridAdapter.class.getSimpleName();
+
+ public static int VIEW_TYPE_ALBUM_CELL = 1;
+ public static int VIEW_TYPE_ALBUM_LINE = 2;
+ public static int VIEW_TYPE_SONG = 3;
+
+ private ImageLoader imageLoader;
+ private boolean largeAlbums;
+ private boolean showArtist = false;
+ private boolean removeFromPlaylist = false;
+ private View header;
+
+ public EntryGridAdapter(Context context, List<Entry> entries, ImageLoader imageLoader, boolean largeCell) {
+ super(context, entries);
+ this.imageLoader = imageLoader;
+ this.largeAlbums = largeCell;
+
+ // Always show artist if they aren't all the same
+ String artist = null;
+ for(MusicDirectory.Entry entry: entries) {
+ if(artist == null) {
+ artist = entry.getArtist();
+ }
+
+ if(artist != null && !artist.equals(entry.getArtist())) {
+ showArtist = true;
+ }
+ }
+ checkable = true;
+ }
+
+ @Override
+ public UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) {
+ UpdateView updateView = null;
+ if(viewType == VIEW_TYPE_ALBUM_LINE || viewType == VIEW_TYPE_ALBUM_CELL) {
+ updateView = new AlbumView(context, viewType == VIEW_TYPE_ALBUM_CELL);
+ } else if(viewType == VIEW_TYPE_SONG) {
+ updateView = new SongView(context);
+ }
+
+ return new UpdateViewHolder(updateView);
+ }
+
+ @Override
+ public void onBindViewHolder(UpdateViewHolder holder, Entry entry, int viewType) {
+ UpdateView view = holder.getUpdateView();
+ if(viewType == VIEW_TYPE_ALBUM_CELL || viewType == VIEW_TYPE_ALBUM_LINE) {
+ AlbumView albumView = (AlbumView) view;
+ albumView.setShowArtist(showArtist);
+ albumView.setObject(entry, imageLoader);
+ } else if(viewType == VIEW_TYPE_SONG) {
+ SongView songView = (SongView) view;
+ songView.setObject(entry, checkable && !entry.isVideo());
+ }
+ }
+
+ public UpdateViewHolder onCreateHeaderHolder(ViewGroup parent) {
+ return new UpdateViewHolder(header, false);
+ }
+ public void onBindHeaderHolder(UpdateViewHolder holder, String header) {
+
+ }
+
+ @Override
+ public int getItemViewType(Entry entry) {
+ if(entry.isDirectory()) {
+ if (largeAlbums) {
+ return VIEW_TYPE_ALBUM_CELL;
+ } else {
+ return VIEW_TYPE_ALBUM_LINE;
+ }
+ } else {
+ return VIEW_TYPE_SONG;
+ }
+ }
+
+ public void setHeader(View header) {
+ this.header = header;
+ this.singleSectionHeader = true;
+ }
+
+ public void setShowArtist(boolean showArtist) {
+ this.showArtist = showArtist;
+ }
+
+ public void removeAt(int index) {
+ sections.get(0).remove(index);
+ notifyItemRemoved(index);
+ }
+
+ public void setRemoveFromPlaylist(boolean removeFromPlaylist) {
+ this.removeFromPlaylist = removeFromPlaylist;
+ }
+
+ @Override
+ public void onCreateActionModeMenu(Menu menu, MenuInflater menuInflater) {
+ if(Util.isOffline(context)) {
+ menuInflater.inflate(R.menu.multiselect_media_offline, menu);
+ } else {
+ menuInflater.inflate(R.menu.multiselect_media, menu);
+ }
+
+ if(!removeFromPlaylist) {
+ menu.removeItem(R.id.menu_remove_playlist);
+ }
+ }
+}
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/EntryInfiniteGridAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/EntryInfiniteGridAdapter.java
new file mode 100644
index 00000000..2c4f75dc
--- /dev/null
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/EntryInfiniteGridAdapter.java
@@ -0,0 +1,146 @@
+/*
+ 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 2015 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.adapter;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.List;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.MusicDirectory;
+import github.daneren2005.dsub.domain.MusicDirectory.Entry;
+import github.daneren2005.dsub.domain.ServerInfo;
+import github.daneren2005.dsub.service.MusicService;
+import github.daneren2005.dsub.service.MusicServiceFactory;
+import github.daneren2005.dsub.util.ImageLoader;
+import github.daneren2005.dsub.util.SilentBackgroundTask;
+import github.daneren2005.dsub.view.UpdateView;
+
+public class EntryInfiniteGridAdapter extends EntryGridAdapter {
+ public static int VIEW_TYPE_LOADING = 4;
+
+ private String type;
+ private String extra;
+ private int size;
+
+ private boolean loading = false;
+ private boolean allLoaded = false;
+
+ public EntryInfiniteGridAdapter(Context context, List<Entry> entries, ImageLoader imageLoader, boolean largeCell) {
+ super(context, entries, imageLoader, largeCell);
+ }
+
+ @Override
+ public UpdateView.UpdateViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ if(viewType == VIEW_TYPE_LOADING) {
+ View progress = LayoutInflater.from(context).inflate(R.layout.tab_progress, null);
+ progress.setVisibility(View.VISIBLE);
+ return new UpdateView.UpdateViewHolder(progress, false);
+ }
+
+ return super.onCreateViewHolder(parent, viewType);
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ if(isLoadingView(position)) {
+ return VIEW_TYPE_LOADING;
+ }
+
+ return super.getItemViewType(position);
+ }
+
+ @Override
+ public void onBindViewHolder(UpdateView.UpdateViewHolder holder, int position) {
+ if(!isLoadingView(position)) {
+ super.onBindViewHolder(holder, position);
+ }
+ }
+
+ @Override
+ public int getItemCount() {
+ int size = super.getItemCount();
+
+ if(!allLoaded) {
+ size++;
+ }
+
+ return size;
+ }
+
+ public void setData(String type, String extra, int size) {
+ this.type = type;
+ this.extra = extra;
+ this.size = size;
+ }
+
+ public void loadMore() {
+ if(loading || allLoaded) {
+ return;
+ }
+ loading = true;
+
+ new SilentBackgroundTask<Void>(context) {
+ private List<Entry> newData;
+
+ @Override
+ protected Void doInBackground() throws Throwable {
+ newData = cacheInBackground();
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ appendCachedData(newData);
+ loading = false;
+
+ if(newData.isEmpty()) {
+ allLoaded = true;
+ notifyDataSetChanged();
+ }
+ }
+ }.execute();
+ }
+
+ protected List<Entry> cacheInBackground() throws Exception {
+ MusicService service = MusicServiceFactory.getMusicService(context);
+ MusicDirectory result;
+ int offset = sections.get(0).size();
+ if(("genres".equals(type) && ServerInfo.checkServerVersion(context, "1.10.0")) || "years".equals(type)) {
+ result = service.getAlbumList(type, extra, size, offset, false, context, null);
+ } else if("genres".equals(type) || "genres-songs".equals(type)) {
+ result = service.getSongsByGenre(extra, size, offset, context, null);
+ } else {
+ result = service.getAlbumList(type, size, offset, false, context, null);
+ }
+ return result.getChildren();
+ }
+
+ protected void appendCachedData(List<Entry> newData) {
+ if(newData.size() > 0) {
+ int start = sections.get(0).size();
+ sections.get(0).addAll(newData);
+ this.notifyItemRangeInserted(start, newData.size());
+ }
+ }
+
+ protected boolean isLoadingView(int position) {
+ return !allLoaded && position >= sections.get(0).size();
+ }
+}
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/GenreAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/GenreAdapter.java
index abb208c9..7e6954f9 100644
--- a/app/src/main/java/github/daneren2005/dsub/adapter/GenreAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/GenreAdapter.java
@@ -1,60 +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/>.
+ 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 2015 (C) Scott Jackson
+*/
- Copyright 2010 (C) Sindre Mehus
- */
package github.daneren2005.dsub.adapter;
-import android.widget.ArrayAdapter;
-import android.widget.SectionIndexer;
import android.content.Context;
-import android.view.View;
import android.view.ViewGroup;
-import github.daneren2005.dsub.R;
import github.daneren2005.dsub.domain.Genre;
import github.daneren2005.dsub.view.GenreView;
+import github.daneren2005.dsub.view.UpdateView;
import java.util.List;
-import java.util.Set;
-import java.util.LinkedHashSet;
-import java.util.ArrayList;
-/**
- * @author Sindre Mehus
-*/
-public class GenreAdapter extends ArrayAdapter<Genre>{
- private Context activity;
- private List<Genre> genres;
-
- public GenreAdapter(Context context, List<Genre> genres) {
- super(context, android.R.layout.simple_list_item_1, genres);
- this.activity = context;
- this.genres = genres;
+public class GenreAdapter extends SectionAdapter<Genre>{
+ public static int VIEW_TYPE_GENRE = 1;
+
+ public GenreAdapter(Context context, List<Genre> genres, OnItemClickedListener listener) {
+ super(context, genres);
+ this.onItemClickedListener = listener;
}
-
+
@Override
- public View getView(int position, View convertView, ViewGroup parent) {
- Genre genre = genres.get(position);
- GenreView view;
- if (convertView != null && convertView instanceof GenreView) {
- view = (GenreView) convertView;
- } else {
- view = new GenreView(activity);
- }
- view.setObject(genre);
- return view;
- }
+ public UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) {
+ return new UpdateView.UpdateViewHolder(new GenreView(context));
+ }
+
+ @Override
+ public void onBindViewHolder(UpdateView.UpdateViewHolder holder, Genre item, int viewType) {
+ holder.getUpdateView().setObject(item);
+ }
+
+ @Override
+ public int getItemViewType(Genre item) {
+ return VIEW_TYPE_GENRE;
+ }
}
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/MainAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/MainAdapter.java
new file mode 100644
index 00000000..8f1f1c38
--- /dev/null
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/MainAdapter.java
@@ -0,0 +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 2015 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.adapter;
+
+import android.content.Context;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+
+import java.util.List;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.ServerInfo;
+import github.daneren2005.dsub.util.Util;
+import github.daneren2005.dsub.view.AlbumListCountView;
+import github.daneren2005.dsub.view.BasicHeaderView;
+import github.daneren2005.dsub.view.BasicListView;
+import github.daneren2005.dsub.view.UpdateView;
+
+public class MainAdapter extends SectionAdapter<Integer> {
+ public static final int VIEW_TYPE_ALBUM_LIST = 1;
+ public static final int VIEW_TYPE_ALBUM_COUNT_LIST = 2;
+
+ public MainAdapter(Context context, List<String> headers, List<List<Integer>> sections, OnItemClickedListener onItemClickedListener) {
+ super(context, headers, sections);
+ this.onItemClickedListener = onItemClickedListener;
+ }
+
+ @Override
+ public UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) {
+ UpdateView updateView;
+ if(viewType == VIEW_TYPE_ALBUM_LIST) {
+ updateView = new BasicListView(context);
+ } else {
+ updateView = new AlbumListCountView(context);
+ }
+
+ return new UpdateView.UpdateViewHolder(updateView);
+ }
+
+ @Override
+ public void onBindViewHolder(UpdateView.UpdateViewHolder holder, Integer item, int viewType) {
+ UpdateView updateView = holder.getUpdateView();
+
+ if(viewType == VIEW_TYPE_ALBUM_LIST) {
+ updateView.setObject(context.getResources().getString(item));
+ } else {
+ updateView.setObject(item);
+ }
+ }
+
+ @Override
+ public int getItemViewType(Integer item) {
+ if(item == R.string.main_albums_newest) {
+ return VIEW_TYPE_ALBUM_COUNT_LIST;
+ } else {
+ return VIEW_TYPE_ALBUM_LIST;
+ }
+ }
+
+ @Override
+ public UpdateView.UpdateViewHolder onCreateHeaderHolder(ViewGroup parent) {
+ return new UpdateView.UpdateViewHolder(new BasicHeaderView(context, R.layout.album_list_header));
+ }
+ @Override
+ public void onBindHeaderHolder(UpdateView.UpdateViewHolder holder, String header) {
+ UpdateView view = holder.getUpdateView();
+ CheckBox checkBox = (CheckBox) view.findViewById(R.id.item_checkbox);
+
+ String display;
+ if ("albums".equals(header)) {
+ display = context.getResources().getString(R.string.main_albums_title);
+
+ if(!Util.isOffline(context) && ServerInfo.canAlbumListPerFolder(context)) {
+ checkBox.setVisibility(View.VISIBLE);
+ checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ Util.setAlbumListsPerFolder(context, isChecked);
+ }
+ });
+ checkBox.setChecked(Util.getAlbumListsPerFolder(context));
+ } else {
+ checkBox.setVisibility(View.GONE);
+ }
+ } else if("videos".equals(header)) {
+ display = context.getResources().getString(R.string.main_videos);
+ checkBox.setVisibility(View.GONE);
+ } else {
+ display = header;
+ checkBox.setVisibility(View.GONE);
+ }
+
+ if(view != null) {
+ view.setObject(display);
+ }
+ }
+}
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/MergeAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/MergeAdapter.java
deleted file mode 100644
index a2db4cf0..00000000
--- a/app/src/main/java/github/daneren2005/dsub/adapter/MergeAdapter.java
+++ /dev/null
@@ -1,290 +0,0 @@
-/***
- Copyright (c) 2008-2009 CommonsWare, LLC
- Portions (c) 2009 Google, Inc.
-
- Licensed under the Apache License, Version 2.0 (the "License"); you may
- not use this file except in compliance with the License. You may obtain
- a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
-
-package github.daneren2005.dsub.adapter;
-
-import android.database.DataSetObserver;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.ListAdapter;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Arrays;
-
-/**
- * Adapter that merges multiple child adapters and views
- * into a single contiguous whole.
- * <p/>
- * Adapters used as pieces within MergeAdapter must
- * have view type IDs monotonically increasing from 0. Ideally,
- * adapters also have distinct ranges for their row ids, as
- * returned by getItemId().
- */
-public class MergeAdapter extends BaseAdapter {
-
- private final CascadeDataSetObserver observer = new CascadeDataSetObserver();
- private final ArrayList<ListAdapter> pieces = new ArrayList<ListAdapter>();
-
- /**
- * Stock constructor, simply chaining to the superclass.
- */
- public MergeAdapter() {
- super();
- }
-
- /**
- * Adds a new adapter to the roster of things to appear
- * in the aggregate list.
- *
- * @param adapter Source for row views for this section
- */
- public void addAdapter(ListAdapter adapter) {
- pieces.add(adapter);
- adapter.registerDataSetObserver(observer);
- }
-
- public void removeAdapter(ListAdapter adapter) {
- adapter.unregisterDataSetObserver(observer);
- pieces.remove(adapter);
- }
-
- /**
- * Adds a new View to the roster of things to appear
- * in the aggregate list.
- *
- * @param view Single view to add
- */
- public ListAdapter addView(View view) {
- return addView(view, false);
- }
-
- /**
- * Adds a new View to the roster of things to appear
- * in the aggregate list.
- *
- * @param view Single view to add
- * @param enabled false if views are disabled, true if enabled
- */
- public ListAdapter addView(View view, boolean enabled) {
- return addViews(Arrays.asList(view), enabled);
- }
-
- /**
- * Adds a list of views to the roster of things to appear
- * in the aggregate list.
- *
- * @param views List of views to add
- */
- public ListAdapter addViews(List<View> views) {
- return addViews(views, false);
- }
-
- /**
- * Adds a list of views to the roster of things to appear
- * in the aggregate list.
- *
- * @param views List of views to add
- * @param enabled false if views are disabled, true if enabled
- */
- public ListAdapter addViews(List<View> views, boolean enabled) {
- ListAdapter adapter = enabled ? new EnabledSackAdapter(views) : new SackOfViewsAdapter(views);
- addAdapter(adapter);
- return adapter;
- }
-
- /**
- * Get the data item associated with the specified
- * position in the data set.
- *
- * @param position Position of the item whose data we want
- */
- @Override
- public Object getItem(int position) {
- for (ListAdapter piece : pieces) {
- int size = piece.getCount();
-
- if (position < size) {
- return (piece.getItem(position));
- }
-
- position -= size;
- }
-
- return (null);
- }
-
- /**
- * How many items are in the data set represented by this
- * Adapter.
- */
- @Override
- public int getCount() {
- int total = 0;
-
- for (ListAdapter piece : pieces) {
- total += piece.getCount();
- }
-
- return (total);
- }
-
- /**
- * Returns the number of types of Views that will be
- * created by getView().
- */
- @Override
- public int getViewTypeCount() {
- int total = 0;
-
- for (ListAdapter piece : pieces) {
- total += piece.getViewTypeCount();
- }
-
- return (Math.max(total, 1)); // needed for setListAdapter() before content add'
- }
-
- /**
- * Get the type of View that will be created by getView()
- * for the specified item.
- *
- * @param position Position of the item whose data we want
- */
- @Override
- public int getItemViewType(int position) {
- int typeOffset = 0;
- int result = -1;
-
- for (ListAdapter piece : pieces) {
- int size = piece.getCount();
-
- if (position < size) {
- result = typeOffset + piece.getItemViewType(position);
- break;
- }
-
- position -= size;
- typeOffset += piece.getViewTypeCount();
- }
-
- return (result);
- }
-
- /**
- * Are all items in this ListAdapter enabled? If yes it
- * means all items are selectable and clickable.
- */
- @Override
- public boolean areAllItemsEnabled() {
- return (false);
- }
-
- /**
- * Returns true if the item at the specified position is
- * not a separator.
- *
- * @param position Position of the item whose data we want
- */
- @Override
- public boolean isEnabled(int position) {
- for (ListAdapter piece : pieces) {
- int size = piece.getCount();
-
- if (position < size) {
- return (piece.isEnabled(position));
- }
-
- position -= size;
- }
-
- return (false);
- }
-
- /**
- * Get a View that displays the data at the specified
- * position in the data set.
- *
- * @param position Position of the item whose data we want
- * @param convertView View to recycle, if not null
- * @param parent ViewGroup containing the returned View
- */
- @Override
- public View getView(int position, View convertView,
- ViewGroup parent) {
- for (ListAdapter piece : pieces) {
- int size = piece.getCount();
-
- if (position < size) {
-
- return (piece.getView(position, convertView, parent));
- }
-
- position -= size;
- }
-
- return (null);
- }
-
- /**
- * Get the row id associated with the specified position
- * in the list.
- *
- * @param position Position of the item whose data we want
- */
- @Override
- public long getItemId(int position) {
- for (ListAdapter piece : pieces) {
- int size = piece.getCount();
-
- if (position < size) {
- return (piece.getItemId(position));
- }
-
- position -= size;
- }
-
- return (-1);
- }
-
- private static class EnabledSackAdapter extends SackOfViewsAdapter {
- public EnabledSackAdapter(List<View> views) {
- super(views);
- }
-
- @Override
- public boolean areAllItemsEnabled() {
- return (true);
- }
-
- @Override
- public boolean isEnabled(int position) {
- return (true);
- }
- }
-
- private class CascadeDataSetObserver extends DataSetObserver {
- @Override
- public void onChanged() {
- notifyDataSetChanged();
- }
-
- @Override
- public void onInvalidated() {
- notifyDataSetInvalidated();
- }
- }
-}
-
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/PlaylistAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/PlaylistAdapter.java
index d56a6b97..4221677e 100644
--- a/app/src/main/java/github/daneren2005/dsub/adapter/PlaylistAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/PlaylistAdapter.java
@@ -1,70 +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 2010 (C) Sindre Mehus
- */
+ 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 2015 (C) Scott Jackson
+*/
package github.daneren2005.dsub.adapter;
import android.content.Context;
-import github.daneren2005.dsub.R;
+
import java.util.List;
-import android.view.View;
+
import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
import github.daneren2005.dsub.domain.Playlist;
+import github.daneren2005.dsub.util.ImageLoader;
import github.daneren2005.dsub.view.PlaylistView;
+import github.daneren2005.dsub.view.UpdateView;
-import java.util.Collections;
-import java.util.Comparator;
+public class PlaylistAdapter extends SectionAdapter<Playlist> {
+ public static int VIEW_TYPE_PLAYLIST = 1;
-/**
- * @author Sindre Mehus
- */
-public class PlaylistAdapter extends ArrayAdapter<Playlist> {
+ private ImageLoader imageLoader;
+ private boolean largeCell;
- private final Context activity;
-
- public PlaylistAdapter(Context activity, List<Playlist> Playlists) {
- super(activity, R.layout.basic_list_item, Playlists);
- this.activity = activity;
+ public PlaylistAdapter(Context context, List<Playlist> playlists, ImageLoader imageLoader, boolean largeCell, OnItemClickedListener listener) {
+ super(context, playlists);
+ this.imageLoader = imageLoader;
+ this.largeCell = largeCell;
+ this.onItemClickedListener = listener;
+ }
+ public PlaylistAdapter(Context context, List<String> headers, List<List<Playlist>> sections, ImageLoader imageLoader, boolean largeCell, OnItemClickedListener listener) {
+ super(context, headers, sections);
+ this.imageLoader = imageLoader;
+ this.largeCell = largeCell;
+ this.onItemClickedListener = listener;
}
@Override
- public View getView(int position, View convertView, ViewGroup parent) {
- Playlist entry = getItem(position);
- PlaylistView view;
- if (convertView != null && convertView instanceof PlaylistView) {
- view = (PlaylistView) convertView;
- } else {
- view = new PlaylistView(activity);
- }
- view.setObject(entry);
- return view;
+ public UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) {
+ return new UpdateView.UpdateViewHolder(new PlaylistView(context, imageLoader, largeCell));
}
- public static class PlaylistComparator implements Comparator<Playlist> {
- @Override
- public int compare(Playlist playlist1, Playlist playlist2) {
- return playlist1.getName().compareToIgnoreCase(playlist2.getName());
- }
-
- public static List<Playlist> sort(List<Playlist> playlists) {
- Collections.sort(playlists, new PlaylistComparator());
- return playlists;
- }
+ @Override
+ public void onBindViewHolder(UpdateView.UpdateViewHolder holder, Playlist playlist, int viewType) {
+ holder.getUpdateView().setObject(playlist);
+ holder.setItem(playlist);
+ }
+ @Override
+ public int getItemViewType(Playlist playlist) {
+ return VIEW_TYPE_PLAYLIST;
}
}
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/PodcastChannelAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/PodcastChannelAdapter.java
index 8ee39a10..dc94178d 100644
--- a/app/src/main/java/github/daneren2005/dsub/adapter/PodcastChannelAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/PodcastChannelAdapter.java
@@ -1,60 +1,47 @@
/*
- 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
- */
+ 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 2015 (C) Scott Jackson
+*/
package github.daneren2005.dsub.adapter;
-import android.widget.ArrayAdapter;
-import android.widget.SectionIndexer;
import android.content.Context;
-import android.view.View;
import android.view.ViewGroup;
-import github.daneren2005.dsub.R;
import github.daneren2005.dsub.domain.PodcastChannel;
import github.daneren2005.dsub.view.PodcastChannelView;
+import github.daneren2005.dsub.view.UpdateView;
import java.util.List;
-import java.util.Set;
-import java.util.LinkedHashSet;
-import java.util.ArrayList;
-/**
- * @author Sindre Mehus
-*/
-public class PodcastChannelAdapter extends ArrayAdapter<PodcastChannel>{
- private Context activity;
- private List<PodcastChannel> podcasts;
+public class PodcastChannelAdapter extends SectionAdapter<PodcastChannel>{
+ public static int VIEW_TYPE_PODCAST = 1;
- public PodcastChannelAdapter(Context context, List<PodcastChannel> podcasts) {
- super(context, android.R.layout.simple_list_item_1, podcasts);
- this.activity = context;
- this.podcasts = podcasts;
+ public PodcastChannelAdapter(Context context, List<PodcastChannel> podcasts, OnItemClickedListener listener) {
+ super(context, podcasts);
+ this.onItemClickedListener = listener;
}
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- PodcastChannel podcast = podcasts.get(position);
- PodcastChannelView view;
- if (convertView != null && convertView instanceof PodcastChannelView) {
- view = (PodcastChannelView) convertView;
- } else {
- view = new PodcastChannelView(activity);
- }
- view.setObject(podcast);
- return view;
+
+ @Override
+ public UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) {
+ return new UpdateView.UpdateViewHolder(new PodcastChannelView(context));
+ }
+
+ @Override
+ public void onBindViewHolder(UpdateView.UpdateViewHolder holder, PodcastChannel item, int viewType) {
+ holder.getUpdateView().setObject(item);
+ }
+
+ @Override
+ public int getItemViewType(PodcastChannel item) {
+ return VIEW_TYPE_PODCAST;
}
}
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/SackOfViewsAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/SackOfViewsAdapter.java
deleted file mode 100644
index e4744cc5..00000000
--- a/app/src/main/java/github/daneren2005/dsub/adapter/SackOfViewsAdapter.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/***
- Copyright (c) 2008-2009 CommonsWare, LLC
- Portions (c) 2009 Google, Inc.
-
- Licensed under the Apache License, Version 2.0 (the "License"); you may
- not use this file except in compliance with the License. You may obtain
- a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
-package github.daneren2005.dsub.adapter;
-
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.ListView;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Adapter that simply returns row views from a list.
- * <p/>
- * If you supply a size, you must implement newView(), to
- * create a required view. The adapter will then cache these
- * views.
- * <p/>
- * If you supply a list of views in the constructor, that
- * list will be used directly. If any elements in the list
- * are null, then newView() will be called just for those
- * slots.
- * <p/>
- * Subclasses may also wish to override areAllItemsEnabled()
- * (default: false) and isEnabled() (default: false), if some
- * of their rows should be selectable.
- * <p/>
- * It is assumed each view is unique, and therefore will not
- * get recycled.
- * <p/>
- * Note that this adapter is not designed for long lists. It
- * is more for screens that should behave like a list. This
- * is particularly useful if you combine this with other
- * adapters (e.g., SectionedAdapter) that might have an
- * arbitrary number of rows, so it all appears seamless.
- */
-public class SackOfViewsAdapter extends BaseAdapter {
- private List<View> views = null;
-
- /**
- * Constructor creating an empty list of views, but with
- * a specified count. Subclasses must override newView().
- */
- public SackOfViewsAdapter(int count) {
- super();
-
- views = new ArrayList<View>(count);
-
- for (int i = 0; i < count; i++) {
- views.add(null);
- }
- }
-
- /**
- * Constructor wrapping a supplied list of views.
- * Subclasses must override newView() if any of the elements
- * in the list are null.
- */
- public SackOfViewsAdapter(List<View> views) {
- for (View view : views) {
- view.setLayoutParams(new ListView.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
- }
- this.views = views;
- }
-
- /**
- * Get the data item associated with the specified
- * position in the data set.
- *
- * @param position Position of the item whose data we want
- */
- @Override
- public Object getItem(int position) {
- return (views.get(position));
- }
-
- /**
- * How many items are in the data set represented by this
- * Adapter.
- */
- @Override
- public int getCount() {
- return (views.size());
- }
-
- /**
- * Returns the number of types of Views that will be
- * created by getView().
- */
- @Override
- public int getViewTypeCount() {
- return (getCount());
- }
-
- /**
- * Get the type of View that will be created by getView()
- * for the specified item.
- *
- * @param position Position of the item whose data we want
- */
- @Override
- public int getItemViewType(int position) {
- return (position);
- }
-
- /**
- * Are all items in this ListAdapter enabled? If yes it
- * means all items are selectable and clickable.
- */
- @Override
- public boolean areAllItemsEnabled() {
- return (false);
- }
-
- /**
- * Returns true if the item at the specified position is
- * not a separator.
- *
- * @param position Position of the item whose data we want
- */
- @Override
- public boolean isEnabled(int position) {
- return (false);
- }
-
- /**
- * Get a View that displays the data at the specified
- * position in the data set.
- *
- * @param position Position of the item whose data we want
- * @param convertView View to recycle, if not null
- * @param parent ViewGroup containing the returned View
- */
- @Override
- public View getView(int position, View convertView,
- ViewGroup parent) {
- View result = views.get(position);
-
- if (result == null) {
- result = newView(position, parent);
- views.set(position, result);
- }
-
- return (result);
- }
-
- /**
- * Get the row id associated with the specified position
- * in the list.
- *
- * @param position Position of the item whose data we want
- */
- @Override
- public long getItemId(int position) {
- return (position);
- }
-
- /**
- * Create a new View to go into the list at the specified
- * position.
- *
- * @param position Position of the item whose data we want
- * @param parent ViewGroup containing the returned View
- */
- protected View newView(int position, ViewGroup parent) {
- throw new RuntimeException("You must override newView()!");
- }
-}
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/SearchAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/SearchAdapter.java
new file mode 100644
index 00000000..66f2db21
--- /dev/null
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/SearchAdapter.java
@@ -0,0 +1,129 @@
+/*
+ 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 2015 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.adapter;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.ViewGroup;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.MusicDirectory.Entry;
+import github.daneren2005.dsub.domain.SearchResult;
+import github.daneren2005.dsub.util.ImageLoader;
+import github.daneren2005.dsub.util.Util;
+import github.daneren2005.dsub.view.AlbumView;
+import github.daneren2005.dsub.view.ArtistView;
+import github.daneren2005.dsub.view.SongView;
+import github.daneren2005.dsub.view.UpdateView;
+
+import static github.daneren2005.dsub.adapter.ArtistAdapter.VIEW_TYPE_ARTIST;
+import static github.daneren2005.dsub.adapter.EntryGridAdapter.VIEW_TYPE_ALBUM_CELL;
+import static github.daneren2005.dsub.adapter.EntryGridAdapter.VIEW_TYPE_ALBUM_LINE;
+import static github.daneren2005.dsub.adapter.EntryGridAdapter.VIEW_TYPE_SONG;
+
+public class SearchAdapter extends SectionAdapter<Serializable> {
+ private SearchResult searchResult;
+ private ImageLoader imageLoader;
+ private boolean largeAlbums;
+
+ public SearchAdapter(Context context, SearchResult searchResult, ImageLoader imageLoader, boolean largeAlbums, OnItemClickedListener listener) {
+ this.context = context;
+ this.searchResult = searchResult;
+ this.imageLoader = imageLoader;
+ this.largeAlbums = largeAlbums;
+
+ this.sections = new ArrayList<>();
+ this.headers = new ArrayList<>();
+ Resources res = context.getResources();
+ if(!searchResult.getArtists().isEmpty()) {
+ this.sections.add((List<Serializable>) (List<?>) searchResult.getArtists());
+ this.headers.add(res.getString(R.string.search_artists));
+ }
+ if(!searchResult.getAlbums().isEmpty()) {
+ this.sections.add((List<Serializable>) (List<?>) searchResult.getAlbums());
+ this.headers.add(res.getString(R.string.search_albums));
+ }
+ if(!searchResult.getSongs().isEmpty()) {
+ this.sections.add((List<Serializable>) (List<?>) searchResult.getSongs());
+ this.headers.add(res.getString(R.string.search_songs));
+ }
+ this.onItemClickedListener = listener;
+ checkable = true;
+ }
+
+ @Override
+ public UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) {
+ UpdateView updateView = null;
+ if(viewType == VIEW_TYPE_ALBUM_CELL || viewType == VIEW_TYPE_ALBUM_LINE) {
+ updateView = new AlbumView(context, viewType == VIEW_TYPE_ALBUM_CELL);
+ } else if(viewType == VIEW_TYPE_SONG) {
+ updateView = new SongView(context);
+ } else if(viewType == VIEW_TYPE_ARTIST) {
+ updateView = new ArtistView(context);
+ }
+
+ return new UpdateView.UpdateViewHolder(updateView);
+ }
+
+ @Override
+ public void onBindViewHolder(UpdateView.UpdateViewHolder holder, Serializable item, int viewType) {
+ UpdateView view = holder.getUpdateView();
+ if(viewType == VIEW_TYPE_ALBUM_CELL || viewType == VIEW_TYPE_ALBUM_LINE) {
+ AlbumView albumView = (AlbumView) view;
+ albumView.setObject((Entry) item, imageLoader);
+ } else if(viewType == VIEW_TYPE_SONG) {
+ SongView songView = (SongView) view;
+ songView.setObject((Entry) item, true);
+ } else if(viewType == VIEW_TYPE_ARTIST) {
+ view.setObject(item);
+ }
+ }
+
+ @Override
+ public int getItemViewType(Serializable item) {
+ if(item instanceof Entry) {
+ Entry entry = (Entry) item;
+ if (entry.isDirectory()) {
+ if (largeAlbums) {
+ return VIEW_TYPE_ALBUM_CELL;
+ } else {
+ return VIEW_TYPE_ALBUM_LINE;
+ }
+ } else {
+ return VIEW_TYPE_SONG;
+ }
+ } else {
+ return VIEW_TYPE_ARTIST;
+ }
+ }
+
+ @Override
+ public void onCreateActionModeMenu(Menu menu, MenuInflater menuInflater) {
+ if(Util.isOffline(context)) {
+ menuInflater.inflate(R.menu.multiselect_media_offline, menu);
+ } else {
+ menuInflater.inflate(R.menu.multiselect_media, menu);
+ }
+
+ menu.removeItem(R.id.menu_remove_playlist);
+ }
+}
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/SectionAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/SectionAdapter.java
new file mode 100644
index 00000000..2dbdf1f1
--- /dev/null
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/SectionAdapter.java
@@ -0,0 +1,463 @@
+/*
+ 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 2015 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.adapter;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.Build;
+import android.support.v7.widget.PopupMenu;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.Toolbar;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.ActionMode;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.activity.SubsonicFragmentActivity;
+import github.daneren2005.dsub.util.MenuUtil;
+import github.daneren2005.dsub.view.BasicHeaderView;
+import github.daneren2005.dsub.view.UpdateView;
+import github.daneren2005.dsub.view.UpdateView.UpdateViewHolder;
+
+public abstract class SectionAdapter<T> extends RecyclerView.Adapter<UpdateViewHolder<T>> {
+ private static String TAG = SectionAdapter.class.getSimpleName();
+ public static int VIEW_TYPE_HEADER = 0;
+
+ protected Context context;
+ protected List<String> headers;
+ protected List<List<T>> sections;
+ protected boolean singleSectionHeader;
+ protected OnItemClickedListener<T> onItemClickedListener;
+ protected List<T> selected = new ArrayList<>();
+ protected List<UpdateView> selectedViews = new ArrayList<>();
+ protected ActionMode currentActionMode;
+ protected boolean checkable = false;
+
+ protected SectionAdapter() {}
+ public SectionAdapter(Context context, List<T> section) {
+ this(context, section, false);
+ }
+ public SectionAdapter(Context context, List<T> section, boolean singleSectionHeader) {
+ this.context = context;
+ this.headers = Arrays.asList("Section");
+ this.sections = new ArrayList<>();
+ this.sections.add(section);
+ this.singleSectionHeader = singleSectionHeader;
+ }
+ public SectionAdapter(Context context, List<String> headers, List<List<T>> sections) {
+ this(context, headers, sections, true);
+ }
+ public SectionAdapter(Context context, List<String> headers, List<List<T>> sections, boolean singleSectionHeader){
+ this.context = context;
+ this.headers = headers;
+ this.sections = sections;
+ this.singleSectionHeader = singleSectionHeader;
+ }
+
+ public void replaceExistingData(List<T> section) {
+ this.sections = new ArrayList<>();
+ this.sections.add(section);
+ notifyDataSetChanged();
+ }
+ public void replaceExistingData(List<String> headers, List<List<T>> sections) {
+ this.headers = headers;
+ this.sections = sections;
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public UpdateViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ if(viewType == VIEW_TYPE_HEADER) {
+ return onCreateHeaderHolder(parent);
+ } else {
+ final UpdateViewHolder<T> holder = onCreateSectionViewHolder(parent, viewType);
+ final UpdateView updateView = holder.getUpdateView();
+
+ if(updateView != null) {
+ updateView.getChildAt(0).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ T item = holder.getItem();
+ updateView.onClick();
+ if (currentActionMode != null) {
+ if(updateView.isCheckable()) {
+ if (selected.contains(item)) {
+ selected.remove(item);
+ selectedViews.remove(updateView);
+ setChecked(updateView, false);
+ } else {
+ selected.add(item);
+ selectedViews.add(updateView);
+ setChecked(updateView, true);
+ }
+
+ if (selected.isEmpty()) {
+ currentActionMode.finish();
+ } else {
+ currentActionMode.setTitle(context.getResources().getString(R.string.select_album_n_selected, selected.size()));
+ }
+ }
+ } else if (onItemClickedListener != null) {
+ onItemClickedListener.onItemClicked(item);
+ }
+ }
+ });
+
+ View moreButton = updateView.findViewById(R.id.more_button);
+ if(moreButton == null) {
+ moreButton = updateView.findViewById(R.id.item_more);
+ }
+ if (moreButton != null) {
+ moreButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ final T item = holder.getItem();
+ if(onItemClickedListener != null) {
+ PopupMenu popup = new PopupMenu(context, v);
+ onItemClickedListener.onCreateContextMenu(popup.getMenu(), popup.getMenuInflater(), updateView, item);
+
+ popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem menuItem) {
+ return onItemClickedListener.onContextItemSelected(menuItem, updateView, item);
+ }
+ });
+ popup.show();
+ }
+ }
+ });
+
+ if(checkable) {
+ updateView.getChildAt(0).setOnLongClickListener(new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ if(currentActionMode == null) {
+ startActionMode(holder);
+ } else {
+ updateView.getChildAt(0).performClick();
+ }
+ return true;
+ }
+ });
+ }
+ }
+ }
+
+ return holder;
+ }
+ }
+
+ @Override
+ public void onBindViewHolder(UpdateViewHolder holder, int position) {
+ UpdateView updateView = holder.getUpdateView();
+
+ if(sections.size() == 1 && !singleSectionHeader) {
+ T item = sections.get(0).get(position);
+ onBindViewHolder(holder, item, getItemViewType(position));
+ postBindView(updateView, item);
+ holder.setItem(item);
+ return;
+ }
+
+ int subPosition = 0;
+ int subHeader = 0;
+ for(List<T> section: sections) {
+ boolean validHeader = headers.get(subHeader) != null;
+ if(position == subPosition && validHeader) {
+ onBindHeaderHolder(holder, headers.get(subHeader));
+ return;
+ }
+
+ int headerOffset = validHeader ? 1 : 0;
+ if(position < (subPosition + section.size() + headerOffset)) {
+ T item = section.get(position - subPosition - headerOffset);
+ onBindViewHolder(holder, item, getItemViewType(item));
+
+ postBindView(updateView, item);
+ holder.setItem(item);
+ return;
+ }
+
+ subPosition += section.size();
+ if(validHeader) {
+ subPosition += 1;
+ }
+ subHeader++;
+ }
+ }
+
+ private void postBindView(UpdateView updateView, T item) {
+ if(updateView.isCheckable()) {
+ setChecked(updateView, selected.contains(item));
+ }
+
+ View moreButton = updateView.findViewById(R.id.more_button);
+ if(moreButton == null) {
+ moreButton = updateView.findViewById(R.id.item_more);
+ }
+ if(moreButton != null) {
+ PopupMenu popup = new PopupMenu(context, moreButton);
+ Menu menu = popup.getMenu();
+ onItemClickedListener.onCreateContextMenu(popup.getMenu(), popup.getMenuInflater(), updateView, item);
+ if(menu.size() == 0) {
+ moreButton.setVisibility(View.GONE);
+ } else {
+ moreButton.setVisibility(View.VISIBLE);
+ }
+ }
+ }
+
+ @Override
+ public int getItemCount() {
+ if(sections.size() == 1 && !singleSectionHeader) {
+ return sections.get(0).size();
+ }
+
+ int count = 0;
+ for(String header: headers) {
+ if(header != null) {
+ count++;
+ }
+ }
+ for(List<T> section: sections) {
+ count += section.size();
+ }
+
+ return count;
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ if(sections.size() == 1 && !singleSectionHeader) {
+ return getItemViewType(sections.get(0).get(position));
+ }
+
+ int subPosition = 0;
+ int subHeader = 0;
+ for(List<T> section: sections) {
+ boolean validHeader = headers.get(subHeader) != null;
+ if(position == subPosition && validHeader) {
+ return VIEW_TYPE_HEADER;
+ }
+
+ int headerOffset = validHeader ? 1 : 0;
+ if(position < (subPosition + section.size() + headerOffset)) {
+ return getItemViewType(section.get(position - subPosition - headerOffset));
+ }
+
+ subPosition += section.size();
+ if(validHeader) {
+ subPosition += 1;
+ }
+ subHeader++;
+ }
+
+ return -1;
+ }
+
+ public UpdateViewHolder onCreateHeaderHolder(ViewGroup parent) {
+ return new UpdateViewHolder(new BasicHeaderView(context));
+ }
+ public void onBindHeaderHolder(UpdateViewHolder holder, String header) {
+ UpdateView view = holder.getUpdateView();
+ if(view != null) {
+ view.setObject(header);
+ }
+ }
+
+ public T getItemForPosition(int position) {
+ if(sections.size() == 1 && !singleSectionHeader) {
+ return sections.get(0).get(position);
+ }
+
+ int subPosition = 0;
+ for(List<T> section: sections) {
+ if(position == subPosition) {
+ return null;
+ }
+
+ if(position <= (subPosition + section.size())) {
+ return section.get(position - subPosition - 1);
+ }
+
+ subPosition += section.size() + 1;
+ }
+
+ return null;
+ }
+ public int getItemPosition(T item) {
+ if(sections.size() == 1 && !singleSectionHeader) {
+ return sections.get(0).indexOf(item);
+ }
+
+ int subPosition = 0;
+ for(List<T> section: sections) {
+ subPosition += section.size() + 1;
+
+ int position = section.indexOf(item);
+ if(position != -1) {
+ return position + subPosition;
+ }
+ }
+
+ return -1;
+ }
+
+ public void setOnItemClickedListener(OnItemClickedListener<T> onItemClickedListener) {
+ this.onItemClickedListener = onItemClickedListener;
+ }
+
+ public void addSelected(T item) {
+ selected.add(item);
+ }
+ public List<T> getSelected() {
+ List<T> selected = new ArrayList<>();
+ selected.addAll(this.selected);
+ return selected;
+ }
+
+ public void clearSelected() {
+ // TODO: This needs to work with multiple sections
+ for(T item: selected) {
+ int index = sections.get(0).indexOf(item);
+
+ if(singleSectionHeader) {
+ index++;
+ }
+ }
+ selected.clear();
+
+ for(UpdateView updateView: selectedViews) {
+ updateView.setChecked(false);
+ }
+ }
+
+ public void removeItem(T item) {
+ int subPosition = 0;
+ for(List<T> section: sections) {
+ if(sections.size() > 1 || singleSectionHeader) {
+ subPosition++;
+ }
+
+ int index = section.indexOf(item);
+ if (index != -1) {
+ section.remove(item);
+ notifyItemRemoved(subPosition + index);
+ break;
+ }
+
+ subPosition += section.size();
+ }
+ }
+
+ public abstract UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType);
+ public abstract void onBindViewHolder(UpdateViewHolder holder, T item, int viewType);
+ public abstract int getItemViewType(T item);
+ public void setCheckable(boolean checkable) {
+ this.checkable = checkable;
+ }
+ public void setChecked(UpdateView updateView, boolean checked) {
+ updateView.setChecked(checked);
+ }
+ public void onCreateActionModeMenu(Menu menu, MenuInflater menuInflater) {}
+
+ private void startActionMode(final UpdateView.UpdateViewHolder<T> holder) {
+ final UpdateView<T> updateView = holder.getUpdateView();
+ if (context instanceof SubsonicFragmentActivity && currentActionMode == null) {
+ final SubsonicFragmentActivity fragmentActivity = (SubsonicFragmentActivity) context;
+ Toolbar toolbar = fragmentActivity.getActiveToolbar();
+
+ toolbar.startActionMode(new ActionMode.Callback() {
+ @Override
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ currentActionMode = mode;
+ onCreateActionModeMenu(menu, mode.getMenuInflater());
+ MenuUtil.hideMenuItems(context, menu);
+
+ T item = holder.getItem();
+ selected.add(item);
+ selectedViews.add(updateView);
+ setChecked(updateView, true);
+
+ mode.setTitle(context.getResources().getString(R.string.select_album_n_selected, selected.size()));
+ if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ TypedValue typedValue = new TypedValue();
+ Resources.Theme theme = context.getTheme();
+ theme.resolveAttribute(R.attr.colorPrimaryDark, typedValue, true);
+ int colorPrimaryDark = typedValue.data;
+
+ Window window = ((SubsonicFragmentActivity) context).getWindow();
+ window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
+ window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
+ window.setStatusBarColor(colorPrimaryDark);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ return false;
+ }
+
+ @Override
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ if (fragmentActivity.onOptionsItemSelected(item)) {
+ currentActionMode.finish();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public void onDestroyActionMode(ActionMode mode) {
+ currentActionMode = null;
+ selected.clear();
+ for(UpdateView<T> updateView: selectedViews) {
+ updateView.setChecked(false);
+ }
+ selectedViews.clear();
+
+ Window window = ((SubsonicFragmentActivity) context).getWindow();
+ window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
+ }
+ });
+ }
+ }
+ public void stopActionMode() {
+ if(currentActionMode != null) {
+ currentActionMode.finish();
+ }
+ }
+
+ public interface OnItemClickedListener<T> {
+ void onItemClicked(T item);
+ void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<T> updateView, T item);
+ boolean onContextItemSelected(MenuItem menuItem, UpdateView<T> updateView, T item);
+ }
+}
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/SettingsAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/SettingsAdapter.java
index 45c3ead1..d99b294b 100644
--- a/app/src/main/java/github/daneren2005/dsub/adapter/SettingsAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/SettingsAdapter.java
@@ -16,44 +16,83 @@
package github.daneren2005.dsub.adapter;
import android.content.Context;
+import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
import java.util.List;
import github.daneren2005.dsub.R;
import github.daneren2005.dsub.domain.User;
+import github.daneren2005.dsub.util.ImageLoader;
import github.daneren2005.dsub.view.SettingView;
+import github.daneren2005.dsub.view.UpdateView;
import static github.daneren2005.dsub.domain.User.Setting;
-public class SettingsAdapter extends ArrayAdapter<Setting> {
- private final Context context;
+public class SettingsAdapter extends SectionAdapter<Setting> {
+ public final int VIEW_TYPE_SETTING = 1;
+
+ private final User user;
private final boolean editable;
+ private final ImageLoader imageLoader;
- public SettingsAdapter(Context context, User user, boolean editable) {
- super(context, R.layout.basic_list_item, user.getSettings());
- this.context = context;
+ public SettingsAdapter(Context context, User user, ImageLoader imageLoader, boolean editable) {
+ super(context, user.getSettings(), imageLoader != null);
+ this.user = user;
+ this.imageLoader = imageLoader;
this.editable = editable;
+
+ List<Setting> settings = sections.get(0);
+ for(Setting setting: settings) {
+ if(setting.getValue()) {
+ addSelected(setting);
+ }
+ }
}
- public SettingsAdapter(Context context, List<Setting> settings, boolean editable) {
- super(context, R.layout.basic_list_item, settings);
- this.context = context;
- this.editable = editable;
+ public UpdateView.UpdateViewHolder onCreateHeaderHolder(ViewGroup parent) {
+ View header = LayoutInflater.from(context).inflate(R.layout.user_header, parent, false);
+ return new UpdateView.UpdateViewHolder(header, false);
}
+ public void onBindHeaderHolder(UpdateView.UpdateViewHolder holder, String description) {
+ View header = holder.getView();
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- Setting entry = getItem(position);
- SettingView view;
- if (convertView != null && convertView instanceof SettingView) {
- view = (SettingView) convertView;
+ 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 {
- view = new SettingView(context);
+ emailView.setVisibility(View.GONE);
+ }
+ }
+
+ @Override
+ public UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) {
+ return new UpdateView.UpdateViewHolder(new SettingView(context));
+ }
+
+ @Override
+ public void onBindViewHolder(UpdateView.UpdateViewHolder holder, Setting item, int viewType) {
+ holder.getUpdateView().setObject(item, editable);
+ }
+
+ @Override
+ public int getItemViewType(Setting item) {
+ return VIEW_TYPE_SETTING;
+ }
+
+ @Override
+ public void setChecked(UpdateView updateView, boolean checked) {
+ if(updateView instanceof SettingView) {
+ ((SettingView) updateView).setChecked(checked);
}
- view.setObject(entry, editable);
- return view;
}
}
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/ShareAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/ShareAdapter.java
index 4121a85a..6db3d927 100644
--- a/app/src/main/java/github/daneren2005/dsub/adapter/ShareAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/ShareAdapter.java
@@ -1,56 +1,49 @@
/*
- 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/>.
+ 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 2015 (C) Scott Jackson
+*/
- Copyright 2010 (C) Sindre Mehus
- */
package github.daneren2005.dsub.adapter;
import android.content.Context;
-import android.view.View;
import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
import java.util.List;
import github.daneren2005.dsub.domain.Share;
import github.daneren2005.dsub.view.ShareView;
+import github.daneren2005.dsub.view.UpdateView;
-/**
- * @author Sindre Mehus
-*/
-public class ShareAdapter extends ArrayAdapter<Share>{
- private Context activity;
- private List<Share> shares;
-
- public ShareAdapter(Context context, List<Share> shares) {
- super(context, android.R.layout.simple_list_item_1, shares);
- this.activity = context;
- this.shares = shares;
+public class ShareAdapter extends SectionAdapter<Share>{
+ public static int VIEW_TYPE_SHARE = 1;
+
+ public ShareAdapter(Context context, List<Share> shares, OnItemClickedListener listener) {
+ super(context, shares);
+ this.onItemClickedListener = listener;
}
-
+
@Override
- public View getView(int position, View convertView, ViewGroup parent) {
- Share share = shares.get(position);
- ShareView view;
- if (convertView != null && convertView instanceof ShareView) {
- view = (ShareView) convertView;
- } else {
- view = new ShareView(activity);
- }
- view.setObject(share);
- return view;
- }
+ public UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) {
+ return new UpdateView.UpdateViewHolder(new ShareView(context));
+ }
+
+ @Override
+ public void onBindViewHolder(UpdateView.UpdateViewHolder holder, Share item, int viewType) {
+ holder.getUpdateView().setObject(item);
+ }
+
+ @Override
+ public int getItemViewType(Share item) {
+ return VIEW_TYPE_SHARE;
+ }
}
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/UserAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/UserAdapter.java
index f0f78d97..95809e48 100644
--- a/app/src/main/java/github/daneren2005/dsub/adapter/UserAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/UserAdapter.java
@@ -25,28 +25,32 @@ import java.util.List;
import github.daneren2005.dsub.R;
import github.daneren2005.dsub.domain.User;
import github.daneren2005.dsub.util.ImageLoader;
+import github.daneren2005.dsub.view.UpdateView;
import github.daneren2005.dsub.view.UserView;
-public class UserAdapter extends ArrayAdapter<User> {
- private final Context activity;
+public class UserAdapter extends SectionAdapter<User> {
+ public static int VIEW_TYPE_USER = 1;
+
private final ImageLoader imageLoader;
- public UserAdapter(Context activity, List<User> users, ImageLoader imageLoader) {
- super(activity, R.layout.basic_list_item, users);
- this.activity = activity;
+ public UserAdapter(Context context, List<User> users, ImageLoader imageLoader, OnItemClickedListener listener) {
+ super(context, users);
this.imageLoader = imageLoader;
+ this.onItemClickedListener = listener;
+ }
+
+ @Override
+ public UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) {
+ return new UpdateView.UpdateViewHolder(new UserView(context));
+ }
+
+ @Override
+ public void onBindViewHolder(UpdateView.UpdateViewHolder holder, User item, int viewType) {
+ holder.getUpdateView().setObject(item, imageLoader);
}
@Override
- public View getView(int position, View convertView, ViewGroup parent) {
- User entry = getItem(position);
- UserView view;
- if (convertView != null && convertView instanceof UserView) {
- view = (UserView) convertView;
- } else {
- view = new UserView(activity);
- }
- view.setObject(entry, imageLoader);
- return view;
+ public int getItemViewType(User item) {
+ return VIEW_TYPE_USER;
}
} \ No newline at end of file
diff --git a/app/src/main/java/github/daneren2005/dsub/domain/Playlist.java b/app/src/main/java/github/daneren2005/dsub/domain/Playlist.java
index 7cd820c0..99b85ce9 100644
--- a/app/src/main/java/github/daneren2005/dsub/domain/Playlist.java
+++ b/app/src/main/java/github/daneren2005/dsub/domain/Playlist.java
@@ -19,6 +19,9 @@
package github.daneren2005.dsub.domain;
import java.io.Serializable;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
/**
* @author Sindre Mehus
@@ -125,4 +128,16 @@ public class Playlist implements Serializable {
Playlist playlist = (Playlist) o;
return playlist.id.equals(this.id);
}
+
+ public static class PlaylistComparator implements Comparator<Playlist> {
+ @Override
+ public int compare(Playlist playlist1, Playlist playlist2) {
+ return playlist1.getName().compareToIgnoreCase(playlist2.getName());
+ }
+
+ public static List<Playlist> sort(List<Playlist> playlists) {
+ Collections.sort(playlists, new PlaylistComparator());
+ return playlists;
+ }
+ }
}
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/AdminFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/AdminFragment.java
index 66ce5f15..69164036 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/AdminFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/AdminFragment.java
@@ -17,6 +17,7 @@ package github.daneren2005.dsub.fragments;
import android.os.Bundle;
import android.view.ContextMenu;
+import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
@@ -28,6 +29,7 @@ import java.util.ArrayList;
import java.util.List;
import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.adapter.SectionAdapter;
import github.daneren2005.dsub.domain.User;
import github.daneren2005.dsub.service.MusicService;
import github.daneren2005.dsub.service.parser.SubsonicRESTException;
@@ -36,8 +38,9 @@ import github.daneren2005.dsub.util.ProgressListener;
import github.daneren2005.dsub.util.UserUtil;
import github.daneren2005.dsub.util.Util;
import github.daneren2005.dsub.adapter.UserAdapter;
+import github.daneren2005.dsub.view.UpdateView;
-public class AdminFragment extends SelectListFragment<User> {
+public class AdminFragment extends SelectRecyclerFragment<User> {
private static String TAG = AdminFragment.class.getSimpleName();
@Override
@@ -56,22 +59,16 @@ public class AdminFragment extends SelectListFragment<User> {
}
@Override
- public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, view, menuInfo);
-
- MenuInflater inflater = context.getMenuInflater();
+ public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<User> updateView, User item) {
if(UserUtil.isCurrentAdmin()) {
- inflater.inflate(R.menu.admin_context, menu);
+ menuInflater.inflate(R.menu.admin_context, menu);
} else if(UserUtil.isCurrentRole(User.SETTINGS)) {
- inflater.inflate(R.menu.admin_context_user, menu);
+ menuInflater.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);
-
+ public boolean onContextItemSelected(MenuItem menuItem, UpdateView<User> updateView, User user) {
switch(menuItem.getItemId()) {
case R.id.admin_change_email:
UserUtil.changeEmail(context, user);
@@ -97,8 +94,8 @@ public class AdminFragment extends SelectListFragment<User> {
}
@Override
- public ArrayAdapter getAdapter(List<User> objs) {
- return new UserAdapter(context, objs, getImageLoader());
+ public SectionAdapter getAdapter(List<User> objs) {
+ return new UserAdapter(context, objs, getImageLoader(), this);
}
@Override
@@ -134,9 +131,7 @@ public class AdminFragment extends SelectListFragment<User> {
}
@Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- User user = (User) parent.getItemAtPosition(position);
-
+ public void onItemClicked(User user) {
SubsonicFragment fragment = new UserFragment();
Bundle args = new Bundle();
args.putSerializable(Constants.INTENT_EXTRA_NAME_ID, user);
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/DownloadFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/DownloadFragment.java
index 59229c3f..36653ff5 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/DownloadFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/DownloadFragment.java
@@ -16,10 +16,17 @@
package github.daneren2005.dsub.fragments;
import android.content.DialogInterface;
+import android.os.Bundle;
import android.os.Handler;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.helper.ItemTouchHelper;
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;
@@ -30,6 +37,7 @@ import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.adapter.SectionAdapter;
import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.service.DownloadFile;
import github.daneren2005.dsub.service.DownloadService;
@@ -38,13 +46,60 @@ import github.daneren2005.dsub.util.ProgressListener;
import github.daneren2005.dsub.util.SilentBackgroundTask;
import github.daneren2005.dsub.util.Util;
import github.daneren2005.dsub.adapter.DownloadFileAdapter;
+import github.daneren2005.dsub.view.SongView;
+import github.daneren2005.dsub.view.UpdateView;
-public class DownloadFragment extends SelectListFragment<DownloadFile> {
+public class DownloadFragment extends SelectRecyclerFragment<DownloadFile> {
private long currentRevision;
private ScheduledExecutorService executorService;
public DownloadFragment() {
serialize = false;
+ pullToRefresh = false;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
+ super.onCreateView(inflater, container, bundle);
+
+ ItemTouchHelper touchHelper = new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, ItemTouchHelper.LEFT) {
+ @Override
+ public boolean onMove(RecyclerView recyclerView, final RecyclerView.ViewHolder fromHolder, final RecyclerView.ViewHolder toHolder) {
+ new SilentBackgroundTask<Void>(context) {
+ private int from;
+ private int to;
+
+ @Override
+ protected Void doInBackground() throws Throwable {
+ from = fromHolder.getAdapterPosition();
+ to = toHolder.getAdapterPosition();
+ getDownloadService().swap(false, from, to);
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ adapter.notifyItemMoved(from, to);
+ }
+ }.execute();
+
+ return true;
+ }
+
+ @Override
+ public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
+ SongView songView = (SongView) ((UpdateView.UpdateViewHolder) viewHolder).getUpdateView();
+ DownloadFile downloadFile = songView.getDownloadFile();
+
+ DownloadService downloadService = getDownloadService();
+ downloadService.removeBackground(downloadFile);
+ adapter.removeItem(downloadFile);
+ currentRevision = downloadService.getDownloadListUpdateRevision();
+ }
+ });
+ touchHelper.attachToRecyclerView(recyclerView);
+
+ return rootView;
}
@Override
@@ -80,7 +135,7 @@ public class DownloadFragment extends SelectListFragment<DownloadFile> {
}
@Override
- public ArrayAdapter getAdapter(List<DownloadFile> objs) {
+ public SectionAdapter getAdapter(List<DownloadFile> objs) {
return new DownloadFileAdapter(context, objs);
}
@@ -91,9 +146,6 @@ public class DownloadFragment extends SelectListFragment<DownloadFile> {
return new ArrayList<DownloadFile>();
}
- listView.setOnScrollListener(null);
- refreshLayout.setEnabled(false);
-
List<DownloadFile> songList = new ArrayList<DownloadFile>();
songList.addAll(downloadService.getBackgroundDownloads());
currentRevision = downloadService.getDownloadListUpdateRevision();
@@ -106,8 +158,25 @@ public class DownloadFragment extends SelectListFragment<DownloadFile> {
}
@Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ public void onItemClicked(DownloadFile item) {
+
+ }
+
+ @Override
+ public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<DownloadFile> updateView, DownloadFile downloadFile) {
+ MusicDirectory.Entry selectedItem = downloadFile.getSong();
+ onCreateContextMenuSupport(menu, menuInflater, updateView, selectedItem);
+ if(!selectedItem.isVideo() && !Util.isOffline(context)) {
+ menu.removeItem(R.id.song_menu_remove_playlist);
+ }
+
+ recreateContextMenu(menu);
+ }
+ @Override
+ public boolean onContextItemSelected(MenuItem menuItem, UpdateView<DownloadFile> updateView, DownloadFile downloadFile) {
+ MusicDirectory.Entry selectedItem = downloadFile.getSong();
+ return onContextItemSelected(menuItem, selectedItem);
}
@Override
@@ -141,36 +210,6 @@ public class DownloadFragment extends SelectListFragment<DownloadFile> {
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) {
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/EqualizerFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/EqualizerFragment.java
index ea61f2c7..e2535e04 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/EqualizerFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/EqualizerFragment.java
@@ -104,6 +104,7 @@ public class EqualizerFragment extends SubsonicFragment {
});
setTitle(R.string.equalizer_label);
+ setSubtitle(null);
return rootView;
}
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/LyricsFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/LyricsFragment.java
index 826029f5..402bd257 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/LyricsFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/LyricsFragment.java
@@ -62,6 +62,7 @@ public final class LyricsFragment extends SubsonicFragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
setTitle(R.string.download_menu_lyrics);
+ setSubtitle(null);
rootView = inflater.inflate(R.layout.lyrics, container, false);
artistView = (TextView) rootView.findViewById(R.id.lyrics_artist);
titleView = (TextView) rootView.findViewById(R.id.lyrics_title);
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/MainFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/MainFragment.java
index ae38534a..d25315ac 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/MainFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/MainFragment.java
@@ -1,6 +1,7 @@
package github.daneren2005.dsub.fragments;
-import android.app.AlertDialog;
+import android.content.res.Resources;
+import android.support.v7.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
@@ -9,69 +10,43 @@ 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.adapter.MainAdapter;
+import github.daneren2005.dsub.adapter.SectionAdapter;
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.ProgressListener;
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 github.daneren2005.dsub.view.UpdateView;
+
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-public class MainFragment extends SubsonicFragment {
+public class MainFragment extends SelectRecyclerFragment<Integer> {
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();
+ public MainFragment() {
+ super();
+ pullToRefresh = false;
+ serialize = false;
+ backgroundUpdate = false;
}
@Override
@@ -116,159 +91,60 @@ public class MainFragment extends SubsonicFragment {
}
@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);
+ public int getOptionsMenu() {
+ return 0;
}
@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;
- }
+ public SectionAdapter getAdapter(List objs) {
+ List<List<Integer>> sections = new ArrayList<>();
+ List<String> headers = new ArrayList<>();
- @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);
+ List<Integer> offline = Arrays.asList(R.string.main_offline);
+ sections.add(offline);
+ headers.add(null);
+
+ List<Integer> albums = new ArrayList<>();
+ albums.add(R.string.main_albums_newest);
+ albums.add(R.string.main_albums_random);
if(ServerInfo.checkServerVersion(context, "1.8")) {
- adapter.addView(albumsAlphabeticalButton, true);
+ albums.add(R.string.main_albums_alphabetical);
}
if(!Util.isTagBrowsing(context)) {
- adapter.addView(albumsHighestButton, true);
+ albums.add(R.string.main_albums_highest);
}
- adapter.addViews(Arrays.asList(albumsStarredButton, albumsGenresButton, albumsYearButton, albumsRecentButton, albumsFrequentButton), true);
+ albums.add(R.string.main_albums_starred);
+ albums.add(R.string.main_albums_genres);
+ albums.add(R.string.main_albums_year);
+ albums.add(R.string.main_albums_recent);
+ albums.add(R.string.main_albums_highest);
+
+ sections.add(albums);
+ headers.add("albums");
+
if(ServerInfo.checkServerVersion(context, "1.8") && !Util.isTagBrowsing(context)) {
- adapter.addView(videoTitle, false);
- adapter.addView(videosButton, true);
+ List<Integer> videos = Arrays.asList(R.string.main_videos);
+ sections.add(videos);
+ headers.add("videos");
}
+ } else {
+ List<Integer> online = Arrays.asList(R.string.main_online);
+ sections.add(online);
+ headers.add(null);
}
- 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();
- }
+ return new MainAdapter(context, headers, sections, this);
}
- 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();
+ @Override
+ public List<Integer> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception {
+ return Arrays.asList(0);
+ }
- }
- Util.setActiveServer(context, instance);
- context.invalidate();
- UserUtil.refreshCurrentUser(context, false, true);
- }
+ @Override
+ public int getTitleResource() {
+ return R.string.common_appname;
}
private void toggleOffline() {
@@ -290,6 +166,7 @@ public class MainFragment extends SubsonicFragment {
}
UserUtil.seedCurrentUser(context);
+ context.updateDrawerHeader();
}
private void showAlbumList(String type) {
@@ -305,9 +182,6 @@ public class MainFragment extends SubsonicFragment {
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();
@@ -404,32 +278,54 @@ public class MainFragment extends SubsonicFragment {
}
private void showAboutDialog() {
- new LoadingTask<String>(context) {
+ new LoadingTask<Void>(context) {
+ Long[] used;
+ long bytesTotalFs;
+ long bytesAvailableFs;
+
@Override
- protected String doInBackground() throws Throwable {
+ protected Void 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));
+ bytesTotalFs = (long) stat.getBlockCount() * (long) stat.getBlockSize();
+ bytesAvailableFs = (long) stat.getAvailableBlocks() * (long) stat.getBlockSize();
+
+ used = FileUtil.getUsedSize(context, rootFolder);
+ return null;
}
@Override
- protected void done(String msg) {
+ protected void done(Void result) {
+ List<Integer> headers = new ArrayList<>();
+ List<String> details = new ArrayList<>();
+
+ headers.add(R.string.details_author);
+ details.add("Scott Jackson");
+
+ headers.add(R.string.details_email);
+ details.add("dsub.android@gmail.com");
+
try {
- Util.info(context, R.string.main_about_title, msg);
+ headers.add(R.string.details_version);
+ details.add(context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName);
} catch(Exception e) {
- Util.toast(context, "Failed to open dialog");
+ details.add("");
}
+
+ Resources res = context.getResources();
+ headers.add(R.string.details_files_cached);
+ details.add(Long.toString(used[0]));
+
+ headers.add(R.string.details_files_permanent);
+ details.add(Long.toString(used[1]));
+
+ headers.add(R.string.details_used_space);
+ details.add(res.getString(R.string.details_of, Util.formatLocalizedBytes(used[2], context), Util.formatLocalizedBytes(Util.getCacheSizeMB(context) * 1024L * 1024L, context)));
+
+ headers.add(R.string.details_available_space);
+ details.add(res.getString(R.string.details_of, Util.formatLocalizedBytes(bytesAvailableFs, context), Util.formatLocalizedBytes(bytesTotalFs, context)));
+
+ Util.showDetailsDialog(context, R.string.main_about_title, headers, details);
}
}.execute();
}
@@ -506,81 +402,39 @@ public class MainFragment extends SubsonicFragment {
}.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);
+
+ @Override
+ public void onItemClicked(Integer item) {
+ if(item == R.string.main_offline || item == R.string.main_online) {
+ toggleOffline();
+ } else if (item == R.string.main_albums_newest) {
+ showAlbumList("newest");
+ } else if (item == R.string.main_albums_random) {
+ showAlbumList("random");
+ } else if (item == R.string.main_albums_highest) {
+ showAlbumList("highest");
+ } else if (item == R.string.main_albums_recent) {
+ showAlbumList("recent");
+ } else if (item == R.string.main_albums_frequent) {
+ showAlbumList("frequent");
+ } else if (item == R.string.main_albums_starred) {
+ showAlbumList("starred");
+ } else if(item == R.string.main_albums_genres) {
+ showAlbumList("genres");
+ } else if(item == R.string.main_albums_year) {
+ showAlbumList("years");
+ } else if(item == R.string.main_albums_alphabetical) {
+ showAlbumList("alphabeticalByName");
+ } else if(item == R.string.main_videos) {
+ showVideos();
}
}
+
+ @Override
+ public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<Integer> updateView, Integer item) {}
+
+ @Override
+ public boolean onContextItemSelected(MenuItem menuItem, UpdateView<Integer> updateView, Integer item) {
+ return false;
+ }
}
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/NowPlayingFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/NowPlayingFragment.java
index 3e30af50..00b780a5 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/NowPlayingFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/NowPlayingFragment.java
@@ -20,7 +20,8 @@ import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
-import android.app.AlertDialog;
+import android.annotation.TargetApi;
+import android.support.v7.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
@@ -30,6 +31,9 @@ import android.os.Bundle;
import android.os.Handler;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.MediaRouteButton;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.helper.ItemTouchHelper;
import android.util.Log;
import android.view.ContextMenu;
import android.view.Display;
@@ -54,6 +58,7 @@ import android.widget.TextView;
import android.widget.ViewFlipper;
import github.daneren2005.dsub.R;
import github.daneren2005.dsub.activity.SubsonicFragmentActivity;
+import github.daneren2005.dsub.adapter.SectionAdapter;
import github.daneren2005.dsub.audiofx.EqualizerController;
import github.daneren2005.dsub.domain.Bookmark;
import github.daneren2005.dsub.domain.PlayerState;
@@ -61,6 +66,7 @@ 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.DownloadService.OnSongChangedListener;
import github.daneren2005.dsub.service.MusicService;
import github.daneren2005.dsub.service.MusicServiceFactory;
import github.daneren2005.dsub.service.OfflineException;
@@ -69,6 +75,7 @@ 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.SongView;
import github.daneren2005.dsub.view.UpdateView;
import github.daneren2005.dsub.util.Util;
@@ -78,14 +85,12 @@ 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 {
+public class NowPlayingFragment extends SubsonicFragment implements OnGestureListener, SectionAdapter.OnItemClickedListener<DownloadFile>, OnSongChangedListener {
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;
@@ -96,7 +101,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
private TextView emptyTextView;
private TextView songTitleTextView;
private ImageView albumArtImageView;
- private DragSortListView playlistView;
+ private RecyclerView playlistView;
private TextView positionTextView;
private TextView durationTextView;
private TextView statusTextView;
@@ -121,13 +126,12 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
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;
+ private int currentPlayingSize = 0;
+ private MenuItem timerMenu;
/**
* Called when the activity is first created.
@@ -141,6 +145,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
startFlipped = true;
}
}
+ primaryFragment = false;
}
@Override
@@ -153,11 +158,8 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
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();
@@ -173,7 +175,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
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);
@@ -185,6 +186,45 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
rateGoodButton = (ImageButton) rootView.findViewById(R.id.download_rating_good);
toggleListButton =rootView.findViewById(R.id.download_toggle_list);
+ playlistView = (RecyclerView)rootView.findViewById(R.id.download_list);
+ setupLayoutManager(playlistView, false);
+ ItemTouchHelper touchHelper = new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, ItemTouchHelper.LEFT) {
+ @Override
+ public boolean onMove(RecyclerView recyclerView, final RecyclerView.ViewHolder fromHolder, final RecyclerView.ViewHolder toHolder) {
+ new SilentBackgroundTask<Void>(context) {
+ private int from;
+ private int to;
+
+ @Override
+ protected Void doInBackground() throws Throwable {
+ from = fromHolder.getAdapterPosition();
+ to = toHolder.getAdapterPosition();
+ getDownloadService().swap(true, from, to);
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ songListAdapter.notifyItemMoved(from, to);
+ }
+ }.execute();
+
+ return true;
+ }
+
+ @Override
+ public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
+ SongView songView = (SongView) ((UpdateView.UpdateViewHolder) viewHolder).getUpdateView();
+ DownloadFile downloadFile = songView.getDownloadFile();
+
+ DownloadService downloadService = getDownloadService();
+ downloadService.remove(downloadFile);
+ songListAdapter.removeItem(downloadFile);
+ currentRevision = downloadService.getDownloadListUpdateRevision();
+ }
+ });
+ touchHelper.attachToRecyclerView(playlistView);
+
starButton = (ImageButton)rootView.findViewById(R.id.download_star);
if(Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_MENU_STAR, true)) {
starButton.setOnClickListener(new OnClickListener() {
@@ -196,7 +236,11 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
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);
+ if(currentSong.isStarred()) {
+ starButton.setImageDrawable(DrawableTint.getTintedDrawable(context, R.drawable.ic_toggle_star));
+ } else {
+ starButton.setImageResource(R.drawable.ic_toggle_star_outline_dark);
+ }
}
});
}
@@ -239,12 +283,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
getDownloadService().previous();
return null;
}
-
- @Override
- protected void done(Void result) {
- onCurrentChanged();
- onProgressChanged();
- }
}.execute();
setControlsVisible(true);
}
@@ -265,14 +303,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
getDownloadService().next();
return true;
}
-
- @Override
- protected void done(Boolean result) {
- if (result) {
- onCurrentChanged();
- onProgressChanged();
- }
- }
}.execute();
setControlsVisible(true);
}
@@ -292,12 +322,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
getDownloadService().pause();
return null;
}
-
- @Override
- protected void done(Void result) {
- onCurrentChanged();
- onProgressChanged();
- }
}.execute();
}
});
@@ -311,12 +335,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
getDownloadService().reset();
return null;
}
-
- @Override
- protected void done(Void result) {
- onCurrentChanged();
- onProgressChanged();
- }
}.execute();
}
});
@@ -331,12 +349,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
start();
return null;
}
-
- @Override
- protected void done(Void result) {
- onCurrentChanged();
- onProgressChanged();
- }
}.execute();
}
});
@@ -346,7 +358,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
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);
@@ -392,21 +403,21 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
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));
+ rateBadButton.setImageResource(DrawableTint.getDrawableRes(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);
+ rateBadButton.setImageDrawable(DrawableTint.getTintedDrawable(context, 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.setImageResource(DrawableTint.getDrawableRes(context, R.attr.rating_good));
}
}
}
@@ -432,18 +443,18 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
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.setImageResource(DrawableTint.getDrawableRes(context, R.attr.rating_good));
}
} else {
// Otherwise set rating to maximum
setRating(entry, 5);
- rateGoodButton.setImageResource(R.drawable.ic_action_rating_good_selected);
+ rateGoodButton.setImageDrawable(DrawableTint.getTintedDrawable(context, 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));
+ rateBadButton.setImageResource(DrawableTint.getDrawableRes(context, R.attr.rating_bad));
}
}
}
@@ -482,7 +493,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
@Override
protected void done(Void result) {
seekInProgress = false;
- NowPlayingFragment.this.onProgressChanged();
}
}.execute();
}
@@ -500,55 +510,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
}
}
});
- 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);
@@ -565,10 +526,11 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
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.getSleepTimer()) {
+ int timeRemaining = downloadService.getSleepTimeRemaining();
+ timerMenu = menu.findItem(R.id.menu_toggle_timer);
+ timerMenu.setTitle(context.getResources().getString(R.string.download_stop_time_remaining, Util.formatDuration(timeRemaining)));
}
if(downloadService != null && downloadService.getKeepScreenOn()) {
menu.findItem(R.id.menu_screen_on_off).setChecked(true);
@@ -604,47 +566,30 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
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;
+ public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<DownloadFile> updateView, DownloadFile downloadFile) {
+ if(Util.isOffline(context)) {
+ menuInflater.inflate(R.menu.nowplaying_context_offline, menu);
+ } else {
+ menuInflater.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 (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);
+ if (downloadFile.getSong().getParent() == null) {
+ menu.findItem(R.id.menu_show_album).setVisible(false);
+ menu.findItem(R.id.menu_show_artist).setVisible(false);
}
+
+ MenuUtil.hideMenuItems(context, menu);
}
@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);
+ public boolean onContextItemSelected(MenuItem menuItem, UpdateView<DownloadFile> updateView, DownloadFile downloadFile) {
+ return menuItemSelected(menuItem.getItemId(), downloadFile);
}
private boolean menuItemSelected(int menuItemId, final DownloadFile song) {
@@ -712,20 +657,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
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());
@@ -745,7 +677,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
@Override
protected void done(Void result) {
- onDownloadListChanged();
+ context.closeNowPlaying();
}
}.execute();
}
@@ -841,26 +773,18 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
@Override
public void onResume() {
super.onResume();
-
+ if(this.primaryFragment) {
+ onResumeHandlers();
+ } else {
+ update();
+ }
+ }
+ private void onResumeHandlers() {
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();
+ final DownloadService downloadService = getDownloadService();
if (downloadService == null || downloadService.getCurrentPlaying() == null || startFlipped) {
playlistFlipper.setDisplayedChild(1);
}
@@ -875,48 +799,62 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
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);
- }
+ context.runWhenServiceAvailable(new Runnable() {
+ @Override
+ public void run() {
+ if(primaryFragment) {
+ DownloadService downloadService = getDownloadService();
+ downloadService.startRemoteScan();
+ downloadService.addOnSongChangedListener(NowPlayingFragment.this, true);
+ }
+ }
+ });
}
@Override
public void onPause() {
super.onPause();
- executorService.shutdown();
- if(getDownloadService() != null) {
- getDownloadService().stopRemoteScan();
+ onPauseHandlers();
+ }
+ private void onPauseHandlers() {
+ if(executorService != null) {
+ DownloadService downloadService = getDownloadService();
+ if (downloadService != null) {
+ downloadService.stopRemoteScan();
+ downloadService.removeOnSongChangeListener(this);
+ }
+ playlistFlipper.setDisplayedChild(0);
}
}
-
+
@Override
public void setPrimaryFragment(boolean primary) {
super.setPrimaryFragment(primary);
if(rootView != null) {
if(primary) {
- mainLayout.setVisibility(View.VISIBLE);
- updateButtons();
+ onResumeHandlers();
} else {
- mainLayout.setVisibility(View.GONE);
+ onPauseHandlers();
}
}
}
+ @Override
+ public void setTitle(int title) {
+ this.title = context.getResources().getString(title);
+ if(this.primaryFragment) {
+ context.setTitle(this.title);
+ }
+ }
+ @Override
+ public void setSubtitle(CharSequence title) {
+ this.subtitle = title;
+ if(this.primaryFragment) {
+ context.setSubtitle(title);
+ }
+ }
+
private void scheduleHideControls() {
if (hideControlsFuture != null) {
hideControlsFuture.cancel(false);
@@ -954,7 +892,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
if(context == null) {
return;
}
-
+
if(Util.isOffline(context)) {
bookmarkButton.setVisibility(View.GONE);
rateBadButton.setVisibility(View.GONE);
@@ -971,46 +909,33 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
}
// Scroll to current playing/downloading.
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
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;
- }
+ // Try to get position of current playing/downloading
+ int position = songListAdapter.getItemPosition(currentPlaying);
+ if(position == -1) {
+ DownloadFile currentDownloading = getDownloadService().getCurrentDownloading();
+ position = songListAdapter.getItemPosition(currentDownloading);
}
- DownloadFile currentDownloading = getDownloadService().getCurrentDownloading();
- for (int i = 0; i < songListAdapter.getCount(); i++) {
- if (currentDownloading == playlistView.getItemAtPosition(i)) {
- playlistView.setSelectionFromTop(i, 40);
- return;
- }
+
+ // If found, scroll to it
+ if(position != -1) {
+ // RecyclerView.scrollToPosition just puts it on the screen (ie: bottom if scrolled below it)
+ LinearLayoutManager layoutManager = (LinearLayoutManager) playlistView.getLayoutManager();
+ layoutManager.scrollToPositionWithOffset(position, 0);
}
}
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() {
@@ -1043,26 +968,26 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
- lengthBar.setProgress(length - 1);
+ 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());
+ .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();
+ 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);
+ getDownloadService().setSleepTimerDuration(length);
+ getDownloadService().startSleepTimer();
+ context.supportInvalidateOptionsMenu();
+ }
+ })
+ .setNegativeButton(R.string.common_cancel, null);
AlertDialog dialog = builder.create();
dialog.show();
}
@@ -1087,7 +1012,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
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();
}
}
@@ -1108,258 +1033,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
}
}
}
- 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) {
- if(Util.isWifiRequiredForDownload(context)) {
- statusTextView.setText(context.getResources().getString(R.string.download_playerstate_mobile_disabled));
- } else {
- 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();
@@ -1395,33 +1068,33 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
}
}.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();
+ .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);
+ createBookmark(currentDownload, comment);
+ }
+ })
+ .setNegativeButton(R.string.common_cancel, null);
AlertDialog dialog = builder.create();
dialog.show();
}
@@ -1430,13 +1103,13 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
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);
-
+ bookmarkButton.setImageDrawable(DrawableTint.getTintedDrawable(context, R.drawable.ic_menu_bookmark_selected));
+
new SilentBackgroundTask<Void>(context) {
@Override
protected Void doInBackground() throws Throwable {
@@ -1458,30 +1131,30 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
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);
+ bookmark = DrawableTint.getDrawableRes(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();
@@ -1538,11 +1211,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
downloadService.seekTo(downloadService.getPlayerPosition() - DownloadService.REWIND);
break;
}
-
- onProgressChanged();
- if(performAction == ACTION_NEXT || performAction == ACTION_PREVIOUS) {
- onCurrentChanged();
- }
return null;
}
}.execute();
@@ -1570,4 +1238,204 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
public boolean onSingleTapUp(MotionEvent e) {
return false;
}
+
+ @Override
+ public void onItemClicked(final DownloadFile item) {
+ warnIfStorageUnavailable();
+ new SilentBackgroundTask<Void>(context) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ getDownloadService().play(item);
+ return null;
+ }
+ }.execute();
+ }
+
+ @Override
+ public void onSongChanged(DownloadFile currentPlaying, int currentPlayingIndex) {
+ this.currentPlaying = currentPlaying;
+ if (currentPlaying != null) {
+ Entry song = currentPlaying.getSong();
+ songTitleTextView.setText(song.getTitle());
+ getImageLoader().loadImage(albumArtImageView, song, true, true);
+ if(song.isStarred()) {
+ starButton.setImageDrawable(DrawableTint.getTintedDrawable(context, R.drawable.ic_toggle_star));
+ } else {
+ starButton.setImageResource(R.drawable.ic_toggle_star_outline_dark);
+ }
+ setSubtitle(context.getResources().getString(R.string.download_playing_out_of, currentPlayingIndex + 1, currentPlayingSize));
+
+ int badRating, goodRating, bookmark;
+ if(song.getRating() == 1) {
+ rateBadButton.setImageDrawable(DrawableTint.getTintedDrawable(context, 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 = DrawableTint.getDrawableRes(context, R.attr.rating_bad);
+ }
+ rateBadButton.setImageResource(badRating);
+ }
+
+ if(song.getRating() == 5) {
+ rateGoodButton.setImageDrawable(DrawableTint.getTintedDrawable(context, 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 = DrawableTint.getDrawableRes(context, R.attr.rating_good);
+ }
+ rateGoodButton.setImageResource(goodRating);
+ }
+
+ if(song.getBookmark() != null) {
+ bookmarkButton.setImageDrawable(DrawableTint.getTintedDrawable(context, R.drawable.ic_menu_bookmark_selected));
+ } else {
+ if(context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
+ bookmark = R.drawable.ic_menu_bookmark_dark;
+ } else {
+ bookmark = DrawableTint.getDrawableRes(context, R.attr.bookmark);
+ }
+ bookmarkButton.setImageResource(bookmark);
+ }
+ } else {
+ songTitleTextView.setText(null);
+ getImageLoader().loadImage(albumArtImageView, (Entry) null, true, false);
+ starButton.setImageResource(R.drawable.ic_toggle_star_outline_dark);
+ setSubtitle(null);
+ }
+ }
+
+ @Override
+ public void onSongsChanged(List<DownloadFile> songs, DownloadFile currentPlaying, int currentPlayingIndex) {
+ currentPlayingSize = songs.size();
+
+ DownloadService downloadService = getDownloadService();
+ if(downloadService.isShufflePlayEnabled()) {
+ emptyTextView.setText(R.string.download_shuffle_loading);
+ }
+ else {
+ emptyTextView.setText(R.string.download_empty);
+ }
+
+ if(songListAdapter == null) {
+ songList = new ArrayList<>();
+ songList.addAll(songs);
+ playlistView.setAdapter(songListAdapter = new DownloadFileAdapter(context, songList, NowPlayingFragment.this));
+ } else {
+ songList.clear();
+ songList.addAll(songs);
+ songListAdapter.notifyDataSetChanged();
+ }
+
+ emptyTextView.setVisibility(songs.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 + 1, currentPlayingSize));
+ if(this.currentPlaying != currentPlaying) {
+ onSongChanged(currentPlaying, currentPlayingIndex);
+ }
+ }
+
+ @Override
+ public void onSongProgress(DownloadFile currentPlaying, int millisPlayed, Integer duration, boolean isSeekable) {
+ 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);
+ }
+
+ DownloadService downloadService = getDownloadService();
+ if(downloadService.getSleepTimer() && timerMenu != null) {
+ int timeRemaining = downloadService.getSleepTimeRemaining();
+ timerMenu.setTitle(context.getResources().getString(R.string.download_stop_time_remaining, Util.formatDuration(timeRemaining)));
+ }
+ }
+
+ @Override
+ public void onStateUpdate(DownloadFile downloadFile, PlayerState playerState) {
+ switch (playerState) {
+ case DOWNLOADING:
+ if(currentPlaying != null) {
+ if(Util.isWifiRequiredForDownload(context)) {
+ statusTextView.setText(context.getResources().getString(R.string.download_playerstate_mobile_disabled));
+ } else {
+ 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;
+ }
+ }
}
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SearchFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SearchFragment.java
index 0f1598dd..f442dabb 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SearchFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SearchFragment.java
@@ -1,12 +1,16 @@
package github.daneren2005.dsub.fragments;
+import java.io.Serializable;
import java.util.ArrayList;
-import java.util.List;
import java.util.Arrays;
+import java.util.List;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.widget.SwipeRefreshLayout;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.util.Log;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -14,11 +18,13 @@ 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.adapter.ArtistAdapter;
+import github.daneren2005.dsub.adapter.EntryGridAdapter;
+import github.daneren2005.dsub.adapter.SearchAdapter;
+import github.daneren2005.dsub.adapter.SectionAdapter;
import github.daneren2005.dsub.domain.Artist;
import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.domain.SearchCritera;
@@ -26,40 +32,24 @@ 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;
+import github.daneren2005.dsub.view.UpdateView;
-public class SearchFragment extends SubsonicFragment {
+public class SearchFragment extends SubsonicFragment implements SectionAdapter.OnItemClickedListener<Serializable> {
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_ALBUMS = 10;
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;
+
+ protected RecyclerView recyclerView;
+ protected SearchAdapter adapter;
+ protected boolean largeAlbums = false;
+
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;
@@ -70,6 +60,7 @@ public class SearchFragment extends SubsonicFragment {
if(savedInstanceState != null) {
searchResult = (SearchResult) savedInstanceState.getSerializable(Constants.FRAGMENT_LIST);
}
+ largeAlbums = Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_LARGE_ALBUM_ART, true);
}
@Override
@@ -80,63 +71,43 @@ public class SearchFragment extends SubsonicFragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
- rootView = inflater.inflate(R.layout.abstract_list_fragment, container, false);
+ rootView = inflater.inflate(R.layout.abstract_recycler_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);
+ recyclerView = (RecyclerView) rootView.findViewById(R.id.fragment_recycler);
+ setupLayoutManager(recyclerView, largeAlbums);
- 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);
+ registerForContextMenu(recyclerView);
context.onNewIntent(context.getIntent());
if(searchResult != null) {
skipSearch = true;
- populateList();
+ recyclerView.setAdapter(adapter = new SearchAdapter(context, searchResult, getImageLoader(), largeAlbums, this));
}
return rootView;
}
@Override
+ public GridLayoutManager.SpanSizeLookup getSpanSizeLookup() {
+ final int columns = getRecyclerColumnCount();
+ return new GridLayoutManager.SpanSizeLookup() {
+ @Override
+ public int getSpanSize(int position) {
+ int viewType = adapter.getItemViewType(position);
+ if(viewType == EntryGridAdapter.VIEW_TYPE_SONG || viewType == EntryGridAdapter.VIEW_TYPE_HEADER || viewType == ArtistAdapter.VIEW_TYPE_ARTIST) {
+ return columns;
+ } else {
+ return 1;
+ }
+ }
+ };
+ }
+
+ @Override
public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
menuInflater.inflate(R.menu.search, menu);
}
@@ -154,43 +125,52 @@ public class SearchFragment extends SubsonicFragment {
}
@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)) {
+ public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<Serializable> updateView, Serializable item) {
+ onCreateContextMenuSupport(menu, menuInflater, updateView, item);
+ if(item instanceof MusicDirectory.Entry && !((MusicDirectory.Entry) item).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;
- }
+ public boolean onContextItemSelected(MenuItem menuItem, UpdateView<Serializable> updateView, Serializable item) {
+ return onContextItemSelected(menuItem, item);
+ }
- return true;
+ @Override
+ public void refresh(boolean refresh) {
+ context.onNewIntent(context.getIntent());
}
-
+
@Override
- public void setPrimaryFragment(boolean primary) {
- super.setPrimaryFragment(primary);
+ public void onItemClicked(Serializable item) {
+ Log.d(TAG, item.getClass().getSimpleName());
+ 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);
+ }
+ }
}
@Override
- public void refresh(boolean refresh) {
- context.onNewIntent(context.getIntent());
+ protected List<MusicDirectory.Entry> getSelectedEntries() {
+ List<Serializable> selected = adapter.getSelected();
+ List<MusicDirectory.Entry> selectedMedia = new ArrayList<>();
+ for(Serializable ser: selected) {
+ if(ser instanceof MusicDirectory.Entry) {
+ selectedMedia.add((MusicDirectory.Entry) ser);
+ }
+ }
+
+ return selectedMedia;
}
public void search(final String query, final boolean autoplay) {
@@ -200,9 +180,6 @@ public class SearchFragment extends SubsonicFragment {
}
currentQuery = query;
- mergeAdapter = new MergeAdapter();
- list.setAdapter(mergeAdapter);
-
BackgroundTask<SearchResult> task = new TabBackgroundTask<SearchResult>(this) {
@Override
protected SearchResult doInBackground() throws Throwable {
@@ -214,7 +191,7 @@ public class SearchFragment extends SubsonicFragment {
@Override
protected void done(SearchResult result) {
searchResult = result;
- populateList();
+ recyclerView.setAdapter(adapter = new SearchAdapter(context, searchResult, getImageLoader(), largeAlbums, SearchFragment.this));
if (autoplay) {
autoplay(query);
}
@@ -224,82 +201,6 @@ public class SearchFragment extends SubsonicFragment {
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();
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectArtistFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectArtistFragment.java
index 5488c95b..a9d5afd6 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectArtistFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectArtistFragment.java
@@ -11,10 +11,11 @@ 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.adapter.ArtistAdapter;
+import github.daneren2005.dsub.adapter.SectionAdapter;
import github.daneren2005.dsub.domain.Artist;
import github.daneren2005.dsub.domain.Indexes;
import github.daneren2005.dsub.domain.MusicDirectory;
@@ -23,19 +24,16 @@ 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 github.daneren2005.dsub.view.UpdateView;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
-public class SelectArtistFragment extends SelectListFragment<Artist> {
+public class SelectArtistFragment extends SelectRecyclerFragment<Artist> implements ArtistAdapter.OnMusicFolderChanged {
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;
@@ -75,110 +73,44 @@ public class SelectArtistFragment extends SelectListFragment<Artist> {
}
}
- 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);
- }
-
+ public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<Artist> updateView, Artist item) {
+ onCreateContextMenuSupport(menu, menuInflater, updateView, item);
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;
+ public boolean onContextItemSelected(MenuItem menuItem, UpdateView<Artist> updateView, Artist item) {
+ return onContextItemSelected(menuItem, item);
}
@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);
+ public void onItemClicked(Artist artist) {
+ 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);
}
-
- replaceFragment(fragment);
+ 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);
}
- }
- @Override
- public void onFinishRefresh() {
- setMusicFolders();
+ replaceFragment(fragment);
}
@Override
@@ -215,9 +147,8 @@ public class SelectArtistFragment extends SelectListFragment<Artist> {
}
@Override
- public ArrayAdapter getAdapter(List<Artist> objects) {
- createMusicFolderButton();
- return new ArtistAdapter(context, objects);
+ public SectionAdapter getAdapter(List<Artist> objects) {
+ return new ArtistAdapter(context, objects, musicFolders, this, this);
}
@Override
@@ -236,12 +167,12 @@ public class SelectArtistFragment extends SelectListFragment<Artist> {
String musicFolderId = Util.getSelectedMusicFolderId(context);
Indexes indexes = musicService.getIndexes(musicFolderId, refresh, context, listener);
- artists = new ArrayList<Artist>(indexes.getShortcuts().size() + indexes.getArtists().size());
+ artists = new ArrayList<>(indexes.getShortcuts().size() + indexes.getArtists().size());
artists.addAll(indexes.getShortcuts());
artists.addAll(indexes.getArtists());
entries = indexes.getEntries();
} else {
- artists = new ArrayList<Artist>();
+ artists = new ArrayList<>();
MusicDirectory dir = musicService.getMusicDirectory(groupId, groupName, refresh, context, listener);
for(MusicDirectory.Entry entry: dir.getChildren(true, false)) {
Artist artist = new Artist();
@@ -251,7 +182,7 @@ public class SelectArtistFragment extends SelectListFragment<Artist> {
artists.add(artist);
}
- entries = new ArrayList<MusicDirectory.Entry>();
+ entries = new ArrayList<>();
entries.addAll(dir.getChildren(false, true));
if(!entries.isEmpty()) {
Artist root = new Artist();
@@ -270,32 +201,14 @@ public class SelectArtistFragment extends SelectListFragment<Artist> {
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);
+ recyclerView.setAdapter(new ArtistAdapter(context, objects, musicFolders, this, this));
+ recyclerView.setVisibility(View.VISIBLE);
View view = rootView.findViewById(R.id.tab_progress);
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) view.getLayoutParams();
@@ -305,29 +218,19 @@ public class SelectArtistFragment extends SelectListFragment<Artist> {
}
}
- 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();
}
+
+ @Override
+ public void onMusicFolderChanged(MusicFolder selectedFolder) {
+ String startMusicFolderId = Util.getSelectedMusicFolderId(context);
+ String musicFolderId = selectedFolder == null ? null : selectedFolder.getId();
+
+ if(!Util.equals(startMusicFolderId, musicFolderId)) {
+ Util.setSelectedMusicFolderId(context, musicFolderId);
+ context.invalidate();
+ }
+ }
}
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectBookmarkFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectBookmarkFragment.java
index c71d99f6..d992319b 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectBookmarkFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectBookmarkFragment.java
@@ -19,43 +19,40 @@
package github.daneren2005.dsub.fragments;
import android.view.ContextMenu;
+import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.activity.DownloadActivity;
+import github.daneren2005.dsub.adapter.SectionAdapter;
import github.daneren2005.dsub.domain.Bookmark;
import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.service.DownloadService;
import github.daneren2005.dsub.service.MusicService;
+import github.daneren2005.dsub.util.MenuUtil;
import github.daneren2005.dsub.util.ProgressListener;
import github.daneren2005.dsub.util.SilentBackgroundTask;
import github.daneren2005.dsub.util.Util;
import github.daneren2005.dsub.adapter.BookmarkAdapter;
+import github.daneren2005.dsub.view.UpdateView;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-public class SelectBookmarkFragment extends SelectListFragment<MusicDirectory.Entry> {
+public class SelectBookmarkFragment extends SelectRecyclerFragment<MusicDirectory.Entry> {
private static final String TAG = SelectBookmarkFragment.class.getSimpleName();
@Override
- public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, view, menuInfo);
-
- MenuInflater inflater = context.getMenuInflater();
- inflater.inflate(R.menu.select_bookmark_context, menu);
-
- hideMenuItems(menu, (AdapterView.AdapterContextMenuInfo) menuInfo);
+ public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<MusicDirectory.Entry> updateView, MusicDirectory.Entry item) {
+ menuInflater.inflate(R.menu.select_bookmark_context, menu);
+ MenuUtil.hideMenuItems(context, menu);
}
@Override
- public boolean onContextItemSelected(MenuItem menuItem) {
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
- MusicDirectory.Entry bookmark = objects.get(info.position);
-
+ public boolean onContextItemSelected(MenuItem menuItem, UpdateView<MusicDirectory.Entry> updateView, MusicDirectory.Entry bookmark) {
switch(menuItem.getItemId()) {
case R.id.bookmark_menu_info:
displayBookmarkInfo(bookmark);
@@ -64,12 +61,8 @@ public class SelectBookmarkFragment extends SelectListFragment<MusicDirectory.En
deleteBookmark(bookmark, adapter);
return true;
}
-
- if(onContextItemSelected(menuItem, bookmark)) {
- return true;
- }
- return true;
+ return onContextItemSelected(menuItem, bookmark);
}
@Override
@@ -78,8 +71,8 @@ public class SelectBookmarkFragment extends SelectListFragment<MusicDirectory.En
}
@Override
- public ArrayAdapter getAdapter(List<MusicDirectory.Entry> bookmarks) {
- return new BookmarkAdapter(context, bookmarks);
+ public SectionAdapter getAdapter(List<MusicDirectory.Entry> bookmarks) {
+ return new BookmarkAdapter(context, bookmarks, this);
}
@Override
@@ -93,13 +86,12 @@ public class SelectBookmarkFragment extends SelectListFragment<MusicDirectory.En
}
@Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ public void onItemClicked(final MusicDirectory.Entry bookmark) {
final DownloadService downloadService = getDownloadService();
if(downloadService == null) {
return;
}
- final MusicDirectory.Entry bookmark = (MusicDirectory.Entry) parent.getItemAtPosition(position);
new SilentBackgroundTask<Void>(context) {
@Override
protected Void doInBackground() throws Throwable {
@@ -110,22 +102,42 @@ public class SelectBookmarkFragment extends SelectListFragment<MusicDirectory.En
@Override
protected void done(Void result) {
- Util.startActivityWithoutTransition(context, DownloadActivity.class);
+ context.openNowPlaying();
}
}.execute();
}
-
+
private void displayBookmarkInfo(final MusicDirectory.Entry entry) {
Bookmark bookmark = entry.getBookmark();
- String comment = bookmark.getComment();
- if(comment == null) {
- comment = "";
+ List<Integer> headers = new ArrayList<>();
+ List<String> details = new ArrayList<>();
+
+ headers.add(R.string.details_song);
+ details.add(entry.getTitle());
+
+ if(entry.getArtist() != null) {
+ headers.add(R.string.details_artist);
+ details.add(entry.getArtist());
+ }
+ if(entry.getAlbum() != null) {
+ headers.add(R.string.details_album);
+ details.add(entry.getAlbum());
+ }
+
+ headers.add(R.string.details_position);
+ details.add(Util.formatDuration(bookmark.getPosition() / 1000));
+
+ headers.add(R.string.details_created);
+ details.add(Util.formatDate(bookmark.getCreated()));
+
+ headers.add(R.string.details_updated);
+ details.add(Util.formatDate(bookmark.getChanged()));
+
+ if(bookmark.getComment() != null) {
+ headers.add(R.string.details_comments);
+ details.add(bookmark.getComment());
}
- String msg = context.getResources().getString(R.string.bookmark_details,
- entry.getTitle(), Util.formatDuration(bookmark.getPosition() / 1000),
- Util.formatDate(bookmark.getCreated()), Util.formatDate(bookmark.getChanged()), comment);
-
- Util.info(context, R.string.bookmark_details_title, msg, false);
+ Util.showDetailsDialog(context, R.string.bookmark_details_title, headers, details);
}
}
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java
index 841a6369..5fdd5c5f 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java
@@ -1,7 +1,7 @@
package github.daneren2005.dsub.fragments;
import android.annotation.TargetApi;
-import android.app.AlertDialog;
+import android.support.v7.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
@@ -9,12 +9,14 @@ import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.widget.SwipeRefreshLayout;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
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;
@@ -22,31 +24,27 @@ 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.adapter.EntryInfiniteGridAdapter;
+import github.daneren2005.dsub.adapter.EntryGridAdapter;
+import github.daneren2005.dsub.adapter.SectionAdapter;
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.DrawableTint;
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;
@@ -59,9 +57,9 @@ 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.GridSpacingDecoration;
import github.daneren2005.dsub.view.MyLeadingMarginSpan2;
+import github.daneren2005.dsub.view.UpdateView;
import java.util.ArrayList;
import java.util.Arrays;
@@ -70,17 +68,14 @@ import java.util.Set;
import static github.daneren2005.dsub.domain.MusicDirectory.Entry;
-public class SelectDirectoryFragment extends SubsonicFragment implements AdapterView.OnItemClickListener {
+public class SelectDirectoryFragment extends SubsonicFragment implements SectionAdapter.OnItemClickedListener<Entry> {
private static final String TAG = SelectDirectoryFragment.class.getSimpleName();
- private GridView albumList;
- private ListView entryList;
+ private RecyclerView recyclerView;
+ private EntryGridAdapter entryGridAdapter;
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;
@@ -114,7 +109,6 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
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;
}
@@ -172,28 +166,35 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
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);
+ recyclerView = (RecyclerView) rootView.findViewById(R.id.select_album_entries);
+ recyclerView.setHasFixedSize(true);
+ setupScrollList(recyclerView);
- setupScrollList(albumList);
+ if(largeAlbums) {
+ final int columns = context.getResources().getInteger(R.integer.Grid_Columns);
+ GridLayoutManager gridLayoutManager = new GridLayoutManager(context, columns);
+ gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
+ @Override
+ public int getSpanSize(int position) {
+ int viewType = entryGridAdapter.getItemViewType(position);
+ if(viewType == EntryGridAdapter.VIEW_TYPE_SONG || viewType == EntryGridAdapter.VIEW_TYPE_HEADER || viewType == EntryInfiniteGridAdapter.VIEW_TYPE_LOADING) {
+ return columns;
+ } else {
+ return 1;
+ }
+ }
+ });
+ recyclerView.addItemDecoration(new GridSpacingDecoration());
+ recyclerView.setLayoutManager(gridLayoutManager);
+ } else {
+ LinearLayoutManager layoutManager = new LinearLayoutManager(context);
+ layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
+ recyclerView.setLayoutManager(layoutManager);
}
- registerForContextMenu(entryList);
- setupAlbumList();
if(entries == null) {
if(primaryFragment || secondaryFragment) {
@@ -273,36 +274,6 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
@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;
@@ -331,32 +302,8 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
}
@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);
+ public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView updateView, Entry entry) {
+ onCreateContextMenuSupport(menu, menuInflater, updateView, 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);
}
@@ -378,88 +325,70 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
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)) {
+ public boolean onContextItemSelected(MenuItem menuItem, UpdateView<Entry> updateView, Entry entry) {
+ if(onContextItemSelected(menuItem, entry)) {
return true;
}
switch (menuItem.getItemId()) {
case R.id.song_menu_remove_playlist:
- removeFromPlaylist(playlistId, playlistName, Arrays.<Integer>asList(info.position - headers));
+ removeFromPlaylist(playlistId, playlistName, Arrays.<Integer>asList(entries.indexOf(entry)));
break;
case R.id.song_menu_server_download:
- downloadPodcastEpisode((PodcastEpisode)selectedItem);
+ downloadPodcastEpisode((PodcastEpisode) entry);
break;
case R.id.song_menu_server_delete:
- deletePodcastEpisode((PodcastEpisode)selectedItem);
+ deletePodcastEpisode((PodcastEpisode) entry);
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);
+ public void onItemClicked(Entry entry) {
+ 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.isAlbum()) {
+ 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;
+ }
- 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));
+ } else {
+ List<Entry> songs = new ArrayList<Entry>();
+
+ if(Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_PLAY_NOW_AFTER, false)) {
+ Iterator it = entries.listIterator(entries.indexOf(entry));
+ while(it.hasNext()) {
+ songs.add((Entry) it.next());
}
-
- playNow(Arrays.asList(entry));
+ } else {
+ songs.add(entry);
}
+
+ playNow(songs);
}
}
@@ -478,8 +407,8 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
if(currentTask != null) {
currentTask.cancel();
}
-
- entryList.setVisibility(View.INVISIBLE);
+
+ recyclerView.setVisibility(View.INVISIBLE);
if (playlistId != null) {
getPlaylist(playlistId, playlistName, refresh);
} else if(podcastId != null) {
@@ -491,7 +420,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
getShare(share, refresh);
}
} else if (albumListType != null) {
- getAlbumList(albumListType, albumListSize);
+ getAlbumList(albumListType, albumListSize, refresh);
} else {
if(showAll) {
getRecursiveMusicDirectory(id, name, refresh);
@@ -626,7 +555,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
}.execute();
}
- private void getAlbumList(final String albumListType, final int size) {
+ private void getAlbumList(final String albumListType, final int size, final boolean refresh) {
if ("newest".equals(albumListType)) {
setTitle(R.string.main_albums_newest);
} else if ("random".equals(albumListType)) {
@@ -652,7 +581,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
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);
+ result = service.getAlbumList(albumListType, albumListExtra, size, 0, refresh, context, this);
if(result.getChildrenSize() == 0 && "genres".equals(albumListType)) {
SelectDirectoryFragment.this.albumListType = "genres-songs";
result = service.getSongsByGenre(albumListExtra, size, 0, context, this);
@@ -660,7 +589,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
} 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);
+ result = service.getAlbumList(albumListType, size, 0, refresh, context, this);
}
return result;
}
@@ -686,11 +615,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
licenseValid = musicService.isLicenseValid(context, this);
albums = dir.getChildren(true, false);
- if(largeAlbums) {
- entries = dir.getChildren(false, true);
- } else {
- entries = dir.getChildren();
- }
+ entries = dir.getChildren();
// This isn't really an artist if no albums on it!
if(albums.size() == 0) {
@@ -727,121 +652,127 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
}
}
+ @Override
+ protected SectionAdapter<Entry> getCurrentAdapter() {
+ return entryGridAdapter;
+ }
+
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();
+ boolean validData = !entries.isEmpty() || !albums.isEmpty();
+ if(!validData) {
+ setEmpty(true);
+ }
- if(header != null && artistInfoDelayed != null) {
- final View finalHeader = header.findViewById(R.id.select_album_header);
- final View headerProgress = header.findViewById(R.id.header_progress);
+ if(validData) {
+ recyclerView.setVisibility(View.VISIBLE);
+ }
- finalHeader.setVisibility(View.INVISIBLE);
- headerProgress.setVisibility(View.VISIBLE);
+ if(albumListType == null || "starred".equals(albumListType)) {
+ entryGridAdapter = new EntryGridAdapter(context, entries, getImageLoader(), largeAlbums);
+ entryGridAdapter.setRemoveFromPlaylist(playlistId != null);
+ } else {
+ entryGridAdapter = new EntryInfiniteGridAdapter(context, entries, getImageLoader(), largeAlbums);
- new SilentBackgroundTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- artistInfo = musicService.getArtistInfo(artistInfoDelayed, false, true, context, this);
+ // Setup infinite loading based on scrolling
+ final EntryInfiniteGridAdapter infiniteGridAdapter = (EntryInfiniteGridAdapter) entryGridAdapter;
+ infiniteGridAdapter.setData(albumListType, albumListExtra, albumListSize);
- return null;
- }
+ recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
+ @Override
+ public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
+ super.onScrollStateChanged(recyclerView, newState);
+ }
- @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);
+ @Override
+ public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
+ super.onScrolled(recyclerView, dx, dy);
+
+ RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
+ int totalItemCount = layoutManager.getItemCount();
+ int lastVisibleItem;
+ if(layoutManager instanceof GridLayoutManager) {
+ lastVisibleItem = ((GridLayoutManager) layoutManager).findLastVisibleItemPosition();
+ } else if(layoutManager instanceof LinearLayoutManager) {
+ lastVisibleItem = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
+ } else {
+ return;
}
- }.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);
+ if(totalItemCount > 0 && lastVisibleItem >= totalItemCount - 2) {
+ infiniteGridAdapter.loadMore();
+ }
}
- }
- addAlbumHeader = false;
+ });
}
-
- boolean validData = !entries.isEmpty() || !albums.isEmpty();
- if(!validData) {
- setEmpty(true);
+ entryGridAdapter.setOnItemClickedListener(this);
+ // Always show artist if this is not a artist we are viewing
+ if(!artist) {
+ entryGridAdapter.setShowArtist(true);
+ }
+ // Podcasts are not checkable
+ if(podcastId != null) {
+ entryGridAdapter.setCheckable(false);
}
- // 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);
+ // 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
+ boolean addedHeader = false;
+ if(albumListType == null && !"root".equals(id) && (!artist || artistInfo != null || artistInfoDelayed != null)) {
+ View header = createHeader();
+
+ if(header != null) {
+ if (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) {
+ setupCoverArt(finalHeader);
+ setupTextDisplay(finalHeader);
+ setupButtonEvents(finalHeader);
+
+ finalHeader.setVisibility(View.VISIBLE);
+ headerProgress.setVisibility(View.GONE);
+ }
+ }.execute();
+ }
+
+ entryGridAdapter.setHeader(header);
+ addedHeader = true;
}
}
- entryList.setAdapter(listAdapter);
- if(validData) {
- entryList.setVisibility(View.VISIBLE);
- }
- context.supportInvalidateOptionsMenu();
+ int scrollToPosition = -1;
if(lookupEntry != null) {
for(int i = 0; i < entries.size(); i++) {
if(lookupEntry.equals(entries.get(i).getTitle())) {
- entryList.setSelection(i + entryList.getHeaderViewsCount());
+ scrollToPosition = i;
+ entryGridAdapter.addSelected(entries.get(i));
lookupEntry = null;
break;
}
}
}
+ recyclerView.setAdapter(entryGridAdapter);
+ context.supportInvalidateOptionsMenu();
+
+ if(scrollToPosition != -1) {
+ recyclerView.scrollToPosition(scrollToPosition + (addedHeader ? 1 : 0));
+ }
+
Bundle args = getArguments();
boolean playAll = args.getBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, false);
if (playAll && !restoredInstance) {
@@ -849,169 +780,52 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
}
}
- 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);
+ @Override
+ protected void playNow(final boolean shuffle, final boolean append, final boolean playNext) {
+ List<Entry> songs = getSelectedEntries();
+ if(!songs.isEmpty()) {
+ download(songs, append, false, !append, playNext, shuffle);
+ entryGridAdapter.clearSelected();
}
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;
- }
+ boolean hasSubFolders = !albums.isEmpty();
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));
+ download(entries, append, false, !append, false, shuffle);
}
}
- 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<Entry> selected = entryGridAdapter.getSelected();
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);
- }
+ for(Entry entry: selected) {
+ indexes.add(entries.indexOf(entry));
}
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()));
- }
- }
- };
-
+ @Override
+ protected void executeOnValid(RecursiveLoader onValid) {
checkLicenseAndTrialPeriod(onValid);
}
- private void downloadBackground(final boolean save) {
+
+ @Override
+ protected void downloadBackground(final boolean save) {
+ List<Entry> songs = getSelectedEntries();
if(playlistId != null) {
- selectAll(true, false);
+ songs = entries;
}
- List<Entry> songs = getSelectedSongs();
if(songs.isEmpty()) {
// Get both songs and albums
downloadRecursively(id, save, false, false, false, true);
@@ -1019,21 +833,23 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
downloadBackground(save, songs);
}
}
- private void downloadBackground(final boolean save, final List<Entry> songs) {
+ @Override
+ protected void downloadBackground(final boolean save, final List<Entry> entries) {
if (getDownloadService() == null) {
return;
}
warnIfStorageUnavailable();
- LoadingTask<Void> onValid = new LoadingTask<Void>(context) {
+ RecursiveLoader onValid = new RecursiveLoader(context) {
@Override
- protected Void doInBackground() throws Throwable {
+ protected Boolean doInBackground() throws Throwable {
+ getSongsRecursively(entries, songs);
getDownloadService().downloadBackground(songs, save);
return null;
}
@Override
- protected void done(Void result) {
+ protected void done(Boolean result) {
Util.toast(context, context.getResources().getQuantityString(R.plurals.select_album_n_songs_downloading, songs.size(), songs.size()));
}
};
@@ -1041,15 +857,21 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
checkLicenseAndTrialPeriod(onValid);
}
- private void delete() {
- List<Entry> songs = getSelectedSongs();
- if(songs.isEmpty()) {
- selectAll(true, false);
- songs = getSelectedSongs();
+ @Override
+ protected void download(List<Entry> entries, boolean append, boolean save, boolean autoplay, boolean playNext, boolean shuffle) {
+ download(entries, append, save, autoplay, playNext, shuffle, playlistName, playlistId);
+ }
- // Also delete all directories
- for(Entry album: albums) {
- deleteRecursively(album);
+ @Override
+ protected void delete() {
+ List<Entry> songs = getSelectedEntries();
+ if(songs.isEmpty()) {
+ for(Entry entry: entries) {
+ if(entry.isDirectory()) {
+ deleteRecursively(entry);
+ } else {
+ songs.add(entry);
+ }
}
}
if (getDownloadService() != null) {
@@ -1068,11 +890,9 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
@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));
+ for(Integer index: indexes) {
+ entryGridAdapter.removeAt(index);
}
- entryAdapter.notifyDataSetChanged();
Util.toast(context, context.getResources().getString(R.string.removed_playlist, indexes.size(), name));
}
@@ -1144,7 +964,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
public void onClick(DialogInterface dialog, int which) {
new LoadingTask<Void>(context, true) {
@Override
- protected Void doInBackground() throws Throwable {
+ protected Void doInBackground() throws Throwable {
MusicService musicService = MusicServiceFactory.getMusicService(context);
musicService.deletePodcastEpisode(episode.getEpisodeId(), episode.getParent(), null, context);
if (getDownloadService() != null) {
@@ -1157,8 +977,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
@Override
protected void done(Void result) {
- entries.remove(episode);
- entryAdapter.notifyDataSetChanged();
+ entryGridAdapter.removeItem(episode);
}
@Override
@@ -1172,7 +991,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
}
public void unstarSelected() {
- List<Entry> selected = getSelectedSongs();
+ List<Entry> selected = getSelectedEntries();
if(selected.size() == 0) {
selected = entries;
}
@@ -1219,10 +1038,8 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
Util.toast(context, context.getResources().getString(R.string.starring_content_unstarred, Integer.toString(unstar.size())));
for(Entry entry: unstar) {
- entries.remove(entry);
+ entryGridAdapter.removeItem(entry);
}
- entryAdapter.notifyDataSetChanged();
- selectAll(false, false);
}
@Override
@@ -1335,31 +1152,19 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
@Override
protected void done(Void result) {
- Util.startActivityWithoutTransition(context, DownloadActivity.class);
+ context.openNowPlaying();
}
}.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;
- }
+ View header = LayoutInflater.from(context).inflate(R.layout.select_album_header, null, false);
setupCoverArt(header);
setupTextDisplay(header);
+ setupButtonEvents(header);
- if(add) {
- setupButtonEvents(header);
- }
-
- if(add) {
- return header;
- } else {
- return null;
- }
+ return header;
}
private void setupCoverArt(View header) {
@@ -1508,11 +1313,6 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
} else {
artistView.setMaxLines(minLines);
}
-
- if(albumList instanceof HeaderGridView) {
- HeaderGridView headerGridView = (HeaderGridView) albumList;
- ((BaseAdapter) headerGridView.getAdapter()).notifyDataSetChanged();
- }
}
});
artistView.setMovementMethod(LinkMovementMethod.getInstance());
@@ -1559,14 +1359,23 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
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);
+ if(directory.isStarred()) {
+ starButton.setImageDrawable(DrawableTint.getTintedDrawable(context, R.drawable.ic_toggle_star));
+ } else {
+ starButton.setImageResource(DrawableTint.getDrawableRes(context, R.attr.star_outline));
+ }
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);
+ if(directory.isStarred()) {
+ starButton.setImageResource(DrawableTint.getDrawableRes(context, R.attr.star_outline));
+ starButton.setImageDrawable(DrawableTint.getTintedDrawable(context, R.drawable.ic_toggle_star));
+ } else {
+ starButton.setImageResource(DrawableTint.getDrawableRes(context, R.attr.star_outline));
+ }
}
});
}
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectGenreFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectGenreFragment.java
index 2d310172..cb57c280 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectGenreFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectGenreFragment.java
@@ -1,37 +1,39 @@
/*
- This file is part of Subsonic.
+ 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 2015 (C) Scott Jackson
+*/
- 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.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.adapter.SectionAdapter;
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 github.daneren2005.dsub.view.UpdateView;
import java.util.List;
-public class SelectGenreFragment extends SelectListFragment<Genre> {
+public class SelectGenreFragment extends SelectRecyclerFragment<Genre> {
private static final String TAG = SelectGenreFragment.class.getSimpleName();
@Override
@@ -40,8 +42,8 @@ public class SelectGenreFragment extends SelectListFragment<Genre> {
}
@Override
- public ArrayAdapter getAdapter(List<Genre> objs) {
- return new GenreAdapter(context, objs);
+ public SectionAdapter getAdapter(List<Genre> objs) {
+ return new GenreAdapter(context, objs, this);
}
@Override
@@ -55,9 +57,7 @@ public class SelectGenreFragment extends SelectListFragment<Genre> {
}
@Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- Genre genre = (Genre) parent.getItemAtPosition(position);
-
+ public void onItemClicked(Genre genre) {
SubsonicFragment fragment = new SelectDirectoryFragment();
Bundle args = new Bundle();
args.putString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE, "genres");
@@ -68,4 +68,12 @@ public class SelectGenreFragment extends SelectListFragment<Genre> {
replaceFragment(fragment);
}
+
+ @Override
+ public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<Genre> updateView, Genre item) {}
+
+ @Override
+ public boolean onContextItemSelected(MenuItem menuItem, UpdateView<Genre> updateView, Genre item) {
+ return false;
+ }
}
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectListFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectListFragment.java
deleted file mode 100644
index 6f73f6e8..00000000
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectListFragment.java
+++ /dev/null
@@ -1,163 +0,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 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/app/src/main/java/github/daneren2005/dsub/fragments/SelectPlaylistFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectPlaylistFragment.java
index 3d7e664f..a2f1aabd 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectPlaylistFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectPlaylistFragment.java
@@ -1,19 +1,21 @@
package github.daneren2005.dsub.fragments;
-import android.app.AlertDialog;
+import android.support.v7.app.AlertDialog;
import android.content.DialogInterface;
+import android.content.res.Resources;
import android.os.Bundle;
import android.support.v4.app.FragmentTransaction;
+import android.support.v7.widget.RecyclerView;
import android.view.ContextMenu;
+import android.view.Menu;
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.adapter.SectionAdapter;
import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.domain.Playlist;
import github.daneren2005.dsub.domain.ServerInfo;
@@ -30,31 +32,37 @@ 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 github.daneren2005.dsub.view.UpdateView;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
-public class SelectPlaylistFragment extends SelectListFragment<Playlist> {
+public class SelectPlaylistFragment extends SelectRecyclerFragment<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);
+ public void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+ if (Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_LARGE_ALBUM_ART, true)) {
+ largeAlbums = true;
+ }
+ }
- MenuInflater inflater = context.getMenuInflater();
+ @Override
+ public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<Playlist> updateView, Playlist playlist) {
if (Util.isOffline(context)) {
- inflater.inflate(R.menu.select_playlist_context_offline, menu);
+ menuInflater.inflate(R.menu.select_playlist_context_offline, menu);
}
else {
- inflater.inflate(R.menu.select_playlist_context, menu);
+ menuInflater.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())) {
@@ -67,48 +75,14 @@ public class SelectPlaylistFragment extends SelectListFragment<Playlist> {
}
@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;
+ public boolean onContextItemSelected(MenuItem menuItem, UpdateView<Playlist> updateView, Playlist playlist) {
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;
@@ -118,10 +92,9 @@ public class SelectPlaylistFragment extends SelectListFragment<Playlist> {
case R.id.playlist_update_info:
updatePlaylistInfo(playlist);
break;
- default:
- return false;
}
- return true;
+
+ return false;
}
@Override
@@ -130,8 +103,31 @@ public class SelectPlaylistFragment extends SelectListFragment<Playlist> {
}
@Override
- public ArrayAdapter getAdapter(List<Playlist> playlists) {
- return new PlaylistAdapter(context, playlists);
+ public SectionAdapter<Playlist> getAdapter(List<Playlist> playlists) {
+ List<Playlist> mine = new ArrayList<>();
+ List<Playlist> shared = new ArrayList<>();
+
+ String currentUsername = UserUtil.getCurrentUsername(context);
+ for(Playlist playlist: playlists) {
+ if(playlist.getOwner() == null || playlist.getOwner().equals(currentUsername)) {
+ mine.add(playlist);
+ } else {
+ shared.add(playlist);
+ }
+ }
+
+ if(shared.isEmpty()) {
+ return new PlaylistAdapter(context, playlists, getImageLoader(), largeAlbums, this);
+ } else {
+ Resources res = context.getResources();
+ List<String> headers = Arrays.asList(res.getString(R.string.playlist_mine), res.getString(R.string.playlist_shared));
+
+ List<List<Playlist>> sections = new ArrayList<>();
+ sections.add(mine);
+ sections.add(shared);
+
+ return new PlaylistAdapter(context, headers, sections, getImageLoader(), largeAlbums, this);
+ }
}
@Override
@@ -149,9 +145,7 @@ public class SelectPlaylistFragment extends SelectListFragment<Playlist> {
}
@Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- Playlist playlist = (Playlist) parent.getItemAtPosition(position);
-
+ public void onItemClicked(Playlist playlist) {
SubsonicFragment fragment = new SelectDirectoryFragment();
Bundle args = new Bundle();
args.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID, playlist.getId());
@@ -179,8 +173,7 @@ public class SelectPlaylistFragment extends SelectListFragment<Playlist> {
@Override
protected void done(Void result) {
- adapter.remove(playlist);
- adapter.notifyDataSetChanged();
+ adapter.removeItem(playlist);
Util.toast(context, context.getResources().getString(R.string.menu_deleted_playlist, playlist.getName()));
}
@@ -201,12 +194,33 @@ public class SelectPlaylistFragment extends SelectListFragment<Playlist> {
}
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);
+ List<Integer> headers = new ArrayList<>();
+ List<String> details = new ArrayList<>();
+
+ if(playlist.getOwner() != null) {
+ headers.add(R.string.details_owner);
+ details.add(playlist.getOwner());
+ }
+
+ if(playlist.getComment() != null) {
+ headers.add(R.string.details_comments);
+ details.add(playlist.getComment());
+ }
+
+ headers.add(R.string.details_song_count);
+ details.add(playlist.getSongCount());
+
+ if(playlist.getPublic() != null) {
+ headers.add(R.string.details_public);
+ details.add(Boolean.toString(playlist.getPublic()));
+ }
+
+ if(playlist.getCreated() != null) {
+ headers.add(R.string.details_created);
+ details.add(Util.formatDate(context, playlist.getCreated()));
+ }
+
+ Util.showDetailsDialog(context, R.string.details_title_playlist, headers, details);
}
private void updatePlaylistInfo(final Playlist playlist) {
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectPodcastsFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectPodcastsFragment.java
index 3a564f1c..381453c0 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectPodcastsFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectPodcastsFragment.java
@@ -1,33 +1,30 @@
/*
- 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
- */
+ 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 2015 (C) Scott Jackson
+*/
package github.daneren2005.dsub.fragments;
-import android.app.AlertDialog;
+import android.support.v7.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.ContextMenu;
+import android.view.Menu;
+import android.view.MenuInflater;
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.adapter.SectionAdapter;
import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.domain.PodcastChannel;
import github.daneren2005.dsub.service.MusicService;
@@ -42,15 +39,12 @@ 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 github.daneren2005.dsub.view.UpdateView;
import java.util.ArrayList;
import java.util.List;
-/**
- *
- * @author Scott
- */
-public class SelectPodcastsFragment extends SelectListFragment<PodcastChannel> {
+public class SelectPodcastsFragment extends SelectRecyclerFragment<PodcastChannel> {
private static final String TAG = SelectPodcastsFragment.class.getSimpleName();
@Override
@@ -70,38 +64,26 @@ public class SelectPodcastsFragment extends SelectListFragment<PodcastChannel> {
return false;
}
-
- @Override
- public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, view, menuInfo);
- android.view.MenuInflater inflater = context.getMenuInflater();
+ @Override
+ public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<PodcastChannel> updateView, PodcastChannel podcast) {
if(!Util.isOffline(context) && UserUtil.canPodcast()) {
- inflater.inflate(R.menu.select_podcasts_context, menu);
+ menuInflater.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);
+ menuInflater.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);
-
+ public boolean onContextItemSelected(MenuItem menuItem, UpdateView<PodcastChannel> updateView, PodcastChannel channel) {
switch (menuItem.getItemId()) {
case R.id.podcast_menu_sync:
syncPodcast(channel);
@@ -116,7 +98,7 @@ public class SelectPodcastsFragment extends SelectListFragment<PodcastChannel> {
deletePodcast(channel);
break;
}
-
+
return true;
}
@@ -126,8 +108,8 @@ public class SelectPodcastsFragment extends SelectListFragment<PodcastChannel> {
}
@Override
- public ArrayAdapter getAdapter(List<PodcastChannel> channels) {
- return new PodcastChannelAdapter(context, channels);
+ public SectionAdapter getAdapter(List<PodcastChannel> channels) {
+ return new PodcastChannelAdapter(context, channels, this);
}
@Override
@@ -141,9 +123,7 @@ public class SelectPodcastsFragment extends SelectListFragment<PodcastChannel> {
}
@Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- PodcastChannel channel = (PodcastChannel) parent.getItemAtPosition(position);
-
+ public void onItemClicked(PodcastChannel channel) {
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())) {
@@ -159,7 +139,7 @@ public class SelectPodcastsFragment extends SelectListFragment<PodcastChannel> {
replaceFragment(fragment);
}
}
-
+
public void refreshPodcasts() {
new SilentBackgroundTask<Void>(context) {
@Override
@@ -234,13 +214,29 @@ public class SelectPodcastsFragment extends SelectListFragment<PodcastChannel> {
}
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);
+ List<Integer> headers = new ArrayList<>();
+ List<String> details = new ArrayList<>();
+
+ if(channel.getName() != null) {
+ headers.add(R.string.details_title);
+ details.add(channel.getName());
+ }
+
+ headers.add(R.string.details_url);
+ details.add(channel.getUrl());
+ headers.add(R.string.details_status);
+ details.add(channel.getStatus());
+
+ if(channel.getErrorMessage() != null) {
+ headers.add(R.string.details_error);
+ details.add(channel.getErrorMessage());
+ }
+ if(channel.getDescription() != null) {
+ headers.add(R.string.details_description);
+ details.add(channel.getDescription());
+ }
+
+ Util.showDetailsDialog(context, R.string.details_title_podcast, headers, details);
}
private void deletePodcast(final PodcastChannel channel) {
@@ -258,8 +254,7 @@ public class SelectPodcastsFragment extends SelectListFragment<PodcastChannel> {
@Override
protected void done(Void result) {
- adapter.remove(channel);
- adapter.notifyDataSetChanged();
+ adapter.removeItem(channel);
Util.toast(context, context.getResources().getString(R.string.select_podcasts_deleted, channel.getName()));
}
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectRecyclerFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectRecyclerFragment.java
new file mode 100644
index 00000000..9dc3e363
--- /dev/null
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectRecyclerFragment.java
@@ -0,0 +1,197 @@
+/*
+ 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 2015 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.fragments;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v4.widget.SwipeRefreshLayout;
+import android.support.v7.widget.RecyclerView;
+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 java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.adapter.SectionAdapter;
+import github.daneren2005.dsub.service.MusicService;
+import github.daneren2005.dsub.service.MusicServiceFactory;
+import github.daneren2005.dsub.util.Constants;
+import github.daneren2005.dsub.util.ProgressListener;
+import github.daneren2005.dsub.util.TabBackgroundTask;
+
+public abstract class SelectRecyclerFragment<T> extends SubsonicFragment implements SectionAdapter.OnItemClickedListener<T> {
+ private static final String TAG = SelectRecyclerFragment.class.getSimpleName();
+ protected RecyclerView recyclerView;
+ protected SectionAdapter<T> adapter;
+ protected UpdateTask currentTask;
+ protected List<T> objects;
+ protected boolean serialize = true;
+ protected boolean largeAlbums = false;
+ protected int columns;
+ protected boolean pullToRefresh = true;
+ protected boolean backgroundUpdate = true;
+
+ @Override
+ public void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+
+ if(bundle != null && serialize) {
+ objects = (List<T>) bundle.getSerializable(Constants.FRAGMENT_LIST);
+ }
+ columns = context.getResources().getInteger(R.integer.Grid_Columns);
+ }
+
+ @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_recycler_fragment, container, false);
+
+ refreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.refresh_layout);
+ refreshLayout.setOnRefreshListener(this);
+
+ recyclerView = (RecyclerView) rootView.findViewById(R.id.fragment_recycler);
+ setupLayoutManager();
+
+ if(pullToRefresh) {
+ setupScrollList(recyclerView);
+ } else {
+ refreshLayout.setEnabled(false);
+ }
+
+ if(objects == null) {
+ refresh(false);
+ } else {
+ recyclerView.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());
+ }
+ if(backgroundUpdate) {
+ recyclerView.setVisibility(View.GONE);
+ }
+
+ // Cancel current running task before starting another one
+ if(currentTask != null) {
+ currentTask.cancel();
+ }
+
+ currentTask = new UpdateTask(this, refresh);
+
+ if(backgroundUpdate) {
+ currentTask.execute();
+ } else {
+ objects = new ArrayList<T>();
+
+ try {
+ objects = getObjects(null, refresh, null);
+ } catch (Exception x) {
+ Log.e(TAG, "Failed to load", x);
+ }
+
+ currentTask.done(objects);
+ }
+ }
+
+ protected SectionAdapter getCurrentAdapter() {
+ return adapter;
+ }
+
+ private void setupLayoutManager() {
+ setupLayoutManager(recyclerView, largeAlbums);
+ }
+
+ public abstract int getOptionsMenu();
+ public abstract SectionAdapter<T> getAdapter(List<T> objs);
+ public abstract List<T> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception;
+ public abstract int getTitleResource();
+
+ public void onFinishRefresh() {
+
+ }
+
+ private class UpdateTask extends TabBackgroundTask<List<T>> {
+ private boolean refresh;
+
+ public UpdateTask(SubsonicFragment fragment, boolean refresh) {
+ super(fragment);
+ this.refresh = refresh;
+ }
+
+ @Override
+ public List<T> doInBackground() throws Exception {
+ 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
+ public void done(List<T> result) {
+ if (result != null && !result.isEmpty()) {
+ recyclerView.setAdapter(adapter = getAdapter(result));
+
+ onFinishRefresh();
+ recyclerView.setVisibility(View.VISIBLE);
+ } else {
+ setEmpty(true);
+ }
+
+ currentTask = null;
+ }
+ }
+}
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectShareFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectShareFragment.java
index 07cd3bef..65723618 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectShareFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectShareFragment.java
@@ -1,13 +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 2015 (C) Scott Jackson
+*/
+
package github.daneren2005.dsub.fragments;
-import android.app.AlertDialog;
+import android.support.v7.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.ContextMenu;
+import android.view.Menu;
+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.CompoundButton;
import android.widget.DatePicker;
@@ -17,6 +32,7 @@ import java.util.Date;
import java.util.List;
import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.adapter.SectionAdapter;
import github.daneren2005.dsub.domain.Share;
import github.daneren2005.dsub.service.MusicService;
import github.daneren2005.dsub.service.MusicServiceFactory;
@@ -27,30 +43,19 @@ import github.daneren2005.dsub.util.LoadingTask;
import github.daneren2005.dsub.util.ProgressListener;
import github.daneren2005.dsub.util.Util;
import github.daneren2005.dsub.adapter.ShareAdapter;
+import github.daneren2005.dsub.view.UpdateView;
-/**
- * Created by Scott on 12/28/13.
- */
-public class SelectShareFragment extends SelectListFragment<Share> {
+public class SelectShareFragment extends SelectRecyclerFragment<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);
+ public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<Share> updateView, Share item) {
+ menuInflater.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);
-
+ public boolean onContextItemSelected(MenuItem menuItem, UpdateView<Share> updateView, Share share) {
switch (menuItem.getItemId()) {
case R.id.share_menu_share:
shareExternal(share);
@@ -75,8 +80,8 @@ public class SelectShareFragment extends SelectListFragment<Share> {
}
@Override
- public ArrayAdapter getAdapter(List<Share> objs) {
- return new ShareAdapter(context, objs);
+ public SectionAdapter getAdapter(List<Share> objs) {
+ return new ShareAdapter(context, objs, this);
}
@Override
@@ -90,9 +95,7 @@ public class SelectShareFragment extends SelectListFragment<Share> {
}
@Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- Share share = (Share) parent.getItemAtPosition(position);
-
+ public void onItemClicked(Share share) {
SubsonicFragment fragment = new SelectDirectoryFragment();
Bundle args = new Bundle();
args.putSerializable(Constants.INTENT_EXTRA_NAME_SHARE, share);
@@ -193,8 +196,7 @@ public class SelectShareFragment extends SelectListFragment<Share> {
@Override
protected void done(Void result) {
- adapter.remove(share);
- adapter.notifyDataSetChanged();
+ adapter.removeItem(share);
Util.toast(context, context.getResources().getString(R.string.share_deleted, share.getName()));
}
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectVideoFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectVideoFragment.java
index b4d34ff9..0a79b3d5 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectVideoFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectVideoFragment.java
@@ -16,28 +16,33 @@
package github.daneren2005.dsub.fragments;
import android.view.ContextMenu;
+import android.view.Menu;
+import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
import java.util.List;
import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.adapter.EntryGridAdapter;
+import github.daneren2005.dsub.adapter.SectionAdapter;
import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.service.MusicService;
import github.daneren2005.dsub.util.ProgressListener;
-import github.daneren2005.dsub.adapter.EntryAdapter;
+import github.daneren2005.dsub.view.UpdateView;
-public class SelectVideoFragment extends SelectListFragment<MusicDirectory.Entry> {
+public class SelectVideoFragment extends SelectRecyclerFragment<MusicDirectory.Entry> {
@Override
public int getOptionsMenu() {
return R.menu.empty;
}
@Override
- public ArrayAdapter getAdapter(List<MusicDirectory.Entry> objs) {
- return new EntryAdapter(context, null, objs, false);
+ public SectionAdapter getAdapter(List<MusicDirectory.Entry> objs) {
+ SectionAdapter adapter = new EntryGridAdapter(context, objs, null, false);
+ adapter.setOnItemClickedListener(this);
+ return adapter;
}
@Override
@@ -52,31 +57,18 @@ public class SelectVideoFragment extends SelectListFragment<MusicDirectory.Entry
}
@Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- MusicDirectory.Entry entry = (MusicDirectory.Entry) parent.getItemAtPosition(position);
+ public void onItemClicked(MusicDirectory.Entry entry) {
playVideo(entry);
}
@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);
-
- onCreateContextMenu(menu, view, menuInfo, entry);
+ public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<MusicDirectory.Entry> updateView, MusicDirectory.Entry item) {
+ onCreateContextMenuSupport(menu, menuInflater, updateView, item);
recreateContextMenu(menu);
}
@Override
- public boolean onContextItemSelected(MenuItem menuItem) {
- if (menuItem.getGroupId() != getSupportTag()) {
- return false;
- }
-
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
- Object entry = listView.getItemAtPosition(info.position);
-
+ public boolean onContextItemSelected(MenuItem menuItem, UpdateView<MusicDirectory.Entry> updateView, MusicDirectory.Entry entry) {
return onContextItemSelected(menuItem, entry);
}
}
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectYearFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectYearFragment.java
index dc19acad..22676d8b 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectYearFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectYearFragment.java
@@ -1,24 +1,24 @@
/*
- This file is part of Subsonic.
-
+ 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
+ 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
+ along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
+ Copyright 2015 (C) Scott Jackson
*/
+
package github.daneren2005.dsub.fragments;
import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
@@ -27,14 +27,21 @@ import java.util.ArrayList;
import java.util.List;
import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.adapter.BasicListAdapter;
+import github.daneren2005.dsub.adapter.SectionAdapter;
import github.daneren2005.dsub.service.MusicService;
import github.daneren2005.dsub.util.Constants;
import github.daneren2005.dsub.util.ProgressListener;
+import github.daneren2005.dsub.view.UpdateView;
+
+public class SelectYearFragment extends SelectRecyclerFragment<String> {
-/**
- * Created by Scott on 12/23/13.
- */
-public class SelectYearFragment extends SelectListFragment<Integer> {
+ public SelectYearFragment() {
+ super();
+ pullToRefresh = false;
+ serialize = false;
+ backgroundUpdate = false;
+ }
@Override
public int getOptionsMenu() {
@@ -42,15 +49,15 @@ public class SelectYearFragment extends SelectListFragment<Integer> {
}
@Override
- public ArrayAdapter getAdapter(List<Integer> objs) {
- return new ArrayAdapter<Integer>(context, android.R.layout.simple_list_item_1, objs);
+ public SectionAdapter getAdapter(List<String> objs) {
+ return new BasicListAdapter(context, objs, this);
}
@Override
- public List<Integer> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception {
- List<Integer> decades = new ArrayList<Integer>();
+ public List<String> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception {
+ List<String> decades = new ArrayList<>();
for(int i = 2010; i >= 1920; i -= 10) {
- decades.add(i);
+ decades.add(String.valueOf(i));
}
return decades;
@@ -62,17 +69,23 @@ public class SelectYearFragment extends SelectListFragment<Integer> {
}
@Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- Integer decade = (Integer) parent.getItemAtPosition(position);
-
+ public void onItemClicked(String decade) {
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));
+ args.putString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_EXTRA, decade);
fragment.setArguments(args);
replaceFragment(fragment);
}
+
+ @Override
+ public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<String> updateView, String item) {}
+
+ @Override
+ public boolean onContextItemSelected(MenuItem menuItem, UpdateView<String> updateView, String item) {
+ return false;
+ }
}
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SimilarArtistFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SimilarArtistFragment.java
index 79e759cc..5677e445 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SimilarArtistFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SimilarArtistFragment.java
@@ -16,13 +16,13 @@
package github.daneren2005.dsub.fragments;
import android.os.Bundle;
-import android.view.ContextMenu;
+import android.view.Menu;
+import android.view.MenuInflater;
import android.view.MenuItem;
-import android.view.View;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.adapter.ArtistAdapter;
+import github.daneren2005.dsub.adapter.SectionAdapter;
import github.daneren2005.dsub.domain.Artist;
import github.daneren2005.dsub.domain.ArtistInfo;
import github.daneren2005.dsub.domain.MusicDirectory;
@@ -32,13 +32,13 @@ import github.daneren2005.dsub.service.MusicServiceFactory;
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 github.daneren2005.dsub.view.UpdateView;
import java.net.URLEncoder;
import java.util.LinkedList;
import java.util.List;
-public class SimilarArtistFragment extends SelectListFragment<Artist> {
+public class SimilarArtistFragment extends SelectRecyclerFragment<Artist> {
private static final String TAG = SimilarArtistFragment.class.getSimpleName();
private ArtistInfo info;
private String artistId;
@@ -52,6 +52,18 @@ public class SimilarArtistFragment extends SelectListFragment<Artist> {
}
@Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
+ super.onCreateOptionsMenu(menu, menuInflater);
+ if(!primaryFragment) {
+ return;
+ }
+
+ if(info.getMissingArtists().isEmpty()) {
+ menu.removeItem(R.id.menu_show_missing);
+ }
+ }
+
+ @Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_play_now:
@@ -69,30 +81,18 @@ public class SimilarArtistFragment extends SelectListFragment<Artist> {
}
@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);
- onCreateContextMenu(menu, view, menuInfo, entry);
-
+ public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<Artist> updateView, Artist item) {
+ onCreateContextMenuSupport(menu, menuInflater, updateView, item);
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);
+ public boolean onContextItemSelected(MenuItem menuItem, UpdateView<Artist> updateView, Artist artist) {
return onContextItemSelected(menuItem, artist);
}
@Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- Artist artist = (Artist) parent.getItemAtPosition(position);
+ public void onItemClicked(Artist artist) {
SubsonicFragment fragment = new SelectDirectoryFragment();
Bundle args = new Bundle();
args.putString(Constants.INTENT_EXTRA_NAME_ID, artist.getId());
@@ -109,8 +109,8 @@ public class SimilarArtistFragment extends SelectListFragment<Artist> {
}
@Override
- public ArrayAdapter getAdapter(List<Artist> objects) {
- return new ArtistAdapter(context, objects);
+ public SectionAdapter getAdapter(List<Artist> objects) {
+ return new ArtistAdapter(context, objects, this);
}
@Override
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java
index 109983ba..ddc39235 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java
@@ -18,8 +18,9 @@
*/
package github.daneren2005.dsub.fragments;
+import android.annotation.TargetApi;
import android.app.Activity;
-import android.app.AlertDialog;
+import android.support.v7.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -28,12 +29,15 @@ import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.media.MediaMetadataRetriever;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.os.StatFs;
import android.support.v4.app.Fragment;
import android.support.v4.widget.SwipeRefreshLayout;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
import android.util.Log;
-import android.view.ContextMenu;
import android.view.GestureDetector;
import android.view.Menu;
import android.view.MenuInflater;
@@ -41,7 +45,6 @@ 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;
@@ -49,9 +52,9 @@ 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.adapter.SectionAdapter;
import github.daneren2005.dsub.domain.Artist;
import github.daneren2005.dsub.domain.Bookmark;
import github.daneren2005.dsub.domain.Genre;
@@ -70,15 +73,16 @@ 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.MenuUtil;
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.GridSpacingDecoration;
import github.daneren2005.dsub.view.PlaylistSongView;
import github.daneren2005.dsub.view.SongView;
import github.daneren2005.dsub.view.UpdateView;
@@ -101,7 +105,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
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;
@@ -116,7 +120,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
protected boolean artistOverride = false;
protected SwipeRefreshLayout refreshLayout;
protected boolean firstRun;
-
+
public SubsonicFragment() {
super();
tag = TAG_INC++;
@@ -171,10 +175,10 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
- case R.id.menu_shuffle:
+ case R.id.menu_global_shuffle:
onShuffleRequested();
return true;
- case R.id.menu_search:
+ case R.id.menu_global_search:
context.onSearchRequested();
return true;
case R.id.menu_exit:
@@ -183,23 +187,50 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
case R.id.menu_refresh:
refresh();
return true;
+ 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);
+ clearSelected();
+ return true;
+ case R.id.menu_cache:
+ downloadBackground(true);
+ clearSelected();
+ return true;
+ case R.id.menu_delete:
+ delete();
+ clearSelected();
+ return true;
+ case R.id.menu_add_playlist:
+ List<Entry> songs = getSelectedEntries();
+ addToPlaylist(songs);
+ clearSelected();
+ return true;
}
return false;
}
-
- public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo, Object selected) {
- MenuInflater inflater = context.getMenuInflater();
+ public void onCreateContextMenuSupport(Menu menu, MenuInflater menuInflater, UpdateView updateView, Object selected) {
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);
+ menuInflater.inflate(R.menu.select_podcast_episode_context_offline, menu);
}
else {
- inflater.inflate(R.menu.select_podcast_episode_context, menu);
-
+ menuInflater.inflate(R.menu.select_podcast_episode_context, menu);
+
if(entry.getBookmark() == null) {
menu.removeItem(R.id.bookmark_menu_delete);
}
@@ -207,10 +238,10 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
}
else if (entry.isDirectory()) {
if(Util.isOffline(context)) {
- inflater.inflate(R.menu.select_album_context_offline, menu);
+ menuInflater.inflate(R.menu.select_album_context_offline, menu);
}
else {
- inflater.inflate(R.menu.select_album_context, menu);
+ menuInflater.inflate(R.menu.select_album_context, menu);
if(Util.isTagBrowsing(context)) {
menu.removeItem(R.id.menu_rate);
@@ -219,11 +250,11 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
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);
+ menuInflater.inflate(R.menu.select_song_context_offline, menu);
}
else {
- inflater.inflate(R.menu.select_song_context, menu);
-
+ menuInflater.inflate(R.menu.select_song_context, menu);
+
if(entry.getBookmark() == null) {
menu.removeItem(R.id.bookmark_menu_delete);
}
@@ -231,112 +262,27 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
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);
+ menuInflater.inflate(R.menu.select_video_context_offline, menu);
}
else {
- inflater.inflate(R.menu.select_video_context, menu);
+ menuInflater.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);
+ // menuInflater.inflate(R.menu.select_artist_context_offline, menu);
+ } else {
+ menuInflater.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);
- }
- }
- }
+ MenuUtil.hideMenuItems(context, menu);
}
- protected void recreateContextMenu(ContextMenu menu) {
+ protected void recreateContextMenu(Menu menu) {
List<MenuItem> menuItems = new ArrayList<MenuItem>();
for(int i = 0; i < menu.size(); i++) {
MenuItem item = menu.getItem(i);
@@ -358,60 +304,12 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
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;
@@ -421,21 +319,9 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
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;
@@ -475,7 +361,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
return true;
}
-
+
public void replaceFragment(SubsonicFragment fragment) {
replaceFragment(fragment, true);
}
@@ -492,7 +378,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
public int getSupportTag() {
return tag;
}
-
+
public void setPrimaryFragment(boolean primary) {
primaryFragment = primary;
if(primary) {
@@ -631,6 +517,68 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
R.color.holo_red_light);
}
}
+ protected void setupScrollList(final RecyclerView recyclerView) {
+ if(!context.isTouchscreen()) {
+ refreshLayout.setEnabled(false);
+ } else {
+ recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
+ @Override
+ public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
+ super.onScrollStateChanged(recyclerView, newState);
+ }
+
+ @Override
+ public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
+ refreshLayout.setEnabled(!recyclerView.canScrollVertically(-1));
+ }
+ });
+
+ refreshLayout.setColorScheme(
+ R.color.holo_blue_light,
+ R.color.holo_orange_light,
+ R.color.holo_green_light,
+ R.color.holo_red_light);
+ }
+ }
+
+ public void setupLayoutManager(RecyclerView recyclerView, boolean largeAlbums) {
+ recyclerView.setLayoutManager(getLayoutManager(recyclerView, largeAlbums));
+ }
+ public RecyclerView.LayoutManager getLayoutManager(RecyclerView recyclerView, boolean largeCells) {
+ if(largeCells) {
+ return getGridLayoutManager(recyclerView);
+ } else {
+ return getLinearLayoutManager();
+ }
+ }
+ public GridLayoutManager getGridLayoutManager(RecyclerView recyclerView) {
+ final int columns = getRecyclerColumnCount();
+ GridLayoutManager gridLayoutManager = new GridLayoutManager(context, columns);
+
+ GridLayoutManager.SpanSizeLookup spanSizeLookup = getSpanSizeLookup();
+ if(spanSizeLookup != null) {
+ gridLayoutManager.setSpanSizeLookup(spanSizeLookup);
+ }
+ RecyclerView.ItemDecoration itemDecoration = getItemDecoration();
+ if(itemDecoration != null) {
+ recyclerView.addItemDecoration(itemDecoration);
+ }
+ return gridLayoutManager;
+ }
+ public LinearLayoutManager getLinearLayoutManager() {
+ LinearLayoutManager layoutManager = new LinearLayoutManager(context);
+ layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
+ return layoutManager;
+ }
+ public GridLayoutManager.SpanSizeLookup getSpanSizeLookup() {
+ return null;
+ }
+ public RecyclerView.ItemDecoration getItemDecoration() {
+ return new GridSpacingDecoration();
+ }
+ public int getRecyclerColumnCount() {
+ return context.getResources().getInteger(R.integer.Grid_Columns);
+ }
protected void warnIfStorageUnavailable() {
if (!Util.isExternalStoragePresent()) {
@@ -650,9 +598,12 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
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);
+ DownloadService downloadService = getDownloadService();
+ if(downloadService == null) {
+ return;
+ }
+ downloadService.setShufflePlayEnabled(true);
+ context.openNowPlaying();
return;
}
@@ -691,21 +642,21 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
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));
- }
- }
- });
+ .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) {
+ protected void error(Throwable error) {
String msg;
if (error instanceof OfflineException || error instanceof ServerTooOldException) {
msg = getErrorMessage(error);
@@ -731,31 +682,34 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
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();
+ .setView(dialogView)
+ .setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ 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();
+
+ DownloadService downloadService = getDownloadService();
+ if (downloadService == null) {
+ return;
+ }
+ downloadService.setShufflePlayEnabled(true);
+ context.openNowPlaying();
}
- 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);
+ })
+ .setNegativeButton(R.string.common_cancel, null);
AlertDialog dialog = builder.create();
dialog.show();
}
@@ -763,6 +717,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
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);
@@ -868,9 +823,11 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
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
@@ -906,7 +863,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
playNowOverride = true;
return false;
}
-
+
if (!append && !background) {
downloadService.clear();
}
@@ -994,7 +951,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
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()) {
@@ -1003,7 +960,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
it.remove();
}
}
-
+
return playlists;
}
@@ -1016,7 +973,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
@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) {
@@ -1033,21 +990,21 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
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);
+ .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) {
+ protected void error(Throwable error) {
String msg;
if (error instanceof OfflineException || error instanceof ServerTooOldException) {
msg = getErrorMessage(error);
@@ -1075,7 +1032,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
}
@Override
- protected void error(Throwable error) {
+ protected void error(Throwable error) {
String msg;
if (error instanceof OfflineException || error instanceof ServerTooOldException) {
msg = getErrorMessage(error);
@@ -1087,7 +1044,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
}
}.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);
@@ -1115,34 +1072,34 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
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);
+ .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);
-
+ })
+ .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();
}
@@ -1184,7 +1141,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
}
@Override
- protected void error(Throwable error) {
+ protected void error(Throwable error) {
String msg;
if (error instanceof OfflineException || error instanceof ServerTooOldException) {
msg = getErrorMessage(error);
@@ -1209,12 +1166,12 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
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);
@@ -1224,7 +1181,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
// 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);
@@ -1236,57 +1193,85 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
}
}
- String msg = "";
+ List<Integer> headers = new ArrayList<>();
+ List<String> details = new ArrayList<>();
if(song instanceof PodcastEpisode) {
- msg += "Podcast: " + song.getArtist() + "\nStatus: " + ((PodcastEpisode)song).getStatus();
+ headers.add(R.string.details_podcast);
+ details.add(song.getArtist());
+
+ headers.add(R.string.details_status);
+ details.add(((PodcastEpisode)song).getStatus());
} else if(!song.isVideo()) {
if(song.getArtist() != null && !"".equals(song.getArtist())) {
- msg += "Artist: " + song.getArtist();
+ headers.add(R.string.details_artist);
+ details.add(song.getArtist());
}
if(song.getAlbum() != null && !"".equals(song.getAlbum())) {
- msg += "\nAlbum: " + song.getAlbum();
+ headers.add(R.string.details_album);
+ details.add(song.getAlbum());
}
}
if(song.getTrack() != null && song.getTrack() != 0) {
- msg += "\nTrack: " + song.getTrack();
+ headers.add(R.string.details_track);
+ details.add(Integer.toString(song.getTrack()));
}
if(song.getGenre() != null && !"".equals(song.getGenre())) {
- msg += "\nGenre: " + song.getGenre();
+ headers.add(R.string.details_genre);
+ details.add(song.getGenre());
}
if(song.getYear() != null && song.getYear() != 0) {
- msg += "\nYear: " + song.getYear();
+ headers.add(R.string.details_year);
+ details.add(Integer.toString(song.getYear()));
}
if(!Util.isOffline(context) && song.getSuffix() != null) {
- msg += "\nServer Format: " + song.getSuffix();
+ headers.add(R.string.details_server_format);
+ details.add(song.getSuffix());
+
if(song.getBitRate() != null && song.getBitRate() != 0) {
- msg += "\nServer Bitrate: " + song.getBitRate() + " kbps";
+ headers.add(R.string.details_server_bitrate);
+ details.add(song.getBitRate() + " kbps");
}
}
if(format != null && !"".equals(format)) {
- msg += "\nCached Format: " + format;
+ headers.add(R.string.details_cached_format);
+ details.add(format);
}
if(bitrate != null && bitrate != 0) {
- msg += "\nCached Bitrate: " + bitrate + " kbps";
+ headers.add(R.string.details_cached_bitrate);
+ details.add(bitrate + " kbps");
}
if(size != 0) {
- msg += "\nSize: " + Util.formatLocalizedBytes(size, context);
+ headers.add(R.string.details_size);
+ details.add(Util.formatLocalizedBytes(size, context));
}
if(song.getDuration() != null && song.getDuration() != 0) {
- msg += "\nLength: " + Util.formatDuration(song.getDuration());
+ headers.add(R.string.details_length);
+ details.add(Util.formatDuration(song.getDuration()));
}
if(song.getBookmark() != null) {
- msg += "\nBookmark Position: " + Util.formatDuration(song.getBookmark().getPosition() / 1000);
+ headers.add(R.string.details_bookmark_position);
+ details.add(Util.formatDuration(song.getBookmark().getPosition() / 1000));
}
if(song.getRating() != 0) {
- msg += "\nRating: " + song.getRating() + " stars";
+ headers.add(R.string.details_rating);
+ details.add(song.getRating() + " stars");
}
if(song instanceof PodcastEpisode) {
- msg += "\n\nDescription: " + song.getAlbum();
+ headers.add(R.string.details_description);
+ details.add(song.getAlbum());
}
- Util.info(context, song.getTitle(), msg);
+ int title;
+ if(song.isDirectory()) {
+ title = R.string.details_title_album;
+ } else if(song instanceof PodcastEpisode) {
+ title = R.string.details_title_podcast;
+ } else {
+ title = R.string.details_title_song;
+ }
+ Util.showDetailsDialog(context, title, headers, details);
}
-
+
protected void playVideo(Entry entry) {
if(entryExists(entry)) {
playExternalPlayer(entry);
@@ -1315,7 +1300,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
intent.putExtra(Intent.EXTRA_TITLE, entry.getTitle());
List<ResolveInfo> intents = context.getPackageManager()
- .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
+ .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
if(intents != null && intents.size() > 0) {
startActivity(intent);
}else {
@@ -1373,11 +1358,12 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
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;
@@ -1434,6 +1420,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
replaceFragment(fragment, true);
}
+
public void showAlbum(Entry entry) {
SubsonicFragment fragment = new SelectDirectoryFragment();
Bundle args = new Bundle();
@@ -1490,7 +1477,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
intent.putExtra(Intent.EXTRA_TEXT, share.getUrl());
context.startActivity(Intent.createChooser(intent, context.getResources().getString(R.string.share_via)));
}
-
+
public GestureDetector getGestureDetector() {
return gestureScanner;
}
@@ -1498,6 +1485,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
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();
@@ -1515,7 +1503,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
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 {
@@ -1528,7 +1516,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
@Override
protected void error(Throwable error) {
song.setBookmark(oldBookmark);
-
+
String msg;
if (error instanceof OfflineException || error instanceof ServerTooOldException) {
msg = getErrorMessage(error);
@@ -1546,26 +1534,37 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
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;
+ protected void playNow(final List<Entry> entries, final String playlistName, final String playlistId) {
+ new RecursiveLoader(context) {
+ @Override
+ protected Boolean doInBackground() throws Throwable {
+ getSongsRecursively(entries, songs);
+ return null;
}
- }
-
- // 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);
- }
+
+ @Override
+ protected void done(Boolean result) {
+ Entry bookmark = null;
+ for(Entry entry: songs) {
+ if(entry.getBookmark() != null) {
+ bookmark = entry;
+ break;
+ }
+ }
+
+ // If no bookmark found, just play from start
+ if(bookmark == null) {
+ playNow(songs, 0, playlistName, playlistId);
+ } else {
+ // If bookmark found, then give user choice to start from there or to start over
+ playBookmark(songs, bookmark, playlistName, playlistId);
+ }
+ }
+ }.execute();
}
protected void playNow(List<Entry> entries, int position) {
playNow(entries, position, null, null);
@@ -1574,9 +1573,11 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
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
@@ -1585,22 +1586,22 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
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);
+ context.openNowPlaying();
}
}.execute();
}
- protected void deleteBookmark(final MusicDirectory.Entry entry, final ArrayAdapter adapter) {
+ protected void deleteBookmark(final MusicDirectory.Entry entry, final SectionAdapter adapter) {
Util.confirmDialog(context, R.string.bookmark_delete_title, entry.getTitle(), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
@@ -1626,8 +1627,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
@Override
protected void done(Void result) {
if (adapter != null) {
- adapter.remove(entry);
- adapter.notifyDataSetChanged();
+ adapter.removeItem(entry);
}
Util.toast(context, context.getResources().getString(R.string.bookmark_deleted, entry.getTitle()));
}
@@ -1657,19 +1657,19 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
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);
-
+ .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();
}
@@ -1684,7 +1684,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
if(onRatingChange != null) {
onRatingChange.ratingChange(rating);
}
-
+
new SilentBackgroundTask<Void>(context) {
@Override
protected Void doInBackground() throws Throwable {
@@ -1711,7 +1711,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
if(onRatingChange != null) {
onRatingChange.ratingChange(oldRating);
}
-
+
String msg;
if (error instanceof OfflineException || error instanceof ServerTooOldException) {
msg = getErrorMessage(error);
@@ -1723,16 +1723,134 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
}
}.execute();
}
-
+
+ protected SectionAdapter getCurrentAdapter() { return null; }
+ public void stopActionMode() {
+ SectionAdapter adapter = getCurrentAdapter();
+ if(adapter != null) {
+ adapter.stopActionMode();
+ }
+ }
+ protected void clearSelected() {
+ if(getCurrentAdapter() != null) {
+ getCurrentAdapter().clearSelected();
+ }
+ }
+ protected List<Entry> getSelectedEntries() {
+ return getCurrentAdapter().getSelected();
+ }
+
+ protected void playNow(final boolean shuffle, final boolean append) {
+ playNow(shuffle, append, false);
+ }
+ protected void playNow(final boolean shuffle, final boolean append, final boolean playNext) {
+ List<Entry> songs = getSelectedEntries();
+ if(!songs.isEmpty()) {
+ download(songs, append, false, !append, playNext, shuffle);
+ clearSelected();
+ }
+ }
+
+ protected void download(List<Entry> entries, boolean append, boolean save, boolean autoplay, boolean playNext, boolean shuffle) {
+ download(entries, append, save, autoplay, playNext, shuffle, null, null);
+ }
+ protected void download(final List<Entry> entries, final boolean append, final boolean save, final boolean autoplay, final boolean playNext, final boolean shuffle, final String playlistName, final String playlistId) {
+ final DownloadService downloadService = getDownloadService();
+ if (downloadService == null) {
+ return;
+ }
+ 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(entries, playlistName, playlistId);
+ return;
+ }
+
+ RecursiveLoader onValid = new RecursiveLoader(context) {
+ @Override
+ protected Boolean doInBackground() throws Throwable {
+ if (!append) {
+ getDownloadService().clear();
+ }
+ getSongsRecursively(entries, songs);
+
+ downloadService.download(songs, save, autoplay, playNext, shuffle);
+ if (playlistName != null) {
+ downloadService.setSuggestedPlaylistName(playlistName, playlistId);
+ } else {
+ downloadService.setSuggestedPlaylistName(null, null);
+ }
+ return null;
+ }
+
+ @Override
+ protected void done(Boolean result) {
+ if (autoplay) {
+ context.openNowPlaying();
+ } 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()));
+ }
+ }
+ };
+
+ executeOnValid(onValid);
+ }
+ protected void executeOnValid(RecursiveLoader onValid) {
+ onValid.execute();
+ }
+ protected void downloadBackground(final boolean save) {
+ List<Entry> songs = getSelectedEntries();
+ if(!songs.isEmpty()) {
+ downloadBackground(save, songs);
+ }
+ }
+
+ protected void downloadBackground(final boolean save, final List<Entry> entries) {
+ if (getDownloadService() == null) {
+ return;
+ }
+
+ warnIfStorageUnavailable();
+ new RecursiveLoader(context) {
+ @Override
+ protected Boolean doInBackground() throws Throwable {
+ getSongsRecursively(entries, songs);
+ getDownloadService().downloadBackground(songs, save);
+ return null;
+ }
+
+ @Override
+ protected void done(Boolean result) {
+ Util.toast(context, context.getResources().getQuantityString(R.plurals.select_album_n_songs_downloading, songs.size(), songs.size()));
+ }
+ }.execute();
+ }
+
+ protected void delete() {
+ List<Entry> songs = getSelectedEntries();
+ if(!songs.isEmpty()) {
+ DownloadService downloadService = getDownloadService();
+ if(downloadService != null) {
+ downloadService.delete(songs);
+ }
+ }
+ }
+
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()) {
@@ -1745,12 +1863,12 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
serializeChanges = true;
}
}
-
+
if(serializeChanges) {
downloadService.serializeQueue();
}
}
-
+
Entry find = UpdateView.findEntry(entry);
if(find != null) {
update(find);
@@ -1769,22 +1887,26 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
protected MusicService musicService;
protected static final int MAX_SONGS = 500;
protected boolean playNowOverride = false;
- protected List<Entry> songs;
+ protected List<Entry> songs = new ArrayList<>();
public RecursiveLoader(Activity context) {
super(context);
+ musicService = MusicServiceFactory.getMusicService(context);
}
+ protected void getSongsRecursively(List<Entry> entry) throws Exception {
+ getSongsRecursively(entry, songs);
+ }
+ protected void getSongsRecursively(List<Entry> entry, List<Entry> songs) throws Exception {
+ MusicDirectory dir = new MusicDirectory();
+ dir.addChildren(entry);
+ getSongsRecursively(dir, songs);
+ }
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;
@@ -1798,6 +1920,12 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
}
getSongsRecursively(musicDirectory, songs);
}
+
+ for (Entry song : parent.getChildren(false, true)) {
+ if (!song.isVideo() && song.getRating() != 1) {
+ songs.add(song);
+ }
+ }
}
@Override
@@ -1810,7 +1938,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
}
if(result) {
- Util.startActivityWithoutTransition(context, DownloadActivity.class);
+ context.openNowPlaying();
}
}
}
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/UserFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/UserFragment.java
index 00c7c603..bdd0472b 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/UserFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/UserFragment.java
@@ -15,71 +15,33 @@
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 java.util.List;
import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.activity.SubsonicActivity;
+import github.daneren2005.dsub.adapter.SectionAdapter;
import github.daneren2005.dsub.domain.ServerInfo;
import github.daneren2005.dsub.domain.User;
+import github.daneren2005.dsub.service.MusicService;
import github.daneren2005.dsub.util.Constants;
-import github.daneren2005.dsub.util.ImageLoader;
+import github.daneren2005.dsub.util.ProgressListener;
import github.daneren2005.dsub.util.UserUtil;
import github.daneren2005.dsub.adapter.SettingsAdapter;
+import github.daneren2005.dsub.view.UpdateView;
-public class UserFragment extends SubsonicFragment{
- private ListView listView;
+public class UserFragment extends SelectRecyclerFragment<User.Setting>{
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);
-
+ public void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
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);
- }
+ pullToRefresh = false;
}
@Override
@@ -103,23 +65,43 @@ public class UserFragment extends SubsonicFragment{
return false;
}
- private void createHeader() {
- View header = LayoutInflater.from(context).inflate(R.layout.user_header, listView, false);
+ @Override
+ public int getOptionsMenu() {
+ if(UserUtil.isCurrentAdmin() && ServerInfo.checkServerVersion(context, "1.10")) {
+ return R.menu.user;
+ } else if(UserUtil.isCurrentRole(User.SETTINGS)) {
+ return R.menu.user_user;
+ } else {
+ return R.menu.empty;
+ }
+ }
+
+ @Override
+ public SectionAdapter<User.Setting> getAdapter(List<User.Setting> objs) {
+ return new SettingsAdapter(context, user, getImageLoader(), UserUtil.isCurrentAdmin() && ServerInfo.checkServerVersion(context, "1.10"));
+ }
- final ImageLoader imageLoader = getImageLoader();
- ImageView coverArtView = (ImageView) header.findViewById(R.id.user_avatar);
- imageLoader.loadAvatar(context, coverArtView, user.getUsername());
+ @Override
+ public List<User.Setting> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception {
+ return user.getSettings();
+ }
- TextView usernameView = (TextView) header.findViewById(R.id.user_username);
- usernameView.setText(user.getUsername());
+ @Override
+ public int getTitleResource() {
+ setTitle(user.getUsername());
+ return 0;
+ }
- final TextView emailView = (TextView) header.findViewById(R.id.user_email);
- if(user.getEmail() != null) {
- emailView.setText(user.getEmail());
- } else {
- emailView.setVisibility(View.GONE);
- }
+ @Override
+ public void onItemClicked(User.Setting item) {
+
+ }
+
+ @Override
+ public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<User.Setting> updateView, User.Setting item) {}
- listView.addHeaderView(header);
+ @Override
+ public boolean onContextItemSelected(MenuItem menuItem, UpdateView<User.Setting> updateView, User.Setting item) {
+ return false;
}
}
diff --git a/app/src/main/java/github/daneren2005/dsub/provider/DSubWidgetProvider.java b/app/src/main/java/github/daneren2005/dsub/provider/DSubWidgetProvider.java
index 444b6cff..7cb3ce8b 100644
--- a/app/src/main/java/github/daneren2005/dsub/provider/DSubWidgetProvider.java
+++ b/app/src/main/java/github/daneren2005/dsub/provider/DSubWidgetProvider.java
@@ -39,7 +39,6 @@ import android.util.Log;
import android.view.View;
import android.widget.RemoteViews;
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.MusicDirectory;
@@ -270,9 +269,7 @@ public class DSubWidgetProvider extends AppWidgetProvider {
/**
* Link up various button actions using {@link PendingIntent}.
*
- * @param playerActive True if player is active in background, which means
- * widget click will launch {@link DownloadActivity},
- * otherwise we launch {@link github.daneren2005.dsub.activity.SubsonicFragmentActivity}.
+ * @param playerActive @param playerActive True if player is active in background. Launch {@link github.daneren2005.dsub.activity.SubsonicFragmentActivity}.
*/
private void linkButtons(Context context, RemoteViews views, boolean playerActive) {
Intent intent = new Intent(context, SubsonicFragmentActivity.class);
diff --git a/app/src/main/java/github/daneren2005/dsub/service/CachedMusicService.java b/app/src/main/java/github/daneren2005/dsub/service/CachedMusicService.java
index 3bc7f2a3..f053c215 100644
--- a/app/src/main/java/github/daneren2005/dsub/service/CachedMusicService.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/CachedMusicService.java
@@ -51,7 +51,6 @@ import github.daneren2005.dsub.domain.SearchCritera;
import github.daneren2005.dsub.domain.SearchResult;
import github.daneren2005.dsub.domain.Share;
import github.daneren2005.dsub.domain.User;
-import github.daneren2005.dsub.util.Pair;
import github.daneren2005.dsub.util.SilentBackgroundTask;
import github.daneren2005.dsub.util.ProgressListener;
import github.daneren2005.dsub.util.TimeLimitedCache;
@@ -384,9 +383,9 @@ public class CachedMusicService implements MusicService {
}
@Override
- public MusicDirectory getAlbumList(String type, int size, int offset, Context context, ProgressListener progressListener) throws Exception {
+ public MusicDirectory getAlbumList(String type, int size, int offset, boolean refresh, Context context, ProgressListener progressListener) throws Exception {
try {
- MusicDirectory dir = musicService.getAlbumList(type, size, offset, context, progressListener);
+ MusicDirectory dir = musicService.getAlbumList(type, size, offset, refresh, context, progressListener);
// Do some serialization updates for changes to recently added
if ("newest".equals(type) && offset == 0) {
@@ -490,6 +489,10 @@ public class CachedMusicService implements MusicService {
return dir;
} catch(IOException e) {
Log.w(TAG, "Failed to refresh album list: ", e);
+ if(refresh) {
+ throw e;
+ }
+
MusicDirectory dir = FileUtil.deserialize(context, getCacheName(context, type, Integer.toString(offset)), MusicDirectory.class);
if(dir == null) {
@@ -507,12 +510,17 @@ public class CachedMusicService implements MusicService {
}
@Override
- public MusicDirectory getAlbumList(String type, String extra, int size, int offset, Context context, ProgressListener progressListener) throws Exception {
+ public MusicDirectory getAlbumList(String type, String extra, int size, int offset, boolean refresh, Context context, ProgressListener progressListener) throws Exception {
try {
- MusicDirectory dir = musicService.getAlbumList(type, extra, size, offset, context, progressListener);
+ MusicDirectory dir = musicService.getAlbumList(type, extra, size, offset, refresh, context, progressListener);
FileUtil.serialize(context, dir, getCacheName(context, type + extra, Integer.toString(offset)));
return dir;
} catch(IOException e) {
+ Log.w(TAG, "Failed to refresh album list: ", e);
+ if(refresh) {
+ throw e;
+ }
+
MusicDirectory dir = FileUtil.deserialize(context, getCacheName(context, type + extra, Integer.toString(offset)), MusicDirectory.class);
if(dir == null) {
diff --git a/app/src/main/java/github/daneren2005/dsub/service/DownloadService.java b/app/src/main/java/github/daneren2005/dsub/service/DownloadService.java
index ae2eb073..211cf0d7 100644
--- a/app/src/main/java/github/daneren2005/dsub/service/DownloadService.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/DownloadService.java
@@ -139,6 +139,7 @@ public class DownloadService extends Service {
private boolean removePlayed;
private boolean shufflePlay;
private boolean artistRadio;
+ private final List<OnSongChangedListener> onSongChangedListeners = new ArrayList<>();
private long revision;
private static DownloadService instance;
private String suggestedPlaylistName;
@@ -156,6 +157,7 @@ public class DownloadService extends Service {
private Timer sleepTimer;
private int timerDuration;
+ private long timerStart;
private boolean autoPlayStart = false;
private MediaRouteManager mediaRouter;
@@ -362,7 +364,6 @@ public class DownloadService extends Service {
}
}
setNextPlaying();
- revision++;
} else {
int size = size();
int index = getCurrentPlayingIndex();
@@ -378,8 +379,9 @@ public class DownloadService extends Service {
if(!autoplay && (size - 1) == index) {
setNextPlaying();
}
- revision++;
}
+ revision++;
+ onSongsChanged();
updateRemotePlaylist();
if(shuffle) {
@@ -543,6 +545,7 @@ public class DownloadService extends Service {
currentPlayingIndex = 0;
}
revision++;
+ onSongsChanged();
lifecycleSupport.serializeDownloadQueue();
updateRemotePlaylist();
setNextPlaying();
@@ -601,10 +604,6 @@ public class DownloadService extends Service {
return downloadFile;
}
- public synchronized void clear() {
- clear(true);
- }
-
public synchronized void clearBackground() {
if(currentDownloading != null && backgroundDownloadList.contains(currentDownloading)) {
currentDownloading.cancelDownload();
@@ -666,6 +665,9 @@ public class DownloadService extends Service {
return downloadList.size();
}
+ public synchronized void clear() {
+ clear(true);
+ }
public synchronized void clear(boolean serialize) {
// Delete podcast if fully listened to
int position = getPlayerPosition();
@@ -694,7 +696,7 @@ public class DownloadService extends Service {
reset();
downloadList.clear();
- revision++;
+ onSongsChanged();
if (currentDownloading != null && !backgroundDownloadList.contains(currentDownloading)) {
currentDownloading.cancelDownload();
currentDownloading = null;
@@ -733,12 +735,23 @@ public class DownloadService extends Service {
currentPlayingIndex = downloadList.indexOf(currentPlaying);
backgroundDownloadList.remove(downloadFile);
revision++;
+ onSongsChanged();
lifecycleSupport.serializeDownloadQueue();
updateRemotePlaylist();
if(downloadFile == nextPlaying) {
setNextPlaying();
}
}
+ public synchronized void removeBackground(DownloadFile downloadFile) {
+ if (downloadFile == currentDownloading && downloadFile != currentPlaying && downloadFile != nextPlaying) {
+ currentDownloading.cancelDownload();
+ currentDownloading = null;
+ }
+
+ backgroundDownloadList.remove(downloadFile);
+ revision++;
+ checkDownloads();
+ }
public synchronized void delete(List<MusicDirectory.Entry> songs) {
for (MusicDirectory.Entry song : songs) {
@@ -779,6 +792,7 @@ public class DownloadService extends Service {
Util.broadcastNewTrackInfo(this, null);
Notifications.hidePlayingNotification(this, this, handler);
}
+ onSongChanged();
}
synchronized void setNextPlaying() {
@@ -886,11 +900,15 @@ public class DownloadService extends Service {
public synchronized void play(int index) {
play(index, true);
}
+ public synchronized void play(DownloadFile downloadFile) {
+ play(downloadList.indexOf(downloadFile));
+ }
private synchronized void play(int index, boolean start) {
play(index, start, 0);
}
private synchronized void play(int index, boolean start, int position) {
int size = this.size();
+ cachedPosition = 0;
if (index < 0 || index >= size) {
reset();
if(index >= size && size != 0) {
@@ -1271,6 +1289,7 @@ public class DownloadService extends Service {
positionCache.stop();
positionCache = null;
}
+ onStateUpdate();
}
private class PositionCache implements Runnable {
@@ -1305,6 +1324,7 @@ public class DownloadService extends Service {
subtractNextPosition = 0;
}
}
+ onSongProgress();
Thread.sleep(1000L);
}
catch(Exception e) {
@@ -1790,6 +1810,11 @@ public class DownloadService extends Service {
}
}, timerDuration * 60 * 1000);
+ timerStart = System.currentTimeMillis();
+ }
+
+ public int getSleepTimeRemaining() {
+ return (int) (timerStart + (timerDuration * 60 * 1000) - System.currentTimeMillis()) / 1000;
}
public void stopSleepTimer() {
@@ -1818,6 +1843,10 @@ public class DownloadService extends Service {
applyReplayGain(mediaPlayer, currentPlaying);
}
+ public synchronized void swap(boolean mainList, DownloadFile from, DownloadFile to) {
+ List<DownloadFile> list = mainList ? downloadList : backgroundDownloadList;
+ swap(mainList, list.indexOf(from), list.indexOf(to));
+ }
public synchronized void swap(boolean mainList, int from, int to) {
List<DownloadFile> list = mainList ? downloadList : backgroundDownloadList;
int max = list.size();
@@ -1972,12 +2001,18 @@ public class DownloadService extends Service {
}
private synchronized void checkRemovePlayed() {
+ boolean changed = false;
SharedPreferences prefs = Util.getPreferences(this);
int keepCount = Integer.parseInt(prefs.getString(Constants.PREFERENCES_KEY_KEEP_PLAYED_CNT, "0"));
while(currentPlayingIndex > keepCount) {
downloadList.remove(0);
currentPlayingIndex = downloadList.indexOf(currentPlaying);
+ changed = true;
+ }
+
+ if(changed) {
revision++;
+ onSongsChanged();
}
}
@@ -2015,6 +2050,7 @@ public class DownloadService extends Service {
currentPlayingIndex = downloadList.indexOf(currentPlaying);
if (revisionBefore != revision) {
+ onSongsChanged();
updateRemotePlaylist();
}
@@ -2056,6 +2092,7 @@ public class DownloadService extends Service {
currentPlayingIndex = downloadList.indexOf(currentPlaying);
if (revisionBefore != revision) {
+ onSongsChanged();
updateRemotePlaylist();
}
@@ -2323,6 +2360,87 @@ public class DownloadService extends Service {
}
}
+ public void addOnSongChangedListener(OnSongChangedListener listener) {
+ addOnSongChangedListener(listener, false);
+ }
+ public void addOnSongChangedListener(OnSongChangedListener listener, boolean run) {
+ int index = onSongChangedListeners.indexOf(listener);
+ if(index == -1) {
+ onSongChangedListeners.add(listener);
+ }
+
+ if(run) {
+ onSongsChanged();
+ onSongProgress();
+ onStateUpdate();
+ }
+ }
+ public void removeOnSongChangeListener(OnSongChangedListener listener) {
+ int index = onSongChangedListeners.indexOf(listener);
+ if(index != -1) {
+ onSongChangedListeners.remove(index);
+ }
+ }
+
+ private void onSongChanged() {
+ final long atRevision = revision;
+ for(final OnSongChangedListener listener: onSongChangedListeners) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ if(revision == atRevision) {
+ listener.onSongChanged(currentPlaying, currentPlayingIndex);
+ }
+ }
+ });
+ }
+
+ if(playerState != STARTED) {
+ onSongProgress();
+ }
+ }
+ private void onSongsChanged() {
+ final long atRevision = revision;
+ for(final OnSongChangedListener listener: onSongChangedListeners) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ if(revision == atRevision) {
+ listener.onSongsChanged(downloadList, currentPlaying, currentPlayingIndex);
+ }
+ }
+ });
+ }
+ }
+ private void onSongProgress() {
+ final long atRevision = revision;
+ final Integer duration = getPlayerDuration();
+ final boolean isSeekable = isSeekable();
+ for(final OnSongChangedListener listener: onSongChangedListeners) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ if(revision == atRevision) {
+ listener.onSongProgress(currentPlaying, cachedPosition, duration, isSeekable);
+ }
+ }
+ });
+ }
+ }
+ private void onStateUpdate() {
+ final long atRevision = revision;
+ for(final OnSongChangedListener listener: onSongChangedListeners) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ if(revision == atRevision) {
+ listener.onStateUpdate(currentPlaying, playerState);
+ }
+ }
+ });
+ }
+ }
+
private class BufferTask extends SilentBackgroundTask<Void> {
private final DownloadFile downloadFile;
private final int position;
@@ -2426,4 +2544,11 @@ public class DownloadService extends Service {
return "CheckCompletionTask (" + downloadFile + ")";
}
}
+
+ public interface OnSongChangedListener {
+ void onSongChanged(DownloadFile currentPlaying, int currentPlayingIndex);
+ void onSongsChanged(List<DownloadFile> songs, DownloadFile currentPlaying, int currentPlayingIndex);
+ void onSongProgress(DownloadFile currentPlaying, int millisPlayed, Integer duration, boolean isSeekable);
+ void onStateUpdate(DownloadFile downloadFile, PlayerState playerState);
+ }
}
diff --git a/app/src/main/java/github/daneren2005/dsub/service/MusicService.java b/app/src/main/java/github/daneren2005/dsub/service/MusicService.java
index 4d014462..0cc8d484 100644
--- a/app/src/main/java/github/daneren2005/dsub/service/MusicService.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/MusicService.java
@@ -90,9 +90,9 @@ public interface MusicService {
void scrobble(String id, boolean submission, Context context, ProgressListener progressListener) throws Exception;
- MusicDirectory getAlbumList(String type, int size, int offset, Context context, ProgressListener progressListener) throws Exception;
+ MusicDirectory getAlbumList(String type, int size, int offset, boolean refresh, Context context, ProgressListener progressListener) throws Exception;
- MusicDirectory getAlbumList(String type, String extra, int size, int offset, Context context, ProgressListener progressListener) throws Exception;
+ MusicDirectory getAlbumList(String type, String extra, int size, int offset, boolean refresh, Context context, ProgressListener progressListener) throws Exception;
MusicDirectory getRandomSongs(int size, String artistId, Context context, ProgressListener progressListener) throws Exception;
MusicDirectory getRandomSongs(int size, String folder, String genre, String startYear, String endYear, Context context, ProgressListener progressListener) throws Exception;
diff --git a/app/src/main/java/github/daneren2005/dsub/service/OfflineMusicService.java b/app/src/main/java/github/daneren2005/dsub/service/OfflineMusicService.java
index b4105d07..82716fa8 100644
--- a/app/src/main/java/github/daneren2005/dsub/service/OfflineMusicService.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/OfflineMusicService.java
@@ -380,8 +380,46 @@ public class OfflineMusicService implements MusicService {
for(File file: fileList) {
if(FileUtil.isPlaylistFile(file)) {
String id = file.getName();
- String filename = server + ": " + FileUtil.getBaseName(id);
- Playlist playlist = new Playlist(server, filename);
+ String filename = FileUtil.getBaseName(id);
+ String name = server + ": " + filename;
+ Playlist playlist = new Playlist(server, name);
+ playlist.setComment(filename);
+
+ Reader reader = null;
+ BufferedReader buffer = null;
+ try {
+ int songCount = 0;
+ reader = new FileReader(file);
+ buffer = new BufferedReader(reader);
+
+ String line = buffer.readLine();
+ while( (line = buffer.readLine()) != null ){
+ // No matter what, end file can't have .complete in it
+ line = line.replace(".complete", "");
+ File entryFile = new File(line);
+
+ // Don't add file to playlist if it doesn't exist as cached or pinned!
+ File checkFile = entryFile;
+ if(!checkFile.exists()) {
+ // If normal file doens't exist, check if .complete version does
+ checkFile = new File(entryFile.getParent(), FileUtil.getBaseName(entryFile.getName())
+ + ".complete." + FileUtil.getExtension(entryFile.getName()));
+ }
+
+ String entryName = getName(entryFile);
+ if(checkFile.exists() && entryName != null){
+ songCount++;
+ }
+ }
+
+ playlist.setSongCount(Integer.toString(songCount));
+ } catch(Exception e) {
+ Log.w(TAG, "Failed to count songs in playlist", e);
+ } finally {
+ Util.close(buffer);
+ Util.close(reader);
+ }
+
playlists.add(playlist);
}
}
@@ -523,12 +561,12 @@ public class OfflineMusicService implements MusicService {
}
@Override
- public MusicDirectory getAlbumList(String type, int size, int offset, Context context, ProgressListener progressListener) throws Exception {
+ public MusicDirectory getAlbumList(String type, int size, int offset, boolean refresh, Context context, ProgressListener progressListener) throws Exception {
throw new OfflineException(ERRORMSG);
}
@Override
- public MusicDirectory getAlbumList(String type, String extra, int size, int offset, Context context, ProgressListener progressListener) throws Exception {
+ public MusicDirectory getAlbumList(String type, String extra, int size, int offset, boolean refresh, Context context, ProgressListener progressListener) throws Exception {
throw new OfflineException(ERRORMSG);
}
@@ -684,10 +722,16 @@ public class OfflineMusicService implements MusicService {
for(File file: dir.listFiles()) {
BufferedReader br = new BufferedReader(new FileReader(file));
while ((line = br.readLine()) != null && !"".equals(line)) {
+ String[] parts = line.split("\t");
+
PodcastChannel channel = new PodcastChannel();
- channel.setId(line);
- channel.setName(line);
+ channel.setId(parts[0]);
+ channel.setName(parts[0]);
channel.setStatus("completed");
+
+ if(parts.length > 1) {
+ channel.setUrl(parts[1]);
+ }
if(FileUtil.getPodcastDirectory(context, channel).exists() && !channels.contains(channel)) {
channels.add(channel);
diff --git a/app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java b/app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java
index 459c8c9e..78a7ec51 100644
--- a/app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java
@@ -522,7 +522,7 @@ public class RESTMusicService implements MusicService {
}
@Override
- public MusicDirectory getAlbumList(String type, int size, int offset, Context context, ProgressListener progressListener) throws Exception {
+ public MusicDirectory getAlbumList(String type, int size, int offset, boolean refresh, Context context, ProgressListener progressListener) throws Exception {
List<String> names = new ArrayList<String>();
List<Object> values = new ArrayList<Object>();
@@ -553,7 +553,7 @@ public class RESTMusicService implements MusicService {
}
@Override
- public MusicDirectory getAlbumList(String type, String extra, int size, int offset, Context context, ProgressListener progressListener) throws Exception {
+ public MusicDirectory getAlbumList(String type, String extra, int size, int offset, boolean refresh, Context context, ProgressListener progressListener) throws Exception {
checkServerVersion(context, "1.10.1", "This type of album list is not supported");
List<String> names = new ArrayList<String>();
@@ -1204,7 +1204,7 @@ public class RESTMusicService implements MusicService {
String content = "";
for(PodcastChannel channel: channels) {
- content += channel.getName() + "\n";
+ content += channel.getName() + "\t" + channel.getUrl() + "\n";
}
File file = FileUtil.getPodcastFile(context, Util.getServerName(context, getInstance(context)));
diff --git a/app/src/main/java/github/daneren2005/dsub/service/parser/PlaylistsParser.java b/app/src/main/java/github/daneren2005/dsub/service/parser/PlaylistsParser.java
index 6f01d510..69e5af64 100644
--- a/app/src/main/java/github/daneren2005/dsub/service/parser/PlaylistsParser.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/parser/PlaylistsParser.java
@@ -22,7 +22,6 @@ import android.content.Context;
import github.daneren2005.dsub.domain.Playlist;
import github.daneren2005.dsub.util.ProgressListener;
-import github.daneren2005.dsub.adapter.PlaylistAdapter;
import org.xmlpull.v1.XmlPullParser;
import java.io.Reader;
@@ -64,7 +63,7 @@ public class PlaylistsParser extends AbstractParser {
validate();
- return PlaylistAdapter.PlaylistComparator.sort(result);
+ return Playlist.PlaylistComparator.sort(result);
}
} \ No newline at end of file
diff --git a/app/src/main/java/github/daneren2005/dsub/service/sync/MostRecentSyncAdapter.java b/app/src/main/java/github/daneren2005/dsub/service/sync/MostRecentSyncAdapter.java
index f7a8634e..8da83be1 100644
--- a/app/src/main/java/github/daneren2005/dsub/service/sync/MostRecentSyncAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/sync/MostRecentSyncAdapter.java
@@ -23,18 +23,14 @@ 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;
/**
@@ -56,7 +52,7 @@ public class MostRecentSyncAdapter extends SubsonicSyncAdapter {
public void onExecuteSync(Context context, int instance) {
try {
ArrayList<String> syncedList = SyncUtil.getSyncedMostRecent(context, instance);
- MusicDirectory albumList = musicService.getAlbumList("newest", 20, 0, context, null);
+ MusicDirectory albumList = musicService.getAlbumList("newest", 20, 0, tagBrowsing, context, null);
List<String> updated = new ArrayList<String>();
boolean firstRun = false;
if(syncedList.size() == 0) {
diff --git a/app/src/main/java/github/daneren2005/dsub/util/Constants.java b/app/src/main/java/github/daneren2005/dsub/util/Constants.java
index b3dd173b..5c477ad9 100644
--- a/app/src/main/java/github/daneren2005/dsub/util/Constants.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/Constants.java
@@ -173,6 +173,8 @@ public final class Constants {
public static final String MAIN_BACK_STACK = "backStackIds";
public static final String MAIN_BACK_STACK_SIZE = "backStackIdsSize";
+ public static final String MAIN_NOW_PLAYING = "nowPlayingId";
+ public static final String MAIN_SLIDE_PANEL_STATE = "slidePanelState";
public static final String FRAGMENT_LIST = "fragmentList";
public static final String FRAGMENT_LIST2 = "fragmentList2";
public static final String FRAGMENT_EXTRA = "fragmentExtra";
diff --git a/app/src/main/java/github/daneren2005/dsub/util/DrawableTint.java b/app/src/main/java/github/daneren2005/dsub/util/DrawableTint.java
new file mode 100644
index 00000000..2da72579
--- /dev/null
+++ b/app/src/main/java/github/daneren2005/dsub/util/DrawableTint.java
@@ -0,0 +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 2015 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.util;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.AttrRes;
+import android.support.annotation.DrawableRes;
+import android.util.TypedValue;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import github.daneren2005.dsub.R;
+
+public class DrawableTint {
+ private static final Map<Integer, Integer> attrMap = new HashMap<>();
+ private static final WeakHashMap<Integer, Drawable> tintedDrawables = new WeakHashMap<>();
+
+ public static Drawable getTintedDrawable(Context context, @DrawableRes int drawableRes) {
+ return getTintedDrawable(context, drawableRes, R.attr.colorAccent);
+ }
+ public static Drawable getTintedDrawable(Context context, @DrawableRes int drawableRes, @AttrRes int colorAttr) {
+ if(tintedDrawables.containsKey(drawableRes)) {
+ return tintedDrawables.get(drawableRes);
+ }
+
+ int color = getColorRes(context, colorAttr);
+ Drawable background = context.getResources().getDrawable(drawableRes);
+ background.setColorFilter(color, PorterDuff.Mode.SRC_IN);
+ tintedDrawables.put(drawableRes, background);
+ return background;
+ }
+ public static int getColorRes(Context context, @AttrRes int colorAttr) {
+ int color;
+ if(attrMap.containsKey(colorAttr)) {
+ color = attrMap.get(colorAttr);
+ } else {
+ TypedValue typedValue = new TypedValue();
+ Resources.Theme theme = context.getTheme();
+ theme.resolveAttribute(colorAttr, typedValue, true);
+ color = typedValue.data;
+ attrMap.put(colorAttr, color);
+ }
+
+ return color;
+ }
+ public static int getDrawableRes(Context context, @AttrRes int drawableAttr) {
+ if(attrMap.containsKey(drawableAttr)) {
+ return attrMap.get(drawableAttr);
+ } else {
+ int[] attrs = new int[]{drawableAttr};
+ TypedArray typedArray = context.obtainStyledAttributes(attrs);
+ @DrawableRes int drawableRes = typedArray.getResourceId(0, 0);
+ typedArray.recycle();
+ attrMap.put(drawableAttr, drawableRes);
+ return drawableRes;
+ }
+ }
+ public static Drawable getTintedAttrDrawable(Context context, @AttrRes int drawableAttr, @AttrRes int colorAttr) {
+ if(tintedDrawables.containsKey(drawableAttr)) {
+ return getTintedDrawable(context, attrMap.get(drawableAttr), colorAttr);
+ }
+
+ @DrawableRes int drawableRes = getDrawableRes(context, drawableAttr);
+ return getTintedDrawable(context, drawableRes, colorAttr);
+ }
+
+ public static void wipeTintCache() {
+ attrMap.clear();
+ tintedDrawables.clear();
+ }
+}
diff --git a/app/src/main/java/github/daneren2005/dsub/util/FileUtil.java b/app/src/main/java/github/daneren2005/dsub/util/FileUtil.java
index 990eae06..332f775c 100644
--- a/app/src/main/java/github/daneren2005/dsub/util/FileUtil.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/FileUtil.java
@@ -182,19 +182,25 @@ public class FileUtil {
}
public static File getAlbumArtFile(Context context, MusicDirectory.Entry entry) {
- File albumDir = getAlbumDirectory(context, entry);
- File artFile;
- File albumFile = getAlbumArtFile(albumDir);
- File hexFile = getHexAlbumArtFile(context, albumDir);
- if(albumDir.exists()) {
- if(hexFile.exists()) {
- hexFile.renameTo(albumFile);
+ if(entry.getId().indexOf("pl-") == -1) {
+ File albumDir = getAlbumDirectory(context, entry);
+ File artFile;
+ File albumFile = getAlbumArtFile(albumDir);
+ File hexFile = getHexAlbumArtFile(context, albumDir);
+ if (albumDir.exists()) {
+ if (hexFile.exists()) {
+ hexFile.renameTo(albumFile);
+ }
+ artFile = albumFile;
+ } else {
+ artFile = hexFile;
}
- artFile = albumFile;
+ return artFile;
} else {
- artFile = hexFile;
+ File playlistDir = getAlbumArtDirectory(context);
+ Log.d(TAG, entry.getTitle() + " => " + Util.md5Hex("pl-" + entry.getTitle()));
+ return new File(playlistDir, Util.md5Hex("pl-" + entry.getTitle()) + ".jpeg");
}
- return artFile;
}
public static File getAlbumArtFile(File albumDir) {
@@ -741,23 +747,28 @@ public class FileUtil {
return index == -1 ? name : name.substring(0, index);
}
- public static Pair<Long, Long> getUsedSize(Context context, File file) {
+ public static Long[] getUsedSize(Context context, File file) {
long number = 0L;
+ long permanent = 0L;
long size = 0L;
if(file.isFile()) {
if(isMediaFile(file)) {
- return new Pair<Long, Long>(1L, file.length());
+ if(file.getAbsolutePath().indexOf(".complete") == -1) {
+ permanent++;
+ }
+ return new Long[] {1L, permanent, file.length()};
} else {
- return new Pair<Long, Long>(0L, 0L);
+ return new Long[] {0L, 0L, 0L};
}
} else {
for (File child : FileUtil.listFiles(file)) {
- Pair<Long, Long> pair = getUsedSize(context, child);
- number += pair.getFirst();
- size += pair.getSecond();
+ Long[] pair = getUsedSize(context, child);
+ number += pair[0];
+ permanent += pair[1];
+ size += pair[2];
}
- return new Pair<Long, Long>(number, size);
+ return new Long[] {number, permanent, size};
}
}
diff --git a/app/src/main/java/github/daneren2005/dsub/util/ImageLoader.java b/app/src/main/java/github/daneren2005/dsub/util/ImageLoader.java
index 7ba2064b..8b027d70 100644
--- a/app/src/main/java/github/daneren2005/dsub/util/ImageLoader.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/ImageLoader.java
@@ -42,6 +42,7 @@ 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.Playlist;
import github.daneren2005.dsub.domain.ServerInfo;
import github.daneren2005.dsub.service.MusicService;
import github.daneren2005.dsub.service.MusicServiceFactory;
@@ -190,8 +191,11 @@ public class ImageLoader {
}
public SilentBackgroundTask loadImage(View view, MusicDirectory.Entry entry, boolean large, boolean crossfade) {
- // TODO: If we know this a artist, try to load artist info instead
int size = large ? imageSizeLarge : imageSizeDefault;
+ return loadImage(view, entry, large, size, crossfade);
+ }
+ public SilentBackgroundTask loadImage(View view, MusicDirectory.Entry entry, boolean large, int size, boolean crossfade) {
+ // TODO: If we know this a artist, try to load artist info instead
if(entry != null && !entry.isAlbum() && ServerInfo.checkServerVersion(context, "1.11") && !Util.isOffline(context)) {
SilentBackgroundTask task = new ArtistImageTask(view.getContext(), entry, size, imageSizeLarge, large, view, crossfade);
task.execute();
@@ -222,7 +226,7 @@ public class ImageLoader {
}
if (!large) {
- setImage(view, Util.createDrawableFromBitmap(context, null), false);
+ setImage(view, null, false);
}
ImageTask task = new ViewImageTask(view.getContext(), entry, size, imageSizeLarge, large, view, crossfade);
task.execute();
@@ -246,6 +250,7 @@ public class ImageLoader {
setImage(view, drawable, true);
return null;
}
+ setImage(view, null, false);
SilentBackgroundTask<Void> task = new ViewUrlTask(view.getContext(), view, url, size);
task.execute();
@@ -274,18 +279,42 @@ public class ImageLoader {
}
public SilentBackgroundTask<Void> loadAvatar(Context context, ImageView view, String username) {
+ if(username == null) {
+ view.setImageResource(R.drawable.ic_social_person);
+ return null;
+ }
+
Bitmap bitmap = cache.get(username);
if (bitmap != null && !bitmap.isRecycled()) {
Drawable drawable = Util.createDrawableFromBitmap(this.context, bitmap);
view.setImageDrawable(drawable);
return null;
}
+ view.setImageDrawable(null);
SilentBackgroundTask<Void> task = new AvatarTask(context, view, username);
task.execute();
return task;
}
+ public SilentBackgroundTask loadImage(View view, Playlist playlist, boolean large, boolean crossfade) {
+ MusicDirectory.Entry entry = new MusicDirectory.Entry();
+ String id;
+ if(Util.isOffline(context)) {
+ id = "pl-" + playlist.getName();
+ entry.setTitle(playlist.getComment());
+ } else {
+ id = "pl-" + playlist.getId();
+ entry.setTitle(playlist.getName());
+ }
+ entry.setId(id);
+ entry.setCoverArt(id);
+ // So this isn't treated as a artist
+ entry.setParent("");
+
+ return loadImage(view, entry, large, crossfade);
+ }
+
private String getKey(String coverArtId, int size) {
return coverArtId + size;
}
@@ -379,12 +408,16 @@ public class ImageLoader {
try {
MusicService musicService = MusicServiceFactory.getMusicService(mContext);
Bitmap bitmap = musicService.getCoverArt(mContext, mEntry, mSize, null, this);
- String key = getKey(mEntry.getCoverArt(), mSize);
- cache.put(key, bitmap);
- // Make sure key is the most recently "used"
- cache.get(key);
- if(mIsNowPlaying) {
- nowPlaying = bitmap;
+ if(bitmap != null) {
+ String key = getKey(mEntry.getCoverArt(), mSize);
+ cache.put(key, bitmap);
+ // Make sure key is the most recently "used"
+ cache.get(key);
+ if (mIsNowPlaying) {
+ nowPlaying = bitmap;
+ }
+ } else {
+ bitmap = getUnknownImage(mEntry, mSize);
}
mDrawable = Util.createDrawableFromBitmap(mContext, bitmap);
@@ -582,7 +615,6 @@ public class ImageLoader {
}
} catch (Throwable x) {
Log.e(TAG, "Failed to download album art.", x);
- cancelled.set(true);
}
return null;
@@ -592,6 +624,8 @@ public class ImageLoader {
protected void done(Void result) {
if(mDrawable != null) {
mView.setImageDrawable(mDrawable);
+ } else {
+ mView.setImageResource(R.drawable.ic_social_person);
}
}
}
diff --git a/app/src/main/java/github/daneren2005/dsub/util/MenuUtil.java b/app/src/main/java/github/daneren2005/dsub/util/MenuUtil.java
new file mode 100644
index 00000000..cd899bb4
--- /dev/null
+++ b/app/src/main/java/github/daneren2005/dsub/util/MenuUtil.java
@@ -0,0 +1,55 @@
+/*
+ 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 2015 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.util;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.view.Menu;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.ServerInfo;
+
+public final class MenuUtil {
+ public static void hideMenuItems(Context context, Menu menu) {
+ 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);
+ }
+ }
+}
diff --git a/app/src/main/java/github/daneren2005/dsub/util/Notifications.java b/app/src/main/java/github/daneren2005/dsub/util/Notifications.java
index d078d77e..c7b6986e 100644
--- a/app/src/main/java/github/daneren2005/dsub/util/Notifications.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/Notifications.java
@@ -260,10 +260,9 @@ public final class Notifications {
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));
+ builder.setContentIntent(PendingIntent.getActivity(context, 2, notificationIntent, 0));
final Notification notification = builder.build();
downloadShowing = true;
diff --git a/app/src/main/java/github/daneren2005/dsub/util/UserUtil.java b/app/src/main/java/github/daneren2005/dsub/util/UserUtil.java
index 29618424..d758c4c9 100644
--- a/app/src/main/java/github/daneren2005/dsub/util/UserUtil.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/UserUtil.java
@@ -16,18 +16,22 @@
package github.daneren2005.dsub.util;
import android.app.Activity;
-import android.app.AlertDialog;
+import android.support.v7.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.support.v7.app.ActionBarActivity;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
+import android.view.WindowManager;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.adapter.SectionAdapter;
import github.daneren2005.dsub.domain.User;
import github.daneren2005.dsub.fragments.SubsonicFragment;
import github.daneren2005.dsub.service.DownloadService;
@@ -182,6 +186,7 @@ public final class UserUtil {
.setCancelable(true);
AlertDialog dialog = builder.create();
+ dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
dialog.show();
}
}
@@ -247,7 +252,6 @@ public final class UserUtil {
protected Void doInBackground() throws Throwable {
MusicService musicService = MusicServiceFactory.getMusicService(context);
musicService.updateUser(user, context, null);
- user.setSettings(user.getSettings());
return null;
}
@@ -326,7 +330,7 @@ public final class UserUtil {
});
}
- public static void deleteUser(final Context context, final User user, final ArrayAdapter adapter) {
+ public static void deleteUser(final Context context, final User user, final SectionAdapter adapter) {
Util.confirmDialog(context, R.string.common_delete, user.getUsername(), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
@@ -341,8 +345,7 @@ public final class UserUtil {
@Override
protected void done(Void v) {
if(adapter != null) {
- adapter.remove(user);
- adapter.notifyDataSetChanged();
+ adapter.removeItem(user);
}
Util.toast(context, context.getResources().getString(R.string.admin_delete_user_success, user.getUsername()));
@@ -378,8 +381,11 @@ public final class UserUtil {
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));
+ final RecyclerView recyclerView = (RecyclerView) layout.findViewById(R.id.settings_list);
+ LinearLayoutManager layoutManager = new LinearLayoutManager(context);
+ layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
+ recyclerView.setLayoutManager(layoutManager);
+ recyclerView.setAdapter(new SettingsAdapter(context, user, null, true));
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.menu_add_user)
diff --git a/app/src/main/java/github/daneren2005/dsub/util/Util.java b/app/src/main/java/github/daneren2005/dsub/util/Util.java
index 032facd6..db0cb7c1 100644
--- a/app/src/main/java/github/daneren2005/dsub/util/Util.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/Util.java
@@ -19,7 +19,8 @@ package github.daneren2005.dsub.util;
import android.annotation.TargetApi;
import android.app.Activity;
-import android.app.AlertDialog;
+import android.support.annotation.StringRes;
+import android.support.v7.app.AlertDialog;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
@@ -45,12 +46,18 @@ import android.text.method.LinkMovementMethod;
import android.text.util.Linkify;
import android.util.Log;
import android.view.Gravity;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.activity.SettingsActivity;
+import github.daneren2005.dsub.activity.SubsonicFragmentActivity;
+import github.daneren2005.dsub.adapter.DetailsAdapter;
import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.domain.PlayerState;
import github.daneren2005.dsub.domain.RepeatMode;
+import github.daneren2005.dsub.domain.ServerInfo;
import github.daneren2005.dsub.receiver.MediaButtonIntentReceiver;
import github.daneren2005.dsub.service.DownloadService;
@@ -68,10 +75,14 @@ import java.lang.reflect.Method;
import java.security.MessageDigest;
import java.text.DecimalFormat;
import java.text.NumberFormat;
+import java.text.ParseException;
import java.text.SimpleDateFormat;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
+import java.util.List;
import java.util.Locale;
+import java.util.TimeZone;
/**
* @author Sindre Mehus
@@ -249,6 +260,32 @@ public final class Util {
SharedPreferences prefs = getPreferences(context);
return prefs.getString(Constants.PREFERENCES_KEY_THEME, null);
}
+ public static int getThemeRes(Context context) {
+ return getThemeRes(context, getTheme(context));
+ }
+ public static int getThemeRes(Context context, String theme) {
+ if(context instanceof SubsonicFragmentActivity || context instanceof SettingsActivity) {
+ if ("dark".equals(theme)) {
+ return R.style.Theme_DSub_Dark_No_Actionbar;
+ } else if ("black".equals(theme)) {
+ return R.style.Theme_DSub_Black_No_Actionbar;
+ } else if ("holo".equals(theme)) {
+ return R.style.Theme_DSub_Holo_No_Actionbar;
+ } else {
+ return R.style.Theme_DSub_Light_No_Actionbar;
+ }
+ } else {
+ if ("dark".equals(theme)) {
+ return R.style.Theme_DSub_Dark;
+ } else if ("black".equals(theme)) {
+ return R.style.Theme_DSub_Black;
+ } else if ("holo".equals(theme)) {
+ return R.style.Theme_DSub_Holo;
+ } else {
+ return R.style.Theme_DSub_Light;
+ }
+ }
+ }
public static void setTheme(Context context, String theme) {
SharedPreferences.Editor editor = getPreferences(context).edit();
editor.putString(Constants.PREFERENCES_KEY_THEME, theme);
@@ -256,15 +293,7 @@ public final class Util {
}
public static void applyTheme(Context context, String theme) {
- if ("dark".equals(theme)) {
- context.setTheme(R.style.Theme_DSub_Dark);
- } else if ("black".equals(theme)) {
- context.setTheme(R.style.Theme_DSub_Black);
- } else if ("holo".equals(theme)) {
- context.setTheme(R.style.Theme_DSub_Holo);
- } else {
- context.setTheme(R.style.Theme_DSub_Light);
- }
+ context.setTheme(getThemeRes(context, theme));
SharedPreferences prefs = Util.getPreferences(context);
if(prefs.getBoolean(Constants.PREFERENCES_KEY_OVERRIDE_SYSTEM_LANGUAGE, false)) {
@@ -809,6 +838,19 @@ public final class Util {
return builder.toString();
}
+ public static String formatDate(Context context, String dateString) {
+ try {
+ 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"));
+ }
+
+ return formatDate(dateFormat.parse(dateString));
+ } catch(ParseException e) {
+ return dateString;
+ }
+ }
public static String formatDate(Date date) {
if(date == null) {
return "Never";
@@ -1101,6 +1143,32 @@ public final class Util {
((TextView)dialog.findViewById(android.R.id.message)).setMovementMethod(LinkMovementMethod.getInstance());
}
+ public static void showDetailsDialog(Context context, @StringRes int title, List<Integer> headers, List<String> details) {
+ List<String> headerStrings = new ArrayList<>();
+ for(@StringRes Integer res: headers) {
+ headerStrings.add(context.getResources().getString(res));
+ }
+ showDetailsDialog(context, context.getResources().getString(title), headerStrings, details);
+ }
+ public static void showDetailsDialog(Context context, String title, List<String> headers, List<String> details) {
+ ListView listView = new ListView(context);
+ listView.setAdapter(new DetailsAdapter(context, R.layout.details_item, headers, details));
+ listView.setDivider(null);
+ listView.setScrollbarFadingEnabled(false);
+
+ new AlertDialog.Builder(context)
+ // .setIcon(android.R.drawable.ic_dialog_info)
+ .setTitle(title)
+ .setView(listView)
+ .setPositiveButton(R.string.common_close, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int i) {
+ dialog.dismiss();
+ }
+ })
+ .show();
+ }
+
public static void sleepQuietly(long millis) {
try {
Thread.sleep(millis);
@@ -1141,15 +1209,6 @@ public final class Util {
}
}
- public static int getAttribute(Context context, int attr) {
- int res;
- int[] attrs = new int[] {attr};
- TypedArray typedArray = context.obtainStyledAttributes(attrs);
- res = typedArray.getResourceId(0, 0);
- typedArray.recycle();
- return res;
- }
-
public static void registerMediaButtonEventReceiver(Context context) {
// Only do it if enabled in the settings.
diff --git a/app/src/main/java/github/daneren2005/dsub/view/AlbumCell.java b/app/src/main/java/github/daneren2005/dsub/view/AlbumCell.java
deleted file mode 100644
index 8707ece7..00000000
--- a/app/src/main/java/github/daneren2005/dsub/view/AlbumCell.java
+++ /dev/null
@@ -1,108 +0,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 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/app/src/main/java/github/daneren2005/dsub/view/AlbumListCountView.java b/app/src/main/java/github/daneren2005/dsub/view/AlbumListCountView.java
new file mode 100644
index 00000000..1ae9ef8e
--- /dev/null
+++ b/app/src/main/java/github/daneren2005/dsub/view/AlbumListCountView.java
@@ -0,0 +1,130 @@
+/*
+ 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 2015 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.view;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.MusicDirectory;
+import github.daneren2005.dsub.service.MusicService;
+import github.daneren2005.dsub.service.MusicServiceFactory;
+import github.daneren2005.dsub.util.Constants;
+import github.daneren2005.dsub.util.FileUtil;
+import github.daneren2005.dsub.util.Util;
+
+public class AlbumListCountView extends UpdateView2<Integer, Void> {
+ private final String TAG = AlbumListCountView.class.getSimpleName();
+
+ private TextView titleView;
+ private TextView countView;
+ private int startCount;
+ private int count = 0;
+
+ public AlbumListCountView(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(Integer albumListString, Void dummy) {
+ titleView.setText(albumListString);
+
+ SharedPreferences prefs = Util.getPreferences(context);
+ startCount = prefs.getInt(Constants.PREFERENCES_KEY_RECENT_COUNT + Util.getActiveServer(context), 0);
+ count = startCount;
+ update();
+ }
+
+ @Override
+ protected void updateBackground() {
+ try {
+ 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, false, 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
+ 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) {
+ // 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();
+ }
+ } catch(Exception e) {
+ Log.w(TAG, "Failed to refresh most recent count", e);
+ }
+ }
+
+ @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);
+ }
+ }
+
+ @Override
+ public void onClick() {
+ SharedPreferences.Editor editor = Util.getPreferences(context).edit();
+ editor.putInt(Constants.PREFERENCES_KEY_RECENT_COUNT + Util.getActiveServer(context), 0);
+ editor.commit();
+
+ count = 0;
+ update();
+ }
+}
diff --git a/app/src/main/java/github/daneren2005/dsub/view/AlbumView.java b/app/src/main/java/github/daneren2005/dsub/view/AlbumView.java
index bd54ea1e..e521babf 100644
--- a/app/src/main/java/github/daneren2005/dsub/view/AlbumView.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/AlbumView.java
@@ -16,10 +16,10 @@
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;
@@ -30,75 +30,76 @@ 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 {
+public class AlbumView extends UpdateView2<MusicDirectory.Entry, ImageLoader> {
private static final String TAG = AlbumView.class.getSimpleName();
- private Context context;
- private MusicDirectory.Entry album;
private File file;
-
+ private View coverArtView;
private TextView titleView;
private TextView artistView;
- private View coverArtView;
+ private boolean showArtist = true;
- public AlbumView(Context context) {
+ public AlbumView(Context context, boolean cell) {
super(context);
- this.context = context;
- LayoutInflater.from(context).inflate(R.layout.album_list_item, this, true);
+ if(cell) {
+ LayoutInflater.from(context).inflate(R.layout.album_cell_item, this, true);
+ } else {
+ LayoutInflater.from(context).inflate(R.layout.album_list_item, this, true);
+ }
+
+ coverArtView = findViewById(R.id.album_coverart);
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);
+ ratingBar.setFocusable(false);
starButton = (ImageButton) findViewById(R.id.album_star);
starButton.setFocusable(false);
+ moreButton = (ImageView) findViewById(R.id.more_button);
- moreButton = (ImageView) findViewById(R.id.album_more);
- moreButton.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- v.showContextMenu();
- }
- });
+ checkable = true;
+ }
+
+ public void setShowArtist(boolean showArtist) {
+ this.showArtist = showArtist;
}
- protected void setObjectImpl(Object obj1, Object obj2) {
- this.album = (MusicDirectory.Entry) obj1;
+ protected void setObjectImpl(MusicDirectory.Entry album, ImageLoader imageLoader) {
titleView.setText(album.getAlbumDisplay());
- String artist = album.getArtist();
- if(artist == null) {
- artist = "";
- }
- if(album.getYear() != null) {
- artist += " - " + album.getYear();
+ 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(artist);
- artistView.setVisibility(album.getArtist() == null ? View.GONE : View.VISIBLE);
- imageTask = ((ImageLoader)obj2).loadImage(coverArtView, album, false, true);
+ artistView.setText(album.getArtist() == null ? "" : artist);
+ imageTask = imageLoader.loadImage(coverArtView, album, false, true);
file = null;
}
@Override
protected void updateBackground() {
if(file == null) {
- file = FileUtil.getAlbumDirectory(context, album);
+ file = FileUtil.getAlbumDirectory(context, item);
}
exists = file.exists();
- isStarred = album.isStarred();
- isRated = album.getRating();
+ isStarred = item.isStarred();
+ isRated = item.getRating();
}
public MusicDirectory.Entry getEntry() {
- return album;
+ return item;
}
public File getFile() {
diff --git a/app/src/main/java/github/daneren2005/dsub/view/ArtistEntryView.java b/app/src/main/java/github/daneren2005/dsub/view/ArtistEntryView.java
index 71bdeb78..f6a6adb3 100644
--- a/app/src/main/java/github/daneren2005/dsub/view/ArtistEntryView.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/ArtistEntryView.java
@@ -19,7 +19,6 @@
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;
@@ -28,26 +27,21 @@ 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 {
+public class ArtistEntryView extends UpdateView<MusicDirectory.Entry> {
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);
@@ -61,8 +55,7 @@ public class ArtistEntryView extends UpdateView {
});
}
- protected void setObjectImpl(Object obj) {
- this.artist = (MusicDirectory.Entry) obj;
+ protected void setObjectImpl(MusicDirectory.Entry artist) {
titleView.setText(artist.getTitle());
file = FileUtil.getArtistDirectory(context, artist);
}
@@ -70,7 +63,7 @@ public class ArtistEntryView extends UpdateView {
@Override
protected void updateBackground() {
exists = file.exists();
- isStarred = artist.isStarred();
+ isStarred = item.isStarred();
}
public File getFile() {
diff --git a/app/src/main/java/github/daneren2005/dsub/view/ArtistView.java b/app/src/main/java/github/daneren2005/dsub/view/ArtistView.java
index c255be69..d86c5d26 100644
--- a/app/src/main/java/github/daneren2005/dsub/view/ArtistView.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/ArtistView.java
@@ -35,18 +35,14 @@ import java.io.File;
*
* @author Sindre Mehus
*/
-public class ArtistView extends UpdateView {
+public class ArtistView extends UpdateView<Artist> {
private static final String TAG = ArtistView.class.getSimpleName();
-
- private Context context;
- private Artist artist;
- private File file;
+ 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);
@@ -60,8 +56,7 @@ public class ArtistView extends UpdateView {
});
}
- protected void setObjectImpl(Object obj) {
- this.artist = (Artist) obj;
+ protected void setObjectImpl(Artist artist) {
titleView.setText(artist.getName());
file = FileUtil.getArtistDirectory(context, artist);
}
@@ -69,7 +64,7 @@ public class ArtistView extends UpdateView {
@Override
protected void updateBackground() {
exists = file.exists();
- isStarred = artist.isStarred();
+ isStarred = item.isStarred();
}
public File getFile() {
diff --git a/app/src/main/java/github/daneren2005/dsub/view/BasicHeaderView.java b/app/src/main/java/github/daneren2005/dsub/view/BasicHeaderView.java
new file mode 100644
index 00000000..01aa42e1
--- /dev/null
+++ b/app/src/main/java/github/daneren2005/dsub/view/BasicHeaderView.java
@@ -0,0 +1,40 @@
+/*
+ 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 2015 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.view;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.widget.TextView;
+
+import github.daneren2005.dsub.R;
+
+public class BasicHeaderView extends UpdateView<String> {
+ TextView nameView;
+
+ public BasicHeaderView(Context context) {
+ this(context, R.layout.basic_header);
+ }
+ public BasicHeaderView(Context context, int layout) {
+ super(context, false);
+
+ LayoutInflater.from(context).inflate(layout, this, true);
+ nameView = (TextView) findViewById(R.id.item_name);
+ }
+
+ protected void setObjectImpl(String string) {
+ nameView.setText(string);
+ }
+}
diff --git a/app/src/main/java/github/daneren2005/dsub/view/BasicListView.java b/app/src/main/java/github/daneren2005/dsub/view/BasicListView.java
new file mode 100644
index 00000000..ca7e8993
--- /dev/null
+++ b/app/src/main/java/github/daneren2005/dsub/view/BasicListView.java
@@ -0,0 +1,44 @@
+/*
+ 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 2015 (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.TextView;
+
+import github.daneren2005.dsub.R;
+
+public class BasicListView extends UpdateView<String> {
+ private TextView titleView;
+
+ public BasicListView(Context context) {
+ super(context, false);
+ 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.setVisibility(View.GONE);
+ }
+
+ protected void setObjectImpl(String string) {
+ titleView.setText(string);
+ }
+}
diff --git a/app/src/main/java/github/daneren2005/dsub/view/ChangeLog.java b/app/src/main/java/github/daneren2005/dsub/view/ChangeLog.java
index 096583c7..e3d24485 100644
--- a/app/src/main/java/github/daneren2005/dsub/view/ChangeLog.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/ChangeLog.java
@@ -42,7 +42,7 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
-import android.app.AlertDialog;
+import android.support.v7.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
diff --git a/app/src/main/java/github/daneren2005/dsub/view/ErrorDialog.java b/app/src/main/java/github/daneren2005/dsub/view/ErrorDialog.java
index 0b9d05a0..3d6eaa52 100644
--- a/app/src/main/java/github/daneren2005/dsub/view/ErrorDialog.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/ErrorDialog.java
@@ -19,7 +19,7 @@
package github.daneren2005.dsub.view;
import android.app.Activity;
-import android.app.AlertDialog;
+import android.support.v7.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
diff --git a/app/src/main/java/github/daneren2005/dsub/view/GenreView.java b/app/src/main/java/github/daneren2005/dsub/view/GenreView.java
index 8dbcf89d..b29aefba 100644
--- a/app/src/main/java/github/daneren2005/dsub/view/GenreView.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/GenreView.java
@@ -25,7 +25,7 @@ import android.widget.TextView;
import github.daneren2005.dsub.R;
import github.daneren2005.dsub.domain.Genre;
-public class GenreView extends UpdateView {
+public class GenreView extends UpdateView<Genre> {
private static final String TAG = GenreView.class.getSimpleName();
private TextView titleView;
@@ -41,8 +41,7 @@ public class GenreView extends UpdateView {
albumsView = (TextView) findViewById(R.id.genre_albums);
}
- public void setObjectImpl(Object obj) {
- Genre genre = (Genre) obj;
+ public void setObjectImpl(Genre genre) {
titleView.setText(genre.getName());
if(genre.getAlbumCount() != null) {
diff --git a/app/src/main/java/github/daneren2005/dsub/view/GridSpacingDecoration.java b/app/src/main/java/github/daneren2005/dsub/view/GridSpacingDecoration.java
new file mode 100644
index 00000000..3bb3e8a1
--- /dev/null
+++ b/app/src/main/java/github/daneren2005/dsub/view/GridSpacingDecoration.java
@@ -0,0 +1,99 @@
+/*
+ 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 2015 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.view;
+
+import android.graphics.Rect;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.util.TypedValue;
+import android.view.View;
+
+public class GridSpacingDecoration extends RecyclerView.ItemDecoration {
+
+ @Override
+ public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
+ super.getItemOffsets(outRect, view, parent, state);
+
+ int spacing = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, view.getResources().getDisplayMetrics());
+ int halfSpacing = spacing / 2;
+
+ int childCount = parent.getChildCount();
+ int childIndex = parent.getChildPosition(view);
+ int spanCount = getTotalSpan(view, parent);
+ int spanIndex = childIndex % spanCount;
+ int spanSize = getSpanSize(parent, childIndex);
+
+ /* INVALID SPAN */
+ if (spanCount < 1 || spanSize > 1) return;
+
+ outRect.top = halfSpacing;
+ outRect.bottom = halfSpacing;
+ outRect.left = halfSpacing;
+ outRect.right = halfSpacing;
+
+ if (isTopEdge(childIndex, spanCount)) {
+ outRect.top = spacing;
+ }
+
+ if (isLeftEdge(spanIndex, spanCount)) {
+ outRect.left = spacing;
+ }
+
+ if (isRightEdge(spanIndex, spanCount)) {
+ outRect.right = spacing;
+ }
+
+ if (isBottomEdge(childIndex, childCount, spanCount)) {
+ outRect.bottom = spacing;
+ }
+ }
+
+ protected int getTotalSpan(View view, RecyclerView parent) {
+ RecyclerView.LayoutManager mgr = parent.getLayoutManager();
+ if (mgr instanceof GridLayoutManager) {
+ return ((GridLayoutManager) mgr).getSpanCount();
+ }
+
+ return -1;
+ }
+ protected int getSpanSize(RecyclerView parent, int childIndex) {
+ RecyclerView.LayoutManager mgr = parent.getLayoutManager();
+ if (mgr instanceof GridLayoutManager) {
+ GridLayoutManager.SpanSizeLookup lookup = ((GridLayoutManager) mgr).getSpanSizeLookup();
+ if(lookup != null) {
+ return lookup.getSpanSize(childIndex);
+ }
+ }
+
+ return 1;
+ }
+
+ protected boolean isLeftEdge(int spanIndex, int spanCount) {
+ return spanIndex == 0;
+ }
+
+ protected boolean isRightEdge(int spanIndex, int spanCount) {
+ return spanIndex == spanCount - 1;
+ }
+
+ protected boolean isTopEdge(int childIndex, int spanCount) {
+ return childIndex < spanCount;
+ }
+
+ protected boolean isBottomEdge(int childIndex, int childCount, int spanCount) {
+ return childIndex >= childCount - spanCount;
+ }
+}
diff --git a/app/src/main/java/github/daneren2005/dsub/view/HeaderGridView.java b/app/src/main/java/github/daneren2005/dsub/view/HeaderGridView.java
deleted file mode 100644
index 8a82f353..00000000
--- a/app/src/main/java/github/daneren2005/dsub/view/HeaderGridView.java
+++ /dev/null
@@ -1,836 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package github.daneren2005.dsub.view;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.database.DataSetObservable;
-import android.database.DataSetObserver;
-import android.os.Build;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.*;
-
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-
-/**
- * A {@link GridView} that supports adding header rows in a
- * very similar way to {@link android.widget.ListView}.
- * See {@link HeaderGridView#addHeaderView(View, Object, boolean)}
- * See {@link HeaderGridView#addFooterView(View, Object, boolean)}
- */
-public class HeaderGridView extends GridView {
- private static final String TAG = HeaderGridView.class.getSimpleName();
- public static boolean DEBUG = false;
-
- /**
- * A class that represents a fixed view in a list, for example a header at the top
- * or a footer at the bottom.
- */
- private static class FixedViewInfo {
- /**
- * The view to add to the grid
- */
- public View view;
- public ViewGroup viewContainer;
- /**
- * The data backing the view. This is returned from {@link ListAdapter#getItem(int)}.
- */
- public Object data;
- /**
- * <code>true</code> if the fixed view should be selectable in the grid
- */
- public boolean isSelectable;
- }
-
- private int mNumColumns = AUTO_FIT;
- private View mViewForMeasureRowHeight = null;
- private int mRowHeight = -1;
- private static final String LOG_TAG = HeaderGridView.class.getSimpleName();
-
- private ArrayList<FixedViewInfo> mHeaderViewInfos = new ArrayList<FixedViewInfo>();
- private ArrayList<FixedViewInfo> mFooterViewInfos = new ArrayList<FixedViewInfo>();
-
- private void initHeaderGridView() {
- }
-
- public HeaderGridView(Context context) {
- super(context);
- initHeaderGridView();
- }
-
- public HeaderGridView(Context context, AttributeSet attrs) {
- super(context, attrs);
- initHeaderGridView();
- }
-
- public HeaderGridView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- initHeaderGridView();
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- ListAdapter adapter = getAdapter();
- if (adapter != null && adapter instanceof HeaderViewGridAdapter) {
- ((HeaderViewGridAdapter) adapter).setNumColumns(getNumColumnsCompatible());
- ((HeaderViewGridAdapter) adapter).setRowHeight(getRowHeight());
- }
- }
-
- @Override
- public void setClipChildren(boolean clipChildren) {
- // Ignore, since the header rows depend on not being clipped
- }
-
- /**
- * Do not call this method unless you know how it works.
- *
- * @param clipChildren
- */
- public void setClipChildrenSupper(boolean clipChildren) {
- super.setClipChildren(false);
- }
-
- /**
- * Add a fixed view to appear at the top of the grid. If addHeaderView is
- * called more than once, the views will appear in the order they were
- * added. Views added using this call can take focus if they want.
- * <p/>
- * NOTE: Call this before calling setAdapter. This is so HeaderGridView can wrap
- * the supplied cursor with one that will also account for header views.
- *
- * @param v The view to add.
- */
- public void addHeaderView(View v) {
- addHeaderView(v, null, true);
- }
-
- /**
- * Add a fixed view to appear at the top of the grid. If addHeaderView is
- * called more than once, the views will appear in the order they were
- * added. Views added using this call can take focus if they want.
- * <p/>
- * NOTE: Call this before calling setAdapter. This is so HeaderGridView can wrap
- * the supplied cursor with one that will also account for header views.
- *
- * @param v The view to add.
- * @param data Data to associate with this view
- * @param isSelectable whether the item is selectable
- */
- public void addHeaderView(View v, Object data, boolean isSelectable) {
- ListAdapter adapter = getAdapter();
- if (adapter != null && !(adapter instanceof HeaderViewGridAdapter)) {
- throw new IllegalStateException(
- "Cannot add header view to grid -- setAdapter has already been called.");
- }
-
- ViewGroup.LayoutParams lyp = v.getLayoutParams();
-
- FixedViewInfo info = new FixedViewInfo();
- FrameLayout fl = new FullWidthFixedViewLayout(getContext());
-
- if (lyp != null) {
- v.setLayoutParams(new FrameLayout.LayoutParams(lyp.width, lyp.height));
- fl.setLayoutParams(new AbsListView.LayoutParams(lyp.width, lyp.height));
- }
- fl.addView(v);
- info.view = v;
- info.viewContainer = fl;
- info.data = data;
- info.isSelectable = isSelectable;
- mHeaderViewInfos.add(info);
- // in the case of re-adding a header view, or adding one later on,
- // we need to notify the observer
- if (adapter != null) {
- ((HeaderViewGridAdapter) adapter).notifyDataSetChanged();
- }
- }
-
- public void addFooterView(View v) {
- addFooterView(v, null, true);
- }
-
- public void addFooterView(View v, Object data, boolean isSelectable) {
- ListAdapter mAdapter = getAdapter();
- if (mAdapter != null && !(mAdapter instanceof HeaderViewGridAdapter)) {
- throw new IllegalStateException(
- "Cannot add header view to grid -- setAdapter has already been called.");
- }
-
- ViewGroup.LayoutParams lyp = v.getLayoutParams();
-
- FixedViewInfo info = new FixedViewInfo();
- FrameLayout fl = new FullWidthFixedViewLayout(getContext());
-
- if (lyp != null) {
- v.setLayoutParams(new FrameLayout.LayoutParams(lyp.width, lyp.height));
- fl.setLayoutParams(new AbsListView.LayoutParams(lyp.width, lyp.height));
- }
- fl.addView(v);
- info.view = v;
- info.viewContainer = fl;
- info.data = data;
- info.isSelectable = isSelectable;
- mFooterViewInfos.add(info);
-
- if (mAdapter != null) {
- ((HeaderViewGridAdapter) mAdapter).notifyDataSetChanged();
- }
- }
-
- public int getHeaderViewCount() {
- return mHeaderViewInfos.size();
- }
-
- public int getFooterViewCount() {
- return mFooterViewInfos.size();
- }
-
- /**
- * Removes a previously-added header view.
- *
- * @param v The view to remove
- * @return true if the view was removed, false if the view was not a header
- * view
- */
- public boolean removeHeaderView(View v) {
- if (mHeaderViewInfos.size() > 0) {
- boolean result = false;
- ListAdapter adapter = getAdapter();
- if (adapter != null && ((HeaderViewGridAdapter) adapter).removeHeader(v)) {
- result = true;
- }
- removeFixedViewInfo(v, mHeaderViewInfos);
- return result;
- }
- return false;
- }
-
- /**
- * Removes a previously-added footer view.
- *
- * @param v The view to remove
- * @return true if the view was removed, false if the view was not a header
- * view
- */
- public boolean removeFooterView(View v) {
- if (mFooterViewInfos.size() > 0) {
- boolean result = false;
- ListAdapter adapter = getAdapter();
- if (adapter != null && ((HeaderViewGridAdapter) adapter).removeFooter(v)) {
- result = true;
- }
- removeFixedViewInfo(v, mFooterViewInfos);
- return result;
- }
- return false;
- }
-
- private void removeFixedViewInfo(View v, ArrayList<FixedViewInfo> where) {
- int len = where.size();
- for (int i = 0; i < len; ++i) {
- FixedViewInfo info = where.get(i);
- if (info.view == v) {
- where.remove(i);
- break;
- }
- }
- }
-
- @TargetApi(11)
- private int getNumColumnsCompatible() {
- if (Build.VERSION.SDK_INT >= 11) {
- return super.getNumColumns();
- } else {
- try {
- Field numColumns = GridView.class.getSuperclass().getDeclaredField("mNumColumns");
- numColumns.setAccessible(true);
- return numColumns.getInt(this);
- } catch (Exception e) {
- if (mNumColumns != -1) {
- return mNumColumns;
- } else {
- return 2;
- }
- }
- }
- }
-
- @TargetApi(16)
- private int getColumnWidthCompatible() {
- if (Build.VERSION.SDK_INT >= 16) {
- return super.getColumnWidth();
- } else {
- try {
- Field numColumns = getClass().getSuperclass().getDeclaredField("mColumnWidth");
- numColumns.setAccessible(true);
- return numColumns.getInt(this);
- } catch (NoSuchFieldException e) {
- throw new RuntimeException(e);
- } catch (IllegalAccessException e) {
- throw new RuntimeException(e);
- }
- }
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- mViewForMeasureRowHeight = null;
- }
-
- public void invalidateRowHeight() {
- mRowHeight = -1;
- }
-
- public int getHeaderHeight(int row) {
- if (row >= 0) {
- return mHeaderViewInfos.get(row).view.getMeasuredHeight();
- }
-
- return 0;
- }
-
- @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
- public int getVerticalSpacing(){
- int value = 0;
-
- try {
- int currentapiVersion = android.os.Build.VERSION.SDK_INT;
- if (currentapiVersion < Build.VERSION_CODES.JELLY_BEAN){
- Field field = this.getClass().getSuperclass().getDeclaredField("mVerticalSpacing");
- field.setAccessible(true);
- value = field.getInt(this);
- } else{
- value = super.getVerticalSpacing();
- }
-
- }catch (Exception ex){
-
- }
-
- return value;
- }
-
- @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
- public int getHorizontalSpacing(){
- int value = 0;
-
- try {
- int currentapiVersion = android.os.Build.VERSION.SDK_INT;
- if (currentapiVersion < Build.VERSION_CODES.JELLY_BEAN){
- Field field = this.getClass().getSuperclass().getDeclaredField("mHorizontalSpacing");
- field.setAccessible(true);
- value = field.getInt(this);
- } else{
- value = super.getHorizontalSpacing();
- }
-
- }catch (Exception ex){
-
- }
-
- return value;
- }
-
- public int getRowHeight() {
- if (mRowHeight > 0) {
- // return mRowHeight;
- }
- ListAdapter adapter = getAdapter();
- int numColumns = getNumColumnsCompatible();
-
- // adapter has not been set or has no views in it;
- if (adapter == null || adapter.getCount() <= numColumns * (mHeaderViewInfos.size() + mFooterViewInfos.size()) || numColumns == -1) {
- return -1;
- }
- int mColumnWidth = getColumnWidthCompatible();
- View view = getAdapter().getView(numColumns * mHeaderViewInfos.size(), mViewForMeasureRowHeight, this);
- AbsListView.LayoutParams p = (AbsListView.LayoutParams) view.getLayoutParams();
- if (p == null) {
- p = new AbsListView.LayoutParams(-1, -2, 0);
- view.setLayoutParams(p);
- }
- int childHeightSpec = getChildMeasureSpec(
- MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 0, p.height);
- int childWidthSpec = getChildMeasureSpec(
- MeasureSpec.makeMeasureSpec(mColumnWidth, MeasureSpec.EXACTLY), 0, p.width);
- view.measure(childWidthSpec, childHeightSpec);
- mViewForMeasureRowHeight = view;
- mRowHeight = view.getMeasuredHeight();
- return mRowHeight;
- }
-
- @TargetApi(11)
- public void tryToScrollToBottomSmoothly() {
- int lastPos = getAdapter().getCount() - 1;
- if (Build.VERSION.SDK_INT >= 11) {
- smoothScrollToPositionFromTop(lastPos, 0);
- } else {
- setSelection(lastPos);
- }
- }
-
- @TargetApi(11)
- public void tryToScrollToBottomSmoothly(int duration) {
- int lastPos = getAdapter().getCount() - 1;
- if (Build.VERSION.SDK_INT >= 11) {
- smoothScrollToPositionFromTop(lastPos, 0, duration);
- } else {
- setSelection(lastPos);
- }
- }
-
- @Override
- public void setAdapter(ListAdapter adapter) {
- if (mHeaderViewInfos.size() > 0 || mFooterViewInfos.size() > 0) {
- HeaderViewGridAdapter headerViewGridAdapter = new HeaderViewGridAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
- int numColumns = getNumColumnsCompatible();
- if (numColumns > 1) {
- headerViewGridAdapter.setNumColumns(numColumns);
- }
- headerViewGridAdapter.setRowHeight(getRowHeight());
- super.setAdapter(headerViewGridAdapter);
- } else {
- super.setAdapter(adapter);
- }
- }
-
- /**
- * full width
- */
- private class FullWidthFixedViewLayout extends FrameLayout {
-
- public FullWidthFixedViewLayout(Context context) {
- super(context);
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- int realLeft = HeaderGridView.this.getPaddingLeft() + getPaddingLeft();
- // Try to make where it should be, from left, full width
- if (realLeft != left) {
- offsetLeftAndRight(realLeft - left);
- }
- super.onLayout(changed, left, top, right, bottom);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int targetWidth = HeaderGridView.this.getMeasuredWidth()
- - HeaderGridView.this.getPaddingLeft()
- - HeaderGridView.this.getPaddingRight();
- widthMeasureSpec = MeasureSpec.makeMeasureSpec(targetWidth,
- MeasureSpec.getMode(widthMeasureSpec));
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
- }
-
- @Override
- public void setNumColumns(int numColumns) {
- super.setNumColumns(numColumns);
- mNumColumns = numColumns;
- ListAdapter adapter = getAdapter();
- if (adapter != null && adapter instanceof HeaderViewGridAdapter) {
- ((HeaderViewGridAdapter) adapter).setNumColumns(numColumns);
- }
- }
-
- /**
- * ListAdapter used when a HeaderGridView has header views. This ListAdapter
- * wraps another one and also keeps track of the header views and their
- * associated data objects.
- * <p>This is intended as a base class; you will probably not need to
- * use this class directly in your own code.
- */
- private static class HeaderViewGridAdapter extends BaseAdapter implements WrapperListAdapter, Filterable {
- // This is used to notify the container of updates relating to number of columns
- // or headers changing, which changes the number of placeholders needed
- private final DataSetObservable mDataSetObservable = new DataSetObservable();
- private final ListAdapter mAdapter;
- static final ArrayList<FixedViewInfo> EMPTY_INFO_LIST =
- new ArrayList<FixedViewInfo>();
-
- // This ArrayList is assumed to NOT be null.
- ArrayList<FixedViewInfo> mHeaderViewInfos;
- ArrayList<FixedViewInfo> mFooterViewInfos;
- private int mNumColumns = 1;
- private int mRowHeight = -1;
- boolean mAreAllFixedViewsSelectable;
- private final boolean mIsFilterable;
- private boolean mCachePlaceHoldView = true;
- // From Recycle Bin or calling getView, this a question...
- private boolean mCacheFirstHeaderView = false;
-
- public HeaderViewGridAdapter(ArrayList<FixedViewInfo> headerViewInfos, ArrayList<FixedViewInfo> footViewInfos, ListAdapter adapter) {
- mAdapter = adapter;
- mIsFilterable = adapter instanceof Filterable;
- if (headerViewInfos == null) {
- mHeaderViewInfos = EMPTY_INFO_LIST;
- } else {
- mHeaderViewInfos = headerViewInfos;
- }
-
- if (footViewInfos == null) {
- mFooterViewInfos = EMPTY_INFO_LIST;
- } else {
- mFooterViewInfos = footViewInfos;
- }
- mAreAllFixedViewsSelectable = areAllListInfosSelectable(mHeaderViewInfos)
- && areAllListInfosSelectable(mFooterViewInfos);
- }
-
- public void setNumColumns(int numColumns) {
- if (numColumns < 1) {
- return;
- }
- if (mNumColumns != numColumns) {
- mNumColumns = numColumns;
- notifyDataSetChanged();
- }
- }
-
- public void setRowHeight(int height) {
- mRowHeight = height;
- }
-
- public int getHeadersCount() {
- return mHeaderViewInfos.size();
- }
-
- public int getFootersCount() {
- return mFooterViewInfos.size();
- }
-
- @Override
- public boolean isEmpty() {
- return (mAdapter == null || mAdapter.isEmpty()) && getHeadersCount() == 0 && getFootersCount() == 0;
- }
-
- private boolean areAllListInfosSelectable(ArrayList<FixedViewInfo> infos) {
- if (infos != null) {
- for (FixedViewInfo info : infos) {
- if (!info.isSelectable) {
- return false;
- }
- }
- }
- return true;
- }
-
- public boolean removeHeader(View v) {
- for (int i = 0; i < mHeaderViewInfos.size(); i++) {
- FixedViewInfo info = mHeaderViewInfos.get(i);
- if (info.view == v) {
- mHeaderViewInfos.remove(i);
- mAreAllFixedViewsSelectable =
- areAllListInfosSelectable(mHeaderViewInfos) && areAllListInfosSelectable(mFooterViewInfos);
- mDataSetObservable.notifyChanged();
- return true;
- }
- }
- return false;
- }
-
- public boolean removeFooter(View v) {
- for (int i = 0; i < mFooterViewInfos.size(); i++) {
- FixedViewInfo info = mFooterViewInfos.get(i);
- if (info.view == v) {
- mFooterViewInfos.remove(i);
- mAreAllFixedViewsSelectable =
- areAllListInfosSelectable(mHeaderViewInfos) && areAllListInfosSelectable(mFooterViewInfos);
- mDataSetObservable.notifyChanged();
- return true;
- }
- }
- return false;
- }
-
- @Override
- public int getCount() {
- if (mAdapter != null) {
- return (getFootersCount() + getHeadersCount()) * mNumColumns + getAdapterAndPlaceHolderCount();
- } else {
- return (getFootersCount() + getHeadersCount()) * mNumColumns;
- }
- }
-
- @Override
- public boolean areAllItemsEnabled() {
- if (mAdapter != null) {
- return mAreAllFixedViewsSelectable && mAdapter.areAllItemsEnabled();
- } else {
- return true;
- }
- }
-
- private int getAdapterAndPlaceHolderCount() {
- final int adapterCount = (int) (Math.ceil(1f * mAdapter.getCount() / mNumColumns) * mNumColumns);
- return adapterCount;
- }
-
- @Override
- public boolean isEnabled(int position) {
- // Header (negative positions will throw an IndexOutOfBoundsException)
- int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;
- if (position < numHeadersAndPlaceholders) {
- return position % mNumColumns == 0
- && mHeaderViewInfos.get(position / mNumColumns).isSelectable;
- }
-
- // Adapter
- final int adjPosition = position - numHeadersAndPlaceholders;
- int adapterCount = 0;
- if (mAdapter != null) {
- adapterCount = getAdapterAndPlaceHolderCount();
- if (adjPosition < adapterCount) {
- return adjPosition < mAdapter.getCount() && mAdapter.isEnabled(adjPosition);
- }
- }
-
- // Footer (off-limits positions will throw an IndexOutOfBoundsException)
- final int footerPosition = adjPosition - adapterCount;
- return footerPosition % mNumColumns == 0
- && mFooterViewInfos.get(footerPosition / mNumColumns).isSelectable;
- }
-
- @Override
- public Object getItem(int position) {
- // Header (negative positions will throw an ArrayIndexOutOfBoundsException)
- int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;
- if (position < numHeadersAndPlaceholders) {
- if (position % mNumColumns == 0) {
- return mHeaderViewInfos.get(position / mNumColumns).data;
- }
- return null;
- }
-
- // Adapter
- final int adjPosition = position - numHeadersAndPlaceholders;
- int adapterCount = 0;
- if (mAdapter != null) {
- adapterCount = getAdapterAndPlaceHolderCount();
- if (adjPosition < adapterCount) {
- if (adjPosition < mAdapter.getCount()) {
- return mAdapter.getItem(adjPosition);
- } else {
- return null;
- }
- }
- }
-
- // Footer (off-limits positions will throw an IndexOutOfBoundsException)
- final int footerPosition = adjPosition - adapterCount;
- if (footerPosition % mNumColumns == 0) {
- return mFooterViewInfos.get(footerPosition).data;
- } else {
- return null;
- }
- }
-
- @Override
- public long getItemId(int position) {
- int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;
- if (mAdapter != null && position >= numHeadersAndPlaceholders) {
- int adjPosition = position - numHeadersAndPlaceholders;
- int adapterCount = mAdapter.getCount();
- if (adjPosition < adapterCount) {
- return mAdapter.getItemId(adjPosition);
- }
- }
- return -1;
- }
-
- @Override
- public boolean hasStableIds() {
- if (mAdapter != null) {
- return mAdapter.hasStableIds();
- }
- return false;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- if (DEBUG) {
- Log.d(LOG_TAG, String.format("getView: %s, reused: %s", position, convertView == null));
- }
- // Header (negative positions will throw an ArrayIndexOutOfBoundsException)
- int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;
- if (position < numHeadersAndPlaceholders) {
- View headerViewContainer = mHeaderViewInfos
- .get(position / mNumColumns).viewContainer;
- if (position % mNumColumns == 0) {
- return headerViewContainer;
- } else {
- if (convertView == null) {
- convertView = new View(parent.getContext());
- }
- // We need to do this because GridView uses the height of the last item
- // in a row to determine the height for the entire row.
- convertView.setVisibility(View.INVISIBLE);
- convertView.setMinimumHeight(headerViewContainer.getHeight());
- return convertView;
- }
- }
- // Adapter
- final int adjPosition = position - numHeadersAndPlaceholders;
- int adapterCount = 0;
- if (mAdapter != null) {
- adapterCount = getAdapterAndPlaceHolderCount();
- if (adjPosition < adapterCount) {
- if (adjPosition < mAdapter.getCount()) {
- View view = mAdapter.getView(adjPosition, convertView, parent);
- return view;
- } else {
- if (convertView == null) {
- convertView = new View(parent.getContext());
- }
- convertView.setVisibility(View.INVISIBLE);
- convertView.setMinimumHeight(mRowHeight);
- return convertView;
- }
- }
- }
- // Footer
- final int footerPosition = adjPosition - adapterCount;
- if (footerPosition < getCount()) {
- View footViewContainer = mFooterViewInfos
- .get(footerPosition / mNumColumns).viewContainer;
- if (position % mNumColumns == 0) {
- return footViewContainer;
- } else {
- if (convertView == null) {
- convertView = new View(parent.getContext());
- }
- // We need to do this because GridView uses the height of the last item
- // in a row to determine the height for the entire row.
- convertView.setVisibility(View.INVISIBLE);
- convertView.setMinimumHeight(footViewContainer.getHeight());
- return convertView;
- }
- }
- throw new ArrayIndexOutOfBoundsException(position);
- }
-
- @Override
- public int getItemViewType(int position) {
-
- final int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;
- final int adapterViewTypeStart = mAdapter == null ? 0 : mAdapter.getViewTypeCount() - 1;
- int type = AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER;
- if (mCachePlaceHoldView) {
- // Header
- if (position < numHeadersAndPlaceholders) {
- if (position == 0) {
- if (mCacheFirstHeaderView) {
- type = adapterViewTypeStart + mHeaderViewInfos.size() + mFooterViewInfos.size() + 1 + 1;
- }
- }
- if (position % mNumColumns != 0) {
- type = adapterViewTypeStart + (position / mNumColumns + 1);
- }
- }
- }
-
- // Adapter
- final int adjPosition = position - numHeadersAndPlaceholders;
- int adapterCount = 0;
- if (mAdapter != null) {
- adapterCount = getAdapterAndPlaceHolderCount();
- if (adjPosition >= 0 && adjPosition < adapterCount) {
- if (adjPosition < mAdapter.getCount()) {
- type = mAdapter.getItemViewType(adjPosition);
- } else {
- if (mCachePlaceHoldView) {
- type = adapterViewTypeStart + mHeaderViewInfos.size() + 1;
- }
- }
- }
- }
-
- if (mCachePlaceHoldView) {
- // Footer
- final int footerPosition = adjPosition - adapterCount;
- if (footerPosition >= 0 && footerPosition < getCount() && (footerPosition % mNumColumns) != 0) {
- type = adapterViewTypeStart + mHeaderViewInfos.size() + 1 + (footerPosition / mNumColumns + 1);
- }
- }
- if (DEBUG) {
- Log.d(LOG_TAG, String.format("getItemViewType: pos: %s, result: %s", position, type, mCachePlaceHoldView, mCacheFirstHeaderView));
- }
- return type;
- }
-
- /**
- * content view, content view holder, header[0], header and footer placeholder(s)
- *
- * @return
- */
- @Override
- public int getViewTypeCount() {
- int count = mAdapter == null ? 1 : mAdapter.getViewTypeCount();
- if (mCachePlaceHoldView) {
- int offset = mHeaderViewInfos.size() + 1 + mFooterViewInfos.size();
- if (mCacheFirstHeaderView) {
- offset += 1;
- }
- count += offset;
- }
- if (DEBUG) {
- Log.d(LOG_TAG, String.format("getViewTypeCount: %s", count));
- }
- return count;
- }
-
- @Override
- public void registerDataSetObserver(DataSetObserver observer) {
- mDataSetObservable.registerObserver(observer);
- if (mAdapter != null) {
- mAdapter.registerDataSetObserver(observer);
- }
- }
-
- @Override
- public void unregisterDataSetObserver(DataSetObserver observer) {
- mDataSetObservable.unregisterObserver(observer);
- if (mAdapter != null) {
- mAdapter.unregisterDataSetObserver(observer);
- }
- }
-
- @Override
- public Filter getFilter() {
- if (mIsFilterable) {
- return ((Filterable) mAdapter).getFilter();
- }
- return null;
- }
-
- @Override
- public ListAdapter getWrappedAdapter() {
- return mAdapter;
- }
-
- public void notifyDataSetChanged() {
- mDataSetObservable.notifyChanged();
- }
- }
-}
diff --git a/app/src/main/java/github/daneren2005/dsub/view/PlaylistSongView.java b/app/src/main/java/github/daneren2005/dsub/view/PlaylistSongView.java
index 0264a785..581b92e4 100644
--- a/app/src/main/java/github/daneren2005/dsub/view/PlaylistSongView.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/PlaylistSongView.java
@@ -34,16 +34,12 @@ import github.daneren2005.dsub.util.FileUtil;
import github.daneren2005.dsub.util.SyncUtil;
import github.daneren2005.dsub.util.Util;
-public class PlaylistSongView extends UpdateView {
+public class PlaylistSongView extends UpdateView2<Playlist, List<MusicDirectory.Entry>> {
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);
@@ -54,9 +50,7 @@ public class PlaylistSongView extends UpdateView {
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;
+ protected void setObjectImpl(Playlist playlist, List<MusicDirectory.Entry> songs) {
count = 0;
titleView.setText(playlist.getName());
// Make sure to hide initially so it's not present briefly before update
@@ -69,11 +63,11 @@ public class PlaylistSongView extends UpdateView {
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(!"-1".equals(item.getId())) {
+ MusicDirectory cache = FileUtil.deserialize(context, Util.getCacheName(context, "playlist", item.getId()), MusicDirectory.class);
if(cache != null) {
// Try to find song instances in the given playlists
- for(MusicDirectory.Entry song: songs) {
+ for(MusicDirectory.Entry song: item2) {
if(cache.getChildren().contains(song)) {
count++;
}
diff --git a/app/src/main/java/github/daneren2005/dsub/view/PlaylistView.java b/app/src/main/java/github/daneren2005/dsub/view/PlaylistView.java
index 25613984..2cd27b19 100644
--- a/app/src/main/java/github/daneren2005/dsub/view/PlaylistView.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/PlaylistView.java
@@ -21,11 +21,11 @@ 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.ImageLoader;
import github.daneren2005.dsub.util.SyncUtil;
/**
@@ -33,37 +33,31 @@ import github.daneren2005.dsub.util.SyncUtil;
*
* @author Sindre Mehus
*/
-public class PlaylistView extends UpdateView {
+public class PlaylistView extends UpdateView<Playlist> {
private static final String TAG = PlaylistView.class.getSimpleName();
- private Context context;
- private Playlist playlist;
-
+ private View coverArtView;
private TextView titleView;
+ private ImageLoader imageLoader;
- public PlaylistView(Context context) {
+ public PlaylistView(Context context, ImageLoader imageLoader, boolean largeCell) {
super(context);
- this.context = context;
- LayoutInflater.from(context).inflate(R.layout.basic_list_item, this, true);
+ LayoutInflater.from(context).inflate(largeCell ? R.layout.playlist_cell_item : R.layout.playlist_list_item, this, true);
- titleView = (TextView) findViewById(R.id.item_name);
- starButton = (ImageButton) findViewById(R.id.item_star);
- starButton.setFocusable(false);
+ coverArtView = findViewById(R.id.playlist_coverart);
+ titleView = (TextView) findViewById(R.id.playlist_title);
moreButton = (ImageView) findViewById(R.id.item_more);
- moreButton.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- v.showContextMenu();
- }
- });
+
+ this.imageLoader = imageLoader;
}
- protected void setObjectImpl(Object obj) {
- this.playlist = (Playlist) obj;
+ protected void setObjectImpl(Playlist playlist) {
titleView.setText(playlist.getName());
+ imageTask = imageLoader.loadImage(coverArtView, playlist, false, true);
}
@Override
protected void updateBackground() {
- pinned = SyncUtil.isSyncedPlaylist(context, playlist.getId());
+ pinned = SyncUtil.isSyncedPlaylist(context, item.getId());
}
}
diff --git a/app/src/main/java/github/daneren2005/dsub/view/PodcastChannelView.java b/app/src/main/java/github/daneren2005/dsub/view/PodcastChannelView.java
index ada8019e..4878ad67 100644
--- a/app/src/main/java/github/daneren2005/dsub/view/PodcastChannelView.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/PodcastChannelView.java
@@ -30,18 +30,14 @@ import github.daneren2005.dsub.util.SyncUtil;
import github.daneren2005.dsub.util.FileUtil;
import java.io.File;
-public class PodcastChannelView extends UpdateView {
+public class PodcastChannelView extends UpdateView<PodcastChannel> {
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);
@@ -55,8 +51,7 @@ public class PodcastChannelView extends UpdateView {
});
}
- protected void setObjectImpl(Object obj) {
- channel = (PodcastChannel) obj;
+ protected void setObjectImpl(PodcastChannel channel) {
if(channel.getName() != null) {
titleView.setText(channel.getName());
} else {
@@ -67,7 +62,7 @@ public class PodcastChannelView extends UpdateView {
@Override
protected void updateBackground() {
- if(SyncUtil.isSyncedPodcast(context, channel.getId())) {
+ if(SyncUtil.isSyncedPodcast(context, item.getId())) {
if(exists) {
shaded = false;
exists = false;
diff --git a/app/src/main/java/github/daneren2005/dsub/view/SettingView.java b/app/src/main/java/github/daneren2005/dsub/view/SettingView.java
index 1c78706e..d46dc5d2 100644
--- a/app/src/main/java/github/daneren2005/dsub/view/SettingView.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/SettingView.java
@@ -17,34 +17,42 @@ package github.daneren2005.dsub.view;
import android.content.Context;
import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.CheckedTextView;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.TextView;
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 class SettingView extends UpdateView2<Setting, Boolean> {
+ private final TextView titleView;
+ private final CheckBox checkBox;
public SettingView(Context context) {
super(context, false);
this.context = context;
- LayoutInflater.from(context).inflate(android.R.layout.simple_list_item_multiple_choice, this, true);
+ LayoutInflater.from(context).inflate(R.layout.basic_choice_item, this, true);
- view = (CheckedTextView) findViewById(android.R.id.text1);
+ titleView = (TextView) findViewById(R.id.item_name);
+ checkBox = (CheckBox) findViewById(R.id.item_checkbox);
+ checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if(item != null) {
+ item.setValue(isChecked);
+ }
+ }
+ });
+ checkBox.setClickable(false);
}
- protected void setObjectImpl(Object obj, Object editable) {
- this.setting = (Setting) obj;
-
+ protected void setObjectImpl(Setting setting, Boolean isEditable) {
// Can't edit non-role parts
String name = setting.getName();
if(name.indexOf("Role") == -1) {
- editable = false;
+ item2 = false;
}
int res = -1;
@@ -74,29 +82,28 @@ public class SettingView extends UpdateView {
res = R.string.admin_role_lastfm;
} else {
// Last resort to display the raw value
- view.setText(name);
+ titleView.setText(name);
}
if(res != -1) {
- view.setText(res);
+ titleView.setText(res);
}
if(setting.getValue()) {
- view.setChecked(setting.getValue());
+ checkBox.setChecked(setting.getValue());
} else {
- view.setChecked(false);
+ checkBox.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);
- }
+ checkBox.setEnabled(item2);
+ }
+
+ @Override
+ public boolean isCheckable() {
+ return item2;
+ }
+
+ public void setChecked(boolean checked) {
+ checkBox.setChecked(checked);
}
}
diff --git a/app/src/main/java/github/daneren2005/dsub/view/ShareView.java b/app/src/main/java/github/daneren2005/dsub/view/ShareView.java
index bfb5b198..12294369 100644
--- a/app/src/main/java/github/daneren2005/dsub/view/ShareView.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/ShareView.java
@@ -31,7 +31,7 @@ import java.util.Locale;
import github.daneren2005.dsub.R;
import github.daneren2005.dsub.domain.Share;
-public class ShareView extends UpdateView {
+public class ShareView extends UpdateView<Share> {
private static final String TAG = ShareView.class.getSimpleName();
private TextView titleView;
@@ -53,8 +53,7 @@ public class ShareView extends UpdateView {
});
}
- public void setObjectImpl(Object obj) {
- Share share = (Share) obj;
+ public void setObjectImpl(Share share) {
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())));
diff --git a/app/src/main/java/github/daneren2005/dsub/view/SongView.java b/app/src/main/java/github/daneren2005/dsub/view/SongView.java
index 2fbaedc3..b9c5fa50 100644
--- a/app/src/main/java/github/daneren2005/dsub/view/SongView.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/SongView.java
@@ -29,6 +29,7 @@ import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.domain.PodcastEpisode;
import github.daneren2005.dsub.service.DownloadService;
import github.daneren2005.dsub.service.DownloadFile;
+import github.daneren2005.dsub.util.DrawableTint;
import github.daneren2005.dsub.util.Util;
import java.io.File;
@@ -38,20 +39,17 @@ import java.io.File;
*
* @author Sindre Mehus
*/
-public class SongView extends UpdateView implements Checkable {
- private static final String TAG = SongView.class.getSimpleName();
+public class SongView extends UpdateView2<MusicDirectory.Entry, Boolean> {
+ private static final String TAG = SongView.class.getSimpleName();
- private MusicDirectory.Entry song;
-
- private CheckedTextView checkedTextView;
- private TextView titleTextView;
- private TextView artistTextView;
- private TextView durationTextView;
- private TextView statusTextView;
+ private TextView titleTextView;
+ private TextView artistTextView;
+ private TextView durationTextView;
+ private TextView statusTextView;
private ImageView statusImageView;
private ImageView bookmarkButton;
private View bottomRowView;
-
+
private DownloadService downloadService;
private long revision = -1;
private DownloadFile downloadFile;
@@ -68,35 +66,28 @@ public class SongView extends UpdateView implements Checkable {
private boolean isBookmarked = false;
private boolean bookmarked = false;
- public SongView(Context context) {
- super(context);
- LayoutInflater.from(context).inflate(R.layout.song_list_item, this, true);
+ public SongView(Context context) {
+ super(context);
+ LayoutInflater.from(context).inflate(R.layout.song_list_item, this, true);
- checkedTextView = (CheckedTextView) findViewById(R.id.song_check);
- titleTextView = (TextView) findViewById(R.id.song_title);
- artistTextView = (TextView) findViewById(R.id.song_artist);
- durationTextView = (TextView) findViewById(R.id.song_duration);
- statusTextView = (TextView) findViewById(R.id.song_status);
+ titleTextView = (TextView) findViewById(R.id.song_title);
+ artistTextView = (TextView) findViewById(R.id.song_artist);
+ durationTextView = (TextView) findViewById(R.id.song_duration);
+ statusTextView = (TextView) findViewById(R.id.song_status);
statusImageView = (ImageView) findViewById(R.id.song_status_icon);
ratingBar = (RatingBar) findViewById(R.id.song_rating);
- starButton = (ImageButton) findViewById(R.id.song_star);
- starButton.setFocusable(false);
+ starButton = (ImageButton) findViewById(R.id.song_star);
+ starButton.setFocusable(false);
bookmarkButton = (ImageButton) findViewById(R.id.song_bookmark);
bookmarkButton.setFocusable(false);
- moreButton = (ImageView) findViewById(R.id.artist_more);
- moreButton.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- v.showContextMenu();
- }
- });
+ moreButton = (ImageView) findViewById(R.id.more_button);
bottomRowView = findViewById(R.id.song_bottom);
- }
+ }
+
+ public void setObjectImpl(MusicDirectory.Entry song, Boolean checkable) {
+ this.checkable = checkable;
- public void setObjectImpl(Object obj1, Object obj2) {
- this.song = (MusicDirectory.Entry) obj1;
- boolean checkable = (Boolean) obj2;
-
- StringBuilder artist = new StringBuilder(40);
+ StringBuilder artist = new StringBuilder(40);
boolean isPodcast = song instanceof PodcastEpisode;
if(!song.isVideo() || isPodcast) {
@@ -110,11 +101,11 @@ public class SongView extends UpdateView implements Checkable {
else if(song.getArtist() != null) {
artist.append(song.getArtist());
}
-
+
if(isPodcast) {
String status = ((PodcastEpisode) song).getStatus();
int statusRes = -1;
-
+
if("error".equals(status)) {
statusRes = R.string.song_details_error;
} else if("skipped".equals(status)) {
@@ -122,7 +113,7 @@ public class SongView extends UpdateView implements Checkable {
} else if("downloading".equals(status)) {
statusRes = R.string.song_details_downloading;
}
-
+
if(statusRes != -1) {
artist.append(" (");
artist.append(getContext().getString(statusRes));
@@ -136,16 +127,15 @@ public class SongView extends UpdateView implements Checkable {
bottomRowView.setVisibility(View.GONE);
statusTextView.setText(Util.formatDuration(song.getDuration()));
}
-
+
String title = song.getTitle();
Integer track = song.getTrack();
if(track != null && Util.getDisplayTrack(context)) {
title = String.format("%02d", track) + " " + title;
}
- titleTextView.setText(title);
+ titleTextView.setText(title);
artistTextView.setText(artist);
- checkedTextView.setVisibility(checkable && !song.isVideo() ? View.VISIBLE : View.GONE);
this.setBackgroundColor(0x00000000);
ratingBar.setVisibility(View.GONE);
@@ -155,28 +145,28 @@ public class SongView extends UpdateView implements Checkable {
loaded = false;
dontChangeDownloadFile = false;
}
-
+
public void setDownloadFile(DownloadFile downloadFile) {
this.downloadFile = downloadFile;
dontChangeDownloadFile = true;
}
-
+
public DownloadFile getDownloadFile() {
return downloadFile;
}
-
+
@Override
protected void updateBackground() {
- if (downloadService == null) {
+ if (downloadService == null) {
downloadService = DownloadService.getInstance();
if(downloadService == null) {
return;
}
- }
+ }
long newRevision = downloadService.getDownloadListUpdateRevision();
if((revision != newRevision && dontChangeDownloadFile == false) || downloadFile == null) {
- downloadFile = downloadService.forSong(song);
+ downloadFile = downloadService.forSong(item);
revision = newRevision;
}
@@ -184,28 +174,31 @@ public class SongView extends UpdateView implements Checkable {
isSaved = downloadFile.isSaved();
partialFile = downloadFile.getPartialFile();
partialFileExists = partialFile.exists();
- isStarred = song.isStarred();
- isBookmarked = song.getBookmark() != null;
- isRated = song.getRating();
-
+ isStarred = item.isStarred();
+ isBookmarked = item.getBookmark() != null;
+ isRated = item.getRating();
+
// Check if needs to load metadata: check against all fields that we know are null in offline mode
- if(song.getBitRate() == null && song.getDuration() == null && song.getDiscNumber() == null && isWorkDone) {
- song.loadMetadata(downloadFile.getCompleteFile());
+ if(item.getBitRate() == null && item.getDuration() == null && item.getDiscNumber() == null && isWorkDone) {
+ item.loadMetadata(downloadFile.getCompleteFile());
loaded = true;
}
}
@Override
- protected void update() {
+ protected void update() {
if(loaded) {
- setObjectImpl(song, checkedTextView.getVisibility() == View.VISIBLE);
+ setObjectImpl(item, item2);
+ }
+ if (downloadService == null || downloadFile == null) {
+ return;
}
- if (downloadService == null || downloadFile == null) {
- return;
- }
- if(song.isStarred()) {
+ if(item.isStarred()) {
if(!starred) {
+ if(starButton.getDrawable() == null) {
+ starButton.setImageDrawable(DrawableTint.getTintedDrawable(context, R.drawable.ic_toggle_star));
+ }
starButton.setVisibility(View.VISIBLE);
starred = true;
}
@@ -216,13 +209,13 @@ public class SongView extends UpdateView implements Checkable {
}
}
- if (isWorkDone) {
+ if (isWorkDone) {
int moreImage = isSaved ? R.drawable.download_pinned : R.drawable.download_cached;
if(moreImage != this.moreImage) {
moreButton.setImageResource(moreImage);
this.moreImage = moreImage;
}
- } else if(this.moreImage != R.drawable.download_none_light) {
+ } else if(this.moreImage != R.drawable.download_none_light) {
int[] attrs = new int[] {R.attr.download_none};
TypedArray typedArray = context.obtainStyledAttributes(attrs);
moreButton.setImageResource(typedArray.getResourceId(0, 0));
@@ -230,7 +223,7 @@ public class SongView extends UpdateView implements Checkable {
this.moreImage = R.drawable.download_none_light;
}
- if (downloadFile.isDownloading() && !downloadFile.isDownloadCancelled() && partialFileExists) {
+ if (downloadFile.isDownloading() && !downloadFile.isDownloadCancelled() && partialFileExists) {
double percentage = (partialFile.length() * 100.0) / downloadFile.getEstimatedSize();
percentage = Math.min(percentage, 100);
statusTextView.setText((int)percentage + " %");
@@ -238,29 +231,33 @@ public class SongView extends UpdateView implements Checkable {
statusImageView.setVisibility(View.VISIBLE);
rightImage = true;
}
- } else if(rightImage) {
- statusTextView.setText(null);
+ } else if(rightImage) {
+ statusTextView.setText(null);
statusImageView.setVisibility(View.GONE);
rightImage = false;
- }
+ }
- boolean playing = downloadService.getCurrentPlaying() == downloadFile;
- if (playing) {
+ boolean playing = downloadService.getCurrentPlaying() == downloadFile;
+ if (playing) {
if(!this.playing) {
this.playing = playing;
int[] attrs = new int[] {R.attr.media_button_start};
TypedArray typedArray = context.obtainStyledAttributes(attrs);
- titleTextView.setCompoundDrawablesWithIntrinsicBounds(typedArray.getResourceId(0, 0), 0, 0, 0);
+ titleTextView.setCompoundDrawablesWithIntrinsicBounds(typedArray.getResourceId(0, 0), 0, 0, 0);
}
- } else {
+ } else {
if(this.playing) {
this.playing = playing;
- titleTextView.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
+ titleTextView.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
}
}
if(isBookmarked) {
if(!bookmarked) {
+ if(bookmarkButton.getDrawable() == null) {
+ bookmarkButton.setImageDrawable(DrawableTint.getTintedDrawable(context, R.drawable.ic_menu_bookmark_selected));
+ }
+
bookmarkButton.setVisibility(View.VISIBLE);
bookmarked = true;
}
@@ -295,24 +292,9 @@ public class SongView extends UpdateView implements Checkable {
rating = isRated;
}
- }
-
- @Override
- public void setChecked(boolean b) {
- checkedTextView.setChecked(b);
- }
-
- @Override
- public boolean isChecked() {
- return checkedTextView.isChecked();
- }
-
- @Override
- public void toggle() {
- checkedTextView.toggle();
- }
+ }
public MusicDirectory.Entry getEntry() {
- return song;
+ return item;
}
}
diff --git a/app/src/main/java/github/daneren2005/dsub/view/UnscrollableGridView.java b/app/src/main/java/github/daneren2005/dsub/view/UnscrollableGridView.java
deleted file mode 100644
index 3047d5d7..00000000
--- a/app/src/main/java/github/daneren2005/dsub/view/UnscrollableGridView.java
+++ /dev/null
@@ -1,128 +0,0 @@
-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/app/src/main/java/github/daneren2005/dsub/view/UpdateView.java b/app/src/main/java/github/daneren2005/dsub/view/UpdateView.java
index f9c62121..fd74c872 100644
--- a/app/src/main/java/github/daneren2005/dsub/view/UpdateView.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/UpdateView.java
@@ -20,9 +20,10 @@ package github.daneren2005.dsub.view;
import android.content.Context;
import android.content.res.TypedArray;
-import android.graphics.Color;
+import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Looper;
+import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -37,20 +38,21 @@ 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.DrawableTint;
import github.daneren2005.dsub.util.SilentBackgroundTask;
-public class UpdateView extends LinearLayout {
+public abstract class UpdateView<T> 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;
+ protected static Handler backgroundHandler;
+ protected static Handler uiHandler;
private static Runnable updateRunnable;
private static int activeActivities = 0;
protected Context context;
+ protected T item;
protected RatingBar ratingBar;
protected ImageButton starButton;
protected ImageView moreButton;
@@ -63,8 +65,10 @@ public class UpdateView extends LinearLayout {
protected int isRated = 0;
protected int rating = 0;
protected SilentBackgroundTask<Void> imageTask = null;
+ protected Drawable startBackgroundDrawable;
protected final boolean autoUpdate;
+ protected boolean checkable;
public UpdateView(Context context) {
this(context, true);
@@ -75,8 +79,8 @@ public class UpdateView extends LinearLayout {
this.autoUpdate = autoUpdate;
setLayoutParams(new AbsListView.LayoutParams(
- ViewGroup.LayoutParams.FILL_PARENT,
- ViewGroup.LayoutParams.WRAP_CONTENT));
+ ViewGroup.LayoutParams.FILL_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT));
if(autoUpdate) {
INSTANCES.put(this, null);
@@ -89,37 +93,16 @@ public class UpdateView extends LinearLayout {
}
- public void setObject(Object obj) {
+ public void setObject(T obj) {
+ item = 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) {
-
+ public void setObject(T obj1, Object obj2) {
+ setObject(obj1, null);
}
+ protected abstract void setObjectImpl(T obj);
private static synchronized void startUpdater() {
if(uiHandler != null) {
@@ -224,8 +207,6 @@ public class UpdateView extends LinearLayout {
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();
}
@@ -261,6 +242,9 @@ public class UpdateView extends LinearLayout {
if(starButton != null) {
if(isStarred) {
if(!starred) {
+ if(starButton.getDrawable() == null) {
+ starButton.setImageDrawable(DrawableTint.getTintedDrawable(context, R.drawable.ic_toggle_star));
+ }
starButton.setVisibility(View.VISIBLE);
starred = true;
}
@@ -283,4 +267,55 @@ public class UpdateView extends LinearLayout {
rating = isRated;
}
}
+
+ public boolean isCheckable() {
+ return checkable;
+ }
+ public void setChecked(boolean checked) {
+ View child = getChildAt(0);
+ if (checked && startBackgroundDrawable == null) {
+ startBackgroundDrawable = child.getBackground();
+ child.setBackgroundColor(DrawableTint.getColorRes(context, R.attr.colorPrimary));
+ } else if (!checked && startBackgroundDrawable != null) {
+ child.setBackgroundDrawable(startBackgroundDrawable);
+ startBackgroundDrawable = null;
+ }
+ }
+
+ public void onClick() {
+
+ }
+
+ public static class UpdateViewHolder<T> extends RecyclerView.ViewHolder {
+ private UpdateView updateView;
+ private View view;
+ private T item;
+
+ public UpdateViewHolder(UpdateView itemView) {
+ super(itemView);
+
+ this.updateView = itemView;
+ this.view = itemView;
+ }
+
+ // Different is so that call is not ambiguous
+ public UpdateViewHolder(View view, boolean different) {
+ super(view);
+ this.view = view;
+ }
+
+ public UpdateView<T> getUpdateView() {
+ return updateView;
+ }
+ public View getView() {
+ return view;
+ }
+ public void setItem(T item) {
+ this.item = item;
+ }
+ public T getItem() {
+ return item;
+ }
+ }
}
+
diff --git a/app/src/main/java/github/daneren2005/dsub/view/UpdateView2.java b/app/src/main/java/github/daneren2005/dsub/view/UpdateView2.java
new file mode 100644
index 00000000..0f0b5455
--- /dev/null
+++ b/app/src/main/java/github/daneren2005/dsub/view/UpdateView2.java
@@ -0,0 +1,47 @@
+package github.daneren2005.dsub.view;
+
+import android.content.Context;
+
+public abstract class UpdateView2<T1, T2> extends UpdateView<T1> {
+ protected T2 item2;
+
+ public UpdateView2(Context context) {
+ super(context);
+ }
+
+ public UpdateView2(Context context, boolean autoUpdate) {
+ super(context, autoUpdate);
+ }
+
+ public final void setObject(T1 obj1) {
+ setObject(obj1, null);
+ }
+ @Override
+ public void setObject(T1 obj1, Object obj2) {
+ item = obj1;
+ item2 = (T2) obj2;
+ if(imageTask != null) {
+ imageTask.cancel();
+ imageTask = null;
+ }
+
+ setObjectImpl(item, item2);
+ backgroundHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ updateBackground();
+ uiHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ update();
+ }
+ });
+ }
+ });
+ }
+
+ protected final void setObjectImpl(T1 obj1) {
+ setObjectImpl(obj1, null);
+ }
+ protected abstract void setObjectImpl(T1 obj1, T2 obj2);
+}
diff --git a/app/src/main/java/github/daneren2005/dsub/view/UserView.java b/app/src/main/java/github/daneren2005/dsub/view/UserView.java
index dec8dbef..a97d755b 100644
--- a/app/src/main/java/github/daneren2005/dsub/view/UserView.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/UserView.java
@@ -25,15 +25,12 @@ 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;
-
+public class UserView extends UpdateView2<User, ImageLoader> {
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);
@@ -46,9 +43,8 @@ public class UserView extends UpdateView {
});
}
- protected void setObjectImpl(Object obj, Object obj2) {
- this.user = (User) obj;
+ protected void setObjectImpl(User user, ImageLoader imageLoader) {
usernameView.setText(user.getUsername());
- imageTask = ((ImageLoader)obj2).loadAvatar(context, avatarView, user.getUsername());
+ imageTask = imageLoader.loadAvatar(context, avatarView, user.getUsername());
}
}
diff --git a/app/src/main/res/drawable-hdpi-v11/notification_close.png b/app/src/main/res/drawable-hdpi-v11/notification_close.png
deleted file mode 100644
index 254e130f..00000000
--- a/app/src/main/res/drawable-hdpi-v11/notification_close.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/res/drawable-hdpi-v11/notification_next.png b/app/src/main/res/drawable-hdpi-v11/notification_next.png
deleted file mode 100644
index 59239305..00000000
--- a/app/src/main/res/drawable-hdpi-v11/notification_next.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/res/drawable-hdpi-v11/notification_pause.png b/app/src/main/res/drawable-hdpi-v11/notification_pause.png
deleted file mode 100644
index cbd61795..00000000
--- a/app/src/main/res/drawable-hdpi-v11/notification_pause.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/res/drawable-hdpi-v11/notification_play.png b/app/src/main/res/drawable-hdpi-v11/notification_play.png
deleted file mode 100644
index 78b4d5bf..00000000
--- a/app/src/main/res/drawable-hdpi-v11/notification_play.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/res/drawable-hdpi-v11/notification_previous.png b/app/src/main/res/drawable-hdpi-v11/notification_previous.png
deleted file mode 100644
index 556eaec3..00000000
--- a/app/src/main/res/drawable-hdpi-v11/notification_previous.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/res/drawable-hdpi-v11/stat_notify_download.png b/app/src/main/res/drawable-hdpi-v11/stat_notify_download.png
deleted file mode 100644
index 48ca6924..00000000
--- a/app/src/main/res/drawable-hdpi-v11/stat_notify_download.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/res/drawable-hdpi-v11/stat_notify_playing.png b/app/src/main/res/drawable-hdpi-v11/stat_notify_playing.png
deleted file mode 100644
index 78b4d5bf..00000000
--- a/app/src/main/res/drawable-hdpi-v11/stat_notify_playing.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/res/drawable-hdpi-v11/stat_notify_sync.png b/app/src/main/res/drawable-hdpi-v11/stat_notify_sync.png
deleted file mode 100644
index f1ff1eb2..00000000
--- a/app/src/main/res/drawable-hdpi-v11/stat_notify_sync.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/res/drawable-hdpi/ic_action_rating_bad_dark.png b/app/src/main/res/drawable-hdpi/ic_action_rating_bad_dark.png
index 855709e9..b21ef12a 100644
--- a/app/src/main/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/app/src/main/res/drawable-hdpi/ic_action_rating_bad_light.png b/app/src/main/res/drawable-hdpi/ic_action_rating_bad_light.png
index 34199d3a..ab8eb54c 100644
--- a/app/src/main/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/app/src/main/res/drawable-hdpi/ic_action_rating_bad_selected.png b/app/src/main/res/drawable-hdpi/ic_action_rating_bad_selected.png
index c57aba50..911eea25 100644
--- a/app/src/main/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/app/src/main/res/drawable-hdpi/ic_action_rating_good_dark.png b/app/src/main/res/drawable-hdpi/ic_action_rating_good_dark.png
index fa91e699..f79fcbcd 100644
--- a/app/src/main/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/app/src/main/res/drawable-hdpi/ic_action_rating_good_light.png b/app/src/main/res/drawable-hdpi/ic_action_rating_good_light.png
index 3427d770..67b30364 100644
--- a/app/src/main/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/app/src/main/res/drawable-hdpi/ic_action_rating_good_selected.png b/app/src/main/res/drawable-hdpi/ic_action_rating_good_selected.png
index 34d53153..09417791 100644
--- a/app/src/main/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/app/src/main/res/drawable-hdpi/ic_menu_bookmark_dark.png b/app/src/main/res/drawable-hdpi/ic_menu_bookmark_dark.png
index e7cd08e4..e149a6e5 100644
--- a/app/src/main/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/app/src/main/res/drawable-hdpi/ic_menu_bookmark_light.png b/app/src/main/res/drawable-hdpi/ic_menu_bookmark_light.png
index fdb46da3..c9ece7c0 100644
--- a/app/src/main/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/app/src/main/res/drawable-hdpi/ic_menu_bookmark_selected.png b/app/src/main/res/drawable-hdpi/ic_menu_bookmark_selected.png
index 5a33d60c..27696f5b 100644
--- a/app/src/main/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/app/src/main/res/drawable-hdpi/ic_stat_star.png b/app/src/main/res/drawable-hdpi/ic_stat_star.png
deleted file mode 100644
index 67ad40f5..00000000
--- a/app/src/main/res/drawable-hdpi/ic_stat_star.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/res/drawable-hdpi/ic_toggle_star.png b/app/src/main/res/drawable-hdpi/ic_toggle_star.png
new file mode 100644
index 00000000..f9ef8772
--- /dev/null
+++ b/app/src/main/res/drawable-hdpi/ic_toggle_star.png
Binary files differ
diff --git a/app/src/main/res/drawable-hdpi/ic_toggle_star_outline_dark.png b/app/src/main/res/drawable-hdpi/ic_toggle_star_outline_dark.png
new file mode 100644
index 00000000..87a0d5c5
--- /dev/null
+++ b/app/src/main/res/drawable-hdpi/ic_toggle_star_outline_dark.png
Binary files differ
diff --git a/app/src/main/res/drawable-hdpi/ic_toggle_star_outline_light.png b/app/src/main/res/drawable-hdpi/ic_toggle_star_outline_light.png
new file mode 100644
index 00000000..b679803f
--- /dev/null
+++ b/app/src/main/res/drawable-hdpi/ic_toggle_star_outline_light.png
Binary files differ
diff --git a/app/src/main/res/drawable-hdpi/launch.png b/app/src/main/res/drawable-hdpi/launch.png
index 0c77b9b4..36f7b683 100644
--- a/app/src/main/res/drawable-hdpi/launch.png
+++ b/app/src/main/res/drawable-hdpi/launch.png
Binary files differ
diff --git a/app/src/main/res/drawable-hdpi/notification_close.png b/app/src/main/res/drawable-hdpi/notification_close.png
index 916c9a0f..254e130f 100644
--- a/app/src/main/res/drawable-hdpi/notification_close.png
+++ b/app/src/main/res/drawable-hdpi/notification_close.png
Binary files differ
diff --git a/app/src/main/res/drawable-hdpi/notification_next.png b/app/src/main/res/drawable-hdpi/notification_next.png
index 078c310f..59239305 100644
--- a/app/src/main/res/drawable-hdpi/notification_next.png
+++ b/app/src/main/res/drawable-hdpi/notification_next.png
Binary files differ
diff --git a/app/src/main/res/drawable-hdpi/notification_pause.png b/app/src/main/res/drawable-hdpi/notification_pause.png
index 16627e44..cbd61795 100644
--- a/app/src/main/res/drawable-hdpi/notification_pause.png
+++ b/app/src/main/res/drawable-hdpi/notification_pause.png
Binary files differ
diff --git a/app/src/main/res/drawable-hdpi/notification_play.png b/app/src/main/res/drawable-hdpi/notification_play.png
index 02f38944..78b4d5bf 100644
--- a/app/src/main/res/drawable-hdpi/notification_play.png
+++ b/app/src/main/res/drawable-hdpi/notification_play.png
Binary files differ
diff --git a/app/src/main/res/drawable-hdpi/notification_previous.png b/app/src/main/res/drawable-hdpi/notification_previous.png
index 9d10abd9..556eaec3 100644
--- a/app/src/main/res/drawable-hdpi/notification_previous.png
+++ b/app/src/main/res/drawable-hdpi/notification_previous.png
Binary files differ
diff --git a/app/src/main/res/drawable-hdpi/stat_notify_download.png b/app/src/main/res/drawable-hdpi/stat_notify_download.png
index aa1b6c92..48ca6924 100644
--- a/app/src/main/res/drawable-hdpi/stat_notify_download.png
+++ b/app/src/main/res/drawable-hdpi/stat_notify_download.png
Binary files differ
diff --git a/app/src/main/res/drawable-hdpi/stat_notify_playing.png b/app/src/main/res/drawable-hdpi/stat_notify_playing.png
index 02f38944..78b4d5bf 100644
--- a/app/src/main/res/drawable-hdpi/stat_notify_playing.png
+++ b/app/src/main/res/drawable-hdpi/stat_notify_playing.png
Binary files differ
diff --git a/app/src/main/res/drawable-hdpi/stat_notify_sync.png b/app/src/main/res/drawable-hdpi/stat_notify_sync.png
index 7dbf0e95..f1ff1eb2 100644
--- a/app/src/main/res/drawable-hdpi/stat_notify_sync.png
+++ b/app/src/main/res/drawable-hdpi/stat_notify_sync.png
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi-v11/notification_close.png b/app/src/main/res/drawable-mdpi-v11/notification_close.png
deleted file mode 100644
index a056fe61..00000000
--- a/app/src/main/res/drawable-mdpi-v11/notification_close.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi-v11/notification_next.png b/app/src/main/res/drawable-mdpi-v11/notification_next.png
deleted file mode 100644
index 7297577f..00000000
--- a/app/src/main/res/drawable-mdpi-v11/notification_next.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi-v11/notification_pause.png b/app/src/main/res/drawable-mdpi-v11/notification_pause.png
deleted file mode 100644
index 5d3ca3f2..00000000
--- a/app/src/main/res/drawable-mdpi-v11/notification_pause.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi-v11/notification_play.png b/app/src/main/res/drawable-mdpi-v11/notification_play.png
deleted file mode 100644
index 999ce798..00000000
--- a/app/src/main/res/drawable-mdpi-v11/notification_play.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi-v11/notification_previous.png b/app/src/main/res/drawable-mdpi-v11/notification_previous.png
deleted file mode 100644
index 55a1f326..00000000
--- a/app/src/main/res/drawable-mdpi-v11/notification_previous.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi-v11/stat_notify_download.png b/app/src/main/res/drawable-mdpi-v11/stat_notify_download.png
deleted file mode 100644
index 4164e0fa..00000000
--- a/app/src/main/res/drawable-mdpi-v11/stat_notify_download.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi-v11/stat_notify_playing.png b/app/src/main/res/drawable-mdpi-v11/stat_notify_playing.png
deleted file mode 100644
index 999ce798..00000000
--- a/app/src/main/res/drawable-mdpi-v11/stat_notify_playing.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi-v11/stat_notify_sync.png b/app/src/main/res/drawable-mdpi-v11/stat_notify_sync.png
deleted file mode 100644
index 3e3c64c0..00000000
--- a/app/src/main/res/drawable-mdpi-v11/stat_notify_sync.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi/ic_action_rating_bad_dark.png b/app/src/main/res/drawable-mdpi/ic_action_rating_bad_dark.png
index 64f3cd1f..00925d7e 100644
--- a/app/src/main/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/app/src/main/res/drawable-mdpi/ic_action_rating_bad_light.png b/app/src/main/res/drawable-mdpi/ic_action_rating_bad_light.png
index d6c8d42a..eb4cc960 100644
--- a/app/src/main/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/app/src/main/res/drawable-mdpi/ic_action_rating_bad_selected.png b/app/src/main/res/drawable-mdpi/ic_action_rating_bad_selected.png
index 34f5a9de..ac563748 100644
--- a/app/src/main/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/app/src/main/res/drawable-mdpi/ic_action_rating_good_dark.png b/app/src/main/res/drawable-mdpi/ic_action_rating_good_dark.png
index cadfbe1e..4978b140 100644
--- a/app/src/main/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/app/src/main/res/drawable-mdpi/ic_action_rating_good_light.png b/app/src/main/res/drawable-mdpi/ic_action_rating_good_light.png
index 75711920..07382a0a 100644
--- a/app/src/main/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/app/src/main/res/drawable-mdpi/ic_action_rating_good_selected.png b/app/src/main/res/drawable-mdpi/ic_action_rating_good_selected.png
index 97d279be..197aa106 100644
--- a/app/src/main/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/app/src/main/res/drawable-mdpi/ic_menu_bookmark_dark.png b/app/src/main/res/drawable-mdpi/ic_menu_bookmark_dark.png
index 3360f37e..281479af 100644
--- a/app/src/main/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/app/src/main/res/drawable-mdpi/ic_menu_bookmark_light.png b/app/src/main/res/drawable-mdpi/ic_menu_bookmark_light.png
index b4d916fb..76bfb63c 100644
--- a/app/src/main/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/app/src/main/res/drawable-mdpi/ic_menu_bookmark_selected.png b/app/src/main/res/drawable-mdpi/ic_menu_bookmark_selected.png
index efcc1afa..9469278e 100644
--- a/app/src/main/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/app/src/main/res/drawable-mdpi/ic_toggle_star.png b/app/src/main/res/drawable-mdpi/ic_toggle_star.png
new file mode 100644
index 00000000..6d949fd0
--- /dev/null
+++ b/app/src/main/res/drawable-mdpi/ic_toggle_star.png
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi/ic_toggle_star_outline_dark.png b/app/src/main/res/drawable-mdpi/ic_toggle_star_outline_dark.png
new file mode 100644
index 00000000..72231a73
--- /dev/null
+++ b/app/src/main/res/drawable-mdpi/ic_toggle_star_outline_dark.png
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi/ic_toggle_star_outline_light.png b/app/src/main/res/drawable-mdpi/ic_toggle_star_outline_light.png
new file mode 100644
index 00000000..85a184bc
--- /dev/null
+++ b/app/src/main/res/drawable-mdpi/ic_toggle_star_outline_light.png
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi/launch.png b/app/src/main/res/drawable-mdpi/launch.png
index 88887e94..9117c4c2 100644
--- a/app/src/main/res/drawable-mdpi/launch.png
+++ b/app/src/main/res/drawable-mdpi/launch.png
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi/notification_close.png b/app/src/main/res/drawable-mdpi/notification_close.png
index 2a8f9a36..a056fe61 100644
--- a/app/src/main/res/drawable-mdpi/notification_close.png
+++ b/app/src/main/res/drawable-mdpi/notification_close.png
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi/notification_next.png b/app/src/main/res/drawable-mdpi/notification_next.png
index f85d45a5..7297577f 100644
--- a/app/src/main/res/drawable-mdpi/notification_next.png
+++ b/app/src/main/res/drawable-mdpi/notification_next.png
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi/notification_pause.png b/app/src/main/res/drawable-mdpi/notification_pause.png
index 06c3cf9d..5d3ca3f2 100644
--- a/app/src/main/res/drawable-mdpi/notification_pause.png
+++ b/app/src/main/res/drawable-mdpi/notification_pause.png
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi/notification_play.png b/app/src/main/res/drawable-mdpi/notification_play.png
index 0248c1cc..999ce798 100644
--- a/app/src/main/res/drawable-mdpi/notification_play.png
+++ b/app/src/main/res/drawable-mdpi/notification_play.png
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi/notification_previous.png b/app/src/main/res/drawable-mdpi/notification_previous.png
index 167d7d05..55a1f326 100644
--- a/app/src/main/res/drawable-mdpi/notification_previous.png
+++ b/app/src/main/res/drawable-mdpi/notification_previous.png
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi/stat_notify_download.png b/app/src/main/res/drawable-mdpi/stat_notify_download.png
index 4c2a22de..4164e0fa 100644
--- a/app/src/main/res/drawable-mdpi/stat_notify_download.png
+++ b/app/src/main/res/drawable-mdpi/stat_notify_download.png
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi/stat_notify_playing.png b/app/src/main/res/drawable-mdpi/stat_notify_playing.png
index 0248c1cc..999ce798 100644
--- a/app/src/main/res/drawable-mdpi/stat_notify_playing.png
+++ b/app/src/main/res/drawable-mdpi/stat_notify_playing.png
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi/stat_notify_sync.png b/app/src/main/res/drawable-mdpi/stat_notify_sync.png
index 35a06857..3e3c64c0 100644
--- a/app/src/main/res/drawable-mdpi/stat_notify_sync.png
+++ b/app/src/main/res/drawable-mdpi/stat_notify_sync.png
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi-v11/notification_close.png b/app/src/main/res/drawable-xhdpi-v11/notification_close.png
deleted file mode 100644
index f1013578..00000000
--- a/app/src/main/res/drawable-xhdpi-v11/notification_close.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi-v11/notification_next.png b/app/src/main/res/drawable-xhdpi-v11/notification_next.png
deleted file mode 100644
index ad070680..00000000
--- a/app/src/main/res/drawable-xhdpi-v11/notification_next.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi-v11/notification_pause.png b/app/src/main/res/drawable-xhdpi-v11/notification_pause.png
deleted file mode 100644
index 709602aa..00000000
--- a/app/src/main/res/drawable-xhdpi-v11/notification_pause.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi-v11/notification_play.png b/app/src/main/res/drawable-xhdpi-v11/notification_play.png
deleted file mode 100644
index e2bafa6a..00000000
--- a/app/src/main/res/drawable-xhdpi-v11/notification_play.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi-v11/notification_previous.png b/app/src/main/res/drawable-xhdpi-v11/notification_previous.png
deleted file mode 100644
index d22488cb..00000000
--- a/app/src/main/res/drawable-xhdpi-v11/notification_previous.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi-v11/stat_notify_download.png b/app/src/main/res/drawable-xhdpi-v11/stat_notify_download.png
deleted file mode 100644
index 96ceb383..00000000
--- a/app/src/main/res/drawable-xhdpi-v11/stat_notify_download.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi-v11/stat_notify_playing.png b/app/src/main/res/drawable-xhdpi-v11/stat_notify_playing.png
deleted file mode 100644
index e2bafa6a..00000000
--- a/app/src/main/res/drawable-xhdpi-v11/stat_notify_playing.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi-v11/stat_notify_sync.png b/app/src/main/res/drawable-xhdpi-v11/stat_notify_sync.png
deleted file mode 100644
index b723bf54..00000000
--- a/app/src/main/res/drawable-xhdpi-v11/stat_notify_sync.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_action_rating_bad_dark.png b/app/src/main/res/drawable-xhdpi/ic_action_rating_bad_dark.png
index 1393be0c..8552fb61 100644
--- a/app/src/main/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/app/src/main/res/drawable-xhdpi/ic_action_rating_bad_light.png b/app/src/main/res/drawable-xhdpi/ic_action_rating_bad_light.png
index fc1959b6..3cc99d4e 100644
--- a/app/src/main/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/app/src/main/res/drawable-xhdpi/ic_action_rating_bad_selected.png b/app/src/main/res/drawable-xhdpi/ic_action_rating_bad_selected.png
index cf7802d5..98494b1f 100644
--- a/app/src/main/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/app/src/main/res/drawable-xhdpi/ic_action_rating_good_dark.png b/app/src/main/res/drawable-xhdpi/ic_action_rating_good_dark.png
index 249ea9ec..abb63162 100644
--- a/app/src/main/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/app/src/main/res/drawable-xhdpi/ic_action_rating_good_light.png b/app/src/main/res/drawable-xhdpi/ic_action_rating_good_light.png
index c8a776b0..cab7d3bc 100644
--- a/app/src/main/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/app/src/main/res/drawable-xhdpi/ic_action_rating_good_selected.png b/app/src/main/res/drawable-xhdpi/ic_action_rating_good_selected.png
index c6770221..f5065d18 100644
--- a/app/src/main/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/app/src/main/res/drawable-xhdpi/ic_menu_bookmark_dark.png b/app/src/main/res/drawable-xhdpi/ic_menu_bookmark_dark.png
index 18f71365..7a396f68 100644
--- a/app/src/main/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/app/src/main/res/drawable-xhdpi/ic_menu_bookmark_light.png b/app/src/main/res/drawable-xhdpi/ic_menu_bookmark_light.png
index d5776317..c91134e7 100644
--- a/app/src/main/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/app/src/main/res/drawable-xhdpi/ic_menu_bookmark_selected.png b/app/src/main/res/drawable-xhdpi/ic_menu_bookmark_selected.png
index 353b7b79..1a1df81b 100644
--- a/app/src/main/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/app/src/main/res/drawable-xhdpi/ic_toggle_star.png b/app/src/main/res/drawable-xhdpi/ic_toggle_star.png
new file mode 100644
index 00000000..5c54298c
--- /dev/null
+++ b/app/src/main/res/drawable-xhdpi/ic_toggle_star.png
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_toggle_star_outline_dark.png b/app/src/main/res/drawable-xhdpi/ic_toggle_star_outline_dark.png
new file mode 100644
index 00000000..aa7e381e
--- /dev/null
+++ b/app/src/main/res/drawable-xhdpi/ic_toggle_star_outline_dark.png
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_toggle_star_outline_light.png b/app/src/main/res/drawable-xhdpi/ic_toggle_star_outline_light.png
new file mode 100644
index 00000000..6ff994be
--- /dev/null
+++ b/app/src/main/res/drawable-xhdpi/ic_toggle_star_outline_light.png
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi/launch.png b/app/src/main/res/drawable-xhdpi/launch.png
index 0f647dfa..4146ebe4 100644
--- a/app/src/main/res/drawable-xhdpi/launch.png
+++ b/app/src/main/res/drawable-xhdpi/launch.png
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi/notification_close.png b/app/src/main/res/drawable-xhdpi/notification_close.png
index 4230842e..f1013578 100644
--- a/app/src/main/res/drawable-xhdpi/notification_close.png
+++ b/app/src/main/res/drawable-xhdpi/notification_close.png
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi/notification_next.png b/app/src/main/res/drawable-xhdpi/notification_next.png
index 44dbbd12..ad070680 100644
--- a/app/src/main/res/drawable-xhdpi/notification_next.png
+++ b/app/src/main/res/drawable-xhdpi/notification_next.png
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi/notification_pause.png b/app/src/main/res/drawable-xhdpi/notification_pause.png
index e8d8c535..709602aa 100644
--- a/app/src/main/res/drawable-xhdpi/notification_pause.png
+++ b/app/src/main/res/drawable-xhdpi/notification_pause.png
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi/notification_play.png b/app/src/main/res/drawable-xhdpi/notification_play.png
index 532041fa..e2bafa6a 100644
--- a/app/src/main/res/drawable-xhdpi/notification_play.png
+++ b/app/src/main/res/drawable-xhdpi/notification_play.png
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi/notification_previous.png b/app/src/main/res/drawable-xhdpi/notification_previous.png
index 87ee8d2f..d22488cb 100644
--- a/app/src/main/res/drawable-xhdpi/notification_previous.png
+++ b/app/src/main/res/drawable-xhdpi/notification_previous.png
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi/stat_notify_download.png b/app/src/main/res/drawable-xhdpi/stat_notify_download.png
index bd4cb567..96ceb383 100644
--- a/app/src/main/res/drawable-xhdpi/stat_notify_download.png
+++ b/app/src/main/res/drawable-xhdpi/stat_notify_download.png
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi/stat_notify_playing.png b/app/src/main/res/drawable-xhdpi/stat_notify_playing.png
index 532041fa..e2bafa6a 100644
--- a/app/src/main/res/drawable-xhdpi/stat_notify_playing.png
+++ b/app/src/main/res/drawable-xhdpi/stat_notify_playing.png
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi/stat_notify_sync.png b/app/src/main/res/drawable-xhdpi/stat_notify_sync.png
index 6da882a2..b723bf54 100644
--- a/app/src/main/res/drawable-xhdpi/stat_notify_sync.png
+++ b/app/src/main/res/drawable-xhdpi/stat_notify_sync.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi-v11/notification_close.png b/app/src/main/res/drawable-xxhdpi-v11/notification_close.png
deleted file mode 100644
index c3ac026a..00000000
--- a/app/src/main/res/drawable-xxhdpi-v11/notification_close.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi-v11/notification_next.png b/app/src/main/res/drawable-xxhdpi-v11/notification_next.png
deleted file mode 100644
index 06911082..00000000
--- a/app/src/main/res/drawable-xxhdpi-v11/notification_next.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi-v11/notification_pause.png b/app/src/main/res/drawable-xxhdpi-v11/notification_pause.png
deleted file mode 100644
index 1513f9d9..00000000
--- a/app/src/main/res/drawable-xxhdpi-v11/notification_pause.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi-v11/notification_play.png b/app/src/main/res/drawable-xxhdpi-v11/notification_play.png
deleted file mode 100644
index 9138a760..00000000
--- a/app/src/main/res/drawable-xxhdpi-v11/notification_play.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi-v11/notification_previous.png b/app/src/main/res/drawable-xxhdpi-v11/notification_previous.png
deleted file mode 100644
index b4456c16..00000000
--- a/app/src/main/res/drawable-xxhdpi-v11/notification_previous.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi-v11/stat_notify_download.png b/app/src/main/res/drawable-xxhdpi-v11/stat_notify_download.png
deleted file mode 100644
index b2dc5651..00000000
--- a/app/src/main/res/drawable-xxhdpi-v11/stat_notify_download.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi-v11/stat_notify_playing.png b/app/src/main/res/drawable-xxhdpi-v11/stat_notify_playing.png
deleted file mode 100644
index 9138a760..00000000
--- a/app/src/main/res/drawable-xxhdpi-v11/stat_notify_playing.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi-v11/stat_notify_sync.png b/app/src/main/res/drawable-xxhdpi-v11/stat_notify_sync.png
deleted file mode 100644
index 61f6a331..00000000
--- a/app/src/main/res/drawable-xxhdpi-v11/stat_notify_sync.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_rating_bad_dark.png b/app/src/main/res/drawable-xxhdpi/ic_action_rating_bad_dark.png
index d784b239..5fe1fd79 100644
--- a/app/src/main/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/app/src/main/res/drawable-xxhdpi/ic_action_rating_bad_light.png b/app/src/main/res/drawable-xxhdpi/ic_action_rating_bad_light.png
index a1484d25..71e6e89c 100644
--- a/app/src/main/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/app/src/main/res/drawable-xxhdpi/ic_action_rating_bad_selected.png b/app/src/main/res/drawable-xxhdpi/ic_action_rating_bad_selected.png
index 13218a08..945bb594 100644
--- a/app/src/main/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/app/src/main/res/drawable-xxhdpi/ic_action_rating_good_dark.png b/app/src/main/res/drawable-xxhdpi/ic_action_rating_good_dark.png
index a332a632..1f0eeeba 100644
--- a/app/src/main/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/app/src/main/res/drawable-xxhdpi/ic_action_rating_good_light.png b/app/src/main/res/drawable-xxhdpi/ic_action_rating_good_light.png
index 2ef75765..aa384864 100644
--- a/app/src/main/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/app/src/main/res/drawable-xxhdpi/ic_action_rating_good_selected.png b/app/src/main/res/drawable-xxhdpi/ic_action_rating_good_selected.png
index bb444806..273615ea 100644
--- a/app/src/main/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/app/src/main/res/drawable-xxhdpi/ic_menu_bookmark_dark.png b/app/src/main/res/drawable-xxhdpi/ic_menu_bookmark_dark.png
index 2523e14c..ab4d7813 100644
--- a/app/src/main/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/app/src/main/res/drawable-xxhdpi/ic_menu_bookmark_light.png b/app/src/main/res/drawable-xxhdpi/ic_menu_bookmark_light.png
index 9e8c4591..56d843cf 100644
--- a/app/src/main/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/app/src/main/res/drawable-xxhdpi/ic_menu_bookmark_selected.png b/app/src/main/res/drawable-xxhdpi/ic_menu_bookmark_selected.png
index a1890fbb..d24f485d 100644
--- a/app/src/main/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/app/src/main/res/drawable-xxhdpi/ic_toggle_star.png b/app/src/main/res/drawable-xxhdpi/ic_toggle_star.png
new file mode 100644
index 00000000..4db2a37c
--- /dev/null
+++ b/app/src/main/res/drawable-xxhdpi/ic_toggle_star.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_toggle_star_outline_dark.png b/app/src/main/res/drawable-xxhdpi/ic_toggle_star_outline_dark.png
new file mode 100644
index 00000000..8ee1a446
--- /dev/null
+++ b/app/src/main/res/drawable-xxhdpi/ic_toggle_star_outline_dark.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_toggle_star_outline_light.png b/app/src/main/res/drawable-xxhdpi/ic_toggle_star_outline_light.png
new file mode 100644
index 00000000..17672e93
--- /dev/null
+++ b/app/src/main/res/drawable-xxhdpi/ic_toggle_star_outline_light.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi/launch.png b/app/src/main/res/drawable-xxhdpi/launch.png
index cae4f99a..c409c9ef 100644
--- a/app/src/main/res/drawable-xxhdpi/launch.png
+++ b/app/src/main/res/drawable-xxhdpi/launch.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi/notification_close.png b/app/src/main/res/drawable-xxhdpi/notification_close.png
index 022a6780..c3ac026a 100644
--- a/app/src/main/res/drawable-xxhdpi/notification_close.png
+++ b/app/src/main/res/drawable-xxhdpi/notification_close.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi/notification_next.png b/app/src/main/res/drawable-xxhdpi/notification_next.png
index dfe129db..06911082 100644
--- a/app/src/main/res/drawable-xxhdpi/notification_next.png
+++ b/app/src/main/res/drawable-xxhdpi/notification_next.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi/notification_pause.png b/app/src/main/res/drawable-xxhdpi/notification_pause.png
index 9c952207..1513f9d9 100644
--- a/app/src/main/res/drawable-xxhdpi/notification_pause.png
+++ b/app/src/main/res/drawable-xxhdpi/notification_pause.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi/notification_play.png b/app/src/main/res/drawable-xxhdpi/notification_play.png
index 4ee0a5eb..9138a760 100644
--- a/app/src/main/res/drawable-xxhdpi/notification_play.png
+++ b/app/src/main/res/drawable-xxhdpi/notification_play.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi/notification_previous.png b/app/src/main/res/drawable-xxhdpi/notification_previous.png
index e6908126..b4456c16 100644
--- a/app/src/main/res/drawable-xxhdpi/notification_previous.png
+++ b/app/src/main/res/drawable-xxhdpi/notification_previous.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi/stat_notify_download.png b/app/src/main/res/drawable-xxhdpi/stat_notify_download.png
index 9d9a7f3e..b2dc5651 100644
--- a/app/src/main/res/drawable-xxhdpi/stat_notify_download.png
+++ b/app/src/main/res/drawable-xxhdpi/stat_notify_download.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi/stat_notify_playing.png b/app/src/main/res/drawable-xxhdpi/stat_notify_playing.png
index 4ee0a5eb..9138a760 100644
--- a/app/src/main/res/drawable-xxhdpi/stat_notify_playing.png
+++ b/app/src/main/res/drawable-xxhdpi/stat_notify_playing.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi/stat_notify_sync.png b/app/src/main/res/drawable-xxhdpi/stat_notify_sync.png
index 89fe6525..61f6a331 100644
--- a/app/src/main/res/drawable-xxhdpi/stat_notify_sync.png
+++ b/app/src/main/res/drawable-xxhdpi/stat_notify_sync.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_action_rating_bad_dark.png b/app/src/main/res/drawable-xxxhdpi/ic_action_rating_bad_dark.png
new file mode 100644
index 00000000..3f08bdde
--- /dev/null
+++ b/app/src/main/res/drawable-xxxhdpi/ic_action_rating_bad_dark.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_action_rating_bad_light.png b/app/src/main/res/drawable-xxxhdpi/ic_action_rating_bad_light.png
new file mode 100644
index 00000000..545e4570
--- /dev/null
+++ b/app/src/main/res/drawable-xxxhdpi/ic_action_rating_bad_light.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_action_rating_bad_selected.png b/app/src/main/res/drawable-xxxhdpi/ic_action_rating_bad_selected.png
new file mode 100644
index 00000000..f585ba00
--- /dev/null
+++ b/app/src/main/res/drawable-xxxhdpi/ic_action_rating_bad_selected.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_action_rating_good_dark.png b/app/src/main/res/drawable-xxxhdpi/ic_action_rating_good_dark.png
new file mode 100644
index 00000000..af9dcfbb
--- /dev/null
+++ b/app/src/main/res/drawable-xxxhdpi/ic_action_rating_good_dark.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_action_rating_good_light.png b/app/src/main/res/drawable-xxxhdpi/ic_action_rating_good_light.png
new file mode 100644
index 00000000..9ed5d62c
--- /dev/null
+++ b/app/src/main/res/drawable-xxxhdpi/ic_action_rating_good_light.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_action_rating_good_selected.png b/app/src/main/res/drawable-xxxhdpi/ic_action_rating_good_selected.png
new file mode 100644
index 00000000..9ee267f7
--- /dev/null
+++ b/app/src/main/res/drawable-xxxhdpi/ic_action_rating_good_selected.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_menu_bookmark_dark.png b/app/src/main/res/drawable-xxxhdpi/ic_menu_bookmark_dark.png
new file mode 100644
index 00000000..4651e2ce
--- /dev/null
+++ b/app/src/main/res/drawable-xxxhdpi/ic_menu_bookmark_dark.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_menu_bookmark_light.png b/app/src/main/res/drawable-xxxhdpi/ic_menu_bookmark_light.png
new file mode 100644
index 00000000..08050801
--- /dev/null
+++ b/app/src/main/res/drawable-xxxhdpi/ic_menu_bookmark_light.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_menu_bookmark_selected.png b/app/src/main/res/drawable-xxxhdpi/ic_menu_bookmark_selected.png
new file mode 100644
index 00000000..bbec912b
--- /dev/null
+++ b/app/src/main/res/drawable-xxxhdpi/ic_menu_bookmark_selected.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_toggle_star.png b/app/src/main/res/drawable-xxxhdpi/ic_toggle_star.png
new file mode 100644
index 00000000..6116210d
--- /dev/null
+++ b/app/src/main/res/drawable-xxxhdpi/ic_toggle_star.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_toggle_star_outline_dark.png b/app/src/main/res/drawable-xxxhdpi/ic_toggle_star_outline_dark.png
new file mode 100644
index 00000000..ddc2a190
--- /dev/null
+++ b/app/src/main/res/drawable-xxxhdpi/ic_toggle_star_outline_dark.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_toggle_star_outline_light.png b/app/src/main/res/drawable-xxxhdpi/ic_toggle_star_outline_light.png
new file mode 100644
index 00000000..d0048a00
--- /dev/null
+++ b/app/src/main/res/drawable-xxxhdpi/ic_toggle_star_outline_light.png
Binary files differ
diff --git a/app/src/main/res/drawable/drawer_header.jpg b/app/src/main/res/drawable/drawer_header.jpg
new file mode 100644
index 00000000..f54a30e2
--- /dev/null
+++ b/app/src/main/res/drawable/drawer_header.jpg
Binary files differ
diff --git a/app/src/main/res/layout-large-land/abstract_fragment_container.xml b/app/src/main/res/layout-large-land/abstract_fragment_container.xml
index 5e3b1561..3901710f 100644
--- a/app/src/main/res/layout-large-land/abstract_fragment_container.xml
+++ b/app/src/main/res/layout-large-land/abstract_fragment_container.xml
@@ -2,8 +2,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">
+ android:layout_height="match_parent"
+ android:layout_marginTop="?attr/actionBarSize">
<FrameLayout
android:id="@+id/fragment_container"
diff --git a/app/src/main/res/layout/abstract_activity.xml b/app/src/main/res/layout/abstract_activity.xml
index be65e437..56db1439 100644
--- a/app/src/main/res/layout/abstract_activity.xml
+++ b/app/src/main/res/layout/abstract_activity.xml
@@ -1,21 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:fitsSystemWindows="true">
+
<!-- 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.design.widget.NavigationView
+ android:id="@+id/left_drawer"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_gravity="start"/>
</android.support.v4.widget.DrawerLayout>
diff --git a/app/src/main/res/layout/abstract_fragment_activity.xml b/app/src/main/res/layout/abstract_fragment_activity.xml
index d9c99f2f..b51922fd 100644
--- a/app/src/main/res/layout/abstract_fragment_activity.xml
+++ b/app/src/main/res/layout/abstract_fragment_activity.xml
@@ -1,84 +1,137 @@
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<com.sothree.slidinguppanel.SlidingUpPanelLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:sothree="http://schemas.android.com/apk/res-auto"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/slide_up_panel"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:gravity="center_horizontal"
- android:orientation="vertical" >
+ android:gravity="bottom"
+ sothree:umanoPanelHeight="?attr/actionBarSize"
+ sothree:umanoShadowHeight="4dp"
+ sothree:umanoDragView="@+id/slide_up_swipe_target">
- <include layout="@layout/abstract_fragment_container" />
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
- <View
- android:layout_width="fill_parent"
- android:layout_height="1px"
- android:background="@color/dividerColor"/>
+ <android.support.v7.widget.Toolbar
+ android:id="@+id/main_toolbar"
+ android:layout_height="?attr/actionBarSize"
+ android:layout_width="match_parent"
+ android:background="?attr/colorPrimary"
+ android:elevation="4dp"
+ android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
+ app:popupTheme="?attr/actionbarPopupStyle"/>
+
+ <include layout="@layout/abstract_fragment_container"/>
+ </FrameLayout>
<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:id="@+id/slide_up_frame"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <FrameLayout
+ android:id="@+id/slide_up_swipe_target"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_weight="1"
- android:orientation="vertical"
- android:paddingLeft="8dip">
+ android:background="?attr/colorPrimary">
- <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.support.v7.widget.Toolbar
+ android:id="@+id/now_playing_toolbar"
+ android:layout_height="?attr/actionBarSize"
+ android:layout_width="match_parent"
+ android:elevation="4dp"
+ android:visibility="gone"
+ app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
+ app:popupTheme="?attr/actionbarPopupStyle"/>
+
+ <LinearLayout
+ android:id="@+id/bottom_bar"
+ android:layout_width="fill_parent"
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>
+ style="@style/BasicButton"
+ android:orientation="horizontal"
+ android:elevation="4dp">
+
+ <github.daneren2005.dsub.view.RecyclingImageView
+ android:id="@+id/album_art"
+ android:layout_width="?attr/actionBarSize"
+ android:layout_height="?attr/actionBarSize"
+ 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:singleLine="true"
+ android:text="@string/main.title"
+ android:ellipsize="marquee"
+ android:marqueeRepeatLimit="marquee_forever"
+ android:scrollHorizontally="true"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ style="?attr/actionbarTitleStyle">
+
+ <requestFocus android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:duplicateParentState="true" />
+ </TextView>
+
+ <TextView
+ android:id="@+id/artist_name"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:singleLine="true"
+ android:text="@string/main.artist"
+ style="?attr/actionbarSubtitleStyle"/>
+ </LinearLayout>
+
+
+ <LinearLayout
+ android:layout_height="fill_parent"
+ android:layout_width="0dp"
+ android:layout_weight="1">
+
+ <ImageButton
+ style="@style/PlaybackControl.Match"
+ android:id="@+id/download_previous"
+ android:src="?attr/actionbar_backward"
+ android:layout_width="0dp"
+ android:layout_weight="1"/>
+
+ <ImageButton
+ style="@style/PlaybackControl.Match"
+ android:id="@+id/download_start"
+ android:src="?attr/actionbar_start"
+ android:layout_width="0dp"
+ android:layout_weight="1"/>
+
+ <ImageButton
+ style="@style/PlaybackControl.Match"
+ android:id="@+id/download_next"
+ android:src="?attr/actionbar_forward"
+ android:layout_width="0dp"
+ android:layout_weight="1"/>
+ </LinearLayout>
+ </LinearLayout>
+ </FrameLayout>
+
+ <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/now_playing_fragment_container"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:clickable="true"/>
</LinearLayout>
-</LinearLayout> \ No newline at end of file
+</com.sothree.slidinguppanel.SlidingUpPanelLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/abstract_fragment_container.xml b/app/src/main/res/layout/abstract_fragment_container.xml
index 61e17d1d..f13356c4 100644
--- a/app/src/main/res/layout/abstract_fragment_container.xml
+++ b/app/src/main/res/layout/abstract_fragment_container.xml
@@ -2,5 +2,5 @@
<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
+ android:layout_height="match_parent"
+ android:layout_marginTop="?attr/actionBarSize"/> \ No newline at end of file
diff --git a/app/src/main/res/layout/abstract_list_fragment.xml b/app/src/main/res/layout/abstract_recycler_fragment.xml
index 618a7341..47141450 100644
--- a/app/src/main/res/layout/abstract_list_fragment.xml
+++ b/app/src/main/res/layout/abstract_recycler_fragment.xml
@@ -10,17 +10,12 @@
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.support.v7.widget.RecyclerView
+ android:id="@+id/fragment_recycler"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1.0"
- android:fastScrollEnabled="true"/>
+ android:scrollbars="vertical"/>
<include layout="@layout/tab_progress" />
</LinearLayout>
diff --git a/app/src/main/res/layout/actionbar_spinner.xml b/app/src/main/res/layout/actionbar_spinner.xml
index 22055901..4d9f95a4 100644
--- a/app/src/main/res/layout/actionbar_spinner.xml
+++ b/app/src/main/res/layout/actionbar_spinner.xml
@@ -1,13 +1,14 @@
<?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="fill_parent"
- android:gravity="fill_horizontal" >
- <Spinner
- android:id="@+id/spinner"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:prompt="@string/common.appname"
- />
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:gravity="fill_horizontal" >
+
+ <Spinner
+ android:id="@+id/spinner"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:prompt="@string/common.appname"
+ android:background="@drawable/abc_spinner_mtrl_am_alpha"/>
</RelativeLayout>
diff --git a/app/src/main/res/layout/album_cell_item.xml b/app/src/main/res/layout/album_cell_item.xml
index 3f708e63..244c6f1f 100644
--- a/app/src/main/res/layout/album_cell_item.xml
+++ b/app/src/main/res/layout/album_cell_item.xml
@@ -2,7 +2,8 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:background="?attr/selectableItemBackground">
<RelativeLayout
android:layout_width="match_parent"
@@ -66,10 +67,10 @@
<ImageButton
android:id="@+id/album_star"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_width="@dimen/Star.Small"
+ android:layout_height="@dimen/Star.Small"
+ android:scaleType="fitCenter"
android:layout_gravity="right|center_vertical"
- android:src="@drawable/ic_stat_star"
android:background="@android:color/transparent"
android:focusable="false"
android:visibility="gone"/>
@@ -77,7 +78,7 @@
</LinearLayout>
<ImageView
- android:id="@+id/album_more"
+ android:id="@+id/more_button"
android:src="?attr/download_none"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/app/src/main/res/layout/album_list_header.xml b/app/src/main/res/layout/album_list_header.xml
new file mode 100644
index 00000000..e78d0ace
--- /dev/null
+++ b/app/src/main/res/layout/album_list_header.xml
@@ -0,0 +1,29 @@
+<?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:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/item_name"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:background="@android:color/transparent"
+ android:textColor="?attr/colorAccent"
+ android:textStyle="bold"
+ android:paddingLeft="6dp"
+ android:paddingRight="6dp"
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp"/>
+
+ <CheckBox
+ android:id="@+id/item_checkbox"
+ 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"
+ android:textColor="?android:textColorPrimary"/>
+</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/album_list_item.xml b/app/src/main/res/layout/album_list_item.xml
index 0ee92edd..ba892365 100644
--- a/app/src/main/res/layout/album_list_item.xml
+++ b/app/src/main/res/layout/album_list_item.xml
@@ -3,7 +3,8 @@
android:id="@id/drag_handle"
android:orientation="horizontal"
android:layout_width="fill_parent"
- android:layout_height="wrap_content">
+ android:layout_height="wrap_content"
+ android:background="?attr/selectableItemBackground">
<RelativeLayout
android:layout_width="@dimen/AlbumArt.Small"
@@ -43,29 +44,31 @@
android:textAppearance="?android:attr/textAppearanceMedium"
android:singleLine="true"
android:ellipsize="marquee"
- android:paddingBottom="6dip"/>
+ android:paddingBottom="6dip"
+ android:textColor="?android:textColorPrimary"/>
<TextView
android:id="@+id/album_artist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
- android:singleLine="true"/>
+ android:singleLine="true"
+ android:textColor="?android:textColorSecondary"/>
</LinearLayout>
<ImageButton
android:id="@+id/album_star"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_width="@dimen/Star.Small"
+ android:layout_height="@dimen/Star.Small"
+ android:scaleType="fitCenter"
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:id="@+id/more_button"
android:src="?attr/download_none"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
diff --git a/app/src/main/res/layout/appwidget4x1.xml b/app/src/main/res/layout/appwidget4x1.xml
index 8f52c872..81e77852 100644
--- a/app/src/main/res/layout/appwidget4x1.xml
+++ b/app/src/main/res/layout/appwidget4x1.xml
@@ -30,7 +30,7 @@
android:clickable="true"
android:focusable="true"
android:orientation="vertical"
- style="@style/BasicButton">
+ style="@style/NotificationButton">
<TextView
android:id="@+id/title"
@@ -81,7 +81,7 @@
android:layout_height="fill_parent"
android:layout_weight="1"
android:scaleType="center"
- style="@style/BasicButton"
+ style="@style/NotificationButton"
android:src="@drawable/ic_appwidget_music_previous" />
<ImageButton
@@ -91,7 +91,7 @@
android:layout_weight="1"
android:scaleType="center"
android:src="@drawable/ic_appwidget_music_play"
- style="@style/BasicButton" />
+ style="@style/NotificationButton" />
<ImageButton
android:id="@+id/control_next"
@@ -100,7 +100,7 @@
android:layout_weight="1"
android:scaleType="center"
android:src="@drawable/ic_appwidget_music_next"
- style="@style/BasicButton" />
+ style="@style/NotificationButton" />
</LinearLayout>
</LinearLayout>
diff --git a/app/src/main/res/layout/appwidget4x2.xml b/app/src/main/res/layout/appwidget4x2.xml
index 5763fb01..d2a42ff0 100644
--- a/app/src/main/res/layout/appwidget4x2.xml
+++ b/app/src/main/res/layout/appwidget4x2.xml
@@ -32,7 +32,7 @@
android:orientation="vertical"
android:paddingTop="4dip"
android:paddingBottom="4dip"
- style="@style/BasicButton">
+ style="@style/NotificationButton">
<TextView
android:id="@+id/title"
@@ -104,7 +104,7 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:scaleType="center"
- style="@style/BasicButton"
+ style="@style/NotificationButton"
android:src="@drawable/ic_appwidget_music_previous" />
<ImageButton
@@ -114,7 +114,7 @@
android:layout_weight="1"
android:scaleType="center"
android:src="@drawable/ic_appwidget_music_play"
- style="@style/BasicButton" />
+ style="@style/NotificationButton" />
<ImageButton
android:id="@+id/control_next"
@@ -123,7 +123,7 @@
android:layout_weight="1"
android:scaleType="center"
android:src="@drawable/ic_appwidget_music_next"
- style="@style/BasicButton" />
+ style="@style/NotificationButton" />
</LinearLayout>
</LinearLayout>
diff --git a/app/src/main/res/layout/appwidget4x3.xml b/app/src/main/res/layout/appwidget4x3.xml
index 539b9f01..77a15ad5 100644
--- a/app/src/main/res/layout/appwidget4x3.xml
+++ b/app/src/main/res/layout/appwidget4x3.xml
@@ -34,7 +34,7 @@
android:orientation="vertical"
android:paddingBottom="4dip"
android:paddingTop="4dip"
- style="@style/BasicButton">
+ style="@style/NotificationButton">
<TextView
android:id="@+id/title"
@@ -86,7 +86,7 @@
android:layout_height="56dip"
android:layout_weight="1"
android:scaleType="center"
- style="@style/BasicButton"
+ style="@style/NotificationButton"
android:src="@drawable/ic_appwidget_music_previous" />
<ImageButton
@@ -96,7 +96,7 @@
android:layout_weight="1"
android:scaleType="center"
android:src="@drawable/ic_appwidget_music_play"
- style="@style/BasicButton" />
+ style="@style/NotificationButton" />
<ImageButton
android:id="@+id/control_next"
@@ -105,7 +105,7 @@
android:layout_weight="1"
android:scaleType="center"
android:src="@drawable/ic_appwidget_music_next"
- style="@style/BasicButton" />
+ style="@style/NotificationButton" />
</LinearLayout>
</LinearLayout>
diff --git a/app/src/main/res/layout/appwidget4x4.xml b/app/src/main/res/layout/appwidget4x4.xml
index 1f2db9e1..56f5c622 100644
--- a/app/src/main/res/layout/appwidget4x4.xml
+++ b/app/src/main/res/layout/appwidget4x4.xml
@@ -35,7 +35,7 @@
android:orientation="vertical"
android:paddingTop="4dip"
android:paddingBottom="4dip"
- style="@style/BasicButton">
+ style="@style/NotificationButton">
<TextView
android:id="@+id/title"
@@ -88,7 +88,7 @@
android:layout_height="56dip"
android:layout_weight="1"
android:scaleType="center"
- style="@style/BasicButton"
+ style="@style/NotificationButton"
android:src="@drawable/ic_appwidget_music_previous" />
<ImageButton
@@ -98,7 +98,7 @@
android:layout_weight="1"
android:scaleType="center"
android:src="@drawable/ic_appwidget_music_play"
- style="@style/BasicButton" />
+ style="@style/NotificationButton" />
<ImageButton
android:id="@+id/control_next"
@@ -107,7 +107,7 @@
android:layout_weight="1"
android:scaleType="center"
android:src="@drawable/ic_appwidget_music_next"
- style="@style/BasicButton" />
+ style="@style/NotificationButton" />
</LinearLayout>
</LinearLayout>
diff --git a/app/src/main/res/layout/basic_choice_item.xml b/app/src/main/res/layout/basic_choice_item.xml
new file mode 100644
index 00000000..e2dc2204
--- /dev/null
+++ b/app/src/main/res/layout/basic_choice_item.xml
@@ -0,0 +1,27 @@
+<?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="?attr/selectableItemBackground">
+
+ <TextView
+ android:id="@+id/item_name"
+ android:layout_width="0dip"
+ android:layout_height="?android:attr/listPreferredItemHeight"
+ android:layout_weight="1"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:gravity="left|center_vertical"
+ android:paddingLeft="6dip"
+ android:paddingRight="6dip"
+ android:background="@android:color/transparent"
+ android:textColor="?android:textColorPrimary"/>
+
+ <CheckBox
+ android:id="@+id/item_checkbox"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_gravity="right|center_vertical"
+ android:checkMark="?android:attr/listChoiceIndicatorMultiple"
+ style="@style/MoreButton"/>
+</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/basic_count_item.xml b/app/src/main/res/layout/basic_count_item.xml
index 08d276db..ce1aa800 100644
--- a/app/src/main/res/layout/basic_count_item.xml
+++ b/app/src/main/res/layout/basic_count_item.xml
@@ -2,7 +2,7 @@
<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:background="?attr/selectableItemBackground"
android:minHeight="50dip">
<TextView
@@ -14,7 +14,8 @@
android:paddingLeft="6dip"
android:paddingRight="6dip"
android:background="@android:color/transparent"
- android:text="Text"/>
+ android:text="Text"
+ android:textColor="?android:textColorPrimary"/>
<TextView
android:id="@+id/basic_count_count"
@@ -31,5 +32,6 @@
android:textSize="11sp"
android:gravity="right|center_vertical"
android:layout_gravity="center_vertical"
+ android:textColor="?android:textColorPrimary"
android:visibility="gone"/>
</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/basic_header.xml b/app/src/main/res/layout/basic_header.xml
new file mode 100644
index 00000000..b1f94b33
--- /dev/null
+++ b/app/src/main/res/layout/basic_header.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ 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"
+ android:textColor="?attr/colorAccent"
+ android:textStyle="bold"
+ android:paddingLeft="6dp"
+ android:paddingRight="6dp"
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp"/> \ No newline at end of file
diff --git a/app/src/main/res/layout/basic_list_item.xml b/app/src/main/res/layout/basic_list_item.xml
index 2338f7e0..ea1df8e8 100644
--- a/app/src/main/res/layout/basic_list_item.xml
+++ b/app/src/main/res/layout/basic_list_item.xml
@@ -3,7 +3,7 @@
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:background="@android:color/transparent">
+ android:background="?attr/selectableItemBackground">
<TextView
android:id="@+id/item_name"
@@ -15,14 +15,15 @@
android:paddingLeft="6dip"
android:paddingRight="6dip"
android:minHeight="50dip"
- android:background="@android:color/transparent"/>
+ android:background="@android:color/transparent"
+ android:textColor="?android:textColorPrimary"/>
<ImageButton
android:id="@+id/item_star"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_width="@dimen/Star.Small"
+ android:layout_height="@dimen/Star.Small"
+ android:scaleType="fitCenter"
android:layout_gravity="right|center_vertical"
- android:src="@drawable/ic_stat_star"
android:background="@android:color/transparent"
android:focusable="false"
android:visibility="gone"/>
diff --git a/app/src/main/res/layout/change_email.xml b/app/src/main/res/layout/change_email.xml
index 18ffc765..87d297be 100644
--- a/app/src/main/res/layout/change_email.xml
+++ b/app/src/main/res/layout/change_email.xml
@@ -15,7 +15,8 @@
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:textSize="20dp"
- android:text="@string/admin.change_email_label" />
+ android:text="@string/admin.change_email_label"
+ android:textColor="?android:textColorPrimary"/>
<EditText
android:id="@+id/new_email"
android:inputType="textEmailAddress"
diff --git a/app/src/main/res/layout/change_password.xml b/app/src/main/res/layout/change_password.xml
index 1a382a6b..d8043c05 100644
--- a/app/src/main/res/layout/change_password.xml
+++ b/app/src/main/res/layout/change_password.xml
@@ -15,7 +15,8 @@
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:textSize="20dp"
- android:text="@string/admin.change_password_label" />
+ android:text="@string/admin.change_password_label"
+ android:textColor="?android:textColorPrimary"/>
<EditText
android:id="@+id/new_password"
android:inputType="textPassword"
diff --git a/app/src/main/res/layout/chat_item.xml b/app/src/main/res/layout/chat_item.xml
index f31f7988..bf5d81ca 100644
--- a/app/src/main/res/layout/chat_item.xml
+++ b/app/src/main/res/layout/chat_item.xml
@@ -2,7 +2,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:layout_height="wrap_content"
+ android:background="?attr/selectableItemBackground">
<github.daneren2005.dsub.view.RecyclingImageView
android:id="@+id/chat_avatar"
@@ -34,7 +35,7 @@
android:singleLine="true"
android:text="User"
android:textAppearance="?android:attr/textAppearanceLarge"
- android:textColor="?android:textColorSecondary"/>
+ android:textColor="?android:textColorPrimary"/>
<TextView
android:id="@+id/chat_time"
@@ -43,7 +44,8 @@
android:layout_marginLeft="6dip"
android:singleLine="true"
android:text="00:00"
- android:textAppearance="?android:attr/textAppearanceSmall" />
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:textColorSecondary"/>
</LinearLayout>
<TextView
@@ -56,6 +58,7 @@
android:linksClickable="true"
android:singleLine="false"
android:text="Message Text Goes Here"
- android:textAppearance="?android:attr/textAppearanceMedium" />
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?android:textColorSecondary"/>
</LinearLayout>
</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/chat_item_reverse.xml b/app/src/main/res/layout/chat_item_reverse.xml
index b8102193..31875208 100644
--- a/app/src/main/res/layout/chat_item_reverse.xml
+++ b/app/src/main/res/layout/chat_item_reverse.xml
@@ -2,7 +2,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:layout_height="wrap_content"
+ android:background="?attr/selectableItemBackground">
<LinearLayout
android:layout_width="0dip"
@@ -24,7 +25,8 @@
android:layout_marginRight="6dip"
android:singleLine="true"
android:text="00:00"
- android:textAppearance="?android:attr/textAppearanceSmall" />
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:textColorSecondary"/>
<TextView
android:id="@+id/chat_username"
@@ -35,7 +37,7 @@
android:singleLine="true"
android:text="User"
android:textAppearance="?android:attr/textAppearanceLarge"
- android:textColor="?android:textColorSecondary"/>
+ android:textColor="?android:textColorPrimary"/>
</LinearLayout>
<TextView
@@ -49,7 +51,8 @@
android:singleLine="false"
android:text="Chat message"
android:textAppearance="?android:attr/textAppearanceMedium"
- android:layout_gravity="right"/>
+ android:layout_gravity="right"
+ android:textColor="?android:textColorSecondary"/>
</LinearLayout>
<github.daneren2005.dsub.view.RecyclingImageView
diff --git a/app/src/main/res/layout/complex_list_item.xml b/app/src/main/res/layout/complex_list_item.xml
index 67851eca..143f9af9 100644
--- a/app/src/main/res/layout/complex_list_item.xml
+++ b/app/src/main/res/layout/complex_list_item.xml
@@ -1,9 +1,9 @@
<?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">
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="?android:attr/listPreferredItemHeight"
+ android:background="?attr/selectableItemBackground">
<LinearLayout android:orientation="vertical"
android:layout_width="0dip"
@@ -19,22 +19,24 @@
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
- android:background="@android:color/transparent"/>
+ android:background="@android:color/transparent"
+ android:textColor="?android:textColorPrimary"/>
<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"/>
+ android:background="@android:color/transparent"
+ android:textColor="?android:textColorSecondary"/>
</LinearLayout>
<ImageButton
android:id="@+id/item_star"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_width="@dimen/Star.Small"
+ android:layout_height="@dimen/Star.Small"
+ android:scaleType="fitCenter"
android:layout_gravity="right|center_vertical"
- android:src="@drawable/ic_stat_star"
android:background="@android:color/transparent"
android:focusable="false"
android:visibility="gone"/>
diff --git a/app/src/main/res/layout/confirm_password.xml b/app/src/main/res/layout/confirm_password.xml
index 27ee04ea..9ec61c0a 100644
--- a/app/src/main/res/layout/confirm_password.xml
+++ b/app/src/main/res/layout/confirm_password.xml
@@ -15,7 +15,8 @@
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:textSize="20dp"
- android:text="@string/admin.add_user_password" />
+ android:text="@string/admin.add_user_password"
+ android:textColor="?android:textColorPrimary"/>
<EditText
android:id="@+id/password"
android:inputType="textPassword"
diff --git a/app/src/main/res/layout/create_bookmark.xml b/app/src/main/res/layout/create_bookmark.xml
index f72b39d8..d6f077c3 100644
--- a/app/src/main/res/layout/create_bookmark.xml
+++ b/app/src/main/res/layout/create_bookmark.xml
@@ -14,7 +14,8 @@
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:textSize="20dp"
- android:text="@string/common.comment" />
+ android:text="@string/common.comment"
+ android:textColor="?android:textColorPrimary"/>
<EditText
android:id="@+id/comment_text"
android:inputType="text"
diff --git a/app/src/main/res/layout/create_podcast.xml b/app/src/main/res/layout/create_podcast.xml
index 5a2ec970..04e74ec3 100644
--- a/app/src/main/res/layout/create_podcast.xml
+++ b/app/src/main/res/layout/create_podcast.xml
@@ -14,7 +14,8 @@
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:textSize="20dp"
- android:text="@string/select_podcasts.add_url"/>
+ android:text="@string/select_podcasts.add_url"
+ android:textColor="?android:textColorPrimary"/>
<EditText
android:id="@+id/create_podcast_url"
android:inputType="textUri"
diff --git a/app/src/main/res/layout/create_user.xml b/app/src/main/res/layout/create_user.xml
index eac6e5cb..b2d8f6e0 100644
--- a/app/src/main/res/layout/create_user.xml
+++ b/app/src/main/res/layout/create_user.xml
@@ -22,7 +22,8 @@
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:layout_marginLeft="4dp" />
+ android:layout_marginLeft="4dp"
+ android:textColor="?android:textColorPrimary"/>
</LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
@@ -43,7 +44,8 @@
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:layout_marginLeft="4dp" />
+ android:layout_marginLeft="4dp"
+ android:textColor="?android:textColorPrimary"/>
</LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
@@ -64,14 +66,14 @@
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:layout_marginLeft="4dp" />
+ android:layout_marginLeft="4dp"
+ android:textColor="?android:textColorPrimary"/>
</LinearLayout>
- <ListView
+ <android.support.v7.widget.RecyclerView
android:id="@+id/settings_list"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1.0"
- android:fastScrollEnabled="true"/>
-
+ android:scrollbars="vertical"/>
</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/details_item.xml b/app/src/main/res/layout/details_item.xml
new file mode 100644
index 00000000..4ef5fef0
--- /dev/null
+++ b/app/src/main/res/layout/details_item.xml
@@ -0,0 +1,28 @@
+<?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="match_parent"
+ android:padding="8dp">
+
+ <TextView
+ android:id="@+id/detail_name"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?android:textColorPrimary"
+ android:paddingLeft="14dp"
+ android:layout_gravity="center_vertical"/>
+
+ <TextView
+ android:id="@+id/detail_value"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:textColorSecondary"
+ android:paddingLeft="14dp"
+ android:layout_gravity="center_vertical"/>
+
+</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/download_activity.xml b/app/src/main/res/layout/download_activity.xml
deleted file mode 100644
index 017e4013..00000000
--- a/app/src/main/res/layout/download_activity.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/fragment_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent" /> \ No newline at end of file
diff --git a/app/src/main/res/layout/download_playlist.xml b/app/src/main/res/layout/download_playlist.xml
index 8a73ef3b..7a83330c 100644
--- a/app/src/main/res/layout/download_playlist.xml
+++ b/app/src/main/res/layout/download_playlist.xml
@@ -1,31 +1,29 @@
<?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">
+ 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"/>
+ android:layout_width="fill_parent"
+ android:layout_height="1px"
+ android:background="?attr/colorPrimary"/>
<TextView
- android:id="@+id/download_empty"
- android:text="@string/download.empty"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:padding="10dip"/>
+ android:id="@+id/download_empty"
+ android:text="@string/download.empty"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:padding="10dip"
+ android:textColor="?android:textColorPrimary"/>
- <com.mobeta.android.dslv.DragSortListView
- style="@style/DragDropListView"
+ <android.support.v7.widget.RecyclerView
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"/>
+ android:scrollbars="vertical"/>
</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/drawer_header.xml b/app/src/main/res/layout/drawer_header.xml
new file mode 100644
index 00000000..91fad9fe
--- /dev/null
+++ b/app/src/main/res/layout/drawer_header.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="178dp"
+ android:orientation="vertical"
+ android:weightSum="1"
+ android:background="@drawable/drawer_header">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="56dp"
+ android:orientation="horizontal"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:background="?attr/selectableItemBackground">
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/header_server_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="16dp"
+ style="?attr/actionbarTitleStyle"/>
+
+ <TextView
+ android:id="@+id/header_user_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="16dp"
+ style="?attr/actionbarSubtitleStyle"/>
+ </LinearLayout>
+
+ <View
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
+
+ <ImageView
+ android:id="@+id/header_select_image"
+ android:src="@drawable/main_select_server_dark"
+ android:layout_gravity="center_vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingRight="20dp"/>
+ </LinearLayout>
+
+ <de.hdodenhof.circleimageview.CircleImageView
+ android:id="@+id/header_user_avatar"
+ android:layout_width="70dp"
+ android:layout_height="70dp"
+ android:layout_marginLeft="16dp"
+ android:layout_marginTop="38dp"/>
+</RelativeLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/drawer_list_item.xml b/app/src/main/res/layout/drawer_list_item.xml
deleted file mode 100644
index 5f17c9e9..00000000
--- a/app/src/main/res/layout/drawer_list_item.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?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/app/src/main/res/layout/edit_play_action.xml b/app/src/main/res/layout/edit_play_action.xml
index a1115da6..95a3d7ff 100644
--- a/app/src/main/res/layout/edit_play_action.xml
+++ b/app/src/main/res/layout/edit_play_action.xml
@@ -14,7 +14,8 @@
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:textSize="20dp"
- android:text="@string/tasker.edit_shuffle_mode" />
+ android:text="@string/tasker.edit_shuffle_mode"
+ android:textColor="?android:textColorPrimary"/>
<CheckBox
android:id="@+id/edit_shuffle_checkbox"
android:layout_width="wrap_content"
@@ -33,7 +34,8 @@
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:textSize="20dp"
- android:text="@string/tasker.edit_shuffle_start_year" />
+ android:text="@string/tasker.edit_shuffle_start_year"
+ android:textColor="?android:textColorPrimary"/>
<CheckBox
android:id="@+id/edit_start_year_checkbox"
@@ -61,7 +63,8 @@
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:textSize="20dp"
- android:text="@string/tasker.edit_shuffle_end_year" />
+ android:text="@string/tasker.edit_shuffle_end_year"
+ android:textColor="?android:textColorPrimary"/>
<CheckBox
android:id="@+id/edit_end_year_checkbox"
@@ -89,7 +92,8 @@
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:textSize="20dp"
- android:text="@string/tasker.edit_shuffle_genre"/>
+ android:text="@string/tasker.edit_shuffle_genre"
+ android:textColor="?android:textColorPrimary"/>
<Button
android:id="@+id/edit_genre_spinner"
@@ -109,7 +113,8 @@
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:textSize="20dp"
- android:text="@string/tasker.edit_server_offline"/>
+ android:text="@string/tasker.edit_server_offline"
+ android:textColor="?android:textColorPrimary"/>
<Spinner
android:id="@+id/edit_offline_spinner"
diff --git a/app/src/main/res/layout/equalizer_bar.xml b/app/src/main/res/layout/equalizer_bar.xml
index 6dc91565..9e3fab5c 100644
--- a/app/src/main/res/layout/equalizer_bar.xml
+++ b/app/src/main/res/layout/equalizer_bar.xml
@@ -1,36 +1,33 @@
<?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">
+ 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"
- />
+ <TextView
+ android:id="@+id/equalizer.frequency"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:layout_alignParentLeft="true"
+ android:textColor="?android:textColorPrimary"/>
+ <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"
+ android:textColor="?android:textColorSecondary"/>
+ <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/app/src/main/res/layout/genre_list_item.xml b/app/src/main/res/layout/genre_list_item.xml
index 6affa24c..fb30e167 100644
--- a/app/src/main/res/layout/genre_list_item.xml
+++ b/app/src/main/res/layout/genre_list_item.xml
@@ -3,7 +3,7 @@
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:background="@android:color/transparent">
+ android:background="?attr/selectableItemBackground">
<TextView
android:id="@+id/genre_name"
@@ -17,7 +17,8 @@
android:minHeight="50dip"
android:singleLine="true"
android:ellipsize="marquee"
- android:background="@android:color/transparent"/>
+ android:background="@android:color/transparent"
+ android:textColor="?android:textColorPrimary"/>
<LinearLayout
android:layout_width="wrap_content"
@@ -31,12 +32,14 @@
android:id="@+id/genre_songs"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceSmall"/>
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:textColorSecondary"/>
<TextView
android:id="@+id/genre_albums"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceSmall"/>
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:textColorSecondary"/>
</LinearLayout>
</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/grid_view.xml b/app/src/main/res/layout/grid_view.xml
deleted file mode 100644
index 599cf92c..00000000
--- a/app/src/main/res/layout/grid_view.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-<?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/app/src/main/res/layout/home.xml b/app/src/main/res/layout/home.xml
index e5bf5a70..043886a2 100644
--- a/app/src/main/res/layout/home.xml
+++ b/app/src/main/res/layout/home.xml
@@ -8,7 +8,7 @@
<View
android:layout_width="fill_parent"
android:layout_height="1px"
- android:background="@color/dividerColor"/>
+ android:background="?attr/colorPrimary"/>
<ListView
android:id="@+id/main_list"
diff --git a/app/src/main/res/layout/jukebox_volume.xml b/app/src/main/res/layout/jukebox_volume.xml
deleted file mode 100644
index 0c49f634..00000000
--- a/app/src/main/res/layout/jukebox_volume.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<RelativeLayout
- android:background="@drawable/toast_frame"
- android:padding="20dip"
- android:layout_height="fill_parent"
- android:layout_width="fill_parent"
- android:orientation="vertical"
- android:id="@+id/toast_layout_root"
- xmlns:android="http://schemas.android.com/apk/res/android">
-
- <TextView
- android:layout_height="wrap_content"
- android:layout_width="fill_parent"
- android:id="@+id/jukebox_volume_title"
- android:paddingBottom="12dp"
- android:paddingRight="32dp"
- android:paddingLeft="32dp"
- android:shadowRadius="2.75"
- android:shadowColor="#bb000000"
- android:textColor="#ffffffff"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:text="@string/download.jukebox_volume"
- android:gravity="center_horizontal"
- android:layout_alignParentTop="true"
- android:layout_alignParentRight="true"
- android:layout_alignParentLeft="true"/>
-
- <ImageView
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:id="@+id/jukebox_volume_image"
- android:paddingRight="12dip"
- android:layout_alignParentLeft="true"
- android:layout_below="@+id/jukebox_volume_title"
- android:src="@drawable/ic_action_volume_dark"/>
-
- <SeekBar
- android:layout_height="wrap_content"
- android:layout_width="fill_parent"
- android:id="@+id/jukebox_volume_progress_bar"
- android:paddingBottom="3dp"
- android:layout_alignParentRight="true"
- android:layout_below="@+id/jukebox_volume_title"
- android:layout_toRightOf="@+id/jukebox_volume_image"
- android:indeterminate="false"/>
-</RelativeLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/lyrics.xml b/app/src/main/res/layout/lyrics.xml
index 747727bd..f2aa7c7f 100644
--- a/app/src/main/res/layout/lyrics.xml
+++ b/app/src/main/res/layout/lyrics.xml
@@ -1,55 +1,54 @@
<?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>
-
+ 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"
+ android:textColor="?android:textColorPrimary"/>
+
+ <TextView
+ android:id="@+id/lyrics_title"
+ 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"
+ android:paddingBottom="12dip"
+ android:textColor="?android:textColorPrimary"/>
+
+ <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"
+ android:textColor="?android:textColorSecondary"/>
+ </LinearLayout>
+ </ScrollView>
</LinearLayout>
diff --git a/app/src/main/res/layout/main_buttons.xml b/app/src/main/res/layout/main_buttons.xml
deleted file mode 100644
index 95a60409..00000000
--- a/app/src/main/res/layout/main_buttons.xml
+++ /dev/null
@@ -1,157 +0,0 @@
-<?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/app/src/main/res/layout/notification.xml b/app/src/main/res/layout/notification.xml
index 12efa4ec..e7c24acf 100644
--- a/app/src/main/res/layout/notification.xml
+++ b/app/src/main/res/layout/notification.xml
@@ -51,7 +51,7 @@
<ImageButton
android:id="@+id/control_previous"
android:src="@drawable/notification_previous"
- style="@style/BasicButton"
+ style="@style/NotificationButton"
android:layout_width="46dip"
android:layout_height="fill_parent"
android:layout_gravity="center|right"
@@ -62,7 +62,7 @@
<ImageButton
android:id="@+id/control_pause"
android:src="@drawable/notification_pause"
- style="@style/BasicButton"
+ style="@style/NotificationButton"
android:layout_width="46dip"
android:layout_height="fill_parent"
android:layout_gravity="center|right"
@@ -73,7 +73,7 @@
<ImageButton
android:id="@+id/control_next"
android:src="@drawable/notification_next"
- style="@style/BasicButton"
+ style="@style/NotificationButton"
android:layout_width="46dip"
android:layout_height="fill_parent"
android:layout_gravity="center|right"
diff --git a/app/src/main/res/layout/notification_expanded.xml b/app/src/main/res/layout/notification_expanded.xml
index aa9fe759..79d694db 100644
--- a/app/src/main/res/layout/notification_expanded.xml
+++ b/app/src/main/res/layout/notification_expanded.xml
@@ -40,7 +40,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/notification_close"
- style="@style/BasicButton"
+ style="@style/NotificationButton"
android:padding="10dip"
android:layout_gravity="right"
android:visibility="gone"/>
@@ -91,7 +91,7 @@
android:layout_gravity="center"
android:layout_weight="1"
android:padding="10dip"
- style="@style/BasicButton"
+ style="@style/NotificationButton"
android:scaleType="fitCenter"
android:src="@drawable/notification_previous" />
@@ -102,7 +102,7 @@
android:layout_gravity="center"
android:layout_weight="1"
android:padding="10dip"
- style="@style/BasicButton"
+ style="@style/NotificationButton"
android:scaleType="fitCenter"
android:src="@drawable/notification_pause" />
@@ -113,7 +113,7 @@
android:layout_gravity="center"
android:layout_weight="1"
android:padding="10dip"
- style="@style/BasicButton"
+ style="@style/NotificationButton"
android:scaleType="fitCenter"
android:src="@drawable/notification_next" />
</LinearLayout>
diff --git a/app/src/main/res/layout/playlist_cell_item.xml b/app/src/main/res/layout/playlist_cell_item.xml
new file mode 100644
index 00000000..7e6ab0cd
--- /dev/null
+++ b/app/src/main/res/layout/playlist_cell_item.xml
@@ -0,0 +1,39 @@
+<?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"
+ android:background="?attr/selectableItemBackground">
+
+ <github.daneren2005.dsub.view.SquareImageView
+ android:id="@+id/playlist_coverart"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:paddingTop="4dp"
+ android:paddingLeft="2dp">
+
+ <TextView
+ android:id="@+id/playlist_title"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:textColor="?android:textColorPrimary"/>
+
+ <ImageView
+ android:id="@+id/more_button"
+ 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/app/src/main/res/layout/playlist_list_item.xml b/app/src/main/res/layout/playlist_list_item.xml
new file mode 100644
index 00000000..c1cc7b3c
--- /dev/null
+++ b/app/src/main/res/layout/playlist_list_item.xml
@@ -0,0 +1,34 @@
+<?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="?attr/selectableItemBackground">
+
+ <github.daneren2005.dsub.view.RecyclingImageView
+ android:id="@+id/playlist_coverart"
+ android:layout_width="@dimen/AlbumArt.Small"
+ android:layout_height="@dimen/AlbumArt.Small"
+ android:layout_gravity="left|center_vertical"/>
+
+ <TextView
+ android:id="@+id/playlist_title"
+ 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"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:textColor="?android:textColorPrimary"/>
+
+ <ImageView
+ android:id="@+id/more_button"
+ 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/app/src/main/res/layout/progress.xml b/app/src/main/res/layout/progress.xml
index a1904c11..8a299d63 100644
--- a/app/src/main/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">
+ 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"/>
+ <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"
+ android:textColor="?android:textColorPrimary"/>
</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/save_playlist.xml b/app/src/main/res/layout/save_playlist.xml
index 8bb21748..9a85d24c 100644
--- a/app/src/main/res/layout/save_playlist.xml
+++ b/app/src/main/res/layout/save_playlist.xml
@@ -1,17 +1,17 @@
<?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">
+ 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"/>
+ <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"
@@ -20,7 +20,8 @@
android:text="@string/playlist.overwrite"
android:layout_marginLeft="4dp"
android:checked="false"
- android:visibility="gone"/>
+ android:visibility="gone"
+ android:textColor="?android:textColorPrimary"/>
</LinearLayout>
diff --git a/app/src/main/res/layout/search_buttons.xml b/app/src/main/res/layout/search_buttons.xml
deleted file mode 100644
index 699ad341..00000000
--- a/app/src/main/res/layout/search_buttons.xml
+++ /dev/null
@@ -1,73 +0,0 @@
-<?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/app/src/main/res/layout/seekbar_preference.xml b/app/src/main/res/layout/seekbar_preference.xml
index 030b608b..74dad4cd 100644
--- a/app/src/main/res/layout/seekbar_preference.xml
+++ b/app/src/main/res/layout/seekbar_preference.xml
@@ -9,7 +9,8 @@
android:padding="5dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:gravity="right" />
+ android:gravity="right"
+ android:textColor="?android:textColorPrimary"/>
<SeekBar
android:id="@+id/seek_bar"
android:padding="15dip"
diff --git a/app/src/main/res/layout/select_album.xml b/app/src/main/res/layout/select_album.xml
index bbdf0e54..e72ccffd 100644
--- a/app/src/main/res/layout/select_album.xml
+++ b/app/src/main/res/layout/select_album.xml
@@ -13,16 +13,15 @@
<View
android:layout_width="fill_parent"
android:layout_height="1px"
- android:background="@color/dividerColor"/>
+ android:background="?attr/colorPrimary"/>
<include layout="@layout/tab_progress"/>
- <ListView
+ <android.support.v7.widget.RecyclerView
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"/>
+ android:scrollbars="vertical"/>
</LinearLayout>
</android.support.v4.widget.SwipeRefreshLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/select_album_header.xml b/app/src/main/res/layout/select_album_header.xml
index d028a476..5b2294f0 100644
--- a/app/src/main/res/layout/select_album_header.xml
+++ b/app/src/main/res/layout/select_album_header.xml
@@ -30,7 +30,8 @@
android:marqueeRepeatLimit="marquee_forever"
android:scrollHorizontally="true"
android:focusable="true"
- android:focusableInTouchMode="true">
+ android:focusableInTouchMode="true"
+ android:textColor="?android:textColorPrimary">
<requestFocus android:focusable="true"
android:focusableInTouchMode="true"
@@ -44,7 +45,8 @@
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:singleLine="true"
- android:ellipsize="end"/>
+ android:ellipsize="end"
+ android:textColor="?android:textColorPrimary"/>
<TextView
android:text="XX SONGS"
@@ -54,7 +56,8 @@
android:paddingTop="14dip"
android:textAppearance="?android:attr/textAppearanceSmall"
android:singleLine="true"
- android:ellipsize="none"/>
+ android:ellipsize="none"
+ android:textColor="?android:textColorSecondary"/>
<TextView
android:text="0:00"
@@ -63,7 +66,8 @@
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:singleLine="true"
- android:ellipsize="none"/>
+ android:ellipsize="none"
+ android:textColor="?android:textColorSecondary"/>
</LinearLayout>
@@ -145,9 +149,16 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
- android:textAppearance="?android:attr/textAppearanceMedium"/>
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?android:textColorPrimary"/>
</LinearLayout>
</FrameLayout>
+
+ <View
+ android:layout_width="fill_parent"
+ android:layout_height="1px"
+ android:background="?attr/colorPrimary"
+ android:layout_gravity="bottom"/>
</FrameLayout>
diff --git a/app/src/main/res/layout/select_artist_header.xml b/app/src/main/res/layout/select_artist_header.xml
index 2821ce43..9ec94ff1 100644
--- a/app/src/main/res/layout/select_artist_header.xml
+++ b/app/src/main/res/layout/select_artist_header.xml
@@ -2,7 +2,9 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
- android:layout_height="wrap_content">
+ android:layout_height="wrap_content"
+ android:background="?attr/selectableItemBackground">
+
<LinearLayout
android:id="@+id/select_artist_folder"
android:orientation="horizontal"
@@ -30,14 +32,21 @@
android:layout_height="wrap_content"
android:layout_marginLeft="10dip"
android:layout_marginTop="6dip"
- android:textAppearance="?android:attr/textAppearanceLarge"/>
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textColor="?attr/colorAccent"/>
<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"/>
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:textColorSecondary"/>
</LinearLayout>
</LinearLayout>
+
+ <View
+ android:layout_width="fill_parent"
+ android:layout_height="1px"
+ android:background="?attr/colorPrimary"/>
</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/settings_activity.xml b/app/src/main/res/layout/settings_activity.xml
new file mode 100644
index 00000000..c77528b2
--- /dev/null
+++ b/app/src/main/res/layout/settings_activity.xml
@@ -0,0 +1,21 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <android.support.v7.widget.Toolbar
+ android:id="@+id/main_toolbar"
+ android:layout_height="?attr/actionBarSize"
+ android:layout_width="match_parent"
+ android:background="?attr/colorPrimary"
+ android:elevation="4dp"
+ app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
+ app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
+
+ <FrameLayout
+ android:id="@+id/fragment_container"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
+</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/shuffle_dialog.xml b/app/src/main/res/layout/shuffle_dialog.xml
index 295f57cb..63778ed7 100644
--- a/app/src/main/res/layout/shuffle_dialog.xml
+++ b/app/src/main/res/layout/shuffle_dialog.xml
@@ -14,7 +14,8 @@
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:textSize="20dp"
- android:text="@string/shuffle.startYear" />
+ android:text="@string/shuffle.startYear"
+ android:textColor="?android:textColorPrimary"/>
<EditText
android:id="@+id/start_year"
android:inputType="number"
@@ -36,7 +37,8 @@
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:textSize="20dp"
- android:text="@string/shuffle.endYear" />
+ android:text="@string/shuffle.endYear"
+ android:textColor="?android:textColorPrimary"/>
<EditText
android:id="@+id/end_year"
android:inputType="number"
@@ -66,7 +68,8 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginLeft="4dp"
- android:hint="@string/shuffle.genre"/>
+ android:hint="@string/shuffle.genre"
+ android:textColor="?android:textColorPrimary"/>
<Button
android:id="@+id/genre_combo"
diff --git a/app/src/main/res/layout/song_list_item.xml b/app/src/main/res/layout/song_list_item.xml
index 86f77869..429dc142 100644
--- a/app/src/main/res/layout/song_list_item.xml
+++ b/app/src/main/res/layout/song_list_item.xml
@@ -3,15 +3,8 @@
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"/>
+ android:layout_height="?android:attr/listPreferredItemHeight"
+ android:background="?attr/selectableItemBackground">
<LinearLayout android:orientation="vertical"
android:layout_width="0dip"
@@ -25,24 +18,24 @@
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"/>
+ 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"
+ android:textColor="?android:textColorPrimary"/>
<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"
@@ -50,10 +43,10 @@
<ImageButton
android:id="@+id/song_star"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_width="@dimen/Star.Small"
+ android:layout_height="@dimen/Star.Small"
+ android:scaleType="fitCenter"
android:layout_gravity="right|center_vertical"
- android:src="@drawable/ic_stat_star"
android:background="@null"
android:focusable="false"
android:visibility="gone"/>
@@ -91,7 +84,8 @@
android:textAppearance="?android:attr/textAppearanceSmall"
android:singleLine="true"
android:ellipsize="middle"
- android:paddingLeft="6dip"/>
+ android:paddingLeft="6dip"
+ android:textColor="?android:textColorSecondary"/>
<RatingBar
android:id="@+id/song_rating"
@@ -111,13 +105,14 @@
android:textAppearance="?android:attr/textAppearanceSmall"
android:singleLine="true"
android:paddingLeft="3dip"
- android:paddingRight="4dip"/>
+ android:paddingRight="4dip"
+ android:textColor="?android:textColorSecondary"/>
</LinearLayout>
</LinearLayout>
<ImageView
- android:id="@+id/artist_more"
+ android:id="@+id/more_button"
android:src="?attr/download_none"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
diff --git a/app/src/main/res/layout/start_timer.xml b/app/src/main/res/layout/start_timer.xml
index 9736a31d..59bd60e3 100644
--- a/app/src/main/res/layout/start_timer.xml
+++ b/app/src/main/res/layout/start_timer.xml
@@ -11,7 +11,8 @@
android:layout_marginLeft="8dp"
android:textSize="20dp"
android:paddingRight="10px"
- android:layout_gravity="center"/>
+ android:layout_gravity="center"
+ android:textColor="?android:textColorPrimary"/>
<SeekBar
android:id="@+id/timer_length_bar"
diff --git a/app/src/main/res/layout/sync_dialog.xml b/app/src/main/res/layout/sync_dialog.xml
index 5133b753..fc657186 100644
--- a/app/src/main/res/layout/sync_dialog.xml
+++ b/app/src/main/res/layout/sync_dialog.xml
@@ -8,5 +8,6 @@
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:checked="false"
- android:text="@string/offline.sync_dialog_default"/>
+ android:text="@string/offline.sync_dialog_default"
+ android:textColor="?android:textColorPrimary"/>
</FrameLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/tab_progress.xml b/app/src/main/res/layout/tab_progress.xml
index f6d326ca..7423777f 100644
--- a/app/src/main/res/layout/tab_progress.xml
+++ b/app/src/main/res/layout/tab_progress.xml
@@ -26,6 +26,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
- android:textAppearance="?android:attr/textAppearanceMedium"/>
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?android:textColorPrimary"/>
</LinearLayout>
</FrameLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/unscrollable_grid_view.xml b/app/src/main/res/layout/unscrollable_grid_view.xml
deleted file mode 100644
index 96bea5ce..00000000
--- a/app/src/main/res/layout/unscrollable_grid_view.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<?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/app/src/main/res/layout/update_playlist.xml b/app/src/main/res/layout/update_playlist.xml
index 7354ef5c..cc7e5ee6 100644
--- a/app/src/main/res/layout/update_playlist.xml
+++ b/app/src/main/res/layout/update_playlist.xml
@@ -22,7 +22,8 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginLeft="4dp"
- android:hint="@string/common.name" />
+ android:hint="@string/common.name"
+ android:textColor="?android:textColorPrimary"/>
</LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
@@ -36,7 +37,8 @@
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:textSize="20dp"
- android:text="@string/common.comment" />
+ android:text="@string/common.comment"
+ android:textColor="?android:textColorPrimary"/>
<EditText
android:id="@+id/get_playlist_comment"
android:inputType="text"
@@ -58,7 +60,8 @@
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:textSize="20dp"
- android:text="@string/common.public" />
+ android:text="@string/common.public"
+ android:textColor="?android:textColorPrimary"/>
<CheckBox
android:id="@+id/get_playlist_public"
android:layout_width="fill_parent"
diff --git a/app/src/main/res/layout/update_share.xml b/app/src/main/res/layout/update_share.xml
index 92b7137b..ef44e304 100644
--- a/app/src/main/res/layout/update_share.xml
+++ b/app/src/main/res/layout/update_share.xml
@@ -14,7 +14,8 @@
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:textSize="20dp"
- android:text="@string/common.name" />
+ android:text="@string/common.name"
+ android:textColor="?android:textColorPrimary"/>
<EditText
android:id="@+id/get_share_name"
android:inputType="text"
@@ -36,7 +37,8 @@
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:textSize="20dp"
- android:text="@string/share.expiration" />
+ android:text="@string/share.expiration"
+ android:textColor="?android:textColorPrimary"/>
<DatePicker
android:id="@+id/get_share_expire"
android:layout_width="fill_parent"
@@ -57,7 +59,8 @@
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:textSize="20dp"
- android:text="@string/share.no_expiration" />
+ android:text="@string/share.no_expiration"
+ android:textColor="?android:textColorPrimary"/>
<CheckBox
android:id="@+id/get_share_no_expire"
android:layout_width="fill_parent"
diff --git a/app/src/main/res/layout/user_header.xml b/app/src/main/res/layout/user_header.xml
index 0b303afe..c8186372 100644
--- a/app/src/main/res/layout/user_header.xml
+++ b/app/src/main/res/layout/user_header.xml
@@ -34,7 +34,8 @@
android:marqueeRepeatLimit="marquee_forever"
android:scrollHorizontally="true"
android:focusable="true"
- android:focusableInTouchMode="true">
+ android:focusableInTouchMode="true"
+ android:textColor="?android:textColorPrimary">
<requestFocus android:focusable="true"
android:focusableInTouchMode="true"
@@ -49,9 +50,16 @@
android:textAppearance="?android:attr/textAppearanceMedium"
android:singleLine="true"
android:ellipsize="end"
- android:autoLink="email"/>
+ android:autoLink="email"
+ android:textColor="?android:textColorSecondary"/>
</LinearLayout>
+
+ <View
+ android:layout_width="fill_parent"
+ android:layout_height="1px"
+ android:background="?attr/colorPrimary"
+ android:layout_alignParentBottom="true"/>
</RelativeLayout>
diff --git a/app/src/main/res/layout/user_list_item.xml b/app/src/main/res/layout/user_list_item.xml
index dc2bdab9..c2e708c8 100644
--- a/app/src/main/res/layout/user_list_item.xml
+++ b/app/src/main/res/layout/user_list_item.xml
@@ -3,7 +3,7 @@
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:background="@android:color/transparent">
+ android:background="?attr/selectableItemBackground">
<github.daneren2005.dsub.view.RecyclingImageView
android:id="@+id/item_avatar"
@@ -22,14 +22,15 @@
android:paddingLeft="12dip"
android:paddingRight="6dip"
android:minHeight="50dip"
- android:background="@android:color/transparent"/>
+ android:background="@android:color/transparent"
+ android:textColor="?android:textColorPrimary"/>
<ImageButton
android:id="@+id/item_star"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_width="@dimen/Star.Small"
+ android:layout_height="@dimen/Star.Small"
+ android:scaleType="fitCenter"
android:layout_gravity="right|center_vertical"
- android:src="@drawable/ic_stat_star"
android:background="@android:color/transparent"
android:focusable="false"
android:visibility="gone"/>
diff --git a/app/src/main/res/menu/abstract_top_menu.xml b/app/src/main/res/menu/abstract_top_menu.xml
index 22499ae9..7c8d414d 100644
--- a/app/src/main/res/menu/abstract_top_menu.xml
+++ b/app/src/main/res/menu/abstract_top_menu.xml
@@ -2,7 +2,7 @@
<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:id="@+id/menu_global_search"
android:icon="?attr/search"
android:title="@string/menu.search"
compat:showAsAction="always|withText"/>
diff --git a/app/src/main/res/menu/drawer_navigation.xml b/app/src/main/res/menu/drawer_navigation.xml
new file mode 100644
index 00000000..88ecd920
--- /dev/null
+++ b/app/src/main/res/menu/drawer_navigation.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <group android:checkableBehavior="single">
+ <item
+ android:id="@+id/drawer_home"
+ android:icon="?attr/drawerHome"
+ android:title="@string/button_bar.home"/>
+ <item
+ android:id="@+id/drawer_library"
+ android:icon="?attr/drawerLibrary"
+ android:title="@string/button_bar.browse"/>
+ <item
+ android:id="@+id/drawer_playlists"
+ android:icon="?attr/drawerPlaylists"
+ android:title="@string/button_bar.playlists"/>
+ <item
+ android:id="@+id/drawer_podcasts"
+ android:icon="?attr/drawerPodcasts"
+ android:title="@string/button_bar.podcasts"/>
+ <item
+ android:id="@+id/drawer_bookmarks"
+ android:icon="?attr/drawerBookmarks"
+ android:title="@string/button_bar.bookmarks"/>
+ <item
+ android:id="@+id/drawer_shares"
+ android:icon="?attr/drawerShares"
+ android:title="@string/button_bar.shares"/>
+ <item
+ android:id="@+id/drawer_chat"
+ android:icon="?attr/drawerChat"
+ android:title="@string/button_bar.chat"/>
+ <item
+ android:id="@+id/drawer_admin"
+ android:icon="?attr/drawerAdmin"
+ android:title="@string/button_bar.admin"/>
+ <item
+ android:id="@+id/drawer_downloading"
+ android:icon="?attr/drawerDownloading"
+ android:title="@string/button_bar.downloading"
+ android:visible="false"/>
+ </group>
+
+ <group
+ android:id="@+id/drawer_bottom"
+ android:checkableBehavior="single">
+
+ <item
+ android:id="@+id/drawer_settings"
+ android:title="@string/menu.settings"/>
+ </group>
+</menu> \ No newline at end of file
diff --git a/app/src/main/res/menu/main.xml b/app/src/main/res/menu/main.xml
index 549c5fb6..0970c8ce 100644
--- a/app/src/main/res/menu/main.xml
+++ b/app/src/main/res/menu/main.xml
@@ -2,13 +2,13 @@
<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:id="@+id/menu_global_search"
android:icon="?attr/search"
android:title="@string/menu.search"
compat:showAsAction="always|withText"/>
<item
- android:id="@+id/menu_shuffle"
+ android:id="@+id/menu_global_shuffle"
android:icon="?attr/shuffle"
android:title="@string/menu.shuffle"
compat:showAsAction="always|withText"/>
diff --git a/app/src/main/res/menu/multiselect_media.xml b/app/src/main/res/menu/multiselect_media.xml
new file mode 100644
index 00000000..75865a14
--- /dev/null
+++ b/app/src/main/res/menu/multiselect_media.xml
@@ -0,0 +1,47 @@
+<?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_play_now"
+ android:icon="?attr/actionbar_start"
+ android:title="@string/menu.play"
+ compat:showAsAction="always|withText"/>
+
+ <item
+ android:id="@+id/menu_shuffle"
+ android:icon="?attr/shuffle"
+ android:title="@string/menu.shuffle"
+ compat:showAsAction="ifRoom|withText"/>
+
+ <item
+ android:id="@+id/menu_download"
+ android:title="@string/common.download"/>
+
+ <item
+ android:id="@+id/menu_cache"
+ android:title="@string/common.pin"/>
+
+ <item
+ android:id="@+id/menu_delete"
+ android:title="@string/menu.delete_cache"/>
+
+ <item
+ android:id="@+id/menu_add_playlist"
+ android:title="@string/menu.add_playlist"/>
+
+ <item
+ android:id="@+id/menu_remove_playlist"
+ android:title="@string/menu.remove_playlist"/>
+
+ <group android:id="@+id/hide_play_next">
+ <item
+ android:id="@+id/menu_play_next"
+ android:title="@string/common.play_next"/>
+ </group>
+
+ <group android:id="@+id/hide_play_last">
+ <item
+ android:id="@+id/menu_play_last"
+ android:title="@string/menu.play_last"/>
+ </group>
+</menu>
diff --git a/app/src/main/res/menu/multiselect_media_offline.xml b/app/src/main/res/menu/multiselect_media_offline.xml
new file mode 100644
index 00000000..5587c106
--- /dev/null
+++ b/app/src/main/res/menu/multiselect_media_offline.xml
@@ -0,0 +1,31 @@
+<?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_play_now"
+ android:icon="?attr/actionbar_start"
+ android:title="@string/menu.play"
+ compat:showAsAction="always|withText"/>
+
+ <item
+ android:id="@+id/menu_shuffle"
+ android:icon="?attr/shuffle"
+ android:title="@string/menu.shuffle"
+ compat:showAsAction="ifRoom|withText"/>
+
+ <item
+ android:id="@+id/menu_delete"
+ android:title="@string/menu.delete_cache"/>
+
+ <group android:id="@+id/hide_play_next">
+ <item
+ android:id="@+id/menu_play_next"
+ android:title="@string/common.play_next"/>
+ </group>
+
+ <group android:id="@+id/hide_play_last">
+ <item
+ android:id="@+id/menu_play_last"
+ android:title="@string/menu.play_last"/>
+ </group>
+</menu>
diff --git a/app/src/main/res/menu/nowplaying_context.xml b/app/src/main/res/menu/nowplaying_context.xml
index c9347353..845a98c8 100644
--- a/app/src/main/res/menu/nowplaying_context.xml
+++ b/app/src/main/res/menu/nowplaying_context.xml
@@ -19,10 +19,6 @@
android:title="@string/download.menu_lyrics"/>
<item
- android:id="@+id/menu_remove"
- android:title="@string/download.menu_remove"/>
-
- <item
android:id="@+id/menu_delete"
android:title="@string/menu.delete_cache"/>
diff --git a/app/src/main/res/menu/nowplaying_context_offline.xml b/app/src/main/res/menu/nowplaying_context_offline.xml
index 24b23a8d..9ce63d79 100644
--- a/app/src/main/res/menu/nowplaying_context_offline.xml
+++ b/app/src/main/res/menu/nowplaying_context_offline.xml
@@ -15,10 +15,6 @@
android:title="@string/download.menu_show_album"/>
<item
- android:id="@+id/menu_remove"
- android:title="@string/download.menu_remove"/>
-
- <item
android:id="@+id/menu_delete"
android:title="@string/menu.delete_cache"/>
diff --git a/app/src/main/res/menu/search.xml b/app/src/main/res/menu/search.xml
index cab9c4f6..e9377d68 100644
--- a/app/src/main/res/menu/search.xml
+++ b/app/src/main/res/menu/search.xml
@@ -2,7 +2,7 @@
<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:id="@+id/menu_global_search"
android:icon="?attr/search"
android:title="@string/menu.search"
compat:showAsAction="ifRoom|withText"/>
diff --git a/app/src/main/res/menu/select_album.xml b/app/src/main/res/menu/select_album.xml
index 3d2228e8..c94a4a3f 100644
--- a/app/src/main/res/menu/select_album.xml
+++ b/app/src/main/res/menu/select_album.xml
@@ -3,7 +3,7 @@
xmlns:compat="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/menu_play_now"
- android:icon="?media_button_start"
+ android:icon="?actionbar_start"
android:title="@string/menu.play"
compat:showAsAction="always|withText"/>
diff --git a/app/src/main/res/menu/select_album_context.xml b/app/src/main/res/menu/select_album_context.xml
index 5b2529e7..388fd1f5 100644
--- a/app/src/main/res/menu/select_album_context.xml
+++ b/app/src/main/res/menu/select_album_context.xml
@@ -1,48 +1,10 @@
<?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">
-
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/album_menu_info"
android:title="@string/common.info"/>
- <item
- android:id="@+id/album_menu_play_now"
- android:title="@string/common.play_now"
- />
-
- <item
- android:id="@+id/album_menu_play_shuffled"
- android:title="@string/common.play_shuffled"
- />
-
- <group android:id="@+id/hide_play_next">
- <item
- android:id="@+id/album_menu_play_next"
- android:title="@string/common.play_next"/>
- </group>
-
- <group android:id="@+id/hide_play_last">
- <item
- android:id="@+id/album_menu_play_last"
- android:title="@string/common.play_last"/>
- </group>
-
- <item
- android:id="@+id/album_menu_download"
- android:title="@string/common.download"
- />
-
- <item
- android:id="@+id/album_menu_pin"
- android:title="@string/common.pin"
- />
-
- <item
- android:id="@+id/album_menu_delete"
- android:title="@string/menu.delete_cache"/>
-
<item
android:id="@+id/album_menu_show_artist"
android:title="@string/menu.show_artist"/>
diff --git a/app/src/main/res/menu/select_album_context_offline.xml b/app/src/main/res/menu/select_album_context_offline.xml
index a1805f5b..60858d91 100644
--- a/app/src/main/res/menu/select_album_context_offline.xml
+++ b/app/src/main/res/menu/select_album_context_offline.xml
@@ -1,31 +1,9 @@
<?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/album_menu_play_now"
- android:title="@string/common.play_now"
- />
-
- <item
- android:id="@+id/album_menu_play_shuffled"
- android:title="@string/common.play_shuffled"
- />
-
- <group android:id="@+id/hide_play_next">
- <item
- android:id="@+id/album_menu_play_next"
- android:title="@string/common.play_next"/>
- </group>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
- <group android:id="@+id/hide_play_last">
- <item
- android:id="@+id/album_menu_play_last"
- android:title="@string/common.play_last"/>
- </group>
-
- <item
- android:id="@+id/album_menu_delete"
- android:title="@string/menu.delete_cache"/>
+ <item
+ android:id="@+id/album_menu_info"
+ android:title="@string/common.info"/>
<item
android:id="@+id/album_menu_star"
diff --git a/app/src/main/res/menu/select_album_list.xml b/app/src/main/res/menu/select_album_list.xml
index a9196d1c..17148a51 100644
--- a/app/src/main/res/menu/select_album_list.xml
+++ b/app/src/main/res/menu/select_album_list.xml
@@ -3,7 +3,7 @@
xmlns:compat="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/menu_play_now"
- android:icon="?media_button_start"
+ android:icon="?actionbar_start"
android:title="@string/menu.play"
compat:showAsAction="always|withText"/>
diff --git a/app/src/main/res/menu/select_artist.xml b/app/src/main/res/menu/select_artist.xml
index 603f0a3b..66ba37ba 100644
--- a/app/src/main/res/menu/select_artist.xml
+++ b/app/src/main/res/menu/select_artist.xml
@@ -2,13 +2,13 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:compat="http://schemas.android.com/apk/res-auto">
<item
- android:id="@+id/menu_shuffle"
+ android:id="@+id/menu_global_shuffle"
android:icon="?attr/shuffle"
android:title="@string/menu.shuffle"
compat:showAsAction="always|withText"/>
<item
- android:id="@+id/menu_search"
+ android:id="@+id/menu_global_search"
android:icon="?attr/search"
android:title="@string/menu.search"
compat:showAsAction="ifRoom|withText"/>
diff --git a/app/src/main/res/menu/select_artist_context.xml b/app/src/main/res/menu/select_artist_context.xml
index debc07c6..b8bce32d 100644
--- a/app/src/main/res/menu/select_artist_context.xml
+++ b/app/src/main/res/menu/select_artist_context.xml
@@ -1,43 +1,5 @@
<?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/artist_menu_play_now"
- android:title="@string/common.play_now"
- />
-
- <item
- android:id="@+id/artist_menu_play_shuffled"
- android:title="@string/common.play_shuffled"
- />
-
- <group android:id="@+id/hide_play_next">
- <item
- android:id="@+id/artist_menu_play_next"
- android:title="@string/common.play_next"/>
- </group>
-
- <group android:id="@+id/hide_play_last">
- <item
- android:id="@+id/artist_menu_play_last"
- android:title="@string/common.play_last"/>
- </group>
-
- <item
- android:id="@+id/artist_menu_download"
- android:title="@string/common.download"
- />
-
- <item
- android:id="@+id/artist_menu_pin"
- android:title="@string/common.pin"
- />
-
- <item
- android:id="@+id/artist_menu_delete"
- android:title="@string/menu.delete_cache"/>
-
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
<group android:id="@+id/server_1_10">
<group android:id="@+id/hide_star">
<item
diff --git a/app/src/main/res/menu/select_artist_context_offline.xml b/app/src/main/res/menu/select_artist_context_offline.xml
deleted file mode 100644
index 17ee97e0..00000000
--- a/app/src/main/res/menu/select_artist_context_offline.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?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/artist_menu_play_now"
- android:title="@string/common.play_now"
- />
-
- <item
- android:id="@+id/artist_menu_play_shuffled"
- android:title="@string/common.play_shuffled"
- />
-
- <group android:id="@+id/hide_play_next">
- <item
- android:id="@+id/artist_menu_play_next"
- android:title="@string/common.play_next"/>
- </group>
-
- <group android:id="@+id/hide_play_last">
- <item
- android:id="@+id/artist_menu_play_last"
- android:title="@string/common.play_last"/>
- </group>
-
- <item
- android:id="@+id/artist_menu_delete"
- android:title="@string/menu.delete_cache"/>
-</menu>
diff --git a/app/src/main/res/menu/select_bookmark_context.xml b/app/src/main/res/menu/select_bookmark_context.xml
index 2b1b83fd..d52db105 100644
--- a/app/src/main/res/menu/select_bookmark_context.xml
+++ b/app/src/main/res/menu/select_bookmark_context.xml
@@ -15,18 +15,6 @@
android:title="@string/menu.show_artist"/>
<item
- android:id="@+id/song_menu_download"
- android:title="@string/common.download"/>
-
- <item
- android:id="@+id/song_menu_pin"
- android:title="@string/common.pin"/>
-
- <item
- android:id="@+id/song_menu_delete"
- android:title="@string/menu.delete_cache"/>
-
- <item
android:id="@+id/bookmark_menu_delete"
android:title="@string/bookmark.delete"/>
</menu>
diff --git a/app/src/main/res/menu/select_playlist_context.xml b/app/src/main/res/menu/select_playlist_context.xml
index 47033d9c..4941e94b 100644
--- a/app/src/main/res/menu/select_playlist_context.xml
+++ b/app/src/main/res/menu/select_playlist_context.xml
@@ -7,21 +7,6 @@
android:title="@string/common.info"
/>
- <item
- android:id="@+id/playlist_menu_play_now"
- android:title="@string/common.play_now"
- />
-
- <item
- android:id="@+id/playlist_menu_play_shuffled"
- android:title="@string/common.play_shuffled"
- />
-
- <item
- android:id="@+id/playlist_menu_download"
- android:title="@string/common.download"
- />
-
<item
android:id="@+id/playlist_menu_sync"
android:title="@string/menu.keep_synced"/>
diff --git a/app/src/main/res/menu/select_playlist_context_offline.xml b/app/src/main/res/menu/select_playlist_context_offline.xml
index d63aec17..6745d850 100644
--- a/app/src/main/res/menu/select_playlist_context_offline.xml
+++ b/app/src/main/res/menu/select_playlist_context_offline.xml
@@ -1,13 +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/playlist_menu_play_now"
- android:title="@string/common.play_now"
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:id="@+id/playlist_info"
+ android:title="@string/common.info"
/>
-
- <item
- android:id="@+id/playlist_menu_play_shuffled"
- android:title="@string/common.play_shuffled"
- />
</menu> \ No newline at end of file
diff --git a/app/src/main/res/menu/select_podcast_episode_context.xml b/app/src/main/res/menu/select_podcast_episode_context.xml
index bacccda3..31f2a051 100644
--- a/app/src/main/res/menu/select_podcast_episode_context.xml
+++ b/app/src/main/res/menu/select_podcast_episode_context.xml
@@ -7,7 +7,7 @@
android:title="@string/common.info"
/>
- <item
+ <item
android:id="@+id/song_menu_play_now"
android:title="@string/common.play_now"
/>
@@ -23,16 +23,16 @@
android:id="@+id/song_menu_play_last"
android:title="@string/common.play_last"/>
</group>
-
- <item
+
+ <item
android:id="@+id/song_menu_download"
android:title="@string/common.download"
/>
-
- <item
+
+ <item
android:id="@+id/song_menu_delete"
android:title="@string/menu.delete_cache"/>
-
+
<group android:id="@+id/server_1.9">
<item
android:id="@+id/bookmark_menu_delete"
diff --git a/app/src/main/res/menu/select_podcast_episode_context_offline.xml b/app/src/main/res/menu/select_podcast_episode_context_offline.xml
index 587d01f7..301746f4 100644
--- a/app/src/main/res/menu/select_podcast_episode_context_offline.xml
+++ b/app/src/main/res/menu/select_podcast_episode_context_offline.xml
@@ -6,8 +6,8 @@
android:id="@+id/song_menu_info"
android:title="@string/common.info"
/>
-
- <item
+
+ <item
android:id="@+id/song_menu_play_now"
android:title="@string/common.play_now"
/>
@@ -23,8 +23,8 @@
android:id="@+id/song_menu_play_last"
android:title="@string/common.play_last"/>
</group>
-
- <item
+
+ <item
android:id="@+id/song_menu_delete"
android:title="@string/menu.delete_cache"/>
</menu>
diff --git a/app/src/main/res/menu/select_podcasts.xml b/app/src/main/res/menu/select_podcasts.xml
index f30429ce..41ad62fa 100644
--- a/app/src/main/res/menu/select_podcasts.xml
+++ b/app/src/main/res/menu/select_podcasts.xml
@@ -2,7 +2,7 @@
<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:id="@+id/menu_global_search"
android:icon="?attr/search"
android:title="@string/menu.search"
compat:showAsAction="always|withText"/>
diff --git a/app/src/main/res/menu/select_song.xml b/app/src/main/res/menu/select_song.xml
index fc4494cb..5718866a 100644
--- a/app/src/main/res/menu/select_song.xml
+++ b/app/src/main/res/menu/select_song.xml
@@ -3,7 +3,7 @@
xmlns:compat="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/menu_play_now"
- android:icon="?attr/media_button_start"
+ android:icon="?attr/actionbar_start"
android:title="@string/menu.play"
compat:showAsAction="always|withText"/>
@@ -32,14 +32,6 @@
<item
android:id="@+id/menu_delete"
android:title="@string/menu.delete_cache"/>
-
- <item
- android:id="@+id/menu_add_playlist"
- android:title="@string/menu.add_playlist"/>
-
- <item
- android:id="@+id/menu_remove_playlist"
- android:title="@string/menu.remove_playlist"/>
<group android:id="@+id/hide_play_next">
<item
diff --git a/app/src/main/res/menu/select_song_context.xml b/app/src/main/res/menu/select_song_context.xml
index d8fc211c..eae8a8bc 100644
--- a/app/src/main/res/menu/select_song_context.xml
+++ b/app/src/main/res/menu/select_song_context.xml
@@ -6,37 +6,6 @@
android:id="@+id/song_menu_info"
android:title="@string/common.info"
/>
-
- <item
- android:id="@+id/song_menu_play_now"
- android:title="@string/common.play_now"
- />
-
- <group android:id="@+id/hide_play_next">
- <item
- android:id="@+id/song_menu_play_next"
- android:title="@string/common.play_next"/>
- </group>
-
- <group android:id="@+id/hide_play_last">
- <item
- android:id="@+id/song_menu_play_last"
- android:title="@string/common.play_last"/>
- </group>
-
- <item
- android:id="@+id/song_menu_download"
- android:title="@string/common.download"
- />
-
- <item
- android:id="@+id/song_menu_pin"
- android:title="@string/common.pin"
- />
-
- <item
- android:id="@+id/song_menu_delete"
- android:title="@string/menu.delete_cache"/>
<group android:id="@+id/server_1.8">
<item
diff --git a/app/src/main/res/menu/select_song_context_offline.xml b/app/src/main/res/menu/select_song_context_offline.xml
index 49445876..77903fb0 100644
--- a/app/src/main/res/menu/select_song_context_offline.xml
+++ b/app/src/main/res/menu/select_song_context_offline.xml
@@ -6,27 +6,6 @@
android:id="@+id/song_menu_info"
android:title="@string/common.info"
/>
-
- <item
- android:id="@+id/song_menu_play_now"
- android:title="@string/common.play_now"
- />
-
- <group android:id="@+id/hide_play_next">
- <item
- android:id="@+id/song_menu_play_next"
- android:title="@string/common.play_next"/>
- </group>
-
- <group android:id="@+id/hide_play_last">
- <item
- android:id="@+id/song_menu_play_last"
- android:title="@string/common.play_last"/>
- </group>
-
- <item
- android:id="@+id/song_menu_delete"
- android:title="@string/menu.delete_cache"/>
<item
android:id="@+id/song_menu_star"
diff --git a/app/src/main/res/menu/select_song_offline.xml b/app/src/main/res/menu/select_song_offline.xml
index c45405fb..7a4a5407 100644
--- a/app/src/main/res/menu/select_song_offline.xml
+++ b/app/src/main/res/menu/select_song_offline.xml
@@ -3,7 +3,7 @@
xmlns:compat="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/menu_play_now"
- android:icon="?attr/media_button_start"
+ android:icon="?attr/actionbar_start"
android:title="@string/menu.play"
compat:showAsAction="always|withText"/>
diff --git a/app/src/main/res/menu/similar_artists.xml b/app/src/main/res/menu/similar_artists.xml
index 771555b6..f6c30fb2 100644
--- a/app/src/main/res/menu/similar_artists.xml
+++ b/app/src/main/res/menu/similar_artists.xml
@@ -4,7 +4,7 @@
<item
android:id="@+id/menu_play_now"
- android:icon="?media_button_start"
+ android:icon="?actionbar_start"
android:title="@string/menu.play"
compat:showAsAction="always|withText"/>
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 25f1b57c..45b89df9 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -42,13 +42,7 @@
<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_title">FAQ</string>
<string name="main.faq_text">
<![CDATA[
<font color="red">Cache vs Permanenter Cache</font>:
@@ -142,8 +136,7 @@
<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.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>
@@ -190,8 +183,7 @@
<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_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>
@@ -214,7 +206,6 @@
<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>
@@ -577,6 +568,74 @@
<item quantity="one">Noch ein Tag bis zum Ablauf des Testzeitraums.</item>
<item quantity="other">%d Tage bis zum Ablauf des Testzeitraums.</item>
</plurals>
+ <string name="details.album">Album</string>
+ <string name="details.artist">Künstler</string>
+ <string name="details.author">Autor</string>
+ <string name="details.available_space">Verfügbarer Platz</string>
+ <string name="details.bookmark_position">Lesezeichenposition</string>
+ <string name="details.comments">Kommentar</string>
+ <string name="details.created">Erstellt</string>
+ <string name="details.description">Beschreibung</string>
+ <string name="details.email">Email</string>
+ <string name="details.error">Fehlermeldung</string>
+ <string name="details.length">Länge</string>
+ <string name="details.of">%1$s von %2$s</string>
+ <string name="details.owner">Besitzer</string>
+ <string name="details.public">Öffentlich</string>
+ <string name="details.podcast">Podcast</string>
+ <string name="details.size">Größe</string>
+ <string name="details.status">Status</string>
+ <string name="details.title">Titel</string>
+ <string name="download.stop_time_remaining">Stoppe in %1$s</string>
+ <string name="playlist.shared">Geteilte Wiedergabelisten</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_32">32 Kbps</string>
+ <string name="settings.max_bitrate_320">320 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_video_bitrate_1000">1000 Kbps</string>
+ <string name="settings.max_video_bitrate_1500">1500 Kbps</string>
+ <string name="settings.max_video_bitrate_200">200 Kbps</string>
+ <string name="settings.max_video_bitrate_2000">2000 Kbps</string>
+ <string name="settings.max_video_bitrate_300">300 Kbps</string>
+ <string name="settings.max_video_bitrate_3000">3000 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_5000">5000 Kbps</string>
+ <string name="settings.max_video_bitrate_700">700 Kbps</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="util.bytes_format.megabyte">0.00 MB</string>
+ <string name="util.bytes_format.kilobyte">0 KB</string>
+ <string name="util.bytes_format.gigabyte">0.00 GB</string>
+ <string name="util.bytes_format.byte">0 B</string>
+ <string name="common.close">Schliessen</string>
+ <string name="details.cached_format">Format im Cache</string>
+ <string name="details.genre">Genre</string>
+ <string name="details.cached_bitrate">Bitrate im Cache</string>
+ <string name="details.files_cached">Dateien im Cache</string>
+ <string name="details.files_permanent">Dateien im permanenten Cache</string>
+ <string name="details.rating">Bewertung</string>
+ <string name="details.server_bitrate">Originale Bitrate</string>
+ <string name="details.server_format">Originales Format</string>
+ <string name="details.song_count">Titelanzahl</string>
+ <string name="details.title.playlist">Details der Wiedergabeliste</string>
+ <string name="details.title.album">Albumdetails</string>
+ <string name="details.title.podcast">Podcastdetails</string>
+ <string name="details.title.song">Mediumdetails</string>
+ <string name="details.used_space">Genutzter Platz</string>
+ <string name="details.url">URL</string>
+ <string name="details.track">Medium</string>
+ <string name="details.year">Jahr</string>
+ <string name="details.version">Version</string>
+ <string name="playlist.mine">Meine Wiedergabelisten</string>
<string name="settings.keep_played_count_none">Alle abgespielten Lieder entfernen</string>
<string name="settings.keep_played_count_one">Das letzte abgespielte Lied behalten</string>
<string name="settings.keep_played_count_three">3 abgespielte Lieder behalten</string>
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index cd255e13..f56e939e 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -41,13 +41,7 @@
<string name="main.welcome_title">Bienvenido!</string>
<string name="main.welcome_text">Bienvenido a DSub! Ahora la aplicación está configurada para usar el servidor de demostración de Subsonic. Cuando configures tu servidor personal (disponible en <b>subsonic.org</b>), accede a <b>Preferencias</b> y cambia la configuración para conectarte.</string>
<string name="main.about_title">Acerca de DSub</string>
- <string name="main.about_text">Autor: Scott Jackson
- \nEmail: dsub.android@gmail.com
- \nVersión: %1$s
- \nArchivos en caché: %2$s
- \nEspacio usado: %3$s of %4$s
- \nEspacio disponible: %5$s of %6$s</string>
- <string name="main.select_server">Seleccionar servidor</string>
+ <string name="main.select_server">Seleccionar servidor</string>
<string name="main.shuffle">Reproducción aleatoria</string>
<string name="main.offline">Modo Offline</string>
<string name="main.online">Modo Online</string>
@@ -130,8 +124,7 @@
<string name="select_album.select">Seleccionar todo</string>
<string name="select_album.n_selected">Seleccionadas %d canciones</string>
- <string name="select_album.n_unselected">%d canciones deseleccionadas</string>
- <string name="select_album.more">Más</string>
+ <string name="select_album.more">Más</string>
<string name="select_album.offline">Offline</string>
<string name="select_album.searching">Buscando...</string>
<string name="select_album.no_sdcard">Error: No hay tarjeta SD disponible</string>
@@ -176,8 +169,7 @@
<string name="download.playerstate_playing_shuffle">Reproduciendo en aleatorio</string>
<string name="download.menu_show_album">Mostrar disco</string>
<string name="download.menu_lyrics">Letras</string>
- <string name="download.menu_remove">Eliminar de la cola</string>
- <string name="download.menu_remove_all">Borrar todo</string>
+ <string name="download.menu_remove_all">Borrar todo</string>
<string name="download.menu_screen_on">Pantalla encendida</string>
<string name="download.menu_shuffle">Aleatorio</string>
<string name="download.menu_toggle">Cambiar</string>
@@ -199,7 +191,6 @@
<string name="download.jukebox_not_authorized">Control remoto no permitido. Por favor, active el modo jukebox en <b>Users &gt; Settings</b> en su servidor Subsonic.</string>
<string name="download.timer_length">Temporizador</string>
<string name="download.start_timer">Iniciar temporizador</string>
- <string name="download.stop_timer">Detener temporizador</string>
<string name="download.need_download">El vídeo ha de ser descargado antes</string>
<string name="download.no_streaming_player">Ningún reproductor puede reproducir este stream</string>
<string name="download.playing_out_of">%1$d/%2$d</string>
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 759e3fd8..4674ca32 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -41,12 +41,6 @@
<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[
@@ -129,8 +123,7 @@
<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.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>
@@ -175,8 +168,7 @@
<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_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>
@@ -199,7 +191,6 @@
<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>
diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml
index 6222989a..91cdf202 100644
--- a/app/src/main/res/values-hu/strings.xml
+++ b/app/src/main/res/values-hu/strings.xml
@@ -42,12 +42,6 @@
<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[
@@ -139,8 +133,7 @@
<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.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>
@@ -186,8 +179,7 @@
<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_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>
@@ -210,7 +202,6 @@
<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>
diff --git a/app/src/main/res/values-large/dimens.xml b/app/src/main/res/values-large/dimens.xml
index b08dda86..fe8f1a6c 100644
--- a/app/src/main/res/values-large/dimens.xml
+++ b/app/src/main/res/values-large/dimens.xml
@@ -4,4 +4,6 @@
<dimen name="Button.Small">54dip</dimen>
<dimen name="AlbumArt.Small">96dip</dimen>
<dimen name="AlbumArt.Header">210dip</dimen>
+ <dimen name="BottomBar.Text.Major">18sp</dimen>
+ <dimen name="BottomBar.Text.Minor">16sp</dimen>
</resources> \ No newline at end of file
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index cfae3ea0..ffbb0485 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -32,12 +32,6 @@
<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>
@@ -89,7 +83,6 @@
<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>
@@ -108,7 +101,6 @@
<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>
@@ -131,8 +123,7 @@
<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.need_download">Необходимо сначала скачать видео</string>
<string name="download.no_streaming_player">Нет плеера для воспроизведения потока</string>
<string name="starring_content_starred">\"%s\" добавлено в закладки</string>
diff --git a/app/src/main/res/values-v11/colors.xml b/app/src/main/res/values-v11/colors.xml
deleted file mode 100644
index f5a422bb..00000000
--- a/app/src/main/res/values-v11/colors.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
- <color name="notificationArtist">#bababa</color>
- <color name="notificationTitle">#dddddd</color>
-</resources>
diff --git a/app/src/main/res/values-v11/styles.xml b/app/src/main/res/values-v11/styles.xml
deleted file mode 100644
index 9a7cb2b2..00000000
--- a/app/src/main/res/values-v11/styles.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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/app/src/main/res/values-v16/themes.xml b/app/src/main/res/values-v16/themes.xml
deleted file mode 100644
index 013ac0aa..00000000
--- a/app/src/main/res/values-v16/themes.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?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/app/src/main/res/values-v21/themes.xml b/app/src/main/res/values-v21/themes.xml
new file mode 100644
index 00000000..acd117b5
--- /dev/null
+++ b/app/src/main/res/values-v21/themes.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <style name="Theme.DSub.Light" parent="Theme.DSub.Light.Base">
+ <item name="android:windowTranslucentStatus">true</item>
+ </style>
+
+ <style name="Theme.DSub.Dark" parent="Theme.DSub.Dark.Base">
+ <item name="android:windowTranslucentStatus">true</item>
+ </style>
+</resources> \ No newline at end of file
diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml
index c103fb15..a7dd932d 100644
--- a/app/src/main/res/values/arrays.xml
+++ b/app/src/main/res/values/arrays.xml
@@ -1,18 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <string-array name="drawerItems">
- <item>@string/button_bar.home</item>
- <item>@string/button_bar.browse</item>
- <item>@string/button_bar.playlists</item>
- <item>@string/button_bar.podcasts</item>
- <item>@string/button_bar.bookmarks</item>
- <item>@string/button_bar.shares</item>
- <item>@string/button_bar.chat</item>
- <item>@string/button_bar.admin</item>
- <item>@string/button_bar.downloading</item>
- <item>@string/menu.settings</item>
- </string-array>
-
<string-array name="drawerItemsDescriptions">
<item>Home</item>
<item>Artist</item>
@@ -46,32 +33,6 @@
<item>Chat</item>
</string-array>
- <array name="drawerItemIconsLight">
- <item>@drawable/main_offline_light</item>
- <item>@drawable/ic_menu_library_light</item>
- <item>@drawable/ic_menu_playlist_light</item>
- <item>@drawable/ic_menu_podcast_light</item>
- <item>@drawable/ic_menu_bookmark_light</item>
- <item>@drawable/ic_menu_share_light</item>
- <item>@drawable/ic_menu_chat_light</item>
- <item>@drawable/ic_menu_admin_light</item>
- <item>@drawable/ic_menu_download_light</item>
- <item>@drawable/ic_menu_settings_light</item>
- </array>
-
- <array name="drawerItemIconsDark">
- <item>@drawable/main_offline_dark</item>
- <item>@drawable/ic_menu_library_dark</item>
- <item>@drawable/ic_menu_playlist_dark</item>
- <item>@drawable/ic_menu_podcast_dark</item>
- <item>@drawable/ic_menu_bookmark_dark</item>
- <item>@drawable/ic_menu_share_dark</item>
- <item>@drawable/ic_menu_chat_dark</item>
- <item>@drawable/ic_menu_admin_dark</item>
- <item>@drawable/ic_menu_download_dark</item>
- <item>@drawable/ic_menu_settings_dark</item>
- </array>
-
<string-array name="themeValues">
<item>light</item>
<item>dark</item>
diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml
index 9667117c..7e43206a 100644
--- a/app/src/main/res/values/attrs.xml
+++ b/app/src/main/res/values/attrs.xml
@@ -7,10 +7,16 @@
<attr name="media_button_repeat_off" format="reference"/>
<attr name="media_button_start" format="reference"/>
<attr name="media_button_stop" format="reference"/>
+ <attr name="actionbar_backward" format="reference"/>
+ <attr name="actionbar_forward" format="reference"/>
+ <attr name="actionbar_pause" format="reference"/>
+ <attr name="actionbar_start" format="reference"/>
+ <attr name="actionbar_stop" format="reference"/>
<attr name="chat_send" format="reference"/>
<attr name="add" format="reference"/>
<attr name="download_none" format="reference"/>
<attr name="shuffle" format="reference"/>
+ <attr name="shuffle_button" format="reference"/>
<attr name="refresh" format="reference"/>
<attr name="search" format="reference"/>
<attr name="remove" format="reference"/>
@@ -26,7 +32,21 @@
<attr name="rating_bad" format="reference"/>
<attr name="rating_good" format="reference"/>
<attr name="radio" format="reference"/>
+ <attr name="star_outline" format="reference"/>
<attr name="drawerItemsIcons" format="reference"/>
+ <attr name="drawerHome" format="reference"/>
+ <attr name="drawerLibrary" format="reference"/>
+ <attr name="drawerPlaylists" format="reference"/>
+ <attr name="drawerPodcasts" format="reference"/>
+ <attr name="drawerBookmarks" format="reference"/>
+ <attr name="drawerShares" format="reference"/>
+ <attr name="drawerChat" format="reference"/>
+ <attr name="drawerAdmin" format="reference"/>
+ <attr name="drawerDownloading" format="reference"/>
+ <attr name="drawerSettings" format="reference"/>
+ <attr name="actionbarTitleStyle" format="reference"/>
+ <attr name="actionbarSubtitleStyle" format="reference"/>
+ <attr name="actionbarPopupStyle" format="reference"/>
<declare-styleable name="SeekBarPreference">
<attr name="min" format="integer"/>
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index b1422ad6..a1827eaf 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -1,17 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <color name="lightBackground">#F1F0E6</color>
- <color name="dividerColor">#FF33B5E5</color>
<color name="appwidget_text">#FFFFFF</color>
- <color name="notificationArtist">#434343</color>
- <color name="notificationTitle">#000000</color>
- <color name="background_holo_light">#ff33b5e5</color>
+ <color name="notificationArtist">#bababa</color>
+ <color name="notificationTitle">#dddddd</color>
<color name="overlayColor">#80000000</color>
- <color name="ics_opaque">#8033b5e5</color>
- <color name="cyan">#ff0099cc</color>
<color name="holo_blue_light">#ff33b5e5</color>
<color name="holo_orange_light">#ffffbb33</color>
<color name="holo_green_light">#ff99cc00</color>
<color name="holo_red_light">#ffff4444</color>
+
+ <color name="lightPrimary">#2196f3</color>
+ <color name="lightPrimaryDark">#1e88e5</color>
+ <color name="lightAccent">#448aff</color>
+
+ <color name="darkPrimary">#f44336</color>
+ <color name="darkPrimaryDark">#e53935</color>
+ <color name="darkAccent">#ff5252</color>
+
+ <color name="holoPrimary">#009688</color>
+ <color name="holoPrimaryDark">#00897b</color>
+ <color name="holoAccent">#64ffda</color>
</resources> \ No newline at end of file
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index be3e843d..a9e59202 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -4,4 +4,7 @@
<dimen name="Button.Small">46dip</dimen>
<dimen name="AlbumArt.Small">78dip</dimen>
<dimen name="AlbumArt.Header">120dip</dimen>
+ <dimen name="BottomBar.Text.Major">13sp</dimen>
+ <dimen name="BottomBar.Text.Minor">12sp</dimen>
+ <dimen name="Star.Small">20dp</dimen>
</resources> \ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 3d67ec37..1cb85711 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -25,6 +25,7 @@
<string name="common.confirm_message_cache">cache</string>
<string name="common.empty">No data</string>
<string name="common.warning">Warning</string>
+ <string name="common.close">Close</string>
<string name="button_bar.home">Home</string>
<string name="button_bar.browse">Library</string>
@@ -42,12 +43,6 @@
<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[
@@ -79,6 +74,8 @@
<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="main.artist">Artist</string>
+ <string name="main.title">Title</string>
<string name="menu.search">Search</string>
<string name="menu.shuffle">Shuffle</string>
@@ -139,9 +136,8 @@
<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.n_selected">%d selected.</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>
@@ -188,8 +184,7 @@
<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_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>
@@ -212,7 +207,7 @@
<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.stop_time_remaining">Stop in %1$s</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>
@@ -234,7 +229,9 @@
<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.mine">My Playlists</string>
+ <string name="playlist.shared">Shared Playlists</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>
@@ -245,11 +242,6 @@
<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>
@@ -592,6 +584,46 @@
<string name="tasker.edit_server_offline">Toggle offline: </string>
<string name="tasker.edit_do_nothing">Do Nothing</string>
+ <string name="details.title.song">Song Details</string>
+ <string name="details.title.album">Album Details</string>
+ <string name="details.title.podcast">Podcast Details</string>
+ <string name="details.title.playlist">Playlist Details</string>
+ <string name="details.podcast">Podcast</string>
+ <string name="details.status">Status</string>
+ <string name="details.artist">Artist</string>
+ <string name="details.album">Album</string>
+ <string name="details.track">Track</string>
+ <string name="details.genre">Genre</string>
+ <string name="details.year">Year</string>
+ <string name="details.server_format">Server Format</string>
+ <string name="details.server_bitrate">Server Bitrate</string>
+ <string name="details.cached_format">Cached Format</string>
+ <string name="details.cached_bitrate">Cached Bitrate</string>
+ <string name="details.size">Size</string>
+ <string name="details.length">Length</string>
+ <string name="details.bookmark_position">Bookmark Position</string>
+ <string name="details.rating">Rating</string>
+ <string name="details.description">Description</string>
+ <string name="details.owner">Owner</string>
+ <string name="details.comments">Comments</string>
+ <string name="details.song_count">Song Count</string>
+ <string name="details.public">Public</string>
+ <string name="details.created">Created</string>
+ <string name="details.title">Title</string>
+ <string name="details.url">URL</string>
+ <string name="details.error">Error Message</string>
+ <string name="details.author">Author</string>
+ <string name="details.email">Email</string>
+ <string name="details.version">Version</string>
+ <string name="details.files_cached">Files Cached</string>
+ <string name="details.files_permanent">Permanent Cached</string>
+ <string name="details.used_space">Used Space</string>
+ <string name="details.available_space">Available Space</string>
+ <string name="details.of">%1$s of %2$s</string>
+ <string name="details.song">Song</string>
+ <string name="details.position">Position</string>
+ <string name="details.updated">Updated</string>
+
<plurals name="select_album_n_songs">
<item quantity="zero">No songs</item>
<item quantity="one">One song</item>
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 43271afd..f5173619 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -1,7 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="BasicButton">
- <item name="android:background">@drawable/abc_item_background_holo_light</item>
+ <item name="android:background">?attr/selectableItemBackgroundBorderless</item>
+ </style>
+ <style name="NotificationButton">
+ <item name="android:background">?android:selectableItemBackground</item>
</style>
<style name="MoreButton" parent="BasicButton">
@@ -23,15 +26,12 @@
<item name="android:layout_width">@dimen/Button.Small</item>
<item name="android:layout_height">@dimen/Button.Small</item>
</style>
-
- <style name="MenuBarButton" parent="@style/BasicButton">
- <item name="android:layout_width">0dip</item>
- <item name="android:layout_height">45dip</item>
- <item name="android:layout_weight">1</item>
- <item name="android:textSize">14sp</item>
- <item name="android:textStyle">bold</item>
- <item name="android:textColor">?android:textColorPrimary</item>
- </style>
+
+ <style name="PlaybackControl.Match" parent="@style/PlaybackControl">
+ <item name="android:padding">4dip</item>
+ <item name="android:layout_height">match_parent</item>
+ <item name="android:layout_width">wrap_content</item>
+ </style>
<style name="DownloadActionButton" parent="@style/BasicButton">
<item name="android:layout_width">wrap_content</item>
@@ -52,44 +52,4 @@
<item name="android:paddingRight">16dip</item>
<item name="android:paddingLeft">16dip</item>
</style>
-
- <style name="DragDropListView">
- <item name="drag_enabled">true</item>
- <item name="collapsed_height">1dp</item>
- <item name="drag_scroll_start">1.0</item>
- <item name="max_drag_scroll_speed">2.0</item>
- <item name="float_alpha">0.6</item>
- <item name="slide_shuffle_speed">0.3</item>
- <item name="track_drag_sort">false</item>
- <item name="use_default_controller">true</item>
- <item name="drag_handle_id">@id/drag_handle</item>
- <item name="sort_enabled">true</item>
- <item name="remove_enabled">false</item>
- <item name="remove_mode">flingRemove</item>
- <item name="drag_start_mode">onLongPress</item>
- <item name="float_background_color">@android:color/transparent</item>
- </style>
-
- <style name="MainAlbumButton">
- <item name="android:drawablePadding">6dip</item>
- <item name="android:layout_width">fill_parent</item>
- <item name="android:layout_height">wrap_content</item>
- <item name="android:textAppearance">?android:attr/textAppearanceMedium</item>
- <item name="android:gravity">center_vertical</item>
- <item name="android:paddingLeft">6dip</item>
- <item name="android:paddingRight">6dip</item>
- <item name="android:minHeight">46dip</item>
- </style>
-
- <style name="MainAlbumButtonLabel">
- <item name="android:layout_width">fill_parent</item>
- <item name="android:layout_height">wrap_content</item>
- <item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
- <item name="android:textColor">@color/cyan</item>
- <item name="android:gravity">center_vertical</item>
- <item name="android:paddingLeft">6dp</item>
- <item name="android:textAllCaps">true</item>
- <item name="android:textStyle">bold</item>
- <item name="android:textSize">16sp</item>
- </style>
</resources> \ No newline at end of file
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index 78a2c34d..511d630f 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -1,8 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <style name="Theme.DSub.Light" parent="@style/Theme.AppCompat.Light">
- <item name="actionBarStyle">@style/Widget.DSub.ActionBarStyle.Light</item>
- <item name="android:actionBarStyle">@style/Widget.DSub.ActionBarStyle.Light</item>
+ <style name="Theme.DSub.Light.Base" parent="@style/Theme.AppCompat.Light.DarkActionBar">
<item name="offline_icon">@drawable/main_offline_light</item>
<item name="media_button_backward">@drawable/media_backward_light</item>
<item name="media_button_forward">@drawable/media_forward_light</item>
@@ -10,34 +8,54 @@
<item name="media_button_repeat_off">@drawable/media_repeat_off_light</item>
<item name="media_button_start">@drawable/media_start_light</item>
<item name="media_button_stop">@drawable/media_stop_light</item>
+ <item name="actionbar_backward">@drawable/media_backward_dark</item>
+ <item name="actionbar_forward">@drawable/media_forward_dark</item>
+ <item name="actionbar_pause">@drawable/media_pause_dark</item>
+ <item name="actionbar_start">@drawable/media_start_dark</item>
+ <item name="actionbar_stop">@drawable/media_stop_dark</item>
<item name="chat_send">@drawable/ic_menu_chat_send_light</item>
- <item name="add">@drawable/ic_action_add_light</item>
+ <item name="add">@drawable/ic_action_add_dark</item>
<item name="download_none">@drawable/download_none_light</item>
- <item name="shuffle">@drawable/ic_menu_shuffle_light</item>
- <item name="refresh">@drawable/ic_menu_refresh_light</item>
- <item name="search">@drawable/ic_menu_search_light</item>
- <item name="remove">@drawable/ic_menu_remove_light</item>
- <item name="save">@drawable/ic_menu_save_light</item>
+ <item name="shuffle">@drawable/ic_menu_shuffle_dark</item>
+ <item name="shuffle_button">@drawable/ic_menu_shuffle_light</item>
+ <item name="refresh">@drawable/ic_menu_refresh_dark</item>
+ <item name="search">@drawable/ic_menu_search_dark</item>
+ <item name="remove">@drawable/ic_menu_remove_dark</item>
+ <item name="save">@drawable/ic_menu_save_dark</item>
<item name="volume">@drawable/ic_action_volume_light</item>
<item name="toggle_list">@drawable/action_toggle_list_light</item>
<item name="select_server">@drawable/main_select_server_light</item>
<item name="downloading">@drawable/downloading_light</item>
<item name="bookmark">@drawable/ic_menu_bookmark_light</item>
<item name="share">@drawable/ic_menu_share_light</item>
- <item name="add_person">@drawable/ic_menu_add_person_light</item>
- <item name="password">@drawable/ic_menu_password_light</item>
+ <item name="add_person">@drawable/ic_menu_add_person_dark</item>
+ <item name="password">@drawable/ic_menu_password_dark</item>
<item name="rating_bad">@drawable/ic_action_rating_bad_light</item>
<item name="rating_good">@drawable/ic_action_rating_good_light</item>
- <item name="radio">@drawable/ic_menu_radio_light</item>
- <item name="drawerItemsIcons">@array/drawerItemIconsLight</item>
- <item name="android:textViewStyle">@style/DSub.TextViewStyle</item>
- <item name="android:buttonStyle">@style/DSub.ButtonStyle.Light</item>
+ <item name="radio">@drawable/ic_menu_radio_dark</item>
+ <item name="star_outline">@drawable/ic_toggle_star_outline_light</item>
+ <item name="drawerHome">@drawable/main_offline_light</item>
+ <item name="drawerLibrary">@drawable/ic_menu_library_light</item>
+ <item name="drawerPlaylists">@drawable/ic_menu_playlist_light</item>
+ <item name="drawerPodcasts">@drawable/ic_menu_podcast_light</item>
+ <item name="drawerBookmarks">@drawable/ic_menu_bookmark_light</item>
+ <item name="drawerShares">@drawable/ic_menu_share_light</item>
+ <item name="drawerChat">@drawable/ic_menu_chat_light</item>
+ <item name="drawerAdmin">@drawable/ic_menu_admin_light</item>
+ <item name="drawerDownloading">@drawable/ic_menu_download_light</item>
+ <item name="drawerSettings">@drawable/ic_menu_settings_light</item>
<item name="drawerArrowStyle">@style/DSub.DrawerArrow</item>
- <item name="colorAccent">@color/cyan</item>
+ <item name="colorPrimary">@color/lightPrimary</item>
+ <item name="colorPrimaryDark">@color/lightPrimaryDark</item>
+ <item name="colorAccent">@color/lightAccent</item>
+ <item name="actionbarTitleStyle">@style/TextAppearance.AppCompat.Widget.ActionBar.Title.Inverse</item>
+ <item name="actionbarSubtitleStyle">@style/TextAppearance.AppCompat.Widget.ActionBar.Subtitle.Inverse</item>
+ <item name="actionbarPopupStyle">@style/ThemeOverlay.AppCompat.Light</item>
+ <item name="android:spinnerItemStyle">@style/DarkSpinnerItem</item>
+ <item name="windowActionModeOverlay">true</item>
+ <item name="actionModeBackground">?attr/colorPrimary</item>
</style>
- <style name="Theme.DSub.Dark" parent="@style/Theme.AppCompat">
- <item name="actionBarStyle">@style/Widget.DSub.ActionBarStyle.Dark</item>
- <item name="android:actionBarStyle">@style/Widget.DSub.ActionBarStyle.Dark</item>
+ <style name="Theme.DSub.Dark.Base" parent="@style/Theme.AppCompat">
<item name="offline_icon">@drawable/main_offline_dark</item>
<item name="media_button_backward">@drawable/media_backward_dark</item>
<item name="media_button_forward">@drawable/media_forward_dark</item>
@@ -45,10 +63,16 @@
<item name="media_button_repeat_off">@drawable/media_repeat_off</item>
<item name="media_button_start">@drawable/media_start_dark</item>
<item name="media_button_stop">@drawable/media_stop_dark</item>
+ <item name="actionbar_backward">@drawable/media_backward_dark</item>
+ <item name="actionbar_forward">@drawable/media_forward_dark</item>
+ <item name="actionbar_pause">@drawable/media_pause_dark</item>
+ <item name="actionbar_start">@drawable/media_start_dark</item>
+ <item name="actionbar_stop">@drawable/media_stop_dark</item>
<item name="chat_send">@drawable/ic_menu_chat_send_dark</item>
<item name="add">@drawable/ic_action_add_dark</item>
<item name="download_none">@drawable/download_none_dark</item>
<item name="shuffle">@drawable/ic_menu_shuffle_dark</item>
+ <item name="shuffle_button">@drawable/ic_menu_shuffle_dark</item>
<item name="refresh">@drawable/ic_menu_refresh_dark</item>
<item name="search">@drawable/ic_menu_search_dark</item>
<item name="remove">@drawable/ic_menu_remove_dark</item>
@@ -64,46 +88,63 @@
<item name="rating_bad">@drawable/ic_action_rating_bad_dark</item>
<item name="rating_good">@drawable/ic_action_rating_good_dark</item>
<item name="radio">@drawable/ic_menu_radio_dark</item>
- <item name="drawerItemsIcons">@array/drawerItemIconsDark</item>
- <item name="android:textViewStyle">@style/DSub.TextViewStyle</item>
- <item name="android:buttonStyle">@style/DSub.ButtonStyle.Dark</item>
+ <item name="star_outline">@drawable/ic_toggle_star_outline_dark</item>
+ <item name="drawerHome">@drawable/main_offline_dark</item>
+ <item name="drawerLibrary">@drawable/ic_menu_library_dark</item>
+ <item name="drawerPlaylists">@drawable/ic_menu_playlist_dark</item>
+ <item name="drawerPodcasts">@drawable/ic_menu_podcast_dark</item>
+ <item name="drawerBookmarks">@drawable/ic_menu_bookmark_dark</item>
+ <item name="drawerShares">@drawable/ic_menu_share_dark</item>
+ <item name="drawerChat">@drawable/ic_menu_chat_dark</item>
+ <item name="drawerAdmin">@drawable/ic_menu_admin_dark</item>
+ <item name="drawerDownloading">@drawable/ic_menu_download_dark</item>
+ <item name="drawerSettings">@drawable/ic_menu_settings_dark</item>
<item name="drawerArrowStyle">@style/DSub.DrawerArrow</item>
- <item name="colorAccent">@color/cyan</item>
+ <item name="colorPrimary">@color/darkPrimary</item>
+ <item name="colorPrimaryDark">@color/darkPrimaryDark</item>
+ <item name="colorAccent">@color/darkAccent</item>
+ <item name="actionbarTitleStyle">@style/TextAppearance.AppCompat.Widget.ActionBar.Title</item>
+ <item name="actionbarSubtitleStyle">@style/TextAppearance.AppCompat.Widget.ActionBar.Subtitle</item>
+ <item name="actionbarPopupStyle">@style/ThemeOverlay.AppCompat.Dark</item>
+ <item name="windowActionModeOverlay">true</item>
+ <item name="actionModeBackground">?attr/colorPrimary</item>
+ </style>
+ <style name="Theme.DSub.Light" parent="Theme.DSub.Light.Base">
+ </style>
+ <style name="Theme.DSub.Dark" parent="Theme.DSub.Dark.Base">
</style>
<style name="Theme.DSub.Black" parent="Theme.DSub.Dark">
<item name="android:windowBackground">@android:color/black</item>
</style>
<style name="Theme.DSub.Holo" parent="Theme.DSub.Dark">
<item name="android:windowBackground">@drawable/background</item>
- </style>
-
- <style name="Widget.DSub.ActionBarStyle.Light" parent="Widget.AppCompat.Light.ActionBar.Solid">
- <item name="background">@android:color/transparent</item>
- <item name="android:background">@android:color/transparent</item>
- <item name="backgroundStacked">@android:color/transparent</item>
- <item name="android:backgroundStacked">@android:color/transparent</item>
+ <item name="colorPrimary">@color/holoPrimary</item>
+ <item name="colorPrimaryDark">@color/holoPrimaryDark</item>
+ <item name="colorAccent">@color/holoAccent</item>
</style>
- <style name="Widget.DSub.ActionBarStyle.Dark" parent="Widget.AppCompat.ActionBar.Solid">
- <item name="background">@android:color/transparent</item>
- <item name="android:background">@android:color/transparent</item>
- <item name="backgroundStacked">@android:color/transparent</item>
- <item name="android:backgroundStacked">@android:color/transparent</item>
+ <style name="Theme.DSub.Light.No_Actionbar" parent="Theme.DSub.Light">
+ <item name="windowActionBar">false</item>
+ <item name="windowNoTitle">true</item>
</style>
-
- <style name="DSub.TextViewStyle" parent="android:Widget.TextView">
+ <style name="Theme.DSub.Black.No_Actionbar" parent="Theme.DSub.Black">
+ <item name="windowActionBar">false</item>
+ <item name="windowNoTitle">true</item>
</style>
-
- <style name="DSub.TextViewStyle.Bold" parent="android:Widget.TextView">
- <item name="android:textStyle">bold</item>
+ <style name="Theme.DSub.Dark.No_Actionbar" parent="Theme.DSub.Dark">
+ <item name="windowActionBar">false</item>
+ <item name="windowNoTitle">true</item>
</style>
-
- <style name="DSub.ButtonStyle.Dark" parent="android:Widget.Holo.Button">
- </style>
- <style name="DSub.ButtonStyle.Light" parent="android:Widget.Holo.Light.Button">
+ <style name="Theme.DSub.Holo.No_Actionbar" parent="Theme.DSub.Holo">
+ <item name="windowActionBar">false</item>
+ <item name="windowNoTitle">true</item>
</style>
<style name="DSub.DrawerArrow" parent="Widget.AppCompat.DrawerArrowToggle">
<item name="spinBars">true</item>
</style>
+
+ <style name="DarkSpinnerItem" parent="Widget.AppCompat.TextView.SpinnerItem">
+ <item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
+ </style>
</resources>