From b2c15ef186c0661a4d4d6043c4accb6af2a343c1 Mon Sep 17 00:00:00 2001 From: h4h13 Date: Fri, 30 Nov 2018 06:36:16 +0530 Subject: [PATCH 1/3] kotlin conversion --- app/app.iml | 134 +- app/build.gradle | 3 +- app/src/main/AndroidManifest.xml | 80 +- app/src/main/assets/retro-changelog.html | 2 +- .../name/monkey/retromusic/Constants.java | 42 - .../code/name/monkey/retromusic/Constants.kt | 97 ++ .../name/monkey/retromusic/Injection.java | 23 - .../code/name/monkey/retromusic/Injection.kt | 23 + .../monkey/retromusic/RetroApplication.java | 125 -- .../monkey/retromusic/RetroApplication.kt | 94 ++ .../AppShortcutIconGenerator.java | 71 - .../AppShortcutLauncherActivity.java | 77 - .../appshortcuts/DynamicShortcutManager.java | 63 - .../shortcuttype/BaseShortcutType.java | 50 - .../shortcuttype/LastAddedShortcutType.java | 34 - .../shortcuttype/ShuffleAllShortcutType.java | 35 - .../shortcuttype/TopTracksShortcutType.java | 35 - .../retromusic/appwidgets/AppWidgetBig.java | 174 -- .../retromusic/appwidgets/AppWidgetCard.java | 197 --- .../appwidgets/AppWidgetClassic.java | 182 --- .../retromusic/appwidgets/AppWidgetSmall.java | 187 --- .../retromusic/appwidgets/BootReceiver.java | 32 - .../appwidgets/base/BaseAppWidget.java | 163 -- .../monkey/retromusic/cast/CastHelper.java | 18 +- .../monkey/retromusic/cast/WebServer.java | 4 +- .../dialogs/AddToPlaylistDialog.java | 2 +- .../retromusic/dialogs/DeleteSongsDialog.java | 2 +- .../MainOptionsBottomSheetDialogFragment.java | 6 +- .../dialogs/RemoveFromPlaylistDialog.java | 93 -- .../dialogs/RemoveFromPlaylistDialog.kt | 91 ++ .../retromusic/dialogs/SleepTimerDialog.java | 2 +- .../retromusic/dialogs/SongDetailDialog.java | 8 +- .../retromusic/dialogs/SongShareDialog.java | 4 +- .../retromusic/glide/ArtistGlideRequest.java | 4 +- .../glide/RetroMusicColoredTarget.java | 53 - .../glide/RetroMusicColoredTarget.kt | 47 + .../retromusic/glide/SongGlideRequest.java | 6 +- .../glide/palette/BitmapPaletteTarget.java | 17 - .../glide/palette/BitmapPaletteTarget.kt | 12 + .../retromusic/helper/EqualizerHelper.java | 171 -- .../retromusic/helper/EqualizerHelper.kt | 109 ++ .../helper/HorizontalAdapterHelper.java | 36 - .../helper/HorizontalAdapterHelper.kt | 36 + .../retromusic/helper/M3UConstants.java | 8 - .../monkey/retromusic/helper/M3UConstants.kt | 10 + .../monkey/retromusic/helper/M3UWriter.java | 58 - .../monkey/retromusic/helper/M3UWriter.kt | 53 + .../retromusic/helper/MusicPlayerRemote.java | 496 ------ .../retromusic/helper/MusicPlayerRemote.kt | 440 +++++ .../helper/MusicProgressViewUpdateHelper.java | 71 - .../helper/MusicProgressViewUpdateHelper.kt | 72 + .../helper/PlayPauseButtonOnClickHandler.java | 15 - .../helper/PlayPauseButtonOnClickHandler.kt | 14 + .../retromusic/helper/SearchQueryHelper.java | 91 -- .../retromusic/helper/SearchQueryHelper.kt | 78 + .../retromusic/helper/ShuffleHelper.java | 25 - .../monkey/retromusic/helper/ShuffleHelper.kt | 20 + .../monkey/retromusic/helper/SortOrder.java | 173 -- .../monkey/retromusic/helper/SortOrder.kt | 179 ++ .../monkey/retromusic/helper/StopWatch.java | 82 - .../monkey/retromusic/helper/StopWatch.kt | 80 + .../helper/menu/GenreMenuHelper.java | 50 - .../retromusic/helper/menu/GenreMenuHelper.kt | 52 + .../helper/menu/PlaylistMenuHelper.java | 91 -- .../helper/menu/PlaylistMenuHelper.kt | 91 ++ .../helper/menu/SongMenuHelper.java | 93 -- .../retromusic/helper/menu/SongMenuHelper.kt | 93 ++ .../helper/menu/SongsMenuHelper.java | 35 - .../retromusic/helper/menu/SongsMenuHelper.kt | 34 + .../retromusic/interfaces/CabHolder.java | 12 - .../monkey/retromusic/interfaces/CabHolder.kt | 9 + .../interfaces/EqualizerInterface.java | 49 - .../interfaces/EqualizerInterface.kt | 41 + .../retromusic/interfaces/LoaderIds.java | 6 - .../monkey/retromusic/interfaces/LoaderIds.kt | 8 + .../MainActivityFragmentCallbacks.java | 11 - .../MainActivityFragmentCallbacks.kt | 11 + .../interfaces/MusicServiceEventListener.java | 20 - .../interfaces/MusicServiceEventListener.kt | 20 + .../interfaces/PaletteColorHolder.java | 12 - .../interfaces/PaletteColorHolder.kt | 12 + .../retromusic/loaders/AlbumLoader.java | 104 -- .../monkey/retromusic/loaders/AlbumLoader.kt | 95 ++ .../retromusic/loaders/ArtistLoader.java | 101 -- .../monkey/retromusic/loaders/ArtistLoader.kt | 99 ++ .../retromusic/loaders/ArtistSongLoader.java | 37 - .../retromusic/loaders/GenreLoader.java | 133 -- .../monkey/retromusic/loaders/GenreLoader.kt | 123 ++ .../retromusic/loaders/GenreSongsLoader.java | 76 - .../monkey/retromusic/loaders/HomeLoader.java | 63 - .../monkey/retromusic/loaders/HomeLoader.kt | 61 + .../loaders/LastAddedSongsLoader.java | 46 - .../loaders/LastAddedSongsLoader.kt | 46 + .../retromusic/loaders/PlaylistLoader.java | 118 -- .../retromusic/loaders/PlaylistLoader.kt | 103 ++ .../loaders/PlaylistSongsLoader.java | 94 -- .../retromusic/loaders/PlaylistSongsLoader.kt | 86 + .../retromusic/loaders/SearchLoader.java | 44 - .../monkey/retromusic/loaders/SearchLoader.kt | 43 + .../monkey/retromusic/loaders/SongLoader.kt | 298 ++-- .../TopAndRecentlyPlayedTracksLoader.java | 158 -- .../TopAndRecentlyPlayedTracksLoader.kt | 142 ++ .../misc/AppBarStateChangeListener.java | 41 - .../misc/AppBarStateChangeListener.kt | 40 + .../misc/SimpleAnimatorListener.java | 26 - .../retromusic/misc/SimpleAnimatorListener.kt | 22 + .../misc/SimpleOnSeekbarChangeListener.java | 21 - .../misc/SimpleOnSeekbarChangeListener.kt | 18 + .../retromusic/misc/WeakContextAsyncTask.java | 1 - .../retromusic/misc/WeakContextAsyncTask.kt | 1 + .../misc/WrappedAsyncTaskLoader.java | 72 - .../retromusic/misc/WrappedAsyncTaskLoader.kt | 64 + .../name/monkey/retromusic/model/Album.java | 103 -- .../name/monkey/retromusic/model/Album.kt | 41 + .../name/monkey/retromusic/model/Artist.java | 110 -- .../name/monkey/retromusic/model/Artist.kt | 57 + .../monkey/retromusic/model/Contributor.java | 34 - .../monkey/retromusic/model/Contributor.kt | 3 + .../name/monkey/retromusic/model/Genre.java | 86 - .../name/monkey/retromusic/model/Genre.kt | 26 + .../name/monkey/retromusic/model/Home.java | 29 - .../name/monkey/retromusic/model/Song.java | 77 - .../code/name/monkey/retromusic/model/Song.kt | 79 + .../model/smartplaylist/HistoryPlaylist.java | 2 +- .../smartplaylist/LastAddedPlaylist.java | 2 +- .../smartplaylist/MyTopTracksPlaylist.java | 2 +- .../smartplaylist/ShuffleAllPlaylist.java | 2 +- .../name/monkey/retromusic/mvp/Presenter.java | 4 +- .../NowPlayingScreenPreferenceDialog.java | 17 +- .../retromusic/providers/BlacklistStore.java | 4 +- .../providers/MusicPlaybackQueueStore.java | 24 +- .../retromusic/providers/RepositoryImpl.java | 40 +- .../monkey/retromusic/rest/KogouClient.java | 93 +- .../service/MediaButtonIntentReceiver.java | 211 --- .../service/MediaButtonIntentReceiver.kt | 186 +++ .../retromusic/service/MultiPlayer.java | 334 ---- .../monkey/retromusic/service/MultiPlayer.kt | 333 ++++ .../retromusic/service/MusicService.java | 1445 ----------------- .../monkey/retromusic/service/MusicService.kt | 1247 ++++++++++++++ .../service/WearBrowserService.java | 50 +- .../service/daydream/RetroMusicAlbums.java | 186 --- .../service/daydream/RetroMusicAlbums.kt | 161 ++ .../notification/PlayingNotificationImpl.java | 19 +- .../PlayingNotificationImpl24.java | 13 +- .../notification/PlayingNotificationOreo.java | 248 --- .../notification/PlayingNotificationOreo.kt | 221 +++ .../ui/activities/AboutActivity.java | 13 +- .../ui/activities/AlbumDetailsActivity.java | 426 ----- .../ui/activities/AlbumDetailsActivity.kt | 338 ++++ .../ui/activities/ArtistDetailActivity.java | 11 +- .../ui/activities/EqualizerActivity.java | 40 +- .../ui/activities/ErrorHandlerActivity.java | 2 +- .../ui/activities/GenreDetailsActivity.java | 8 +- .../ui/activities/LockScreenActivity.java | 86 - .../ui/activities/LyricsActivity.java | 26 +- .../ui/activities/MainActivity.java | 341 ---- .../retromusic/ui/activities/MainActivity.kt | 294 ++++ .../ui/activities/NowPayingActivity.java | 180 -- .../ui/activities/PlayingQueueActivity.java | 18 +- .../ui/activities/PlaylistDetailActivity.java | 12 +- .../ui/activities/ProVersionActivity.java | 2 +- .../ui/activities/SettingsActivity.java | 5 - .../SupportDevelopmentActivity.java | 2 +- .../ui/activities/WhatsNewActivity.java | 98 -- .../ui/activities/WhatsNewActivity.kt | 75 + .../ui/activities/base/AbsBaseActivity.java | 159 -- .../ui/activities/base/AbsBaseActivity.kt | 145 ++ .../ui/activities/base/AbsCastActivity.java | 157 -- .../ui/activities/base/AbsCastActivity.kt | 135 ++ .../base/AbsMusicServiceActivity.java | 224 --- .../base/AbsMusicServiceActivity.kt | 168 ++ .../base/AbsSlidingMusicPanelActivity.java | 455 ------ .../base/AbsSlidingMusicPanelActivity.kt | 301 ++++ .../ui/activities/base/AbsThemeActivity.java | 216 --- .../ui/activities/base/AbsThemeActivity.kt | 206 +++ .../tageditor/AlbumTagEditorActivity.java | 4 +- .../tageditor/SongTagEditorActivity.java | 2 +- .../ui/adapter/CollageSongAdapter.java | 4 +- .../ui/adapter/ContributorAdapter.java | 71 - .../ui/adapter/ContributorAdapter.kt | 48 + .../retromusic/ui/adapter/GenreAdapter.java | 87 - .../retromusic/ui/adapter/GenreAdapter.kt | 73 + .../retromusic/ui/adapter/SearchAdapter.java | 163 -- .../retromusic/ui/adapter/SearchAdapter.kt | 132 ++ .../ui/adapter/SongFileAdapter.java | 215 --- .../retromusic/ui/adapter/SongFileAdapter.kt | 180 ++ .../ui/adapter/album/AlbumAdapter.java | 249 --- .../ui/adapter/album/AlbumAdapter.kt | 214 +++ .../adapter/album/AlbumCoverPagerAdapter.java | 200 --- .../adapter/album/AlbumCoverPagerAdapter.kt | 172 ++ .../adapter/album/AlbumFullWithAdapter.java | 151 -- .../ui/adapter/album/AlbumFullWithAdapter.kt | 105 ++ .../adapter/album/HorizontalAlbumAdapter.java | 83 - .../adapter/album/HorizontalAlbumAdapter.kt | 68 + .../ui/adapter/artist/ArtistAdapter.java | 201 --- .../ui/adapter/artist/ArtistAdapter.kt | 157 ++ .../ui/adapter/base/MediaEntryViewHolder.java | 101 -- .../ui/adapter/base/MediaEntryViewHolder.kt | 80 + .../ui/adapter/playlist/AddToPlaylist.java | 69 - .../ui/adapter/playlist/AddToPlaylist.kt | 44 + .../ui/adapter/playlist/PlaylistAdapter.java | 297 ---- .../ui/adapter/playlist/PlaylistAdapter.kt | 268 +++ .../ui/adapter/song/AbsOffsetSongAdapter.java | 110 -- .../ui/adapter/song/AbsOffsetSongAdapter.kt | 85 + .../song/OrderablePlaylistSongAdapter.java | 145 -- .../song/OrderablePlaylistSongAdapter.kt | 136 ++ .../ui/adapter/song/PlayingQueueAdapter.java | 215 --- .../ui/adapter/song/PlayingQueueAdapter.kt | 199 +++ .../ui/adapter/song/PlaylistSongAdapter.java | 93 -- .../ui/adapter/song/PlaylistSongAdapter.kt | 86 + .../song/ShuffleButtonSongAdapter.java | 80 - .../adapter/song/ShuffleButtonSongAdapter.kt | 64 + .../ui/adapter/song/SimpleSongAdapter.java | 69 - .../ui/adapter/song/SimpleSongAdapter.kt | 63 + .../ui/adapter/song/SongAdapter.java | 295 ---- .../retromusic/ui/adapter/song/SongAdapter.kt | 224 +++ .../ui/fragments/MiniPlayerFragment.java | 228 --- .../ui/fragments/MiniPlayerFragment.kt | 165 ++ .../ui/fragments/NowPlayingScreen.java | 13 +- .../ui/fragments/PlayingQueueFragment.java | 12 +- .../ui/fragments/VolumeFragment.java | 172 -- .../retromusic/ui/fragments/VolumeFragment.kt | 129 ++ .../base/AbsLibraryPagerFragment.java | 19 - .../fragments/base/AbsLibraryPagerFragment.kt | 17 + ...gerRecyclerViewCustomGridSizeFragment.java | 169 -- ...PagerRecyclerViewCustomGridSizeFragment.kt | 157 ++ .../AbsLibraryPagerRecyclerViewFragment.java | 162 -- .../AbsLibraryPagerRecyclerViewFragment.kt | 116 ++ .../base/AbsMainActivityFragment.java | 58 - .../fragments/base/AbsMainActivityFragment.kt | 56 + .../base/AbsMusicServiceFragment.java | 93 -- .../fragments/base/AbsMusicServiceFragment.kt | 75 + .../base/AbsPlayerControlsFragment.java | 52 - .../base/AbsPlayerControlsFragment.kt | 80 + .../ui/fragments/base/AbsPlayerFragment.java | 231 --- .../ui/fragments/base/AbsPlayerFragment.kt | 227 +++ .../mainactivity/AlbumsFragment.java | 175 -- .../fragments/mainactivity/AlbumsFragment.kt | 149 ++ .../mainactivity/ArtistsFragment.java | 173 -- .../fragments/mainactivity/ArtistsFragment.kt | 143 ++ .../fragments/mainactivity/GenreFragment.java | 110 -- .../fragments/mainactivity/GenreFragment.kt | 94 ++ .../mainactivity/LibraryFragment.java | 476 ------ .../fragments/mainactivity/LibraryFragment.kt | 360 ++++ .../mainactivity/PlaylistsFragment.java | 120 -- .../mainactivity/PlaylistsFragment.kt | 99 ++ .../fragments/mainactivity/SongsFragment.java | 177 -- .../fragments/mainactivity/SongsFragment.kt | 141 ++ .../mainactivity/folders/FoldersFragment.java | 12 +- .../mainactivity/home/BannerHomeFragment.java | 361 ---- .../mainactivity/home/BannerHomeFragment.kt | 302 ++++ .../player/PlayerAlbumCoverFragment.java | 151 -- .../player/PlayerAlbumCoverFragment.kt | 119 ++ .../player/adaptive/AdaptiveFragment.java | 160 -- .../player/adaptive/AdaptiveFragment.kt | 106 ++ .../AdaptivePlaybackControlsFragment.java | 285 ---- .../AdaptivePlaybackControlsFragment.kt | 209 +++ .../blur/BlurPlaybackControlsFragment.java | 299 ---- .../blur/BlurPlaybackControlsFragment.kt | 231 +++ .../player/blur/BlurPlayerFragment.java | 271 ---- .../player/blur/BlurPlayerFragment.kt | 198 +++ .../fragments/player/card/CardFragment.java | 281 ---- .../card/CardPlaybackControlsFragment.java | 334 ---- .../player/cardblur/CardBlurFragment.java | 201 --- .../CardBlurPlaybackControlsFragment.java | 309 ---- .../fragments/player/color/ColorFragment.java | 309 ---- .../color/ColorPlaybackControlsFragment.java | 311 ---- .../ui/fragments/player/fit/FitFragment.java | 154 -- .../fit/FitPlaybackControlsFragment.java | 344 ---- .../flat/FlatPlaybackControlsFragment.java | 297 ---- .../player/flat/FlatPlayerFragment.java | 177 -- .../full/FullPlaybackControlsFragment.java | 313 ---- .../player/full/FullPlayerFragment.java | 124 -- .../hmm/HmmPlaybackControlsFragment.java | 142 -- .../player/hmm/HmmPlayerFragment.java | 222 --- .../LockScreenPlayerControlsFragment.java | 306 ---- .../material/MaterialControlsFragment.java | 290 ---- .../player/material/MaterialFragment.java | 149 -- .../player/normal/PlayerFragment.java | 175 -- .../PlayerPlaybackControlsFragment.java | 344 ---- .../plain/PlainPlaybackControlsFragment.java | 324 ---- .../player/plain/PlainPlayerFragment.java | 161 -- .../SimplePlaybackControlsFragment.java | 312 ---- .../player/simple/SimplePlayerFragment.java | 130 -- .../settings/MainSettingsFragment.java | 2 +- .../NotificationSettingsFragment.java | 2 +- .../settings/NowPlayingSettingsFragment.java | 2 +- .../settings/PersonaizeSettingsFragment.java | 2 +- .../settings/ThemeSettingsFragment.java | 13 +- .../util/CustomArtistImageUtil.java | 20 +- .../name/monkey/retromusic/util/FileUtil.java | 4 +- .../monkey/retromusic/util/MusicUtil.java | 22 +- .../retromusic/util/NavigationUtil.java | 11 +- .../monkey/retromusic/util/PlaylistsUtil.java | 6 +- .../retromusic/util/PreferenceUtil.java | 22 +- .../monkey/retromusic/util/RetroUtil.java | 14 +- .../monkey/retromusic/util/SystemUtils.java | 4 +- .../views/MetalRecyclerViewPager.java | 127 -- .../views/MetalRecyclerViewPager.kt | 86 + .../views/TintIconColorToolbar.java | 33 - .../retromusic/views/TintIconColorToolbar.kt | 22 + .../main/res/layout-land/activity_album.xml | 10 +- .../main/res/layout-sw600dp/abs_playlists.xml | 6 +- .../layout-sw600dp/fragment_mini_player.xml | 12 +- .../res/layout-xlarge-land/activity_album.xml | 12 +- .../main/res/layout-xlarge/activity_album.xml | 12 +- .../fragment_main_activity_recycler_view.xml | 2 +- app/src/main/res/layout/abs_playlists.xml | 6 +- app/src/main/res/layout/activity_album.xml | 14 +- .../res/layout/activity_album_content.xml | 8 +- .../layout/activity_main_drawer_layout.xml | 2 +- .../main/res/layout/activity_whats_new.xml | 6 +- .../fragment_main_activity_recycler_view.xml | 2 +- .../main/res/layout/fragment_mini_player.xml | 14 +- .../layout/fragment_player_album_cover.xml | 2 +- app/src/main/res/layout/fragment_volume.xml | 8 +- 316 files changed, 13055 insertions(+), 22983 deletions(-) delete mode 100644 app/src/main/java/code/name/monkey/retromusic/Constants.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/Constants.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/Injection.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/Injection.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/RetroApplication.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/RetroApplication.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/appshortcuts/AppShortcutIconGenerator.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/appshortcuts/AppShortcutLauncherActivity.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/appshortcuts/DynamicShortcutManager.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/appshortcuts/shortcuttype/BaseShortcutType.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/appshortcuts/shortcuttype/LastAddedShortcutType.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/appshortcuts/shortcuttype/ShuffleAllShortcutType.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/appshortcuts/shortcuttype/TopTracksShortcutType.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/appwidgets/AppWidgetBig.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/appwidgets/AppWidgetCard.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/appwidgets/AppWidgetClassic.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/appwidgets/AppWidgetSmall.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/appwidgets/BootReceiver.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/appwidgets/base/BaseAppWidget.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/dialogs/RemoveFromPlaylistDialog.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/dialogs/RemoveFromPlaylistDialog.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/glide/RetroMusicColoredTarget.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/glide/RetroMusicColoredTarget.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/glide/palette/BitmapPaletteTarget.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/glide/palette/BitmapPaletteTarget.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/helper/EqualizerHelper.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/helper/EqualizerHelper.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/helper/HorizontalAdapterHelper.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/helper/HorizontalAdapterHelper.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/helper/M3UConstants.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/helper/M3UConstants.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/helper/M3UWriter.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/helper/M3UWriter.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/helper/MusicPlayerRemote.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/helper/MusicPlayerRemote.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/helper/MusicProgressViewUpdateHelper.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/helper/MusicProgressViewUpdateHelper.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/helper/PlayPauseButtonOnClickHandler.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/helper/PlayPauseButtonOnClickHandler.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/helper/SearchQueryHelper.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/helper/SearchQueryHelper.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/helper/ShuffleHelper.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/helper/ShuffleHelper.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/helper/SortOrder.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/helper/SortOrder.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/helper/StopWatch.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/helper/StopWatch.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/helper/menu/GenreMenuHelper.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/helper/menu/GenreMenuHelper.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/helper/menu/PlaylistMenuHelper.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/helper/menu/PlaylistMenuHelper.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/helper/menu/SongMenuHelper.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/helper/menu/SongMenuHelper.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/helper/menu/SongsMenuHelper.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/helper/menu/SongsMenuHelper.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/interfaces/CabHolder.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/interfaces/CabHolder.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/interfaces/EqualizerInterface.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/interfaces/EqualizerInterface.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/interfaces/LoaderIds.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/interfaces/LoaderIds.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/interfaces/MainActivityFragmentCallbacks.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/interfaces/MainActivityFragmentCallbacks.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/interfaces/MusicServiceEventListener.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/interfaces/MusicServiceEventListener.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/interfaces/PaletteColorHolder.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/interfaces/PaletteColorHolder.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/loaders/AlbumLoader.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/loaders/AlbumLoader.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/loaders/ArtistLoader.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/loaders/ArtistLoader.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/loaders/ArtistSongLoader.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/loaders/GenreLoader.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/loaders/GenreLoader.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/loaders/GenreSongsLoader.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/loaders/HomeLoader.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/loaders/HomeLoader.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/loaders/LastAddedSongsLoader.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/loaders/LastAddedSongsLoader.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/loaders/PlaylistLoader.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/loaders/PlaylistLoader.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/loaders/PlaylistSongsLoader.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/loaders/PlaylistSongsLoader.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/loaders/SearchLoader.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/loaders/SearchLoader.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/loaders/TopAndRecentlyPlayedTracksLoader.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/loaders/TopAndRecentlyPlayedTracksLoader.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/misc/AppBarStateChangeListener.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/misc/AppBarStateChangeListener.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/misc/SimpleAnimatorListener.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/misc/SimpleAnimatorListener.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/misc/SimpleOnSeekbarChangeListener.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/misc/SimpleOnSeekbarChangeListener.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/misc/WeakContextAsyncTask.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/misc/WeakContextAsyncTask.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/misc/WrappedAsyncTaskLoader.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/misc/WrappedAsyncTaskLoader.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/model/Album.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/model/Album.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/model/Artist.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/model/Artist.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/model/Contributor.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/model/Contributor.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/model/Genre.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/model/Genre.kt delete mode 100755 app/src/main/java/code/name/monkey/retromusic/model/Home.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/model/Song.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/model/Song.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/service/MediaButtonIntentReceiver.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/service/MediaButtonIntentReceiver.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/service/MultiPlayer.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/service/MultiPlayer.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/service/MusicService.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/service/MusicService.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/service/daydream/RetroMusicAlbums.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/service/daydream/RetroMusicAlbums.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/service/notification/PlayingNotificationOreo.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/service/notification/PlayingNotificationOreo.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/activities/AlbumDetailsActivity.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/activities/AlbumDetailsActivity.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/activities/LockScreenActivity.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/activities/MainActivity.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/activities/MainActivity.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/activities/NowPayingActivity.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/activities/WhatsNewActivity.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/activities/WhatsNewActivity.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsBaseActivity.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsBaseActivity.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsCastActivity.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsCastActivity.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsMusicServiceActivity.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsMusicServiceActivity.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsSlidingMusicPanelActivity.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsSlidingMusicPanelActivity.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsThemeActivity.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsThemeActivity.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/adapter/ContributorAdapter.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/adapter/ContributorAdapter.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/adapter/GenreAdapter.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/adapter/GenreAdapter.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/adapter/SearchAdapter.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/adapter/SearchAdapter.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/adapter/SongFileAdapter.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/adapter/SongFileAdapter.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/adapter/album/AlbumAdapter.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/adapter/album/AlbumAdapter.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/adapter/album/AlbumCoverPagerAdapter.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/adapter/album/AlbumCoverPagerAdapter.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/adapter/album/AlbumFullWithAdapter.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/adapter/album/AlbumFullWithAdapter.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/adapter/album/HorizontalAlbumAdapter.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/adapter/album/HorizontalAlbumAdapter.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/adapter/artist/ArtistAdapter.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/adapter/artist/ArtistAdapter.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/adapter/base/MediaEntryViewHolder.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/adapter/base/MediaEntryViewHolder.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/adapter/playlist/AddToPlaylist.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/adapter/playlist/AddToPlaylist.kt delete mode 100755 app/src/main/java/code/name/monkey/retromusic/ui/adapter/playlist/PlaylistAdapter.java create mode 100755 app/src/main/java/code/name/monkey/retromusic/ui/adapter/playlist/PlaylistAdapter.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/AbsOffsetSongAdapter.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/AbsOffsetSongAdapter.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/OrderablePlaylistSongAdapter.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/OrderablePlaylistSongAdapter.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/PlayingQueueAdapter.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/PlayingQueueAdapter.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/PlaylistSongAdapter.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/PlaylistSongAdapter.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/ShuffleButtonSongAdapter.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/ShuffleButtonSongAdapter.kt delete mode 100755 app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/SimpleSongAdapter.java create mode 100755 app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/SimpleSongAdapter.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/SongAdapter.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/SongAdapter.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/MiniPlayerFragment.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/MiniPlayerFragment.kt delete mode 100755 app/src/main/java/code/name/monkey/retromusic/ui/fragments/VolumeFragment.java create mode 100755 app/src/main/java/code/name/monkey/retromusic/ui/fragments/VolumeFragment.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsLibraryPagerFragment.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsLibraryPagerFragment.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsLibraryPagerRecyclerViewCustomGridSizeFragment.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsLibraryPagerRecyclerViewCustomGridSizeFragment.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsLibraryPagerRecyclerViewFragment.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsLibraryPagerRecyclerViewFragment.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsMainActivityFragment.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsMainActivityFragment.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsMusicServiceFragment.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsMusicServiceFragment.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsPlayerControlsFragment.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsPlayerControlsFragment.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsPlayerFragment.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsPlayerFragment.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/AlbumsFragment.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/AlbumsFragment.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/ArtistsFragment.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/ArtistsFragment.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/GenreFragment.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/GenreFragment.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/LibraryFragment.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/LibraryFragment.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/PlaylistsFragment.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/PlaylistsFragment.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/SongsFragment.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/SongsFragment.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/home/BannerHomeFragment.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/home/BannerHomeFragment.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/PlayerAlbumCoverFragment.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/PlayerAlbumCoverFragment.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/adaptive/AdaptiveFragment.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/adaptive/AdaptiveFragment.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/adaptive/AdaptivePlaybackControlsFragment.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/adaptive/AdaptivePlaybackControlsFragment.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/blur/BlurPlaybackControlsFragment.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/blur/BlurPlaybackControlsFragment.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/blur/BlurPlayerFragment.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/blur/BlurPlayerFragment.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/card/CardFragment.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/card/CardPlaybackControlsFragment.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/cardblur/CardBlurFragment.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/cardblur/CardBlurPlaybackControlsFragment.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/color/ColorFragment.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/color/ColorPlaybackControlsFragment.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/fit/FitFragment.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/fit/FitPlaybackControlsFragment.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/flat/FlatPlaybackControlsFragment.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/flat/FlatPlayerFragment.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/full/FullPlaybackControlsFragment.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/full/FullPlayerFragment.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/hmm/HmmPlaybackControlsFragment.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/hmm/HmmPlayerFragment.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/lockscreen/LockScreenPlayerControlsFragment.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/material/MaterialControlsFragment.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/material/MaterialFragment.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/normal/PlayerFragment.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/normal/PlayerPlaybackControlsFragment.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/plain/PlainPlaybackControlsFragment.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/plain/PlainPlayerFragment.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/simple/SimplePlaybackControlsFragment.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/simple/SimplePlayerFragment.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/views/MetalRecyclerViewPager.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/views/MetalRecyclerViewPager.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/views/TintIconColorToolbar.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/views/TintIconColorToolbar.kt diff --git a/app/app.iml b/app/app.iml index 2566dab7..c37e4467 100644 --- a/app/app.iml +++ b/app/app.iml @@ -35,10 +35,16 @@ @@ -50,7 +56,6 @@ - @@ -64,7 +69,6 @@ - @@ -148,10 +152,13 @@ + + + @@ -166,7 +173,6 @@ - @@ -189,87 +195,87 @@ - + - - + + + + + + + + - - - - - - - - - - + + + + + + + + + + + - - - - - - - - - + + + + - - - + + - - - - - + + + + + + + + - - - - + + + + - - - - - + + - + + - - - + + - - + + + + + - - - - - - - - + + + - + + - - - - - + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 6141b996..32115020 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,5 +1,6 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' +apply plugin: 'kotlin-android-extensions' android { compileSdkVersion 28 @@ -122,7 +123,7 @@ dependencies { implementation "com.squareup.retrofit2:adapter-rxjava2:2.4.0" implementation "com.jakewharton:butterknife:$butterKnife" - annotationProcessor "com.jakewharton:butterknife-compiler:$butterKnife" + kapt "com.jakewharton:butterknife-compiler:$butterKnife" implementation "com.afollestad.material-dialogs:core:$materialDialog" implementation "com.afollestad.material-dialogs:commons:$materialDialog" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e9ade2f9..ca258b34 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -103,27 +103,19 @@ - - + - - + + --> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/assets/retro-changelog.html b/app/src/main/assets/retro-changelog.html index 5bae13be..281b5626 100644 --- a/app/src/main/assets/retro-changelog.html +++ b/app/src/main/assets/retro-changelog.html @@ -1 +1 @@ -

Are you subscribed to PewDiePie

You can view the changelog dialog again at any time from the about section.

Version 2.2.100

  • Click new music mix to play songs
  • Gradient image option for gird list
  • Clear button for playing queue
  • Click toolbar (Library) to open options
  • Folder list back button
  • New theme Fit
  • On library click on toolbar for accessing main menu
  • On home click on toolbar for accessing search
  • BottomSheetDialogue is now adaptable to screens, background colour and text size consistency.
  • Removed coloured navigation bar option to making app adapt the primary colour
  • Swipe up gesture for now playing removed, replaced with "tap to open", To achieve transparent navigation bar for desired themes.
  • Improved tablet UI and home screen by adding suggestions toggle banner issues.
  • Improving lyrics page

FAQ's

*If you face any UI related issues you clear app data and cache, if its not working try to uninstall and install again.

\ No newline at end of file +

Version 2.2.100

  • Click new music mix to play songs
  • Gradient image option for gird list
  • Clear button for playing queue
  • Click toolbar (Library) to open options
  • Folder list back button
  • New theme Fit
  • On library click on toolbar for accessing main menu
  • On home click on toolbar for accessing search
  • BottomSheetDialogue is now adaptable to screens, background colour and text size consistency.
  • Removed coloured navigation bar option to making app adapt the primary colour
  • Swipe up gesture for now playing removed, replaced with "tap to open", To achieve transparent navigation bar for desired themes.
  • Improved tablet UI and home screen by adding suggestions toggle banner issues.
  • Improving lyrics page

FAQ's

*If you face any UI related issues you clear app data and cache, if its not working try to uninstall and install again.

\ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/Constants.java b/app/src/main/java/code/name/monkey/retromusic/Constants.java deleted file mode 100644 index 529e3d80..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/Constants.java +++ /dev/null @@ -1,42 +0,0 @@ -package code.name.monkey.retromusic; - -public class Constants { - - public static final String DISCORD_LINK = "https://discord.gg/qTecXXn"; - - public static final String RETRO_MUSIC_PACKAGE_NAME = "code.name.monkey.retromusic"; - public static final String MUSIC_PACKAGE_NAME = "com.android.music"; - public static final String ACTION_TOGGLE_PAUSE = RETRO_MUSIC_PACKAGE_NAME + ".togglepause"; - public static final String ACTION_PLAY = RETRO_MUSIC_PACKAGE_NAME + ".play"; - public static final String ACTION_PLAY_PLAYLIST = RETRO_MUSIC_PACKAGE_NAME + ".play.playlist"; - public static final String ACTION_PAUSE = RETRO_MUSIC_PACKAGE_NAME + ".pause"; - public static final String ACTION_STOP = RETRO_MUSIC_PACKAGE_NAME + ".stop"; - public static final String ACTION_SKIP = RETRO_MUSIC_PACKAGE_NAME + ".skip"; - public static final String ACTION_REWIND = RETRO_MUSIC_PACKAGE_NAME + ".rewind"; - public static final String ACTION_QUIT = RETRO_MUSIC_PACKAGE_NAME + ".quitservice"; - public static final String INTENT_EXTRA_PLAYLIST = RETRO_MUSIC_PACKAGE_NAME + "intentextra.playlist"; - public static final String INTENT_EXTRA_SHUFFLE_MODE = RETRO_MUSIC_PACKAGE_NAME + ".intentextra.shufflemode"; - public static final String APP_WIDGET_UPDATE = RETRO_MUSIC_PACKAGE_NAME + ".appwidgetupdate"; - public static final String EXTRA_APP_WIDGET_NAME = RETRO_MUSIC_PACKAGE_NAME + "app_widget_name"; - // do not change these three strings as it will break support with other apps (e.g. last.fm scrobbling) - public static final String META_CHANGED = RETRO_MUSIC_PACKAGE_NAME + ".metachanged"; - public static final String QUEUE_CHANGED = RETRO_MUSIC_PACKAGE_NAME + ".queuechanged"; - public static final String PLAY_STATE_CHANGED = RETRO_MUSIC_PACKAGE_NAME + ".playstatechanged"; - public static final String REPEAT_MODE_CHANGED = RETRO_MUSIC_PACKAGE_NAME + ".repeatmodechanged"; - public static final String SHUFFLE_MODE_CHANGED = RETRO_MUSIC_PACKAGE_NAME + ".shufflemodechanged"; - public static final String MEDIA_STORE_CHANGED = RETRO_MUSIC_PACKAGE_NAME + ".mediastorechanged"; - public static final String RATE_ON_GOOGLE_PLAY = "https://play.google.com/store/apps/details?id=code.name.monkey.retromusic"; - public static final String PAYPAL_ME_URL = "https://www.paypal.me/h4h14"; - public static final String GOOGLE_PLUS_COMMUNITY = "https://plus.google.com/communities/110811566242871492162"; - public static final String TRANSLATE = "http://monkeycodeapp.oneskyapp.com/collaboration/project?id=238534"; - public static final String GITHUB_PROJECT = "https://github.com/h4h13/RetroMusicPlayer"; - public static final String BASE_API_URL_KUGOU = "http://lyrics.kugou.com/"; - public static final String TELEGRAM_CHANGE_LOG = "https://t.me/retromusiclog"; - public static final String USER_PROFILE = "profile.jpg"; - public static final String USER_BANNER = "banner.jpg"; - public static final String APP_INSTAGRAM_LINK = "https://www.instagram.com/retromusicapp/"; - public static final String APP_TELEGRAM_LINK = "https://t.me/retromusicapp/"; - public static final String APP_TWITTER_LINK = "https://twitter.com/retromusicapp"; - public static final String FAQ_LINK = "https://github.com/h4h13/RetroMusicPlayer/blob/master/FAQ.md"; - public static final int CAST_SERVER_PORT = 8080; -} diff --git a/app/src/main/java/code/name/monkey/retromusic/Constants.kt b/app/src/main/java/code/name/monkey/retromusic/Constants.kt new file mode 100644 index 00000000..38f3bfb8 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/Constants.kt @@ -0,0 +1,97 @@ +package code.name.monkey.retromusic + +import android.provider.BaseColumns +import android.provider.MediaStore + +object Constants { + + @JvmField + val DISCORD_LINK = "https://discord.gg/qTecXXn" + + @JvmField + val RETRO_MUSIC_PACKAGE_NAME = "code.name.monkey.retromusic" + @JvmField + val MUSIC_PACKAGE_NAME = "com.android.music" + @JvmField + val ACTION_TOGGLE_PAUSE = "$RETRO_MUSIC_PACKAGE_NAME.togglepause" + @JvmField + val ACTION_PLAY = "$RETRO_MUSIC_PACKAGE_NAME.play" + @JvmField + val ACTION_PLAY_PLAYLIST = "$RETRO_MUSIC_PACKAGE_NAME.play.playlist" + @JvmField + val ACTION_PAUSE = "$RETRO_MUSIC_PACKAGE_NAME.pause" + @JvmField + val ACTION_STOP = "$RETRO_MUSIC_PACKAGE_NAME.stop" + @JvmField + val ACTION_SKIP = "$RETRO_MUSIC_PACKAGE_NAME.skip" + @JvmField + val ACTION_REWIND = "$RETRO_MUSIC_PACKAGE_NAME.rewind" + @JvmField + val ACTION_QUIT = "$RETRO_MUSIC_PACKAGE_NAME.quitservice" + @JvmField + val INTENT_EXTRA_PLAYLIST = RETRO_MUSIC_PACKAGE_NAME + "intentextra.playlist" + @JvmField + val INTENT_EXTRA_SHUFFLE_MODE = "$RETRO_MUSIC_PACKAGE_NAME.intentextra.shufflemode" + @JvmField + val APP_WIDGET_UPDATE = "$RETRO_MUSIC_PACKAGE_NAME.appwidgetupdate" + @JvmField + val EXTRA_APP_WIDGET_NAME = RETRO_MUSIC_PACKAGE_NAME + "app_widget_name" + + @JvmField + val META_CHANGED = "$RETRO_MUSIC_PACKAGE_NAME.metachanged" + @JvmField + val QUEUE_CHANGED = "$RETRO_MUSIC_PACKAGE_NAME.queuechanged" + @JvmField + val PLAY_STATE_CHANGED = "$RETRO_MUSIC_PACKAGE_NAME.playstatechanged" + @JvmField + val REPEAT_MODE_CHANGED = "$RETRO_MUSIC_PACKAGE_NAME.repeatmodechanged" + @JvmField + val SHUFFLE_MODE_CHANGED = "$RETRO_MUSIC_PACKAGE_NAME.shufflemodechanged" + @JvmField + val MEDIA_STORE_CHANGED = "$RETRO_MUSIC_PACKAGE_NAME.mediastorechanged" + @JvmField + val RATE_ON_GOOGLE_PLAY = "https://play.google.com/store/apps/details?id=code.name.monkey.retromusic" + @JvmField + val PAYPAL_ME_URL = "https://www.paypal.me/h4h14" + @JvmField + val GOOGLE_PLUS_COMMUNITY = "https://plus.google.com/communities/110811566242871492162" + @JvmField + val TRANSLATE = "http://monkeycodeapp.oneskyapp.com/collaboration/project?id=238534" + @JvmField + val GITHUB_PROJECT = "https://github.com/h4h13/RetroMusicPlayer" + @JvmField + val BASE_API_URL_KUGOU = "http://lyrics.kugou.com/" + @JvmField + val TELEGRAM_CHANGE_LOG = "https://t.me/retromusiclog" + @JvmField + val USER_PROFILE = "profile.jpg" + @JvmField + val USER_BANNER = "banner.jpg" + @JvmField + val APP_INSTAGRAM_LINK = "https://www.instagram.com/retromusicapp/" + @JvmField + val APP_TELEGRAM_LINK = "https://t.me/retromusicapp/" + @JvmField + val APP_TWITTER_LINK = "https://twitter.com/retromusicapp" + @JvmField + val FAQ_LINK = "https://github.com/h4h13/RetroMusicPlayer/blob/master/FAQ.md" + @JvmField + val CAST_SERVER_PORT = 8080 + + const val BASE_SELECTION = MediaStore.Audio.AudioColumns.IS_MUSIC + "=1" + " AND " + MediaStore.Audio.AudioColumns.TITLE + " != ''" + @JvmField + val BASE_PROJECTION = arrayOf(BaseColumns._ID, // 0 + MediaStore.Audio.AudioColumns.TITLE, // 1 + MediaStore.Audio.AudioColumns.TRACK, // 2 + MediaStore.Audio.AudioColumns.YEAR, // 3 + MediaStore.Audio.AudioColumns.DURATION, // 4 + MediaStore.Audio.AudioColumns.DATA, // 5 + MediaStore.Audio.AudioColumns.DATE_MODIFIED, // 6 + MediaStore.Audio.AudioColumns.ALBUM_ID, // 7 + MediaStore.Audio.AudioColumns.ALBUM, // 8 + MediaStore.Audio.AudioColumns.ARTIST_ID, // 9 + MediaStore.Audio.AudioColumns.ARTIST)// 10 + const val NUMBER_OF_TOP_TRACKS = 99 + + +} diff --git a/app/src/main/java/code/name/monkey/retromusic/Injection.java b/app/src/main/java/code/name/monkey/retromusic/Injection.java deleted file mode 100644 index 4fe94a74..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/Injection.java +++ /dev/null @@ -1,23 +0,0 @@ -package code.name.monkey.retromusic; - -import code.name.monkey.retromusic.providers.RepositoryImpl; -import code.name.monkey.retromusic.providers.interfaces.Repository; -import code.name.monkey.retromusic.rest.KogouClient; -import code.name.monkey.retromusic.rest.service.KuGouApiService; -import code.name.monkey.retromusic.util.schedulers.BaseSchedulerProvider; -import code.name.monkey.retromusic.util.schedulers.SchedulerProvider; - -public class Injection { - - public static Repository provideRepository() { - return RepositoryImpl.getInstance(); - } - - public static BaseSchedulerProvider provideSchedulerProvider() { - return SchedulerProvider.getInstance(); - } - - public static KuGouApiService provideKuGouApiService() { - return new KogouClient().getApiService(); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/Injection.kt b/app/src/main/java/code/name/monkey/retromusic/Injection.kt new file mode 100644 index 00000000..349d029f --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/Injection.kt @@ -0,0 +1,23 @@ +package code.name.monkey.retromusic + +import code.name.monkey.retromusic.providers.RepositoryImpl +import code.name.monkey.retromusic.providers.interfaces.Repository +import code.name.monkey.retromusic.rest.KogouClient +import code.name.monkey.retromusic.rest.service.KuGouApiService +import code.name.monkey.retromusic.util.schedulers.BaseSchedulerProvider +import code.name.monkey.retromusic.util.schedulers.SchedulerProvider + +object Injection { + + fun provideRepository(): Repository { + return RepositoryImpl.getInstance() + } + + fun provideSchedulerProvider(): BaseSchedulerProvider { + return SchedulerProvider.getInstance() + } + + fun provideKuGouApiService(): KuGouApiService { + return KogouClient().apiService + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/RetroApplication.java b/app/src/main/java/code/name/monkey/retromusic/RetroApplication.java deleted file mode 100644 index 8e75f135..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/RetroApplication.java +++ /dev/null @@ -1,125 +0,0 @@ -package code.name.monkey.retromusic; - -import android.content.Context; -import android.os.Build; - -import com.anjlab.android.iab.v3.BillingProcessor; -import com.anjlab.android.iab.v3.TransactionDetails; -import com.bumptech.glide.Glide; - -import androidx.annotation.NonNull; -import androidx.multidex.MultiDexApplication; -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.retromusic.appshortcuts.DynamicShortcutManager; -import uk.co.chrisjenx.calligraphy.CalligraphyConfig; - -public class RetroApplication extends MultiDexApplication { - - public static final String PRO_VERSION_PRODUCT_ID = "pro_version"; - - private static RetroApplication app; - - private BillingProcessor billingProcessor; - - public static RetroApplication getInstance() { - return app; - } - - public static Context getContext() { - return app.getApplicationContext(); - } - - public static boolean isProVersion() { - return BuildConfig.DEBUG || app.billingProcessor.isPurchased(PRO_VERSION_PRODUCT_ID); - } - - public static void deleteAppData() { - try { - // clearing app data - String packageName = app.getPackageName(); - Runtime runtime = Runtime.getRuntime(); - runtime.exec("pm clear " + packageName); - - System.exit(0); - - } catch (Exception e) { - e.printStackTrace(); - } - } - - @Override - public void onCreate() { - super.onCreate(); - app = this; - - setupErrorHandler(); - - // default theme - if (!ThemeStore.isConfigured(this, 3)) { - ThemeStore.editTheme(this) - .accentColorRes(R.color.md_green_A200) - .coloredNavigationBar(true) - .commit(); - } - - CalligraphyConfig.initDefault(new CalligraphyConfig.Builder() - .setDefaultFontPath("fonts/circular_std_book.otf") - .setFontAttrId(R.attr.fontPath) - .build() - ); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { - new DynamicShortcutManager(this).initDynamicShortcuts(); - } - - // automatically restores purchases - billingProcessor = new BillingProcessor(this, BuildConfig.GOOGLE_PLAY_LICENSE_KEY, - new BillingProcessor.IBillingHandler() { - @Override - public void onProductPurchased(@NonNull String productId, TransactionDetails details) { - } - - @Override - public void onPurchaseHistoryRestored() { - //Toast.makeText(App.this, R.string.restored_previous_purchase_please_restart, Toast.LENGTH_LONG).show(); - } - - @Override - public void onBillingError(int errorCode, Throwable error) { - } - - @Override - public void onBillingInitialized() { - } - }); - } - - private void setupErrorHandler() { - Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { - @Override - public void uncaughtException(Thread thread, Throwable throwable) { - handleUncaughtException(thread, throwable); - } - }); - } - - private void handleUncaughtException(Thread thread, Throwable throwable) { - throwable.printStackTrace(); - deleteAppData(); - //Intent intent = new Intent(this, ErrorHandlerActivity.class); - //intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - //startActivity(intent); - } - - @Override - public void onLowMemory() { - super.onLowMemory(); - Glide.with(this).onLowMemory(); - } - - @Override - public void onTerminate() { - super.onTerminate(); - billingProcessor.release(); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/RetroApplication.kt b/app/src/main/java/code/name/monkey/retromusic/RetroApplication.kt new file mode 100644 index 00000000..176e6a03 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/RetroApplication.kt @@ -0,0 +1,94 @@ +package code.name.monkey.retromusic + +import android.content.Context +import androidx.multidex.MultiDexApplication +import code.name.monkey.appthemehelper.ThemeStore +import com.anjlab.android.iab.v3.BillingProcessor +import com.anjlab.android.iab.v3.TransactionDetails +import com.bumptech.glide.Glide +import uk.co.chrisjenx.calligraphy.CalligraphyConfig + +class RetroApplication : MultiDexApplication() { + + lateinit var billingProcessor: BillingProcessor + + override fun onCreate() { + super.onCreate() + instance = this + + setupErrorHandler() + + // default theme + if (!ThemeStore.isConfigured(this, 3)) { + ThemeStore.editTheme(this) + .accentColorRes(R.color.md_green_A200) + .coloredNavigationBar(true) + .commit() + } + + // automatically restores purchases + billingProcessor = BillingProcessor(this, BuildConfig.GOOGLE_PLAY_LICENSE_KEY, + object : BillingProcessor.IBillingHandler { + override fun onProductPurchased(productId: String, details: TransactionDetails?) {} + + override fun onPurchaseHistoryRestored() { + //Toast.makeText(App.this, R.string.restored_previous_purchase_please_restart, Toast.LENGTH_LONG).show(); + } + + override fun onBillingError(errorCode: Int, error: Throwable?) {} + + override fun onBillingInitialized() {} + }) + } + + private fun setupErrorHandler() { + Thread.setDefaultUncaughtExceptionHandler { _, throwable -> handleUncaughtException(throwable) } + } + + private fun handleUncaughtException(throwable: Throwable) { + throwable.printStackTrace() + deleteAppData() + //Intent intent = new Intent(this, ErrorHandlerActivity.class); + //intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + //startActivity(intent); + } + + override fun onLowMemory() { + super.onLowMemory() + Glide.with(this).onLowMemory() + } + + override fun onTerminate() { + super.onTerminate() + billingProcessor.release() + } + + companion object { + + const val PRO_VERSION_PRODUCT_ID = "pro_version" + + lateinit var instance: RetroApplication + private set + + val context: Context + get() = instance.applicationContext + + val isProVersion: Boolean + get() = BuildConfig.DEBUG || instance.billingProcessor.isPurchased(PRO_VERSION_PRODUCT_ID) + + fun deleteAppData() { + try { + // clearing app data + val packageName = instance.packageName + val runtime = Runtime.getRuntime() + runtime.exec("pm clear $packageName") + + System.exit(0) + + } catch (e: Exception) { + e.printStackTrace() + } + + } + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/appshortcuts/AppShortcutIconGenerator.java b/app/src/main/java/code/name/monkey/retromusic/appshortcuts/AppShortcutIconGenerator.java deleted file mode 100644 index 40db1bfc..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/appshortcuts/AppShortcutIconGenerator.java +++ /dev/null @@ -1,71 +0,0 @@ -package code.name.monkey.retromusic.appshortcuts; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.Icon; -import android.graphics.drawable.LayerDrawable; -import android.os.Build; -import androidx.annotation.RequiresApi; -import android.util.TypedValue; - -import code.name.monkey.appthemehelper.ThemeStore; - -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.util.PreferenceUtil; -import code.name.monkey.retromusic.util.RetroUtil; - -/** - * @author Adrian Campos - */ -@RequiresApi(Build.VERSION_CODES.N_MR1) -public final class AppShortcutIconGenerator { - public static Icon generateThemedIcon(Context context, int iconId) { - if (PreferenceUtil.getInstance().coloredAppShortcuts()){ - return generateUserThemedIcon(context, iconId); - } else { - return generateDefaultThemedIcon(context, iconId); - } - } - - private static Icon generateDefaultThemedIcon(Context context, int iconId) { - // Return an Icon of iconId with default colors - return generateThemedIcon(context, iconId, - context.getColor(R.color.app_shortcut_default_foreground), - context.getColor(R.color.app_shortcut_default_background) - ); - } - - private static Icon generateUserThemedIcon(Context context, int iconId) { - // Get background color from context's theme - final TypedValue typedColorBackground = new TypedValue(); - context.getTheme().resolveAttribute(android.R.attr.colorBackground, typedColorBackground, true); - - // Return an Icon of iconId with those colors - return generateThemedIcon(context, iconId, - ThemeStore.accentColor(context), - typedColorBackground.data - ); - } - - private static Icon generateThemedIcon(Context context, int iconId, int foregroundColor, int backgroundColor) { - // Get and tint foreground and background drawables - Drawable vectorDrawable = RetroUtil.getTintedVectorDrawable(context, iconId, foregroundColor); - Drawable backgroundDrawable = RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_app_shortcut_background, backgroundColor); - - // Squash the two drawables together - LayerDrawable layerDrawable = new LayerDrawable(new Drawable[]{backgroundDrawable, vectorDrawable}); - - // Return as an Icon - return Icon.createWithBitmap(drawableToBitmap(layerDrawable)); - } - - private static Bitmap drawableToBitmap(Drawable drawable) { - Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(bitmap); - drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); - drawable.draw(canvas); - return bitmap; - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/appshortcuts/AppShortcutLauncherActivity.java b/app/src/main/java/code/name/monkey/retromusic/appshortcuts/AppShortcutLauncherActivity.java deleted file mode 100644 index 85e9c402..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/appshortcuts/AppShortcutLauncherActivity.java +++ /dev/null @@ -1,77 +0,0 @@ -package code.name.monkey.retromusic.appshortcuts; - -import android.app.Activity; -import android.content.Intent; -import android.os.Bundle; - -import code.name.monkey.retromusic.model.Playlist; -import code.name.monkey.retromusic.model.smartplaylist.LastAddedPlaylist; -import code.name.monkey.retromusic.model.smartplaylist.MyTopTracksPlaylist; -import code.name.monkey.retromusic.model.smartplaylist.ShuffleAllPlaylist; - -import code.name.monkey.retromusic.appshortcuts.shortcuttype.LastAddedShortcutType; -import code.name.monkey.retromusic.appshortcuts.shortcuttype.ShuffleAllShortcutType; -import code.name.monkey.retromusic.appshortcuts.shortcuttype.TopTracksShortcutType; -import code.name.monkey.retromusic.service.MusicService; - -import static code.name.monkey.retromusic.Constants.*; - -/** - * @author Adrian Campos - */ - -public class AppShortcutLauncherActivity extends Activity { - public static final String KEY_SHORTCUT_TYPE = "code.name.monkey.retromusic.appshortcuts.ShortcutType"; - - public static final int SHORTCUT_TYPE_SHUFFLE_ALL = 0; - public static final int SHORTCUT_TYPE_TOP_TRACKS = 1; - public static final int SHORTCUT_TYPE_LAST_ADDED = 2; - public static final int SHORTCUT_TYPE_NONE = 3; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - int shortcutType = SHORTCUT_TYPE_NONE; - - // Set shortcutType from the intent extras - Bundle extras = getIntent().getExtras(); - if (extras != null) { - //noinspection WrongConstant - shortcutType = extras.getInt(KEY_SHORTCUT_TYPE, SHORTCUT_TYPE_NONE); - } - - switch (shortcutType) { - case SHORTCUT_TYPE_SHUFFLE_ALL: - startServiceWithPlaylist(MusicService.SHUFFLE_MODE_SHUFFLE, - new ShuffleAllPlaylist(getApplicationContext())); - DynamicShortcutManager.reportShortcutUsed(this, ShuffleAllShortcutType.getId()); - break; - case SHORTCUT_TYPE_TOP_TRACKS: - startServiceWithPlaylist(MusicService.SHUFFLE_MODE_NONE, - new MyTopTracksPlaylist(getApplicationContext())); - DynamicShortcutManager.reportShortcutUsed(this, TopTracksShortcutType.getId()); - break; - case SHORTCUT_TYPE_LAST_ADDED: - startServiceWithPlaylist(MusicService.SHUFFLE_MODE_NONE, - new LastAddedPlaylist(getApplicationContext())); - DynamicShortcutManager.reportShortcutUsed(this, LastAddedShortcutType.getId()); - break; - } - - finish(); - } - - private void startServiceWithPlaylist(int shuffleMode, Playlist playlist) { - Intent intent = new Intent(this, MusicService.class); - intent.setAction(ACTION_PLAY_PLAYLIST); - - Bundle bundle = new Bundle(); - bundle.putParcelable(INTENT_EXTRA_PLAYLIST, playlist); - bundle.putInt(INTENT_EXTRA_SHUFFLE_MODE, shuffleMode); - - intent.putExtras(bundle); - - startService(intent); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/appshortcuts/DynamicShortcutManager.java b/app/src/main/java/code/name/monkey/retromusic/appshortcuts/DynamicShortcutManager.java deleted file mode 100644 index 5fb28054..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/appshortcuts/DynamicShortcutManager.java +++ /dev/null @@ -1,63 +0,0 @@ -package code.name.monkey.retromusic.appshortcuts; - -import android.annotation.TargetApi; -import android.content.Context; -import android.content.Intent; -import android.content.pm.ShortcutInfo; -import android.content.pm.ShortcutManager; -import android.graphics.drawable.Icon; -import android.os.Build; - -import code.name.monkey.retromusic.appshortcuts.shortcuttype.LastAddedShortcutType; -import code.name.monkey.retromusic.appshortcuts.shortcuttype.ShuffleAllShortcutType; -import code.name.monkey.retromusic.appshortcuts.shortcuttype.TopTracksShortcutType; - -import java.util.Arrays; -import java.util.List; - -/** - * @author Adrian Campos - */ - -@TargetApi(Build.VERSION_CODES.N_MR1) -public class DynamicShortcutManager { - - private Context context; - private ShortcutManager shortcutManager; - - public DynamicShortcutManager(Context context) { - this.context = context; - shortcutManager = this.context.getSystemService(ShortcutManager.class); - } - - public static ShortcutInfo createShortcut(Context context, String id, String shortLabel, String longLabel, Icon icon, Intent intent) { - return new ShortcutInfo.Builder(context, id) - .setShortLabel(shortLabel) - .setLongLabel(longLabel) - .setIcon(icon) - .setIntent(intent) - .build(); - } - - public void initDynamicShortcuts() { - if (shortcutManager.getDynamicShortcuts().size() == 0) { - shortcutManager.setDynamicShortcuts(getDefaultShortcuts()); - } - } - - public void updateDynamicShortcuts() { - shortcutManager.updateShortcuts(getDefaultShortcuts()); - } - - public List getDefaultShortcuts() { - return (Arrays.asList( - new ShuffleAllShortcutType(context).getShortcutInfo(), - new TopTracksShortcutType(context).getShortcutInfo(), - new LastAddedShortcutType(context).getShortcutInfo() - )); - } - - public static void reportShortcutUsed(Context context, String shortcutId){ - context.getSystemService(ShortcutManager.class).reportShortcutUsed(shortcutId); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/appshortcuts/shortcuttype/BaseShortcutType.java b/app/src/main/java/code/name/monkey/retromusic/appshortcuts/shortcuttype/BaseShortcutType.java deleted file mode 100644 index 28f16a7e..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/appshortcuts/shortcuttype/BaseShortcutType.java +++ /dev/null @@ -1,50 +0,0 @@ -package code.name.monkey.retromusic.appshortcuts.shortcuttype; - -import android.annotation.TargetApi; -import android.content.Context; -import android.content.Intent; -import android.content.pm.ShortcutInfo; -import android.os.Build; -import android.os.Bundle; - -import code.name.monkey.retromusic.appshortcuts.AppShortcutLauncherActivity; - - -/** - * @author Adrian Campos - */ -@TargetApi(Build.VERSION_CODES.N_MR1) -public abstract class BaseShortcutType { - - static final String ID_PREFIX = "code.name.monkey.retromusic.appshortcuts.id."; - - Context context; - - public BaseShortcutType(Context context) { - this.context = context; - } - - static public String getId() { - return ID_PREFIX + "invalid"; - } - - abstract ShortcutInfo getShortcutInfo(); - - /** - * Creates an Intent that will launch MainActivtiy and immediately play {@param songs} in either shuffle or normal mode - * - * @param shortcutType Describes the type of shortcut to create (ShuffleAll, TopTracks, custom playlist, etc.) - * @return - */ - Intent getPlaySongsIntent(int shortcutType) { - Intent intent = new Intent(context, AppShortcutLauncherActivity.class); - intent.setAction(Intent.ACTION_VIEW); - - Bundle b = new Bundle(); - b.putInt(AppShortcutLauncherActivity.KEY_SHORTCUT_TYPE, shortcutType); - - intent.putExtras(b); - - return intent; - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/appshortcuts/shortcuttype/LastAddedShortcutType.java b/app/src/main/java/code/name/monkey/retromusic/appshortcuts/shortcuttype/LastAddedShortcutType.java deleted file mode 100644 index 72db3eeb..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/appshortcuts/shortcuttype/LastAddedShortcutType.java +++ /dev/null @@ -1,34 +0,0 @@ -package code.name.monkey.retromusic.appshortcuts.shortcuttype; - -import android.annotation.TargetApi; -import android.content.Context; -import android.content.pm.ShortcutInfo; -import android.os.Build; - -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.appshortcuts.AppShortcutIconGenerator; -import code.name.monkey.retromusic.appshortcuts.AppShortcutLauncherActivity; - - -/** - * @author Adrian Campos - */ -@TargetApi(Build.VERSION_CODES.N_MR1) -public final class LastAddedShortcutType extends BaseShortcutType { - public LastAddedShortcutType(Context context) { - super(context); - } - - public static String getId() { - return ID_PREFIX + "last_added"; - } - - public ShortcutInfo getShortcutInfo() { - return new ShortcutInfo.Builder(context, getId()) - .setShortLabel(context.getString(R.string.app_shortcut_last_added_short)) - .setLongLabel(context.getString(R.string.app_shortcut_last_added_long)) - .setIcon(AppShortcutIconGenerator.generateThemedIcon(context, R.drawable.ic_app_shortcut_last_added)) - .setIntent(getPlaySongsIntent(AppShortcutLauncherActivity.SHORTCUT_TYPE_LAST_ADDED)) - .build(); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/appshortcuts/shortcuttype/ShuffleAllShortcutType.java b/app/src/main/java/code/name/monkey/retromusic/appshortcuts/shortcuttype/ShuffleAllShortcutType.java deleted file mode 100644 index be3ba008..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/appshortcuts/shortcuttype/ShuffleAllShortcutType.java +++ /dev/null @@ -1,35 +0,0 @@ -package code.name.monkey.retromusic.appshortcuts.shortcuttype; - -import android.annotation.TargetApi; -import android.content.Context; -import android.content.pm.ShortcutInfo; -import android.os.Build; - -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.appshortcuts.AppShortcutIconGenerator; -import code.name.monkey.retromusic.appshortcuts.AppShortcutLauncherActivity; - - - -/** - * @author Adrian Campos - */ -@TargetApi(Build.VERSION_CODES.N_MR1) -public final class ShuffleAllShortcutType extends BaseShortcutType { - public ShuffleAllShortcutType(Context context) { - super(context); - } - - public static String getId() { - return ID_PREFIX + "shuffle_all"; - } - - public ShortcutInfo getShortcutInfo() { - return new ShortcutInfo.Builder(context, getId()) - .setShortLabel(context.getString(R.string.app_shortcut_shuffle_all_short)) - .setLongLabel(context.getString(R.string.app_shortcut_shuffle_all_long)) - .setIcon(AppShortcutIconGenerator.generateThemedIcon(context, R.drawable.ic_app_shortcut_shuffle_all)) - .setIntent(getPlaySongsIntent(AppShortcutLauncherActivity.SHORTCUT_TYPE_SHUFFLE_ALL)) - .build(); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/appshortcuts/shortcuttype/TopTracksShortcutType.java b/app/src/main/java/code/name/monkey/retromusic/appshortcuts/shortcuttype/TopTracksShortcutType.java deleted file mode 100644 index 3e78db33..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/appshortcuts/shortcuttype/TopTracksShortcutType.java +++ /dev/null @@ -1,35 +0,0 @@ -package code.name.monkey.retromusic.appshortcuts.shortcuttype; - -import android.annotation.TargetApi; -import android.content.Context; -import android.content.pm.ShortcutInfo; -import android.os.Build; - -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.appshortcuts.AppShortcutIconGenerator; -import code.name.monkey.retromusic.appshortcuts.AppShortcutLauncherActivity; - - - -/** - * @author Adrian Campos - */ -@TargetApi(Build.VERSION_CODES.N_MR1) -public final class TopTracksShortcutType extends BaseShortcutType { - public TopTracksShortcutType(Context context) { - super(context); - } - - public static String getId() { - return ID_PREFIX + "top_tracks"; - } - - public ShortcutInfo getShortcutInfo() { - return new ShortcutInfo.Builder(context, getId()) - .setShortLabel(context.getString(R.string.app_shortcut_top_tracks_short)) - .setLongLabel(context.getString(R.string.app_shortcut_top_tracks_long)) - .setIcon(AppShortcutIconGenerator.generateThemedIcon(context, R.drawable.ic_app_shortcut_top_tracks)) - .setIntent(getPlaySongsIntent(AppShortcutLauncherActivity.SHORTCUT_TYPE_TOP_TRACKS)) - .build(); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/appwidgets/AppWidgetBig.java b/app/src/main/java/code/name/monkey/retromusic/appwidgets/AppWidgetBig.java deleted file mode 100644 index 11145dbd..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/appwidgets/AppWidgetBig.java +++ /dev/null @@ -1,174 +0,0 @@ -package code.name.monkey.retromusic.appwidgets; - -import android.app.PendingIntent; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.graphics.Bitmap; -import android.graphics.Point; -import android.graphics.drawable.Drawable; -import android.text.TextUtils; -import android.view.View; -import android.widget.RemoteViews; - -import com.bumptech.glide.Glide; -import com.bumptech.glide.request.animation.GlideAnimation; -import com.bumptech.glide.request.target.SimpleTarget; -import com.bumptech.glide.request.target.Target; - -import androidx.annotation.Nullable; -import code.name.monkey.appthemehelper.util.MaterialValueHelper; -import code.name.monkey.retromusic.Constants; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget; -import code.name.monkey.retromusic.glide.SongGlideRequest; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.service.MusicService; -import code.name.monkey.retromusic.ui.activities.MainActivity; -import code.name.monkey.retromusic.ui.activities.NowPayingActivity; -import code.name.monkey.retromusic.util.RetroUtil; - -public class AppWidgetBig extends BaseAppWidget { - - public static final String NAME = "app_widget_big"; - - private static AppWidgetBig mInstance; - private Target target; // for cancellation - - public static synchronized AppWidgetBig getInstance() { - if (mInstance == null) { - mInstance = new AppWidgetBig(); - } - return mInstance; - } - - /** - * Initialize given widgets to default state, where we launch Music on default click and hide - * actions if service not running. - */ - protected void defaultAppWidget(final Context context, final int[] appWidgetIds) { - final RemoteViews appWidgetView = new RemoteViews(context.getPackageName(), - R.layout.app_widget_big); - - appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE); - appWidgetView.setImageViewResource(R.id.image, R.drawable.default_album_art); - appWidgetView.setImageViewBitmap(R.id.button_next, createBitmap( - RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_skip_next_white_24dp, - MaterialValueHelper.getPrimaryTextColor(context, false)), 1f)); - appWidgetView.setImageViewBitmap(R.id.button_prev, createBitmap( - RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_skip_previous_white_24dp, - MaterialValueHelper.getPrimaryTextColor(context, false)), 1f)); - appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, createBitmap( - RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_play_arrow_white_24dp, - MaterialValueHelper.getPrimaryTextColor(context, false)), 1f)); - - linkButtons(context, appWidgetView); - pushUpdate(context, appWidgetIds, appWidgetView); - } - - /** - * Update all active widget instances by pushing changes - */ - public void performUpdate(final MusicService service, final int[] appWidgetIds) { - final RemoteViews appWidgetView = new RemoteViews(service.getPackageName(), - R.layout.app_widget_big); - - final boolean isPlaying = service.isPlaying(); - final Song song = service.getCurrentSong(); - - // Set the titles and artwork - if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName)) { - appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE); - } else { - appWidgetView.setViewVisibility(R.id.media_titles, View.VISIBLE); - appWidgetView.setTextViewText(R.id.title, song.title); - appWidgetView.setTextViewText(R.id.text, getSongArtistAndAlbum(song)); - } - - // Set correct drawable for pause state - int playPauseRes = - isPlaying ? R.drawable.ic_pause_white_24dp : R.drawable.ic_play_arrow_white_24dp; - appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, createBitmap( - RetroUtil.getTintedVectorDrawable(service, playPauseRes, - MaterialValueHelper.getPrimaryTextColor(service, false)), 1f)); - - // Set prev/next button drawables - appWidgetView.setImageViewBitmap(R.id.button_next, createBitmap( - RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_next_white_24dp, - MaterialValueHelper.getPrimaryTextColor(service, false)), 1f)); - appWidgetView.setImageViewBitmap(R.id.button_prev, createBitmap( - RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_previous_white_24dp, - MaterialValueHelper.getPrimaryTextColor(service, false)), 1f)); - - // Link actions buttons to intents - linkButtons(service, appWidgetView); - - // Load the album cover async and push the update on completion - Point p = RetroUtil.getScreenSize(service); - final int widgetImageSize = Math.min(p.x, p.y); - final Context appContext = service.getApplicationContext(); - service.runOnUiThread(new Runnable() { - @Override - public void run() { - if (target != null) { - Glide.clear(target); - } - target = SongGlideRequest.Builder.from(Glide.with(appContext), song) - .checkIgnoreMediaStore(appContext) - .asBitmap().build() - .into(new SimpleTarget(widgetImageSize, widgetImageSize) { - @Override - public void onResourceReady(Bitmap resource, - GlideAnimation glideAnimation) { - update(resource); - } - - @Override - public void onLoadFailed(Exception e, Drawable errorDrawable) { - super.onLoadFailed(e, errorDrawable); - update(null); - } - - private void update(@Nullable Bitmap bitmap) { - if (bitmap == null) { - appWidgetView.setImageViewResource(R.id.image, R.drawable.default_album_art); - } else { - appWidgetView.setImageViewBitmap(R.id.image, bitmap); - } - pushUpdate(appContext, appWidgetIds, appWidgetView); - } - }); - } - }); - } - - /** - * Link up various button actions using {@link PendingIntent}. - */ - private void linkButtons(final Context context, final RemoteViews views) { - Intent action; - PendingIntent pendingIntent; - - final ComponentName serviceName = new ComponentName(context, MusicService.class); - - // Home - action = new Intent(context, NowPayingActivity.class); - action.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); - pendingIntent = PendingIntent.getActivity(context, 0, action, 0); - views.setOnClickPendingIntent(R.id.clickable_area, pendingIntent); - - // Previous track - pendingIntent = buildPendingIntent(context, Constants.ACTION_REWIND, serviceName); - views.setOnClickPendingIntent(R.id.button_prev, pendingIntent); - - // Play and pause - pendingIntent = buildPendingIntent(context, Constants.ACTION_TOGGLE_PAUSE, serviceName); - views.setOnClickPendingIntent(R.id.button_toggle_play_pause, pendingIntent); - - // Next track - pendingIntent = buildPendingIntent(context, Constants.ACTION_SKIP, serviceName); - views.setOnClickPendingIntent(R.id.button_next, pendingIntent); - - - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/appwidgets/AppWidgetCard.java b/app/src/main/java/code/name/monkey/retromusic/appwidgets/AppWidgetCard.java deleted file mode 100644 index f995c47f..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/appwidgets/AppWidgetCard.java +++ /dev/null @@ -1,197 +0,0 @@ -package code.name.monkey.retromusic.appwidgets; - -import android.app.PendingIntent; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.graphics.Bitmap; -import android.graphics.drawable.Drawable; -import android.text.TextUtils; -import android.view.View; -import android.widget.RemoteViews; - -import com.bumptech.glide.Glide; -import com.bumptech.glide.request.animation.GlideAnimation; -import com.bumptech.glide.request.target.SimpleTarget; -import com.bumptech.glide.request.target.Target; - -import androidx.annotation.Nullable; -import androidx.palette.graphics.Palette; -import code.name.monkey.appthemehelper.util.MaterialValueHelper; -import code.name.monkey.retromusic.Constants; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget; -import code.name.monkey.retromusic.glide.SongGlideRequest; -import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.service.MusicService; -import code.name.monkey.retromusic.ui.activities.MainActivity; -import code.name.monkey.retromusic.ui.activities.NowPayingActivity; -import code.name.monkey.retromusic.util.RetroUtil; - -public class AppWidgetCard extends BaseAppWidget { - - public static final String NAME = "app_widget_card"; - - private static AppWidgetCard mInstance; - private static int imageSize = 0; - private static float cardRadius = 0f; - private Target target; // for cancellation - - public static synchronized AppWidgetCard getInstance() { - if (mInstance == null) { - mInstance = new AppWidgetCard(); - } - return mInstance; - } - - /** - * Initialize given widgets to default state, where we launch Music on default click and hide - * actions if service not running. - */ - protected void defaultAppWidget(final Context context, final int[] appWidgetIds) { - final RemoteViews appWidgetView = new RemoteViews(context.getPackageName(), - R.layout.app_widget_card); - - appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE); - appWidgetView.setImageViewResource(R.id.image, R.drawable.default_album_art); - appWidgetView.setImageViewBitmap(R.id.button_next, createBitmap( - RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_skip_next_white_24dp, - MaterialValueHelper.getSecondaryTextColor(context, true)), 1f)); - appWidgetView.setImageViewBitmap(R.id.button_prev, createBitmap( - RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_skip_previous_white_24dp, - MaterialValueHelper.getSecondaryTextColor(context, true)), 1f)); - appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, createBitmap( - RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_play_arrow_white_24dp, - MaterialValueHelper.getSecondaryTextColor(context, true)), 1f)); - - linkButtons(context, appWidgetView); - pushUpdate(context, appWidgetIds, appWidgetView); - } - - /** - * Update all active widget instances by pushing changes - */ - public void performUpdate(final MusicService service, final int[] appWidgetIds) { - final RemoteViews appWidgetView = new RemoteViews(service.getPackageName(), - R.layout.app_widget_card); - - final boolean isPlaying = service.isPlaying(); - final Song song = service.getCurrentSong(); - - // Set the titles and artwork - if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName)) { - appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE); - } else { - appWidgetView.setViewVisibility(R.id.media_titles, View.VISIBLE); - appWidgetView.setTextViewText(R.id.title, song.title); - appWidgetView.setTextViewText(R.id.text, getSongArtistAndAlbum(song)); - } - - // Set correct drawable for pause state - int playPauseRes = - isPlaying ? R.drawable.ic_pause_white_24dp : R.drawable.ic_play_arrow_white_24dp; - appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, createBitmap( - RetroUtil.getTintedVectorDrawable(service, playPauseRes, - MaterialValueHelper.getSecondaryTextColor(service, true)), 1f)); - - // Set prev/next button drawables - appWidgetView.setImageViewBitmap(R.id.button_next, createBitmap( - RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_next_white_24dp, - MaterialValueHelper.getSecondaryTextColor(service, true)), 1f)); - appWidgetView.setImageViewBitmap(R.id.button_prev, createBitmap( - RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_previous_white_24dp, - MaterialValueHelper.getSecondaryTextColor(service, true)), 1f)); - - // Link actions buttons to intents - linkButtons(service, appWidgetView); - - if (imageSize == 0) { - imageSize = service.getResources().getDimensionPixelSize(R.dimen.app_widget_card_image_size); - } - if (cardRadius == 0f) { - cardRadius = service.getResources().getDimension(R.dimen.app_widget_card_radius); - } - - // Load the album cover async and push the update on completion - service.runOnUiThread(new Runnable() { - @Override - public void run() { - if (target != null) { - Glide.clear(target); - } - target = SongGlideRequest.Builder.from(Glide.with(service), song) - .checkIgnoreMediaStore(service) - .generatePalette(service).build() - .centerCrop() - .into(new SimpleTarget(imageSize, imageSize) { - @Override - public void onResourceReady(BitmapPaletteWrapper resource, - GlideAnimation glideAnimation) { - Palette palette = resource.getPalette(); - update(resource.getBitmap(), palette.getVibrantColor(palette - .getMutedColor(MaterialValueHelper.getSecondaryTextColor(service, true)))); - } - - @Override - public void onLoadFailed(Exception e, Drawable errorDrawable) { - super.onLoadFailed(e, errorDrawable); - update(null, MaterialValueHelper.getSecondaryTextColor(service, true)); - } - - private void update(@Nullable Bitmap bitmap, int color) { - // Set correct drawable for pause state - int playPauseRes = isPlaying ? R.drawable.ic_pause_white_24dp - : R.drawable.ic_play_arrow_white_24dp; - appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, - createBitmap(RetroUtil.getTintedVectorDrawable(service, playPauseRes, color), 1f)); - - // Set prev/next button drawables - appWidgetView.setImageViewBitmap(R.id.button_next, createBitmap( - RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_next_white_24dp, - color), 1f)); - appWidgetView.setImageViewBitmap(R.id.button_prev, createBitmap( - RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_previous_white_24dp, - color), 1f)); - - final Drawable image = getAlbumArtDrawable(service.getResources(), bitmap); - final Bitmap roundedBitmap = createRoundedBitmap(image, imageSize, imageSize, - cardRadius, 0, cardRadius, 0); - appWidgetView.setImageViewBitmap(R.id.image, roundedBitmap); - - pushUpdate(service, appWidgetIds, appWidgetView); - } - }); - } - }); - } - - /** - * Link up various button actions using {@link PendingIntent}. - */ - private void linkButtons(final Context context, final RemoteViews views) { - Intent action; - PendingIntent pendingIntent; - - final ComponentName serviceName = new ComponentName(context, MusicService.class); - - // Home - action = new Intent(context, NowPayingActivity.class); - action.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); - pendingIntent = PendingIntent.getActivity(context, 0, action, 0); - views.setOnClickPendingIntent(R.id.image, pendingIntent); - views.setOnClickPendingIntent(R.id.media_titles, pendingIntent); - - // Previous track - pendingIntent = buildPendingIntent(context, Constants.ACTION_REWIND, serviceName); - views.setOnClickPendingIntent(R.id.button_prev, pendingIntent); - - // Play and pause - pendingIntent = buildPendingIntent(context, Constants.ACTION_TOGGLE_PAUSE, serviceName); - views.setOnClickPendingIntent(R.id.button_toggle_play_pause, pendingIntent); - - // Next track - pendingIntent = buildPendingIntent(context, Constants.ACTION_SKIP, serviceName); - views.setOnClickPendingIntent(R.id.button_next, pendingIntent); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/appwidgets/AppWidgetClassic.java b/app/src/main/java/code/name/monkey/retromusic/appwidgets/AppWidgetClassic.java deleted file mode 100644 index 9d8afa32..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/appwidgets/AppWidgetClassic.java +++ /dev/null @@ -1,182 +0,0 @@ -package code.name.monkey.retromusic.appwidgets; - -import android.app.PendingIntent; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.graphics.Bitmap; -import android.graphics.drawable.Drawable; -import androidx.annotation.Nullable; -import androidx.palette.graphics.Palette; -import android.text.TextUtils; -import android.view.View; -import android.widget.RemoteViews; -import code.name.monkey.appthemehelper.util.MaterialValueHelper; -import code.name.monkey.retromusic.Constants; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget; -import code.name.monkey.retromusic.glide.SongGlideRequest; -import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.service.MusicService; -import code.name.monkey.retromusic.ui.activities.MainActivity; -import code.name.monkey.retromusic.ui.activities.NowPayingActivity; -import code.name.monkey.retromusic.util.RetroUtil; -import com.bumptech.glide.Glide; -import com.bumptech.glide.request.animation.GlideAnimation; -import com.bumptech.glide.request.target.SimpleTarget; -import com.bumptech.glide.request.target.Target; - -public class AppWidgetClassic extends BaseAppWidget { - - public static final String NAME = "app_widget_classic"; - - private static AppWidgetClassic mInstance; - private static int imageSize = 0; - private static float cardRadius = 0f; - private Target target; // for cancellation - - public static synchronized AppWidgetClassic getInstance() { - if (mInstance == null) { - mInstance = new AppWidgetClassic(); - } - return mInstance; - } - - /** - * Initialize given widgets to default state, where we launch Music on default click and hide - * actions if service not running. - */ - protected void defaultAppWidget(final Context context, final int[] appWidgetIds) { - final RemoteViews appWidgetView = new RemoteViews(context.getPackageName(), - R.layout.app_widget_classic); - - appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE); - appWidgetView.setImageViewResource(R.id.image, R.drawable.default_album_art); - appWidgetView.setImageViewBitmap(R.id.button_next, createBitmap( - RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_skip_next_white_24dp, - MaterialValueHelper.getSecondaryTextColor(context, true)), 1f)); - appWidgetView.setImageViewBitmap(R.id.button_prev, createBitmap( - RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_skip_previous_white_24dp, - MaterialValueHelper.getSecondaryTextColor(context, true)), 1f)); - appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, createBitmap( - RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_play_arrow_white_24dp, - MaterialValueHelper.getSecondaryTextColor(context, true)), 1f)); - - linkButtons(context, appWidgetView); - pushUpdate(context, appWidgetIds, appWidgetView); - } - - /** - * Update all active widget instances by pushing changes - */ - public void performUpdate(final MusicService service, final int[] appWidgetIds) { - final RemoteViews appWidgetView = new RemoteViews(service.getPackageName(), - R.layout.app_widget_classic); - - final boolean isPlaying = service.isPlaying(); - final Song song = service.getCurrentSong(); - - // Set the titles and artwork - if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName)) { - appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE); - } else { - appWidgetView.setViewVisibility(R.id.media_titles, View.VISIBLE); - appWidgetView.setTextViewText(R.id.title, song.title); - appWidgetView.setTextViewText(R.id.text, getSongArtistAndAlbum(song)); - } - - // Link actions buttons to intents - linkButtons(service, appWidgetView); - - if (imageSize == 0) { - imageSize = service.getResources() - .getDimensionPixelSize(R.dimen.app_widget_classic_image_size); - } - if (cardRadius == 0f) { - cardRadius = service.getResources().getDimension(R.dimen.app_widget_card_radius); - } - - // Load the album cover async and push the update on completion - final Context appContext = service.getApplicationContext(); - service.runOnUiThread(new Runnable() { - @Override - public void run() { - if (target != null) { - Glide.clear(target); - } - target = SongGlideRequest.Builder.from(Glide.with(appContext), song) - .checkIgnoreMediaStore(appContext) - .generatePalette(service).build() - .centerCrop() - .into(new SimpleTarget(imageSize, imageSize) { - @Override - public void onResourceReady(BitmapPaletteWrapper resource, - GlideAnimation glideAnimation) { - Palette palette = resource.getPalette(); - update(resource.getBitmap(), palette.getVibrantColor(palette - .getMutedColor(MaterialValueHelper.getSecondaryTextColor(appContext, true)))); - } - - @Override - public void onLoadFailed(Exception e, Drawable errorDrawable) { - super.onLoadFailed(e, errorDrawable); - update(null, MaterialValueHelper.getSecondaryTextColor(appContext, true)); - } - - private void update(@Nullable Bitmap bitmap, int color) { - // Set correct drawable for pause state - int playPauseRes = isPlaying ? R.drawable.ic_pause_white_24dp - : R.drawable.ic_play_arrow_white_24dp; - appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, - createBitmap(RetroUtil.getTintedVectorDrawable(service, playPauseRes, color), 1f)); - - // Set prev/next button drawables - appWidgetView.setImageViewBitmap(R.id.button_next, createBitmap( - RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_next_white_24dp, - color), 1f)); - appWidgetView.setImageViewBitmap(R.id.button_prev, createBitmap( - RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_previous_white_24dp, - color), 1f)); - - final Drawable image = getAlbumArtDrawable(service.getResources(), bitmap); - final Bitmap roundedBitmap = createRoundedBitmap(image, imageSize, imageSize, - cardRadius, 0, cardRadius, 0); - appWidgetView.setImageViewBitmap(R.id.image, roundedBitmap); - - pushUpdate(appContext, appWidgetIds, appWidgetView); - } - }); - } - }); - } - - /** - * Link up various button actions using {@link PendingIntent}. - */ - private void linkButtons(final Context context, final RemoteViews views) { - Intent action; - PendingIntent pendingIntent; - - final ComponentName serviceName = new ComponentName(context, MusicService.class); - - // Home - action = new Intent(context, NowPayingActivity.class); - action.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); - pendingIntent = PendingIntent.getActivity(context, 0, action, 0); - views.setOnClickPendingIntent(R.id.image, pendingIntent); - views.setOnClickPendingIntent(R.id.media_titles, pendingIntent); - - // Previous track - pendingIntent = buildPendingIntent(context, Constants.ACTION_REWIND, serviceName); - views.setOnClickPendingIntent(R.id.button_prev, pendingIntent); - - // Play and pause - pendingIntent = buildPendingIntent(context, Constants.ACTION_TOGGLE_PAUSE, serviceName); - views.setOnClickPendingIntent(R.id.button_toggle_play_pause, pendingIntent); - - // Next track - pendingIntent = buildPendingIntent(context, Constants.ACTION_SKIP, serviceName); - views.setOnClickPendingIntent(R.id.button_next, pendingIntent); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/appwidgets/AppWidgetSmall.java b/app/src/main/java/code/name/monkey/retromusic/appwidgets/AppWidgetSmall.java deleted file mode 100644 index 376254f8..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/appwidgets/AppWidgetSmall.java +++ /dev/null @@ -1,187 +0,0 @@ -package code.name.monkey.retromusic.appwidgets; - -import android.app.PendingIntent; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.graphics.Bitmap; -import android.graphics.drawable.Drawable; -import androidx.annotation.Nullable; -import androidx.palette.graphics.Palette; -import android.text.TextUtils; -import android.view.View; -import android.widget.RemoteViews; -import code.name.monkey.appthemehelper.util.MaterialValueHelper; -import code.name.monkey.retromusic.Constants; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget; -import code.name.monkey.retromusic.glide.SongGlideRequest; -import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.service.MusicService; -import code.name.monkey.retromusic.ui.activities.MainActivity; -import code.name.monkey.retromusic.ui.activities.NowPayingActivity; -import code.name.monkey.retromusic.util.RetroUtil; -import com.bumptech.glide.Glide; -import com.bumptech.glide.request.animation.GlideAnimation; -import com.bumptech.glide.request.target.SimpleTarget; -import com.bumptech.glide.request.target.Target; - -public class AppWidgetSmall extends BaseAppWidget { - - public static final String NAME = "app_widget_small"; - - private static AppWidgetSmall mInstance; - private static int imageSize = 0; - private static float cardRadius = 0f; - private Target target; // for cancellation - - public static synchronized AppWidgetSmall getInstance() { - if (mInstance == null) { - mInstance = new AppWidgetSmall(); - } - return mInstance; - } - - /** - * Initialize given widgets to default state, where we launch Music on default click and hide - * actions if service not running. - */ - protected void defaultAppWidget(final Context context, final int[] appWidgetIds) { - final RemoteViews appWidgetView = new RemoteViews(context.getPackageName(), - R.layout.app_widget_small); - - appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE); - appWidgetView.setImageViewResource(R.id.image, R.drawable.default_album_art); - appWidgetView.setImageViewBitmap(R.id.button_next, createBitmap( - RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_skip_next_white_24dp, - MaterialValueHelper.getSecondaryTextColor(context, true)), 1f)); - appWidgetView.setImageViewBitmap(R.id.button_prev, createBitmap( - RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_skip_previous_white_24dp, - MaterialValueHelper.getSecondaryTextColor(context, true)), 1f)); - appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, createBitmap( - RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_play_arrow_white_24dp, - MaterialValueHelper.getSecondaryTextColor(context, true)), 1f)); - - linkButtons(context, appWidgetView); - pushUpdate(context, appWidgetIds, appWidgetView); - } - - /** - * Update all active widget instances by pushing changes - */ - public void performUpdate(final MusicService service, final int[] appWidgetIds) { - final RemoteViews appWidgetView = new RemoteViews(service.getPackageName(), - R.layout.app_widget_small); - - final boolean isPlaying = service.isPlaying(); - final Song song = service.getCurrentSong(); - - // Set the titles and artwork - if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName)) { - appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE); - } else { - if (TextUtils.isEmpty(song.title) || TextUtils.isEmpty(song.artistName)) { - appWidgetView.setTextViewText(R.id.text_separator, ""); - } else { - appWidgetView.setTextViewText(R.id.text_separator, "•"); - } - - appWidgetView.setViewVisibility(R.id.media_titles, View.VISIBLE); - appWidgetView.setTextViewText(R.id.title, song.title); - appWidgetView.setTextViewText(R.id.text, song.artistName); - } - - // Link actions buttons to intents - linkButtons(service, appWidgetView); - - if (imageSize == 0) { - imageSize = service.getResources().getDimensionPixelSize(R.dimen.app_widget_small_image_size); - } - if (cardRadius == 0f) { - cardRadius = service.getResources().getDimension(R.dimen.app_widget_card_radius); - } - - // Load the album cover async and push the update on completion - final Context appContext = service.getApplicationContext(); - service.runOnUiThread(new Runnable() { - @Override - public void run() { - if (target != null) { - Glide.clear(target); - } - target = SongGlideRequest.Builder.from(Glide.with(appContext), song) - .checkIgnoreMediaStore(appContext) - .generatePalette(service).build() - .centerCrop() - .into(new SimpleTarget(imageSize, imageSize) { - @Override - public void onResourceReady(BitmapPaletteWrapper resource, - GlideAnimation glideAnimation) { - Palette palette = resource.getPalette(); - update(resource.getBitmap(), palette.getVibrantColor(palette - .getMutedColor(MaterialValueHelper.getSecondaryTextColor(appContext, true)))); - } - - @Override - public void onLoadFailed(Exception e, Drawable errorDrawable) { - super.onLoadFailed(e, errorDrawable); - update(null, MaterialValueHelper.getSecondaryTextColor(appContext, true)); - } - - private void update(@Nullable Bitmap bitmap, int color) { - // Set correct drawable for pause state - int playPauseRes = isPlaying ? R.drawable.ic_pause_white_24dp - : R.drawable.ic_play_arrow_white_24dp; - appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, - createBitmap(RetroUtil.getTintedVectorDrawable(service, playPauseRes, color), 1f)); - - // Set prev/next button drawables - appWidgetView.setImageViewBitmap(R.id.button_next, createBitmap( - RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_next_white_24dp, - color), 1f)); - appWidgetView.setImageViewBitmap(R.id.button_prev, createBitmap( - RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_previous_white_24dp, - color), 1f)); - - final Drawable image = getAlbumArtDrawable(service.getResources(), bitmap); - final Bitmap roundedBitmap = createRoundedBitmap(image, imageSize, imageSize, - cardRadius, 0, 0, 0); - appWidgetView.setImageViewBitmap(R.id.image, roundedBitmap); - - pushUpdate(appContext, appWidgetIds, appWidgetView); - } - }); - } - }); - } - - /** - * Link up various button actions using {@link PendingIntent}. - */ - private void linkButtons(final Context context, final RemoteViews views) { - Intent action; - PendingIntent pendingIntent; - - final ComponentName serviceName = new ComponentName(context, MusicService.class); - - // Home - action = new Intent(context, NowPayingActivity.class); - action.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); - pendingIntent = PendingIntent.getActivity(context, 0, action, 0); - views.setOnClickPendingIntent(R.id.image, pendingIntent); - views.setOnClickPendingIntent(R.id.media_titles, pendingIntent); - - // Previous track - pendingIntent = buildPendingIntent(context, Constants.ACTION_REWIND, serviceName); - views.setOnClickPendingIntent(R.id.button_prev, pendingIntent); - - // Play and pause - pendingIntent = buildPendingIntent(context, Constants.ACTION_TOGGLE_PAUSE, serviceName); - views.setOnClickPendingIntent(R.id.button_toggle_play_pause, pendingIntent); - - // Next track - pendingIntent = buildPendingIntent(context, Constants.ACTION_SKIP, serviceName); - views.setOnClickPendingIntent(R.id.button_next, pendingIntent); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/appwidgets/BootReceiver.java b/app/src/main/java/code/name/monkey/retromusic/appwidgets/BootReceiver.java deleted file mode 100644 index 38fb2989..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/appwidgets/BootReceiver.java +++ /dev/null @@ -1,32 +0,0 @@ -package code.name.monkey.retromusic.appwidgets; - -import android.appwidget.AppWidgetManager; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.os.Build; - -import code.name.monkey.retromusic.service.MusicService; - - -/** - * @author Eugene Cheung (arkon) - */ -public class BootReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - final AppWidgetManager widgetManager = AppWidgetManager.getInstance(context); - - // Start music service if there are any existing widgets - if (widgetManager.getAppWidgetIds(new ComponentName(context, AppWidgetBig.class)).length > 0 || - widgetManager.getAppWidgetIds(new ComponentName(context, AppWidgetClassic.class)).length > 0 || - widgetManager.getAppWidgetIds(new ComponentName(context, AppWidgetSmall.class)).length > 0 || - widgetManager.getAppWidgetIds(new ComponentName(context, AppWidgetCard.class)).length > 0) { - final Intent serviceIntent = new Intent(context, MusicService.class); - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { // not allowed on Oreo - context.startService(serviceIntent); - } - } - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/appwidgets/base/BaseAppWidget.java b/app/src/main/java/code/name/monkey/retromusic/appwidgets/base/BaseAppWidget.java deleted file mode 100644 index acb9476d..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/appwidgets/base/BaseAppWidget.java +++ /dev/null @@ -1,163 +0,0 @@ -package code.name.monkey.retromusic.appwidgets.base; - -import android.app.PendingIntent; -import android.appwidget.AppWidgetManager; -import android.appwidget.AppWidgetProvider; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.BitmapShader; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.RectF; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.os.Build; -import android.text.TextUtils; -import android.widget.RemoteViews; - -import code.name.monkey.retromusic.Constants; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.service.MusicService; - -public abstract class BaseAppWidget extends AppWidgetProvider { - - public static final String NAME = "app_widget"; - - protected static Bitmap createBitmap(Drawable drawable, float sizeMultiplier) { - Bitmap bitmap = Bitmap.createBitmap((int) (drawable.getIntrinsicWidth() * sizeMultiplier), - (int) (drawable.getIntrinsicHeight() * sizeMultiplier), Bitmap.Config.ARGB_8888); - Canvas c = new Canvas(bitmap); - drawable.setBounds(0, 0, c.getWidth(), c.getHeight()); - drawable.draw(c); - return bitmap; - } - - protected static Bitmap createRoundedBitmap(Drawable drawable, int width, int height, float tl, - float tr, float bl, float br) { - if (drawable == null) { - return null; - } - - Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - Canvas c = new Canvas(bitmap); - drawable.setBounds(0, 0, width, height); - drawable.draw(c); - - Bitmap rounded = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - - Canvas canvas = new Canvas(rounded); - Paint paint = new Paint(); - paint.setShader( - new BitmapShader(bitmap, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP)); - paint.setAntiAlias(true); - canvas.drawPath(composeRoundedRectPath(new RectF(0, 0, width, height), tl, tr, bl, br), paint); - - return rounded; - } - - protected static Path composeRoundedRectPath(RectF rect, float tl, float tr, float bl, float br) { - Path path = new Path(); - tl = tl < 0 ? 0 : tl; - tr = tr < 0 ? 0 : tr; - bl = bl < 0 ? 0 : bl; - br = br < 0 ? 0 : br; - - path.moveTo(rect.left + tl, rect.top); - path.lineTo(rect.right - tr, rect.top); - path.quadTo(rect.right, rect.top, rect.right, rect.top + tr); - path.lineTo(rect.right, rect.bottom - br); - path.quadTo(rect.right, rect.bottom, rect.right - br, rect.bottom); - path.lineTo(rect.left + bl, rect.bottom); - path.quadTo(rect.left, rect.bottom, rect.left, rect.bottom - bl); - path.lineTo(rect.left, rect.top + tl); - path.quadTo(rect.left, rect.top, rect.left + tl, rect.top); - path.close(); - - return path; - } - - /** - * {@inheritDoc} - */ - @Override - public void onUpdate(final Context context, final AppWidgetManager appWidgetManager, - final int[] appWidgetIds) { - defaultAppWidget(context, appWidgetIds); - final Intent updateIntent = new Intent(Constants.APP_WIDGET_UPDATE); - updateIntent.putExtra(Constants.EXTRA_APP_WIDGET_NAME, NAME); - updateIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds); - updateIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); - context.sendBroadcast(updateIntent); - } - - /** - * Handle a change notification coming over from {@link MusicService} - */ - public void notifyChange(final MusicService service, final String what) { - if (hasInstances(service)) { - if (Constants.META_CHANGED.equals(what) || Constants.PLAY_STATE_CHANGED.equals(what)) { - performUpdate(service, null); - } - } - } - - protected void pushUpdate(final Context context, final int[] appWidgetIds, - final RemoteViews views) { - final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); - if (appWidgetIds != null) { - appWidgetManager.updateAppWidget(appWidgetIds, views); - } else { - appWidgetManager.updateAppWidget(new ComponentName(context, getClass()), views); - } - } - - /** - * Check against {@link AppWidgetManager} if there are any instances of this widget. - */ - protected boolean hasInstances(final Context context) { - final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); - final int[] mAppWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, - getClass())); - return mAppWidgetIds.length > 0; - } - - protected PendingIntent buildPendingIntent(Context context, final String action, - final ComponentName serviceName) { - Intent intent = new Intent(action); - intent.setComponent(serviceName); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - return PendingIntent.getForegroundService(context, 0, intent, 0); - } else { - return PendingIntent.getService(context, 0, intent, 0); - } - } - - abstract protected void defaultAppWidget(final Context context, final int[] appWidgetIds); - - abstract public void performUpdate(final MusicService service, final int[] appWidgetIds); - - protected Drawable getAlbumArtDrawable(final Resources resources, final Bitmap bitmap) { - Drawable image; - if (bitmap == null) { - image = resources.getDrawable(R.drawable.default_album_art); - } else { - image = new BitmapDrawable(resources, bitmap); - } - return image; - } - - protected String getSongArtistAndAlbum(final Song song) { - final StringBuilder builder = new StringBuilder(); - builder.append(song.artistName); - if (!TextUtils.isEmpty(song.artistName) && !TextUtils.isEmpty(song.albumName)) { - builder.append(" • "); - } - builder.append(song.albumName); - return builder.toString(); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/cast/CastHelper.java b/app/src/main/java/code/name/monkey/retromusic/cast/CastHelper.java index 89bfa519..a8951ee0 100644 --- a/app/src/main/java/code/name/monkey/retromusic/cast/CastHelper.java +++ b/app/src/main/java/code/name/monkey/retromusic/cast/CastHelper.java @@ -16,6 +16,8 @@ import code.name.monkey.retromusic.Constants; import code.name.monkey.retromusic.model.Song; import code.name.monkey.retromusic.util.RetroUtil; +import static code.name.monkey.retromusic.Constants.CAST_SERVER_PORT; + public class CastHelper { public static void startCasting(CastSession castSession, Song song) { @@ -23,22 +25,22 @@ public class CastHelper { String ipAddress = RetroUtil.getIPAddress(true); URL baseUrl; try { - baseUrl = new URL("https", ipAddress, Constants.CAST_SERVER_PORT, ""); + baseUrl = new URL("https", ipAddress,CAST_SERVER_PORT, ""); } catch (MalformedURLException e) { e.printStackTrace(); return; } - String songUrl = baseUrl.toString() + "/song?id=" + song.id; - String albumArtUrl = baseUrl.toString() + "/albumart?id=" + song.albumId; + String songUrl = baseUrl.toString() + "/song?id=" + song.getId(); + String albumArtUrl = baseUrl.toString() + "/albumart?id=" + song.getAlbumId(); MediaMetadata musicMetadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MUSIC_TRACK); - musicMetadata.putString(MediaMetadata.KEY_TITLE, song.title); - musicMetadata.putString(MediaMetadata.KEY_ARTIST, song.artistName); - musicMetadata.putString(MediaMetadata.KEY_ALBUM_TITLE, song.albumName); - musicMetadata.putInt(MediaMetadata.KEY_TRACK_NUMBER, song.trackNumber); + musicMetadata.putString(MediaMetadata.KEY_TITLE, song.getTitle()); + musicMetadata.putString(MediaMetadata.KEY_ARTIST, song.getArtistName()); + musicMetadata.putString(MediaMetadata.KEY_ALBUM_TITLE, song.getAlbumName()); + musicMetadata.putInt(MediaMetadata.KEY_TRACK_NUMBER, song.getTrackNumber()); musicMetadata.addImage(new WebImage(Uri.parse(albumArtUrl))); try { @@ -46,7 +48,7 @@ public class CastHelper { .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED) .setContentType("audio/mpeg") .setMetadata(musicMetadata) - .setStreamDuration(song.duration) + .setStreamDuration(song.getDuration()) .build(); RemoteMediaClient remoteMediaClient = castSession.getRemoteMediaClient(); remoteMediaClient.load(mediaInfo, new MediaLoadOptions.Builder() diff --git a/app/src/main/java/code/name/monkey/retromusic/cast/WebServer.java b/app/src/main/java/code/name/monkey/retromusic/cast/WebServer.java index fd96ae8c..172514de 100644 --- a/app/src/main/java/code/name/monkey/retromusic/cast/WebServer.java +++ b/app/src/main/java/code/name/monkey/retromusic/cast/WebServer.java @@ -13,13 +13,15 @@ import code.name.monkey.retromusic.Constants; import code.name.monkey.retromusic.util.RetroUtil; import fi.iki.elonen.NanoHTTPD; +import static code.name.monkey.retromusic.Constants.CAST_SERVER_PORT; + public class WebServer extends NanoHTTPD { private Context context; private Uri songUri, albumArtUri; public WebServer(Context context) { - super(Constants.CAST_SERVER_PORT); + super(CAST_SERVER_PORT); this.context = context; } diff --git a/app/src/main/java/code/name/monkey/retromusic/dialogs/AddToPlaylistDialog.java b/app/src/main/java/code/name/monkey/retromusic/dialogs/AddToPlaylistDialog.java index 4741ef29..2a8cfa7c 100644 --- a/app/src/main/java/code/name/monkey/retromusic/dialogs/AddToPlaylistDialog.java +++ b/app/src/main/java/code/name/monkey/retromusic/dialogs/AddToPlaylistDialog.java @@ -75,7 +75,7 @@ public class AddToPlaylistDialog extends RoundedBottomSheetDialogFragment { super.onViewCreated(view, savedInstanceState); title.setTextColor(ThemeStore.textColorPrimary(getContext())); final ArrayList songs = getArguments().getParcelableArrayList("songs"); - final ArrayList playlists = PlaylistLoader.getAllPlaylists(getActivity()).blockingFirst(); + final ArrayList playlists = PlaylistLoader.INSTANCE.getAllPlaylists(getActivity()).blockingFirst(); final AddToPlaylist playlistAdapter = new AddToPlaylist(getActivity(), playlists, R.layout.item_playlist, songs, getDialog()); playlist.setLayoutManager(new LinearLayoutManager(getContext())); playlist.setItemAnimator(new DefaultItemAnimator()); diff --git a/app/src/main/java/code/name/monkey/retromusic/dialogs/DeleteSongsDialog.java b/app/src/main/java/code/name/monkey/retromusic/dialogs/DeleteSongsDialog.java index ca670503..0f725635 100644 --- a/app/src/main/java/code/name/monkey/retromusic/dialogs/DeleteSongsDialog.java +++ b/app/src/main/java/code/name/monkey/retromusic/dialogs/DeleteSongsDialog.java @@ -82,7 +82,7 @@ public class DeleteSongsDialog extends RoundedBottomSheetDialogFragment { if (songs.size() > 1) { content = Html.fromHtml(getString(R.string.delete_x_songs, songs.size())); } else { - content = Html.fromHtml(getString(R.string.delete_song_x, songs.get(0).title)); + content = Html.fromHtml(getString(R.string.delete_song_x, songs.get(0).getTitle())); } this.title.setText(content); } diff --git a/app/src/main/java/code/name/monkey/retromusic/dialogs/MainOptionsBottomSheetDialogFragment.java b/app/src/main/java/code/name/monkey/retromusic/dialogs/MainOptionsBottomSheetDialogFragment.java index 6c08adaa..0374f023 100644 --- a/app/src/main/java/code/name/monkey/retromusic/dialogs/MainOptionsBottomSheetDialogFragment.java +++ b/app/src/main/java/code/name/monkey/retromusic/dialogs/MainOptionsBottomSheetDialogFragment.java @@ -81,8 +81,8 @@ public class MainOptionsBottomSheetDialogFragment extends RoundedBottomSheetDial public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View layout = inflater.inflate(R.layout.fragment_main_options, container, false); ButterKnife.bind(this, layout); - layout.findViewById(R.id.action_buy_pro).setVisibility(RetroApplication.isProVersion() ? View.GONE : View.VISIBLE); - ButterKnife.apply(materialButtons, textColor, ThemeStore.textColorPrimary(getContext())); + layout.findViewById(R.id.action_buy_pro).setVisibility(RetroApplication.Companion.isProVersion() ? View.GONE : View.VISIBLE); + //ButterKnife.apply(materialButtons, textColor, ThemeStore.textColorPrimary(getContext())); return layout; } @@ -157,7 +157,7 @@ public class MainOptionsBottomSheetDialogFragment extends RoundedBottomSheetDial .setQuality(75) .setCompressFormat(Bitmap.CompressFormat.WEBP) .compressToBitmapAsFlowable( - new File(PreferenceUtil.getInstance().getProfileImage(), USER_PROFILE)) + new File(PreferenceUtil.getInstance().getProfileImage(),USER_PROFILE)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(userImageBottom::setImageBitmap, diff --git a/app/src/main/java/code/name/monkey/retromusic/dialogs/RemoveFromPlaylistDialog.java b/app/src/main/java/code/name/monkey/retromusic/dialogs/RemoveFromPlaylistDialog.java deleted file mode 100644 index 6f144747..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/dialogs/RemoveFromPlaylistDialog.java +++ /dev/null @@ -1,93 +0,0 @@ -package code.name.monkey.retromusic.dialogs; - -import android.os.Bundle; -import android.text.Html; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import java.util.ArrayList; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.model.PlaylistSong; -import code.name.monkey.retromusic.util.PlaylistsUtil; -import code.name.monkey.retromusic.views.RoundedBottomSheetDialogFragment; - - -public class RemoveFromPlaylistDialog extends RoundedBottomSheetDialogFragment { - @BindView(R.id.action_remove) - TextView remove; - - @BindView(R.id.title) - TextView title; - - @BindView(R.id.action_cancel) - TextView cancel; - - @NonNull - public static RemoveFromPlaylistDialog create(PlaylistSong song) { - ArrayList list = new ArrayList<>(); - list.add(song); - return create(list); - } - - @NonNull - public static RemoveFromPlaylistDialog create(ArrayList songs) { - RemoveFromPlaylistDialog dialog = new RemoveFromPlaylistDialog(); - Bundle args = new Bundle(); - args.putParcelableArrayList("songs", songs); - dialog.setArguments(args); - return dialog; - } - - @OnClick({R.id.action_cancel, R.id.action_remove}) - void actions(View view) { - final ArrayList songs = getArguments().getParcelableArrayList("songs"); - switch (view.getId()) { - case R.id.action_remove: - if (getActivity() == null) - return; - PlaylistsUtil.removeFromPlaylist(getActivity(), songs); - break; - default: - } - dismiss(); - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View layout = inflater.inflate(R.layout.dialog_remove_from_playlist, container, false); - ButterKnife.bind(this, layout); - return layout; - } - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - //noinspection unchecked - final ArrayList songs = getArguments().getParcelableArrayList("songs"); - int title; - CharSequence content; - if (songs != null && songs.size() > 1) { - title = R.string.remove_songs_from_playlist_title; - content = Html.fromHtml(getString(R.string.remove_x_songs_from_playlist, songs.size())); - } else { - title = R.string.remove_song_from_playlist_title; - content = Html.fromHtml(getString(R.string.remove_song_x_from_playlist, songs.get(0).title)); - } - this.remove.setText(content); - this.title.setText(title); - - this.title.setTextColor(ThemeStore.textColorPrimary(getContext())); - this.remove.setTextColor(ThemeStore.textColorSecondary(getContext())); - this.cancel.setTextColor(ThemeStore.textColorSecondary(getContext())); - } -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/dialogs/RemoveFromPlaylistDialog.kt b/app/src/main/java/code/name/monkey/retromusic/dialogs/RemoveFromPlaylistDialog.kt new file mode 100644 index 00000000..38726666 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/dialogs/RemoveFromPlaylistDialog.kt @@ -0,0 +1,91 @@ +package code.name.monkey.retromusic.dialogs + +import android.os.Build +import android.os.Bundle +import android.text.Html +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import butterknife.BindView +import butterknife.ButterKnife +import butterknife.OnClick +import code.name.monkey.appthemehelper.ThemeStore +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.model.PlaylistSong +import code.name.monkey.retromusic.util.PlaylistsUtil +import code.name.monkey.retromusic.views.RoundedBottomSheetDialogFragment +import java.util.* + + +class RemoveFromPlaylistDialog : RoundedBottomSheetDialogFragment() { + @BindView(R.id.action_remove) + internal var remove: TextView? = null + + @BindView(R.id.title) + internal var title: TextView? = null + + @BindView(R.id.action_cancel) + internal var cancel: TextView? = null + + @OnClick(R.id.action_cancel, R.id.action_remove) + internal fun actions(view: View) { + val songs = arguments!!.getParcelableArrayList("songs") + when (view.id) { + R.id.action_remove -> { + if (activity == null) + return + PlaylistsUtil.removeFromPlaylist(activity!!, songs!!) + } + } + dismiss() + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + val layout = inflater.inflate(R.layout.dialog_remove_from_playlist, container, false) + ButterKnife.bind(this, layout) + return layout + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + val songs = arguments!!.getParcelableArrayList("songs") + val title: Int + val content: CharSequence + if (songs != null && songs.size > 1) { + title = R.string.remove_songs_from_playlist_title + content = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + Html.fromHtml(getString(R.string.remove_x_songs_from_playlist, songs.size), Html.FROM_HTML_MODE_LEGACY) + } else { + Html.fromHtml(getString(R.string.remove_x_songs_from_playlist, songs.size)) + } + } else { + title = R.string.remove_song_from_playlist_title + content = Html.fromHtml(getString(R.string.remove_song_x_from_playlist, songs!![0].title)) + } + this.remove!!.text = content + this.title!!.setText(title) + + this.title!!.setTextColor(ThemeStore.textColorPrimary(context!!)) + this.remove!!.setTextColor(ThemeStore.textColorSecondary(context!!)) + this.cancel!!.setTextColor(ThemeStore.textColorSecondary(context!!)) + } + + companion object { + + fun create(song: PlaylistSong): RemoveFromPlaylistDialog { + val list = ArrayList() + list.add(song) + return create(list) + } + + fun create(songs: ArrayList): RemoveFromPlaylistDialog { + val dialog = RemoveFromPlaylistDialog() + val args = Bundle() + args.putParcelableArrayList("songs", songs) + dialog.arguments = args + return dialog + } + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/dialogs/SleepTimerDialog.java b/app/src/main/java/code/name/monkey/retromusic/dialogs/SleepTimerDialog.java index abfad350..3e661319 100755 --- a/app/src/main/java/code/name/monkey/retromusic/dialogs/SleepTimerDialog.java +++ b/app/src/main/java/code/name/monkey/retromusic/dialogs/SleepTimerDialog.java @@ -168,7 +168,7 @@ public class SleepTimerDialog extends RoundedBottomSheetDialogFragment { private Intent makeTimerIntent() { return new Intent(getActivity(), MusicService.class) - .setAction(ACTION_QUIT); + .setAction( ACTION_QUIT); } private class TimerUpdater extends CountDownTimer { diff --git a/app/src/main/java/code/name/monkey/retromusic/dialogs/SongDetailDialog.java b/app/src/main/java/code/name/monkey/retromusic/dialogs/SongDetailDialog.java index 6d3a6930..e67a4a68 100644 --- a/app/src/main/java/code/name/monkey/retromusic/dialogs/SongDetailDialog.java +++ b/app/src/main/java/code/name/monkey/retromusic/dialogs/SongDetailDialog.java @@ -94,7 +94,7 @@ public class SongDetailDialog extends RoundedBottomSheetDialogFragment { final Song song = getArguments().getParcelable("song"); if (song != null) { - final File songFile = new File(song.data); + final File songFile = new File(song.getData()); if (songFile.exists()) { textViews.get(1).setText(makeTextWithTitle(context, R.string.label_file_name, songFile.getName())); textViews.get(2).setText(makeTextWithTitle(context, R.string.label_file_path, songFile.getAbsolutePath())); @@ -110,12 +110,12 @@ public class SongDetailDialog extends RoundedBottomSheetDialogFragment { } catch (@NonNull CannotReadException | IOException | TagException | ReadOnlyFileException | InvalidAudioFrameException e) { Log.e(TAG, "error while reading the song file", e); // fallback - textViews.get(5).setText(makeTextWithTitle(context, R.string.label_track_length, MusicUtil.getReadableDurationString(song.duration))); + textViews.get(5).setText(makeTextWithTitle(context, R.string.label_track_length, MusicUtil.getReadableDurationString(song.getDuration()))); } } else { // fallback - textViews.get(1).setText(makeTextWithTitle(context, R.string.label_file_name, song.title)); - textViews.get(5).setText(makeTextWithTitle(context, R.string.label_track_length, MusicUtil.getReadableDurationString(song.duration))); + textViews.get(1).setText(makeTextWithTitle(context, R.string.label_file_name, song.getTitle())); + textViews.get(5).setText(makeTextWithTitle(context, R.string.label_track_length, MusicUtil.getReadableDurationString(song.getDuration()))); } } diff --git a/app/src/main/java/code/name/monkey/retromusic/dialogs/SongShareDialog.java b/app/src/main/java/code/name/monkey/retromusic/dialogs/SongShareDialog.java index 96b70347..7b804a7e 100644 --- a/app/src/main/java/code/name/monkey/retromusic/dialogs/SongShareDialog.java +++ b/app/src/main/java/code/name/monkey/retromusic/dialogs/SongShareDialog.java @@ -51,7 +51,7 @@ public class SongShareDialog extends RoundedBottomSheetDialogFragment { super.onViewCreated(view, savedInstanceState); final Song song = getArguments().getParcelable("song"); - audioFile.setText(getString(R.string.currently_listening_to_x_by_x, song.title, song.artistName)); + audioFile.setText(getString(R.string.currently_listening_to_x_by_x, song.getTitle(), song.getArtistName())); audioFile.setTextColor(ThemeStore.textColorSecondary(getContext())); audioText.setTextColor(ThemeStore.textColorSecondary(getContext())); title.setTextColor(ThemeStore.textColorPrimary(getContext())); @@ -60,7 +60,7 @@ public class SongShareDialog extends RoundedBottomSheetDialogFragment { @OnClick({R.id.option_2, R.id.option_1}) void onClick(View view) { final Song song = getArguments().getParcelable("song"); - final String currentlyListening = getString(R.string.currently_listening_to_x_by_x, song.title, song.artistName); + final String currentlyListening = getString(R.string.currently_listening_to_x_by_x, song.getTitle(), song.getArtistName()); switch (view.getId()) { case R.id.option_1: startActivity(Intent.createChooser( diff --git a/app/src/main/java/code/name/monkey/retromusic/glide/ArtistGlideRequest.java b/app/src/main/java/code/name/monkey/retromusic/glide/ArtistGlideRequest.java index 968fdd17..7bf2c1e9 100644 --- a/app/src/main/java/code/name/monkey/retromusic/glide/ArtistGlideRequest.java +++ b/app/src/main/java/code/name/monkey/retromusic/glide/ArtistGlideRequest.java @@ -31,7 +31,7 @@ public class ArtistGlideRequest { private static DrawableTypeRequest createBaseRequest(RequestManager requestManager, Artist artist, boolean noCustomImage, boolean forceDownload) { - boolean hasCustomImage = CustomArtistImageUtil.getInstance(RetroApplication.getInstance()) + boolean hasCustomImage = CustomArtistImageUtil.getInstance(RetroApplication.Companion.getInstance()) .hasCustomArtistImage(artist); if (noCustomImage || !hasCustomImage) { return requestManager.load(new ArtistImage(artist.getName(), forceDownload)); @@ -41,7 +41,7 @@ public class ArtistGlideRequest { } private static Key createSignature(Artist artist) { - return ArtistSignatureUtil.getInstance(RetroApplication.getInstance()) + return ArtistSignatureUtil.getInstance(RetroApplication.Companion.getInstance()) .getArtistSignature(artist.getName()); } diff --git a/app/src/main/java/code/name/monkey/retromusic/glide/RetroMusicColoredTarget.java b/app/src/main/java/code/name/monkey/retromusic/glide/RetroMusicColoredTarget.java deleted file mode 100644 index c20dd0a1..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/glide/RetroMusicColoredTarget.java +++ /dev/null @@ -1,53 +0,0 @@ -package code.name.monkey.retromusic.glide; - -import android.graphics.drawable.Drawable; -import android.widget.ImageView; - -import com.bumptech.glide.request.animation.GlideAnimation; - -import code.name.monkey.appthemehelper.util.ATHUtil; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.RetroApplication; -import code.name.monkey.retromusic.glide.palette.BitmapPaletteTarget; -import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper; -import code.name.monkey.retromusic.util.PreferenceUtil; - -import static code.name.monkey.retromusic.util.RetroColorUtil.getColor; -import static code.name.monkey.retromusic.util.RetroColorUtil.getDominantColor; - - -public abstract class RetroMusicColoredTarget extends BitmapPaletteTarget { - - public RetroMusicColoredTarget(ImageView view) { - super(view); - } - - @Override - public void onLoadFailed(Exception e, Drawable errorDrawable) { - super.onLoadFailed(e, errorDrawable); - onColorReady(getDefaultFooterColor()); - } - - @Override - public void onResourceReady(BitmapPaletteWrapper resource, - GlideAnimation glideAnimation) { - super.onResourceReady(resource, glideAnimation); - int defaultColor = getDefaultFooterColor(); - - int primaryColor = getColor(resource.getPalette(), defaultColor); - int dominantColor = getDominantColor(resource.getBitmap(), defaultColor); - - onColorReady(PreferenceUtil.getInstance().isDominantColor() ? - dominantColor : primaryColor); - } - - protected int getDefaultFooterColor() { - return ATHUtil.resolveColor(getView().getContext(), R.attr.defaultFooterColor); - } - - protected int getAlbumArtistFooterColor() { - return ATHUtil.resolveColor(getView().getContext(), R.attr.cardBackgroundColor); - } - - public abstract void onColorReady(int color); -} diff --git a/app/src/main/java/code/name/monkey/retromusic/glide/RetroMusicColoredTarget.kt b/app/src/main/java/code/name/monkey/retromusic/glide/RetroMusicColoredTarget.kt new file mode 100644 index 00000000..281dc96a --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/glide/RetroMusicColoredTarget.kt @@ -0,0 +1,47 @@ +package code.name.monkey.retromusic.glide + +import android.graphics.drawable.Drawable +import android.widget.ImageView + +import com.bumptech.glide.request.animation.GlideAnimation + +import code.name.monkey.appthemehelper.util.ATHUtil +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.RetroApplication +import code.name.monkey.retromusic.glide.palette.BitmapPaletteTarget +import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper +import code.name.monkey.retromusic.util.PreferenceUtil + +import code.name.monkey.retromusic.util.RetroColorUtil.getColor +import code.name.monkey.retromusic.util.RetroColorUtil.getDominantColor + + +abstract class RetroMusicColoredTarget(view: ImageView) : BitmapPaletteTarget(view) { + + protected val defaultFooterColor: Int + get() = ATHUtil.resolveColor(getView().context, R.attr.defaultFooterColor) + + protected val albumArtistFooterColor: Int + get() = ATHUtil.resolveColor(getView().context, R.attr.cardBackgroundColor) + + override fun onLoadFailed(e: Exception?, errorDrawable: Drawable?) { + super.onLoadFailed(e, errorDrawable) + onColorReady(defaultFooterColor) + } + + override fun onResourceReady(resource: BitmapPaletteWrapper, + glideAnimation: GlideAnimation?) { + super.onResourceReady(resource, glideAnimation) + val defaultColor = defaultFooterColor + + val primaryColor = getColor(resource.palette, defaultColor) + val dominantColor = getDominantColor(resource.bitmap, defaultColor) + + onColorReady(if (PreferenceUtil.getInstance().isDominantColor) + dominantColor + else + primaryColor) + } + + abstract fun onColorReady(color: Int) +} diff --git a/app/src/main/java/code/name/monkey/retromusic/glide/SongGlideRequest.java b/app/src/main/java/code/name/monkey/retromusic/glide/SongGlideRequest.java index 0d44984c..39e28d7b 100644 --- a/app/src/main/java/code/name/monkey/retromusic/glide/SongGlideRequest.java +++ b/app/src/main/java/code/name/monkey/retromusic/glide/SongGlideRequest.java @@ -29,14 +29,14 @@ public class SongGlideRequest { static DrawableTypeRequest createBaseRequest(RequestManager requestManager, Song song, boolean ignoreMediaStore) { if (ignoreMediaStore) { - return requestManager.load(new AudioFileCover(song.data)); + return requestManager.load(new AudioFileCover(song.getData())); } else { - return requestManager.loadFromMediaStore(MusicUtil.getMediaStoreAlbumCoverUri(song.albumId)); + return requestManager.loadFromMediaStore(MusicUtil.getMediaStoreAlbumCoverUri(song.getAlbumId())); } } static Key createSignature(Song song) { - return new MediaStoreSignature("", song.dateModified, 0); + return new MediaStoreSignature("", song.getDateModified(), 0); } public static class Builder { diff --git a/app/src/main/java/code/name/monkey/retromusic/glide/palette/BitmapPaletteTarget.java b/app/src/main/java/code/name/monkey/retromusic/glide/palette/BitmapPaletteTarget.java deleted file mode 100644 index 662db217..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/glide/palette/BitmapPaletteTarget.java +++ /dev/null @@ -1,17 +0,0 @@ -package code.name.monkey.retromusic.glide.palette; - -import android.widget.ImageView; -import com.bumptech.glide.request.target.ImageViewTarget; - -public class BitmapPaletteTarget extends ImageViewTarget { - - public BitmapPaletteTarget(ImageView view) { - super(view); - } - - @Override - protected void setResource(BitmapPaletteWrapper bitmapPaletteWrapper) { - view.setImageBitmap(bitmapPaletteWrapper.getBitmap()); - } - -} diff --git a/app/src/main/java/code/name/monkey/retromusic/glide/palette/BitmapPaletteTarget.kt b/app/src/main/java/code/name/monkey/retromusic/glide/palette/BitmapPaletteTarget.kt new file mode 100644 index 00000000..663927fb --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/glide/palette/BitmapPaletteTarget.kt @@ -0,0 +1,12 @@ +package code.name.monkey.retromusic.glide.palette + +import android.widget.ImageView +import com.bumptech.glide.request.target.ImageViewTarget + +open class BitmapPaletteTarget(view: ImageView) : ImageViewTarget(view) { + + override fun setResource(bitmapPaletteWrapper: BitmapPaletteWrapper) { + view.setImageBitmap(bitmapPaletteWrapper.bitmap) + } + +} diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/EqualizerHelper.java b/app/src/main/java/code/name/monkey/retromusic/helper/EqualizerHelper.java deleted file mode 100644 index 12f98ba4..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/helper/EqualizerHelper.java +++ /dev/null @@ -1,171 +0,0 @@ -package code.name.monkey.retromusic.helper; - -import android.media.audiofx.BassBoost; -import android.media.audiofx.Equalizer; -import android.media.audiofx.Virtualizer; -import android.util.Log; - -import code.name.monkey.retromusic.interfaces.EqualizerInterface; - -/** - * @author Hemanth S (h4h13). - */ - -public class EqualizerHelper implements EqualizerInterface { - private static final String TAG = "EqualizerHelper"; - private static volatile EqualizerHelper ourInstance; - private Equalizer mEqualizer; - private BassBoost mBassBoost; - private Virtualizer mVirtualizer; - - private int mMaxLevel, mMinLevel; - private boolean isRunning = false; - - private EqualizerHelper() { - - //Prevent form the reflection api. - if (ourInstance != null) { - throw new RuntimeException("Use getInstance() method to get the single instance of this class."); - } - - int i = MusicPlayerRemote.getAudioSessionId(); - - mEqualizer = new Equalizer(100, i); - if (mEqualizer == null) { - Log.i(TAG, "onCreate: Equalizer is null"); - return; - } - mEqualizer.setEnabled(true); - - - mBassBoost = new BassBoost(100, i); - if (mBassBoost == null) { - Log.i(TAG, "onCreate: BassBoost is null"); - return; - } - - mVirtualizer = new Virtualizer(100, i); - if (mVirtualizer == null) { - Log.i(TAG, "onCreate: Virtualizer is null"); - return; - } - - mMaxLevel = (int) mEqualizer.getBandLevelRange()[1]; - mMinLevel = (int) mEqualizer.getBandLevelRange()[0]; - - Log.i(TAG, "onCreate: " + mMaxLevel + " " + mMinLevel); - isRunning = true; - } - - public static EqualizerHelper getInstance() { - //Double check locking pattern - if (ourInstance == null) {//Check for the first time - - synchronized (EqualizerHelper.class) {//Check for the second time. - - //if there is no instance available... create new one - if (ourInstance == null) { - ourInstance = new EqualizerHelper(); - } - } - } - return ourInstance; - } - - //Make singleton from serialize and deserialize operation. - protected EqualizerHelper readResolve() { - return getInstance(); - } - - @Override - public Equalizer getEqualizer() { - return mEqualizer; - } - - @Override - public BassBoost getBassBoost() { - return mBassBoost; - } - - @Override - public Virtualizer getVirtualizer() { - return mVirtualizer; - } - - @Override - public int getBandLevelLow() { - return mMinLevel; - } - - @Override - public int getBandLevelHigh() { - return mMaxLevel; - } - - @Override - public int getNumberOfBands() { - return (int) mEqualizer.getNumberOfBands(); - } - - @Override - public int getCenterFreq(int band) { - return (int) mEqualizer.getCenterFreq((short) band); - } - - - @Override - public int getBandLevel(int band) { - return (int) mEqualizer.getBandLevel((short) band); - } - - @Override - public void setBandLevel(int band, int level) { - mEqualizer.setBandLevel((short) band, (short) level); - } - - @Override - public boolean isBassBoostEnabled() { - return mBassBoost.getEnabled(); - } - - @Override - public void setBassBoostEnabled(boolean isEnabled) { - mBassBoost.setEnabled(isEnabled); - } - - @Override - public int getBassBoostStrength() { - return (int) mBassBoost.getRoundedStrength(); - } - - @Override - public void setBassBoostStrength(int strength) { - mBassBoost.setStrength((short) strength); - } - - @Override - public boolean isVirtualizerEnabled() { - return mVirtualizer.getEnabled(); - } - - @Override - public void setVirtualizerEnabled(boolean isEnabled) { - mVirtualizer.setEnabled(isEnabled); - } - - @Override - public int getVirtualizerStrength() { - return mVirtualizer.getRoundedStrength(); - } - - @Override - public void setVirtualizerStrength(int strength) { - mVirtualizer.setStrength((short) strength); - } - - @Override - public boolean isRunning() { - return isRunning; - } - -} diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/EqualizerHelper.kt b/app/src/main/java/code/name/monkey/retromusic/helper/EqualizerHelper.kt new file mode 100644 index 00000000..262ac7d3 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/helper/EqualizerHelper.kt @@ -0,0 +1,109 @@ +package code.name.monkey.retromusic.helper + +import android.media.audiofx.BassBoost +import android.media.audiofx.Equalizer +import android.media.audiofx.Virtualizer +import android.util.Log + +import code.name.monkey.retromusic.interfaces.EqualizerInterface + +/** + * @author Hemanth S (h4h13). + */ + +class EqualizerHelper private constructor() : EqualizerInterface { + override val equalizer: Equalizer + override val bassBoost: BassBoost + override val virtualizer: Virtualizer + + override val bandLevelHigh: Int + override val bandLevelLow: Int + override var isRunning = false + + override val numberOfBands: Int + get() = equalizer.numberOfBands.toInt() + + override var isBassBoostEnabled: Boolean + get() = bassBoost.enabled + set(isEnabled) { + bassBoost.enabled = isEnabled + } + + override var bassBoostStrength: Int + get() = bassBoost.roundedStrength.toInt() + set(strength) = bassBoost.setStrength(strength.toShort()) + + override var isVirtualizerEnabled: Boolean + get() = virtualizer.enabled + set(isEnabled) { + virtualizer.enabled = isEnabled + } + + override var virtualizerStrength: Int + get() = virtualizer.roundedStrength.toInt() + set(strength) = virtualizer.setStrength(strength.toShort()) + + init { + + //Prevent form the reflection api. + if (ourInstance != null) { + throw RuntimeException("Use getInstance() method to get the single instance of this class.") + } + + val i = MusicPlayerRemote.audioSessionId + + equalizer = Equalizer(100, i) + + equalizer.enabled = true + bassBoost = BassBoost(100, i) + virtualizer = Virtualizer(100, i) + + bandLevelHigh = equalizer.bandLevelRange[1].toInt() + bandLevelLow = equalizer.bandLevelRange[0].toInt() + + Log.i(TAG, "onCreate: $bandLevelHigh $bandLevelLow") + isRunning = true + } + + //Make singleton from serialize and deserialize operation. + protected fun readResolve(): EqualizerHelper? { + return instance + } + + override fun getCenterFreq(band: Int): Int { + return equalizer.getCenterFreq(band.toShort()) + } + + + override fun getBandLevel(band: Int): Int { + return equalizer.getBandLevel(band.toShort()).toInt() + } + + override fun setBandLevel(band: Int, level: Int) { + equalizer.setBandLevel(band.toShort(), level.toShort()) + } + + companion object { + private val TAG = "EqualizerHelper" + @Volatile + private var ourInstance: EqualizerHelper? = null + + //Double check locking pattern + //Check for the first time + //Check for the second time. + //if there is no instance available... create new one + val instance: EqualizerHelper? + get() { + if (ourInstance == null) { + + synchronized(EqualizerHelper::class.java) { + if (ourInstance == null) { + ourInstance = EqualizerHelper() + } + } + } + return ourInstance + } + } + +} diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/HorizontalAdapterHelper.java b/app/src/main/java/code/name/monkey/retromusic/helper/HorizontalAdapterHelper.java deleted file mode 100644 index fd1ae91a..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/helper/HorizontalAdapterHelper.java +++ /dev/null @@ -1,36 +0,0 @@ -package code.name.monkey.retromusic.helper; - -import android.content.Context; -import android.view.ViewGroup; -import code.name.monkey.retromusic.R; - - -public class HorizontalAdapterHelper { - - public static final int LAYOUT_RES = R.layout.item_image; - - public static final int TYPE_FIRST = 1; - public static final int TYPE_MIDDLE = 2; - public static final int TYPE_LAST = 3; - - public static void applyMarginToLayoutParams(Context context, - ViewGroup.MarginLayoutParams layoutParams, int viewType) { - int listMargin = context.getResources() - .getDimensionPixelSize(R.dimen.now_playing_top_margin); - if (viewType == TYPE_FIRST) { - layoutParams.leftMargin = listMargin; - } else if (viewType == TYPE_LAST) { - layoutParams.rightMargin = listMargin; - } - } - - public static int getItemViewtype(int position, int itemCount) { - if (position == 0) { - return TYPE_FIRST; - } else if (position == itemCount - 1) { - return TYPE_LAST; - } else { - return TYPE_MIDDLE; - } - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/HorizontalAdapterHelper.kt b/app/src/main/java/code/name/monkey/retromusic/helper/HorizontalAdapterHelper.kt new file mode 100644 index 00000000..613dc7e3 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/helper/HorizontalAdapterHelper.kt @@ -0,0 +1,36 @@ +package code.name.monkey.retromusic.helper + +import android.content.Context +import android.view.ViewGroup +import code.name.monkey.retromusic.R + + +object HorizontalAdapterHelper { + + val LAYOUT_RES = R.layout.item_image + + val TYPE_FIRST = 1 + val TYPE_MIDDLE = 2 + val TYPE_LAST = 3 + + fun applyMarginToLayoutParams(context: Context, + layoutParams: ViewGroup.MarginLayoutParams, viewType: Int) { + val listMargin = context.resources + .getDimensionPixelSize(R.dimen.now_playing_top_margin) + if (viewType == TYPE_FIRST) { + layoutParams.leftMargin = listMargin + } else if (viewType == TYPE_LAST) { + layoutParams.rightMargin = listMargin + } + } + + fun getItemViewtype(position: Int, itemCount: Int): Int { + return if (position == 0) { + TYPE_FIRST + } else if (position == itemCount - 1) { + TYPE_LAST + } else { + TYPE_MIDDLE + } + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/M3UConstants.java b/app/src/main/java/code/name/monkey/retromusic/helper/M3UConstants.java deleted file mode 100644 index e6ffdf39..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/helper/M3UConstants.java +++ /dev/null @@ -1,8 +0,0 @@ -package code.name.monkey.retromusic.helper; - -public interface M3UConstants { - String EXTENSION = "m3u"; - String HEADER = "#EXTM3U"; - String ENTRY = "#EXTINF:"; - String DURATION_SEPARATOR = ","; -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/M3UConstants.kt b/app/src/main/java/code/name/monkey/retromusic/helper/M3UConstants.kt new file mode 100644 index 00000000..93850141 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/helper/M3UConstants.kt @@ -0,0 +1,10 @@ +package code.name.monkey.retromusic.helper + +interface M3UConstants { + companion object { + val EXTENSION = "m3u" + val HEADER = "#EXTM3U" + val ENTRY = "#EXTINF:" + val DURATION_SEPARATOR = "," + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/M3UWriter.java b/app/src/main/java/code/name/monkey/retromusic/helper/M3UWriter.java deleted file mode 100644 index c976b74f..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/helper/M3UWriter.java +++ /dev/null @@ -1,58 +0,0 @@ -package code.name.monkey.retromusic.helper; - -import android.content.Context; -import androidx.annotation.NonNull; - -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.util.ArrayList; - -import code.name.monkey.retromusic.loaders.PlaylistSongsLoader; -import code.name.monkey.retromusic.model.AbsCustomPlaylist; -import code.name.monkey.retromusic.model.Playlist; -import code.name.monkey.retromusic.model.Song; -import io.reactivex.Observable; -import io.reactivex.ObservableEmitter; - -public class M3UWriter implements M3UConstants { - public static final String TAG = M3UWriter.class.getSimpleName(); - - public static Observable write(@NonNull Context context, - @NonNull File dir, @NonNull Playlist playlist) { - if (!dir.exists()) //noinspection ResultOfMethodCallIgnored - dir.mkdirs(); - File file = new File(dir, playlist.name.concat("." + EXTENSION)); - - if (playlist instanceof AbsCustomPlaylist) { - return Observable.create(e -> { - ((AbsCustomPlaylist) playlist).getSongs(context).subscribe(songs -> { - saveSongsToFile(file, e, songs); - }); - }); - } else - return Observable.create(e -> - PlaylistSongsLoader.getPlaylistSongList(context, playlist.id) - .subscribe(songs -> { - saveSongsToFile(file, e, songs); - })); - } - - private static void saveSongsToFile(File file, ObservableEmitter e, ArrayList songs) throws IOException { - if (songs.size() > 0) { - BufferedWriter bw = new BufferedWriter(new FileWriter(file)); - bw.write(HEADER); - for (Song song : songs) { - bw.newLine(); - bw.write(ENTRY + song.duration + DURATION_SEPARATOR + song.artistName + " - " + song.title); - bw.newLine(); - bw.write(song.data); - } - - bw.close(); - } - e.onNext(file); - e.onComplete(); - } -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/M3UWriter.kt b/app/src/main/java/code/name/monkey/retromusic/helper/M3UWriter.kt new file mode 100644 index 00000000..1bd284fe --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/helper/M3UWriter.kt @@ -0,0 +1,53 @@ +package code.name.monkey.retromusic.helper + +import android.content.Context + +import java.io.BufferedWriter +import java.io.File +import java.io.FileWriter +import java.io.IOException +import java.util.ArrayList + +import code.name.monkey.retromusic.loaders.PlaylistSongsLoader +import code.name.monkey.retromusic.model.AbsCustomPlaylist +import code.name.monkey.retromusic.model.Playlist +import code.name.monkey.retromusic.model.Song +import io.reactivex.Observable +import io.reactivex.ObservableEmitter + +class M3UWriter : M3UConstants { + companion object { + val TAG: String = M3UWriter::class.java.simpleName + + fun write(context: Context, + dir: File, playlist: Playlist): Observable { + if (!dir.exists()) + + dir.mkdirs() + val file = File(dir, playlist.name + ("." + M3UConstants.EXTENSION)) + + return if (playlist is AbsCustomPlaylist) { + Observable.create { e -> playlist.getSongs(context).subscribe { songs -> saveSongsToFile(file, e, songs) } } + } else + Observable.create { e -> PlaylistSongsLoader.getPlaylistSongList(context, playlist.id).subscribe { songs -> saveSongsToFile(file, e, songs) } } + } + + @Throws(IOException::class) + private fun saveSongsToFile(file: File, e: ObservableEmitter, songs: ArrayList) { + if (songs.size > 0) { + val bw = BufferedWriter(FileWriter(file)) + bw.write(M3UConstants.HEADER) + for (song in songs) { + bw.newLine() + bw.write(M3UConstants.ENTRY + song.duration + M3UConstants.DURATION_SEPARATOR + song.artistName + " - " + song.title) + bw.newLine() + bw.write(song.data) + } + + bw.close() + } + e.onNext(file) + e.onComplete() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/MusicPlayerRemote.java b/app/src/main/java/code/name/monkey/retromusic/helper/MusicPlayerRemote.java deleted file mode 100644 index a26f6b76..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/helper/MusicPlayerRemote.java +++ /dev/null @@ -1,496 +0,0 @@ -package code.name.monkey.retromusic.helper; - -import android.annotation.TargetApi; -import android.app.Activity; -import android.content.ComponentName; -import android.content.ContentResolver; -import android.content.Context; -import android.content.ContextWrapper; -import android.content.Intent; -import android.content.ServiceConnection; -import android.database.Cursor; -import android.net.Uri; -import android.os.Build; -import android.os.Environment; -import android.os.IBinder; -import android.provider.DocumentsContract; -import android.provider.MediaStore; -import android.util.Log; -import android.widget.Toast; - -import com.google.android.gms.cast.framework.CastContext; -import com.google.android.gms.cast.framework.CastSession; - -import java.io.File; -import java.util.ArrayList; -import java.util.Random; -import java.util.WeakHashMap; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.RetroApplication; -import code.name.monkey.retromusic.loaders.SongLoader; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.service.MusicService; -import code.name.monkey.retromusic.util.PreferenceUtil; -import io.reactivex.schedulers.Schedulers; - - -public class MusicPlayerRemote { - public static final String TAG = MusicPlayerRemote.class.getSimpleName(); - private static final WeakHashMap mConnectionMap = new WeakHashMap<>(); - @Nullable - public static MusicService musicService; - private static int playbackLocation = PlaybackLocation.LOCAL; - - - private static CastSession getCastSession() { - CastSession castSession = CastContext.getSharedInstance(RetroApplication.getInstance()).getSessionManager().getCurrentCastSession(); - if (castSession != null) { - playbackLocation = PlaybackLocation.REMOTE; - } else { - playbackLocation = PlaybackLocation.LOCAL; - } - return castSession; - } - - public static ServiceToken bindToService(@NonNull final Context context, - final ServiceConnection callback) { - - Activity realActivity = ((Activity) context).getParent(); - if (realActivity == null) { - realActivity = (Activity) context; - } - - final ContextWrapper contextWrapper = new ContextWrapper(realActivity); - contextWrapper.startService(new Intent(contextWrapper, MusicService.class)); - - final ServiceBinder binder = new ServiceBinder(callback); - - if (contextWrapper.bindService(new Intent().setClass(contextWrapper, MusicService.class), binder, Context.BIND_AUTO_CREATE)) { - mConnectionMap.put(contextWrapper, binder); - return new ServiceToken(contextWrapper); - } - return null; - } - - public static void unbindFromService(@Nullable final ServiceToken token) { - if (token == null) { - return; - } - final ContextWrapper mContextWrapper = token.mWrappedContext; - final ServiceBinder mBinder = mConnectionMap.remove(mContextWrapper); - if (mBinder == null) { - return; - } - mContextWrapper.unbindService(mBinder); - if (mConnectionMap.isEmpty()) { - musicService = null; - } - } - - @Nullable - private static String getFilePathFromUri(Context context, Uri uri) { - - final String column = "_data"; - final String[] projection = { - column - }; - try (Cursor cursor = context.getContentResolver().query(uri, projection, null, null, - null)) { - if (cursor != null && cursor.moveToFirst()) { - final int column_index = cursor.getColumnIndexOrThrow(column); - return cursor.getString(column_index); - } - } catch (Exception e) { - Log.e(TAG, e.getMessage()); - } - return null; - } - - /** - * Async - */ - public static void playSongAt(final int position) { - if (musicService != null) { - musicService.playSongAt(position); - } - } - - public static void pauseSong() { - if (musicService != null) { - musicService.pause(); - } - } - - /** - * Async - */ - public static void playNextSong() { - if (musicService != null) { - musicService.playNextSong(true); - } - } - - /** - * Async - */ - public static void playPreviousSong() { - if (musicService != null) { - musicService.playPreviousSong(true); - } - } - - /** - * Async - */ - public static void back() { - if (musicService != null) { - musicService.back(true); - } - } - - public static boolean isPlaying() { - return musicService != null && musicService.isPlaying(); - } - - public static void resumePlaying() { - if (musicService != null) { - musicService.play(); - } - } - - /** - * Async - */ - public static void openQueue(final ArrayList queue, final int startPosition, final boolean startPlaying) { - if (!tryToHandleOpenPlayingQueue(queue, startPosition, startPlaying) && musicService != null) { - musicService.openQueue(queue, startPosition, startPlaying); - if (PreferenceUtil.getInstance().isShuffleModeOn()) - setShuffleMode(MusicService.SHUFFLE_MODE_NONE); - } - } - - /** - * Async - */ - public static void openAndShuffleQueue(final ArrayList queue, boolean startPlaying) { - int startPosition = 0; - if (!queue.isEmpty()) { - startPosition = new Random().nextInt(queue.size()); - } - - if (!tryToHandleOpenPlayingQueue(queue, startPosition, startPlaying) && musicService != null) { - openQueue(queue, startPosition, startPlaying); - setShuffleMode(MusicService.SHUFFLE_MODE_SHUFFLE); - } - } - - private static boolean tryToHandleOpenPlayingQueue(final ArrayList queue, final int startPosition, final boolean startPlaying) { - if (getPlayingQueue() == queue) { - if (startPlaying) { - playSongAt(startPosition); - } else { - setPosition(startPosition); - } - return true; - } - return false; - } - - public static Song getCurrentSong() { - if (musicService != null) { - return musicService.getCurrentSong(); - } - return Song.EMPTY_SONG; - } - - public static int getPosition() { - if (musicService != null) { - return musicService.getPosition(); - } - return -1; - } - - /** - * Async - */ - public static void setPosition(final int position) { - if (musicService != null) { - musicService.setPosition(position); - } - } - - public static ArrayList getPlayingQueue() { - if (musicService != null) { - return musicService.getPlayingQueue(); - } - return new ArrayList<>(); - } - - public static int getSongProgressMillis() { - if (musicService != null) { - return musicService.getSongProgressMillis(); - } - return -1; - } - - public static int getSongDurationMillis() { - if (musicService != null) { - return musicService.getSongDurationMillis(); - } - return -1; - } - - public static long getQueueDurationMillis(int position) { - if (musicService != null) { - return musicService.getQueueDurationMillis(position); - } - return -1; - } - - public static int seekTo(int millis) { - if (musicService != null) { - return musicService.seek(millis); - } - return -1; - } - - public static int getRepeatMode() { - if (musicService != null) { - return musicService.getRepeatMode(); - } - return MusicService.REPEAT_MODE_NONE; - } - - public static int getShuffleMode() { - if (musicService != null) { - return musicService.getShuffleMode(); - } - return MusicService.SHUFFLE_MODE_NONE; - } - - public static boolean cycleRepeatMode() { - if (musicService != null) { - musicService.cycleRepeatMode(); - return true; - } - return false; - } - - public static boolean toggleShuffleMode() { - if (musicService != null) { - musicService.toggleShuffle(); - return true; - } - return false; - } - - public static boolean setShuffleMode(final int shuffleMode) { - if (musicService != null) { - musicService.setShuffleMode(shuffleMode); - return true; - } - return false; - } - - public static boolean playNext(Song song) { - if (musicService != null) { - if (getPlayingQueue().size() > 0) { - musicService.addSong(getPosition() + 1, song); - } else { - ArrayList queue = new ArrayList<>(); - queue.add(song); - openQueue(queue, 0, false); - } - Toast.makeText(musicService, musicService.getResources().getString(R.string.added_title_to_playing_queue), Toast.LENGTH_SHORT).show(); - return true; - } - return false; - } - - public static boolean playNext(@NonNull ArrayList songs) { - if (musicService != null) { - if (getPlayingQueue().size() > 0) { - musicService.addSongs(getPosition() + 1, songs); - } else { - openQueue(songs, 0, false); - } - final String toast = songs.size() == 1 ? musicService.getResources().getString(R.string.added_title_to_playing_queue) : musicService.getResources().getString(R.string.added_x_titles_to_playing_queue, songs.size()); - Toast.makeText(musicService, toast, Toast.LENGTH_SHORT).show(); - return true; - } - return false; - } - - public static boolean enqueue(Song song) { - if (musicService != null) { - if (getPlayingQueue().size() > 0) { - musicService.addSong(song); - } else { - ArrayList queue = new ArrayList<>(); - queue.add(song); - openQueue(queue, 0, false); - } - Toast.makeText(musicService, musicService.getResources().getString(R.string.added_title_to_playing_queue), Toast.LENGTH_SHORT).show(); - return true; - } - return false; - } - - public static boolean enqueue(@NonNull ArrayList songs) { - if (musicService != null) { - if (getPlayingQueue().size() > 0) { - musicService.addSongs(songs); - } else { - openQueue(songs, 0, false); - } - final String toast = songs.size() == 1 ? musicService.getResources().getString(R.string.added_title_to_playing_queue) : musicService.getResources().getString(R.string.added_x_titles_to_playing_queue, songs.size()); - Toast.makeText(musicService, toast, Toast.LENGTH_SHORT).show(); - return true; - } - return false; - } - - public static boolean removeFromQueue(@NonNull Song song) { - if (musicService != null) { - musicService.removeSong(song); - return true; - } - return false; - } - - public static boolean removeFromQueue(int position) { - if (musicService != null && position >= 0 && position < getPlayingQueue().size()) { - musicService.removeSong(position); - return true; - } - return false; - } - - public static boolean moveSong(int from, int to) { - if (musicService != null && from >= 0 && to >= 0 && from < getPlayingQueue().size() && to < getPlayingQueue().size()) { - musicService.moveSong(from, to); - return true; - } - return false; - } - - public static boolean clearQueue() { - if (musicService != null) { - musicService.clearQueue(); - return true; - } - return false; - } - - public static int getAudioSessionId() { - if (musicService != null) { - return musicService.getAudioSessionId(); - } - return -1; - } - - public static void playFromUri(Uri uri) { - if (musicService != null) { - ArrayList songs = null; - if (uri.getScheme() != null && uri.getAuthority() != null) { - if (uri.getScheme().equals(ContentResolver.SCHEME_CONTENT)) { - String songId = null; - if (uri.getAuthority().equals("com.android.providers.media.documents")) { - songId = getSongIdFromMediaProvider(uri); - } else if (uri.getAuthority().equals("media")) { - songId = uri.getLastPathSegment(); - } - if (songId != null) { - /* songs = SongLoader.getSongs(SongLoader.makeSongCursor( - musicService, - MediaStore.Audio.AudioColumns._ID + "=?", - new String[]{songId} - ));*/ - songs = SongLoader.Companion.getSongs(SongLoader.Companion.makeSongCursor( - musicService, - MediaStore.Audio.AudioColumns._ID + "=?", - new String[]{songId})) - .subscribeOn(Schedulers.io()).blockingFirst(); - } - } - } - if (songs == null) { - File songFile = null; - if (uri.getAuthority() != null && uri.getAuthority().equals("com.android.externalstorage.documents")) { - songFile = new File(Environment.getExternalStorageDirectory(), uri.getPath().split(":", 2)[1]); - } - if (songFile == null) { - String path = getFilePathFromUri(musicService, uri); - if (path != null) - songFile = new File(path); - } - if (songFile == null && uri.getPath() != null) { - songFile = new File(uri.getPath()); - } - if (songFile != null) { - songs = SongLoader.Companion.getSongs(SongLoader.Companion.makeSongCursor( - musicService, - MediaStore.Audio.AudioColumns.DATA + "=?", - new String[]{songFile.getAbsolutePath()} - )).blockingFirst(); - } - } - if (songs != null && !songs.isEmpty()) { - openQueue(songs, 0, true); - } else { - //TODO the file is not listed in the media store - } - } - } - - @TargetApi(Build.VERSION_CODES.KITKAT) - private static String getSongIdFromMediaProvider(Uri uri) { - return DocumentsContract.getDocumentId(uri).split(":")[1]; - } - - public static boolean isServiceConnected() { - return musicService != null; - } - - @interface PlaybackLocation { - int REMOTE = 0; - int LOCAL = 1; - } - - public static final class ServiceBinder implements ServiceConnection { - private final ServiceConnection mCallback; - - ServiceBinder(final ServiceConnection callback) { - mCallback = callback; - } - - @Override - public void onServiceConnected(final ComponentName className, final IBinder service) { - MusicService.MusicBinder binder = (MusicService.MusicBinder) service; - musicService = binder.getService(); - if (mCallback != null) { - mCallback.onServiceConnected(className, service); - } - } - - @Override - public void onServiceDisconnected(final ComponentName className) { - if (mCallback != null) { - mCallback.onServiceDisconnected(className); - } - musicService = null; - } - } - - public static final class ServiceToken { - ContextWrapper mWrappedContext; - - ServiceToken(final ContextWrapper context) { - mWrappedContext = context; - } - } - -} diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/MusicPlayerRemote.kt b/app/src/main/java/code/name/monkey/retromusic/helper/MusicPlayerRemote.kt new file mode 100644 index 00000000..cf685bb2 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/helper/MusicPlayerRemote.kt @@ -0,0 +1,440 @@ +package code.name.monkey.retromusic.helper + +import android.annotation.TargetApi +import android.app.Activity +import android.content.* +import android.net.Uri +import android.os.Build +import android.os.Environment +import android.os.IBinder +import android.provider.DocumentsContract +import android.provider.MediaStore +import android.util.Log +import android.widget.Toast +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.RetroApplication +import code.name.monkey.retromusic.loaders.SongLoader +import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.service.MusicService +import code.name.monkey.retromusic.util.PreferenceUtil +import com.google.android.gms.cast.framework.CastContext +import com.google.android.gms.cast.framework.CastSession +import io.reactivex.schedulers.Schedulers +import java.io.File +import java.util.* + + +object MusicPlayerRemote { + val TAG = MusicPlayerRemote::class.java.simpleName + private val mConnectionMap = WeakHashMap() + var musicService: MusicService? = null + private var playbackLocation = PlaybackLocation.LOCAL + + + private val castSession: CastSession? + get() { + val castSession = CastContext.getSharedInstance(RetroApplication.instance).sessionManager.currentCastSession + if (castSession != null) { + playbackLocation = PlaybackLocation.REMOTE + } else { + playbackLocation = PlaybackLocation.LOCAL + } + return castSession + } + + val isPlaying: Boolean + get() = musicService != null && musicService!!.isPlaying + + val currentSong: Song + get() = if (musicService != null) { + musicService!!.currentSong + } else Song.EMPTY_SONG + + /** + * Async + */ + var position: Int + get() = if (musicService != null) { + musicService!!.position + } else -1 + set(position) { + if (musicService != null) { + musicService!!.position = position + } + } + + val playingQueue: ArrayList + get() = if (musicService != null) { + musicService!!.playingQueue + } else ArrayList() + + val songProgressMillis: Int + get() = if (musicService != null) { + musicService!!.songProgressMillis + } else -1 + + val songDurationMillis: Int + get() = if (musicService != null) { + musicService!!.songDurationMillis + } else -1 + + val repeatMode: Int + get() = if (musicService != null) { + musicService!!.repeatMode + } else MusicService.REPEAT_MODE_NONE + + val shuffleMode: Int + get() = if (musicService != null) { + musicService!!.shuffleMode + } else MusicService.SHUFFLE_MODE_NONE + + val audioSessionId: Int + get() = if (musicService != null) { + musicService!!.audioSessionId + } else -1 + + val isServiceConnected: Boolean + get() = musicService != null + + fun bindToService(context: Context, + callback: ServiceConnection): ServiceToken? { + + var realActivity: Activity? = (context as Activity).parent + if (realActivity == null) { + realActivity = context + } + + val contextWrapper = ContextWrapper(realActivity) + contextWrapper.startService(Intent(contextWrapper, MusicService::class.java)) + + val binder = ServiceBinder(callback) + + if (contextWrapper.bindService(Intent().setClass(contextWrapper, MusicService::class.java), binder, Context.BIND_AUTO_CREATE)) { + mConnectionMap[contextWrapper] = binder + return ServiceToken(contextWrapper) + } + return null + } + + fun unbindFromService(token: ServiceToken?) { + if (token == null) { + return + } + val mContextWrapper = token.mWrappedContext + val mBinder = mConnectionMap.remove(mContextWrapper) ?: return + mContextWrapper.unbindService(mBinder) + if (mConnectionMap.isEmpty()) { + musicService = null + } + } + + private fun getFilePathFromUri(context: Context, uri: Uri): String? { + + val column = "_data" + val projection = arrayOf(column) + try { + context.contentResolver.query(uri, projection, null, null, null)!!.use { cursor -> + if (cursor.moveToFirst()) { + val column_index = cursor.getColumnIndexOrThrow(column) + return cursor.getString(column_index) + } + } + } catch (e: Exception) { + Log.e(TAG, e.message) + } + + return null + } + + /** + * Async + */ + fun playSongAt(position: Int) { + if (musicService != null) { + musicService!!.playSongAt(position) + } + } + + fun pauseSong() { + if (musicService != null) { + musicService!!.pause() + } + } + + /** + * Async + */ + fun playNextSong() { + if (musicService != null) { + musicService!!.playNextSong(true) + } + } + + /** + * Async + */ + fun playPreviousSong() { + if (musicService != null) { + musicService!!.playPreviousSong(true) + } + } + + /** + * Async + */ + fun back() { + if (musicService != null) { + musicService!!.back(true) + } + } + + fun resumePlaying() { + if (musicService != null) { + musicService!!.play() + } + } + + /** + * Async + */ + fun openQueue(queue: ArrayList, startPosition: Int, startPlaying: Boolean) { + if (!tryToHandleOpenPlayingQueue(queue, startPosition, startPlaying) && musicService != null) { + musicService!!.openQueue(queue, startPosition, startPlaying) + if (PreferenceUtil.getInstance().isShuffleModeOn) + setShuffleMode(MusicService.SHUFFLE_MODE_NONE) + } + } + + /** + * Async + */ + fun openAndShuffleQueue(queue: ArrayList, startPlaying: Boolean) { + var startPosition = 0 + if (!queue.isEmpty()) { + startPosition = Random().nextInt(queue.size) + } + + if (!tryToHandleOpenPlayingQueue(queue, startPosition, startPlaying) && musicService != null) { + openQueue(queue, startPosition, startPlaying) + setShuffleMode(MusicService.SHUFFLE_MODE_SHUFFLE) + } + } + + private fun tryToHandleOpenPlayingQueue(queue: ArrayList, startPosition: Int, startPlaying: Boolean): Boolean { + if (playingQueue === queue) { + if (startPlaying) { + playSongAt(startPosition) + } else { + position = startPosition + } + return true + } + return false + } + + fun getQueueDurationMillis(position: Int): Long { + return if (musicService != null) { + musicService!!.getQueueDurationMillis(position) + } else -1 + } + + fun seekTo(millis: Int): Int { + return if (musicService != null) { + musicService!!.seek(millis) + } else -1 + } + + fun cycleRepeatMode(): Boolean { + if (musicService != null) { + musicService!!.cycleRepeatMode() + return true + } + return false + } + + fun toggleShuffleMode(): Boolean { + if (musicService != null) { + musicService!!.toggleShuffle() + return true + } + return false + } + + fun setShuffleMode(shuffleMode: Int): Boolean { + if (musicService != null) { + musicService!!.shuffleMode = shuffleMode + return true + } + return false + } + + fun playNext(song: Song): Boolean { + if (musicService != null) { + if (playingQueue.size > 0) { + musicService!!.addSong(position + 1, song) + } else { + val queue = ArrayList() + queue.add(song) + openQueue(queue, 0, false) + } + Toast.makeText(musicService, musicService!!.resources.getString(R.string.added_title_to_playing_queue), Toast.LENGTH_SHORT).show() + return true + } + return false + } + + fun playNext(songs: ArrayList): Boolean { + if (musicService != null) { + if (playingQueue.size > 0) { + musicService!!.addSongs(position + 1, songs) + } else { + openQueue(songs, 0, false) + } + val toast = if (songs.size == 1) musicService!!.resources.getString(R.string.added_title_to_playing_queue) else musicService!!.resources.getString(R.string.added_x_titles_to_playing_queue, songs.size) + Toast.makeText(musicService, toast, Toast.LENGTH_SHORT).show() + return true + } + return false + } + + fun enqueue(song: Song): Boolean { + if (musicService != null) { + if (playingQueue.size > 0) { + musicService!!.addSong(song) + } else { + val queue = ArrayList() + queue.add(song) + openQueue(queue, 0, false) + } + Toast.makeText(musicService, musicService!!.resources.getString(R.string.added_title_to_playing_queue), Toast.LENGTH_SHORT).show() + return true + } + return false + } + + fun enqueue(songs: ArrayList): Boolean { + if (musicService != null) { + if (playingQueue.size > 0) { + musicService!!.addSongs(songs) + } else { + openQueue(songs, 0, false) + } + val toast = if (songs.size == 1) musicService!!.resources.getString(R.string.added_title_to_playing_queue) else musicService!!.resources.getString(R.string.added_x_titles_to_playing_queue, songs.size) + Toast.makeText(musicService, toast, Toast.LENGTH_SHORT).show() + return true + } + return false + } + + fun removeFromQueue(song: Song): Boolean { + if (musicService != null) { + musicService!!.removeSong(song) + return true + } + return false + } + + fun removeFromQueue(position: Int): Boolean { + if (musicService != null && position >= 0 && position < playingQueue.size) { + musicService!!.removeSong(position) + return true + } + return false + } + + fun moveSong(from: Int, to: Int): Boolean { + if (musicService != null && from >= 0 && to >= 0 && from < playingQueue.size && to < playingQueue.size) { + musicService!!.moveSong(from, to) + return true + } + return false + } + + fun clearQueue(): Boolean { + if (musicService != null) { + musicService!!.clearQueue() + return true + } + return false + } + + fun playFromUri(uri: Uri) { + if (musicService != null) { + var songs: ArrayList? = null + if (uri.scheme != null && uri.authority != null) { + if (uri.scheme == ContentResolver.SCHEME_CONTENT) { + var songId: String? = null + if (uri.authority == "com.android.providers.media.documents") { + songId = getSongIdFromMediaProvider(uri) + } else if (uri.authority == "media") { + songId = uri.lastPathSegment + } + if (songId != null) { + /* songs = SongLoader.getSongs(SongLoader.makeSongCursor( + musicService, + MediaStore.Audio.AudioColumns._ID + "=?", + new String[]{songId} + ));*/ + songs = SongLoader.getSongs(SongLoader.makeSongCursor( + musicService!!, + MediaStore.Audio.AudioColumns._ID + "=?", + arrayOf(songId))) + .subscribeOn(Schedulers.io()).blockingFirst() + } + } + } + if (songs == null) { + var songFile: File? = null + if (uri.authority != null && uri.authority == "com.android.externalstorage.documents") { + songFile = File(Environment.getExternalStorageDirectory(), uri.path!!.split(":".toRegex(), 2).toTypedArray()[1]) + } + if (songFile == null) { + val path = getFilePathFromUri(musicService!!, uri) + if (path != null) + songFile = File(path) + } + if (songFile == null && uri.path != null) { + songFile = File(uri.path) + } + if (songFile != null) { + songs = SongLoader.getSongs(SongLoader.makeSongCursor(musicService!!, MediaStore.Audio.AudioColumns.DATA + "=?", arrayOf(songFile.absolutePath) + )).blockingFirst() + } + } + if (songs != null && !songs.isEmpty()) { + openQueue(songs, 0, true) + } else { + //TODO the file is not listed in the media store + } + } + } + + @TargetApi(Build.VERSION_CODES.KITKAT) + private fun getSongIdFromMediaProvider(uri: Uri): String { + return DocumentsContract.getDocumentId(uri).split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[1] + } + + internal annotation class PlaybackLocation { + companion object { + val REMOTE = 0 + val LOCAL = 1 + } + } + + class ServiceBinder internal constructor(private val mCallback: ServiceConnection?) : ServiceConnection { + + override fun onServiceConnected(className: ComponentName, service: IBinder) { + val binder = service as MusicService.MusicBinder + musicService = binder.service + mCallback?.onServiceConnected(className, service) + } + + override fun onServiceDisconnected(className: ComponentName) { + mCallback?.onServiceDisconnected(className) + musicService = null + } + } + + class ServiceToken internal constructor(internal var mWrappedContext: ContextWrapper) + +} diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/MusicProgressViewUpdateHelper.java b/app/src/main/java/code/name/monkey/retromusic/helper/MusicProgressViewUpdateHelper.java deleted file mode 100644 index 4559a240..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/helper/MusicProgressViewUpdateHelper.java +++ /dev/null @@ -1,71 +0,0 @@ -package code.name.monkey.retromusic.helper; - -import android.os.Handler; -import android.os.Message; -import androidx.annotation.NonNull; - - -public class MusicProgressViewUpdateHelper extends Handler { - private static final int CMD_REFRESH_PROGRESS_VIEWS = 1; - - private static final int MIN_INTERVAL = 20; - private static final int UPDATE_INTERVAL_PLAYING = 1000; - private static final int UPDATE_INTERVAL_PAUSED = 500; - - private Callback callback; - private int intervalPlaying; - private int intervalPaused; - - public void start() { - queueNextRefresh(1); - } - - public void stop() { - removeMessages(CMD_REFRESH_PROGRESS_VIEWS); - } - - public MusicProgressViewUpdateHelper(Callback callback) { - this.callback = callback; - this.intervalPlaying = UPDATE_INTERVAL_PLAYING; - this.intervalPaused = UPDATE_INTERVAL_PAUSED; - } - - public MusicProgressViewUpdateHelper(Callback callback, int intervalPlaying, int intervalPaused) { - this.callback = callback; - this.intervalPlaying = intervalPlaying; - this.intervalPaused = intervalPaused; - } - - @Override - public void handleMessage(@NonNull Message msg) { - super.handleMessage(msg); - if (msg.what == CMD_REFRESH_PROGRESS_VIEWS) { - queueNextRefresh(refreshProgressViews()); - } - } - - private int refreshProgressViews() { - final int progressMillis = MusicPlayerRemote.getSongProgressMillis(); - final int totalMillis = MusicPlayerRemote.getSongDurationMillis(); - - callback.onUpdateProgressViews(progressMillis, totalMillis); - - if (!MusicPlayerRemote.isPlaying()) { - return intervalPaused; - } - - final int remainingMillis = intervalPlaying - progressMillis % intervalPlaying; - - return Math.max(MIN_INTERVAL, remainingMillis); - } - - private void queueNextRefresh(final long delay) { - final Message message = obtainMessage(CMD_REFRESH_PROGRESS_VIEWS); - removeMessages(CMD_REFRESH_PROGRESS_VIEWS); - sendMessageDelayed(message, delay); - } - - public interface Callback { - void onUpdateProgressViews(int progress, int total); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/MusicProgressViewUpdateHelper.kt b/app/src/main/java/code/name/monkey/retromusic/helper/MusicProgressViewUpdateHelper.kt new file mode 100644 index 00000000..3103452e --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/helper/MusicProgressViewUpdateHelper.kt @@ -0,0 +1,72 @@ +package code.name.monkey.retromusic.helper + +import android.os.Handler +import android.os.Message + + +class MusicProgressViewUpdateHelper : Handler { + + private var callback: Callback? = null + private var intervalPlaying: Int = 0 + private var intervalPaused: Int = 0 + + fun start() { + queueNextRefresh(1) + } + + fun stop() { + removeMessages(CMD_REFRESH_PROGRESS_VIEWS) + } + + constructor(callback: Callback) { + this.callback = callback + this.intervalPlaying = UPDATE_INTERVAL_PLAYING + this.intervalPaused = UPDATE_INTERVAL_PAUSED + } + + constructor(callback: Callback, intervalPlaying: Int, intervalPaused: Int) { + this.callback = callback + this.intervalPlaying = intervalPlaying + this.intervalPaused = intervalPaused + } + + override fun handleMessage(msg: Message) { + super.handleMessage(msg) + if (msg.what == CMD_REFRESH_PROGRESS_VIEWS) { + queueNextRefresh(refreshProgressViews().toLong()) + } + } + + private fun refreshProgressViews(): Int { + val progressMillis = MusicPlayerRemote.songProgressMillis + val totalMillis = MusicPlayerRemote.songDurationMillis + + callback!!.onUpdateProgressViews(progressMillis, totalMillis) + + if (!MusicPlayerRemote.isPlaying) { + return intervalPaused + } + + val remainingMillis = intervalPlaying - progressMillis % intervalPlaying + + return Math.max(MIN_INTERVAL, remainingMillis) + } + + private fun queueNextRefresh(delay: Long) { + val message = obtainMessage(CMD_REFRESH_PROGRESS_VIEWS) + removeMessages(CMD_REFRESH_PROGRESS_VIEWS) + sendMessageDelayed(message, delay) + } + + interface Callback { + fun onUpdateProgressViews(progress: Int, total: Int) + } + + companion object { + private val CMD_REFRESH_PROGRESS_VIEWS = 1 + + private val MIN_INTERVAL = 20 + private val UPDATE_INTERVAL_PLAYING = 1000 + private val UPDATE_INTERVAL_PAUSED = 500 + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/PlayPauseButtonOnClickHandler.java b/app/src/main/java/code/name/monkey/retromusic/helper/PlayPauseButtonOnClickHandler.java deleted file mode 100644 index 4d69a5a4..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/helper/PlayPauseButtonOnClickHandler.java +++ /dev/null @@ -1,15 +0,0 @@ -package code.name.monkey.retromusic.helper; - -import android.view.View; - - -public class PlayPauseButtonOnClickHandler implements View.OnClickListener { - @Override - public void onClick(View v) { - if (MusicPlayerRemote.isPlaying()) { - MusicPlayerRemote.pauseSong(); - } else { - MusicPlayerRemote.resumePlaying(); - } - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/PlayPauseButtonOnClickHandler.kt b/app/src/main/java/code/name/monkey/retromusic/helper/PlayPauseButtonOnClickHandler.kt new file mode 100644 index 00000000..5e8b3c66 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/helper/PlayPauseButtonOnClickHandler.kt @@ -0,0 +1,14 @@ +package code.name.monkey.retromusic.helper + +import android.view.View + + +class PlayPauseButtonOnClickHandler : View.OnClickListener { + override fun onClick(v: View) { + if (MusicPlayerRemote.isPlaying) { + MusicPlayerRemote.pauseSong() + } else { + MusicPlayerRemote.resumePlaying() + } + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/SearchQueryHelper.java b/app/src/main/java/code/name/monkey/retromusic/helper/SearchQueryHelper.java deleted file mode 100644 index acef195a..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/helper/SearchQueryHelper.java +++ /dev/null @@ -1,91 +0,0 @@ -package code.name.monkey.retromusic.helper; - -import android.app.SearchManager; -import android.content.Context; -import android.os.Bundle; -import android.provider.MediaStore; -import androidx.annotation.NonNull; - -import java.util.ArrayList; - -import code.name.monkey.retromusic.loaders.SongLoader; -import code.name.monkey.retromusic.model.Song; - - -public class SearchQueryHelper { - private static final String TITLE_SELECTION = "lower(" + MediaStore.Audio.AudioColumns.TITLE + ") = ?"; - private static final String ALBUM_SELECTION = "lower(" + MediaStore.Audio.AudioColumns.ALBUM + ") = ?"; - private static final String ARTIST_SELECTION = "lower(" + MediaStore.Audio.AudioColumns.ARTIST + ") = ?"; - private static final String AND = " AND "; - private static ArrayList songs = new ArrayList<>(); - - public static ArrayList getSongs() { - return songs; - } - - public static void setSongs(ArrayList songs) { - SearchQueryHelper.songs = songs; - } - - @NonNull - public static ArrayList getSongs(@NonNull final Context context, @NonNull final Bundle extras) { - final String query = extras.getString(SearchManager.QUERY, null); - final String artistName = extras.getString(MediaStore.EXTRA_MEDIA_ARTIST, null); - final String albumName = extras.getString(MediaStore.EXTRA_MEDIA_ALBUM, null); - final String titleName = extras.getString(MediaStore.EXTRA_MEDIA_TITLE, null); - - ArrayList songs = new ArrayList<>(); - if (artistName != null && albumName != null && titleName != null) { - songs = SongLoader.Companion.getSongs(SongLoader.Companion.makeSongCursor(context, ARTIST_SELECTION + AND + ALBUM_SELECTION + AND + TITLE_SELECTION, new String[]{artistName.toLowerCase(), albumName.toLowerCase(), titleName.toLowerCase()})).blockingFirst(); - } - if (!songs.isEmpty()) { - return songs; - } - if (artistName != null && titleName != null) { - songs = SongLoader.Companion.getSongs(SongLoader.Companion.makeSongCursor(context, ARTIST_SELECTION + AND + TITLE_SELECTION, new String[]{artistName.toLowerCase(), titleName.toLowerCase()})).blockingFirst(); - } - if (!songs.isEmpty()) { - return songs; - } - if (albumName != null && titleName != null) { - songs = SongLoader.Companion.getSongs(SongLoader.Companion.makeSongCursor(context, ALBUM_SELECTION + AND + TITLE_SELECTION, new String[]{albumName.toLowerCase(), titleName.toLowerCase()})).blockingFirst(); - } - if (!songs.isEmpty()) { - return songs; - } - if (artistName != null) { - songs = SongLoader.Companion.getSongs(SongLoader.Companion.makeSongCursor(context, ARTIST_SELECTION, new String[]{artistName.toLowerCase()})).blockingFirst(); - } - if (!songs.isEmpty()) { - return songs; - } - if (albumName != null) { - songs = SongLoader.Companion.getSongs(SongLoader.Companion.makeSongCursor(context, ALBUM_SELECTION, new String[]{albumName.toLowerCase()})).blockingFirst(); - } - if (!songs.isEmpty()) { - return songs; - } - if (titleName != null) { - songs = SongLoader.Companion.getSongs(SongLoader.Companion.makeSongCursor(context, TITLE_SELECTION, new String[]{titleName.toLowerCase()})).blockingFirst(); - } - if (!songs.isEmpty()) { - return songs; - } - songs = SongLoader.Companion.getSongs(SongLoader.Companion.makeSongCursor(context, ARTIST_SELECTION, new String[]{query.toLowerCase()})).blockingFirst(); - - if (!songs.isEmpty()) { - return songs; - } - songs = SongLoader.Companion.getSongs(SongLoader.Companion.makeSongCursor(context, ALBUM_SELECTION, new String[]{query.toLowerCase()})).blockingFirst(); - if (!songs.isEmpty()) { - return songs; - } - songs = SongLoader.Companion.getSongs(SongLoader.Companion.makeSongCursor(context, TITLE_SELECTION, new String[]{query.toLowerCase()})).blockingFirst(); - if (!songs.isEmpty()) { - return songs; - } - return new ArrayList(); - } - - -} diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/SearchQueryHelper.kt b/app/src/main/java/code/name/monkey/retromusic/helper/SearchQueryHelper.kt new file mode 100644 index 00000000..44b09581 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/helper/SearchQueryHelper.kt @@ -0,0 +1,78 @@ +package code.name.monkey.retromusic.helper + +import android.app.SearchManager +import android.content.Context +import android.os.Bundle +import android.provider.MediaStore +import code.name.monkey.retromusic.loaders.SongLoader +import code.name.monkey.retromusic.model.Song +import java.util.* + + +object SearchQueryHelper { + private val TITLE_SELECTION = "lower(" + MediaStore.Audio.AudioColumns.TITLE + ") = ?" + private val ALBUM_SELECTION = "lower(" + MediaStore.Audio.AudioColumns.ALBUM + ") = ?" + private val ARTIST_SELECTION = "lower(" + MediaStore.Audio.AudioColumns.ARTIST + ") = ?" + private val AND = " AND " + var songs = ArrayList() + + fun getSongs(context: Context, extras: Bundle): ArrayList { + val query = extras.getString(SearchManager.QUERY, null) + val artistName = extras.getString(MediaStore.EXTRA_MEDIA_ARTIST, null) + val albumName = extras.getString(MediaStore.EXTRA_MEDIA_ALBUM, null) + val titleName = extras.getString(MediaStore.EXTRA_MEDIA_TITLE, null) + + var songs = ArrayList() + if (artistName != null && albumName != null && titleName != null) { + songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, ARTIST_SELECTION + AND + ALBUM_SELECTION + AND + TITLE_SELECTION, arrayOf(artistName.toLowerCase(), albumName.toLowerCase(), titleName.toLowerCase()))).blockingFirst() + } + if (!songs.isEmpty()) { + return songs + } + if (artistName != null && titleName != null) { + songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, ARTIST_SELECTION + AND + TITLE_SELECTION, arrayOf(artistName.toLowerCase(), titleName.toLowerCase()))).blockingFirst() + } + if (!songs.isEmpty()) { + return songs + } + if (albumName != null && titleName != null) { + songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, ALBUM_SELECTION + AND + TITLE_SELECTION, arrayOf(albumName.toLowerCase(), titleName.toLowerCase()))).blockingFirst() + } + if (!songs.isEmpty()) { + return songs + } + if (artistName != null) { + songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, ARTIST_SELECTION, arrayOf(artistName.toLowerCase()))).blockingFirst() + } + if (!songs.isEmpty()) { + return songs + } + if (albumName != null) { + songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, ALBUM_SELECTION, arrayOf(albumName.toLowerCase()))).blockingFirst() + } + if (!songs.isEmpty()) { + return songs + } + if (titleName != null) { + songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, TITLE_SELECTION, arrayOf(titleName.toLowerCase()))).blockingFirst() + } + if (!songs.isEmpty()) { + return songs + } + songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, ARTIST_SELECTION, arrayOf(query.toLowerCase()))).blockingFirst() + + if (!songs.isEmpty()) { + return songs + } + songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, ALBUM_SELECTION, arrayOf(query.toLowerCase()))).blockingFirst() + if (!songs.isEmpty()) { + return songs + } + songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, TITLE_SELECTION, arrayOf(query.toLowerCase()))).blockingFirst() + return if (!songs.isEmpty()) { + songs + } else ArrayList() + } + + +} diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/ShuffleHelper.java b/app/src/main/java/code/name/monkey/retromusic/helper/ShuffleHelper.java deleted file mode 100644 index 46721017..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/helper/ShuffleHelper.java +++ /dev/null @@ -1,25 +0,0 @@ -package code.name.monkey.retromusic.helper; - -import androidx.annotation.NonNull; - -import code.name.monkey.retromusic.model.Song; - -import java.util.Collections; -import java.util.List; - - -public class ShuffleHelper { - - public static void makeShuffleList(@NonNull List listToShuffle, final int current) { - if (listToShuffle.isEmpty()) return; - if (current >= 0) { - Song song = listToShuffle.remove(current); - Collections.shuffle(listToShuffle); - listToShuffle.add(0, song); - } else { - Collections.shuffle(listToShuffle); - } - } - - -} diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/ShuffleHelper.kt b/app/src/main/java/code/name/monkey/retromusic/helper/ShuffleHelper.kt new file mode 100644 index 00000000..aa50421b --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/helper/ShuffleHelper.kt @@ -0,0 +1,20 @@ +package code.name.monkey.retromusic.helper + +import code.name.monkey.retromusic.model.Song + +import java.util.Collections + + +object ShuffleHelper { + + fun makeShuffleList(listToShuffle: MutableList, current: Int) { + if (listToShuffle.isEmpty()) return + if (current >= 0) { + val song = listToShuffle.removeAt(current) + listToShuffle.shuffle() + listToShuffle.add(0, song) + } else { + listToShuffle.shuffle() + } + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/SortOrder.java b/app/src/main/java/code/name/monkey/retromusic/helper/SortOrder.java deleted file mode 100644 index 2c871702..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/helper/SortOrder.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (C) 2012 Andrew Neal 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 code.name.monkey.retromusic.helper; - -import android.provider.MediaStore; - -/** - * Holds all of the sort orders for each list type. - * - * @author Andrew Neal (andrewdneal@gmail.com) - */ -public final class SortOrder { - - /** - * This class is never instantiated - */ - public SortOrder() { - } - - /** - * Artist sort order entries. - */ - public interface ArtistSortOrder { - - /* Artist sort order A-Z */ - String ARTIST_A_Z = MediaStore.Audio.Artists.DEFAULT_SORT_ORDER; - - /* Artist sort order Z-A */ - String ARTIST_Z_A = ARTIST_A_Z + " DESC"; - - /* Artist sort order number of songs */ - String ARTIST_NUMBER_OF_SONGS = MediaStore.Audio.Artists.NUMBER_OF_TRACKS - + " DESC"; - - /* Artist sort order number of albums */ - String ARTIST_NUMBER_OF_ALBUMS = MediaStore.Audio.Artists.NUMBER_OF_ALBUMS - + " DESC"; - } - - /** - * Album sort order entries. - */ - public interface AlbumSortOrder { - - /* Album sort order A-Z */ - String ALBUM_A_Z = MediaStore.Audio.Albums.DEFAULT_SORT_ORDER; - - /* Album sort order Z-A */ - String ALBUM_Z_A = ALBUM_A_Z + " DESC"; - - /* Album sort order songs */ - String ALBUM_NUMBER_OF_SONGS = MediaStore.Audio.Albums.NUMBER_OF_SONGS - + " DESC"; - - /* Album sort order artist */ - String ALBUM_ARTIST = MediaStore.Audio.Artists.DEFAULT_SORT_ORDER - + ", " + MediaStore.Audio.Albums.DEFAULT_SORT_ORDER; - - /* Album sort order year */ - String ALBUM_YEAR = MediaStore.Audio.Media.YEAR + " DESC"; - } - - /** - * Song sort order entries. - */ - public interface SongSortOrder { - - /* Song sort order A-Z */ - String SONG_A_Z = MediaStore.Audio.Media.DEFAULT_SORT_ORDER; - - /* Song sort order Z-A */ - String SONG_Z_A = SONG_A_Z + " DESC"; - - /* Song sort order artist */ - String SONG_ARTIST = MediaStore.Audio.Artists.DEFAULT_SORT_ORDER; - - /* Song sort order album */ - String SONG_ALBUM = MediaStore.Audio.Albums.DEFAULT_SORT_ORDER; - - /* Song sort order year */ - String SONG_YEAR = MediaStore.Audio.Media.YEAR + " DESC"; - - /* Song sort order duration */ - String SONG_DURATION = MediaStore.Audio.Media.DURATION + " DESC"; - - /* Song sort order date */ - String SONG_DATE = MediaStore.Audio.Media.DATE_ADDED + " DESC"; - } - - /** - * Album song sort order entries. - */ - public interface AlbumSongSortOrder { - - /* Album song sort order A-Z */ - String SONG_A_Z = MediaStore.Audio.Media.DEFAULT_SORT_ORDER; - - /* Album song sort order Z-A */ - String SONG_Z_A = SONG_A_Z + " DESC"; - - /* Album song sort order track list */ - String SONG_TRACK_LIST = MediaStore.Audio.Media.TRACK + ", " - + MediaStore.Audio.Media.DEFAULT_SORT_ORDER; - - /* Album song sort order duration */ - String SONG_DURATION = SongSortOrder.SONG_DURATION; - } - - /** - * Artist song sort order entries. - */ - public interface ArtistSongSortOrder { - - /* Artist song sort order A-Z */ - String SONG_A_Z = MediaStore.Audio.Media.DEFAULT_SORT_ORDER; - - /* Artist song sort order Z-A */ - String SONG_Z_A = SONG_A_Z + " DESC"; - - /* Artist song sort order album */ - String SONG_ALBUM = MediaStore.Audio.Media.ALBUM; - - /* Artist song sort order year */ - String SONG_YEAR = MediaStore.Audio.Media.YEAR + " DESC"; - - /* Artist song sort order duration */ - String SONG_DURATION = MediaStore.Audio.Media.DURATION + " DESC"; - - /* Artist song sort order date */ - String SONG_DATE = MediaStore.Audio.Media.DATE_ADDED + " DESC"; - } - - /** - * Artist album sort order entries. - */ - public interface ArtistAlbumSortOrder { - - /* Artist album sort order A-Z */ - String ALBUM_A_Z = MediaStore.Audio.Albums.DEFAULT_SORT_ORDER; - - /* Artist album sort order Z-A */ - String ALBUM_Z_A = ALBUM_A_Z + " DESC"; - - /* Artist album sort order year */ - String ALBUM_YEAR = MediaStore.Audio.Media.YEAR - + " DESC"; - - /* Artist album sort order year */ - String ALBUM_YEAR_ASC = MediaStore.Audio.Media.YEAR - + " ASC"; - } - - /** - * Genre sort order entries. - */ - public interface GenreSortOrder { - - /* Genre sort order A-Z */ - String GENRE_A_Z = MediaStore.Audio.Genres.DEFAULT_SORT_ORDER; - - /* Genre sort order Z-A */ - String ALBUM_Z_A = GENRE_A_Z + " DESC"; - } - -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/SortOrder.kt b/app/src/main/java/code/name/monkey/retromusic/helper/SortOrder.kt new file mode 100644 index 00000000..b2fb952e --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/helper/SortOrder.kt @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2012 Andrew Neal 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 code.name.monkey.retromusic.helper + +import android.provider.MediaStore + +/** + * Holds all of the sort orders for each list type. + * + * @author Andrew Neal (andrewdneal@gmail.com) + */ +/** + * This class is never instantiated + */ +class SortOrder { + + /** + * Artist sort order entries. + */ + interface ArtistSortOrder { + companion object { + + /* Artist sort order A-Z */ + val ARTIST_A_Z = MediaStore.Audio.Artists.DEFAULT_SORT_ORDER + + /* Artist sort order Z-A */ + val ARTIST_Z_A = "$ARTIST_A_Z DESC" + + /* Artist sort order number of songs */ + val ARTIST_NUMBER_OF_SONGS = MediaStore.Audio.Artists.NUMBER_OF_TRACKS + " DESC" + + /* Artist sort order number of albums */ + val ARTIST_NUMBER_OF_ALBUMS = MediaStore.Audio.Artists.NUMBER_OF_ALBUMS + " DESC" + } + } + + /** + * Album sort order entries. + */ + interface AlbumSortOrder { + companion object { + + /* Album sort order A-Z */ + val ALBUM_A_Z = MediaStore.Audio.Albums.DEFAULT_SORT_ORDER + + /* Album sort order Z-A */ + val ALBUM_Z_A = "$ALBUM_A_Z DESC" + + /* Album sort order songs */ + val ALBUM_NUMBER_OF_SONGS = MediaStore.Audio.Albums.NUMBER_OF_SONGS + " DESC" + + /* Album sort order artist */ + val ALBUM_ARTIST = (MediaStore.Audio.Artists.DEFAULT_SORT_ORDER + + ", " + MediaStore.Audio.Albums.DEFAULT_SORT_ORDER) + + /* Album sort order year */ + val ALBUM_YEAR = MediaStore.Audio.Media.YEAR + " DESC" + } + } + + /** + * Song sort order entries. + */ + interface SongSortOrder { + companion object { + + /* Song sort order A-Z */ + val SONG_A_Z = MediaStore.Audio.Media.DEFAULT_SORT_ORDER + + /* Song sort order Z-A */ + val SONG_Z_A = "$SONG_A_Z DESC" + + /* Song sort order artist */ + val SONG_ARTIST = MediaStore.Audio.Artists.DEFAULT_SORT_ORDER + + /* Song sort order album */ + val SONG_ALBUM = MediaStore.Audio.Albums.DEFAULT_SORT_ORDER + + /* Song sort order year */ + val SONG_YEAR = MediaStore.Audio.Media.YEAR + " DESC" + + /* Song sort order duration */ + val SONG_DURATION = MediaStore.Audio.Media.DURATION + " DESC" + + /* Song sort order date */ + val SONG_DATE = MediaStore.Audio.Media.DATE_ADDED + " DESC" + } + } + + /** + * Album song sort order entries. + */ + interface AlbumSongSortOrder { + companion object { + + /* Album song sort order A-Z */ + val SONG_A_Z = MediaStore.Audio.Media.DEFAULT_SORT_ORDER + + /* Album song sort order Z-A */ + val SONG_Z_A = "$SONG_A_Z DESC" + + /* Album song sort order track list */ + val SONG_TRACK_LIST = (MediaStore.Audio.Media.TRACK + ", " + + MediaStore.Audio.Media.DEFAULT_SORT_ORDER) + + /* Album song sort order duration */ + val SONG_DURATION = SongSortOrder.SONG_DURATION + } + } + + /** + * Artist song sort order entries. + */ + interface ArtistSongSortOrder { + companion object { + + /* Artist song sort order A-Z */ + val SONG_A_Z = MediaStore.Audio.Media.DEFAULT_SORT_ORDER + + /* Artist song sort order Z-A */ + val SONG_Z_A = "$SONG_A_Z DESC" + + /* Artist song sort order album */ + val SONG_ALBUM = MediaStore.Audio.Media.ALBUM + + /* Artist song sort order year */ + val SONG_YEAR = MediaStore.Audio.Media.YEAR + " DESC" + + /* Artist song sort order duration */ + val SONG_DURATION = MediaStore.Audio.Media.DURATION + " DESC" + + /* Artist song sort order date */ + val SONG_DATE = MediaStore.Audio.Media.DATE_ADDED + " DESC" + } + } + + /** + * Artist album sort order entries. + */ + interface ArtistAlbumSortOrder { + companion object { + + /* Artist album sort order A-Z */ + val ALBUM_A_Z = MediaStore.Audio.Albums.DEFAULT_SORT_ORDER + + /* Artist album sort order Z-A */ + val ALBUM_Z_A = "$ALBUM_A_Z DESC" + + /* Artist album sort order year */ + val ALBUM_YEAR = MediaStore.Audio.Media.YEAR + " DESC" + + /* Artist album sort order year */ + val ALBUM_YEAR_ASC = MediaStore.Audio.Media.YEAR + " ASC" + } + } + + /** + * Genre sort order entries. + */ + interface GenreSortOrder { + companion object { + + /* Genre sort order A-Z */ + val GENRE_A_Z = MediaStore.Audio.Genres.DEFAULT_SORT_ORDER + + /* Genre sort order Z-A */ + val ALBUM_Z_A = "$GENRE_A_Z DESC" + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/StopWatch.java b/app/src/main/java/code/name/monkey/retromusic/helper/StopWatch.java deleted file mode 100644 index 896f68ac..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/helper/StopWatch.java +++ /dev/null @@ -1,82 +0,0 @@ -package code.name.monkey.retromusic.helper; - -/** - * Simple thread safe stop watch. - * - * @author Karim Abou Zeid (kabouzeid) - */ -public class StopWatch { - - /** - * The time the stop watch was last started. - */ - private long startTime; - - /** - * The time elapsed before the current {@link #startTime}. - */ - private long previousElapsedTime; - - /** - * Whether the stop watch is currently running or not. - */ - private boolean isRunning; - - /** - * Starts or continues the stop watch. - * - * @see #pause() - * @see #reset() - */ - public void start() { - synchronized (this) { - startTime = System.currentTimeMillis(); - isRunning = true; - } - } - - /** - * Pauses the stop watch. It can be continued later from {@link #start()}. - * - * @see #start() - * @see #reset() - */ - public void pause() { - synchronized (this) { - previousElapsedTime += System.currentTimeMillis() - startTime; - isRunning = false; - } - } - - /** - * Stops and resets the stop watch to zero milliseconds. - * - * @see #start() - * @see #pause() - */ - public void reset() { - synchronized (this) { - startTime = 0; - previousElapsedTime = 0; - isRunning = false; - } - } - - /** - * @return the total elapsed time in milliseconds - */ - public final long getElapsedTime() { - synchronized (this) { - long currentElapsedTime = 0; - if (isRunning) { - currentElapsedTime = System.currentTimeMillis() - startTime; - } - return previousElapsedTime + currentElapsedTime; - } - } - - @Override - public String toString() { - return String.format("%d millis", getElapsedTime()); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/StopWatch.kt b/app/src/main/java/code/name/monkey/retromusic/helper/StopWatch.kt new file mode 100644 index 00000000..21ab67fa --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/helper/StopWatch.kt @@ -0,0 +1,80 @@ +package code.name.monkey.retromusic.helper + +/** + * Simple thread safe stop watch. + * + * @author Karim Abou Zeid (kabouzeid) + */ +class StopWatch { + + /** + * The time the stop watch was last started. + */ + private var startTime: Long = 0 + + /** + * The time elapsed before the current [.startTime]. + */ + private var previousElapsedTime: Long = 0 + + /** + * Whether the stop watch is currently running or not. + */ + private var isRunning: Boolean = false + + /** + * @return the total elapsed time in milliseconds + */ + val elapsedTime: Long + get() = synchronized(this) { + var currentElapsedTime: Long = 0 + if (isRunning) { + currentElapsedTime = System.currentTimeMillis() - startTime + } + return previousElapsedTime + currentElapsedTime + } + + /** + * Starts or continues the stop watch. + * + * @see .pause + * @see .reset + */ + fun start() { + synchronized(this) { + startTime = System.currentTimeMillis() + isRunning = true + } + } + + /** + * Pauses the stop watch. It can be continued later from [.start]. + * + * @see .start + * @see .reset + */ + fun pause() { + synchronized(this) { + previousElapsedTime += System.currentTimeMillis() - startTime + isRunning = false + } + } + + /** + * Stops and resets the stop watch to zero milliseconds. + * + * @see .start + * @see .pause + */ + fun reset() { + synchronized(this) { + startTime = 0 + previousElapsedTime = 0 + isRunning = false + } + } + + override fun toString(): String { + return String.format("%d millis", elapsedTime) + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/menu/GenreMenuHelper.java b/app/src/main/java/code/name/monkey/retromusic/helper/menu/GenreMenuHelper.java deleted file mode 100644 index f78ec189..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/helper/menu/GenreMenuHelper.java +++ /dev/null @@ -1,50 +0,0 @@ -package code.name.monkey.retromusic.helper.menu; - -import android.app.Activity; -import androidx.annotation.NonNull; -import androidx.appcompat.app.AppCompatActivity; -import android.view.MenuItem; - -import java.util.ArrayList; - -import code.name.monkey.retromusic.loaders.GenreLoader; -import code.name.monkey.retromusic.model.Genre; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; - -/** - * @author Hemanth S (h4h13). - */ - -public class GenreMenuHelper { - public static boolean handleMenuClick(@NonNull AppCompatActivity activity, - @NonNull Genre genre, - @NonNull MenuItem item) { - switch (item.getItemId()) { - case R.id.action_play: - MusicPlayerRemote.openQueue(getGenreSongs(activity, genre), 0, true); - return true; - case R.id.action_play_next: - MusicPlayerRemote.playNext(getGenreSongs(activity, genre)); - return true; - case R.id.action_add_to_playlist: - AddToPlaylistDialog.create(getGenreSongs(activity, genre)) - .show(activity.getSupportFragmentManager(), "ADD_PLAYLIST"); - return true; - case R.id.action_add_to_current_playing: - MusicPlayerRemote.enqueue(getGenreSongs(activity, genre)); - return true; - } - return false; - } - - @NonNull - private static ArrayList getGenreSongs(@NonNull Activity activity, - @NonNull Genre genre) { - ArrayList songs; - songs = GenreLoader.getSongs(activity, genre.id).blockingFirst(); - return songs; - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/menu/GenreMenuHelper.kt b/app/src/main/java/code/name/monkey/retromusic/helper/menu/GenreMenuHelper.kt new file mode 100644 index 00000000..6796361f --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/helper/menu/GenreMenuHelper.kt @@ -0,0 +1,52 @@ +package code.name.monkey.retromusic.helper.menu + +import android.app.Activity +import androidx.appcompat.app.AppCompatActivity +import android.view.MenuItem + +import java.util.ArrayList + +import code.name.monkey.retromusic.loaders.GenreLoader +import code.name.monkey.retromusic.model.Genre +import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog +import code.name.monkey.retromusic.helper.MusicPlayerRemote + +/** + * @author Hemanth S (h4h13). + */ + +object GenreMenuHelper { + fun handleMenuClick(activity: AppCompatActivity, + genre: Genre, + item: MenuItem): Boolean { + when (item.itemId) { + R.id.action_play -> { + MusicPlayerRemote.openQueue(getGenreSongs(activity, genre), 0, true) + return true + } + R.id.action_play_next -> { + MusicPlayerRemote.playNext(getGenreSongs(activity, genre)) + return true + } + R.id.action_add_to_playlist -> { + AddToPlaylistDialog.create(getGenreSongs(activity, genre)) + .show(activity.supportFragmentManager, "ADD_PLAYLIST") + return true + } + R.id.action_add_to_current_playing -> { + MusicPlayerRemote.enqueue(getGenreSongs(activity, genre)) + return true + } + } + return false + } + + private fun getGenreSongs(activity: Activity, + genre: Genre): ArrayList { + val songs: ArrayList + songs = GenreLoader.getSongs(activity, genre.id).blockingFirst() + return songs + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/menu/PlaylistMenuHelper.java b/app/src/main/java/code/name/monkey/retromusic/helper/menu/PlaylistMenuHelper.java deleted file mode 100644 index 214ae79c..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/helper/menu/PlaylistMenuHelper.java +++ /dev/null @@ -1,91 +0,0 @@ -package code.name.monkey.retromusic.helper.menu; - -import android.app.Activity; -import android.content.Context; -import androidx.annotation.NonNull; -import androidx.appcompat.app.AppCompatActivity; -import android.view.MenuItem; -import android.widget.Toast; - -import java.util.ArrayList; - -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.RetroApplication; -import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog; -import code.name.monkey.retromusic.dialogs.DeletePlaylistDialog; -import code.name.monkey.retromusic.dialogs.RenamePlaylistDialog; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.loaders.PlaylistSongsLoader; -import code.name.monkey.retromusic.misc.WeakContextAsyncTask; -import code.name.monkey.retromusic.model.AbsCustomPlaylist; -import code.name.monkey.retromusic.model.Playlist; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.util.PlaylistsUtil; - - -public class PlaylistMenuHelper { - - public static boolean handleMenuClick(@NonNull AppCompatActivity activity, - @NonNull final Playlist playlist, @NonNull MenuItem item) { - switch (item.getItemId()) { - case R.id.action_play: - MusicPlayerRemote.openQueue(getPlaylistSongs(activity, playlist), 9, true); - return true; - case R.id.action_play_next: - MusicPlayerRemote.playNext(getPlaylistSongs(activity, playlist)); - return true; - case R.id.action_add_to_playlist: - AddToPlaylistDialog.create(getPlaylistSongs(activity, playlist)) - .show(activity.getSupportFragmentManager(), "ADD_PLAYLIST"); - return true; - case R.id.action_add_to_current_playing: - MusicPlayerRemote.enqueue(getPlaylistSongs(activity, playlist)); - return true; - case R.id.action_rename_playlist: - RenamePlaylistDialog.create(playlist.id) - .show(activity.getSupportFragmentManager(), "RENAME_PLAYLIST"); - return true; - case R.id.action_delete_playlist: - DeletePlaylistDialog.create(playlist) - .show(activity.getSupportFragmentManager(), "DELETE_PLAYLIST"); - return true; - case R.id.action_save_playlist: - new SavePlaylistAsyncTask(activity).execute(playlist); - return true; - } - return false; - } - - @NonNull - private static ArrayList getPlaylistSongs(@NonNull Activity activity, - @NonNull Playlist playlist) { - ArrayList songs; - if (playlist instanceof AbsCustomPlaylist) { - songs = ((AbsCustomPlaylist) playlist).getSongs(activity).blockingFirst(); - } else { - songs = PlaylistSongsLoader.getPlaylistSongList(activity, playlist).blockingFirst(); - } - return songs; - } - - private static class SavePlaylistAsyncTask extends WeakContextAsyncTask { - SavePlaylistAsyncTask(Context context) { - super(context); - } - - @Override - protected String doInBackground(Playlist... params) { - return String.format(RetroApplication.getInstance().getApplicationContext().getString(R.string - .saved_playlist_to), PlaylistsUtil.savePlaylist(RetroApplication.getInstance().getApplicationContext(), params[0]).blockingFirst()); - } - - @Override - protected void onPostExecute(String string) { - super.onPostExecute(string); - Context context = getContext(); - if (context != null) { - Toast.makeText(context, string, Toast.LENGTH_LONG).show(); - } - } - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/menu/PlaylistMenuHelper.kt b/app/src/main/java/code/name/monkey/retromusic/helper/menu/PlaylistMenuHelper.kt new file mode 100644 index 00000000..5289ad31 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/helper/menu/PlaylistMenuHelper.kt @@ -0,0 +1,91 @@ +package code.name.monkey.retromusic.helper.menu + +import android.app.Activity +import android.content.Context +import androidx.appcompat.app.AppCompatActivity +import android.view.MenuItem +import android.widget.Toast + +import java.util.ArrayList + +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.RetroApplication +import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog +import code.name.monkey.retromusic.dialogs.DeletePlaylistDialog +import code.name.monkey.retromusic.dialogs.RenamePlaylistDialog +import code.name.monkey.retromusic.helper.MusicPlayerRemote +import code.name.monkey.retromusic.loaders.PlaylistSongsLoader +import code.name.monkey.retromusic.misc.WeakContextAsyncTask +import code.name.monkey.retromusic.model.AbsCustomPlaylist +import code.name.monkey.retromusic.model.Playlist +import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.util.PlaylistsUtil + + +object PlaylistMenuHelper { + + fun handleMenuClick(activity: AppCompatActivity, + playlist: Playlist, item: MenuItem): Boolean { + when (item.itemId) { + R.id.action_play -> { + MusicPlayerRemote.openQueue(getPlaylistSongs(activity, playlist), 9, true) + return true + } + R.id.action_play_next -> { + MusicPlayerRemote.playNext(getPlaylistSongs(activity, playlist)) + return true + } + R.id.action_add_to_playlist -> { + AddToPlaylistDialog.create(getPlaylistSongs(activity, playlist)) + .show(activity.supportFragmentManager, "ADD_PLAYLIST") + return true + } + R.id.action_add_to_current_playing -> { + MusicPlayerRemote.enqueue(getPlaylistSongs(activity, playlist)) + return true + } + R.id.action_rename_playlist -> { + RenamePlaylistDialog.create(playlist.id.toLong()) + .show(activity.supportFragmentManager, "RENAME_PLAYLIST") + return true + } + R.id.action_delete_playlist -> { + DeletePlaylistDialog.create(playlist) + .show(activity.supportFragmentManager, "DELETE_PLAYLIST") + return true + } + R.id.action_save_playlist -> { + SavePlaylistAsyncTask(activity).execute(playlist) + return true + } + } + return false + } + + private fun getPlaylistSongs(activity: Activity, + playlist: Playlist): ArrayList { + val songs: ArrayList + if (playlist is AbsCustomPlaylist) { + songs = playlist.getSongs(activity).blockingFirst() + } else { + songs = PlaylistSongsLoader.getPlaylistSongList(activity, playlist).blockingFirst() + } + return songs + } + + private class SavePlaylistAsyncTask internal constructor(context: Context) : WeakContextAsyncTask(context) { + + override fun doInBackground(vararg params: Playlist): String { + return String.format(RetroApplication.instance.applicationContext.getString(R.string + .saved_playlist_to), PlaylistsUtil.savePlaylist(RetroApplication.instance.applicationContext, params[0]).blockingFirst()) + } + + override fun onPostExecute(string: String) { + super.onPostExecute(string) + val context = context + if (context != null) { + Toast.makeText(context, string, Toast.LENGTH_LONG).show() + } + } + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/menu/SongMenuHelper.java b/app/src/main/java/code/name/monkey/retromusic/helper/menu/SongMenuHelper.java deleted file mode 100644 index 23265664..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/helper/menu/SongMenuHelper.java +++ /dev/null @@ -1,93 +0,0 @@ -package code.name.monkey.retromusic.helper.menu; - -import android.content.Intent; -import androidx.annotation.NonNull; -import androidx.fragment.app.FragmentActivity; -import androidx.appcompat.app.AppCompatActivity; -import android.view.MenuItem; -import android.view.View; -import android.widget.PopupMenu; - -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog; -import code.name.monkey.retromusic.dialogs.DeleteSongsDialog; -import code.name.monkey.retromusic.dialogs.SongDetailDialog; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.interfaces.PaletteColorHolder; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.ui.activities.tageditor.AbsTagEditorActivity; -import code.name.monkey.retromusic.ui.activities.tageditor.SongTagEditorActivity; -import code.name.monkey.retromusic.util.MusicUtil; -import code.name.monkey.retromusic.util.NavigationUtil; - - -public class SongMenuHelper { - public static final int MENU_RES = R.menu.menu_item_song; - - public static boolean handleMenuClick(@NonNull FragmentActivity activity, @NonNull Song song, int menuItemId) { - switch (menuItemId) { - case R.id.action_set_as_ringtone: - MusicUtil.setRingtone(activity, song.id); - return true; - case R.id.action_share: - activity.startActivity(Intent.createChooser(MusicUtil.createShareSongFileIntent(song, activity), null)); - return true; - case R.id.action_delete_from_device: - DeleteSongsDialog.create(song).show(activity.getSupportFragmentManager(), "DELETE_SONGS"); - return true; - case R.id.action_add_to_playlist: - AddToPlaylistDialog.create(song).show(activity.getSupportFragmentManager(), "ADD_PLAYLIST"); - return true; - case R.id.action_play_next: - MusicPlayerRemote.playNext(song); - return true; - case R.id.action_add_to_current_playing: - MusicPlayerRemote.enqueue(song); - return true; - case R.id.action_tag_editor: - Intent tagEditorIntent = new Intent(activity, SongTagEditorActivity.class); - tagEditorIntent.putExtra(AbsTagEditorActivity.EXTRA_ID, song.id); - if (activity instanceof PaletteColorHolder) - tagEditorIntent.putExtra(AbsTagEditorActivity.EXTRA_PALETTE, ((PaletteColorHolder) activity).getPaletteColor()); - activity.startActivity(tagEditorIntent); - return true; - case R.id.action_details: - SongDetailDialog.create(song).show(activity.getSupportFragmentManager(), "SONG_DETAILS"); - return true; - case R.id.action_go_to_album: - NavigationUtil.goToAlbum(activity, song.albumId); - return true; - case R.id.action_go_to_artist: - NavigationUtil.goToArtist(activity, song.artistId); - return true; - } - return false; - } - - public static abstract class OnClickSongMenu implements View.OnClickListener, PopupMenu.OnMenuItemClickListener { - private AppCompatActivity activity; - - protected OnClickSongMenu(@NonNull AppCompatActivity activity) { - this.activity = activity; - } - - public int getMenuRes() { - return MENU_RES; - } - - @Override - public void onClick(View v) { - PopupMenu popupMenu = new PopupMenu(activity, v); - popupMenu.inflate(getMenuRes()); - popupMenu.setOnMenuItemClickListener(this); - popupMenu.show(); - } - - @Override - public boolean onMenuItemClick(MenuItem item) { - return handleMenuClick(activity, getSong(), item.getItemId()); - } - - public abstract Song getSong(); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/menu/SongMenuHelper.kt b/app/src/main/java/code/name/monkey/retromusic/helper/menu/SongMenuHelper.kt new file mode 100644 index 00000000..ac6adc85 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/helper/menu/SongMenuHelper.kt @@ -0,0 +1,93 @@ +package code.name.monkey.retromusic.helper.menu + +import android.content.Intent +import android.view.MenuItem +import android.view.View +import android.widget.PopupMenu +import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.FragmentActivity +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog +import code.name.monkey.retromusic.dialogs.DeleteSongsDialog +import code.name.monkey.retromusic.dialogs.SongDetailDialog +import code.name.monkey.retromusic.helper.MusicPlayerRemote +import code.name.monkey.retromusic.interfaces.PaletteColorHolder +import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.ui.activities.tageditor.AbsTagEditorActivity +import code.name.monkey.retromusic.ui.activities.tageditor.SongTagEditorActivity +import code.name.monkey.retromusic.util.MusicUtil +import code.name.monkey.retromusic.util.NavigationUtil + + +object SongMenuHelper { + val MENU_RES = R.menu.menu_item_song + + fun handleMenuClick(activity: FragmentActivity, song: Song, menuItemId: Int): Boolean { + when (menuItemId) { + R.id.action_set_as_ringtone -> { + MusicUtil.setRingtone(activity, song.id) + return true + } + R.id.action_share -> { + activity.startActivity(Intent.createChooser(MusicUtil.createShareSongFileIntent(song, activity), null)) + return true + } + R.id.action_delete_from_device -> { + DeleteSongsDialog.create(song).show(activity.supportFragmentManager, "DELETE_SONGS") + return true + } + R.id.action_add_to_playlist -> { + AddToPlaylistDialog.create(song).show(activity.supportFragmentManager, "ADD_PLAYLIST") + return true + } + R.id.action_play_next -> { + MusicPlayerRemote.playNext(song) + return true + } + R.id.action_add_to_current_playing -> { + MusicPlayerRemote.enqueue(song) + return true + } + R.id.action_tag_editor -> { + val tagEditorIntent = Intent(activity, SongTagEditorActivity::class.java) + tagEditorIntent.putExtra(AbsTagEditorActivity.EXTRA_ID, song.id) + if (activity is PaletteColorHolder) + tagEditorIntent.putExtra(AbsTagEditorActivity.EXTRA_PALETTE, (activity as PaletteColorHolder).paletteColor) + activity.startActivity(tagEditorIntent) + return true + } + R.id.action_details -> { + SongDetailDialog.create(song).show(activity.supportFragmentManager, "SONG_DETAILS") + return true + } + R.id.action_go_to_album -> { + NavigationUtil.goToAlbum(activity, song.albumId) + return true + } + R.id.action_go_to_artist -> { + NavigationUtil.goToArtist(activity, song.artistId) + return true + } + } + return false + } + + abstract class OnClickSongMenu protected constructor(private val activity: AppCompatActivity) : View.OnClickListener, PopupMenu.OnMenuItemClickListener { + + open val menuRes: Int + get() = MENU_RES + + abstract val song: Song + + override fun onClick(v: View) { + val popupMenu = PopupMenu(activity, v) + popupMenu.inflate(menuRes) + popupMenu.setOnMenuItemClickListener(this) + popupMenu.show() + } + + override fun onMenuItemClick(item: MenuItem): Boolean { + return handleMenuClick(activity, song, item.itemId) + } + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/menu/SongsMenuHelper.java b/app/src/main/java/code/name/monkey/retromusic/helper/menu/SongsMenuHelper.java deleted file mode 100644 index e341e976..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/helper/menu/SongsMenuHelper.java +++ /dev/null @@ -1,35 +0,0 @@ -package code.name.monkey.retromusic.helper.menu; - -import androidx.annotation.NonNull; -import androidx.fragment.app.FragmentActivity; - -import code.name.monkey.retromusic.model.Song; - -import java.util.ArrayList; - -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog; -import code.name.monkey.retromusic.dialogs.DeleteSongsDialog; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; - - - -public class SongsMenuHelper { - public static boolean handleMenuClick(@NonNull FragmentActivity activity, @NonNull ArrayList songs, int menuItemId) { - switch (menuItemId) { - case R.id.action_play_next: - MusicPlayerRemote.playNext(songs); - return true; - case R.id.action_add_to_current_playing: - MusicPlayerRemote.enqueue(songs); - return true; - case R.id.action_add_to_playlist: - AddToPlaylistDialog.create(songs).show(activity.getSupportFragmentManager(), "ADD_PLAYLIST"); - return true; - case R.id.action_delete_from_device: - DeleteSongsDialog.create(songs).show(activity.getSupportFragmentManager(), "DELETE_SONGS"); - return true; - } - return false; - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/menu/SongsMenuHelper.kt b/app/src/main/java/code/name/monkey/retromusic/helper/menu/SongsMenuHelper.kt new file mode 100644 index 00000000..b246864a --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/helper/menu/SongsMenuHelper.kt @@ -0,0 +1,34 @@ +package code.name.monkey.retromusic.helper.menu + +import androidx.fragment.app.FragmentActivity +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog +import code.name.monkey.retromusic.dialogs.DeleteSongsDialog +import code.name.monkey.retromusic.helper.MusicPlayerRemote +import code.name.monkey.retromusic.model.Song +import java.util.* + + +object SongsMenuHelper { + fun handleMenuClick(activity: FragmentActivity, songs: ArrayList, menuItemId: Int): Boolean { + when (menuItemId) { + R.id.action_play_next -> { + MusicPlayerRemote.playNext(songs) + return true + } + R.id.action_add_to_current_playing -> { + MusicPlayerRemote.enqueue(songs) + return true + } + R.id.action_add_to_playlist -> { + AddToPlaylistDialog.create(songs).show(activity.supportFragmentManager, "ADD_PLAYLIST") + return true + } + R.id.action_delete_from_device -> { + DeleteSongsDialog.create(songs).show(activity.supportFragmentManager, "DELETE_SONGS") + return true + } + } + return false + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/interfaces/CabHolder.java b/app/src/main/java/code/name/monkey/retromusic/interfaces/CabHolder.java deleted file mode 100644 index e55195de..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/interfaces/CabHolder.java +++ /dev/null @@ -1,12 +0,0 @@ -package code.name.monkey.retromusic.interfaces; - -import androidx.annotation.NonNull; - -import com.afollestad.materialcab.MaterialCab; - - -public interface CabHolder { - - @NonNull - MaterialCab openCab(final int menuRes, final MaterialCab.Callback callback); -} diff --git a/app/src/main/java/code/name/monkey/retromusic/interfaces/CabHolder.kt b/app/src/main/java/code/name/monkey/retromusic/interfaces/CabHolder.kt new file mode 100644 index 00000000..88378195 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/interfaces/CabHolder.kt @@ -0,0 +1,9 @@ +package code.name.monkey.retromusic.interfaces + +import com.afollestad.materialcab.MaterialCab + + +interface CabHolder { + + fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab +} diff --git a/app/src/main/java/code/name/monkey/retromusic/interfaces/EqualizerInterface.java b/app/src/main/java/code/name/monkey/retromusic/interfaces/EqualizerInterface.java deleted file mode 100644 index 88143418..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/interfaces/EqualizerInterface.java +++ /dev/null @@ -1,49 +0,0 @@ -package code.name.monkey.retromusic.interfaces; - -import android.media.audiofx.BassBoost; -import android.media.audiofx.Equalizer; -import android.media.audiofx.Virtualizer; - -/** - * @author Hemanth S (h4h13). - */ - -public interface EqualizerInterface { - int getBandLevelLow(); - - int getBandLevelHigh(); - - int getNumberOfBands(); - - int getCenterFreq(int band); - - int getBandLevel(int band); - - void setBandLevel(int band, int level); - - boolean isBassBoostEnabled(); - - void setBassBoostEnabled(boolean isEnabled); - - int getBassBoostStrength(); - - void setBassBoostStrength(int strength); - - boolean isVirtualizerEnabled(); - - void setVirtualizerEnabled(boolean isEnabled); - - int getVirtualizerStrength(); - - void setVirtualizerStrength(int strength); - - boolean isRunning(); - - Equalizer getEqualizer(); - - BassBoost getBassBoost(); - - Virtualizer getVirtualizer(); - - -} diff --git a/app/src/main/java/code/name/monkey/retromusic/interfaces/EqualizerInterface.kt b/app/src/main/java/code/name/monkey/retromusic/interfaces/EqualizerInterface.kt new file mode 100644 index 00000000..5b3570b8 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/interfaces/EqualizerInterface.kt @@ -0,0 +1,41 @@ +package code.name.monkey.retromusic.interfaces + +import android.media.audiofx.BassBoost +import android.media.audiofx.Equalizer +import android.media.audiofx.Virtualizer + +/** + * @author Hemanth S (h4h13). + */ + +interface EqualizerInterface { + val bandLevelLow: Int + + val bandLevelHigh: Int + + val numberOfBands: Int + + var isBassBoostEnabled: Boolean + + var bassBoostStrength: Int + + var isVirtualizerEnabled: Boolean + + var virtualizerStrength: Int + + val isRunning: Boolean + + val equalizer: Equalizer + + val bassBoost: BassBoost + + val virtualizer: Virtualizer + + fun getCenterFreq(band: Int): Int + + fun getBandLevel(band: Int): Int + + fun setBandLevel(band: Int, level: Int) + + +} diff --git a/app/src/main/java/code/name/monkey/retromusic/interfaces/LoaderIds.java b/app/src/main/java/code/name/monkey/retromusic/interfaces/LoaderIds.java deleted file mode 100644 index e7792818..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/interfaces/LoaderIds.java +++ /dev/null @@ -1,6 +0,0 @@ -package code.name.monkey.retromusic.interfaces; - - -public interface LoaderIds { - int FOLDERS_FRAGMENT = 5; -} diff --git a/app/src/main/java/code/name/monkey/retromusic/interfaces/LoaderIds.kt b/app/src/main/java/code/name/monkey/retromusic/interfaces/LoaderIds.kt new file mode 100644 index 00000000..90f23871 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/interfaces/LoaderIds.kt @@ -0,0 +1,8 @@ +package code.name.monkey.retromusic.interfaces + + +interface LoaderIds { + companion object { + val FOLDERS_FRAGMENT = 5 + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/interfaces/MainActivityFragmentCallbacks.java b/app/src/main/java/code/name/monkey/retromusic/interfaces/MainActivityFragmentCallbacks.java deleted file mode 100644 index 81dbbccb..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/interfaces/MainActivityFragmentCallbacks.java +++ /dev/null @@ -1,11 +0,0 @@ -package code.name.monkey.retromusic.interfaces; - -/** - * Created by hemanths on 14/08/17. - */ - -public interface MainActivityFragmentCallbacks { - boolean handleBackPress(); - - //void selectedFragment(Fragment fragment); -} diff --git a/app/src/main/java/code/name/monkey/retromusic/interfaces/MainActivityFragmentCallbacks.kt b/app/src/main/java/code/name/monkey/retromusic/interfaces/MainActivityFragmentCallbacks.kt new file mode 100644 index 00000000..537e80b0 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/interfaces/MainActivityFragmentCallbacks.kt @@ -0,0 +1,11 @@ +package code.name.monkey.retromusic.interfaces + +/** + * Created by hemanths on 14/08/17. + */ + +interface MainActivityFragmentCallbacks { + fun handleBackPress(): Boolean + + //void selectedFragment(Fragment fragment); +} diff --git a/app/src/main/java/code/name/monkey/retromusic/interfaces/MusicServiceEventListener.java b/app/src/main/java/code/name/monkey/retromusic/interfaces/MusicServiceEventListener.java deleted file mode 100644 index ea609019..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/interfaces/MusicServiceEventListener.java +++ /dev/null @@ -1,20 +0,0 @@ -package code.name.monkey.retromusic.interfaces; - - -public interface MusicServiceEventListener { - void onServiceConnected(); - - void onServiceDisconnected(); - - void onQueueChanged(); - - void onPlayingMetaChanged(); - - void onPlayStateChanged(); - - void onRepeatModeChanged(); - - void onShuffleModeChanged(); - - void onMediaStoreChanged(); -} diff --git a/app/src/main/java/code/name/monkey/retromusic/interfaces/MusicServiceEventListener.kt b/app/src/main/java/code/name/monkey/retromusic/interfaces/MusicServiceEventListener.kt new file mode 100644 index 00000000..66a1a016 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/interfaces/MusicServiceEventListener.kt @@ -0,0 +1,20 @@ +package code.name.monkey.retromusic.interfaces + + +interface MusicServiceEventListener { + fun onServiceConnected() + + fun onServiceDisconnected() + + fun onQueueChanged() + + fun onPlayingMetaChanged() + + fun onPlayStateChanged() + + fun onRepeatModeChanged() + + fun onShuffleModeChanged() + + fun onMediaStoreChanged() +} diff --git a/app/src/main/java/code/name/monkey/retromusic/interfaces/PaletteColorHolder.java b/app/src/main/java/code/name/monkey/retromusic/interfaces/PaletteColorHolder.java deleted file mode 100644 index f20757c0..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/interfaces/PaletteColorHolder.java +++ /dev/null @@ -1,12 +0,0 @@ -package code.name.monkey.retromusic.interfaces; - -import androidx.annotation.ColorInt; - -/** - * @author Aidan Follestad (afollestad) - */ -public interface PaletteColorHolder { - - @ColorInt - int getPaletteColor(); -} diff --git a/app/src/main/java/code/name/monkey/retromusic/interfaces/PaletteColorHolder.kt b/app/src/main/java/code/name/monkey/retromusic/interfaces/PaletteColorHolder.kt new file mode 100644 index 00000000..70d3778d --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/interfaces/PaletteColorHolder.kt @@ -0,0 +1,12 @@ +package code.name.monkey.retromusic.interfaces + +import androidx.annotation.ColorInt + +/** + * @author Aidan Follestad (afollestad) + */ +interface PaletteColorHolder { + + @get:ColorInt + val paletteColor: Int +} diff --git a/app/src/main/java/code/name/monkey/retromusic/loaders/AlbumLoader.java b/app/src/main/java/code/name/monkey/retromusic/loaders/AlbumLoader.java deleted file mode 100644 index 0edf4ebc..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/loaders/AlbumLoader.java +++ /dev/null @@ -1,104 +0,0 @@ -package code.name.monkey.retromusic.loaders; - -import android.content.Context; -import android.provider.MediaStore.Audio.AudioColumns; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import code.name.monkey.retromusic.model.Album; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.util.PreferenceUtil; -import io.reactivex.Observable; -import java.util.ArrayList; - -/** - * Created by hemanths on 11/08/17. - */ - -public class AlbumLoader { - - public static Observable> getAllAlbums(@NonNull Context context) { - Observable> songs = SongLoader.Companion.getSongs(SongLoader.Companion.makeSongCursor( - context, - null, - null, - getSongLoaderSortOrder(context)) - ); - - return splitIntoAlbums(songs); - } - - @NonNull - public static Observable> getAlbums(@NonNull final Context context, - String query) { - Observable> songs = SongLoader.Companion.getSongs(SongLoader.Companion.makeSongCursor( - context, - AudioColumns.ALBUM + " LIKE ?", - new String[]{"%" + query + "%"}, - getSongLoaderSortOrder(context)) - ); - return splitIntoAlbums(songs); - } - - @NonNull - public static Observable getAlbum(@NonNull final Context context, int albumId) { - return Observable.create(e -> { - Observable> songs = SongLoader.Companion.getSongs(SongLoader.Companion - .makeSongCursor(context, AudioColumns.ALBUM_ID + "=?", - new String[]{String.valueOf(albumId)}, getSongLoaderSortOrder(context))); - songs.subscribe(songs1 -> { - e.onNext(new Album(songs1)); - e.onComplete(); - }); - }); - } - - @NonNull - public static Observable> splitIntoAlbums( - @Nullable final Observable> songs) { - return Observable.create(e -> { - ArrayList albums = new ArrayList<>(); - if (songs != null) { - songs.subscribe(songs1 -> { - for (Song song : songs1) { - getOrCreateAlbum(albums, song.albumId).subscribe(album -> album.songs.add(song)); - } - }); - } - e.onNext(albums); - e.onComplete(); - }); - } - - @NonNull - public static ArrayList splitIntoAlbums(@Nullable final ArrayList songs) { - ArrayList albums = new ArrayList<>(); - if (songs != null) { - for (Song song : songs) { - getOrCreateAlbum(albums, song.albumId).subscribe(album -> album.songs.add(song)); - } - } - return albums; - } - - private static Observable getOrCreateAlbum(ArrayList albums, int albumId) { - return Observable.create(e -> { - for (Album album : albums) { - if (!album.songs.isEmpty() && album.songs.get(0).albumId == albumId) { - e.onNext(album); - e.onComplete(); - return; - } - } - Album album = new Album(); - albums.add(album); - e.onNext(album); - e.onComplete(); - }); - } - - public static String getSongLoaderSortOrder(Context context) { - return PreferenceUtil.getInstance().getAlbumSortOrder() + ", " + - //PreferenceUtil.getInstance().getAlbumSongSortOrder() + "," + - PreferenceUtil.getInstance().getAlbumDetailSongSortOrder(); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/loaders/AlbumLoader.kt b/app/src/main/java/code/name/monkey/retromusic/loaders/AlbumLoader.kt new file mode 100644 index 00000000..b0dc7da4 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/loaders/AlbumLoader.kt @@ -0,0 +1,95 @@ +package code.name.monkey.retromusic.loaders + +import android.content.Context +import android.provider.MediaStore.Audio.AudioColumns +import code.name.monkey.retromusic.model.Album +import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.util.PreferenceUtil +import io.reactivex.Observable +import java.util.* + +/** + * Created by hemanths on 11/08/17. + */ + +open class AlbumLoader { + companion object { + fun getAllAlbums(context: Context): Observable> { + val songs = SongLoader.getSongs(SongLoader.makeSongCursor( + context, null, null, + getSongLoaderSortOrder()) + ) + + return splitIntoAlbums(songs) + } + + fun getAlbums(context: Context, + query: String): Observable> { + val songs = SongLoader.getSongs(SongLoader.makeSongCursor( + context, + AudioColumns.ALBUM + " LIKE ?", + arrayOf("%$query%"), + getSongLoaderSortOrder()) + ) + return splitIntoAlbums(songs) + } + + fun getAlbum(context: Context, albumId: Int): Observable { + return Observable.create { e -> + val songs = SongLoader.getSongs(SongLoader + .makeSongCursor(context, AudioColumns.ALBUM_ID + "=?", + arrayOf(albumId.toString()), getSongLoaderSortOrder())) + songs.subscribe { songs1 -> + e.onNext(Album(songs1)) + e.onComplete() + } + } + } + + fun splitIntoAlbums( + songs: Observable>?): Observable> { + return Observable.create { e -> + val albums = ArrayList() + songs?.subscribe { songs1 -> + for (song in songs1) { + getOrCreateAlbum(albums, song.albumId).subscribe { album -> album.songs!!.add(song) } + } + } + e.onNext(albums) + e.onComplete() + } + } + + fun splitIntoAlbums(songs: ArrayList?): ArrayList { + val albums = ArrayList() + if (songs != null) { + for (song in songs) { + getOrCreateAlbum(albums, song.albumId).subscribe { album -> album.songs!!.add(song) } + } + } + return albums + } + + private fun getOrCreateAlbum(albums: ArrayList, albumId: Int): Observable { + return Observable.create { e -> + for (album in albums) { + if (!album.songs!!.isEmpty() && album.songs[0].albumId == albumId) { + e.onNext(album) + e.onComplete() + return@create + } + } + val album = Album() + albums.add(album) + e.onNext(album) + e.onComplete() + } + } + + fun getSongLoaderSortOrder(): String { + return PreferenceUtil.getInstance().albumSortOrder + ", " + + //PreferenceUtil.getInstance().getAlbumSongSortOrder() + "," + + PreferenceUtil.getInstance().albumDetailSongSortOrder + } + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/loaders/ArtistLoader.java b/app/src/main/java/code/name/monkey/retromusic/loaders/ArtistLoader.java deleted file mode 100644 index 88e8fa57..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/loaders/ArtistLoader.java +++ /dev/null @@ -1,101 +0,0 @@ -package code.name.monkey.retromusic.loaders; - -import android.content.Context; -import android.provider.MediaStore.Audio.AudioColumns; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import java.util.ArrayList; - -import code.name.monkey.retromusic.model.Album; -import code.name.monkey.retromusic.model.Artist; -import code.name.monkey.retromusic.util.PreferenceUtil; -import io.reactivex.Observable; - -public class ArtistLoader { - private static String getSongLoaderSortOrder(Context context) { - return PreferenceUtil.getInstance().getArtistSortOrder() + ", " + - PreferenceUtil.getInstance().getArtistAlbumSortOrder() + ", " + - PreferenceUtil.getInstance().getAlbumDetailSongSortOrder() + ", " + - PreferenceUtil.getInstance().getArtistDetailSongSortOrder(); - } - - @NonNull - public static Observable getArtist(@NonNull final Context context, int artistId) { - return Observable.create(e -> SongLoader.Companion.getSongs(SongLoader.Companion.makeSongCursor( - context, - AudioColumns.ARTIST_ID + "=?", - new String[]{String.valueOf(artistId)}, - getSongLoaderSortOrder(context))) - .subscribe(songs -> { - Artist artist = new Artist(AlbumLoader.splitIntoAlbums(songs)); - e.onNext(artist); - e.onComplete(); - })); - } - - @NonNull - public static Observable> getAllArtists(@NonNull final Context context) { - return Observable.create(e -> SongLoader.Companion - .getSongs(SongLoader.Companion.makeSongCursor( - context, - null, - null, - getSongLoaderSortOrder(context)) - ).subscribe(songs -> { - e.onNext(splitIntoArtists(AlbumLoader.splitIntoAlbums(songs))); - e.onComplete(); - })); - - } - - @NonNull - public static Observable> getArtists(@NonNull final Context context, String query) { - return Observable.create(e -> SongLoader.Companion.getSongs(SongLoader.Companion.makeSongCursor( - context, - AudioColumns.ARTIST + " LIKE ?", - new String[]{"%" + query + "%"}, - getSongLoaderSortOrder(context)) - ).subscribe(songs -> { - e.onNext(splitIntoArtists(AlbumLoader.splitIntoAlbums(songs))); - e.onComplete(); - })); - } - - @NonNull - public static ArrayList splitIntoArtists(@Nullable final ArrayList albums) { - ArrayList artists = new ArrayList<>(); - if (albums != null) { - for (Album album : albums) { - getOrCreateArtist(artists, album.getArtistId()).albums.add(album); - } - } - return artists; - } - - private static Artist getOrCreateArtist(ArrayList artists, int artistId) { - for (Artist artist : artists) { - if (!artist.albums.isEmpty() && !artist.albums.get(0).songs.isEmpty() && artist.albums.get(0).songs.get(0).artistId == artistId) { - return artist; - } - } - Artist album = new Artist(); - artists.add(album); - return album; - } - - public static Observable> splitIntoArtists(Observable> albums) { - return Observable.create(e -> { - ArrayList artists = new ArrayList<>(); - albums.subscribe(localAlbums -> { - if (localAlbums != null) { - for (Album album : localAlbums) { - getOrCreateArtist(artists, album.getArtistId()).albums.add(album); - } - } - e.onNext(artists); - e.onComplete(); - }); - }); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/loaders/ArtistLoader.kt b/app/src/main/java/code/name/monkey/retromusic/loaders/ArtistLoader.kt new file mode 100644 index 00000000..96f778fe --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/loaders/ArtistLoader.kt @@ -0,0 +1,99 @@ +package code.name.monkey.retromusic.loaders + +import android.content.Context +import android.provider.MediaStore.Audio.AudioColumns + +import java.util.ArrayList + +import code.name.monkey.retromusic.model.Album +import code.name.monkey.retromusic.model.Artist +import code.name.monkey.retromusic.util.PreferenceUtil +import io.reactivex.Observable + +object ArtistLoader { + private fun getSongLoaderSortOrder(): String { + return PreferenceUtil.getInstance().artistSortOrder + ", " + + PreferenceUtil.getInstance().artistAlbumSortOrder + ", " + + PreferenceUtil.getInstance().albumDetailSongSortOrder + ", " + + PreferenceUtil.getInstance().artistDetailSongSortOrder + } + + fun getArtist(context: Context, artistId: Int): Observable { + return Observable.create { e -> + SongLoader.getSongs(SongLoader.makeSongCursor( + context, + AudioColumns.ARTIST_ID + "=?", + arrayOf(artistId.toString()), + getSongLoaderSortOrder())) + .subscribe { songs -> + val artist = Artist(AlbumLoader.splitIntoAlbums(songs)) + e.onNext(artist) + e.onComplete() + } + } + } + + fun getAllArtists(context: Context): Observable> { + return Observable.create { e -> + SongLoader + .getSongs(SongLoader.makeSongCursor( + context, null, null, + getSongLoaderSortOrder()) + ).subscribe { songs -> + e.onNext(splitIntoArtists(AlbumLoader.splitIntoAlbums(songs))) + e.onComplete() + } + } + + } + + fun getArtists(context: Context, query: String): Observable> { + return Observable.create { e -> + SongLoader.getSongs(SongLoader.makeSongCursor( + context, + AudioColumns.ARTIST + " LIKE ?", + arrayOf("%$query%"), + getSongLoaderSortOrder()) + ).subscribe { songs -> + e.onNext(splitIntoArtists(AlbumLoader.splitIntoAlbums(songs))) + e.onComplete() + } + } + } + + fun splitIntoArtists(albums: ArrayList?): ArrayList { + val artists = ArrayList() + if (albums != null) { + for (album in albums) { + getOrCreateArtist(artists, album.artistId).albums!!.add(album) + } + } + return artists + } + + private fun getOrCreateArtist(artists: ArrayList, artistId: Int): Artist { + for (artist in artists) { + if (!artist.albums!!.isEmpty() && !artist.albums[0].songs!!.isEmpty() && artist.albums[0].songs!![0].artistId == artistId) { + return artist + } + } + val album = Artist() + artists.add(album) + return album + } + + fun splitIntoArtists(albums: Observable>): Observable> { + return Observable.create { e -> + val artists = ArrayList() + albums.subscribe { localAlbums -> + if (localAlbums != null) { + for (album in localAlbums) { + getOrCreateArtist(artists, album.artistId).albums!!.add(album) + } + } + e.onNext(artists) + e.onComplete() + } + } + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/loaders/ArtistSongLoader.java b/app/src/main/java/code/name/monkey/retromusic/loaders/ArtistSongLoader.java deleted file mode 100644 index 99be9459..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/loaders/ArtistSongLoader.java +++ /dev/null @@ -1,37 +0,0 @@ -package code.name.monkey.retromusic.loaders; - -import android.content.Context; -import android.database.Cursor; -import android.provider.MediaStore; -import androidx.annotation.NonNull; - -import code.name.monkey.retromusic.model.Song; - -import java.util.ArrayList; - -import code.name.monkey.retromusic.util.PreferenceUtil; -import io.reactivex.Observable; - - -public class ArtistSongLoader extends SongLoader { - - @NonNull - public static Observable> getArtistSongList(@NonNull final Context context, final int artistId) { - return Companion.getSongs(makeArtistSongCursor(context, artistId)); - } - - public static Cursor makeArtistSongCursor(@NonNull final Context context, final int artistId) { - try { - return Companion.makeSongCursor( - context, - MediaStore.Audio.AudioColumns.ARTIST_ID + "=?", - new String[]{ - String.valueOf(artistId) - }, - PreferenceUtil.getInstance().getArtistSongSortOrder() - ); - } catch (SecurityException e) { - return null; - } - } -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/loaders/GenreLoader.java b/app/src/main/java/code/name/monkey/retromusic/loaders/GenreLoader.java deleted file mode 100644 index 34f9b059..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/loaders/GenreLoader.java +++ /dev/null @@ -1,133 +0,0 @@ -package code.name.monkey.retromusic.loaders; - -import android.content.Context; -import android.database.Cursor; -import android.net.Uri; -import android.provider.BaseColumns; -import android.provider.MediaStore.Audio.Genres; - -import java.util.ArrayList; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import code.name.monkey.retromusic.model.Genre; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.util.PreferenceUtil; -import io.reactivex.Observable; - -public class GenreLoader { - - @NonNull - public static Observable> getAllGenres(@NonNull final Context context) { - return getGenresFromCursor(context, makeGenreCursor(context)); - } - - @NonNull - public static Observable> getSongs(@NonNull final Context context, final int genreId) { - // The genres table only stores songs that have a genre specified, - // so we need to get songs without a genre a different way. - if (genreId == -1) { - return getSongsWithNoGenre(context); - } - - return SongLoader.Companion.getSongs(makeGenreSongCursor(context, genreId)); - } - - @NonNull - private static Genre getGenreFromCursor(@NonNull Context context, @NonNull final Cursor cursor) { - final int id = cursor.getInt(0); - final String name = cursor.getString(1); - final int songCount = getSongs(context, id).blockingFirst().size(); - return new Genre(id, name, songCount); - - } - - @NonNull - private static Observable> getSongsWithNoGenre(@NonNull final Context context) { - String selection = BaseColumns._ID + " NOT IN " + - "(SELECT " + Genres.Members.AUDIO_ID + " FROM audio_genres_map)"; - return SongLoader.Companion.getSongs(SongLoader.Companion.makeSongCursor(context, selection, null)); - } - - private static boolean hasSongsWithNoGenre(@NonNull final Context context) { - final Cursor allSongsCursor = SongLoader.Companion.makeSongCursor(context, null, null); - final Cursor allSongsWithGenreCursor = makeAllSongsWithGenreCursor(context); - - if (allSongsCursor == null || allSongsWithGenreCursor == null) { - return false; - } - - final boolean hasSongsWithNoGenre = allSongsCursor.getCount() > allSongsWithGenreCursor.getCount(); - allSongsCursor.close(); - allSongsWithGenreCursor.close(); - return hasSongsWithNoGenre; - } - - @Nullable - private static Cursor makeAllSongsWithGenreCursor(@NonNull final Context context) { - try { - return context.getContentResolver().query( - Uri.parse("content://media/external/audio/genres/all/members"), - new String[]{Genres.Members.AUDIO_ID}, null, null, null); - } catch (SecurityException e) { - return null; - } - } - - @Nullable - private static Cursor makeGenreSongCursor(@NonNull final Context context, int genreId) { - try { - return context.getContentResolver().query( - Genres.Members.getContentUri("external", genreId), - SongLoader.Companion.getBASE_PROJECTION(), SongLoader.BASE_SELECTION, null, PreferenceUtil.getInstance().getSongSortOrder()); - } catch (SecurityException e) { - return null; - } - } - - @NonNull - private static Observable> getGenresFromCursor(@NonNull final Context context, @Nullable final Cursor cursor) { - return Observable.create(e -> { - final ArrayList genres = new ArrayList<>(); - if (cursor != null) { - if (cursor.moveToFirst()) { - do { - Genre genre = getGenreFromCursor(context, cursor); - if (genre.songCount > 0) { - genres.add(genre); - } else { - // try to remove the empty genre from the media store - try { - context.getContentResolver().delete(Genres.EXTERNAL_CONTENT_URI, Genres._ID + " == " + genre.id, null); - } catch (Exception ex) { - ex.printStackTrace(); - // nothing we can do then - } - } - } while (cursor.moveToNext()); - } - cursor.close(); - } - e.onNext(genres); - e.onComplete(); - }); - } - - - @Nullable - private static Cursor makeGenreCursor(@NonNull final Context context) { - final String[] projection = new String[]{ - Genres._ID, - Genres.NAME - }; - - try { - return context.getContentResolver().query( - Genres.EXTERNAL_CONTENT_URI, - projection, null, null, PreferenceUtil.getInstance().getGenreSortOrder()); - } catch (SecurityException e) { - return null; - } - } - -} diff --git a/app/src/main/java/code/name/monkey/retromusic/loaders/GenreLoader.kt b/app/src/main/java/code/name/monkey/retromusic/loaders/GenreLoader.kt new file mode 100644 index 00000000..96a9ad51 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/loaders/GenreLoader.kt @@ -0,0 +1,123 @@ +package code.name.monkey.retromusic.loaders + +import android.content.Context +import android.database.Cursor +import android.net.Uri +import android.provider.BaseColumns +import android.provider.MediaStore.Audio.Genres +import code.name.monkey.retromusic.Constants.BASE_PROJECTION +import code.name.monkey.retromusic.Constants.BASE_SELECTION +import code.name.monkey.retromusic.model.Genre +import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.util.PreferenceUtil +import io.reactivex.Observable +import java.util.* + +object GenreLoader { + + fun getAllGenres(context: Context): Observable> { + return getGenresFromCursor(context, makeGenreCursor(context)) + } + + fun getSongs(context: Context, genreId: Int): Observable> { + // The genres table only stores songs that have a genre specified, + // so we need to get songs without a genre a different way. + return if (genreId == -1) { + getSongsWithNoGenre(context) + } else SongLoader.getSongs(makeGenreSongCursor(context, genreId)) + + } + + private fun getGenreFromCursor(context: Context, cursor: Cursor): Genre { + val id = cursor.getInt(0) + val name = cursor.getString(1) + val songCount = getSongs(context, id).blockingFirst().size + return Genre(id, name, songCount) + + } + + private fun getSongsWithNoGenre(context: Context): Observable> { + val selection = BaseColumns._ID + " NOT IN " + + "(SELECT " + Genres.Members.AUDIO_ID + " FROM audio_genres_map)" + return SongLoader.getSongs(SongLoader.makeSongCursor(context, selection, null)) + } + + private fun hasSongsWithNoGenre(context: Context): Boolean { + val allSongsCursor = SongLoader.makeSongCursor(context, null, null) + val allSongsWithGenreCursor = makeAllSongsWithGenreCursor(context) + + if (allSongsCursor == null || allSongsWithGenreCursor == null) { + return false + } + + val hasSongsWithNoGenre = allSongsCursor.count > allSongsWithGenreCursor.count + allSongsCursor.close() + allSongsWithGenreCursor.close() + return hasSongsWithNoGenre + } + + private fun makeAllSongsWithGenreCursor(context: Context): Cursor? { + try { + return context.contentResolver.query( + Uri.parse("content://media/external/audio/genres/all/members"), + arrayOf(Genres.Members.AUDIO_ID), null, null, null) + } catch (e: SecurityException) { + return null + } + + } + + private fun makeGenreSongCursor(context: Context, genreId: Int): Cursor? { + try { + return context.contentResolver.query( + Genres.Members.getContentUri("external", genreId.toLong()), + BASE_PROJECTION, BASE_SELECTION, null, PreferenceUtil.getInstance().songSortOrder) + } catch (e: SecurityException) { + return null + } + + } + + private fun getGenresFromCursor(context: Context, cursor: Cursor?): Observable> { + return Observable.create { e -> + val genres = ArrayList() + if (cursor != null) { + if (cursor.moveToFirst()) { + do { + val genre = getGenreFromCursor(context, cursor) + if (genre.songCount > 0) { + genres.add(genre) + } else { + // try to remove the empty genre from the media store + try { + context.contentResolver.delete(Genres.EXTERNAL_CONTENT_URI, Genres._ID + " == " + genre.id, null) + } catch (ex: Exception) { + ex.printStackTrace() + // nothing we can do then + } + + } + } while (cursor.moveToNext()) + } + cursor.close() + } + e.onNext(genres) + e.onComplete() + } + } + + + private fun makeGenreCursor(context: Context): Cursor? { + val projection = arrayOf(Genres._ID, Genres.NAME) + + try { + return context.contentResolver.query( + Genres.EXTERNAL_CONTENT_URI, + projection, null, null, PreferenceUtil.getInstance().genreSortOrder) + } catch (e: SecurityException) { + return null + } + + } + +} diff --git a/app/src/main/java/code/name/monkey/retromusic/loaders/GenreSongsLoader.java b/app/src/main/java/code/name/monkey/retromusic/loaders/GenreSongsLoader.java deleted file mode 100644 index bf53c13f..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/loaders/GenreSongsLoader.java +++ /dev/null @@ -1,76 +0,0 @@ -package code.name.monkey.retromusic.loaders; - -import android.content.Context; -import android.database.Cursor; -import android.provider.MediaStore; -import android.provider.MediaStore.Audio.AudioColumns; - -import java.util.ArrayList; -import java.util.List; - -import androidx.annotation.NonNull; -import code.name.monkey.retromusic.model.Song; -import io.reactivex.Observable; - -/** - * @author Hemanth S (h4h13). - */ - -public class GenreSongsLoader { - - public static Observable> getGenreSongsList(@NonNull Context context, int genreId) { - return Observable.create(e -> { - ArrayList list = new ArrayList<>(); - Cursor cursor = makeGenreSongCursor(context, genreId); - if (cursor != null && cursor.moveToFirst()) { - do { - list.add(getGenreSongFromCursorImpl(cursor)); - } while (cursor.moveToNext()); - } - if (cursor != null) { - cursor.close(); - } - e.onNext((ArrayList) (List) list); - e.onComplete(); - }); - } - - @NonNull - private static Song getGenreSongFromCursorImpl(@NonNull Cursor cursor) { - final int id = cursor.getInt(0); - final String title = cursor.getString(1); - final int trackNumber = cursor.getInt(2); - final int year = cursor.getInt(3); - final long duration = cursor.getLong(4); - final String data = cursor.getString(5); - final int dateModified = cursor.getInt(6); - final int albumId = cursor.getInt(7); - final String albumName = cursor.getString(8); - final int artistId = cursor.getInt(9); - final String artistName = cursor.getString(10); - return new Song(id, title, trackNumber, year, duration, data, dateModified, albumId, albumName, artistId, artistName); - } - - private static Cursor makeGenreSongCursor(Context context, long genreId) { - try { - return context.getContentResolver().query( - MediaStore.Audio.Genres.Members.getContentUri("external", genreId), - new String[]{ - MediaStore.Audio.Playlists.Members.AUDIO_ID,// 0 - AudioColumns.TITLE,// 1 - AudioColumns.TRACK,// 2 - AudioColumns.YEAR,// 3 - AudioColumns.DURATION,// 4 - AudioColumns.DATA,// 5 - AudioColumns.DATE_MODIFIED,// 6 - AudioColumns.ALBUM_ID,// 7 - AudioColumns.ALBUM,// 8 - AudioColumns.ARTIST_ID,// 9 - AudioColumns.ARTIST,// 10 - }, SongLoader.BASE_SELECTION, null, - MediaStore.Audio.Genres.Members.DEFAULT_SORT_ORDER); - } catch (SecurityException e) { - return null; - } - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/loaders/HomeLoader.java b/app/src/main/java/code/name/monkey/retromusic/loaders/HomeLoader.java deleted file mode 100644 index cc181e79..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/loaders/HomeLoader.java +++ /dev/null @@ -1,63 +0,0 @@ -package code.name.monkey.retromusic.loaders; - - -import android.content.Context; -import androidx.annotation.NonNull; - -import java.util.ArrayList; - -import code.name.monkey.retromusic.model.Playlist; -import code.name.monkey.retromusic.model.smartplaylist.AbsSmartPlaylist; -import code.name.monkey.retromusic.model.smartplaylist.HistoryPlaylist; -import code.name.monkey.retromusic.model.smartplaylist.LastAddedPlaylist; -import code.name.monkey.retromusic.model.smartplaylist.MyTopTracksPlaylist; -import io.reactivex.Observable; - -public class HomeLoader { - - public static Observable> getRecentAndTopThings( - @NonNull Context context) { - ArrayList objects = new ArrayList<>(); - return Observable.create(e -> { - - new HistoryPlaylist(context).getSongs(context).subscribe(songs -> { - if (!songs.isEmpty()) { - objects.add(new HistoryPlaylist(context)); - } - }); - new LastAddedPlaylist(context).getSongs(context).subscribe(songs -> { - if (!songs.isEmpty()) { - objects.add(new LastAddedPlaylist(context)); - } - }); - new MyTopTracksPlaylist(context).getSongs(context).subscribe(songs -> { - if (!songs.isEmpty()) { - objects.add(new MyTopTracksPlaylist(context)); - } - }); - - e.onNext(objects); - e.onComplete(); - }); - } - - public static Observable> getHomeLoader(@NonNull Context context) { - ArrayList playlists = new ArrayList<>(); - PlaylistLoader.getAllPlaylists(context) - .subscribe(playlists1 -> { - if (playlists1.size() > 0) { - for (Playlist playlist : playlists1) { - PlaylistSongsLoader.getPlaylistSongList(context, playlist) - .subscribe(songs -> { - if (songs.size() > 0) { - playlists.add(playlist); - } - }); - } - } - }); - return Observable.just(playlists); - } - - -} diff --git a/app/src/main/java/code/name/monkey/retromusic/loaders/HomeLoader.kt b/app/src/main/java/code/name/monkey/retromusic/loaders/HomeLoader.kt new file mode 100644 index 00000000..273d1a81 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/loaders/HomeLoader.kt @@ -0,0 +1,61 @@ +package code.name.monkey.retromusic.loaders + + +import android.content.Context + +import java.util.ArrayList +import code.name.monkey.retromusic.model.Playlist +import code.name.monkey.retromusic.model.smartplaylist.AbsSmartPlaylist +import code.name.monkey.retromusic.model.smartplaylist.HistoryPlaylist +import code.name.monkey.retromusic.model.smartplaylist.LastAddedPlaylist +import code.name.monkey.retromusic.model.smartplaylist.MyTopTracksPlaylist +import io.reactivex.Observable + +object HomeLoader { + + fun getRecentAndTopThings( + context: Context): Observable> { + val objects = ArrayList() + return Observable.create { e -> + + HistoryPlaylist(context).getSongs(context).subscribe { songs -> + if (!songs.isEmpty()) { + objects.add(HistoryPlaylist(context)) + } + } + LastAddedPlaylist(context).getSongs(context).subscribe { songs -> + if (!songs.isEmpty()) { + objects.add(LastAddedPlaylist(context)) + } + } + MyTopTracksPlaylist(context).getSongs(context).subscribe { songs -> + if (!songs.isEmpty()) { + objects.add(MyTopTracksPlaylist(context)) + } + } + + e.onNext(objects) + e.onComplete() + } + } + + fun getHomeLoader(context: Context): Observable> { + val playlists = ArrayList() + PlaylistLoader.getAllPlaylists(context) + .subscribe { playlists1 -> + if (playlists1.size > 0) { + for (playlist in playlists1) { + PlaylistSongsLoader.getPlaylistSongList(context, playlist) + .subscribe { songs -> + if (songs.size > 0) { + playlists.add(playlist) + } + } + } + } + } + return Observable.just(playlists) + } + + +} diff --git a/app/src/main/java/code/name/monkey/retromusic/loaders/LastAddedSongsLoader.java b/app/src/main/java/code/name/monkey/retromusic/loaders/LastAddedSongsLoader.java deleted file mode 100644 index 2de6083d..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/loaders/LastAddedSongsLoader.java +++ /dev/null @@ -1,46 +0,0 @@ -package code.name.monkey.retromusic.loaders; - -import android.content.Context; -import android.database.Cursor; -import android.provider.MediaStore; - -import java.util.ArrayList; - -import code.name.monkey.retromusic.model.Album; -import code.name.monkey.retromusic.model.Artist; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.util.PreferenceUtil; -import io.reactivex.Observable; -import io.reactivex.annotations.NonNull; - -/** - * Created by hemanths on 16/08/17. - */ - -public class LastAddedSongsLoader { - - @NonNull - public static Observable> getLastAddedSongs(@NonNull Context context) { - return SongLoader.Companion.getSongs(makeLastAddedCursor(context)); - } - - public static Cursor makeLastAddedCursor(@NonNull final Context context) { - long cutoff = PreferenceUtil.getInstance().getLastAddedCutoff(); - - return SongLoader.Companion.makeSongCursor( - context, - MediaStore.Audio.Media.DATE_ADDED + ">?", - new String[]{String.valueOf(cutoff)}, - MediaStore.Audio.Media.DATE_ADDED + " DESC"); - } - - @NonNull - public static Observable> getLastAddedAlbums(@NonNull Context context) { - return AlbumLoader.splitIntoAlbums(getLastAddedSongs(context)); - } - - @NonNull - public static Observable> getLastAddedArtists(@NonNull Context context) { - return ArtistLoader.splitIntoArtists(getLastAddedAlbums(context)); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/loaders/LastAddedSongsLoader.kt b/app/src/main/java/code/name/monkey/retromusic/loaders/LastAddedSongsLoader.kt new file mode 100644 index 00000000..3aa2ab06 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/loaders/LastAddedSongsLoader.kt @@ -0,0 +1,46 @@ +package code.name.monkey.retromusic.loaders + +import android.content.Context +import android.database.Cursor +import android.provider.MediaStore + +import java.util.ArrayList + +import code.name.monkey.retromusic.model.Album +import code.name.monkey.retromusic.model.Artist +import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.util.PreferenceUtil +import io.reactivex.Observable +import io.reactivex.annotations.NonNull + +/** + * Created by hemanths on 16/08/17. + */ + +object LastAddedSongsLoader { + + @NonNull + fun getLastAddedSongs(@NonNull context: Context): Observable> { + return SongLoader.getSongs(makeLastAddedCursor(context)) + } + + fun makeLastAddedCursor(@NonNull context: Context): Cursor? { + val cutoff = PreferenceUtil.getInstance().lastAddedCutoff + + return SongLoader.makeSongCursor( + context, + MediaStore.Audio.Media.DATE_ADDED + ">?", + arrayOf(cutoff.toString()), + MediaStore.Audio.Media.DATE_ADDED + " DESC") + } + + @NonNull + fun getLastAddedAlbums(@NonNull context: Context): Observable> { + return AlbumLoader.splitIntoAlbums(getLastAddedSongs(context)) + } + + @NonNull + fun getLastAddedArtists(@NonNull context: Context): Observable> { + return ArtistLoader.splitIntoArtists(getLastAddedAlbums(context)) + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/loaders/PlaylistLoader.java b/app/src/main/java/code/name/monkey/retromusic/loaders/PlaylistLoader.java deleted file mode 100644 index c382f283..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/loaders/PlaylistLoader.java +++ /dev/null @@ -1,118 +0,0 @@ -package code.name.monkey.retromusic.loaders; - -import android.content.Context; -import android.database.Cursor; -import android.net.Uri; -import android.provider.BaseColumns; -import android.provider.MediaStore; -import android.provider.MediaStore.Audio.PlaylistsColumns; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import code.name.monkey.retromusic.model.Playlist; - -import java.util.ArrayList; - -import io.reactivex.Observable; - -/** - * Created by hemanths on 16/08/17. - */ - -public class PlaylistLoader { - @Nullable - public static Cursor makePlaylistCursor(@NonNull final Context context, final String selection, final String[] values) { - try { - return context.getContentResolver().query(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, - new String[]{ - /* 0 */ - BaseColumns._ID, - /* 1 */ - PlaylistsColumns.NAME - }, selection, values, MediaStore.Audio.Playlists.DEFAULT_SORT_ORDER); - } catch (SecurityException e) { - return null; - } - } - - @NonNull - public static Observable getPlaylist(@Nullable final Cursor cursor) { - return Observable.create(e -> { - Playlist playlist = new Playlist(); - - if (cursor != null && cursor.moveToFirst()) { - playlist = getPlaylistFromCursorImpl(cursor); - } - if (cursor != null) - cursor.close(); - - e.onNext(playlist); - e.onComplete(); - }); - - - } - - @NonNull - public static Observable getPlaylist(@NonNull final Context context, final String playlistName) { - return getPlaylist(makePlaylistCursor( - context, - PlaylistsColumns.NAME + "=?", - new String[]{ - playlistName - } - )); - } - - @NonNull - public static Observable getPlaylist(@NonNull final Context context, final int playlistId) { - return getPlaylist(makePlaylistCursor( - context, - BaseColumns._ID + "=?", - new String[]{ - String.valueOf(playlistId) - } - )); - } - - @NonNull - private static Playlist getPlaylistFromCursorImpl(@NonNull final Cursor cursor) { - - final int id = cursor.getInt(0); - final String name = cursor.getString(1); - return new Playlist(id, name); - } - - - @NonNull - public static Observable> getAllPlaylists(@Nullable final Cursor cursor) { - return Observable.create(e -> { - ArrayList playlists = new ArrayList<>(); - - if (cursor != null && cursor.moveToFirst()) { - do { - playlists.add(getPlaylistFromCursorImpl(cursor)); - } while (cursor.moveToNext()); - } - if (cursor != null) - cursor.close(); - - e.onNext(playlists); - e.onComplete(); - }); - } - - @NonNull - public static Observable> getAllPlaylists(@NonNull final Context context) { - return getAllPlaylists(makePlaylistCursor(context, null, null)); - } - - public static void deletePlaylists(Context context, long playlistId) { - Uri localUri = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI; - StringBuilder localStringBuilder = new StringBuilder(); - localStringBuilder.append("_id IN ("); - localStringBuilder.append((playlistId)); - localStringBuilder.append(")"); - context.getContentResolver().delete(localUri, localStringBuilder.toString(), null); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/loaders/PlaylistLoader.kt b/app/src/main/java/code/name/monkey/retromusic/loaders/PlaylistLoader.kt new file mode 100644 index 00000000..4602708f --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/loaders/PlaylistLoader.kt @@ -0,0 +1,103 @@ +package code.name.monkey.retromusic.loaders + +import android.content.Context +import android.database.Cursor +import android.net.Uri +import android.provider.BaseColumns +import android.provider.MediaStore +import android.provider.MediaStore.Audio.PlaylistsColumns + +import code.name.monkey.retromusic.model.Playlist + +import java.util.ArrayList + +import io.reactivex.Observable + +/** + * Created by hemanths on 16/08/17. + */ + +object PlaylistLoader { + fun makePlaylistCursor(context: Context, selection: String?, values: Array?): Cursor? { + try { + return context.contentResolver.query(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, + arrayOf( + /* 0 */ + BaseColumns._ID, + /* 1 */ + PlaylistsColumns.NAME), selection, values, MediaStore.Audio.Playlists.DEFAULT_SORT_ORDER) + } catch (e: SecurityException) { + return null + } + + } + + fun getPlaylist(cursor: Cursor?): Observable { + return Observable.create { e -> + var playlist = Playlist() + + if (cursor != null && cursor.moveToFirst()) { + playlist = getPlaylistFromCursorImpl(cursor) + } + cursor?.close() + + e.onNext(playlist) + e.onComplete() + } + + + } + + fun getPlaylist(context: Context, playlistName: String): Observable { + return getPlaylist(makePlaylistCursor( + context, + PlaylistsColumns.NAME + "=?", + arrayOf(playlistName) + )) + } + + fun getPlaylist(context: Context, playlistId: Int): Observable { + return getPlaylist(makePlaylistCursor( + context, + BaseColumns._ID + "=?", + arrayOf(playlistId.toString()) + )) + } + + private fun getPlaylistFromCursorImpl(cursor: Cursor): Playlist { + + val id = cursor.getInt(0) + val name = cursor.getString(1) + return Playlist(id, name) + } + + + fun getAllPlaylists(cursor: Cursor?): Observable> { + return Observable.create { e -> + val playlists = ArrayList() + + if (cursor != null && cursor.moveToFirst()) { + do { + playlists.add(getPlaylistFromCursorImpl(cursor)) + } while (cursor.moveToNext()) + } + cursor?.close() + + e.onNext(playlists) + e.onComplete() + } + } + + fun getAllPlaylists(context: Context): Observable> { + return getAllPlaylists(makePlaylistCursor(context, null, null)) + } + + fun deletePlaylists(context: Context, playlistId: Long) { + val localUri = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI + val localStringBuilder = StringBuilder() + localStringBuilder.append("_id IN (") + localStringBuilder.append(playlistId) + localStringBuilder.append(")") + context.contentResolver.delete(localUri, localStringBuilder.toString(), null) + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/loaders/PlaylistSongsLoader.java b/app/src/main/java/code/name/monkey/retromusic/loaders/PlaylistSongsLoader.java deleted file mode 100644 index 2e434778..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/loaders/PlaylistSongsLoader.java +++ /dev/null @@ -1,94 +0,0 @@ -package code.name.monkey.retromusic.loaders; - -import android.content.Context; -import android.database.Cursor; -import android.provider.MediaStore; -import android.provider.MediaStore.Audio.AudioColumns; - -import java.util.ArrayList; -import java.util.List; - -import code.name.monkey.retromusic.model.AbsCustomPlaylist; -import code.name.monkey.retromusic.model.Playlist; -import code.name.monkey.retromusic.model.PlaylistSong; -import code.name.monkey.retromusic.model.Song; -import io.reactivex.Observable; -import io.reactivex.annotations.NonNull; - -/** - * Created by hemanths on 16/08/17. - */ - -public class PlaylistSongsLoader { - - @NonNull - public static Observable> getPlaylistSongList(@NonNull Context context, Playlist playlist) { - if (playlist instanceof AbsCustomPlaylist) { - return ((AbsCustomPlaylist) playlist).getSongs(context); - } else { - //noinspection unchecked - return getPlaylistSongList(context, playlist.id); - } - } - - @NonNull - public static Observable> getPlaylistSongList(@NonNull Context context, final int playlistId) { - return Observable.create(e -> { - ArrayList songs = new ArrayList<>(); - Cursor cursor = makePlaylistSongCursor(context, playlistId); - - if (cursor != null && cursor.moveToFirst()) { - do { - songs.add(getPlaylistSongFromCursorImpl(cursor, playlistId)); - } while (cursor.moveToNext()); - } - if (cursor != null) { - cursor.close(); - } - e.onNext((ArrayList) (List) songs); - e.onComplete(); - }); - } - - @NonNull - private static PlaylistSong getPlaylistSongFromCursorImpl(@NonNull Cursor cursor, int playlistId) { - final int id = cursor.getInt(0); - final String title = cursor.getString(1); - final int trackNumber = cursor.getInt(2); - final int year = cursor.getInt(3); - final long duration = cursor.getLong(4); - final String data = cursor.getString(5); - final int dateModified = cursor.getInt(6); - final int albumId = cursor.getInt(7); - final String albumName = cursor.getString(8); - final int artistId = cursor.getInt(9); - final String artistName = cursor.getString(10); - final int idInPlaylist = cursor.getInt(11); - - return new PlaylistSong(id, title, trackNumber, year, duration, data, dateModified, albumId, albumName, artistId, artistName, playlistId, idInPlaylist); - } - - private static Cursor makePlaylistSongCursor(@NonNull final Context context, final int playlistId) { - try { - return context.getContentResolver().query( - MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId), - new String[]{ - MediaStore.Audio.Playlists.Members.AUDIO_ID,// 0 - AudioColumns.TITLE,// 1 - AudioColumns.TRACK,// 2 - AudioColumns.YEAR,// 3 - AudioColumns.DURATION,// 4 - AudioColumns.DATA,// 5 - AudioColumns.DATE_MODIFIED,// 6 - AudioColumns.ALBUM_ID,// 7 - AudioColumns.ALBUM,// 8 - AudioColumns.ARTIST_ID,// 9 - AudioColumns.ARTIST,// 10 - MediaStore.Audio.Playlists.Members._ID, // 11 - }, SongLoader.BASE_SELECTION, null, - MediaStore.Audio.Playlists.Members.DEFAULT_SORT_ORDER); - } catch (SecurityException e) { - return null; - } - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/loaders/PlaylistSongsLoader.kt b/app/src/main/java/code/name/monkey/retromusic/loaders/PlaylistSongsLoader.kt new file mode 100644 index 00000000..41aadb2c --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/loaders/PlaylistSongsLoader.kt @@ -0,0 +1,86 @@ +package code.name.monkey.retromusic.loaders + +import android.content.Context +import android.database.Cursor +import android.provider.MediaStore +import android.provider.MediaStore.Audio.AudioColumns +import code.name.monkey.retromusic.Constants.BASE_SELECTION +import code.name.monkey.retromusic.model.AbsCustomPlaylist +import code.name.monkey.retromusic.model.Playlist +import code.name.monkey.retromusic.model.PlaylistSong +import code.name.monkey.retromusic.model.Song +import io.reactivex.Observable +import io.reactivex.annotations.NonNull +import java.util.* + +/** + * Created by hemanths on 16/08/17. + */ + +object PlaylistSongsLoader { + + @NonNull + fun getPlaylistSongList(@NonNull context: Context, playlist: Playlist): Observable> { + return (playlist as? AbsCustomPlaylist)?.getSongs(context) + ?: getPlaylistSongList(context, playlist.id) + } + + @NonNull + fun getPlaylistSongList(@NonNull context: Context, playlistId: Int): Observable> { + return Observable.create { e -> + val songs = ArrayList() + val cursor = makePlaylistSongCursor(context, playlistId) + + if (cursor != null && cursor.moveToFirst()) { + do { + songs.add(getPlaylistSongFromCursorImpl(cursor, playlistId)) + } while (cursor.moveToNext()) + } + cursor?.close() + e.onNext(songs) + e.onComplete() + } + } + + @NonNull + private fun getPlaylistSongFromCursorImpl(@NonNull cursor: Cursor, playlistId: Int): PlaylistSong { + val id = cursor.getInt(0) + val title = cursor.getString(1) + val trackNumber = cursor.getInt(2) + val year = cursor.getInt(3) + val duration = cursor.getLong(4) + val data = cursor.getString(5) + val dateModified = cursor.getInt(6) + val albumId = cursor.getInt(7) + val albumName = cursor.getString(8) + val artistId = cursor.getInt(9) + val artistName = cursor.getString(10) + val idInPlaylist = cursor.getInt(11) + + return PlaylistSong(id, title, trackNumber, year, duration, data, dateModified, albumId, albumName, artistId, artistName, playlistId, idInPlaylist) + } + + private fun makePlaylistSongCursor(@NonNull context: Context, playlistId: Int): Cursor? { + try { + return context.contentResolver.query( + MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId.toLong()), + arrayOf(MediaStore.Audio.Playlists.Members.AUDIO_ID, // 0 + AudioColumns.TITLE, // 1 + AudioColumns.TRACK, // 2 + AudioColumns.YEAR, // 3 + AudioColumns.DURATION, // 4 + AudioColumns.DATA, // 5 + AudioColumns.DATE_MODIFIED, // 6 + AudioColumns.ALBUM_ID, // 7 + AudioColumns.ALBUM, // 8 + AudioColumns.ARTIST_ID, // 9 + AudioColumns.ARTIST, // 10 + MediaStore.Audio.Playlists.Members._ID)// 11 + , BASE_SELECTION, null, + MediaStore.Audio.Playlists.Members.DEFAULT_SORT_ORDER) + } catch (e: SecurityException) { + return null + } + + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/loaders/SearchLoader.java b/app/src/main/java/code/name/monkey/retromusic/loaders/SearchLoader.java deleted file mode 100644 index 7117e7db..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/loaders/SearchLoader.java +++ /dev/null @@ -1,44 +0,0 @@ -package code.name.monkey.retromusic.loaders; - -import android.content.Context; -import androidx.annotation.NonNull; -import android.text.TextUtils; -import java.util.ArrayList; - -import code.name.monkey.retromusic.R; -import io.reactivex.Observable; - -public class SearchLoader { - - public static Observable> searchAll(@NonNull Context context, @NonNull String query) { - ArrayList results = new ArrayList<>(); - return Observable.create(e -> { - if (!TextUtils.isEmpty(query)) { - SongLoader.Companion.getSongs(context, query) - .subscribe(songs -> { - if (!songs.isEmpty()) { - results.add(context.getResources().getString(R.string.songs)); - results.addAll(songs); - } - }); - - ArtistLoader.getArtists(context, query) - .subscribe(artists -> { - if (!artists.isEmpty()) { - results.add(context.getResources().getString(R.string.artists)); - results.addAll(artists); - } - }); - AlbumLoader.getAlbums(context, query) - .subscribe(albums -> { - if (!albums.isEmpty()) { - results.add(context.getResources().getString(R.string.albums)); - results.addAll(albums); - } - }); - } - e.onNext(results); - e.onComplete(); - }); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/loaders/SearchLoader.kt b/app/src/main/java/code/name/monkey/retromusic/loaders/SearchLoader.kt new file mode 100644 index 00000000..adbb8599 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/loaders/SearchLoader.kt @@ -0,0 +1,43 @@ +package code.name.monkey.retromusic.loaders + +import android.content.Context +import android.text.TextUtils +import java.util.ArrayList + +import code.name.monkey.retromusic.R +import io.reactivex.Observable + +object SearchLoader { + + fun searchAll(context: Context, query: String): Observable> { + val results = ArrayList() + return Observable.create { e -> + if (!TextUtils.isEmpty(query)) { + SongLoader.getSongs(context, query) + .subscribe { songs -> + if (!songs.isEmpty()) { + results.add(context.resources.getString(R.string.songs)) + results.addAll(songs) + } + } + + ArtistLoader.getArtists(context, query) + .subscribe { artists -> + if (!artists.isEmpty()) { + results.add(context.resources.getString(R.string.artists)) + results.addAll(artists) + } + } + AlbumLoader.getAlbums(context, query) + .subscribe { albums -> + if (!albums.isEmpty()) { + results.add(context.resources.getString(R.string.albums)) + results.addAll(albums) + } + } + } + e.onNext(results) + e.onComplete() + } + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/loaders/SongLoader.kt b/app/src/main/java/code/name/monkey/retromusic/loaders/SongLoader.kt index 4691189a..c6e7f40c 100644 --- a/app/src/main/java/code/name/monkey/retromusic/loaders/SongLoader.kt +++ b/app/src/main/java/code/name/monkey/retromusic/loaders/SongLoader.kt @@ -2,9 +2,10 @@ package code.name.monkey.retromusic.loaders import android.content.Context import android.database.Cursor -import android.provider.BaseColumns import android.provider.MediaStore import android.provider.MediaStore.Audio.AudioColumns +import code.name.monkey.retromusic.Constants.BASE_PROJECTION +import code.name.monkey.retromusic.Constants.BASE_SELECTION import code.name.monkey.retromusic.helper.ShuffleHelper import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.providers.BlacklistStore @@ -16,167 +17,152 @@ import java.util.* * Created by hemanths on 10/08/17. */ -open class SongLoader { - companion object { - const val BASE_SELECTION = AudioColumns.IS_MUSIC + "=1" + " AND " + AudioColumns.TITLE + " != ''" - val BASE_PROJECTION = arrayOf(BaseColumns._ID, // 0 - AudioColumns.TITLE, // 1 - AudioColumns.TRACK, // 2 - AudioColumns.YEAR, // 3 - AudioColumns.DURATION, // 4 - AudioColumns.DATA, // 5 - AudioColumns.DATE_MODIFIED, // 6 - AudioColumns.ALBUM_ID, // 7 - AudioColumns.ALBUM, // 8 - AudioColumns.ARTIST_ID, // 9 - AudioColumns.ARTIST)// 10 +object SongLoader { - fun getAllSongs(context: Context): Observable> { - val cursor = makeSongCursor(context, null, null) - return getSongs(cursor) - } + fun getAllSongs(context: Context): Observable> { + val cursor = makeSongCursor(context, null, null) + return getSongs(cursor) + } - fun getSongs(context: Context, query: String): Observable> { - val cursor = makeSongCursor(context, AudioColumns.TITLE + " LIKE ?", arrayOf("%$query%")) - return getSongs(cursor) - } + fun getSongs(context: Context, query: String): Observable> { + val cursor = makeSongCursor(context, AudioColumns.TITLE + " LIKE ?", arrayOf("%$query%")) + return getSongs(cursor) + } - fun getSongs(cursor: Cursor?): Observable> { - return Observable.create { e -> - val songs = ArrayList() - if (cursor != null && cursor.moveToFirst()) { - do { - songs.add(getSongFromCursorImpl(cursor)) - } while (cursor.moveToNext()) - } - - cursor?.close() - e.onNext(songs) - e.onComplete() - } - } - - private fun getSongFromCursorImpl(cursor: Cursor): Song { - val id = cursor.getInt(0) - val title = cursor.getString(1) - val trackNumber = cursor.getInt(2) - val year = cursor.getInt(3) - val duration = cursor.getLong(4) - val data = cursor.getString(5) - val dateModified = cursor.getLong(6) - val albumId = cursor.getInt(7) - val albumName = cursor.getString(8) - val artistId = cursor.getInt(9) - val artistName = cursor.getString(10) - - return Song(id, title, trackNumber, year, duration, data, dateModified, albumId, albumName, - artistId, artistName) - } - - @JvmOverloads - fun makeSongCursor(context: Context, selection: String?, selectionValues: Array?, sortOrder: String = PreferenceUtil.getInstance().songSortOrder): Cursor? { - var selectionFinal = selection - var selectionValuesFinal = selectionValues - selectionFinal = if (selection != null && selection.trim { it <= ' ' } != "") { - "$BASE_SELECTION AND $selectionFinal" - } else { - BASE_SELECTION + fun getSongs(cursor: Cursor?): Observable> { + return Observable.create { e -> + val songs = ArrayList() + if (cursor != null && cursor.moveToFirst()) { + do { + songs.add(getSongFromCursorImpl(cursor)) + } while (cursor.moveToNext()) } - // Blacklist - val paths = BlacklistStore.getInstance(context).paths - if (!paths.isEmpty()) { - selectionFinal = generateBlacklistSelection(selectionFinal, paths.size) - selectionValuesFinal = addBlacklistSelectionValues(selectionValuesFinal, paths) - } - - try { - return context.contentResolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, - BASE_PROJECTION, selectionFinal, selectionValuesFinal, sortOrder) - } catch (e: SecurityException) { - return null - } - - } - - private fun generateBlacklistSelection(selection: String?, pathCount: Int): String { - val newSelection = StringBuilder( - if (selection != null && selection.trim { it <= ' ' } != "") "$selection AND " else "") - newSelection.append(AudioColumns.DATA + " NOT LIKE ?") - for (i in 0 until pathCount - 1) { - newSelection.append(" AND " + AudioColumns.DATA + " NOT LIKE ?") - } - return newSelection.toString() - } - - private fun addBlacklistSelectionValues(selectionValues: Array?, - paths: ArrayList): Array? { - var selectionValuesFinal = selectionValues - if (selectionValuesFinal == null) { - selectionValuesFinal = emptyArray() - } - val newSelectionValues = Array(selectionValuesFinal.size + paths.size) { - "n = $it" - } - System.arraycopy(selectionValuesFinal, 0, newSelectionValues, 0, selectionValuesFinal.size) - for (i in selectionValuesFinal.size until newSelectionValues.size) { - newSelectionValues[i] = paths[i - selectionValuesFinal.size] + "%" - } - return newSelectionValues - } - - fun getSong(cursor: Cursor?): Observable { - return Observable.create { e -> - val song: Song - if (cursor != null && cursor.moveToFirst()) { - song = getSongFromCursorImpl(cursor) - } else { - song = Song.EMPTY_SONG - } - cursor?.close() - e.onNext(song) - e.onComplete() - } - } - - fun getSong(context: Context, queryId: Int): Observable { - val cursor = makeSongCursor(context, AudioColumns._ID + "=?", - arrayOf(queryId.toString())) - return getSong(cursor) - } - - fun suggestSongs(context: Context): Observable> { - return SongLoader.getAllSongs(context) - .flatMap { - val list = ArrayList() - ShuffleHelper.makeShuffleList(it, -1) - if (it.size > 9) { - list.addAll(it.subList(0, 9)) - } - return@flatMap Observable.just(list) - } - /*.flatMap({ songs -> - val list = ArrayList() - ShuffleHelper.makeShuffleList(songs, -1) - if (songs.size > 9) { - list.addAll(songs.subList(0, 9)) - } - Observable.just(list) - } as Function, ObservableSource>>)*/ - /*.subscribe(songs -> { - ArrayList list = new ArrayList<>(); - if (songs.isEmpty()) { - return; - } - ShuffleHelper.makeShuffleList(songs, -1); - if (songs.size() > 10) { - list.addAll(songs.subList(0, 10)); - } else { - list.addAll(songs); - } - return; - });*/ + cursor?.close() + e.onNext(songs) + e.onComplete() } } + + private fun getSongFromCursorImpl(cursor: Cursor): Song { + val id = cursor.getInt(0) + val title = cursor.getString(1) + val trackNumber = cursor.getInt(2) + val year = cursor.getInt(3) + val duration = cursor.getLong(4) + val data = cursor.getString(5) + val dateModified = cursor.getLong(6) + val albumId = cursor.getInt(7) + val albumName = cursor.getString(8) + val artistId = cursor.getInt(9) + val artistName = cursor.getString(10) + + return Song(id, title, trackNumber, year, duration, data, dateModified, albumId, albumName, + artistId, artistName) + } + + @JvmOverloads + fun makeSongCursor(context: Context, selection: String?, selectionValues: Array?, sortOrder: String = PreferenceUtil.getInstance().songSortOrder): Cursor? { + var selectionFinal = selection + var selectionValuesFinal = selectionValues + selectionFinal = if (selection != null && selection.trim { it <= ' ' } != "") { + "$BASE_SELECTION AND $selectionFinal" + } else { + BASE_SELECTION + } + + // Blacklist + val paths = BlacklistStore.getInstance(context).paths + if (!paths.isEmpty()) { + selectionFinal = generateBlacklistSelection(selectionFinal, paths.size) + selectionValuesFinal = addBlacklistSelectionValues(selectionValuesFinal, paths) + } + + try { + return context.contentResolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, + BASE_PROJECTION, selectionFinal, selectionValuesFinal, sortOrder) + } catch (e: SecurityException) { + return null + } + + } + + private fun generateBlacklistSelection(selection: String?, pathCount: Int): String { + val newSelection = StringBuilder( + if (selection != null && selection.trim { it <= ' ' } != "") "$selection AND " else "") + newSelection.append(AudioColumns.DATA + " NOT LIKE ?") + for (i in 0 until pathCount - 1) { + newSelection.append(" AND " + AudioColumns.DATA + " NOT LIKE ?") + } + return newSelection.toString() + } + + private fun addBlacklistSelectionValues(selectionValues: Array?, + paths: ArrayList): Array? { + var selectionValuesFinal = selectionValues + if (selectionValuesFinal == null) { + selectionValuesFinal = emptyArray() + } + val newSelectionValues = Array(selectionValuesFinal.size + paths.size) { + "n = $it" + } + System.arraycopy(selectionValuesFinal, 0, newSelectionValues, 0, selectionValuesFinal.size) + for (i in selectionValuesFinal.size until newSelectionValues.size) { + newSelectionValues[i] = paths[i - selectionValuesFinal.size] + "%" + } + return newSelectionValues + } + + fun getSong(cursor: Cursor?): Observable { + return Observable.create { e -> + val song: Song = if (cursor != null && cursor.moveToFirst()) { + getSongFromCursorImpl(cursor) + } else { + Song.EMPTY_SONG + } + cursor?.close() + e.onNext(song) + e.onComplete() + } + } + + fun getSong(context: Context, queryId: Int): Observable { + val cursor = makeSongCursor(context, AudioColumns._ID + "=?", + arrayOf(queryId.toString())) + return getSong(cursor) + } + + fun suggestSongs(context: Context): Observable> { + return SongLoader.getAllSongs(context) + .flatMap { + val list = ArrayList() + ShuffleHelper.makeShuffleList(it, -1) + if (it.size > 9) { + list.addAll(it.subList(0, 9)) + } + return@flatMap Observable.just(list) + } + /*.flatMap({ songs -> + val list = ArrayList() + ShuffleHelper.makeShuffleList(songs, -1) + if (songs.size > 9) { + list.addAll(songs.subList(0, 9)) + } + Observable.just(list) + } as Function, ObservableSource>>)*/ + /*.subscribe(songs -> { + ArrayList list = new ArrayList<>(); + if (songs.isEmpty()) { + return; + } + ShuffleHelper.makeShuffleList(songs, -1); + if (songs.size() > 10) { + list.addAll(songs.subList(0, 10)); + } else { + list.addAll(songs); + } + return; + });*/ + } } diff --git a/app/src/main/java/code/name/monkey/retromusic/loaders/TopAndRecentlyPlayedTracksLoader.java b/app/src/main/java/code/name/monkey/retromusic/loaders/TopAndRecentlyPlayedTracksLoader.java deleted file mode 100644 index 43244b14..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/loaders/TopAndRecentlyPlayedTracksLoader.java +++ /dev/null @@ -1,158 +0,0 @@ -package code.name.monkey.retromusic.loaders; - -import android.content.Context; -import android.database.Cursor; -import android.provider.BaseColumns; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import code.name.monkey.retromusic.model.Album; -import code.name.monkey.retromusic.model.Artist; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.providers.HistoryStore; -import code.name.monkey.retromusic.providers.SongPlayCountStore; -import io.reactivex.Observable; -import java.util.ArrayList; - -/** - * Created by hemanths on 16/08/17. - */ - -public class TopAndRecentlyPlayedTracksLoader { - - private static final int NUMBER_OF_TOP_TRACKS = 99; - - @NonNull - public static Observable> getRecentlyPlayedTracks(@NonNull Context context) { - return SongLoader.Companion.getSongs(makeRecentTracksCursorAndClearUpDatabase(context)); - } - - @NonNull - public static Observable> getTopTracks(@NonNull Context context) { - return SongLoader.Companion.getSongs(makeTopTracksCursorAndClearUpDatabase(context)); - } - - @Nullable - private static Cursor makeRecentTracksCursorAndClearUpDatabase(@NonNull final Context context) { - SortedLongCursor retCursor = makeRecentTracksCursorImpl(context); - - // clean up the databases with any ids not found - if (retCursor != null) { - ArrayList missingIds = retCursor.getMissingIds(); - if (missingIds != null && missingIds.size() > 0) { - for (long id : missingIds) { - HistoryStore.getInstance(context).removeSongId(id); - } - } - } - return retCursor; - } - - @Nullable - private static Cursor makeTopTracksCursorAndClearUpDatabase(@NonNull final Context context) { - SortedLongCursor retCursor = makeTopTracksCursorImpl(context); - - // clean up the databases with any ids not found - if (retCursor != null) { - ArrayList missingIds = retCursor.getMissingIds(); - if (missingIds != null && missingIds.size() > 0) { - for (long id : missingIds) { - SongPlayCountStore.getInstance(context).removeItem(id); - } - } - } - return retCursor; - } - - @Nullable - private static SortedLongCursor makeRecentTracksCursorImpl(@NonNull final Context context) { - // first get the top results ids from the internal database - Cursor songs = HistoryStore.getInstance(context).queryRecentIds(); - - try { - return makeSortedCursor(context, songs, - songs.getColumnIndex(HistoryStore.RecentStoreColumns.ID)); - } finally { - if (songs != null) { - songs.close(); - } - } - } - - @Nullable - private static SortedLongCursor makeTopTracksCursorImpl(@NonNull final Context context) { - // first get the top results ids from the internal database - Cursor songs = SongPlayCountStore.getInstance(context) - .getTopPlayedResults(NUMBER_OF_TOP_TRACKS); - - try { - return makeSortedCursor(context, songs, - songs.getColumnIndex(SongPlayCountStore.SongPlayCountColumns.ID)); - } finally { - if (songs != null) { - songs.close(); - } - } - } - - @Nullable - private static SortedLongCursor makeSortedCursor(@NonNull final Context context, - @Nullable final Cursor cursor, final int idColumn) { - - if (cursor != null && cursor.moveToFirst()) { - // create the list of ids to select against - StringBuilder selection = new StringBuilder(); - selection.append(BaseColumns._ID); - selection.append(" IN ("); - - // this tracks the order of the ids - long[] order = new long[cursor.getCount()]; - - long id = cursor.getLong(idColumn); - selection.append(id); - order[cursor.getPosition()] = id; - - while (cursor.moveToNext()) { - selection.append(","); - - id = cursor.getLong(idColumn); - order[cursor.getPosition()] = id; - selection.append(String.valueOf(id)); - } - - selection.append(")"); - - // get a list of songs with the data given the selection statement - Cursor songCursor = SongLoader.Companion.makeSongCursor(context, selection.toString(), null); - if (songCursor != null) { - // now return the wrapped TopTracksCursor to handle sorting given order - return new SortedLongCursor(songCursor, order, BaseColumns._ID); - } - } - - return null; - } - - @NonNull - public static Observable> getTopAlbums(@NonNull Context context) { - return Observable.create(e -> { - getTopTracks(context).subscribe(songs -> { - if (songs.size() > 0) { - e.onNext(AlbumLoader.splitIntoAlbums(songs)); - } - e.onComplete(); - }); - }); - } - - @NonNull - public static Observable> getTopArtists(@NonNull Context context) { - return Observable.create(e -> { - getTopAlbums(context).subscribe(albums -> { - if (albums.size() > 0) { - e.onNext(ArtistLoader.splitIntoArtists(albums)); - } - e.onComplete(); - }); - }); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/loaders/TopAndRecentlyPlayedTracksLoader.kt b/app/src/main/java/code/name/monkey/retromusic/loaders/TopAndRecentlyPlayedTracksLoader.kt new file mode 100644 index 00000000..cce045f9 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/loaders/TopAndRecentlyPlayedTracksLoader.kt @@ -0,0 +1,142 @@ +package code.name.monkey.retromusic.loaders + +import android.content.Context +import android.database.Cursor +import android.provider.BaseColumns +import code.name.monkey.retromusic.Constants.NUMBER_OF_TOP_TRACKS +import code.name.monkey.retromusic.model.Album +import code.name.monkey.retromusic.model.Artist +import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.providers.HistoryStore +import code.name.monkey.retromusic.providers.SongPlayCountStore +import io.reactivex.Observable +import java.util.* + +/** + * Created by hemanths on 16/08/17. + */ + +object TopAndRecentlyPlayedTracksLoader { + + fun getRecentlyPlayedTracks(context: Context): Observable> { + return SongLoader.getSongs(makeRecentTracksCursorAndClearUpDatabase(context)) + } + + fun getTopTracks(context: Context): Observable> { + return SongLoader.getSongs(makeTopTracksCursorAndClearUpDatabase(context)) + } + + private fun makeRecentTracksCursorAndClearUpDatabase(context: Context): Cursor? { + val retCursor = makeRecentTracksCursorImpl(context) + + // clean up the databases with any ids not found + if (retCursor != null) { + val missingIds = retCursor.missingIds + if (missingIds != null && missingIds.size > 0) { + for (id in missingIds) { + HistoryStore.getInstance(context).removeSongId(id) + } + } + } + return retCursor + } + + private fun makeTopTracksCursorAndClearUpDatabase(context: Context): Cursor? { + val retCursor = makeTopTracksCursorImpl(context) + + // clean up the databases with any ids not found + if (retCursor != null) { + val missingIds = retCursor.missingIds + if (missingIds != null && missingIds.size > 0) { + for (id in missingIds) { + SongPlayCountStore.getInstance(context).removeItem(id) + } + } + } + return retCursor + } + + private fun makeRecentTracksCursorImpl(context: Context): SortedLongCursor? { + // first get the top results ids from the internal database + val songs = HistoryStore.getInstance(context).queryRecentIds() + + try { + return makeSortedCursor(context, songs, + songs!!.getColumnIndex(HistoryStore.RecentStoreColumns.ID)) + } finally { + songs?.close() + } + } + + private fun makeTopTracksCursorImpl(context: Context): SortedLongCursor? { + // first get the top results ids from the internal database + val songs = SongPlayCountStore.getInstance(context) + .getTopPlayedResults(NUMBER_OF_TOP_TRACKS) + + try { + return makeSortedCursor(context, songs, + songs!!.getColumnIndex(SongPlayCountStore.SongPlayCountColumns.ID)) + } finally { + songs?.close() + } + } + + private fun makeSortedCursor(context: Context, + cursor: Cursor?, idColumn: Int): SortedLongCursor? { + + if (cursor != null && cursor.moveToFirst()) { + // create the list of ids to select against + val selection = StringBuilder() + selection.append(BaseColumns._ID) + selection.append(" IN (") + + // this tracks the order of the ids + val order = LongArray(cursor.count) + + var id = cursor.getLong(idColumn) + selection.append(id) + order[cursor.position] = id + + while (cursor.moveToNext()) { + selection.append(",") + + id = cursor.getLong(idColumn) + order[cursor.position] = id + selection.append(id.toString()) + } + + selection.append(")") + + // get a list of songs with the data given the selection statement + val songCursor = SongLoader.makeSongCursor(context, selection.toString(), null) + if (songCursor != null) { + // now return the wrapped TopTracksCursor to handle sorting given order + return SortedLongCursor(songCursor, order, BaseColumns._ID) + } + } + + return null + } + + fun getTopAlbums(context: Context): Observable> { + return Observable.create { e -> + getTopTracks(context).subscribe { songs -> + if (songs.size > 0) { + e.onNext(AlbumLoader.splitIntoAlbums(songs)) + } + e.onComplete() + } + } + } + + fun getTopArtists(context: Context): Observable> { + return Observable.create { e -> + getTopAlbums(context).subscribe { albums -> + if (albums.size > 0) { + e.onNext(ArtistLoader.splitIntoArtists(albums)) + } + e.onComplete() + } + } + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/misc/AppBarStateChangeListener.java b/app/src/main/java/code/name/monkey/retromusic/misc/AppBarStateChangeListener.java deleted file mode 100644 index a55676a3..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/misc/AppBarStateChangeListener.java +++ /dev/null @@ -1,41 +0,0 @@ -package code.name.monkey.retromusic.misc; - -import com.google.android.material.appbar.AppBarLayout; - -/** - * @author Hemanth S (h4h13). - * https://stackoverflow.com/a/33891727 - */ - -public abstract class AppBarStateChangeListener implements AppBarLayout.OnOffsetChangedListener { - - private State mCurrentState = State.IDLE; - - @Override - public final void onOffsetChanged(AppBarLayout appBarLayout, int i) { - if (i == 0) { - if (mCurrentState != State.EXPANDED) { - onStateChanged(appBarLayout, State.EXPANDED); - } - mCurrentState = State.EXPANDED; - } else if (Math.abs(i) >= appBarLayout.getTotalScrollRange()) { - if (mCurrentState != State.COLLAPSED) { - onStateChanged(appBarLayout, State.COLLAPSED); - } - mCurrentState = State.COLLAPSED; - } else { - if (mCurrentState != State.IDLE) { - onStateChanged(appBarLayout, State.IDLE); - } - mCurrentState = State.IDLE; - } - } - - public abstract void onStateChanged(AppBarLayout appBarLayout, State state); - - public enum State { - EXPANDED, - COLLAPSED, - IDLE - } -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/misc/AppBarStateChangeListener.kt b/app/src/main/java/code/name/monkey/retromusic/misc/AppBarStateChangeListener.kt new file mode 100644 index 00000000..a76dd596 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/misc/AppBarStateChangeListener.kt @@ -0,0 +1,40 @@ +package code.name.monkey.retromusic.misc + +import com.google.android.material.appbar.AppBarLayout + +/** + * @author Hemanth S (h4h13). + * https://stackoverflow.com/a/33891727 + */ + +abstract class AppBarStateChangeListener : AppBarLayout.OnOffsetChangedListener { + + private var mCurrentState = State.IDLE + + override fun onOffsetChanged(appBarLayout: AppBarLayout, i: Int) { + if (i == 0) { + if (mCurrentState != State.EXPANDED) { + onStateChanged(appBarLayout, State.EXPANDED) + } + mCurrentState = State.EXPANDED + } else if (Math.abs(i) >= appBarLayout.totalScrollRange) { + if (mCurrentState != State.COLLAPSED) { + onStateChanged(appBarLayout, State.COLLAPSED) + } + mCurrentState = State.COLLAPSED + } else { + if (mCurrentState != State.IDLE) { + onStateChanged(appBarLayout, State.IDLE) + } + mCurrentState = State.IDLE + } + } + + abstract fun onStateChanged(appBarLayout: AppBarLayout, state: State) + + enum class State { + EXPANDED, + COLLAPSED, + IDLE + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/misc/SimpleAnimatorListener.java b/app/src/main/java/code/name/monkey/retromusic/misc/SimpleAnimatorListener.java deleted file mode 100644 index 2f405577..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/misc/SimpleAnimatorListener.java +++ /dev/null @@ -1,26 +0,0 @@ -package code.name.monkey.retromusic.misc; - -import android.animation.Animator; - - -public abstract class SimpleAnimatorListener implements Animator.AnimatorListener { - @Override - public void onAnimationStart(Animator animation) { - - } - - @Override - public void onAnimationEnd(Animator animation) { - - } - - @Override - public void onAnimationCancel(Animator animation) { - - } - - @Override - public void onAnimationRepeat(Animator animation) { - - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/misc/SimpleAnimatorListener.kt b/app/src/main/java/code/name/monkey/retromusic/misc/SimpleAnimatorListener.kt new file mode 100644 index 00000000..8090be93 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/misc/SimpleAnimatorListener.kt @@ -0,0 +1,22 @@ +package code.name.monkey.retromusic.misc + +import android.animation.Animator + + +abstract class SimpleAnimatorListener : Animator.AnimatorListener { + override fun onAnimationStart(animation: Animator) { + + } + + override fun onAnimationEnd(animation: Animator) { + + } + + override fun onAnimationCancel(animation: Animator) { + + } + + override fun onAnimationRepeat(animation: Animator) { + + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/misc/SimpleOnSeekbarChangeListener.java b/app/src/main/java/code/name/monkey/retromusic/misc/SimpleOnSeekbarChangeListener.java deleted file mode 100644 index 44a39f6e..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/misc/SimpleOnSeekbarChangeListener.java +++ /dev/null @@ -1,21 +0,0 @@ -package code.name.monkey.retromusic.misc; - -import android.widget.SeekBar; - - -public abstract class SimpleOnSeekbarChangeListener implements SeekBar.OnSeekBarChangeListener { - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - - } - - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/misc/SimpleOnSeekbarChangeListener.kt b/app/src/main/java/code/name/monkey/retromusic/misc/SimpleOnSeekbarChangeListener.kt new file mode 100644 index 00000000..f71a0a78 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/misc/SimpleOnSeekbarChangeListener.kt @@ -0,0 +1,18 @@ +package code.name.monkey.retromusic.misc + +import android.widget.SeekBar + + +abstract class SimpleOnSeekbarChangeListener : SeekBar.OnSeekBarChangeListener { + override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { + + } + + override fun onStartTrackingTouch(seekBar: SeekBar) { + + } + + override fun onStopTrackingTouch(seekBar: SeekBar) { + + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/misc/WeakContextAsyncTask.java b/app/src/main/java/code/name/monkey/retromusic/misc/WeakContextAsyncTask.java deleted file mode 100644 index a2a8b7dd..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/misc/WeakContextAsyncTask.java +++ /dev/null @@ -1 +0,0 @@ -package code.name.monkey.retromusic.misc; import android.content.Context; import android.os.AsyncTask; import androidx.annotation.Nullable; import java.lang.ref.WeakReference; public abstract class WeakContextAsyncTask extends AsyncTask { private WeakReference contextWeakReference; public WeakContextAsyncTask(Context context) { contextWeakReference = new WeakReference<>(context); } @Nullable protected Context getContext() { return contextWeakReference.get(); } } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/misc/WeakContextAsyncTask.kt b/app/src/main/java/code/name/monkey/retromusic/misc/WeakContextAsyncTask.kt new file mode 100644 index 00000000..29df9680 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/misc/WeakContextAsyncTask.kt @@ -0,0 +1 @@ +package code.name.monkey.retromusic.misc import android.content.Context import android.os.AsyncTask import java.lang.ref.WeakReference abstract class WeakContextAsyncTask(context: Context) : AsyncTask() { private val contextWeakReference: WeakReference = WeakReference(context) protected val context: Context? get() = contextWeakReference.get() } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/misc/WrappedAsyncTaskLoader.java b/app/src/main/java/code/name/monkey/retromusic/misc/WrappedAsyncTaskLoader.java deleted file mode 100644 index 599fe6ea..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/misc/WrappedAsyncTaskLoader.java +++ /dev/null @@ -1,72 +0,0 @@ - -package code.name.monkey.retromusic.misc; - -import android.content.Context; -import androidx.loader.content.AsyncTaskLoader; - -/** - * Issue - * 14944 - * - * @author Alexander Blom - */ -public abstract class WrappedAsyncTaskLoader extends AsyncTaskLoader { - - private D mData; - - /** - * Constructor of WrappedAsyncTaskLoader - * - * @param context The {@link Context} to use. - */ - public WrappedAsyncTaskLoader(Context context) { - super(context); - } - - /** - * {@inheritDoc} - */ - @Override - public void deliverResult(D data) { - if (!isReset()) { - this.mData = data; - super.deliverResult(data); - } else { - // An asynchronous query came in while the loader is stopped - } - } - - /** - * {@inheritDoc} - */ - @Override - protected void onStartLoading() { - super.onStartLoading(); - if (this.mData != null) { - deliverResult(this.mData); - } else if (takeContentChanged() || this.mData == null) { - forceLoad(); - } - } - - /** - * {@inheritDoc} - */ - @Override - protected void onStopLoading() { - super.onStopLoading(); - // Attempt to cancel the current load task if possible - cancelLoad(); - } - - /** - * {@inheritDoc} - */ - @Override - protected void onReset() { - super.onReset(); - // Ensure the loader is stopped - onStopLoading(); - this.mData = null; - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/misc/WrappedAsyncTaskLoader.kt b/app/src/main/java/code/name/monkey/retromusic/misc/WrappedAsyncTaskLoader.kt new file mode 100644 index 00000000..21f328fd --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/misc/WrappedAsyncTaskLoader.kt @@ -0,0 +1,64 @@ +package code.name.monkey.retromusic.misc + +import android.content.Context +import androidx.loader.content.AsyncTaskLoader + +/** + * [Issue + * 14944](http://code.google.com/p/android/issues/detail?id=14944) + * + * @author Alexander Blom + */ +abstract class WrappedAsyncTaskLoader +/** + * Constructor of `WrappedAsyncTaskLoader` + * + * @param context The [Context] to use. + */ +(context: Context) : AsyncTaskLoader(context) { + + private var mData: D? = null + + /** + * {@inheritDoc} + */ + override fun deliverResult(data: D?) { + if (!isReset) { + this.mData = data + super.deliverResult(data) + } else { + // An asynchronous query came in while the loader is stopped + } + } + + /** + * {@inheritDoc} + */ + override fun onStartLoading() { + super.onStartLoading() + if (this.mData != null) { + deliverResult(this.mData) + } else if (takeContentChanged() || this.mData == null) { + forceLoad() + } + } + + /** + * {@inheritDoc} + */ + override fun onStopLoading() { + super.onStopLoading() + // Attempt to cancel the current load task if possible + cancelLoad() + } + + /** + * {@inheritDoc} + */ + override fun onReset() { + super.onReset() + // Ensure the loader is stopped + onStopLoading() + this.mData = null + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/model/Album.java b/app/src/main/java/code/name/monkey/retromusic/model/Album.java deleted file mode 100644 index 9d556b8a..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/model/Album.java +++ /dev/null @@ -1,103 +0,0 @@ -package code.name.monkey.retromusic.model; - -import android.os.Parcel; -import android.os.Parcelable; -import androidx.annotation.NonNull; -import java.util.ArrayList; - - -public class Album implements Parcelable { - - public static final Creator CREATOR = new Creator() { - public Album createFromParcel(Parcel source) { - return new Album(source); - } - - public Album[] newArray(int size) { - return new Album[size]; - } - }; - public final ArrayList songs; - - public Album(ArrayList songs) { - this.songs = songs; - } - - public Album() { - this.songs = new ArrayList<>(); - } - - protected Album(Parcel in) { - this.songs = in.createTypedArrayList(Song.CREATOR); - } - - public int getId() { - return safeGetFirstSong().albumId; - } - - public String getTitle() { - return safeGetFirstSong().albumName; - } - - public int getArtistId() { - return safeGetFirstSong().artistId; - } - - public String getArtistName() { - return safeGetFirstSong().artistName; - } - - public int getYear() { - return safeGetFirstSong().year; - } - - public long getDateModified() { - return safeGetFirstSong().dateModified; - } - - public int getSongCount() { - return songs.size(); - } - - @NonNull - public Song safeGetFirstSong() { - return songs.isEmpty() ? Song.EMPTY_SONG : songs.get(0); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - Album that = (Album) o; - - return songs != null ? songs.equals(that.songs) : that.songs == null; - - } - - @Override - public int hashCode() { - return songs != null ? songs.hashCode() : 0; - } - - @Override - public String toString() { - return "Album{" + - "songs=" + songs + - '}'; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeTypedList(songs); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/model/Album.kt b/app/src/main/java/code/name/monkey/retromusic/model/Album.kt new file mode 100644 index 00000000..27405c44 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/model/Album.kt @@ -0,0 +1,41 @@ +package code.name.monkey.retromusic.model + +import java.util.* + + +class Album { + val songs: ArrayList? + + val id: Int + get() = safeGetFirstSong().albumId + + val title: String? + get() = safeGetFirstSong().albumName + + val artistId: Int + get() = safeGetFirstSong().artistId + + val artistName: String? + get() = safeGetFirstSong().artistName + + val year: Int + get() = safeGetFirstSong().year + + val dateModified: Long + get() = safeGetFirstSong().dateModified + + val songCount: Int + get() = songs!!.size + + constructor(songs: ArrayList) { + this.songs = songs + } + + constructor() { + this.songs = ArrayList() + } + + fun safeGetFirstSong(): Song { + return if (songs!!.isEmpty()) Song.EMPTY_SONG else songs[0] + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/model/Artist.java b/app/src/main/java/code/name/monkey/retromusic/model/Artist.java deleted file mode 100644 index 7cec500a..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/model/Artist.java +++ /dev/null @@ -1,110 +0,0 @@ -package code.name.monkey.retromusic.model; - -import android.os.Parcel; -import android.os.Parcelable; -import androidx.annotation.NonNull; - -import java.util.ArrayList; - -import code.name.monkey.retromusic.util.MusicUtil; - - -public class Artist implements Parcelable { - public static final String UNKNOWN_ARTIST_DISPLAY_NAME = "Unknown Artist"; - public final ArrayList albums; - - public Artist(ArrayList albums) { - this.albums = albums; - } - - public Artist() { - this.albums = new ArrayList<>(); - } - - public int getId() { - return safeGetFirstAlbum().getArtistId(); - } - - public String getName() { - String name = safeGetFirstAlbum().getArtistName(); - if (MusicUtil.isArtistNameUnknown(name)) { - return UNKNOWN_ARTIST_DISPLAY_NAME; - } - return name; - } - - public int getSongCount() { - int songCount = 0; - for (Album album : albums) { - songCount += album.getSongCount(); - } - return songCount; - } - - public int getAlbumCount() { - return albums.size(); - } - - public ArrayList getSongs() { - ArrayList songs = new ArrayList<>(); - for (Album album : albums) { - songs.addAll(album.songs); - } - return songs; - } - - @NonNull - public Album safeGetFirstAlbum() { - return albums.isEmpty() ? new Album() : albums.get(0); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - Artist artist = (Artist) o; - - return albums != null ? albums.equals(artist.albums) : artist.albums == null; - - } - - @Override - public int hashCode() { - return albums != null ? albums.hashCode() : 0; - } - - @Override - public String toString() { - return "Artist{" + - "albums=" + albums + - '}'; - } - - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeTypedList(this.albums); - } - - protected Artist(Parcel in) { - this.albums = in.createTypedArrayList(Album.CREATOR); - } - - public static final Creator CREATOR = new Creator() { - @Override - public Artist createFromParcel(Parcel source) { - return new Artist(source); - } - - @Override - public Artist[] newArray(int size) { - return new Artist[size]; - } - }; -} diff --git a/app/src/main/java/code/name/monkey/retromusic/model/Artist.kt b/app/src/main/java/code/name/monkey/retromusic/model/Artist.kt new file mode 100644 index 00000000..0f5a5c35 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/model/Artist.kt @@ -0,0 +1,57 @@ +package code.name.monkey.retromusic.model + +import code.name.monkey.retromusic.util.MusicUtil +import java.util.* + + +class Artist { + val albums: ArrayList? + + val id: Int + get() = safeGetFirstAlbum().artistId + + val name: String + get() { + val name = safeGetFirstAlbum().artistName + return if (MusicUtil.isArtistNameUnknown(name)) { + UNKNOWN_ARTIST_DISPLAY_NAME + } else name!! + } + + val songCount: Int + get() { + var songCount = 0 + for (album in albums!!) { + songCount += album.songCount + } + return songCount + } + + val albumCount: Int + get() = albums!!.size + + val songs: ArrayList + get() { + val songs = ArrayList() + for (album in albums!!) { + songs.addAll(album.songs!!) + } + return songs + } + + constructor(albums: ArrayList) { + this.albums = albums + } + + constructor() { + this.albums = ArrayList() + } + + fun safeGetFirstAlbum(): Album { + return if (albums!!.isEmpty()) Album() else albums[0] + } + + companion object { + const val UNKNOWN_ARTIST_DISPLAY_NAME = "Unknown Artist" + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/model/Contributor.java b/app/src/main/java/code/name/monkey/retromusic/model/Contributor.java deleted file mode 100644 index 0488f1ca..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/model/Contributor.java +++ /dev/null @@ -1,34 +0,0 @@ -package code.name.monkey.retromusic.model; - -public class Contributor { - private String name; - private String summary; - private String link; - private String profile_image; - - public Contributor() { - } - - public Contributor(String name, String summary, String link, String profile_image) { - this.name = name; - this.summary = summary; - this.link = link; - this.profile_image = profile_image; - } - - public String getName() { - return name; - } - - public String getSummary() { - return summary; - } - - public String getLink() { - return link; - } - - public String getProfileImage() { - return profile_image; - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/model/Contributor.kt b/app/src/main/java/code/name/monkey/retromusic/model/Contributor.kt new file mode 100644 index 00000000..de25933c --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/model/Contributor.kt @@ -0,0 +1,3 @@ +package code.name.monkey.retromusic.model + +class Contributor(val name: String, val summary: String, val link: String, val profileImage: String) diff --git a/app/src/main/java/code/name/monkey/retromusic/model/Genre.java b/app/src/main/java/code/name/monkey/retromusic/model/Genre.java deleted file mode 100644 index 9522536d..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/model/Genre.java +++ /dev/null @@ -1,86 +0,0 @@ -package code.name.monkey.retromusic.model; - -import android.os.Parcel; -import android.os.Parcelable; - -/** - * @author Hemanth S (h4h13). - */ - -public class Genre implements Parcelable { - - public static final Creator CREATOR = new Creator() { - @Override - public Genre createFromParcel(Parcel in) { - return new Genre(in); - } - - @Override - public Genre[] newArray(int size) { - return new Genre[size]; - } - }; - public final int id; - public final String name; - public final int songCount; - - public Genre(final int id, final String name, int songCount) { - this.id = id; - this.name = name; - this.songCount = songCount; - } - - - // For unknown genre - public Genre(final String name, final int songCount) { - this.id = -1; - this.name = name; - this.songCount = songCount; - } - - protected Genre(Parcel in) { - id = in.readInt(); - name = in.readString(); - songCount = in.readInt(); - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(id); - dest.writeString(name); - dest.writeInt(songCount); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - Genre genre = (Genre) o; - - if (id != genre.id) return false; - return name != null ? name.equals(genre.name) : genre.name == null; - } - - @Override - public int hashCode() { - int result = id; - result = 31 * result + (name != null ? name.hashCode() : 0); - return result; - } - - @Override - public String toString() { - return "Genre{" + - "id=" + id + - ", name='" + name + '\'' + - '}'; - } - - -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/model/Genre.kt b/app/src/main/java/code/name/monkey/retromusic/model/Genre.kt new file mode 100644 index 00000000..9448e18e --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/model/Genre.kt @@ -0,0 +1,26 @@ +package code.name.monkey.retromusic.model + +import java.io.Serializable + +/** + * @author Hemanth S (h4h13). + */ + +class Genre : Serializable { + val id: Int + val name: String? + val songCount: Int + + constructor(id: Int, name: String, songCount: Int) { + this.id = id + this.name = name + this.songCount = songCount + } + + // For unknown genre + constructor(name: String, songCount: Int) { + this.id = -1 + this.name = name + this.songCount = songCount + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/model/Home.java b/app/src/main/java/code/name/monkey/retromusic/model/Home.java deleted file mode 100755 index 2814bc9b..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/model/Home.java +++ /dev/null @@ -1,29 +0,0 @@ -package code.name.monkey.retromusic.model; - -import androidx.annotation.StringRes; - -import java.util.ArrayList; - - -/** - * Created by BlackFootSanji on 5/22/2017. - */ - -public class Home { - - private int sectionTitle; - private ArrayList list; - - public Home(@StringRes int sectionTitle, ArrayList list) { - this.sectionTitle = sectionTitle; - this.list = list; - } - - public int getSectionTitle() { - return sectionTitle; - } - - public ArrayList getList() { - return list; - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/model/Song.java b/app/src/main/java/code/name/monkey/retromusic/model/Song.java deleted file mode 100644 index 54c95bd5..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/model/Song.java +++ /dev/null @@ -1,77 +0,0 @@ -package code.name.monkey.retromusic.model; - -import android.os.Parcel; -import android.os.Parcelable; - - -public class Song implements Parcelable { - public static final Song EMPTY_SONG = new Song(-1, "", -1, -1, -1, "", -1, -1, "", -1, ""); - public static final Creator CREATOR = new Creator() { - public Song createFromParcel(Parcel source) { - return new Song(source); - } - - public Song[] newArray(int size) { - return new Song[size]; - } - }; - public final int id; - public final String title; - public final int trackNumber; - public final int year; - public final long duration; - public final String data; - public final long dateModified; - public final int albumId; - public final String albumName; - public final int artistId; - public final String artistName; - - public Song(int id, String title, int trackNumber, int year, long duration, String data, long dateModified, int albumId, String albumName, int artistId, String artistName) { - this.id = id; - this.title = title; - this.trackNumber = trackNumber; - this.year = year; - this.duration = duration; - this.data = data; - this.dateModified = dateModified; - this.albumId = albumId; - this.albumName = albumName; - this.artistId = artistId; - this.artistName = artistName; - } - - protected Song(Parcel in) { - this.id = in.readInt(); - this.title = in.readString(); - this.trackNumber = in.readInt(); - this.year = in.readInt(); - this.duration = in.readLong(); - this.data = in.readString(); - this.dateModified = in.readLong(); - this.albumId = in.readInt(); - this.albumName = in.readString(); - this.artistId = in.readInt(); - this.artistName = in.readString(); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(this.id); - dest.writeString(this.title); - dest.writeInt(this.trackNumber); - dest.writeInt(this.year); - dest.writeLong(this.duration); - dest.writeString(this.data); - dest.writeLong(this.dateModified); - dest.writeInt(this.albumId); - dest.writeString(this.albumName); - dest.writeInt(this.artistId); - dest.writeString(this.artistName); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/model/Song.kt b/app/src/main/java/code/name/monkey/retromusic/model/Song.kt new file mode 100644 index 00000000..fab260e2 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/model/Song.kt @@ -0,0 +1,79 @@ +package code.name.monkey.retromusic.model + +import android.os.Parcel +import android.os.Parcelable + + +open class Song : Parcelable { + val id: Int + val title: String? + val trackNumber: Int + val year: Int + val duration: Long + val data: String? + val dateModified: Long + val albumId: Int + val albumName: String? + val artistId: Int + val artistName: String? + + constructor(id: Int, title: String, trackNumber: Int, year: Int, duration: Long, data: String, dateModified: Long, albumId: Int, albumName: String, artistId: Int, artistName: String) { + this.id = id + this.title = title + this.trackNumber = trackNumber + this.year = year + this.duration = duration + this.data = data + this.dateModified = dateModified + this.albumId = albumId + this.albumName = albumName + this.artistId = artistId + this.artistName = artistName + } + + protected constructor(`in`: Parcel) { + this.id = `in`.readInt() + this.title = `in`.readString() + this.trackNumber = `in`.readInt() + this.year = `in`.readInt() + this.duration = `in`.readLong() + this.data = `in`.readString() + this.dateModified = `in`.readLong() + this.albumId = `in`.readInt() + this.albumName = `in`.readString() + this.artistId = `in`.readInt() + this.artistName = `in`.readString() + } + + override fun describeContents(): Int { + return 0 + } + + override fun writeToParcel(dest: Parcel, flags: Int) { + dest.writeInt(this.id) + dest.writeString(this.title) + dest.writeInt(this.trackNumber) + dest.writeInt(this.year) + dest.writeLong(this.duration) + dest.writeString(this.data) + dest.writeLong(this.dateModified) + dest.writeInt(this.albumId) + dest.writeString(this.albumName) + dest.writeInt(this.artistId) + dest.writeString(this.artistName) + } + + companion object { + val EMPTY_SONG = Song(-1, "", -1, -1, -1, "", -1, -1, "", -1, "") + @JvmField + val CREATOR: Parcelable.Creator = object : Parcelable.Creator { + override fun createFromParcel(source: Parcel): Song { + return Song(source) + } + + override fun newArray(size: Int): Array { + return emptyArray() + } + } + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/HistoryPlaylist.java b/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/HistoryPlaylist.java index ea748bc2..1ab5e0d7 100644 --- a/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/HistoryPlaylist.java +++ b/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/HistoryPlaylist.java @@ -37,7 +37,7 @@ public class HistoryPlaylist extends AbsSmartPlaylist { @NonNull @Override public Observable> getSongs(@NonNull Context context) { - return TopAndRecentlyPlayedTracksLoader.getRecentlyPlayedTracks(context); + return TopAndRecentlyPlayedTracksLoader.INSTANCE.getRecentlyPlayedTracks(context); } @Override diff --git a/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/LastAddedPlaylist.java b/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/LastAddedPlaylist.java index ec8c5209..d4dd4c7c 100644 --- a/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/LastAddedPlaylist.java +++ b/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/LastAddedPlaylist.java @@ -38,7 +38,7 @@ public class LastAddedPlaylist extends AbsSmartPlaylist { @NonNull @Override public Observable> getSongs(@NonNull Context context) { - return LastAddedSongsLoader.getLastAddedSongs(context); + return LastAddedSongsLoader.INSTANCE.getLastAddedSongs(context); } @Override diff --git a/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/MyTopTracksPlaylist.java b/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/MyTopTracksPlaylist.java index ee155ce4..18515d20 100644 --- a/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/MyTopTracksPlaylist.java +++ b/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/MyTopTracksPlaylist.java @@ -38,7 +38,7 @@ public class MyTopTracksPlaylist extends AbsSmartPlaylist implements Parcelable @NonNull @Override public Observable> getSongs(@NonNull Context context) { - return TopAndRecentlyPlayedTracksLoader.getTopTracks(context); + return TopAndRecentlyPlayedTracksLoader.INSTANCE.getTopTracks(context); } @Override diff --git a/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/ShuffleAllPlaylist.java b/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/ShuffleAllPlaylist.java index ca4bcbbf..e4b752f0 100644 --- a/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/ShuffleAllPlaylist.java +++ b/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/ShuffleAllPlaylist.java @@ -37,7 +37,7 @@ public class ShuffleAllPlaylist extends AbsSmartPlaylist { @NonNull @Override public Observable> getSongs(@NonNull Context context) { - return SongLoader.Companion.getAllSongs(context); + return SongLoader.INSTANCE.getAllSongs(context); } @Override diff --git a/app/src/main/java/code/name/monkey/retromusic/mvp/Presenter.java b/app/src/main/java/code/name/monkey/retromusic/mvp/Presenter.java index 5bc63f1c..59e52b04 100644 --- a/app/src/main/java/code/name/monkey/retromusic/mvp/Presenter.java +++ b/app/src/main/java/code/name/monkey/retromusic/mvp/Presenter.java @@ -20,8 +20,8 @@ public class Presenter { protected BaseSchedulerProvider schedulerProvider; public Presenter() { - this.repository = Injection.provideRepository(); - this.schedulerProvider = Injection.provideSchedulerProvider(); + this.repository = Injection.INSTANCE.provideRepository(); + this.schedulerProvider = Injection.INSTANCE.provideSchedulerProvider(); this.disposable = new CompositeDisposable(); } } diff --git a/app/src/main/java/code/name/monkey/retromusic/preferences/NowPlayingScreenPreferenceDialog.java b/app/src/main/java/code/name/monkey/retromusic/preferences/NowPlayingScreenPreferenceDialog.java index e49bd8d2..6c68c8ca 100644 --- a/app/src/main/java/code/name/monkey/retromusic/preferences/NowPlayingScreenPreferenceDialog.java +++ b/app/src/main/java/code/name/monkey/retromusic/preferences/NowPlayingScreenPreferenceDialog.java @@ -5,10 +5,6 @@ import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.fragment.app.DialogFragment; -import androidx.viewpager.widget.PagerAdapter; -import androidx.viewpager.widget.ViewPager; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -20,6 +16,10 @@ import com.afollestad.materialdialogs.DialogAction; import com.afollestad.materialdialogs.MaterialDialog; import com.bumptech.glide.Glide; +import androidx.annotation.NonNull; +import androidx.fragment.app.DialogFragment; +import androidx.viewpager.widget.PagerAdapter; +import androidx.viewpager.widget.ViewPager; import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.RetroApplication; import code.name.monkey.retromusic.ui.fragments.NowPlayingScreen; @@ -83,12 +83,12 @@ public class NowPlayingScreenPreferenceDialog extends DialogFragment implements private boolean isNowPlayingThemes(NowPlayingScreen nowPlayingScreen) { - if (nowPlayingScreen.equals(NowPlayingScreen.BLUR_CARD)) { + /*if (nowPlayingScreen.equals(NowPlayingScreen.BLUR_CARD)) { PreferenceUtil.getInstance().resetCarouselEffect(); PreferenceUtil.getInstance().resetCircularAlbumArt(); - } + }*/ - return (nowPlayingScreen.equals(NowPlayingScreen.FULL) || + /* return (nowPlayingScreen.equals(NowPlayingScreen.FULL) || nowPlayingScreen.equals(NowPlayingScreen.CARD) || nowPlayingScreen.equals(NowPlayingScreen.PLAIN) || nowPlayingScreen.equals(NowPlayingScreen.BLUR) || @@ -97,7 +97,8 @@ public class NowPlayingScreenPreferenceDialog extends DialogFragment implements nowPlayingScreen.equals(NowPlayingScreen.TINY) || nowPlayingScreen.equals(NowPlayingScreen.BLUR_CARD)|| nowPlayingScreen.equals(NowPlayingScreen.ADAPTIVE)) - && !RetroApplication.isProVersion(); + && !RetroApplication.Companion.isProVersion();*/ + return !RetroApplication.Companion.isProVersion(); } @Override diff --git a/app/src/main/java/code/name/monkey/retromusic/providers/BlacklistStore.java b/app/src/main/java/code/name/monkey/retromusic/providers/BlacklistStore.java index 66b4ea03..91fe2774 100644 --- a/app/src/main/java/code/name/monkey/retromusic/providers/BlacklistStore.java +++ b/app/src/main/java/code/name/monkey/retromusic/providers/BlacklistStore.java @@ -16,6 +16,8 @@ import code.name.monkey.retromusic.Constants; import code.name.monkey.retromusic.util.FileUtil; import code.name.monkey.retromusic.util.PreferenceUtil; +import static code.name.monkey.retromusic.Constants.MEDIA_STORE_CHANGED; + public class BlacklistStore extends SQLiteOpenHelper { public static final String DATABASE_NAME = "blacklist.db"; private static final int VERSION = 2; @@ -125,7 +127,7 @@ public class BlacklistStore extends SQLiteOpenHelper { } private void notifyMediaStoreChanged() { - context.sendBroadcast(new Intent(Constants.MEDIA_STORE_CHANGED)); + context.sendBroadcast(new Intent(MEDIA_STORE_CHANGED)); } @NonNull diff --git a/app/src/main/java/code/name/monkey/retromusic/providers/MusicPlaybackQueueStore.java b/app/src/main/java/code/name/monkey/retromusic/providers/MusicPlaybackQueueStore.java index dc9b61bd..ab8ce844 100644 --- a/app/src/main/java/code/name/monkey/retromusic/providers/MusicPlaybackQueueStore.java +++ b/app/src/main/java/code/name/monkey/retromusic/providers/MusicPlaybackQueueStore.java @@ -162,17 +162,17 @@ public class MusicPlaybackQueueStore extends SQLiteOpenHelper { Song song = queue.get(i); ContentValues values = new ContentValues(4); - values.put(BaseColumns._ID, song.id); - values.put(AudioColumns.TITLE, song.title); - values.put(AudioColumns.TRACK, song.trackNumber); - values.put(AudioColumns.YEAR, song.year); - values.put(AudioColumns.DURATION, song.duration); - values.put(AudioColumns.DATA, song.data); - values.put(AudioColumns.DATE_MODIFIED, song.dateModified); - values.put(AudioColumns.ALBUM_ID, song.albumId); - values.put(AudioColumns.ALBUM, song.albumName); - values.put(AudioColumns.ARTIST_ID, song.artistId); - values.put(AudioColumns.ARTIST, song.artistName); + values.put(BaseColumns._ID, song.getId()); + values.put(AudioColumns.TITLE, song.getTitle()); + values.put(AudioColumns.TRACK, song.getTrackNumber()); + values.put(AudioColumns.YEAR, song.getYear()); + values.put(AudioColumns.DURATION, song.getDuration()); + values.put(AudioColumns.DATA, song.getData()); + values.put(AudioColumns.DATE_MODIFIED, song.getDateModified()); + values.put(AudioColumns.ALBUM_ID, song.getAlbumId()); + values.put(AudioColumns.ALBUM, song.getAlbumName()); + values.put(AudioColumns.ARTIST_ID, song.getArtistId()); + values.put(AudioColumns.ARTIST, song.getArtistName()); database.insert(tableName, null, values); } @@ -198,6 +198,6 @@ public class MusicPlaybackQueueStore extends SQLiteOpenHelper { private Observable> getQueue(@NonNull final String tableName) { Cursor cursor = getReadableDatabase().query(tableName, null, null, null, null, null, null); - return SongLoader.Companion.getSongs(cursor); + return SongLoader.INSTANCE.getSongs(cursor); } } diff --git a/app/src/main/java/code/name/monkey/retromusic/providers/RepositoryImpl.java b/app/src/main/java/code/name/monkey/retromusic/providers/RepositoryImpl.java index 42299eec..7deda91e 100644 --- a/app/src/main/java/code/name/monkey/retromusic/providers/RepositoryImpl.java +++ b/app/src/main/java/code/name/monkey/retromusic/providers/RepositoryImpl.java @@ -41,69 +41,69 @@ public class RepositoryImpl implements Repository { public static synchronized RepositoryImpl getInstance() { if (INSTANCE == null) { - INSTANCE = new RepositoryImpl(RetroApplication.getInstance()); + INSTANCE = new RepositoryImpl(RetroApplication.Companion.getInstance()); } return INSTANCE; } @Override public Observable> getAllSongs() { - return SongLoader.Companion.getAllSongs(context); + return SongLoader.INSTANCE.getAllSongs(context); } @Override public Observable> getSuggestionSongs() { - return SongLoader.Companion.suggestSongs(context); + return SongLoader.INSTANCE.suggestSongs(context); } @Override public Observable getSong(int id) { - return SongLoader.Companion.getSong(context, id); + return SongLoader.INSTANCE.getSong(context, id); } @Override public Observable> getAllAlbums() { - return AlbumLoader.getAllAlbums(context); + return AlbumLoader.Companion.getAllAlbums(context); } @Override public Observable> getRecentAlbums() { - return LastAddedSongsLoader.getLastAddedAlbums(context); + return LastAddedSongsLoader.INSTANCE.getLastAddedAlbums(context); } @Override public Observable> getTopAlbums() { - return TopAndRecentlyPlayedTracksLoader.getTopAlbums(context); + return TopAndRecentlyPlayedTracksLoader.INSTANCE.getTopAlbums(context); } @Override public Observable getAlbum(int albumId) { - return AlbumLoader.getAlbum(context, albumId); + return AlbumLoader.Companion.getAlbum(context, albumId); } @Override public Observable> getAllArtists() { - return ArtistLoader.getAllArtists(context); + return ArtistLoader.INSTANCE.getAllArtists(context); } @Override public Observable> getRecentArtists() { - return LastAddedSongsLoader.getLastAddedArtists(context); + return LastAddedSongsLoader.INSTANCE.getLastAddedArtists(context); } @Override public Observable> getTopArtists() { - return TopAndRecentlyPlayedTracksLoader.getTopArtists(context); + return TopAndRecentlyPlayedTracksLoader.INSTANCE.getTopArtists(context); } @Override public Observable getArtistById(long artistId) { - return ArtistLoader.getArtist(context, (int) artistId); + return ArtistLoader.INSTANCE.getArtist(context, (int) artistId); } @Override public Observable> getAllPlaylists() { - return PlaylistLoader.getAllPlaylists(context); + return PlaylistLoader.INSTANCE.getAllPlaylists(context); } @Override @@ -113,37 +113,37 @@ public class RepositoryImpl implements Repository { @Override public Observable> search(String query) { - return SearchLoader.searchAll(context, query); + return SearchLoader.INSTANCE.searchAll(context, query); } @Override public Observable> getPlaylistSongs(Playlist playlist) { - return PlaylistSongsLoader.getPlaylistSongList(context, playlist); + return PlaylistSongsLoader.INSTANCE.getPlaylistSongList(context, playlist); } @Override public Observable> getHomeList() { - return HomeLoader.getHomeLoader(context); + return HomeLoader.INSTANCE.getHomeLoader(context); } @Override public Observable> getAllThings() { - return HomeLoader.getRecentAndTopThings(context); + return HomeLoader.INSTANCE.getRecentAndTopThings(context); } @Override public Observable> getAllGenres() { - return GenreLoader.getAllGenres(context); + return GenreLoader.INSTANCE.getAllGenres(context); } @Override public Observable> getGenre(int genreId) { - return GenreLoader.getSongs(context, genreId); + return GenreLoader.INSTANCE.getSongs(context, genreId); } @Override public Observable downloadLrcFile(String title, String artist, long duration) { - KuGouApiService service = Injection.provideKuGouApiService(); + KuGouApiService service = Injection.INSTANCE.provideKuGouApiService(); return service.searchLyric(title, String.valueOf(duration)) .subscribeOn(Schedulers.io()) .flatMap(kuGouSearchLyricResult -> { diff --git a/app/src/main/java/code/name/monkey/retromusic/rest/KogouClient.java b/app/src/main/java/code/name/monkey/retromusic/rest/KogouClient.java index 23db6a19..fcf0c7cd 100644 --- a/app/src/main/java/code/name/monkey/retromusic/rest/KogouClient.java +++ b/app/src/main/java/code/name/monkey/retromusic/rest/KogouClient.java @@ -1,12 +1,13 @@ package code.name.monkey.retromusic.rest; import android.content.Context; + +import java.io.File; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import code.name.monkey.retromusic.Constants; import code.name.monkey.retromusic.RetroApplication; import code.name.monkey.retromusic.rest.service.KuGouApiService; -import java.io.File; import okhttp3.Cache; import okhttp3.Call; import okhttp3.Interceptor; @@ -17,60 +18,62 @@ import retrofit2.Retrofit; import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; import retrofit2.converter.gson.GsonConverterFactory; +import static code.name.monkey.retromusic.Constants.BASE_API_URL_KUGOU; + /** * Created by hemanths on 23/08/17. */ public class KogouClient { - private static final String BASE_URL = Constants.BASE_API_URL_KUGOU; + private static final String BASE_URL = BASE_API_URL_KUGOU; - private KuGouApiService apiService; + private KuGouApiService apiService; - public KogouClient() { - this(createDefaultOkHttpClientBuilder().build()); - } - - private KogouClient(@NonNull Call.Factory client) { - Retrofit restAdapter = new Retrofit.Builder() - .baseUrl(BASE_URL) - .callFactory(client) - .addConverterFactory(GsonConverterFactory.create()) - .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) - .build(); - - apiService = restAdapter.create(KuGouApiService.class); - } - - @Nullable - private static Cache createDefaultCache(Context context) { - File cacheDir = new File(context.getCacheDir().getAbsolutePath(), "/okhttp-lastfm/"); - if (cacheDir.mkdirs() || cacheDir.isDirectory()) { - return new Cache(cacheDir, 1024 * 1024 * 10); + public KogouClient() { + this(createDefaultOkHttpClientBuilder().build()); } - return null; - } - private static Interceptor createCacheControlInterceptor() { - return chain -> { - Request modifiedRequest = chain.request().newBuilder() - .addHeader("Cache-Control", String.format("max-age=%d, max-stale=%d", 31536000, 31536000)) - .build(); - return chain.proceed(modifiedRequest); - }; - } + private KogouClient(@NonNull Call.Factory client) { + Retrofit restAdapter = new Retrofit.Builder() + .baseUrl(BASE_URL) + .callFactory(client) + .addConverterFactory(GsonConverterFactory.create()) + .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) + .build(); - private static OkHttpClient.Builder createDefaultOkHttpClientBuilder() { - HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(); - interceptor.setLevel(HttpLoggingInterceptor.Level.BODY); + apiService = restAdapter.create(KuGouApiService.class); + } - return new OkHttpClient.Builder() - .addInterceptor(interceptor) - .cache(createDefaultCache(RetroApplication.getInstance())) - .addInterceptor(createCacheControlInterceptor()); - } + @Nullable + private static Cache createDefaultCache(Context context) { + File cacheDir = new File(context.getCacheDir().getAbsolutePath(), "/okhttp-lastfm/"); + if (cacheDir.mkdirs() || cacheDir.isDirectory()) { + return new Cache(cacheDir, 1024 * 1024 * 10); + } + return null; + } - public KuGouApiService getApiService() { - return apiService; - } + private static Interceptor createCacheControlInterceptor() { + return chain -> { + Request modifiedRequest = chain.request().newBuilder() + .addHeader("Cache-Control", String.format("max-age=%d, max-stale=%d", 31536000, 31536000)) + .build(); + return chain.proceed(modifiedRequest); + }; + } + + private static OkHttpClient.Builder createDefaultOkHttpClientBuilder() { + HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(); + interceptor.setLevel(HttpLoggingInterceptor.Level.BODY); + + return new OkHttpClient.Builder() + .addInterceptor(interceptor) + .cache(createDefaultCache(RetroApplication.Companion.getInstance())) + .addInterceptor(createCacheControlInterceptor()); + } + + public KuGouApiService getApiService() { + return apiService; + } } diff --git a/app/src/main/java/code/name/monkey/retromusic/service/MediaButtonIntentReceiver.java b/app/src/main/java/code/name/monkey/retromusic/service/MediaButtonIntentReceiver.java deleted file mode 100644 index a728e7b4..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/service/MediaButtonIntentReceiver.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright (C) 2007 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. - */ - -// Modified for Phonograph by Karim Abou Zeid (kabouzeid). - -package code.name.monkey.retromusic.service; - -import android.annotation.SuppressLint; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.os.Build; -import android.os.Handler; -import android.os.Message; -import android.os.PowerManager; -import android.os.PowerManager.WakeLock; -import android.util.Log; -import android.view.KeyEvent; - -import code.name.monkey.retromusic.BuildConfig; - -import static code.name.monkey.retromusic.Constants.ACTION_PAUSE; -import static code.name.monkey.retromusic.Constants.ACTION_PLAY; -import static code.name.monkey.retromusic.Constants.ACTION_REWIND; -import static code.name.monkey.retromusic.Constants.ACTION_SKIP; -import static code.name.monkey.retromusic.Constants.ACTION_STOP; -import static code.name.monkey.retromusic.Constants.ACTION_TOGGLE_PAUSE; - -/** - * Used to control headset playback. - * Single press: pause/resume - * Double press: next track - * Triple press: previous track - */ -public class MediaButtonIntentReceiver extends BroadcastReceiver { - public static final String TAG = MediaButtonIntentReceiver.class.getSimpleName(); - private static final boolean DEBUG = BuildConfig.DEBUG; - private static final int MSG_HEADSET_DOUBLE_CLICK_TIMEOUT = 2; - - private static final int DOUBLE_CLICK = 400; - - private static WakeLock mWakeLock = null; - private static int mClickCounter = 0; - private static long mLastClickTime = 0; - - @SuppressLint("HandlerLeak") // false alarm, handler is already static - private static Handler mHandler = new Handler() { - - @Override - public void handleMessage(final Message msg) { - switch (msg.what) { - case MSG_HEADSET_DOUBLE_CLICK_TIMEOUT: - final int clickCount = msg.arg1; - final String command; - - if (DEBUG) Log.v(TAG, "Handling headset click, count = " + clickCount); - switch (clickCount) { - case 1: - command = ACTION_TOGGLE_PAUSE; - break; - case 2: - command = ACTION_SKIP; - break; - case 3: - command = ACTION_REWIND; - break; - default: - command = null; - break; - } - - if (command != null) { - final Context context = (Context) msg.obj; - startService(context, command); - } - break; - } - releaseWakeLockIfHandlerIdle(); - } - }; - - public static boolean handleIntent(final Context context, final Intent intent) { - final String intentAction = intent.getAction(); - if (Intent.ACTION_MEDIA_BUTTON.equals(intentAction)) { - final KeyEvent event = intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT); - if (event == null) { - return false; - } - - final int keycode = event.getKeyCode(); - final int action = event.getAction(); - final long eventTime = event.getEventTime() != 0 ? - event.getEventTime() : System.currentTimeMillis(); - - String command = null; - switch (keycode) { - case KeyEvent.KEYCODE_MEDIA_STOP: - command = ACTION_STOP; - break; - case KeyEvent.KEYCODE_HEADSETHOOK: - case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: - command = ACTION_TOGGLE_PAUSE; - break; - case KeyEvent.KEYCODE_MEDIA_NEXT: - command = ACTION_SKIP; - break; - case KeyEvent.KEYCODE_MEDIA_PREVIOUS: - command = ACTION_REWIND; - break; - case KeyEvent.KEYCODE_MEDIA_PAUSE: - command = ACTION_PAUSE; - break; - case KeyEvent.KEYCODE_MEDIA_PLAY: - command = ACTION_PLAY; - break; - } - if (command != null) { - if (action == KeyEvent.ACTION_DOWN) { - if (event.getRepeatCount() == 0) { - // Only consider the first event in a sequence, not the repeat events, - // so that we don't trigger in cases where the first event went to - // a different app (e.g. when the user ends a phone call by - // long pressing the headset button) - - // The service may or may not be running, but we need to send it - // a command. - if (keycode == KeyEvent.KEYCODE_HEADSETHOOK || keycode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) { - if (eventTime - mLastClickTime >= DOUBLE_CLICK) { - mClickCounter = 0; - } - - mClickCounter++; - if (DEBUG) Log.v(TAG, "Got headset click, count = " + mClickCounter); - mHandler.removeMessages(MSG_HEADSET_DOUBLE_CLICK_TIMEOUT); - - Message msg = mHandler.obtainMessage( - MSG_HEADSET_DOUBLE_CLICK_TIMEOUT, mClickCounter, 0, context); - - long delay = mClickCounter < 3 ? DOUBLE_CLICK : 0; - if (mClickCounter >= 3) { - mClickCounter = 0; - } - mLastClickTime = eventTime; - acquireWakeLockAndSendMessage(context, msg, delay); - } else { - startService(context, command); - } - return true; - } - } - } - } - return false; - } - - private static void startService(Context context, String command) { - final Intent intent = new Intent(context, MusicService.class); - intent.setAction(command); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - context.startForegroundService(intent); - } else { - context.startService(intent); - } - } - - private static void acquireWakeLockAndSendMessage(Context context, Message msg, long delay) { - if (mWakeLock == null) { - Context appContext = context.getApplicationContext(); - PowerManager pm = (PowerManager) appContext.getSystemService(Context.POWER_SERVICE); - if (pm != null) { - mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, - "RetroMusicApp:Wakelock headset button"); - } - mWakeLock.setReferenceCounted(false); - } - if (DEBUG) Log.v(TAG, "Acquiring wake lock and sending " + msg.what); - // Make sure we don't indefinitely hold the wake lock under any circumstances - mWakeLock.acquire(10000); - - mHandler.sendMessageDelayed(msg, delay); - } - - private static void releaseWakeLockIfHandlerIdle() { - if (mHandler.hasMessages(MSG_HEADSET_DOUBLE_CLICK_TIMEOUT)) { - if (DEBUG) Log.v(TAG, "Handler still has messages pending, not releasing wake lock"); - return; - } - - if (mWakeLock != null) { - if (DEBUG) Log.v(TAG, "Releasing wake lock"); - mWakeLock.release(); - mWakeLock = null; - } - } - - @Override - public void onReceive(final Context context, final Intent intent) { - if (DEBUG) Log.v(TAG, "Received intent: " + intent); - if (handleIntent(context, intent) && isOrderedBroadcast()) { - abortBroadcast(); - } - } -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/service/MediaButtonIntentReceiver.kt b/app/src/main/java/code/name/monkey/retromusic/service/MediaButtonIntentReceiver.kt new file mode 100644 index 00000000..c4093384 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/service/MediaButtonIntentReceiver.kt @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2007 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 code.name.monkey.retromusic.service + +import android.annotation.SuppressLint +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.os.Build +import android.os.Handler +import android.os.Message +import android.os.PowerManager +import android.os.PowerManager.WakeLock +import android.util.Log +import android.view.KeyEvent + +import code.name.monkey.retromusic.BuildConfig +import code.name.monkey.retromusic.Constants.ACTION_PAUSE +import code.name.monkey.retromusic.Constants.ACTION_PLAY +import code.name.monkey.retromusic.Constants.ACTION_REWIND +import code.name.monkey.retromusic.Constants.ACTION_SKIP +import code.name.monkey.retromusic.Constants.ACTION_STOP +import code.name.monkey.retromusic.Constants.ACTION_TOGGLE_PAUSE + +/** + * Used to control headset playback. + * Single press: pause/resume + * Double press: actionNext track + * Triple press: previous track + */ +class MediaButtonIntentReceiver : BroadcastReceiver() { + + override fun onReceive(context: Context, intent: Intent) { + if (DEBUG) Log.v(TAG, "Received intent: $intent") + if (handleIntent(context, intent) && isOrderedBroadcast) { + abortBroadcast() + } + } + + companion object { + val TAG = MediaButtonIntentReceiver::class.java.simpleName + private val DEBUG = BuildConfig.DEBUG + private val MSG_HEADSET_DOUBLE_CLICK_TIMEOUT = 2 + + private val DOUBLE_CLICK = 400 + + private var wakeLock: WakeLock? = null + private var mClickCounter = 0 + private var mLastClickTime: Long = 0 + + @SuppressLint("HandlerLeak") // false alarm, handler is already static + private val mHandler = object : Handler() { + + override fun handleMessage(msg: Message) { + when (msg.what) { + MSG_HEADSET_DOUBLE_CLICK_TIMEOUT -> { + val clickCount = msg.arg1 + val command: String? + + if (DEBUG) Log.v(TAG, "Handling headset click, count = $clickCount") + when (clickCount) { + 1 -> command = ACTION_TOGGLE_PAUSE + 2 -> command = ACTION_SKIP + 3 -> command = ACTION_REWIND + else -> command = null + } + + if (command != null) { + val context = msg.obj as Context + startService(context, command) + } + } + } + releaseWakeLockIfHandlerIdle() + } + } + + fun handleIntent(context: Context, intent: Intent): Boolean { + val intentAction = intent.action + if (Intent.ACTION_MEDIA_BUTTON == intentAction) { + val event = intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT) + ?: return false + + val keycode = event.keyCode + val action = event.action + val eventTime = if (event.eventTime != 0L) + event.eventTime + else + System.currentTimeMillis() + + var command: String? = null + when (keycode) { + KeyEvent.KEYCODE_MEDIA_STOP -> command = ACTION_STOP + KeyEvent.KEYCODE_HEADSETHOOK, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE -> command = ACTION_TOGGLE_PAUSE + KeyEvent.KEYCODE_MEDIA_NEXT -> command = ACTION_SKIP + KeyEvent.KEYCODE_MEDIA_PREVIOUS -> command = ACTION_REWIND + KeyEvent.KEYCODE_MEDIA_PAUSE -> command = ACTION_PAUSE + KeyEvent.KEYCODE_MEDIA_PLAY -> command = ACTION_PLAY + } + if (command != null) { + if (action == KeyEvent.ACTION_DOWN) { + if (event.repeatCount == 0) { + // Only consider the first event in a sequence, not the repeat events, + // so that we don't trigger in cases where the first event went to + // a different app (e.g. when the user ends a phone call by + // long pressing the headset button) + + // The service may or may not be running, but we need to send it + // a command. + if (keycode == KeyEvent.KEYCODE_HEADSETHOOK || keycode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) { + if (eventTime - mLastClickTime >= DOUBLE_CLICK) { + mClickCounter = 0 + } + + mClickCounter++ + if (DEBUG) Log.v(TAG, "Got headset click, count = $mClickCounter") + mHandler.removeMessages(MSG_HEADSET_DOUBLE_CLICK_TIMEOUT) + + val msg = mHandler.obtainMessage( + MSG_HEADSET_DOUBLE_CLICK_TIMEOUT, mClickCounter, 0, context) + + val delay = (if (mClickCounter < 3) DOUBLE_CLICK else 0).toLong() + if (mClickCounter >= 3) { + mClickCounter = 0 + } + mLastClickTime = eventTime + acquireWakeLockAndSendMessage(context, msg, delay) + } else { + startService(context, command) + } + return true + } + } + } + } + return false + } + + private fun startService(context: Context, command: String?) { + val intent = Intent(context, MusicService::class.java) + intent.action = command + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + context.startForegroundService(intent) + } else { + context.startService(intent) + } + } + + private fun acquireWakeLockAndSendMessage(context: Context, msg: Message, delay: Long) { + if (wakeLock == null) { + val appContext = context.applicationContext + val pm = appContext.getSystemService(Context.POWER_SERVICE) as PowerManager + wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "RetroMusicApp:Wakelock headset button") + wakeLock!!.setReferenceCounted(false) + } + if (DEBUG) Log.v(TAG, "Acquiring wake lock and sending " + msg.what) + // Make sure we don't indefinitely hold the wake lock under any circumstances + wakeLock!!.acquire(10000) + + mHandler.sendMessageDelayed(msg, delay) + } + + private fun releaseWakeLockIfHandlerIdle() { + if (mHandler.hasMessages(MSG_HEADSET_DOUBLE_CLICK_TIMEOUT)) { + if (DEBUG) Log.v(TAG, "Handler still has messages pending, not releasing wake lock") + return + } + + if (wakeLock != null) { + if (DEBUG) Log.v(TAG, "Releasing wake lock") + wakeLock!!.release() + wakeLock = null + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/service/MultiPlayer.java b/app/src/main/java/code/name/monkey/retromusic/service/MultiPlayer.java deleted file mode 100644 index d725f40b..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/service/MultiPlayer.java +++ /dev/null @@ -1,334 +0,0 @@ -package code.name.monkey.retromusic.service; - -import android.content.Context; -import android.content.Intent; -import android.media.AudioManager; -import android.media.MediaPlayer; -import android.media.audiofx.AudioEffect; -import android.net.Uri; -import android.os.PowerManager; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import android.util.Log; -import android.widget.Toast; - -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.service.playback.Playback; -import code.name.monkey.retromusic.util.PreferenceUtil; - -/** - * @author Andrew Neal, Karim Abou Zeid (kabouzeid) - */ -public class MultiPlayer implements Playback, MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener { - public static final String TAG = MultiPlayer.class.getSimpleName(); - - private MediaPlayer mCurrentMediaPlayer = new MediaPlayer(); - private MediaPlayer mNextMediaPlayer; - - private Context context; - @Nullable - private Playback.PlaybackCallbacks callbacks; - - private boolean mIsInitialized = false; - - /** - * Constructor of MultiPlayer - */ - MultiPlayer(final Context context) { - this.context = context; - mCurrentMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK); - } - - /** - * @param path The path of the file, or the http/rtsp URL of the stream - * you want to play - * @return True if the player has been prepared and is - * ready to play, false otherwise - */ - @Override - public boolean setDataSource(@NonNull final String path) { - mIsInitialized = false; - mIsInitialized = setDataSourceImpl(mCurrentMediaPlayer, path); - if (mIsInitialized) { - setNextDataSource(null); - } - return mIsInitialized; - } - - /** - * @param player The {@link MediaPlayer} to use - * @param path The path of the file, or the http/rtsp URL of the stream - * you want to play - * @return True if the player has been prepared and is - * ready to play, false otherwise - */ - private boolean setDataSourceImpl(@NonNull final MediaPlayer player, @NonNull final String path) { - if (context == null) { - return false; - } - try { - player.reset(); - player.setOnPreparedListener(null); - if (path.startsWith("content://")) { - player.setDataSource(context, Uri.parse(path)); - } else { - player.setDataSource(path); - } - player.setAudioStreamType(AudioManager.STREAM_MUSIC); - player.prepare(); - } catch (Exception e) { - return false; - } - player.setOnCompletionListener(this); - player.setOnErrorListener(this); - final Intent intent = new Intent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION); - intent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, getAudioSessionId()); - intent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, context.getPackageName()); - intent.putExtra(AudioEffect.EXTRA_CONTENT_TYPE, AudioEffect.CONTENT_TYPE_MUSIC); - context.sendBroadcast(intent); - return true; - } - - /** - * Set the MediaPlayer to start when this MediaPlayer finishes playback. - * - * @param path The path of the file, or the http/rtsp URL of the stream - * you want to play - */ - @Override - public void setNextDataSource(@Nullable final String path) { - if (context == null) { - return; - } - try { - mCurrentMediaPlayer.setNextMediaPlayer(null); - } catch (IllegalArgumentException e) { - Log.i(TAG, "Next media player is current one, continuing"); - } catch (IllegalStateException e) { - Log.e(TAG, "Media player not initialized!"); - return; - } - if (mNextMediaPlayer != null) { - mNextMediaPlayer.release(); - mNextMediaPlayer = null; - } - if (path == null) { - return; - } - if (PreferenceUtil.getInstance().gaplessPlayback()) { - mNextMediaPlayer = new MediaPlayer(); - mNextMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK); - mNextMediaPlayer.setAudioSessionId(getAudioSessionId()); - if (setDataSourceImpl(mNextMediaPlayer, path)) { - try { - mCurrentMediaPlayer.setNextMediaPlayer(mNextMediaPlayer); - } catch (@NonNull IllegalArgumentException | IllegalStateException e) { - Log.e(TAG, "setNextDataSource: setNextMediaPlayer()", e); - if (mNextMediaPlayer != null) { - mNextMediaPlayer.release(); - mNextMediaPlayer = null; - } - } - } else { - if (mNextMediaPlayer != null) { - mNextMediaPlayer.release(); - mNextMediaPlayer = null; - } - } - } - } - - /** - * Sets the callbacks - * - * @param callbacks The callbacks to use - */ - @Override - public void setCallbacks(@Nullable final Playback.PlaybackCallbacks callbacks) { - this.callbacks = callbacks; - } - - /** - * @return True if the player is ready to go, false otherwise - */ - @Override - public boolean isInitialized() { - return mIsInitialized; - } - - /** - * Starts or resumes playback. - */ - @Override - public boolean start() { - try { - mCurrentMediaPlayer.start(); - return true; - } catch (IllegalStateException e) { - return false; - } - } - - /** - * Resets the MediaPlayer to its uninitialized state. - */ - @Override - public void stop() { - mCurrentMediaPlayer.reset(); - mIsInitialized = false; - } - - /** - * Releases resources associated with this MediaPlayer object. - */ - @Override - public void release() { - stop(); - mCurrentMediaPlayer.release(); - if (mNextMediaPlayer != null) { - mNextMediaPlayer.release(); - } - } - - /** - * Pauses playback. Call start() to resume. - */ - @Override - public boolean pause() { - try { - mCurrentMediaPlayer.pause(); - return true; - } catch (IllegalStateException e) { - return false; - } - } - - /** - * Checks whether the MultiPlayer is playing. - */ - @Override - public boolean isPlaying() { - return mIsInitialized && mCurrentMediaPlayer.isPlaying(); - } - - /** - * Gets the duration of the file. - * - * @return The duration in milliseconds - */ - @Override - public int duration() { - if (!mIsInitialized) { - return -1; - } - try { - return mCurrentMediaPlayer.getDuration(); - } catch (IllegalStateException e) { - return -1; - } - } - - /** - * Gets the current playback position. - * - * @return The current position in milliseconds - */ - @Override - public int position() { - if (!mIsInitialized) { - return -1; - } - try { - return mCurrentMediaPlayer.getCurrentPosition(); - } catch (IllegalStateException e) { - return -1; - } - } - - /** - * Gets the current playback position. - * - * @param whereto The offset in milliseconds from the start to seek to - * @return The offset in milliseconds from the start to seek to - */ - @Override - public int seek(final int whereto) { - try { - mCurrentMediaPlayer.seekTo(whereto); - return whereto; - } catch (IllegalStateException e) { - return -1; - } - } - - @Override - public boolean setVolume(final float vol) { - try { - mCurrentMediaPlayer.setVolume(vol, vol); - return true; - } catch (IllegalStateException e) { - return false; - } - } - - /** - * Sets the audio session ID. - * - * @param sessionId The audio session ID - */ - @Override - public boolean setAudioSessionId(final int sessionId) { - try { - mCurrentMediaPlayer.setAudioSessionId(sessionId); - return true; - } catch (@NonNull IllegalArgumentException | IllegalStateException e) { - return false; - } - } - - /** - * Returns the audio session ID. - * - * @return The current audio session ID. - */ - @Override - public int getAudioSessionId() { - return mCurrentMediaPlayer.getAudioSessionId(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean onError(final MediaPlayer mp, final int what, final int extra) { - mIsInitialized = false; - mCurrentMediaPlayer.release(); - mCurrentMediaPlayer = new MediaPlayer(); - mCurrentMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK); - if (context != null) { - Toast.makeText(context, context.getResources().getString(R.string.unplayable_file), Toast.LENGTH_SHORT).show(); - } - return false; - } - - /** - * {@inheritDoc} - */ - @Override - public void onCompletion(final MediaPlayer mp) { - if (mp == mCurrentMediaPlayer && mNextMediaPlayer != null) { - mIsInitialized = false; - mCurrentMediaPlayer.release(); - mCurrentMediaPlayer = mNextMediaPlayer; - mIsInitialized = true; - mNextMediaPlayer = null; - if (callbacks != null) - callbacks.onTrackWentToNext(); - } else { - if (callbacks != null) - callbacks.onTrackEnded(); - } - } - - -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/service/MultiPlayer.kt b/app/src/main/java/code/name/monkey/retromusic/service/MultiPlayer.kt new file mode 100644 index 00000000..ba24b958 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/service/MultiPlayer.kt @@ -0,0 +1,333 @@ +package code.name.monkey.retromusic.service + +import android.content.Context +import android.content.Intent +import android.media.AudioManager +import android.media.MediaPlayer +import android.media.audiofx.AudioEffect +import android.net.Uri +import android.os.PowerManager +import android.util.Log +import android.widget.Toast + +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.service.playback.Playback +import code.name.monkey.retromusic.util.PreferenceUtil + +/** + * @author Andrew Neal, Karim Abou Zeid (kabouzeid) + */ +class MultiPlayer +/** + * Constructor of `MultiPlayer` + */ +internal constructor(private val context: Context?) : Playback, MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener { + + private var mCurrentMediaPlayer = MediaPlayer() + private var mNextMediaPlayer: MediaPlayer? = null + private var callbacks: Playback.PlaybackCallbacks? = null + + private var mIsInitialized = false + + init { + mCurrentMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK) + } + + /** + * @param path The path of the file, or the http/rtsp URL of the stream + * you want to play + * @return True if the `player` has been prepared and is + * ready to play, false otherwise + */ + override fun setDataSource(path: String): Boolean { + mIsInitialized = false + mIsInitialized = setDataSourceImpl(mCurrentMediaPlayer, path) + if (mIsInitialized) { + setNextDataSource(null) + } + return mIsInitialized + } + + /** + * @param player The [MediaPlayer] to use + * @param path The path of the file, or the http/rtsp URL of the stream + * you want to play + * @return True if the `player` has been prepared and is + * ready to play, false otherwise + */ + private fun setDataSourceImpl(player: MediaPlayer, path: String): Boolean { + if (context == null) { + return false + } + try { + player.reset() + player.setOnPreparedListener(null) + if (path.startsWith("content://")) { + player.setDataSource(context, Uri.parse(path)) + } else { + player.setDataSource(path) + } + player.setAudioStreamType(AudioManager.STREAM_MUSIC) + player.prepare() + } catch (e: Exception) { + return false + } + + player.setOnCompletionListener(this) + player.setOnErrorListener(this) + val intent = Intent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION) + intent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, audioSessionId) + intent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, context.packageName) + intent.putExtra(AudioEffect.EXTRA_CONTENT_TYPE, AudioEffect.CONTENT_TYPE_MUSIC) + context.sendBroadcast(intent) + return true + } + + /** + * Set the MediaPlayer to start when this MediaPlayer finishes playback. + * + * @param path The path of the file, or the http/rtsp URL of the stream + * you want to play + */ + override fun setNextDataSource(path: String?) { + if (context == null) { + return + } + try { + mCurrentMediaPlayer.setNextMediaPlayer(null) + } catch (e: IllegalArgumentException) { + Log.i(TAG, "Next media player is current one, continuing") + } catch (e: IllegalStateException) { + Log.e(TAG, "Media player not initialized!") + return + } + + if (mNextMediaPlayer != null) { + mNextMediaPlayer!!.release() + mNextMediaPlayer = null + } + if (path == null) { + return + } + if (PreferenceUtil.getInstance().gaplessPlayback()) { + mNextMediaPlayer = MediaPlayer() + mNextMediaPlayer!!.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK) + mNextMediaPlayer!!.audioSessionId = audioSessionId + if (setDataSourceImpl(mNextMediaPlayer!!, path)) { + try { + mCurrentMediaPlayer.setNextMediaPlayer(mNextMediaPlayer) + } catch (e: IllegalArgumentException) { + Log.e(TAG, "setNextDataSource: setNextMediaPlayer()", e) + if (mNextMediaPlayer != null) { + mNextMediaPlayer!!.release() + mNextMediaPlayer = null + } + } catch (e: IllegalStateException) { + Log.e(TAG, "setNextDataSource: setNextMediaPlayer()", e) + if (mNextMediaPlayer != null) { + mNextMediaPlayer!!.release() + mNextMediaPlayer = null + } + } + + } else { + if (mNextMediaPlayer != null) { + mNextMediaPlayer!!.release() + mNextMediaPlayer = null + } + } + } + } + + /** + * Sets the callbacks + * + * @param callbacks The callbacks to use + */ + override fun setCallbacks(callbacks: Playback.PlaybackCallbacks?) { + this.callbacks = callbacks + } + + /** + * @return True if the player is ready to go, false otherwise + */ + override fun isInitialized(): Boolean { + return mIsInitialized + } + + /** + * Starts or resumes playback. + */ + override fun start(): Boolean { + try { + mCurrentMediaPlayer.start() + return true + } catch (e: IllegalStateException) { + return false + } + + } + + /** + * Resets the MediaPlayer to its uninitialized state. + */ + override fun stop() { + mCurrentMediaPlayer.reset() + mIsInitialized = false + } + + /** + * Releases resources associated with this MediaPlayer object. + */ + override fun release() { + stop() + mCurrentMediaPlayer.release() + if (mNextMediaPlayer != null) { + mNextMediaPlayer!!.release() + } + } + + /** + * Pauses playback. Call start() to resume. + */ + override fun pause(): Boolean { + try { + mCurrentMediaPlayer.pause() + return true + } catch (e: IllegalStateException) { + return false + } + + } + + /** + * Checks whether the MultiPlayer is playing. + */ + override fun isPlaying(): Boolean { + return mIsInitialized && mCurrentMediaPlayer.isPlaying + } + + /** + * Gets the duration of the file. + * + * @return The duration in milliseconds + */ + override fun duration(): Int { + if (!mIsInitialized) { + return -1 + } + try { + return mCurrentMediaPlayer.duration + } catch (e: IllegalStateException) { + return -1 + } + + } + + /** + * Gets the current playback position. + * + * @return The current position in milliseconds + */ + override fun position(): Int { + if (!mIsInitialized) { + return -1 + } + try { + return mCurrentMediaPlayer.currentPosition + } catch (e: IllegalStateException) { + return -1 + } + + } + + /** + * Gets the current playback position. + * + * @param whereto The offset in milliseconds from the start to seek to + * @return The offset in milliseconds from the start to seek to + */ + override fun seek(whereto: Int): Int { + try { + mCurrentMediaPlayer.seekTo(whereto) + return whereto + } catch (e: IllegalStateException) { + return -1 + } + + } + + override fun setVolume(vol: Float): Boolean { + try { + mCurrentMediaPlayer.setVolume(vol, vol) + return true + } catch (e: IllegalStateException) { + return false + } + + } + + /** + * Sets the audio session ID. + * + * @param sessionId The audio session ID + */ + override fun setAudioSessionId(sessionId: Int): Boolean { + try { + mCurrentMediaPlayer.audioSessionId = sessionId + return true + } catch (e: IllegalArgumentException) { + return false + } catch (e: IllegalStateException) { + return false + } + + } + + /** + * Returns the audio session ID. + * + * @return The current audio session ID. + */ + override fun getAudioSessionId(): Int { + return mCurrentMediaPlayer.audioSessionId + } + + /** + * {@inheritDoc} + */ + override fun onError(mp: MediaPlayer, what: Int, extra: Int): Boolean { + mIsInitialized = false + mCurrentMediaPlayer.release() + mCurrentMediaPlayer = MediaPlayer() + mCurrentMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK) + if (context != null) { + Toast.makeText(context, context.resources.getString(R.string.unplayable_file), Toast.LENGTH_SHORT).show() + } + return false + } + + /** + * {@inheritDoc} + */ + override fun onCompletion(mp: MediaPlayer) { + if (mp === mCurrentMediaPlayer && mNextMediaPlayer != null) { + mIsInitialized = false + mCurrentMediaPlayer.release() + mCurrentMediaPlayer = mNextMediaPlayer as MediaPlayer + mIsInitialized = true + mNextMediaPlayer = null + if (callbacks != null) + callbacks!!.onTrackWentToNext() + } else { + if (callbacks != null) + callbacks!!.onTrackEnded() + } + } + + companion object { + val TAG: String = MultiPlayer::class.java.simpleName + } + + +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/service/MusicService.java b/app/src/main/java/code/name/monkey/retromusic/service/MusicService.java deleted file mode 100644 index 72326e0a..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/service/MusicService.java +++ /dev/null @@ -1,1445 +0,0 @@ -package code.name.monkey.retromusic.service; - -import android.app.PendingIntent; -import android.app.Service; -import android.appwidget.AppWidgetManager; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.SharedPreferences; -import android.database.ContentObserver; -import android.graphics.Bitmap; -import android.graphics.Point; -import android.graphics.drawable.Drawable; -import android.media.AudioManager; -import android.media.audiofx.AudioEffect; -import android.media.session.MediaSession; -import android.os.Binder; -import android.os.Build; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.IBinder; -import android.os.Looper; -import android.os.Message; -import android.os.PowerManager; -import android.os.Process; -import android.preference.PreferenceManager; -import android.provider.MediaStore; -import android.support.v4.media.MediaMetadataCompat; -import android.support.v4.media.session.MediaSessionCompat; -import android.support.v4.media.session.PlaybackStateCompat; -import android.telephony.PhoneStateListener; -import android.telephony.TelephonyManager; -import android.util.Log; -import android.widget.Toast; - -import com.bumptech.glide.BitmapRequestBuilder; -import com.bumptech.glide.Glide; -import com.bumptech.glide.request.animation.GlideAnimation; -import com.bumptech.glide.request.target.SimpleTarget; - -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.List; -import java.util.Random; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.appwidgets.AppWidgetBig; -import code.name.monkey.retromusic.appwidgets.AppWidgetCard; -import code.name.monkey.retromusic.appwidgets.AppWidgetClassic; -import code.name.monkey.retromusic.appwidgets.AppWidgetSmall; -import code.name.monkey.retromusic.glide.BlurTransformation; -import code.name.monkey.retromusic.glide.SongGlideRequest; -import code.name.monkey.retromusic.helper.ShuffleHelper; -import code.name.monkey.retromusic.helper.StopWatch; -import code.name.monkey.retromusic.loaders.PlaylistSongsLoader; -import code.name.monkey.retromusic.model.AbsCustomPlaylist; -import code.name.monkey.retromusic.model.Playlist; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.providers.HistoryStore; -import code.name.monkey.retromusic.providers.MusicPlaybackQueueStore; -import code.name.monkey.retromusic.providers.SongPlayCountStore; -import code.name.monkey.retromusic.service.notification.PlayingNotification; -import code.name.monkey.retromusic.service.notification.PlayingNotificationImpl24; -import code.name.monkey.retromusic.service.notification.PlayingNotificationOreo; -import code.name.monkey.retromusic.service.playback.Playback; -import code.name.monkey.retromusic.util.MusicUtil; -import code.name.monkey.retromusic.util.PreferenceUtil; -import code.name.monkey.retromusic.util.RetroUtil; - -import static code.name.monkey.retromusic.Constants.ACTION_PAUSE; -import static code.name.monkey.retromusic.Constants.ACTION_PLAY; -import static code.name.monkey.retromusic.Constants.ACTION_PLAY_PLAYLIST; -import static code.name.monkey.retromusic.Constants.ACTION_QUIT; -import static code.name.monkey.retromusic.Constants.ACTION_REWIND; -import static code.name.monkey.retromusic.Constants.ACTION_SKIP; -import static code.name.monkey.retromusic.Constants.ACTION_STOP; -import static code.name.monkey.retromusic.Constants.ACTION_TOGGLE_PAUSE; -import static code.name.monkey.retromusic.Constants.APP_WIDGET_UPDATE; -import static code.name.monkey.retromusic.Constants.EXTRA_APP_WIDGET_NAME; -import static code.name.monkey.retromusic.Constants.INTENT_EXTRA_PLAYLIST; -import static code.name.monkey.retromusic.Constants.INTENT_EXTRA_SHUFFLE_MODE; -import static code.name.monkey.retromusic.Constants.MEDIA_STORE_CHANGED; -import static code.name.monkey.retromusic.Constants.META_CHANGED; -import static code.name.monkey.retromusic.Constants.MUSIC_PACKAGE_NAME; -import static code.name.monkey.retromusic.Constants.PLAY_STATE_CHANGED; -import static code.name.monkey.retromusic.Constants.QUEUE_CHANGED; -import static code.name.monkey.retromusic.Constants.REPEAT_MODE_CHANGED; -import static code.name.monkey.retromusic.Constants.RETRO_MUSIC_PACKAGE_NAME; -import static code.name.monkey.retromusic.Constants.SHUFFLE_MODE_CHANGED; - -/** - * @author Karim Abou Zeid (kabouzeid), Andrew Neal - */ -public class MusicService extends Service implements SharedPreferences.OnSharedPreferenceChangeListener, Playback.PlaybackCallbacks { - public static final String TAG = MusicService.class.getSimpleName(); - - public static final String SAVED_POSITION = "POSITION"; - public static final String SAVED_POSITION_IN_TRACK = "POSITION_IN_TRACK"; - public static final String SAVED_SHUFFLE_MODE = "SHUFFLE_MODE"; - public static final String SAVED_REPEAT_MODE = "REPEAT_MODE"; - - public static final int RELEASE_WAKELOCK = 0; - public static final int TRACK_ENDED = 1; - public static final int TRACK_WENT_TO_NEXT = 2; - public static final int PLAY_SONG = 3; - public static final int PREPARE_NEXT = 4; - public static final int SET_POSITION = 5; - public static final int RESTORE_QUEUES = 9; - public static final int SHUFFLE_MODE_NONE = 0; - public static final int SHUFFLE_MODE_SHUFFLE = 1; - public static final int REPEAT_MODE_NONE = 0; - public static final int REPEAT_MODE_ALL = 1; - public static final int REPEAT_MODE_THIS = 2; - public static final int SAVE_QUEUES = 0; - private static final int FOCUS_CHANGE = 6; - private static final int DUCK = 7; - private static final int UNDUCK = 8; - private static final long MEDIA_SESSION_ACTIONS = PlaybackStateCompat.ACTION_PLAY - | PlaybackStateCompat.ACTION_PAUSE - | PlaybackStateCompat.ACTION_PLAY_PAUSE - | PlaybackStateCompat.ACTION_SKIP_TO_NEXT - | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS - | PlaybackStateCompat.ACTION_STOP - | PlaybackStateCompat.ACTION_SEEK_TO; - private final IBinder musicBind = new MusicBinder(); - private AppWidgetBig appWidgetBig = AppWidgetBig.getInstance(); - private AppWidgetClassic appWidgetClassic = AppWidgetClassic.getInstance(); - private AppWidgetSmall appWidgetSmall = AppWidgetSmall.getInstance(); - private AppWidgetCard appWidgetCard = AppWidgetCard.getInstance(); - private Playback playback; - private ArrayList playingQueue = new ArrayList<>(); - private ArrayList originalPlayingQueue = new ArrayList<>(); - private int position = -1; - private int nextPosition = -1; - private int shuffleMode; - private int repeatMode; - private boolean queuesRestored; - private boolean pausedByTransientLossOfFocus; - private final BroadcastReceiver becomingNoisyReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, @NonNull Intent intent) { - if (intent.getAction() != null && intent.getAction().equals(AudioManager.ACTION_AUDIO_BECOMING_NOISY)) { - pause(); - } - } - }; - private PlayingNotification playingNotification; - private AudioManager audioManager; - @SuppressWarnings("deprecation") - private MediaSessionCompat mediaSession; - private PowerManager.WakeLock wakeLock; - private PlaybackHandler playerHandler; - private final AudioManager.OnAudioFocusChangeListener audioFocusListener = new AudioManager.OnAudioFocusChangeListener() { - @Override - public void onAudioFocusChange(final int focusChange) { - playerHandler.obtainMessage(FOCUS_CHANGE, focusChange, 0).sendToTarget(); - } - }; - private QueueSaveHandler queueSaveHandler; - private HandlerThread musicPlayerHandlerThread; - private HandlerThread queueSaveHandlerThread; - private SongPlayCountHelper songPlayCountHelper = new SongPlayCountHelper(); - private ThrottledSeekHandler throttledSeekHandler; - private boolean becomingNoisyReceiverRegistered; - private IntentFilter becomingNoisyReceiverIntentFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY); - private ContentObserver mediaStoreObserver; - private boolean notHandledMetaChangedForCurrentTrack; - private PhoneStateListener phoneStateListener = new PhoneStateListener() { - @Override - public void onCallStateChanged(int state, String incomingNumber) { - switch (state) { - case TelephonyManager.CALL_STATE_IDLE: - //Not in call: Play music - play(); - break; - case TelephonyManager.CALL_STATE_RINGING: - case TelephonyManager.CALL_STATE_OFFHOOK: - //A call is dialing, active or on hold - pause(); - break; - default: - } - super.onCallStateChanged(state, incomingNumber); - } - }; - private boolean isServiceBound; - private Handler uiThreadHandler; - private final BroadcastReceiver widgetIntentReceiver = new BroadcastReceiver() { - @Override - public void onReceive(final Context context, final Intent intent) { - final String command = intent.getStringExtra(EXTRA_APP_WIDGET_NAME); - - if (AppWidgetClassic.NAME.equals(command)) { - final int[] ids = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS); - appWidgetClassic.performUpdate(MusicService.this, ids); - } else if (AppWidgetSmall.NAME.equals(command)) { - final int[] ids = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS); - appWidgetSmall.performUpdate(MusicService.this, ids); - } else if (AppWidgetBig.NAME.equals(command)) { - final int[] ids = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS); - appWidgetBig.performUpdate(MusicService.this, ids); - } else if (AppWidgetCard.NAME.equals(command)) { - final int[] ids = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS); - appWidgetCard.performUpdate(MusicService.this, ids); - } - } - }; - private IntentFilter headsetReceiverIntentFilter = new IntentFilter(Intent.ACTION_HEADSET_PLUG); - private boolean headsetReceiverRegistered = false; - private BroadcastReceiver headsetReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (action != null) { - switch (action) { - case Intent.ACTION_HEADSET_PLUG: - int state = intent.getIntExtra("state", -1); - switch (state) { - case 0: - Log.d(TAG, "Headset unplugged"); - pause(); - break; - case 1: - Log.d(TAG, "Headset plugged"); - play(); - break; - } - break; - } - } - } - }; - - private static String getTrackUri(@NonNull Song song) { - return MusicUtil.getSongFileUri(song.id).toString(); - } - - private static Bitmap copy(Bitmap bitmap) { - Bitmap.Config config = bitmap.getConfig(); - if (config == null) { - config = Bitmap.Config.RGB_565; - } - try { - return bitmap.copy(config, false); - } catch (OutOfMemoryError e) { - e.printStackTrace(); - return null; - } - } - - - @Override - public void onCreate() { - super.onCreate(); - - final TelephonyManager telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE); - if (telephonyManager != null) { - telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE); - } - - final PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); - if (powerManager != null) { - wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getClass().getName()); - } - wakeLock.setReferenceCounted(false); - - musicPlayerHandlerThread = new HandlerThread("PlaybackHandler"); - musicPlayerHandlerThread.start(); - playerHandler = new PlaybackHandler(this, musicPlayerHandlerThread.getLooper()); - - playback = new MultiPlayer(this); - playback.setCallbacks(this); - - setupMediaSession(); - - // queue saving needs to run on a separate thread so that it doesn't block the playback handler events - queueSaveHandlerThread = new HandlerThread("QueueSaveHandler", Process.THREAD_PRIORITY_BACKGROUND); - queueSaveHandlerThread.start(); - queueSaveHandler = new QueueSaveHandler(this, queueSaveHandlerThread.getLooper()); - - uiThreadHandler = new Handler(); - - registerReceiver(widgetIntentReceiver, new IntentFilter(APP_WIDGET_UPDATE)); - - initNotification(); - - mediaStoreObserver = new MediaStoreObserver(playerHandler); - throttledSeekHandler = new ThrottledSeekHandler(playerHandler); - getContentResolver().registerContentObserver( - MediaStore.Audio.Media.INTERNAL_CONTENT_URI, true, mediaStoreObserver); - getContentResolver().registerContentObserver( - MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, true, mediaStoreObserver); - - PreferenceUtil.getInstance().registerOnSharedPreferenceChangedListener(this); - - restoreState(); - - mediaSession.setActive(true); - - sendBroadcast(new Intent("code.name.monkey.retromusic.RETRO_MUSIC_SERVICE_CREATED")); - - registerHeadsetEvents(); - - - } - - private AudioManager getAudioManager() { - if (audioManager == null) { - audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); - } - return audioManager; - } - - private void setupMediaSession() { - ComponentName mediaButtonReceiverComponentName = new ComponentName(getApplicationContext(), MediaButtonIntentReceiver.class); - - Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); - mediaButtonIntent.setComponent(mediaButtonReceiverComponentName); - - - PendingIntent mediaButtonReceiverPendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, mediaButtonIntent, 0); - - mediaSession = new MediaSessionCompat(this, "RetroMusicPlayer", mediaButtonReceiverComponentName, mediaButtonReceiverPendingIntent); - mediaSession.setCallback(new MediaSessionCompat.Callback() { - @Override - public void onPlay() { - play(); - } - - @Override - public void onPause() { - pause(); - } - - @Override - public void onSkipToNext() { - playNextSong(true); - } - - @Override - public void onSkipToPrevious() { - back(true); - } - - @Override - public void onStop() { - quit(); - } - - @Override - public void onSeekTo(long pos) { - seek((int) pos); - } - - @Override - public boolean onMediaButtonEvent(Intent mediaButtonEvent) { - return MediaButtonIntentReceiver.handleIntent(MusicService.this, mediaButtonEvent); - } - }); - - mediaSession.setFlags(MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS - | MediaSession.FLAG_HANDLES_MEDIA_BUTTONS); - - mediaSession.setMediaButtonReceiver(mediaButtonReceiverPendingIntent); - } - - @Override - public int onStartCommand(@Nullable Intent intent, int flags, int startId) { - if (intent != null) { - if (intent.getAction() != null) { - restoreQueuesAndPositionIfNecessary(); - String action = intent.getAction(); - switch (action) { - case ACTION_TOGGLE_PAUSE: - if (isPlaying()) { - pause(); - } else { - play(); - } - break; - case ACTION_PAUSE: - pause(); - break; - case ACTION_PLAY: - play(); - break; - case ACTION_PLAY_PLAYLIST: - Playlist playlist = intent.getParcelableExtra(INTENT_EXTRA_PLAYLIST); - int shuffleMode = intent.getIntExtra(INTENT_EXTRA_SHUFFLE_MODE, getShuffleMode()); - if (playlist != null) { - ArrayList playlistSongs; - if (playlist instanceof AbsCustomPlaylist) { - playlistSongs = ((AbsCustomPlaylist) playlist).getSongs(getApplicationContext()).blockingFirst(); - } else { - //noinspection unchecked - playlistSongs = PlaylistSongsLoader.getPlaylistSongList(getApplicationContext(), playlist.id).blockingFirst(); - } - if (!playlistSongs.isEmpty()) { - if (shuffleMode == SHUFFLE_MODE_SHUFFLE) { - int startPosition; - startPosition = new Random().nextInt(playlistSongs.size()); - openQueue(playlistSongs, startPosition, true); - setShuffleMode(shuffleMode); - } else { - openQueue(playlistSongs, 0, true); - } - } else { - Toast.makeText(getApplicationContext(), R.string.playlist_is_empty, Toast.LENGTH_LONG).show(); - } - } else { - Toast.makeText(getApplicationContext(), R.string.playlist_is_empty, Toast.LENGTH_LONG).show(); - } - break; - case ACTION_REWIND: - back(true); - break; - case ACTION_SKIP: - playNextSong(true); - break; - case ACTION_STOP: - case ACTION_QUIT: - return quit(); - } - } - } - - return START_STICKY; - } - - - @Override - public void onDestroy() { - unregisterReceiver(widgetIntentReceiver); - if (becomingNoisyReceiverRegistered) { - unregisterReceiver(becomingNoisyReceiver); - becomingNoisyReceiverRegistered = false; - } - if (headsetReceiverRegistered) { - unregisterReceiver(headsetReceiver); - headsetReceiverRegistered = false; - } - mediaSession.setActive(false); - quit(); - releaseResources(); - getContentResolver().unregisterContentObserver(mediaStoreObserver); - PreferenceUtil.getInstance().unregisterOnSharedPreferenceChangedListener(this); - wakeLock.release(); - - sendBroadcast(new Intent("code.name.monkey.retromusic.RETRO_MUSIC_MUSIC_SERVICE_DESTROYED")); - } - - @Override - public IBinder onBind(Intent intent) { - isServiceBound = true; - return musicBind; - } - - @Override - public void onRebind(Intent intent) { - isServiceBound = true; - } - - @Override - public boolean onUnbind(Intent intent) { - isServiceBound = false; - if (!isPlaying()) { - stopSelf(); - } - return true; - } - - private void saveQueuesImpl() { - MusicPlaybackQueueStore.getInstance(this).saveQueues(playingQueue, originalPlayingQueue); - } - - private void savePosition() { - PreferenceManager.getDefaultSharedPreferences(this).edit().putInt(SAVED_POSITION, getPosition()).apply(); - } - - private void savePositionInTrack() { - PreferenceManager.getDefaultSharedPreferences(this).edit().putInt(SAVED_POSITION_IN_TRACK, getSongProgressMillis()).apply(); - } - - public void saveState() { - saveQueues(); - savePosition(); - savePositionInTrack(); - } - - private void saveQueues() { - queueSaveHandler.removeMessages(SAVE_QUEUES); - queueSaveHandler.sendEmptyMessage(SAVE_QUEUES); - } - - private void restoreState() { - shuffleMode = PreferenceManager.getDefaultSharedPreferences(this).getInt(SAVED_SHUFFLE_MODE, 0); - repeatMode = PreferenceManager.getDefaultSharedPreferences(this).getInt(SAVED_REPEAT_MODE, 0); - handleAndSendChangeInternal(SHUFFLE_MODE_CHANGED); - handleAndSendChangeInternal(REPEAT_MODE_CHANGED); - - playerHandler.removeMessages(RESTORE_QUEUES); - playerHandler.sendEmptyMessage(RESTORE_QUEUES); - } - - private synchronized void restoreQueuesAndPositionIfNecessary() { - if (!queuesRestored && playingQueue.isEmpty()) { - ArrayList restoredQueue = MusicPlaybackQueueStore.getInstance(this).getSavedPlayingQueue() - .blockingFirst(); - - ArrayList restoredOriginalQueue = MusicPlaybackQueueStore.getInstance(this).getSavedOriginalPlayingQueue() - .blockingFirst(); - - int restoredPosition = PreferenceManager.getDefaultSharedPreferences(this).getInt(SAVED_POSITION, -1); - int restoredPositionInTrack = PreferenceManager.getDefaultSharedPreferences(this).getInt(SAVED_POSITION_IN_TRACK, -1); - - if (restoredQueue.size() > 0 && restoredQueue.size() == restoredOriginalQueue.size() && restoredPosition != -1) { - this.originalPlayingQueue = restoredOriginalQueue; - this.playingQueue = restoredQueue; - - position = restoredPosition; - openCurrent(); - prepareNext(); - - if (restoredPositionInTrack > 0) seek(restoredPositionInTrack); - - notHandledMetaChangedForCurrentTrack = true; - sendChangeInternal(META_CHANGED); - sendChangeInternal(QUEUE_CHANGED); - } - } - queuesRestored = true; - } - - private int quit() { - pause(); - playingNotification.stop(); - - if (isServiceBound) { - return START_STICKY; - } else { - closeAudioEffectSession(); - getAudioManager().abandonAudioFocus(audioFocusListener); - stopSelf(); - return START_NOT_STICKY; - } - } - - private void releaseResources() { - playerHandler.removeCallbacksAndMessages(null); - musicPlayerHandlerThread.quitSafely(); - queueSaveHandler.removeCallbacksAndMessages(null); - queueSaveHandlerThread.quitSafely(); - playback.release(); - playback = null; - mediaSession.release(); - } - - public boolean isPlaying() { - return playback != null && playback.isPlaying(); - } - - public int getPosition() { - return position; - } - - public void setPosition(final int position) { - // handle this on the handlers thread to avoid blocking the ui thread - playerHandler.removeMessages(SET_POSITION); - playerHandler.obtainMessage(SET_POSITION, position, 0).sendToTarget(); - } - - public void playNextSong(boolean force) { - playSongAt(getNextPosition(force)); - } - - private boolean openTrackAndPrepareNextAt(int position) { - synchronized (this) { - this.position = position; - boolean prepared = openCurrent(); - if (prepared) prepareNextImpl(); - notifyChange(META_CHANGED); - notHandledMetaChangedForCurrentTrack = false; - return prepared; - } - } - - private boolean openCurrent() { - synchronized (this) { - try { - return playback.setDataSource(getTrackUri(getCurrentSong())); - } catch (Exception e) { - return false; - } - } - } - - private void prepareNext() { - playerHandler.removeMessages(PREPARE_NEXT); - playerHandler.obtainMessage(PREPARE_NEXT).sendToTarget(); - } - - private boolean prepareNextImpl() { - synchronized (this) { - try { - int nextPosition = getNextPosition(false); - playback.setNextDataSource(getTrackUri(getSongAt(nextPosition))); - this.nextPosition = nextPosition; - return true; - } catch (Exception e) { - return false; - } - } - } - - private void closeAudioEffectSession() { - final Intent audioEffectsIntent = new Intent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION); - audioEffectsIntent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, playback.getAudioSessionId()); - audioEffectsIntent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, getPackageName()); - sendBroadcast(audioEffectsIntent); - } - - private boolean requestFocus() { - return (getAudioManager().requestAudioFocus(audioFocusListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN) == AudioManager.AUDIOFOCUS_REQUEST_GRANTED); - } - - public void initNotification() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && !PreferenceUtil.getInstance().classicNotification()) { - playingNotification = new PlayingNotificationImpl24(); - } else { - playingNotification = new PlayingNotificationOreo(); - } - playingNotification.init(this); - } - - public void updateNotification() { - if (playingNotification != null && getCurrentSong().id != -1) { - playingNotification.update(); - } - } - - private void updateMediaSessionPlaybackState() { - mediaSession.setPlaybackState( - new PlaybackStateCompat.Builder() - .setActions(MEDIA_SESSION_ACTIONS) - .setState(isPlaying() ? PlaybackStateCompat.STATE_PLAYING : PlaybackStateCompat.STATE_PAUSED, - getPosition(), 1) - .build()); - } - - private void updateMediaSessionMetaData() { - final Song song = getCurrentSong(); - - if (song.id == -1) { - mediaSession.setMetadata(null); - return; - } - - final MediaMetadataCompat.Builder metaData = new MediaMetadataCompat.Builder() - .putString(MediaMetadataCompat.METADATA_KEY_ARTIST, song.artistName) - .putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ARTIST, song.artistName) - .putString(MediaMetadataCompat.METADATA_KEY_ALBUM, song.albumName) - .putString(MediaMetadataCompat.METADATA_KEY_TITLE, song.title) - .putLong(MediaMetadataCompat.METADATA_KEY_DURATION, song.duration) - .putLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER, getPosition() + 1) - .putLong(MediaMetadataCompat.METADATA_KEY_YEAR, song.year) - .putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, null); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - metaData.putLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS, getPlayingQueue().size()); - } - - if (PreferenceUtil.getInstance().albumArtOnLockscreen()) { - final Point screenSize = RetroUtil.getScreenSize(MusicService.this); - final BitmapRequestBuilder request = SongGlideRequest.Builder.from(Glide.with(MusicService.this), song) - .checkIgnoreMediaStore(MusicService.this) - .asBitmap().build(); - if (PreferenceUtil.getInstance().blurredAlbumArt()) { - request.transform(new BlurTransformation.Builder(MusicService.this).build()); - } - runOnUiThread(new Runnable() { - @Override - public void run() { - request.into(new SimpleTarget(screenSize.x, screenSize.y) { - @Override - public void onLoadFailed(Exception e, Drawable errorDrawable) { - super.onLoadFailed(e, errorDrawable); - mediaSession.setMetadata(metaData.build()); - } - - @Override - public void onResourceReady(Bitmap resource, GlideAnimation glideAnimation) { - metaData.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, copy(resource)); - mediaSession.setMetadata(metaData.build()); - } - }); - } - }); - } else { - mediaSession.setMetadata(metaData.build()); - } - } - - public void runOnUiThread(Runnable runnable) { - uiThreadHandler.post(runnable); - } - - public Song getCurrentSong() { - return getSongAt(getPosition()); - } - - public Song getSongAt(int position) { - if (position >= 0 && position < getPlayingQueue().size()) { - return getPlayingQueue().get(position); - } else { - return Song.EMPTY_SONG; - } - } - - public int getNextPosition(boolean force) { - int position = getPosition() + 1; - switch (getRepeatMode()) { - case REPEAT_MODE_ALL: - if (isLastTrack()) { - position = 0; - } - break; - case REPEAT_MODE_THIS: - if (force) { - if (isLastTrack()) { - position = 0; - } - } else { - position -= 1; - } - break; - default: - case REPEAT_MODE_NONE: - if (isLastTrack()) { - position -= 1; - } - break; - } - return position; - } - - private boolean isLastTrack() { - return getPosition() == getPlayingQueue().size() - 1; - } - - public ArrayList getPlayingQueue() { - return playingQueue; - } - - public int getRepeatMode() { - return repeatMode; - } - - public void setRepeatMode(final int repeatMode) { - switch (repeatMode) { - case REPEAT_MODE_NONE: - case REPEAT_MODE_ALL: - case REPEAT_MODE_THIS: - this.repeatMode = repeatMode; - PreferenceManager.getDefaultSharedPreferences(this).edit() - .putInt(SAVED_REPEAT_MODE, repeatMode) - .apply(); - prepareNext(); - handleAndSendChangeInternal(REPEAT_MODE_CHANGED); - break; - } - } - - public void openQueue(@Nullable final ArrayList playingQueue, final int startPosition, final boolean startPlaying) { - if (playingQueue != null && !playingQueue.isEmpty() && startPosition >= 0 && startPosition < playingQueue.size()) { - // it is important to copy the playing queue here first as we might add/remove songs later - originalPlayingQueue = new ArrayList<>(playingQueue); - this.playingQueue = new ArrayList<>(originalPlayingQueue); - - int position = startPosition; - if (shuffleMode == SHUFFLE_MODE_SHUFFLE) { - ShuffleHelper.makeShuffleList(this.playingQueue, startPosition); - position = 0; - } - if (startPlaying) { - playSongAt(position); - } else { - setPosition(position); - } - notifyChange(QUEUE_CHANGED); - } - } - - public void addSong(int position, Song song) { - playingQueue.add(position, song); - originalPlayingQueue.add(position, song); - notifyChange(QUEUE_CHANGED); - } - - public void addSong(Song song) { - playingQueue.add(song); - originalPlayingQueue.add(song); - notifyChange(QUEUE_CHANGED); - } - - public void addSongs(int position, List songs) { - playingQueue.addAll(position, songs); - originalPlayingQueue.addAll(position, songs); - notifyChange(QUEUE_CHANGED); - } - - public void addSongs(List songs) { - playingQueue.addAll(songs); - originalPlayingQueue.addAll(songs); - notifyChange(QUEUE_CHANGED); - } - - public void removeSong(int position) { - if (getShuffleMode() == SHUFFLE_MODE_NONE) { - playingQueue.remove(position); - originalPlayingQueue.remove(position); - } else { - originalPlayingQueue.remove(playingQueue.remove(position)); - } - - rePosition(position); - - notifyChange(QUEUE_CHANGED); - } - - public void removeSong(@NonNull Song song) { - for (int i = 0; i < playingQueue.size(); i++) { - if (playingQueue.get(i).id == song.id) { - playingQueue.remove(i); - rePosition(i); - } - } - for (int i = 0; i < originalPlayingQueue.size(); i++) { - if (originalPlayingQueue.get(i).id == song.id) { - originalPlayingQueue.remove(i); - } - } - notifyChange(QUEUE_CHANGED); - } - - private void rePosition(int deletedPosition) { - int currentPosition = getPosition(); - if (deletedPosition < currentPosition) { - position = currentPosition - 1; - } else if (deletedPosition == currentPosition) { - if (playingQueue.size() > deletedPosition) { - setPosition(position); - } else { - setPosition(position - 1); - } - } - } - - public void moveSong(int from, int to) { - if (from == to) return; - final int currentPosition = getPosition(); - Song songToMove = playingQueue.remove(from); - playingQueue.add(to, songToMove); - if (getShuffleMode() == SHUFFLE_MODE_NONE) { - Song tmpSong = originalPlayingQueue.remove(from); - originalPlayingQueue.add(to, tmpSong); - } - if (from > currentPosition && to <= currentPosition) { - position = currentPosition + 1; - } else if (from < currentPosition && to >= currentPosition) { - position = currentPosition - 1; - } else if (from == currentPosition) { - position = to; - } - notifyChange(QUEUE_CHANGED); - } - - public void clearQueue() { - playingQueue.clear(); - originalPlayingQueue.clear(); - - setPosition(-1); - notifyChange(QUEUE_CHANGED); - } - - public void playSongAt(final int position) { - // handle this on the handlers thread to avoid blocking the ui thread - playerHandler.removeMessages(PLAY_SONG); - playerHandler.obtainMessage(PLAY_SONG, position, 0).sendToTarget(); - } - - private void playSongAtImpl(int position) { - if (openTrackAndPrepareNextAt(position)) { - play(); - } else { - Toast.makeText(this, getResources().getString(R.string.unplayable_file), Toast.LENGTH_SHORT).show(); - } - } - - public void pause() { - pausedByTransientLossOfFocus = false; - if (playback.isPlaying()) { - playback.pause(); - notifyChange(PLAY_STATE_CHANGED); - } - } - - public void play() { - synchronized (this) { - if (requestFocus()) { - if (!playback.isPlaying()) { - if (!playback.isInitialized()) { - playSongAt(getPosition()); - } else { - playback.start(); - if (!becomingNoisyReceiverRegistered) { - registerReceiver(becomingNoisyReceiver, becomingNoisyReceiverIntentFilter); - becomingNoisyReceiverRegistered = true; - } - if (notHandledMetaChangedForCurrentTrack) { - handleChangeInternal(META_CHANGED); - notHandledMetaChangedForCurrentTrack = false; - } - notifyChange(PLAY_STATE_CHANGED); - - // fixes a bug where the volume would stay ducked because the AudioManager.AUDIOFOCUS_GAIN event is not sent - playerHandler.removeMessages(DUCK); - playerHandler.sendEmptyMessage(UNDUCK); - } - } - } else { - Toast.makeText(this, getResources().getString(R.string.audio_focus_denied), Toast.LENGTH_SHORT).show(); - } - } - } - - public void playSongs(ArrayList songs, int shuffleMode) { - if (songs != null && !songs.isEmpty()) { - if (shuffleMode == SHUFFLE_MODE_SHUFFLE) { - int startPosition = 0; - if (!songs.isEmpty()) { - startPosition = new Random().nextInt(songs.size()); - } - openQueue(songs, startPosition, false); - setShuffleMode(shuffleMode); - } else { - openQueue(songs, 0, false); - } - play(); - } else { - Toast.makeText(getApplicationContext(), R.string.playlist_is_empty, Toast.LENGTH_LONG).show(); - } - } - - public void playPreviousSong(boolean force) { - playSongAt(getPreviousPosition(force)); - } - - public void back(boolean force) { - if (getSongProgressMillis() > 2000) { - seek(0); - } else { - playPreviousSong(force); - } - } - - public int getPreviousPosition(boolean force) { - int newPosition = getPosition() - 1; - switch (repeatMode) { - case REPEAT_MODE_ALL: - if (newPosition < 0) { - newPosition = getPlayingQueue().size() - 1; - } - break; - case REPEAT_MODE_THIS: - if (force) { - if (newPosition < 0) { - newPosition = getPlayingQueue().size() - 1; - } - } else { - newPosition = getPosition(); - } - break; - default: - case REPEAT_MODE_NONE: - if (newPosition < 0) { - newPosition = 0; - } - break; - } - return newPosition; - } - - public int getSongProgressMillis() { - return playback.position(); - } - - public int getSongDurationMillis() { - return playback.duration(); - } - - public long getQueueDurationMillis(int position) { - long duration = 0; - for (int i = position + 1; i < playingQueue.size(); i++) - duration += playingQueue.get(i).duration; - return duration; - } - - public int seek(int millis) { - synchronized (this) { - try { - int newPosition = playback.seek(millis); - throttledSeekHandler.notifySeek(); - return newPosition; - } catch (Exception e) { - return -1; - } - } - } - - public void cycleRepeatMode() { - switch (getRepeatMode()) { - case REPEAT_MODE_NONE: - setRepeatMode(REPEAT_MODE_ALL); - break; - case REPEAT_MODE_ALL: - setRepeatMode(REPEAT_MODE_THIS); - break; - default: - setRepeatMode(REPEAT_MODE_NONE); - break; - } - } - - public void toggleShuffle() { - if (getShuffleMode() == SHUFFLE_MODE_NONE) { - setShuffleMode(SHUFFLE_MODE_SHUFFLE); - } else { - setShuffleMode(SHUFFLE_MODE_NONE); - } - } - - public int getShuffleMode() { - return shuffleMode; - } - - public void setShuffleMode(final int shuffleMode) { - PreferenceManager.getDefaultSharedPreferences(this).edit() - .putInt(SAVED_SHUFFLE_MODE, shuffleMode) - .apply(); - switch (shuffleMode) { - case SHUFFLE_MODE_SHUFFLE: - this.shuffleMode = shuffleMode; - ShuffleHelper.makeShuffleList(this.getPlayingQueue(), getPosition()); - position = 0; - break; - case SHUFFLE_MODE_NONE: - this.shuffleMode = shuffleMode; - int currentSongId = getCurrentSong().id; - playingQueue = new ArrayList<>(originalPlayingQueue); - int newPosition = 0; - for (Song song : getPlayingQueue()) { - if (song.id == currentSongId) { - newPosition = getPlayingQueue().indexOf(song); - } - } - position = newPosition; - break; - } - handleAndSendChangeInternal(SHUFFLE_MODE_CHANGED); - notifyChange(QUEUE_CHANGED); - } - - private void notifyChange(@NonNull final String what) { - handleAndSendChangeInternal(what); - sendPublicIntent(what); - } - - private void handleAndSendChangeInternal(@NonNull final String what) { - handleChangeInternal(what); - sendChangeInternal(what); - } - - // to let other apps know whats playing. i.E. last.fm (scrobbling) or musixmatch - private void sendPublicIntent(@NonNull final String what) { - final Intent intent = new Intent(what.replace(RETRO_MUSIC_PACKAGE_NAME, MUSIC_PACKAGE_NAME)); - - final Song song = getCurrentSong(); - - intent.putExtra("id", song.id); - intent.putExtra("artist", song.artistName); - intent.putExtra("album", song.albumName); - intent.putExtra("track", song.title); - intent.putExtra("duration", song.duration); - intent.putExtra("position", (long) getSongProgressMillis()); - intent.putExtra("playing", isPlaying()); - intent.putExtra("scrobbling_source", RETRO_MUSIC_PACKAGE_NAME); - - sendStickyBroadcast(intent); - - } - - private void sendChangeInternal(final String what) { - sendBroadcast(new Intent(what)); - appWidgetBig.notifyChange(this, what); - appWidgetClassic.notifyChange(this, what); - appWidgetSmall.notifyChange(this, what); - appWidgetCard.notifyChange(this, what); - } - - private void handleChangeInternal(@NonNull final String what) { - switch (what) { - case PLAY_STATE_CHANGED: - updateNotification(); - updateMediaSessionPlaybackState(); - final boolean isPlaying = isPlaying(); - if (!isPlaying && getSongProgressMillis() > 0) { - savePositionInTrack(); - } - songPlayCountHelper.notifyPlayStateChanged(isPlaying); - break; - case META_CHANGED: - updateNotification(); - updateMediaSessionMetaData(); - savePosition(); - savePositionInTrack(); - final Song currentSong = getCurrentSong(); - HistoryStore.getInstance(this).addSongId(currentSong.id); - if (songPlayCountHelper.shouldBumpPlayCount()) { - SongPlayCountStore.getInstance(this).bumpPlayCount(songPlayCountHelper.getSong().id); - } - songPlayCountHelper.notifySongChanged(currentSong); - break; - case QUEUE_CHANGED: - updateMediaSessionMetaData(); // because playing queue size might have changed - saveState(); - if (playingQueue.size() > 0) { - prepareNext(); - } else { - playingNotification.stop(); - } - break; - } - } - - public int getAudioSessionId() { - return playback.getAudioSessionId(); - } - - public MediaSessionCompat getMediaSession() { - return mediaSession; - } - - public void releaseWakeLock() { - if (wakeLock.isHeld()) { - wakeLock.release(); - } - } - - public void acquireWakeLock(long milli) { - wakeLock.acquire(milli); - } - - @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - switch (key) { - case PreferenceUtil.GAPLESS_PLAYBACK: - if (sharedPreferences.getBoolean(key, false)) { - prepareNext(); - } else { - playback.setNextDataSource(null); - } - break; - case PreferenceUtil.ALBUM_ART_ON_LOCKSCREEN: - case PreferenceUtil.BLURRED_ALBUM_ART: - updateMediaSessionMetaData(); - break; - case PreferenceUtil.COLORED_NOTIFICATION: - case PreferenceUtil.DOMINANT_COLOR: - updateNotification(); - break; - case PreferenceUtil.CLASSIC_NOTIFICATION: - initNotification(); - updateNotification(); - break; - case PreferenceUtil.TOGGLE_HEADSET: - registerHeadsetEvents(); - break; - } - } - - private void registerHeadsetEvents() { - if (!headsetReceiverRegistered && PreferenceUtil.getInstance().getHeadsetPlugged()) { - registerReceiver(headsetReceiver, headsetReceiverIntentFilter); - headsetReceiverRegistered = true; - } - } - - @Override - public void onTrackWentToNext() { - playerHandler.sendEmptyMessage(TRACK_WENT_TO_NEXT); - } - - @Override - public void onTrackEnded() { - acquireWakeLock(30000); - playerHandler.sendEmptyMessage(TRACK_ENDED); - } - - - private static final class QueueSaveHandler extends Handler { - @NonNull - private final WeakReference mService; - - QueueSaveHandler(final MusicService service, @NonNull final Looper looper) { - super(looper); - mService = new WeakReference<>(service); - } - - @Override - public void handleMessage(@NonNull Message msg) { - final MusicService service = mService.get(); - switch (msg.what) { - case SAVE_QUEUES: - service.saveQueuesImpl(); - break; - } - } - } - - private static final class PlaybackHandler extends Handler { - @NonNull - private final WeakReference mService; - private float currentDuckVolume = 1.0f; - - PlaybackHandler(final MusicService service, @NonNull final Looper looper) { - super(looper); - mService = new WeakReference<>(service); - } - - @Override - public void handleMessage(@NonNull final Message msg) { - final MusicService service = mService.get(); - if (service == null) { - return; - } - - switch (msg.what) { - case DUCK: - if (PreferenceUtil.getInstance().audioDucking()) { - currentDuckVolume -= .05f; - if (currentDuckVolume > .2f) { - sendEmptyMessageDelayed(DUCK, 10); - } else { - currentDuckVolume = .2f; - } - } else { - currentDuckVolume = 1f; - } - service.playback.setVolume(currentDuckVolume); - break; - - case UNDUCK: - if (PreferenceUtil.getInstance().audioDucking()) { - currentDuckVolume += .03f; - if (currentDuckVolume < 1f) { - sendEmptyMessageDelayed(UNDUCK, 10); - } else { - currentDuckVolume = 1f; - } - } else { - currentDuckVolume = 1f; - } - service.playback.setVolume(currentDuckVolume); - break; - - case TRACK_WENT_TO_NEXT: - if (service.getRepeatMode() == REPEAT_MODE_NONE && service.isLastTrack()) { - service.pause(); - service.seek(0); - } else { - service.position = service.nextPosition; - service.prepareNextImpl(); - service.notifyChange(META_CHANGED); - } - break; - - case TRACK_ENDED: - if (service.getRepeatMode() == REPEAT_MODE_NONE && service.isLastTrack()) { - service.notifyChange(PLAY_STATE_CHANGED); - service.seek(0); - } else { - service.playNextSong(false); - } - sendEmptyMessage(RELEASE_WAKELOCK); - break; - - case RELEASE_WAKELOCK: - service.releaseWakeLock(); - break; - - case PLAY_SONG: - service.playSongAtImpl(msg.arg1); - break; - - case SET_POSITION: - service.openTrackAndPrepareNextAt(msg.arg1); - service.notifyChange(PLAY_STATE_CHANGED); - break; - - case PREPARE_NEXT: - service.prepareNextImpl(); - break; - - case RESTORE_QUEUES: - service.restoreQueuesAndPositionIfNecessary(); - break; - - case FOCUS_CHANGE: - switch (msg.arg1) { - case AudioManager.AUDIOFOCUS_GAIN: - if (!service.isPlaying() && service.pausedByTransientLossOfFocus) { - service.play(); - service.pausedByTransientLossOfFocus = false; - } - removeMessages(DUCK); - sendEmptyMessage(UNDUCK); - break; - - case AudioManager.AUDIOFOCUS_LOSS: - // Lost focus for an unbounded amount of time: stop playback and release media playback - service.pause(); - break; - - case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: - // Lost focus for a short time, but we have to stop - // playback. We don't release the media playback because playback - // is likely to resume - boolean wasPlaying = service.isPlaying(); - service.pause(); - service.pausedByTransientLossOfFocus = wasPlaying; - break; - - case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: - // Lost focus for a short time, but it's ok to keep playing - // at an attenuated level - removeMessages(UNDUCK); - sendEmptyMessage(DUCK); - break; - } - break; - } - } - } - - private static class SongPlayCountHelper { - public static final String TAG = SongPlayCountHelper.class.getSimpleName(); - - private StopWatch stopWatch = new StopWatch(); - private Song song = Song.EMPTY_SONG; - - public Song getSong() { - return song; - } - - boolean shouldBumpPlayCount() { - return song.duration * 0.5d < stopWatch.getElapsedTime(); - } - - void notifySongChanged(Song song) { - synchronized (this) { - stopWatch.reset(); - this.song = song; - } - } - - void notifyPlayStateChanged(boolean isPlaying) { - synchronized (this) { - if (isPlaying) { - stopWatch.start(); - } else { - stopWatch.pause(); - } - } - } - } - - public class MusicBinder extends Binder { - @NonNull - public MusicService getService() { - return MusicService.this; - } - } - - private class MediaStoreObserver extends ContentObserver implements Runnable { - // milliseconds to delay before calling refresh to aggregate events - private static final long REFRESH_DELAY = 500; - private Handler mHandler; - - MediaStoreObserver(Handler handler) { - super(handler); - mHandler = handler; - } - - @Override - public void onChange(boolean selfChange) { - // if a change is detected, remove any scheduled callback - // then post a new one. This is intended to prevent closely - // spaced events from generating multiple refresh calls - mHandler.removeCallbacks(this); - mHandler.postDelayed(this, REFRESH_DELAY); - } - - @Override - public void run() { - // actually call refresh when the delayed callback fires - // do not send a sticky broadcast here - handleAndSendChangeInternal(MEDIA_STORE_CHANGED); - } - } - - private class ThrottledSeekHandler implements Runnable { - // milliseconds to throttle before calling run() to aggregate events - private static final long THROTTLE = 500; - private Handler mHandler; - - ThrottledSeekHandler(Handler handler) { - mHandler = handler; - } - - void notifySeek() { - mHandler.removeCallbacks(this); - mHandler.postDelayed(this, THROTTLE); - } - - @Override - public void run() { - savePositionInTrack(); - sendPublicIntent(PLAY_STATE_CHANGED); // for musixmatch synced lyrics - } - } -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/service/MusicService.kt b/app/src/main/java/code/name/monkey/retromusic/service/MusicService.kt new file mode 100644 index 00000000..813385cf --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/service/MusicService.kt @@ -0,0 +1,1247 @@ +package code.name.monkey.retromusic.service + +import android.annotation.SuppressLint +import android.app.PendingIntent +import android.app.Service +import android.content.* +import android.database.ContentObserver +import android.graphics.Bitmap +import android.graphics.drawable.Drawable +import android.media.AudioManager +import android.media.audiofx.AudioEffect +import android.media.session.MediaSession +import android.os.* +import android.preference.PreferenceManager +import android.provider.MediaStore +import android.support.v4.media.MediaMetadataCompat +import android.support.v4.media.session.MediaSessionCompat +import android.support.v4.media.session.PlaybackStateCompat +import android.telephony.PhoneStateListener +import android.telephony.TelephonyManager +import android.util.Log +import android.widget.Toast +import code.name.monkey.retromusic.Constants.ACTION_PAUSE +import code.name.monkey.retromusic.Constants.ACTION_PLAY +import code.name.monkey.retromusic.Constants.ACTION_PLAY_PLAYLIST +import code.name.monkey.retromusic.Constants.ACTION_QUIT +import code.name.monkey.retromusic.Constants.ACTION_SKIP +import code.name.monkey.retromusic.Constants.ACTION_STOP +import code.name.monkey.retromusic.Constants.ACTION_TOGGLE_PAUSE +import code.name.monkey.retromusic.Constants.APP_WIDGET_UPDATE +import code.name.monkey.retromusic.Constants.EXTRA_APP_WIDGET_NAME +import code.name.monkey.retromusic.Constants.INTENT_EXTRA_PLAYLIST +import code.name.monkey.retromusic.Constants.INTENT_EXTRA_SHUFFLE_MODE +import code.name.monkey.retromusic.Constants.MEDIA_STORE_CHANGED +import code.name.monkey.retromusic.Constants.META_CHANGED +import code.name.monkey.retromusic.Constants.MUSIC_PACKAGE_NAME +import code.name.monkey.retromusic.Constants.PLAY_STATE_CHANGED +import code.name.monkey.retromusic.Constants.QUEUE_CHANGED +import code.name.monkey.retromusic.Constants.REPEAT_MODE_CHANGED +import code.name.monkey.retromusic.Constants.RETRO_MUSIC_PACKAGE_NAME +import code.name.monkey.retromusic.Constants.SHUFFLE_MODE_CHANGED +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.glide.BlurTransformation +import code.name.monkey.retromusic.glide.SongGlideRequest +import code.name.monkey.retromusic.helper.MusicPlayerRemote.setShuffleMode +import code.name.monkey.retromusic.helper.ShuffleHelper +import code.name.monkey.retromusic.helper.StopWatch +import code.name.monkey.retromusic.loaders.PlaylistSongsLoader +import code.name.monkey.retromusic.model.AbsCustomPlaylist +import code.name.monkey.retromusic.model.Playlist +import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.providers.HistoryStore +import code.name.monkey.retromusic.providers.MusicPlaybackQueueStore +import code.name.monkey.retromusic.providers.SongPlayCountStore +import code.name.monkey.retromusic.service.notification.PlayingNotification +import code.name.monkey.retromusic.service.notification.PlayingNotificationImpl24 +import code.name.monkey.retromusic.service.notification.PlayingNotificationOreo +import code.name.monkey.retromusic.service.playback.Playback +import code.name.monkey.retromusic.util.MusicUtil +import code.name.monkey.retromusic.util.PreferenceUtil +import code.name.monkey.retromusic.util.RetroUtil +import com.bumptech.glide.Glide +import com.bumptech.glide.request.animation.GlideAnimation +import com.bumptech.glide.request.target.SimpleTarget +import com.google.android.gms.cast.framework.media.MediaIntentReceiver.ACTION_REWIND +import java.lang.ref.WeakReference +import java.util.* + +/** + * @author Karim Abou Zeid (kabouzeid), Andrew Neal + */ +class MusicService : Service(), SharedPreferences.OnSharedPreferenceChangeListener, Playback.PlaybackCallbacks { + private val musicBind = MusicBinder() + private var playback: Playback? = null + var playingQueue = ArrayList() + private set + private var originalPlayingQueue = ArrayList() + var position = -1 + set(value) { + playerHandler!!.removeMessages(SET_POSITION) + playerHandler!!.obtainMessage(SET_POSITION, value, 0).sendToTarget() + } + private var nextPosition = -1 + var shuffleMode: Int = 0 + set(value) { + PreferenceManager.getDefaultSharedPreferences(this).edit() + .putInt(SAVED_SHUFFLE_MODE, value) + .apply() + when (value) { + SHUFFLE_MODE_SHUFFLE -> { + field = value + ShuffleHelper.makeShuffleList(this.playingQueue, position) + position = 0 + } + SHUFFLE_MODE_NONE -> { + field = value + val currentSongId = currentSong.id + playingQueue = ArrayList(originalPlayingQueue) + var newPosition = 0 + for (song in playingQueue) { + if (song.id == currentSongId) { + newPosition = playingQueue.indexOf(song) + } + } + position = newPosition + } + } + handleAndSendChangeInternal(SHUFFLE_MODE_CHANGED) + notifyChange(QUEUE_CHANGED) + } + var repeatMode: Int = 0 + set(value) { + when (value) { + REPEAT_MODE_NONE, REPEAT_MODE_ALL, REPEAT_MODE_THIS -> { + field = value + PreferenceManager.getDefaultSharedPreferences(this).edit() + .putInt(SAVED_REPEAT_MODE, repeatMode) + .apply() + prepareNext() + handleAndSendChangeInternal(REPEAT_MODE_CHANGED) + } + } + } + private var queuesRestored: Boolean = false + private var pausedByTransientLossOfFocus: Boolean = false + private val becomingNoisyReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + if (intent.action != null && intent.action == AudioManager.ACTION_AUDIO_BECOMING_NOISY) { + pause() + } + } + } + private var playingNotification: PlayingNotification? = null + private var audioManager: AudioManager? = null + var mediaSession: MediaSessionCompat? = null + private set + private var wakeLock: PowerManager.WakeLock? = null + private var playerHandler: PlaybackHandler? = null + private val audioFocusListener = AudioManager.OnAudioFocusChangeListener { focusChange -> playerHandler!!.obtainMessage(FOCUS_CHANGE, focusChange, 0).sendToTarget() } + private var queueSaveHandler: QueueSaveHandler? = null + private var musicPlayerHandlerThread: HandlerThread? = null + private var queueSaveHandlerThread: HandlerThread? = null + private val songPlayCountHelper = SongPlayCountHelper() + private var throttledSeekHandler: ThrottledSeekHandler? = null + private var becomingNoisyReceiverRegistered: Boolean = false + private val becomingNoisyReceiverIntentFilter = IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY) + private var mediaStoreObserver: ContentObserver? = null + private var notHandledMetaChangedForCurrentTrack: Boolean = false + private val phoneStateListener = object : PhoneStateListener() { + override fun onCallStateChanged(state: Int, incomingNumber: String) { + when (state) { + TelephonyManager.CALL_STATE_IDLE -> + //Not in call: Play music + play() + TelephonyManager.CALL_STATE_RINGING, TelephonyManager.CALL_STATE_OFFHOOK -> + //A call is dialing, active or on hold + pause() + } + super.onCallStateChanged(state, incomingNumber) + } + } + private var isServiceBound: Boolean = false + private var uiThreadHandler: Handler? = null + private val widgetIntentReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + val command = intent.getStringExtra(EXTRA_APP_WIDGET_NAME) + } + } + private val headsetReceiverIntentFilter = IntentFilter(Intent.ACTION_HEADSET_PLUG) + private var headsetReceiverRegistered = false + private val headsetReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + val action = intent.action + if (action != null) { + when (action) { + Intent.ACTION_HEADSET_PLUG -> { + val state = intent.getIntExtra("state", -1) + when (state) { + 0 -> { + Log.d(TAG, "Headset unplugged") + pause() + } + 1 -> { + Log.d(TAG, "Headset plugged") + play() + } + } + } + } + } + } + } + + val isPlaying: Boolean + get() = playback != null && playback!!.isPlaying + + val currentSong: Song + get() = getSongAt(position) + + private val isLastTrack: Boolean + get() = position == playingQueue.size - 1 + + val songProgressMillis: Int + get() = playback!!.position() + + val songDurationMillis: Int + get() = playback!!.duration() + + val audioSessionId: Int + get() = playback!!.audioSessionId + + + override fun onCreate() { + super.onCreate() + + val telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager + telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE) + + val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager + wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, javaClass.name) + wakeLock!!.setReferenceCounted(false) + + musicPlayerHandlerThread = HandlerThread("PlaybackHandler") + musicPlayerHandlerThread!!.start() + playerHandler = PlaybackHandler(this, musicPlayerHandlerThread!!.looper) + + playback = MultiPlayer(this) + playback!!.setCallbacks(this) + + setupMediaSession() + + // queue saving needs to run on a separate thread so that it doesn't block the playback handler events + queueSaveHandlerThread = HandlerThread("QueueSaveHandler", Process.THREAD_PRIORITY_BACKGROUND) + queueSaveHandlerThread!!.start() + queueSaveHandler = QueueSaveHandler(this, queueSaveHandlerThread!!.looper) + + uiThreadHandler = Handler() + + registerReceiver(widgetIntentReceiver, IntentFilter(APP_WIDGET_UPDATE)) + + initNotification() + + mediaStoreObserver = MediaStoreObserver(playerHandler!!) + throttledSeekHandler = ThrottledSeekHandler(playerHandler!!) + contentResolver.registerContentObserver( + MediaStore.Audio.Media.INTERNAL_CONTENT_URI, true, mediaStoreObserver!!) + contentResolver.registerContentObserver( + MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, true, mediaStoreObserver!!) + + PreferenceUtil.getInstance().registerOnSharedPreferenceChangedListener(this) + + restoreState() + + mediaSession!!.isActive = true + + sendBroadcast(Intent("code.name.monkey.retromusic.RETRO_MUSIC_SERVICE_CREATED")) + + registerHeadsetEvents() + + + } + + private fun getAudioManager(): AudioManager? { + if (audioManager == null) { + audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager + } + return audioManager + } + + @SuppressLint("WrongConstant") + private fun setupMediaSession() { + val mediaButtonReceiverComponentName = ComponentName(applicationContext, MediaButtonIntentReceiver::class.java) + + val mediaButtonIntent = Intent(Intent.ACTION_MEDIA_BUTTON) + mediaButtonIntent.component = mediaButtonReceiverComponentName + + + val mediaButtonReceiverPendingIntent = PendingIntent.getBroadcast(applicationContext, 0, mediaButtonIntent, 0) + + mediaSession = MediaSessionCompat(this, "RetroMusicPlayer", mediaButtonReceiverComponentName, mediaButtonReceiverPendingIntent) + mediaSession!!.setCallback(object : MediaSessionCompat.Callback() { + override fun onPlay() { + play() + } + + override fun onPause() { + pause() + } + + override fun onSkipToNext() { + playNextSong(true) + } + + override fun onSkipToPrevious() { + back(true) + } + + override fun onStop() { + quit() + } + + override fun onSeekTo(pos: Long) { + seek(pos.toInt()) + } + + override fun onMediaButtonEvent(mediaButtonEvent: Intent): Boolean { + return MediaButtonIntentReceiver.handleIntent(this@MusicService, mediaButtonEvent) + } + }) + + mediaSession!!.setFlags(MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS or MediaSession.FLAG_HANDLES_MEDIA_BUTTONS) + + mediaSession!!.setMediaButtonReceiver(mediaButtonReceiverPendingIntent) + } + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + if (intent != null) { + if (intent.action != null) { + restoreQueuesAndPositionIfNecessary() + val action = intent.action + when (action) { + ACTION_TOGGLE_PAUSE -> if (isPlaying) { + pause() + } else { + play() + } + ACTION_PAUSE -> pause() + ACTION_PLAY -> play() + ACTION_PLAY_PLAYLIST -> { + val playlist = intent.getParcelableExtra(INTENT_EXTRA_PLAYLIST) + val shuffleMode = intent.getIntExtra(INTENT_EXTRA_SHUFFLE_MODE, shuffleMode) + if (playlist != null) { + val playlistSongs: ArrayList + if (playlist is AbsCustomPlaylist) { + playlistSongs = playlist.getSongs(applicationContext).blockingFirst() + } else { + + playlistSongs = PlaylistSongsLoader.getPlaylistSongList(applicationContext, playlist.id).blockingFirst() + } + if (!playlistSongs.isEmpty()) { + if (shuffleMode == SHUFFLE_MODE_SHUFFLE) { + val startPosition: Int = Random().nextInt(playlistSongs.size) + openQueue(playlistSongs, startPosition, true) + setShuffleMode(shuffleMode) + } else { + openQueue(playlistSongs, 0, true) + } + } else { + Toast.makeText(applicationContext, R.string.playlist_is_empty, Toast.LENGTH_LONG).show() + } + } else { + Toast.makeText(applicationContext, R.string.playlist_is_empty, Toast.LENGTH_LONG).show() + } + } + ACTION_REWIND -> back(true) + ACTION_SKIP -> playNextSong(true) + ACTION_STOP, ACTION_QUIT -> return quit() + } + } + } + + return Service.START_STICKY + } + + + override fun onDestroy() { + unregisterReceiver(widgetIntentReceiver) + if (becomingNoisyReceiverRegistered) { + unregisterReceiver(becomingNoisyReceiver) + becomingNoisyReceiverRegistered = false + } + if (headsetReceiverRegistered) { + unregisterReceiver(headsetReceiver) + headsetReceiverRegistered = false + } + mediaSession!!.isActive = false + quit() + releaseResources() + contentResolver.unregisterContentObserver(mediaStoreObserver!!) + PreferenceUtil.getInstance().unregisterOnSharedPreferenceChangedListener(this) + wakeLock!!.release() + + sendBroadcast(Intent("code.name.monkey.retromusic.RETRO_MUSIC_MUSIC_SERVICE_DESTROYED")) + } + + override fun onBind(intent: Intent): IBinder? { + isServiceBound = true + return musicBind + } + + override fun onRebind(intent: Intent) { + isServiceBound = true + } + + override fun onUnbind(intent: Intent): Boolean { + isServiceBound = false + if (!isPlaying) { + stopSelf() + } + return true + } + + private fun saveQueuesImpl() { + MusicPlaybackQueueStore.getInstance(this).saveQueues(playingQueue, originalPlayingQueue) + } + + private fun savePosition() { + PreferenceManager.getDefaultSharedPreferences(this).edit().putInt(SAVED_POSITION, position).apply() + } + + private fun savePositionInTrack() { + PreferenceManager.getDefaultSharedPreferences(this).edit().putInt(SAVED_POSITION_IN_TRACK, songProgressMillis).apply() + } + + private fun saveState() { + saveQueues() + savePosition() + savePositionInTrack() + } + + private fun saveQueues() { + queueSaveHandler!!.removeMessages(SAVE_QUEUES) + queueSaveHandler!!.sendEmptyMessage(SAVE_QUEUES) + } + + private fun restoreState() { + shuffleMode = PreferenceManager.getDefaultSharedPreferences(this).getInt(SAVED_SHUFFLE_MODE, 0) + repeatMode = PreferenceManager.getDefaultSharedPreferences(this).getInt(SAVED_REPEAT_MODE, 0) + handleAndSendChangeInternal(SHUFFLE_MODE_CHANGED) + handleAndSendChangeInternal(REPEAT_MODE_CHANGED) + + playerHandler!!.removeMessages(RESTORE_QUEUES) + playerHandler!!.sendEmptyMessage(RESTORE_QUEUES) + } + + @Synchronized + private fun restoreQueuesAndPositionIfNecessary() { + if (!queuesRestored && playingQueue.isEmpty()) { + val restoredQueue = MusicPlaybackQueueStore.getInstance(this).savedPlayingQueue + .blockingFirst() + + val restoredOriginalQueue = MusicPlaybackQueueStore.getInstance(this).savedOriginalPlayingQueue + .blockingFirst() + + val restoredPosition = PreferenceManager.getDefaultSharedPreferences(this).getInt(SAVED_POSITION, -1) + val restoredPositionInTrack = PreferenceManager.getDefaultSharedPreferences(this).getInt(SAVED_POSITION_IN_TRACK, -1) + + if (restoredQueue.size > 0 && restoredQueue.size == restoredOriginalQueue.size && restoredPosition != -1) { + this.originalPlayingQueue = restoredOriginalQueue + this.playingQueue = restoredQueue + + position = restoredPosition + openCurrent() + prepareNext() + + if (restoredPositionInTrack > 0) seek(restoredPositionInTrack) + + notHandledMetaChangedForCurrentTrack = true + sendChangeInternal(META_CHANGED) + sendChangeInternal(QUEUE_CHANGED) + } + } + queuesRestored = true + } + + @SuppressLint("WrongConstant") + private fun quit(): Int { + pause() + playingNotification!!.stop() + + if (isServiceBound) { + return Service.START_STICKY + } else { + closeAudioEffectSession() + getAudioManager()!!.abandonAudioFocus(audioFocusListener) + stopSelf() + return Service.START_NOT_STICKY + } + } + + private fun releaseResources() { + playerHandler!!.removeCallbacksAndMessages(null) + musicPlayerHandlerThread!!.quitSafely() + queueSaveHandler!!.removeCallbacksAndMessages(null) + queueSaveHandlerThread!!.quitSafely() + playback!!.release() + playback = null + mediaSession!!.release() + } + + fun playNextSong(force: Boolean) { + playSongAt(getNextPosition(force)) + } + + private fun openTrackAndPrepareNextAt(position: Int): Boolean { + synchronized(this) { + this.position = position + val prepared = openCurrent() + if (prepared) prepareNextImpl() + notifyChange(META_CHANGED) + notHandledMetaChangedForCurrentTrack = false + return prepared + } + } + + private fun openCurrent(): Boolean { + synchronized(this) { + try { + return playback!!.setDataSource(getTrackUri(currentSong)) + } catch (e: Exception) { + return false + } + + } + } + + private fun prepareNext() { + playerHandler!!.removeMessages(PREPARE_NEXT) + playerHandler!!.obtainMessage(PREPARE_NEXT).sendToTarget() + } + + private fun prepareNextImpl(): Boolean { + synchronized(this) { + return try { + val nextPosition = getNextPosition(false) + playback!!.setNextDataSource(getTrackUri(getSongAt(nextPosition))) + this.nextPosition = nextPosition + true + } catch (e: Exception) { + false + } + + } + } + + private fun closeAudioEffectSession() { + val audioEffectsIntent = Intent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION) + audioEffectsIntent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, playback!!.audioSessionId) + audioEffectsIntent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, packageName) + sendBroadcast(audioEffectsIntent) + } + + private fun requestFocus(): Boolean { + return getAudioManager()!!.requestAudioFocus(audioFocusListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN) == AudioManager.AUDIOFOCUS_REQUEST_GRANTED + } + + fun initNotification() { + playingNotification = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && !PreferenceUtil.getInstance().classicNotification()) { + PlayingNotificationImpl24() + } else { + PlayingNotificationOreo() + } + playingNotification!!.init(this) + } + + fun updateNotification() { + if (playingNotification != null && currentSong.id != -1) { + playingNotification!!.update() + } + } + + private fun updateMediaSessionPlaybackState() { + mediaSession!!.setPlaybackState( + PlaybackStateCompat.Builder() + .setActions(MEDIA_SESSION_ACTIONS) + .setState(if (isPlaying) PlaybackStateCompat.STATE_PLAYING else PlaybackStateCompat.STATE_PAUSED, + position.toLong(), 1f) + .build()) + } + + private fun updateMediaSessionMetaData() { + val song = currentSong + + if (song.id == -1) { + mediaSession!!.setMetadata(null) + return + } + + val metaData = MediaMetadataCompat.Builder() + .putString(MediaMetadataCompat.METADATA_KEY_ARTIST, song.artistName) + .putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ARTIST, song.artistName) + .putString(MediaMetadataCompat.METADATA_KEY_ALBUM, song.albumName) + .putString(MediaMetadataCompat.METADATA_KEY_TITLE, song.title) + .putLong(MediaMetadataCompat.METADATA_KEY_DURATION, song.duration) + .putLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER, (position + 1).toLong()) + .putLong(MediaMetadataCompat.METADATA_KEY_YEAR, song.year.toLong()) + .putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, null) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + metaData.putLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS, playingQueue.size.toLong()) + } + + if (PreferenceUtil.getInstance().albumArtOnLockscreen()) { + val screenSize = RetroUtil.getScreenSize(this@MusicService) + val request = SongGlideRequest.Builder.from(Glide.with(this@MusicService), song) + .checkIgnoreMediaStore(this@MusicService) + .asBitmap().build() + if (PreferenceUtil.getInstance().blurredAlbumArt()) { + request.transform(BlurTransformation.Builder(this@MusicService).build()) + } + runOnUiThread(Runnable { + request.into(object : SimpleTarget(screenSize.x, screenSize.y) { + override fun onLoadFailed(e: Exception?, errorDrawable: Drawable?) { + super.onLoadFailed(e, errorDrawable) + mediaSession!!.setMetadata(metaData.build()) + } + + override fun onResourceReady(resource: Bitmap, glideAnimation: GlideAnimation) { + metaData.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, copy(resource)) + mediaSession!!.setMetadata(metaData.build()) + } + }) + }) + } else { + mediaSession!!.setMetadata(metaData.build()) + } + } + + fun runOnUiThread(runnable: Runnable) { + uiThreadHandler!!.post(runnable) + } + + private fun getSongAt(position: Int): Song { + return if (position >= 0 && position < playingQueue.size) { + playingQueue[position] + } else { + Song.EMPTY_SONG + } + } + + private fun getNextPosition(force: Boolean): Int { + var position = position + 1 + when (repeatMode) { + REPEAT_MODE_ALL -> if (isLastTrack) { + position = 0 + } + REPEAT_MODE_THIS -> if (force) { + if (isLastTrack) { + position = 0 + } + } else { + position -= 1 + } + REPEAT_MODE_NONE -> if (isLastTrack) { + position -= 1 + } + else -> if (isLastTrack) { + position -= 1 + } + } + return position + } + + + fun openQueue(playingQueue: ArrayList?, startPosition: Int, startPlaying: Boolean) { + if (playingQueue != null && !playingQueue.isEmpty() && startPosition >= 0 && startPosition < playingQueue.size) { + // it is important to copy the playing queue here first as we might add/remove songs later + originalPlayingQueue = ArrayList(playingQueue) + this.playingQueue = ArrayList(originalPlayingQueue) + + var position = startPosition + if (shuffleMode == SHUFFLE_MODE_SHUFFLE) { + ShuffleHelper.makeShuffleList(this.playingQueue, startPosition) + position = 0 + } + if (startPlaying) { + playSongAt(position) + } else { + this.position = position + } + notifyChange(QUEUE_CHANGED) + } + } + + fun addSong(position: Int, song: Song) { + playingQueue.add(position, song) + originalPlayingQueue.add(position, song) + notifyChange(QUEUE_CHANGED) + } + + fun addSong(song: Song) { + playingQueue.add(song) + originalPlayingQueue.add(song) + notifyChange(QUEUE_CHANGED) + } + + fun addSongs(position: Int, songs: List) { + playingQueue.addAll(position, songs) + originalPlayingQueue.addAll(position, songs) + notifyChange(QUEUE_CHANGED) + } + + fun addSongs(songs: List) { + playingQueue.addAll(songs) + originalPlayingQueue.addAll(songs) + notifyChange(QUEUE_CHANGED) + } + + fun removeSong(position: Int) { + if (shuffleMode == SHUFFLE_MODE_NONE) { + playingQueue.removeAt(position) + originalPlayingQueue.removeAt(position) + } else { + originalPlayingQueue.remove(playingQueue.removeAt(position)) + } + + rePosition(position) + + notifyChange(QUEUE_CHANGED) + } + + fun removeSong(song: Song) { + for (i in playingQueue.indices) { + if (playingQueue[i].id == song.id) { + playingQueue.removeAt(i) + rePosition(i) + } + } + for (i in originalPlayingQueue.indices) { + if (originalPlayingQueue[i].id == song.id) { + originalPlayingQueue.removeAt(i) + } + } + notifyChange(QUEUE_CHANGED) + } + + private fun rePosition(deletedPosition: Int) { + val currentPosition = position + if (deletedPosition < currentPosition) { + position = currentPosition - 1 + } else if (deletedPosition == currentPosition) { + if (playingQueue.size > deletedPosition) { + this.position = position + } else { + this.position = position - 1 + } + } + } + + fun moveSong(from: Int, to: Int) { + if (from == to) return + val currentPosition = position + val songToMove = playingQueue.removeAt(from) + playingQueue.add(to, songToMove) + if (shuffleMode == SHUFFLE_MODE_NONE) { + val tmpSong = originalPlayingQueue.removeAt(from) + originalPlayingQueue.add(to, tmpSong) + } + if (currentPosition in to..(from - 1)) { + position = currentPosition + 1 + } else if (currentPosition in (from + 1)..to) { + position = currentPosition - 1 + } else if (from == currentPosition) { + position = to + } + notifyChange(QUEUE_CHANGED) + } + + fun clearQueue() { + playingQueue.clear() + originalPlayingQueue.clear() + + position = -1 + notifyChange(QUEUE_CHANGED) + } + + fun playSongAt(position: Int) { + // handle this on the handlers thread to avoid blocking the ui thread + playerHandler!!.removeMessages(PLAY_SONG) + playerHandler!!.obtainMessage(PLAY_SONG, position, 0).sendToTarget() + } + + private fun playSongAtImpl(position: Int) { + if (openTrackAndPrepareNextAt(position)) { + play() + } else { + Toast.makeText(this, resources.getString(R.string.unplayable_file), Toast.LENGTH_SHORT).show() + } + } + + fun pause() { + pausedByTransientLossOfFocus = false + if (playback!!.isPlaying) { + playback!!.pause() + notifyChange(PLAY_STATE_CHANGED) + } + } + + fun play() { + synchronized(this) { + if (requestFocus()) { + if (!playback!!.isPlaying) { + if (!playback!!.isInitialized) { + playSongAt(position) + } else { + playback!!.start() + if (!becomingNoisyReceiverRegistered) { + registerReceiver(becomingNoisyReceiver, becomingNoisyReceiverIntentFilter) + becomingNoisyReceiverRegistered = true + } + if (notHandledMetaChangedForCurrentTrack) { + handleChangeInternal(META_CHANGED) + notHandledMetaChangedForCurrentTrack = false + } + notifyChange(PLAY_STATE_CHANGED) + + // fixes a bug where the volume would stay ducked because the AudioManager.AUDIOFOCUS_GAIN event is not sent + playerHandler!!.removeMessages(DUCK) + playerHandler!!.sendEmptyMessage(UN_DUCK) + } + } + } else { + Toast.makeText(this, resources.getString(R.string.audio_focus_denied), Toast.LENGTH_SHORT).show() + } + } + } + + fun playSongs(songs: ArrayList?, shuffleMode: Int) { + if (songs != null && !songs.isEmpty()) { + if (shuffleMode == SHUFFLE_MODE_SHUFFLE) { + var startPosition = 0 + if (!songs.isEmpty()) { + startPosition = Random().nextInt(songs.size) + } + openQueue(songs, startPosition, false) + setShuffleMode(shuffleMode) + } else { + openQueue(songs, 0, false) + } + play() + } else { + Toast.makeText(applicationContext, R.string.playlist_is_empty, Toast.LENGTH_LONG).show() + } + } + + fun playPreviousSong(force: Boolean) { + playSongAt(getPreviousPosition(force)) + } + + fun back(force: Boolean) { + if (songProgressMillis > 2000) { + seek(0) + } else { + playPreviousSong(force) + } + } + + private fun getPreviousPosition(force: Boolean): Int { + var newPosition = position - 1 + when (repeatMode) { + REPEAT_MODE_ALL -> if (newPosition < 0) { + newPosition = playingQueue.size - 1 + } + REPEAT_MODE_THIS -> if (force) { + if (newPosition < 0) { + newPosition = playingQueue.size - 1 + } + } else { + newPosition = position + } + REPEAT_MODE_NONE -> if (newPosition < 0) { + newPosition = 0 + } + else -> if (newPosition < 0) { + newPosition = 0 + } + } + return newPosition + } + + fun getQueueDurationMillis(position: Int): Long { + var duration: Long = 0 + for (i in position + 1 until playingQueue.size) + duration += playingQueue[i].duration + return duration + } + + fun seek(millis: Int): Int { + synchronized(this) { + try { + val newPosition = playback!!.seek(millis) + throttledSeekHandler!!.notifySeek() + return newPosition + } catch (e: Exception) { + return -1 + } + + } + } + + fun cycleRepeatMode() { + repeatMode = when (repeatMode) { + REPEAT_MODE_NONE -> REPEAT_MODE_ALL + REPEAT_MODE_ALL -> REPEAT_MODE_THIS + else -> REPEAT_MODE_NONE + } + } + + fun toggleShuffle() { + if (shuffleMode == SHUFFLE_MODE_NONE) { + setShuffleMode(SHUFFLE_MODE_SHUFFLE) + } else { + setShuffleMode(SHUFFLE_MODE_NONE) + } + } + + + private fun notifyChange(what: String) { + handleAndSendChangeInternal(what) + sendPublicIntent(what) + } + + private fun handleAndSendChangeInternal(what: String) { + handleChangeInternal(what) + sendChangeInternal(what) + } + + // to let other apps know whats playing. i.E. last.fm (scrobbling) or musixmatch + @SuppressLint("WrongConstant") + private fun sendPublicIntent(what: String) { + val intent = Intent(what.replace(RETRO_MUSIC_PACKAGE_NAME, MUSIC_PACKAGE_NAME)) + + val song = currentSong + + intent.putExtra("id", song.id) + intent.putExtra("artist", song.artistName) + intent.putExtra("album", song.albumName) + intent.putExtra("track", song.title) + intent.putExtra("duration", song.duration) + intent.putExtra("position", songProgressMillis.toLong()) + intent.putExtra("playing", isPlaying) + intent.putExtra("scrobbling_source", RETRO_MUSIC_PACKAGE_NAME) + + sendStickyBroadcast(intent) + + } + + private fun sendChangeInternal(what: String) { + sendBroadcast(Intent(what)) + } + + private fun handleChangeInternal(what: String) { + when (what) { + PLAY_STATE_CHANGED -> { + updateNotification() + updateMediaSessionPlaybackState() + val isPlaying = isPlaying + if (!isPlaying && songProgressMillis > 0) { + savePositionInTrack() + } + songPlayCountHelper.notifyPlayStateChanged(isPlaying) + } + META_CHANGED -> { + updateNotification() + updateMediaSessionMetaData() + savePosition() + savePositionInTrack() + val currentSong = currentSong + HistoryStore.getInstance(this).addSongId(currentSong.id.toLong()) + if (songPlayCountHelper.shouldBumpPlayCount()) { + SongPlayCountStore.getInstance(this).bumpPlayCount(songPlayCountHelper.song.id.toLong()) + } + songPlayCountHelper.notifySongChanged(currentSong) + } + QUEUE_CHANGED -> { + updateMediaSessionMetaData() // because playing queue size might have changed + saveState() + if (playingQueue.size > 0) { + prepareNext() + } else { + playingNotification!!.stop() + } + } + } + } + + fun releaseWakeLock() { + if (wakeLock!!.isHeld) { + wakeLock!!.release() + } + } + + private fun acquireWakeLock(milli: Long) { + wakeLock!!.acquire(milli) + } + + override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) { + when (key) { + PreferenceUtil.GAPLESS_PLAYBACK -> if (sharedPreferences.getBoolean(key, false)) { + prepareNext() + } else { + playback!!.setNextDataSource(null) + } + PreferenceUtil.ALBUM_ART_ON_LOCKSCREEN, PreferenceUtil.BLURRED_ALBUM_ART -> updateMediaSessionMetaData() + PreferenceUtil.COLORED_NOTIFICATION, PreferenceUtil.DOMINANT_COLOR -> updateNotification() + PreferenceUtil.CLASSIC_NOTIFICATION -> { + initNotification() + updateNotification() + } + PreferenceUtil.TOGGLE_HEADSET -> registerHeadsetEvents() + } + } + + private fun registerHeadsetEvents() { + if (!headsetReceiverRegistered && PreferenceUtil.getInstance().headsetPlugged) { + registerReceiver(headsetReceiver, headsetReceiverIntentFilter) + headsetReceiverRegistered = true + } + } + + override fun onTrackWentToNext() { + playerHandler!!.sendEmptyMessage(TRACK_WENT_TO_NEXT) + } + + override fun onTrackEnded() { + acquireWakeLock(30000) + playerHandler!!.sendEmptyMessage(TRACK_ENDED) + } + + + inner class QueueSaveHandler internal constructor(service: MusicService, looper: Looper) : Handler(looper) { + val mService: WeakReference = WeakReference(service) + + override fun handleMessage(msg: Message) { + val service = mService.get() + when (msg.what) { + SAVE_QUEUES -> service!!.saveQueuesImpl() + } + } + } + + inner class PlaybackHandler internal constructor(service: MusicService, looper: Looper) : Handler(looper) { + val mService: WeakReference = WeakReference(service) + var currentDuckVolume = 1.0f + + override fun handleMessage(msg: Message) { + val service = mService.get() ?: return + + when (msg.what) { + DUCK -> { + if (PreferenceUtil.getInstance().audioDucking()) { + currentDuckVolume -= .05f + if (currentDuckVolume > .2f) { + sendEmptyMessageDelayed(DUCK, 10) + } else { + currentDuckVolume = .2f + } + } else { + currentDuckVolume = 1f + } + service.playback!!.setVolume(currentDuckVolume) + } + + UN_DUCK -> { + if (PreferenceUtil.getInstance().audioDucking()) { + currentDuckVolume += .03f + if (currentDuckVolume < 1f) { + sendEmptyMessageDelayed(UN_DUCK, 10) + } else { + currentDuckVolume = 1f + } + } else { + currentDuckVolume = 1f + } + service.playback!!.setVolume(currentDuckVolume) + } + + TRACK_WENT_TO_NEXT -> if (service.repeatMode == REPEAT_MODE_NONE && service.isLastTrack) { + service.pause() + service.seek(0) + } else { + service.position = service.nextPosition + service.prepareNextImpl() + service.notifyChange(META_CHANGED) + } + + TRACK_ENDED -> { + if (service.repeatMode == REPEAT_MODE_NONE && service.isLastTrack) { + service.notifyChange(PLAY_STATE_CHANGED) + service.seek(0) + } else { + service.playNextSong(false) + } + sendEmptyMessage(RELEASE_WAKELOCK) + } + + RELEASE_WAKELOCK -> service.releaseWakeLock() + + PLAY_SONG -> service.playSongAtImpl(msg.arg1) + + SET_POSITION -> { + service.openTrackAndPrepareNextAt(msg.arg1) + service.notifyChange(PLAY_STATE_CHANGED) + } + + PREPARE_NEXT -> service.prepareNextImpl() + + RESTORE_QUEUES -> service.restoreQueuesAndPositionIfNecessary() + + FOCUS_CHANGE -> when (msg.arg1) { + AudioManager.AUDIOFOCUS_GAIN -> { + if (!service.isPlaying && service.pausedByTransientLossOfFocus) { + service.play() + service.pausedByTransientLossOfFocus = false + } + removeMessages(DUCK) + sendEmptyMessage(UN_DUCK) + } + + AudioManager.AUDIOFOCUS_LOSS -> + // Lost focus for an unbounded amount of time: stop playback and release media playback + service.pause() + + AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> { + // Lost focus for a short time, but we have to stop + // playback. We don't release the media playback because playback + // is likely to resume + val wasPlaying = service.isPlaying + service.pause() + service.pausedByTransientLossOfFocus = wasPlaying + } + + AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> { + // Lost focus for a short time, but it's ok to keep playing + // at an attenuated level + removeMessages(UN_DUCK) + sendEmptyMessage(DUCK) + } + } + } + } + } + + inner class SongPlayCountHelper { + + private val stopWatch = StopWatch() + var song = Song.EMPTY_SONG + private set + + internal fun shouldBumpPlayCount(): Boolean { + return song.duration * 0.5 < stopWatch.elapsedTime + } + + internal fun notifySongChanged(song: Song) { + synchronized(this) { + stopWatch.reset() + this.song = song + } + } + + internal fun notifyPlayStateChanged(isPlaying: Boolean) { + synchronized(this) { + if (isPlaying) { + stopWatch.start() + } else { + stopWatch.pause() + } + } + } + } + + inner class MusicBinder : Binder() { + val service: MusicService + get() = this@MusicService + } + + inner class MediaStoreObserver internal constructor(private val mHandler: Handler) : ContentObserver(mHandler), Runnable { + + override fun onChange(selfChange: Boolean) { + // if a change is detected, remove any scheduled callback + // then post a new one. This is intended to prevent closely + // spaced events from generating multiple refresh calls + mHandler.removeCallbacks(this) + mHandler.postDelayed(this, 500) + } + + override fun run() { + // actually call refresh when the delayed callback fires + // do not send a sticky broadcast here + handleAndSendChangeInternal(MEDIA_STORE_CHANGED) + } + } + + inner class ThrottledSeekHandler internal constructor(private val mHandler: Handler) : Runnable { + + internal fun notifySeek() { + mHandler.removeCallbacks(this) + mHandler.postDelayed(this, 500) + } + + override fun run() { + savePositionInTrack() + sendPublicIntent(PLAY_STATE_CHANGED) // for musixmatch synced lyrics + } + } + + companion object { + val TAG: String = MusicService::class.java.simpleName + + const val SAVED_POSITION = "POSITION" + const val SAVED_POSITION_IN_TRACK = "POSITION_IN_TRACK" + const val SAVED_SHUFFLE_MODE = "SHUFFLE_MODE" + const val SAVED_REPEAT_MODE = "REPEAT_MODE" + + const val RELEASE_WAKELOCK = 0 + const val TRACK_ENDED = 1 + const val TRACK_WENT_TO_NEXT = 2 + const val PLAY_SONG = 3 + const val PREPARE_NEXT = 4 + const val SET_POSITION = 5 + const val RESTORE_QUEUES = 9 + const val SHUFFLE_MODE_NONE = 0 + const val SHUFFLE_MODE_SHUFFLE = 1 + const val REPEAT_MODE_NONE = 0 + const val REPEAT_MODE_ALL = 1 + const val REPEAT_MODE_THIS = 2 + const val SAVE_QUEUES = 0 + const val FOCUS_CHANGE = 6 + const val DUCK = 7 + const val UN_DUCK = 8 + const val MEDIA_SESSION_ACTIONS = (PlaybackStateCompat.ACTION_PLAY + or PlaybackStateCompat.ACTION_PAUSE + or PlaybackStateCompat.ACTION_PLAY_PAUSE + or PlaybackStateCompat.ACTION_SKIP_TO_NEXT + or PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS + or PlaybackStateCompat.ACTION_STOP + or PlaybackStateCompat.ACTION_SEEK_TO) + + private fun getTrackUri(song: Song): String { + return MusicUtil.getSongFileUri(song.id).toString() + } + + private fun copy(bitmap: Bitmap): Bitmap? { + var config: Bitmap.Config? = bitmap.config + if (config == null) { + config = Bitmap.Config.RGB_565 + } + try { + return bitmap.copy(config, false) + } catch (e: OutOfMemoryError) { + e.printStackTrace() + return null + } + + } + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/service/WearBrowserService.java b/app/src/main/java/code/name/monkey/retromusic/service/WearBrowserService.java index 27072584..00c5ff7e 100644 --- a/app/src/main/java/code/name/monkey/retromusic/service/WearBrowserService.java +++ b/app/src/main/java/code/name/monkey/retromusic/service/WearBrowserService.java @@ -194,9 +194,9 @@ public class WearBrowserService extends MediaBrowserService { } else { switch (Integer.parseInt(Character.toString(parentId.charAt(0)))) { case TYPE_ARTIST: - List artistList = ArtistLoader.getAllArtists(mContext).blockingFirst(); + List artistList = ArtistLoader.INSTANCE.getAllArtists(mContext).blockingFirst(); for (Artist artist : artistList) { - String albumNmber = String.format("%d %s", artist.albums.size(), artist.albums.size() > 1 ? "Albums" : "Album"); + String albumNmber = String.format("%d %s", artist.getAlbums().size(), artist.getAlbums().size() > 1 ? "Albums" : "Album"); String songCount = String.format("%d %s", artist.getSongs().size(), artist.getSongs().size() > 1 ? "Songs" : "Song"); fillMediaItems(mediaItems, Integer.toString(TYPE_ARTIST_SONG_ALBUMS) + Long.toString(artist.getId()), @@ -215,9 +215,9 @@ public class WearBrowserService extends MediaBrowserService { Uri.parse("android.resource://code.name.monkey.retromusic/drawable/default_artist_art"), MediaBrowser.MediaItem.FLAG_BROWSABLE); - List artistAlbums = ArtistLoader.getArtist(mContext, Integer.parseInt(parentId.substring(1))).blockingFirst().albums; //ArtistAlbumLoader.getAlbumsForArtist(mContext, Long.parseLong(parentId.substring(1))); + List artistAlbums = ArtistLoader.INSTANCE.getArtist(mContext, Integer.parseInt(parentId.substring(1))).blockingFirst().getAlbums(); //ArtistAlbumLoader.getAlbumsForArtist(mContext, Long.parseLong(parentId.substring(1))); for (Album album : artistAlbums) { - String songCount = String.format("%d %s", album.songs.size(), album.songs.size() > 1 ? "Songs" : "Song"); + String songCount = String.format("%d %s", album.getSongs().size(), album.getSongs().size() > 1 ? "Songs" : "Song"); fillMediaItems(mediaItems, Integer.toString(TYPE_ALBUM_SONGS) + Long.toString(album.getId()), album.getTitle(), @@ -227,7 +227,7 @@ public class WearBrowserService extends MediaBrowserService { } break; case TYPE_ALBUM: - List albumList = AlbumLoader.getAllAlbums(mContext).blockingFirst(); + List albumList = AlbumLoader.Companion.getAllAlbums(mContext).blockingFirst(); for (Album album : albumList) { fillMediaItems(mediaItems, Integer.toString(TYPE_ALBUM_SONGS) + Long.toString(album.getId()), @@ -238,43 +238,43 @@ public class WearBrowserService extends MediaBrowserService { } break; case TYPE_SONG: - List songList = SongLoader.Companion.getAllSongs(mContext).blockingFirst(); + List songList = SongLoader.INSTANCE.getAllSongs(mContext).blockingFirst(); for (Song song : songList) { fillMediaItems(mediaItems, - String.valueOf(song.id), - song.title, - song.artistName, + String.valueOf(song.getId()), + song.getTitle(), + song.getArtistName(), Uri.parse("android.resource://code.name.monkey.retromusic/drawable/default_album_art"), MediaBrowser.MediaItem.FLAG_PLAYABLE); } break; case TYPE_ALBUM_SONGS: - List albumSongList = AlbumLoader.getAlbum(mContext, Integer.parseInt(parentId.substring(1))).blockingFirst().songs; + List albumSongList = AlbumLoader.Companion.getAlbum(mContext, Integer.parseInt(parentId.substring(1))).blockingFirst().getSongs(); for (Song song : albumSongList) { fillMediaItems(mediaItems, - String.valueOf(song.id), - song.title, - song.artistName, + String.valueOf(song.getId()), + song.getTitle(), + song.getArtistName(), Uri.parse("android.resource://code.name.monkey.retromusic/drawable/default_album_art"), MediaBrowser.MediaItem.FLAG_PLAYABLE); } break; case TYPE_ARTIST_ALL_SONGS: - List artistSongs = ArtistLoader.getArtist(mContext, Integer.parseInt(parentId.substring(1))).blockingFirst().getSongs(); + List artistSongs = ArtistLoader.INSTANCE.getArtist(mContext, Integer.parseInt(parentId.substring(1))).blockingFirst().getSongs(); for (Song song : artistSongs) { fillMediaItems(mediaItems, - String.valueOf(song.id), - song.title, - song.albumName, + String.valueOf(song.getId()), + song.getTitle(), + song.getAlbumName(), Uri.parse("android.resource://code.name.monkey.retromusic/drawable/default_album_art"), MediaBrowser.MediaItem.FLAG_PLAYABLE); } break; case TYPE_PLAYLIST: - List playlistList = PlaylistLoader.getAllPlaylists(mContext).blockingFirst(); + List playlistList = PlaylistLoader.INSTANCE.getAllPlaylists(mContext).blockingFirst(); for (Playlist playlist : playlistList) { - int size = PlaylistSongsLoader.getPlaylistSongList(mContext, playlist).blockingFirst().size(); + int size = PlaylistSongsLoader.INSTANCE.getPlaylistSongList(mContext, playlist).blockingFirst().size(); String songCount = String.format("%d %s", size, size > 1 ? "Songs" : "Song"); fillMediaItems(mediaItems, Integer.toString(TYPE_PLAYLIST_ALL_SONGS) + Long.toString(playlist.id), @@ -285,12 +285,12 @@ public class WearBrowserService extends MediaBrowserService { } break; case TYPE_PLAYLIST_ALL_SONGS: - List playlistSongs = PlaylistSongsLoader.getPlaylistSongList(mContext, Integer.parseInt(parentId.substring(1))).blockingFirst(); + List playlistSongs = PlaylistSongsLoader.INSTANCE.getPlaylistSongList(mContext, Integer.parseInt(parentId.substring(1))).blockingFirst(); for (Song song : playlistSongs) { fillMediaItems(mediaItems, - String.valueOf(song.id), - song.title, - song.albumName, + String.valueOf(song.getId()), + song.getTitle(), + song.getAlbumName(), Uri.parse("android.resource://code.name.monkey.retromusic/drawable/default_album_art"), MediaBrowser.MediaItem.FLAG_PLAYABLE); } @@ -326,8 +326,8 @@ public class WearBrowserService extends MediaBrowserService { long songId = Long.parseLong(mediaId); setSessionActive(); ArrayList songs = new ArrayList<>(); - songs.add(SongLoader.Companion.getSong(mContext, Integer.parseInt(mediaId)).blockingFirst()); - MusicPlayerRemote.openQueue(songs, 0, true); + songs.add(SongLoader.INSTANCE.getSong(mContext, Integer.parseInt(mediaId)).blockingFirst()); + MusicPlayerRemote.INSTANCE.openQueue(songs, 0, true); } @Override diff --git a/app/src/main/java/code/name/monkey/retromusic/service/daydream/RetroMusicAlbums.java b/app/src/main/java/code/name/monkey/retromusic/service/daydream/RetroMusicAlbums.java deleted file mode 100644 index 134cd7e2..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/service/daydream/RetroMusicAlbums.java +++ /dev/null @@ -1,186 +0,0 @@ -package code.name.monkey.retromusic.service.daydream; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.service.dreams.DreamService; -import androidx.transition.TransitionManager; -import androidx.recyclerview.widget.DefaultItemAnimator; -import androidx.recyclerview.widget.GridLayoutManager; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import com.bumptech.glide.Glide; -import code.name.monkey.retromusic.loaders.SongLoader; -import code.name.monkey.retromusic.model.Song; - -import java.util.ArrayList; - -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.Unbinder; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.glide.RetroMusicColoredTarget; -import code.name.monkey.retromusic.glide.SongGlideRequest; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.ui.adapter.base.MediaEntryViewHolder; -import io.reactivex.Observable; -import io.reactivex.ObservableSource; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.CompositeDisposable; -import io.reactivex.functions.Function; -import io.reactivex.schedulers.Schedulers; - -/** - * Created by hemanths on 25/09/17. - */ - -public class RetroMusicAlbums extends DreamService { - @BindView(R.id.recycler_view) - RecyclerView mRecyclerView; - @BindView(R.id.title) - TextView mTitle; - @BindView(R.id.text) - TextView mText; - @BindView(R.id.title_container) - ViewGroup mViewGroup; - - private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (intent == null) { - return; - } - String artist = intent.getStringExtra("artist"); - String track = intent.getStringExtra("track"); - if (mViewGroup != null) { - mViewGroup.setVisibility(View.VISIBLE); - TransitionManager.beginDelayedTransition(mViewGroup); - if (mTitle != null) { - mTitle.setText(track); - } - if (mText != null) { - mText.setText(artist); - } - } - - } - }; - private Unbinder unbinder; - private CompositeDisposable mDisposable; - - @Override - public void onAttachedToWindow() { - super.onAttachedToWindow(); - - View view = LayoutInflater.from(this).inflate(R.layout.dream_service, null); - setContentView(view); - unbinder = ButterKnife.bind(this, view); - - mRecyclerView.setItemAnimator(new DefaultItemAnimator()); - GridLayoutManager layoutManager = new GridLayoutManager(this, 4, LinearLayoutManager.VERTICAL, false); - mRecyclerView.setLayoutManager(layoutManager); - - - mDisposable.add(getPlayingQueue() - .subscribeOn(Schedulers.computation()) - .observeOn(AndroidSchedulers.mainThread()) - .flatMap((Function, ObservableSource>>) songs -> Observable.create(e -> { - if (songs.isEmpty()) { - e.onNext(SongLoader.Companion.getAllSongs(RetroMusicAlbums.this).blockingFirst()); - } else { - e.onNext(songs); - } - e.onComplete(); - })) - .subscribe(songs -> { - if (songs.size() > 0) { - ImagesAdapter imagesAdapter = new ImagesAdapter(songs); - mRecyclerView.setAdapter(imagesAdapter); - } - })); - - } - - @Override - public void onCreate() { - super.onCreate(); - setInteractive(true); - setFullscreen(true); - - mDisposable = new CompositeDisposable(); - - IntentFilter iF = new IntentFilter(); - iF.addAction("com.android.music.musicservicecommand"); - iF.addAction("com.android.music.metachanged"); - registerReceiver(mBroadcastReceiver, iF); - - } - - @Override - public void onDestroy() { - super.onDestroy(); - unbinder.unbind(); - mDisposable.clear(); - unregisterReceiver(mBroadcastReceiver); - } - - private Observable> getPlayingQueue() { - return Observable.just(MusicPlayerRemote.getPlayingQueue()); - } - - class ImagesAdapter extends RecyclerView.Adapter { - - private final ArrayList list; - - public ImagesAdapter(ArrayList songs) { - this.list = songs; - } - - @Override - public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) { - return new ViewHolder(LayoutInflater.from(getApplicationContext()) - .inflate(R.layout.image, viewGroup, false)); - } - - @Override - public void onBindViewHolder(ViewHolder holder, int i) { - Song song = list.get(i); - SongGlideRequest.Builder.from(Glide.with(getApplicationContext()), song) - .checkIgnoreMediaStore(getApplicationContext()) - .generatePalette(getApplicationContext()).build() - .override(400, 400) - .into(new RetroMusicColoredTarget(holder.image) { - - @Override - public void onColorReady(int color) { - - } - }); - - } - - @Override - public int getItemCount() { - return list.size(); - } - - class ViewHolder extends MediaEntryViewHolder { - - public ViewHolder(View itemView) { - super(itemView); - } - - @Override - public void onClick(View v) { - super.onClick(v); - MusicPlayerRemote.openQueue(list, getAdapterPosition(), true); - } - } - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/service/daydream/RetroMusicAlbums.kt b/app/src/main/java/code/name/monkey/retromusic/service/daydream/RetroMusicAlbums.kt new file mode 100644 index 00000000..b8404c3c --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/service/daydream/RetroMusicAlbums.kt @@ -0,0 +1,161 @@ +package code.name.monkey.retromusic.service.daydream + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.service.dreams.DreamService +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.DefaultItemAnimator +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.RecyclerView +import androidx.transition.TransitionManager +import butterknife.BindView +import butterknife.ButterKnife +import butterknife.Unbinder +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.glide.RetroMusicColoredTarget +import code.name.monkey.retromusic.glide.SongGlideRequest +import code.name.monkey.retromusic.helper.MusicPlayerRemote +import code.name.monkey.retromusic.loaders.SongLoader +import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.ui.adapter.base.MediaEntryViewHolder +import com.bumptech.glide.Glide +import io.reactivex.Observable +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.schedulers.Schedulers +import java.util.* + +/** + * Created by hemanths on 25/09/17. + */ + +class RetroMusicAlbums : DreamService() { + @BindView(R.id.recycler_view) + internal var mRecyclerView: RecyclerView? = null + @BindView(R.id.title) + internal var mTitle: TextView? = null + @BindView(R.id.text) + internal var mText: TextView? = null + @BindView(R.id.title_container) + internal var mViewGroup: ViewGroup? = null + + private val mBroadcastReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent?) { + if (intent == null) { + return + } + val artist = intent.getStringExtra("artist") + val track = intent.getStringExtra("track") + if (mViewGroup != null) { + mViewGroup!!.visibility = View.VISIBLE + TransitionManager.beginDelayedTransition(mViewGroup!!) + if (mTitle != null) { + mTitle!!.text = track + } + if (mText != null) { + mText!!.text = artist + } + } + + } + } + private var unbinder: Unbinder? = null + private var mDisposable: CompositeDisposable? = null + + private val playingQueue: Observable> + get() = Observable.just(MusicPlayerRemote.playingQueue) + + override fun onAttachedToWindow() { + super.onAttachedToWindow() + + val view = LayoutInflater.from(this).inflate(R.layout.dream_service, null) + setContentView(view) + unbinder = ButterKnife.bind(this, view) + + mRecyclerView!!.itemAnimator = DefaultItemAnimator() + val layoutManager = GridLayoutManager(this, 4, RecyclerView.VERTICAL, false) + mRecyclerView!!.layoutManager = layoutManager + + + mDisposable!!.add(playingQueue + .subscribeOn(Schedulers.computation()) + .observeOn(AndroidSchedulers.mainThread()) + .flatMap { + return@flatMap Observable.create> { e -> + if (it.isEmpty()) { + e.onNext(SongLoader.getAllSongs(this@RetroMusicAlbums).blockingFirst()) + } else { + e.onNext(it) + } + e.onComplete() + } + } + .subscribe { songs -> + if (songs.size > 0) { + val imagesAdapter = ImagesAdapter(songs) + mRecyclerView!!.adapter = imagesAdapter + } + }) + + } + + override fun onCreate() { + super.onCreate() + isInteractive = true + isFullscreen = true + + mDisposable = CompositeDisposable() + + val iF = IntentFilter() + iF.addAction("com.android.music.musicservicecommand") + iF.addAction("com.android.music.metachanged") + registerReceiver(mBroadcastReceiver, iF) + + } + + override fun onDestroy() { + super.onDestroy() + unbinder!!.unbind() + mDisposable!!.clear() + unregisterReceiver(mBroadcastReceiver) + } + + internal inner class ImagesAdapter(private val list: ArrayList) : RecyclerView.Adapter() { + + override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): ViewHolder { + return ViewHolder(LayoutInflater.from(applicationContext) + .inflate(R.layout.image, viewGroup, false)) + } + + override fun onBindViewHolder(holder: ViewHolder, i: Int) { + val song = list[i] + SongGlideRequest.Builder.from(Glide.with(applicationContext), song) + .checkIgnoreMediaStore(applicationContext) + .generatePalette(applicationContext).build() + .override(400, 400) + .into(object : RetroMusicColoredTarget(holder.image!!) { + override fun onColorReady(color: Int) { + + } + }) + + } + + override fun getItemCount(): Int { + return list.size + } + + internal inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) { + + override fun onClick(v: View?) { + super.onClick(v) + MusicPlayerRemote.openQueue(list, adapterPosition, true) + } + } + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/service/notification/PlayingNotificationImpl.java b/app/src/main/java/code/name/monkey/retromusic/service/notification/PlayingNotificationImpl.java index 772029db..8cffe5f2 100644 --- a/app/src/main/java/code/name/monkey/retromusic/service/notification/PlayingNotificationImpl.java +++ b/app/src/main/java/code/name/monkey/retromusic/service/notification/PlayingNotificationImpl.java @@ -28,7 +28,6 @@ import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper; import code.name.monkey.retromusic.model.Song; import code.name.monkey.retromusic.service.MusicService; import code.name.monkey.retromusic.ui.activities.MainActivity; -import code.name.monkey.retromusic.ui.activities.NowPayingActivity; import code.name.monkey.retromusic.util.PreferenceUtil; import code.name.monkey.retromusic.util.RetroColorUtil; import code.name.monkey.retromusic.util.RetroUtil; @@ -57,27 +56,27 @@ public class PlayingNotificationImpl extends PlayingNotification { final RemoteViews notificationLayoutBig = new RemoteViews(service.getPackageName(), R.layout.notification_big); - if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName)) { + if (TextUtils.isEmpty(song.getTitle()) && TextUtils.isEmpty(song.getArtistName())) { notificationLayout.setViewVisibility(R.id.media_titles, View.INVISIBLE); } else { notificationLayout.setViewVisibility(R.id.media_titles, View.VISIBLE); - notificationLayout.setTextViewText(R.id.title, song.title); - notificationLayout.setTextViewText(R.id.text, song.artistName); + notificationLayout.setTextViewText(R.id.title, song.getTitle()); + notificationLayout.setTextViewText(R.id.text, song.getArtistName()); } - if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName) && TextUtils - .isEmpty(song.albumName)) { + if (TextUtils.isEmpty(song.getTitle()) && TextUtils.isEmpty(song.getArtistName()) && TextUtils + .isEmpty(song.getAlbumName())) { notificationLayoutBig.setViewVisibility(R.id.media_titles, View.INVISIBLE); } else { notificationLayoutBig.setViewVisibility(R.id.media_titles, View.VISIBLE); - notificationLayoutBig.setTextViewText(R.id.title, song.title); - notificationLayoutBig.setTextViewText(R.id.text, song.artistName); - notificationLayoutBig.setTextViewText(R.id.text2, song.albumName); + notificationLayoutBig.setTextViewText(R.id.title, song.getTitle()); + notificationLayoutBig.setTextViewText(R.id.text, song.getArtistName()); + notificationLayoutBig.setTextViewText(R.id.text2, song.getAlbumName()); } linkButtons(notificationLayout, notificationLayoutBig); - Intent action = new Intent(service, NowPayingActivity.class); + Intent action = new Intent(service, MainActivity.class); action.putExtra("expand", true); action.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); diff --git a/app/src/main/java/code/name/monkey/retromusic/service/notification/PlayingNotificationImpl24.java b/app/src/main/java/code/name/monkey/retromusic/service/notification/PlayingNotificationImpl24.java index a361d23a..2efe0fbf 100644 --- a/app/src/main/java/code/name/monkey/retromusic/service/notification/PlayingNotificationImpl24.java +++ b/app/src/main/java/code/name/monkey/retromusic/service/notification/PlayingNotificationImpl24.java @@ -16,13 +16,12 @@ import com.bumptech.glide.request.target.SimpleTarget; import androidx.core.app.NotificationCompat; import androidx.media.app.NotificationCompat.MediaStyle; -import code.name.monkey.retromusic.Constants; import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.glide.SongGlideRequest; import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper; import code.name.monkey.retromusic.model.Song; import code.name.monkey.retromusic.service.MusicService; -import code.name.monkey.retromusic.ui.activities.NowPayingActivity; +import code.name.monkey.retromusic.ui.activities.MainActivity; import code.name.monkey.retromusic.util.PreferenceUtil; import code.name.monkey.retromusic.util.RetroColorUtil; @@ -43,14 +42,14 @@ public class PlayingNotificationImpl24 extends PlayingNotification { final int playButtonResId = isPlaying ? R.drawable.ic_pause_white_24dp : R.drawable.ic_play_arrow_white_24dp; - Intent action = new Intent(service, NowPayingActivity.class); + Intent action = new Intent(service, MainActivity.class); action.putExtra("expand", true); action.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); final PendingIntent clickIntent = PendingIntent .getActivity(service, 0, action, PendingIntent.FLAG_UPDATE_CURRENT); final ComponentName serviceName = new ComponentName(service, MusicService.class); - Intent intent = new Intent(Constants.ACTION_QUIT); + Intent intent = new Intent(ACTION_QUIT); intent.setComponent(serviceName); final PendingIntent deleteIntent = PendingIntent.getService(service, 0, intent, 0); @@ -106,9 +105,9 @@ public class PlayingNotificationImpl24 extends PlayingNotification { .setLargeIcon(bitmap) .setContentIntent(clickIntent) .setDeleteIntent(deleteIntent) - .setContentTitle(Html.fromHtml("" + song.title + "")) - .setContentText(song.artistName) - .setSubText(Html.fromHtml("" + song.albumName + "")) + .setContentTitle(Html.fromHtml("" + song.getTitle() + "")) + .setContentText(song.getArtistName()) + .setSubText(Html.fromHtml("" + song.getAlbumName() + "")) .setOngoing(isPlaying) .setShowWhen(false) .addAction(previousAction) diff --git a/app/src/main/java/code/name/monkey/retromusic/service/notification/PlayingNotificationOreo.java b/app/src/main/java/code/name/monkey/retromusic/service/notification/PlayingNotificationOreo.java deleted file mode 100644 index fdbf1a3c..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/service/notification/PlayingNotificationOreo.java +++ /dev/null @@ -1,248 +0,0 @@ -package code.name.monkey.retromusic.service.notification; - -import android.app.PendingIntent; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.res.TypedArray; -import android.graphics.Bitmap; -import android.graphics.Color; -import android.graphics.drawable.Drawable; -import android.widget.RemoteViews; - -import com.bumptech.glide.Glide; -import com.bumptech.glide.request.animation.GlideAnimation; -import com.bumptech.glide.request.target.SimpleTarget; -import com.bumptech.glide.request.target.Target; - -import androidx.annotation.Nullable; -import androidx.core.app.NotificationCompat; -import code.name.monkey.appthemehelper.util.ColorUtil; -import code.name.monkey.appthemehelper.util.MaterialValueHelper; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.glide.SongGlideRequest; -import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.service.MusicService; -import code.name.monkey.retromusic.ui.activities.NowPayingActivity; -import code.name.monkey.retromusic.util.PreferenceUtil; -import code.name.monkey.retromusic.util.RetroUtil; -import code.name.monkey.retromusic.util.color.MediaNotificationProcessor; - -import static code.name.monkey.retromusic.Constants.ACTION_QUIT; -import static code.name.monkey.retromusic.Constants.ACTION_REWIND; -import static code.name.monkey.retromusic.Constants.ACTION_SKIP; -import static code.name.monkey.retromusic.Constants.ACTION_TOGGLE_PAUSE; -import static code.name.monkey.retromusic.util.RetroUtil.createBitmap; - -/** - * @author Hemanth S (h4h13). - */ -public class PlayingNotificationOreo extends PlayingNotification { - - private Target target; - - private RemoteViews getCombinedRemoteViews(boolean collapsed, Song song) { - RemoteViews remoteViews = new RemoteViews(service.getPackageName(), - collapsed ? R.layout.layout_notification_collapsed : R.layout.layout_notification_expanded); - - remoteViews.setTextViewText(R.id.appName, - service.getString(R.string.app_name) + " • " + song.albumName); - remoteViews.setTextViewText(R.id.title, song.title); - remoteViews.setTextViewText(R.id.subtitle, song.artistName); - - TypedArray typedArray = service - .obtainStyledAttributes(new int[]{android.R.attr.selectableItemBackground}); - int selectableItemBackground = typedArray.getResourceId(0, 0); - typedArray.recycle(); - - remoteViews.setInt(R.id.content, "setBackgroundResource", selectableItemBackground); - - linkButtons(remoteViews); - - //setNotificationContent(remoteViews, ColorUtil.isColorLight(backgroundColor)); - return remoteViews; - } - - @Override - public void update() { - stopped = false; - final Song song = service.getCurrentSong(); - final boolean isPlaying = service.isPlaying(); - - final RemoteViews notificationLayout = getCombinedRemoteViews(true, song); - final RemoteViews notificationLayoutBig = getCombinedRemoteViews(false, song); - - Intent action = new Intent(service, NowPayingActivity.class); - action.putExtra("expand", true); - action.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); - - final PendingIntent clickIntent = PendingIntent - .getActivity(service, 0, action, PendingIntent.FLAG_UPDATE_CURRENT); - final PendingIntent deleteIntent = buildPendingIntent(service, ACTION_QUIT, null); - - final NotificationCompat.Builder builder = new NotificationCompat.Builder(service, - NOTIFICATION_CHANNEL_ID) - .setSmallIcon(R.drawable.ic_notification) - .setContentIntent(clickIntent) - .setDeleteIntent(deleteIntent) - .setCategory(NotificationCompat.CATEGORY_SERVICE) - .setPriority(NotificationCompat.PRIORITY_MAX) - .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) - .setCustomContentView(notificationLayout) - .setCustomBigContentView(notificationLayoutBig) - .setOngoing(isPlaying); - - final int bigNotificationImageSize = service.getResources() - .getDimensionPixelSize(R.dimen.notification_big_image_size); - service.runOnUiThread(new Runnable() { - @Override - public void run() { - if (target != null) { - Glide.clear(target); - } - target = SongGlideRequest.Builder.from(Glide.with(service), song) - .checkIgnoreMediaStore(service) - .generatePalette(service).build() - .into(new SimpleTarget(bigNotificationImageSize, - bigNotificationImageSize) { - @Override - public void onResourceReady(BitmapPaletteWrapper resource, - GlideAnimation glideAnimation) { - - MediaNotificationProcessor mediaNotificationProcessor = new MediaNotificationProcessor( - service, service, (i, i2) -> update(resource.getBitmap(), i, i2)); - mediaNotificationProcessor.processNotification(resource.getBitmap()); - - } - - @Override - public void onLoadFailed(Exception e, Drawable errorDrawable) { - super.onLoadFailed(e, errorDrawable); - update(null, Color.WHITE, Color.BLACK); - } - - private void update(@Nullable Bitmap bitmap, int bgColor, int textColor) { - if (bitmap != null) { - notificationLayout.setImageViewBitmap(R.id.largeIcon, bitmap); - notificationLayoutBig.setImageViewBitmap(R.id.largeIcon, bitmap); - } else { - notificationLayout - .setImageViewResource(R.id.largeIcon, R.drawable.default_album_art); - notificationLayoutBig - .setImageViewResource(R.id.largeIcon, R.drawable.default_album_art); - } - - if (!PreferenceUtil.getInstance().coloredNotification()) { - bgColor = Color.WHITE; - } - setBackgroundColor(bgColor); - setNotificationContent(ColorUtil.isColorLight(bgColor)); - - if (stopped) { - return; // notification has been stopped before loading was finished - } - updateNotifyModeAndPostNotification(builder.build()); - } - - private void setBackgroundColor(int color) { - - notificationLayout.setInt(R.id.image, "setBackgroundColor", color); - notificationLayoutBig.setInt(R.id.image, "setBackgroundColor", color); - - notificationLayout.setInt(R.id.foregroundImage, "setColorFilter", color); - notificationLayoutBig.setInt(R.id.foregroundImage, "setColorFilter", color); - } - - private void setNotificationContent(boolean dark) { - int primary = MaterialValueHelper.getPrimaryTextColor(service, dark); - int secondary = MaterialValueHelper.getSecondaryTextColor(service, dark); - - Bitmap close = createBitmap( - RetroUtil - .getTintedVectorDrawable(service, R.drawable.ic_close_white_24dp, primary), - NOTIFICATION_CONTROLS_SIZE_MULTIPLIER); - Bitmap prev = createBitmap( - RetroUtil - .getTintedVectorDrawable(service, R.drawable.ic_skip_previous_white_24dp, - primary), NOTIFICATION_CONTROLS_SIZE_MULTIPLIER); - Bitmap next = createBitmap( - RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_next_white_24dp, - primary), NOTIFICATION_CONTROLS_SIZE_MULTIPLIER); - Bitmap playPause = createBitmap(RetroUtil.getTintedVectorDrawable(service, - isPlaying ? R.drawable.ic_pause_white_24dp - : R.drawable.ic_play_arrow_white_24dp, primary), - NOTIFICATION_CONTROLS_SIZE_MULTIPLIER); - - notificationLayout.setTextColor(R.id.title, primary); - notificationLayout.setTextColor(R.id.subtitle, secondary); - notificationLayout.setTextColor(R.id.appName, secondary); - - notificationLayout.setImageViewBitmap(R.id.action_prev, prev); - notificationLayout.setImageViewBitmap(R.id.action_next, next); - notificationLayout.setImageViewBitmap(R.id.action_play_pause, playPause); - - notificationLayoutBig.setTextColor(R.id.title, primary); - notificationLayoutBig.setTextColor(R.id.subtitle, secondary); - notificationLayoutBig.setTextColor(R.id.appName, secondary); - - notificationLayoutBig.setImageViewBitmap(R.id.action_quit, close); - notificationLayoutBig.setImageViewBitmap(R.id.action_prev, prev); - notificationLayoutBig.setImageViewBitmap(R.id.action_next, next); - notificationLayoutBig.setImageViewBitmap(R.id.action_play_pause, playPause); - - notificationLayout.setImageViewBitmap(R.id.smallIcon, - createBitmap(RetroUtil - .getTintedVectorDrawable(service, R.drawable.ic_notification, secondary), - 0.6f)); - notificationLayoutBig.setImageViewBitmap(R.id.smallIcon, - createBitmap(RetroUtil - .getTintedVectorDrawable(service, R.drawable.ic_notification, secondary), - 0.6f)); - - notificationLayout.setInt(R.id.arrow, "setColorFilter", secondary); - notificationLayoutBig.setInt(R.id.arrow, "setColorFilter", secondary); - - } - }); - } - }); - - if (stopped) { - return; // notification has been stopped before loading was finished - } - updateNotifyModeAndPostNotification(builder.build()); - } - - - private PendingIntent buildPendingIntent(Context context, final String action, - final ComponentName serviceName) { - Intent intent = new Intent(action); - intent.setComponent(serviceName); - return PendingIntent.getService(context, 0, intent, 0); - } - - - private void linkButtons(final RemoteViews notificationLayout) { - PendingIntent pendingIntent; - - final ComponentName serviceName = new ComponentName(service, MusicService.class); - - // Previous track - pendingIntent = buildPendingIntent(service, ACTION_REWIND, serviceName); - notificationLayout.setOnClickPendingIntent(R.id.action_prev, pendingIntent); - - // Play and pause - pendingIntent = buildPendingIntent(service, ACTION_TOGGLE_PAUSE, serviceName); - notificationLayout.setOnClickPendingIntent(R.id.action_play_pause, pendingIntent); - - // Next track - pendingIntent = buildPendingIntent(service, ACTION_SKIP, serviceName); - notificationLayout.setOnClickPendingIntent(R.id.action_next, pendingIntent); - - // Close - pendingIntent = buildPendingIntent(service, ACTION_QUIT, serviceName); - notificationLayout.setOnClickPendingIntent(R.id.action_quit, pendingIntent); - } - -} diff --git a/app/src/main/java/code/name/monkey/retromusic/service/notification/PlayingNotificationOreo.kt b/app/src/main/java/code/name/monkey/retromusic/service/notification/PlayingNotificationOreo.kt new file mode 100644 index 00000000..85b5aa40 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/service/notification/PlayingNotificationOreo.kt @@ -0,0 +1,221 @@ +package code.name.monkey.retromusic.service.notification + +import android.app.PendingIntent +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.graphics.Bitmap +import android.graphics.Color +import android.graphics.drawable.Drawable +import android.widget.RemoteViews +import androidx.core.app.NotificationCompat +import code.name.monkey.appthemehelper.util.ColorUtil +import code.name.monkey.appthemehelper.util.MaterialValueHelper +import code.name.monkey.retromusic.Constants.ACTION_QUIT +import code.name.monkey.retromusic.Constants.ACTION_REWIND +import code.name.monkey.retromusic.Constants.ACTION_SKIP +import code.name.monkey.retromusic.Constants.ACTION_TOGGLE_PAUSE +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.glide.SongGlideRequest +import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper +import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.service.MusicService +import code.name.monkey.retromusic.ui.activities.MainActivity +import code.name.monkey.retromusic.util.PreferenceUtil +import code.name.monkey.retromusic.util.RetroUtil +import code.name.monkey.retromusic.util.RetroUtil.createBitmap +import code.name.monkey.retromusic.util.color.MediaNotificationProcessor +import com.bumptech.glide.Glide +import com.bumptech.glide.request.animation.GlideAnimation +import com.bumptech.glide.request.target.SimpleTarget +import com.bumptech.glide.request.target.Target + +/** + * @author Hemanth S (h4h13). + */ +class PlayingNotificationOreo : PlayingNotification() { + + private var target: Target? = null + + private fun getCombinedRemoteViews(collapsed: Boolean, song: Song): RemoteViews { + val remoteViews = RemoteViews(service.packageName, + if (collapsed) R.layout.layout_notification_collapsed else R.layout.layout_notification_expanded) + + remoteViews.setTextViewText(R.id.appName, service.getString(R.string.app_name) + " • " + song.albumName) + remoteViews.setTextViewText(R.id.title, song.title) + remoteViews.setTextViewText(R.id.subtitle, song.artistName) + + val typedArray = service.obtainStyledAttributes(intArrayOf(android.R.attr.selectableItemBackground)) + val selectableItemBackground = typedArray.getResourceId(0, 0) + typedArray.recycle() + + remoteViews.setInt(R.id.content, "setBackgroundResource", selectableItemBackground) + + linkButtons(remoteViews) + + //setNotificationContent(remoteViews, ColorUtil.isColorLight(backgroundColor)); + return remoteViews + } + + override fun update() { + stopped = false + val song = service.currentSong + val isPlaying = service.isPlaying + + val notificationLayout = getCombinedRemoteViews(true, song) + val notificationLayoutBig = getCombinedRemoteViews(false, song) + + val action = Intent(service, MainActivity::class.java) + action.putExtra("expand", true) + action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP + + val clickIntent = PendingIntent + .getActivity(service, 0, action, PendingIntent.FLAG_UPDATE_CURRENT) + val deleteIntent = buildPendingIntent(service, ACTION_QUIT, null) + + val builder = NotificationCompat.Builder(service, + PlayingNotification.NOTIFICATION_CHANNEL_ID) + .setSmallIcon(R.drawable.ic_notification) + .setContentIntent(clickIntent) + .setDeleteIntent(deleteIntent) + .setCategory(NotificationCompat.CATEGORY_SERVICE) + .setPriority(NotificationCompat.PRIORITY_MAX) + .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + .setCustomContentView(notificationLayout) + .setCustomBigContentView(notificationLayoutBig) + .setOngoing(isPlaying) + + val bigNotificationImageSize = service.resources + .getDimensionPixelSize(R.dimen.notification_big_image_size) + service.runOnUiThread(Runnable { + if (target != null) { + Glide.clear(target!!) + } + target = SongGlideRequest.Builder.from(Glide.with(service), song) + .checkIgnoreMediaStore(service) + .generatePalette(service).build() + .into(object : SimpleTarget(bigNotificationImageSize, + bigNotificationImageSize) { + override fun onResourceReady(resource: BitmapPaletteWrapper, + glideAnimation: GlideAnimation) { + + val mediaNotificationProcessor = MediaNotificationProcessor( + service, service) { i, _ -> update(resource.bitmap, i) } + mediaNotificationProcessor.processNotification(resource.bitmap) + + } + + override fun onLoadFailed(e: Exception?, errorDrawable: Drawable?) { + super.onLoadFailed(e, errorDrawable) + update(null, Color.WHITE) + } + + private fun update(bitmap: Bitmap?, bgColor: Int) { + var bgColorFinal = bgColor + if (bitmap != null) { + notificationLayout.setImageViewBitmap(R.id.largeIcon, bitmap) + notificationLayoutBig.setImageViewBitmap(R.id.largeIcon, bitmap) + } else { + notificationLayout.setImageViewResource(R.id.largeIcon, R.drawable.default_album_art) + notificationLayoutBig.setImageViewResource(R.id.largeIcon, R.drawable.default_album_art) + } + + if (!PreferenceUtil.getInstance().coloredNotification()) { + bgColorFinal = Color.WHITE + } + setBackgroundColor(bgColorFinal) + setNotificationContent(ColorUtil.isColorLight(bgColorFinal)) + + if (stopped) { + return // notification has been stopped before loading was finished + } + updateNotifyModeAndPostNotification(builder.build()) + } + + private fun setBackgroundColor(color: Int) { + + notificationLayout.setInt(R.id.image, "setBackgroundColor", color) + notificationLayoutBig.setInt(R.id.image, "setBackgroundColor", color) + + notificationLayout.setInt(R.id.foregroundImage, "setColorFilter", color) + notificationLayoutBig.setInt(R.id.foregroundImage, "setColorFilter", color) + } + + private fun setNotificationContent(dark: Boolean) { + val primary = MaterialValueHelper.getPrimaryTextColor(service, dark) + val secondary = MaterialValueHelper.getSecondaryTextColor(service, dark) + + val close = createBitmap(RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_close_white_24dp, primary)!!, PlayingNotification.NOTIFICATION_CONTROLS_SIZE_MULTIPLIER) + val prev = createBitmap(RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_previous_white_24dp, primary)!!, PlayingNotification.NOTIFICATION_CONTROLS_SIZE_MULTIPLIER) + val next = createBitmap(RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_next_white_24dp, primary)!!, PlayingNotification.NOTIFICATION_CONTROLS_SIZE_MULTIPLIER) + val playPause = createBitmap(RetroUtil.getTintedVectorDrawable(service, + if (isPlaying) + R.drawable.ic_pause_white_24dp + else + R.drawable.ic_play_arrow_white_24dp, primary)!!, PlayingNotification.NOTIFICATION_CONTROLS_SIZE_MULTIPLIER) + + notificationLayout.setTextColor(R.id.title, primary) + notificationLayout.setTextColor(R.id.subtitle, secondary) + notificationLayout.setTextColor(R.id.appName, secondary) + + notificationLayout.setImageViewBitmap(R.id.action_prev, prev) + notificationLayout.setImageViewBitmap(R.id.action_next, next) + notificationLayout.setImageViewBitmap(R.id.action_play_pause, playPause) + + notificationLayoutBig.setTextColor(R.id.title, primary) + notificationLayoutBig.setTextColor(R.id.subtitle, secondary) + notificationLayoutBig.setTextColor(R.id.appName, secondary) + + notificationLayoutBig.setImageViewBitmap(R.id.action_quit, close) + notificationLayoutBig.setImageViewBitmap(R.id.action_prev, prev) + notificationLayoutBig.setImageViewBitmap(R.id.action_next, next) + notificationLayoutBig.setImageViewBitmap(R.id.action_play_pause, playPause) + + notificationLayout.setImageViewBitmap(R.id.smallIcon, createBitmap(RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_notification, secondary)!!, 0.6f)) + notificationLayoutBig.setImageViewBitmap(R.id.smallIcon, createBitmap(RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_notification, secondary)!!, 0.6f)) + + notificationLayout.setInt(R.id.arrow, "setColorFilter", secondary) + notificationLayoutBig.setInt(R.id.arrow, "setColorFilter", secondary) + + } + }) + }) + + if (stopped) { + return // notification has been stopped before loading was finished + } + updateNotifyModeAndPostNotification(builder.build()) + } + + + private fun buildPendingIntent(context: Context, action: String, + serviceName: ComponentName?): PendingIntent { + val intent = Intent(action) + intent.component = serviceName + return PendingIntent.getService(context, 0, intent, 0) + } + + + private fun linkButtons(notificationLayout: RemoteViews) { + var pendingIntent: PendingIntent + + val serviceName = ComponentName(service, MusicService::class.java) + + // Previous track + pendingIntent = buildPendingIntent(service, ACTION_REWIND, serviceName) + notificationLayout.setOnClickPendingIntent(R.id.action_prev, pendingIntent) + + // Play and pause + pendingIntent = buildPendingIntent(service, ACTION_TOGGLE_PAUSE, serviceName) + notificationLayout.setOnClickPendingIntent(R.id.action_play_pause, pendingIntent) + + // Next track + pendingIntent = buildPendingIntent(service, ACTION_SKIP, serviceName) + notificationLayout.setOnClickPendingIntent(R.id.action_next, pendingIntent) + + // Close + pendingIntent = buildPendingIntent(service, ACTION_QUIT, serviceName) + notificationLayout.setOnClickPendingIntent(R.id.action_quit, pendingIntent) + } + +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/AboutActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/AboutActivity.java index fab16387..4d94c2d3 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/AboutActivity.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/AboutActivity.java @@ -18,6 +18,7 @@ import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Type; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.List; import androidx.appcompat.widget.Toolbar; @@ -30,7 +31,6 @@ import butterknife.ButterKnife; import butterknife.OnClick; import code.name.monkey.appthemehelper.ThemeStore; import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper; -import code.name.monkey.retromusic.Constants; import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.model.Contributor; import code.name.monkey.retromusic.ui.activities.base.AbsBaseActivity; @@ -38,7 +38,10 @@ import code.name.monkey.retromusic.ui.adapter.ContributorAdapter; import code.name.monkey.retromusic.util.NavigationUtil; import static code.name.monkey.retromusic.Constants.APP_INSTAGRAM_LINK; +import static code.name.monkey.retromusic.Constants.APP_TELEGRAM_LINK; import static code.name.monkey.retromusic.Constants.APP_TWITTER_LINK; +import static code.name.monkey.retromusic.Constants.DISCORD_LINK; +import static code.name.monkey.retromusic.Constants.FAQ_LINK; import static code.name.monkey.retromusic.Constants.GITHUB_PROJECT; import static code.name.monkey.retromusic.Constants.GOOGLE_PLUS_COMMUNITY; import static code.name.monkey.retromusic.Constants.RATE_ON_GOOGLE_PLAY; @@ -115,13 +118,13 @@ public class AboutActivity extends AbsBaseActivity { public void onViewClicked(View view) { switch (view.getId()) { case R.id.faq_link: - openUrl(Constants.FAQ_LINK); + openUrl(FAQ_LINK); break; case R.id.telegram_link: - openUrl(Constants.APP_TELEGRAM_LINK); + openUrl(APP_TELEGRAM_LINK); break; case R.id.discord_link: - openUrl(Constants.DISCORD_LINK); + openUrl(DISCORD_LINK); break; case R.id.app_github: openUrl(GITHUB_PROJECT); @@ -200,7 +203,7 @@ public class AboutActivity extends AbsBaseActivity { }.getType(); List contributors = new Gson().fromJson(data, type); - ContributorAdapter contributorAdapter = new ContributorAdapter(contributors); + ContributorAdapter contributorAdapter = new ContributorAdapter((ArrayList) contributors); recyclerView.setLayoutManager(new LinearLayoutManager(this)); recyclerView.setItemAnimator(new DefaultItemAnimator()); recyclerView.setAdapter(contributorAdapter); diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/AlbumDetailsActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/AlbumDetailsActivity.java deleted file mode 100644 index 2c7c868e..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/AlbumDetailsActivity.java +++ /dev/null @@ -1,426 +0,0 @@ -package code.name.monkey.retromusic.ui.activities; - -import android.content.Intent; -import android.graphics.Color; -import android.os.Bundle; -import android.transition.Slide; -import android.view.Gravity; -import android.view.Menu; -import android.view.MenuItem; -import android.view.SubMenu; -import android.view.View; -import android.view.ViewGroup; -import android.view.animation.AnimationUtils; -import android.widget.ImageView; -import android.widget.TextView; - -import com.bumptech.glide.Glide; -import com.google.android.material.appbar.AppBarLayout; -import com.google.android.material.appbar.CollapsingToolbarLayout; - -import java.util.ArrayList; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.widget.AppCompatTextView; -import androidx.appcompat.widget.Toolbar; -import androidx.core.app.ActivityCompat; -import androidx.core.util.Pair; -import androidx.core.widget.NestedScrollView; -import androidx.recyclerview.widget.DefaultItemAnimator; -import androidx.recyclerview.widget.GridLayoutManager; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.appthemehelper.util.ColorUtil; -import code.name.monkey.appthemehelper.util.TintHelper; -import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog; -import code.name.monkey.retromusic.dialogs.DeleteSongsDialog; -import code.name.monkey.retromusic.glide.ArtistGlideRequest; -import code.name.monkey.retromusic.glide.RetroMusicColoredTarget; -import code.name.monkey.retromusic.glide.SongGlideRequest; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.helper.SortOrder.AlbumSongSortOrder; -import code.name.monkey.retromusic.loaders.ArtistLoader; -import code.name.monkey.retromusic.misc.AppBarStateChangeListener; -import code.name.monkey.retromusic.model.Album; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.mvp.contract.AlbumDetailsContract; -import code.name.monkey.retromusic.mvp.presenter.AlbumDetailsPresenter; -import code.name.monkey.retromusic.ui.activities.base.AbsSlidingMusicPanelActivity; -import code.name.monkey.retromusic.ui.activities.tageditor.AbsTagEditorActivity; -import code.name.monkey.retromusic.ui.activities.tageditor.AlbumTagEditorActivity; -import code.name.monkey.retromusic.ui.adapter.album.AlbumAdapter; -import code.name.monkey.retromusic.ui.adapter.album.HorizontalAlbumAdapter; -import code.name.monkey.retromusic.ui.adapter.song.SimpleSongAdapter; -import code.name.monkey.retromusic.util.MusicUtil; -import code.name.monkey.retromusic.util.NavigationUtil; -import code.name.monkey.retromusic.util.PreferenceUtil; -import code.name.monkey.retromusic.util.RetroUtil; -import code.name.monkey.retromusic.views.CollapsingFAB; - -public class AlbumDetailsActivity extends AbsSlidingMusicPanelActivity implements - AlbumDetailsContract.AlbumDetailsView { - - public static final String EXTRA_ALBUM_ID = "extra_album_id"; - private static final int TAG_EDITOR_REQUEST = 2001; - - @BindView(R.id.image) - ImageView image; - - @BindView(R.id.recycler_view) - RecyclerView recyclerView; - - @BindView(R.id.title) - TextView title; - - @BindView(R.id.text) - TextView text; - - @BindView(R.id.song_title) - AppCompatTextView songTitle; - - @BindView(R.id.action_shuffle_all) - CollapsingFAB shuffleButton; - - @BindView(R.id.collapsing_toolbar) - @Nullable - CollapsingToolbarLayout collapsingToolbarLayout; - - @BindView(R.id.app_bar) - @Nullable - AppBarLayout appBarLayout; - - @BindView(R.id.content) - NestedScrollView contentContainer; - - @BindView(R.id.toolbar) - Toolbar toolbar; - - @BindView(R.id.more_recycler_view) - RecyclerView moreRecyclerView; - - @BindView(R.id.more_title) - TextView moreTitle; - - @BindView(R.id.artist_image) - @Nullable - ImageView artistImage; - - private AlbumDetailsPresenter albumDetailsPresenter; - - private SimpleSongAdapter adapter; - private Album album; - - @Override - protected View createContentView() { - return wrapSlidingMusicPanel(R.layout.activity_album); - } - - void setupWindowTransition() { - Slide slide = new Slide(Gravity.BOTTOM); - slide.setInterpolator( - AnimationUtils.loadInterpolator(this, android.R.interpolator.linear_out_slow_in)); - getWindow().setEnterTransition(slide); - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - setDrawUnderStatusBar(); - setupWindowTransition(); - super.onCreate(savedInstanceState); - ButterKnife.bind(this); - - toggleBottomNavigationView(true); - setLightNavigationBar(true); - setNavigationbarColorAuto(); - - ActivityCompat.postponeEnterTransition(this); - - int albumId = getIntent().getIntExtra(EXTRA_ALBUM_ID, -1); - albumDetailsPresenter = new AlbumDetailsPresenter(this, albumId); - albumDetailsPresenter.subscribe(); - - setupRecyclerView(); - setupToolbarMarginHeight(); - - contentContainer.setOnScrollChangeListener((NestedScrollView.OnScrollChangeListener) (v, scrollX, scrollY, oldScrollX, oldScrollY) -> { - if (scrollY > oldScrollY) { - shuffleButton.setShowTitle(false); - } - if (scrollY < oldScrollY) { - shuffleButton.setShowTitle(true); - } - }); - - - } - - private void setupRecyclerView() { - adapter = new SimpleSongAdapter(this, new ArrayList<>(), R.layout.item_song); - recyclerView.setLayoutManager(new LinearLayoutManager(this)); - recyclerView.setItemAnimator(new DefaultItemAnimator()); - recyclerView.setNestedScrollingEnabled(false); - recyclerView.setAdapter(adapter); - - } - - private void setupToolbarMarginHeight() { - int primaryColor = ThemeStore.primaryColor(this); - TintHelper.setTintAuto(contentContainer, primaryColor, true); - if (collapsingToolbarLayout != null) { - collapsingToolbarLayout.setContentScrimColor(primaryColor); - collapsingToolbarLayout.setStatusBarScrimColor(ColorUtil.darkenColor(primaryColor)); - } - - toolbar.setNavigationIcon(R.drawable.ic_keyboard_backspace_black_24dp); - setSupportActionBar(toolbar); - //noinspection ConstantConditions - getSupportActionBar().setTitle(null); - - - if (toolbar != null && !PreferenceUtil.getInstance().getFullScreenMode()) { - ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) toolbar.getLayoutParams(); - params.topMargin = RetroUtil.getStatusBarHeight( ); - toolbar.setLayoutParams(params); - } - - if (appBarLayout != null) { - appBarLayout.addOnOffsetChangedListener(new AppBarStateChangeListener() { - @Override - public void onStateChanged(AppBarLayout appBarLayout, State state) { - int color; - switch (state) { - case COLLAPSED: - setLightStatusbar(ColorUtil.isColorLight(ThemeStore.primaryColor(AlbumDetailsActivity.this))); - color = ThemeStore.primaryColor(AlbumDetailsActivity.this); - break; - default: - case EXPANDED: - case IDLE: - setLightStatusbar(false); - color = Color.TRANSPARENT; - break; - } - ToolbarContentTintHelper.setToolbarContentColorBasedOnToolbarColor(AlbumDetailsActivity.this, toolbar, color); - } - }); - } - } - - @OnClick({R.id.action_shuffle_all, R.id.artist_image}) - public void onViewClicked(View view) { - switch (view.getId()) { - case R.id.artist_image: - Pair[] artistPairs = new Pair[]{Pair.create(image, - getResources().getString(R.string.transition_artist_image))}; - NavigationUtil.goToArtist(this, getAlbum().getArtistId(), - artistPairs); - break; - case R.id.action_shuffle_all: - if (getAlbum().songs != null) { - MusicPlayerRemote.openAndShuffleQueue(getAlbum().songs, true); - } - break; - } - } - - @Override - protected void onPause() { - super.onPause(); - albumDetailsPresenter.unsubscribe(); - } - - @Override - public void loading() { - - } - - @Override - public void showEmptyView() { - - } - - @Override - public void completed() { - ActivityCompat.startPostponedEnterTransition(this); - } - - @Override - public void showData(Album album) { - if (album.songs.isEmpty()) { - finish(); - return; - } - this.album = album; - - title.setText(album.getTitle()); - text.setText(String.format("%s%s • %s", album.getArtistName(), - " • " + MusicUtil.getYearString(album.getYear()), - MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(this, album.songs)))); - - loadAlbumCover(); - loadMoreFrom(album); - adapter.swapDataSet(album.songs); - } - - private void loadMoreFrom(Album album) { - if (artistImage != null) { - ArtistGlideRequest.Builder.from(Glide.with(this), - ArtistLoader.getArtist(this, album.getArtistId()).blockingFirst()) - .forceDownload(false) - .generatePalette(this).build() - .dontAnimate() - .into(new RetroMusicColoredTarget(artistImage) { - @Override - public void onColorReady(int color) { - //setColors(color); - } - }); - } - - ArrayList albums = ArtistLoader.getArtist(this, album.getArtistId()) - .blockingFirst().albums; - albums.remove(album); - if (!albums.isEmpty()) { - moreTitle.setVisibility(View.VISIBLE); - moreRecyclerView.setVisibility(View.VISIBLE); - } else { - return; - } - moreTitle.setText(String.format("More from %s", album.getArtistName())); - - AlbumAdapter albumAdapter = new HorizontalAlbumAdapter(this, albums, false, null); - moreRecyclerView.setLayoutManager(new GridLayoutManager(this, 1, GridLayoutManager.HORIZONTAL, false)); - moreRecyclerView.setAdapter(albumAdapter); - } - - public Album getAlbum() { - return album; - } - - private void loadAlbumCover() { - SongGlideRequest.Builder.from(Glide.with(this), getAlbum().safeGetFirstSong()) - .checkIgnoreMediaStore(this) - .generatePalette(this).build() - .dontAnimate() - .into(new RetroMusicColoredTarget(image) { - @Override - public void onColorReady(int color) { - setColors(color); - } - }); - } - - private void setColors(int color) { - int themeColor = - PreferenceUtil.getInstance().getAdaptiveColor() ? color : ThemeStore.accentColor(this); - songTitle.setTextColor(themeColor); - moreTitle.setTextColor(themeColor); - - shuffleButton.setColor(themeColor); - } - - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.menu_album_detail, menu); - MenuItem sortOrder = menu.findItem(R.id.action_sort_order); - setUpSortOrderMenu(sortOrder.getSubMenu()); - return super.onCreateOptionsMenu(menu); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - return handleSortOrderMenuItem(item); - } - - private boolean handleSortOrderMenuItem(@NonNull MenuItem item) { - String sortOrder = null; - final ArrayList songs = adapter.getDataSet(); - switch (item.getItemId()) { - case R.id.action_play_next: - MusicPlayerRemote.playNext(songs); - return true; - case R.id.action_add_to_current_playing: - MusicPlayerRemote.enqueue(songs); - return true; - case R.id.action_add_to_playlist: - AddToPlaylistDialog.create(songs).show(getSupportFragmentManager(), "ADD_PLAYLIST"); - return true; - case R.id.action_delete_from_device: - DeleteSongsDialog.create(songs).show(getSupportFragmentManager(), "DELETE_SONGS"); - return true; - case android.R.id.home: - super.onBackPressed(); - return true; - case R.id.action_tag_editor: - Intent intent = new Intent(this, AlbumTagEditorActivity.class); - intent.putExtra(AbsTagEditorActivity.EXTRA_ID, getAlbum().getId()); - startActivityForResult(intent, TAG_EDITOR_REQUEST); - return true; - case R.id.action_go_to_artist: - NavigationUtil.goToArtist(this, getAlbum().getArtistId()); - return true; - /*Sort*/ - case R.id.action_sort_order_title: - sortOrder = AlbumSongSortOrder.SONG_A_Z; - break; - case R.id.action_sort_order_title_desc: - sortOrder = AlbumSongSortOrder.SONG_Z_A; - break; - case R.id.action_sort_order_track_list: - sortOrder = AlbumSongSortOrder.SONG_TRACK_LIST; - break; - case R.id.action_sort_order_artist_song_duration: - sortOrder = AlbumSongSortOrder.SONG_DURATION; - break; - } - if (sortOrder != null) { - item.setChecked(true); - setSaveSortOrder(sortOrder); - } - return true; - } - - private String getSavedSortOrder() { - return PreferenceUtil.getInstance().getAlbumDetailSongSortOrder(); - } - - private void setUpSortOrderMenu(@NonNull SubMenu sortOrder) { - switch (getSavedSortOrder()) { - case AlbumSongSortOrder.SONG_A_Z: - sortOrder.findItem(R.id.action_sort_order_title).setChecked(true); - break; - case AlbumSongSortOrder.SONG_Z_A: - sortOrder.findItem(R.id.action_sort_order_title_desc).setChecked(true); - break; - case AlbumSongSortOrder.SONG_TRACK_LIST: - sortOrder.findItem(R.id.action_sort_order_track_list).setChecked(true); - break; - case AlbumSongSortOrder.SONG_DURATION: - sortOrder.findItem(R.id.action_sort_order_artist_song_duration).setChecked(true); - break; - } - } - - private void setSaveSortOrder(String sortOrder) { - PreferenceUtil.getInstance().setAlbumDetailSongSortOrder(sortOrder); - reload(); - } - - @Override - public void onMediaStoreChanged() { - super.onMediaStoreChanged(); - reload(); - } - - private void reload() { - albumDetailsPresenter.subscribe(); - } -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/AlbumDetailsActivity.kt b/app/src/main/java/code/name/monkey/retromusic/ui/activities/AlbumDetailsActivity.kt new file mode 100644 index 00000000..af628a2c --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/AlbumDetailsActivity.kt @@ -0,0 +1,338 @@ +package code.name.monkey.retromusic.ui.activities + +import android.content.Intent +import android.graphics.Color +import android.os.Bundle +import android.transition.Slide +import android.view.* +import android.view.animation.AnimationUtils +import androidx.core.app.ActivityCompat +import androidx.core.util.Pair +import androidx.core.widget.NestedScrollView +import androidx.recyclerview.widget.DefaultItemAnimator +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.LinearLayoutManager +import butterknife.ButterKnife +import butterknife.OnClick +import code.name.monkey.appthemehelper.ThemeStore +import code.name.monkey.appthemehelper.util.ColorUtil +import code.name.monkey.appthemehelper.util.TintHelper +import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog +import code.name.monkey.retromusic.dialogs.DeleteSongsDialog +import code.name.monkey.retromusic.glide.ArtistGlideRequest +import code.name.monkey.retromusic.glide.RetroMusicColoredTarget +import code.name.monkey.retromusic.glide.SongGlideRequest +import code.name.monkey.retromusic.helper.MusicPlayerRemote +import code.name.monkey.retromusic.helper.SortOrder.AlbumSongSortOrder +import code.name.monkey.retromusic.loaders.ArtistLoader +import code.name.monkey.retromusic.misc.AppBarStateChangeListener +import code.name.monkey.retromusic.model.Album +import code.name.monkey.retromusic.mvp.contract.AlbumDetailsContract +import code.name.monkey.retromusic.mvp.presenter.AlbumDetailsPresenter +import code.name.monkey.retromusic.ui.activities.base.AbsSlidingMusicPanelActivity +import code.name.monkey.retromusic.ui.activities.tageditor.AbsTagEditorActivity +import code.name.monkey.retromusic.ui.activities.tageditor.AlbumTagEditorActivity +import code.name.monkey.retromusic.ui.adapter.album.HorizontalAlbumAdapter +import code.name.monkey.retromusic.ui.adapter.song.SimpleSongAdapter +import code.name.monkey.retromusic.util.MusicUtil +import code.name.monkey.retromusic.util.NavigationUtil +import code.name.monkey.retromusic.util.PreferenceUtil +import code.name.monkey.retromusic.util.RetroUtil +import code.name.monkey.retromusic.views.CircularImageView +import com.bumptech.glide.Glide +import com.google.android.material.appbar.AppBarLayout +import kotlinx.android.synthetic.main.activity_album.* +import kotlinx.android.synthetic.main.activity_album_content.* +import java.util.* + +class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), AlbumDetailsContract.AlbumDetailsView { + + private var albumDetailsPresenter: AlbumDetailsPresenter? = null + + private var adapter: SimpleSongAdapter? = null + var album: Album? = null + private set + + private val savedSortOrder: String + get() = PreferenceUtil.getInstance().albumDetailSongSortOrder + + override fun createContentView(): View { + return wrapSlidingMusicPanel(R.layout.activity_album) + } + + private fun setupWindowTransition() { + val slide = Slide(Gravity.BOTTOM) + slide.interpolator = AnimationUtils.loadInterpolator(this, android.R.interpolator.linear_out_slow_in) + window.enterTransition = slide + } + + override fun onCreate(savedInstanceState: Bundle?) { + setDrawUnderStatusBar() + setupWindowTransition() + super.onCreate(savedInstanceState) + ButterKnife.bind(this) + + toggleBottomNavigationView(true) + setLightNavigationBar(true) + setNavigationbarColorAuto() + + ActivityCompat.postponeEnterTransition(this) + + val albumId = intent.getIntExtra(EXTRA_ALBUM_ID, -1) + albumDetailsPresenter = AlbumDetailsPresenter(this, albumId) + albumDetailsPresenter!!.subscribe() + + setupRecyclerView() + setupToolbarMarginHeight() + + + contentContainer.setOnScrollChangeListener { v: NestedScrollView?, scrollX: Int, scrollY: Int, oldScrollX: Int, oldScrollY: Int -> + run { + if (scrollY > oldScrollY) { + actionShuffleAll!!.setShowTitle(false) + } + if (scrollY < oldScrollY) { + actionShuffleAll!!.setShowTitle(true) + } + } + } + } + + private fun setupRecyclerView() { + adapter = SimpleSongAdapter(this, ArrayList(), R.layout.item_song) + recyclerView.apply { + layoutManager = LinearLayoutManager(this@AlbumDetailsActivity) + itemAnimator = DefaultItemAnimator() + isNestedScrollingEnabled = false + adapter = adapter + } + } + + private fun setupToolbarMarginHeight() { + val primaryColor = ThemeStore.primaryColor(this) + TintHelper.setTintAuto(contentContainer!!, primaryColor, true) + if (collapsingToolbarLayout != null) { + collapsingToolbarLayout!!.setContentScrimColor(primaryColor) + collapsingToolbarLayout!!.setStatusBarScrimColor(ColorUtil.darkenColor(primaryColor)) + } + + toolbar.setNavigationIcon(R.drawable.ic_keyboard_backspace_black_24dp) + setSupportActionBar(toolbar) + + supportActionBar!!.title = null + + + if (toolbar != null && !PreferenceUtil.getInstance().fullScreenMode) { + val params = toolbar!!.layoutParams as ViewGroup.MarginLayoutParams + params.topMargin = RetroUtil.getStatusBarHeight() + toolbar!!.layoutParams = params + } + + if (appBarLayout != null) { + appBarLayout!!.addOnOffsetChangedListener(object : AppBarStateChangeListener() { + override fun onStateChanged(appBarLayout: AppBarLayout, state: AppBarStateChangeListener.State) { + val color: Int + when (state) { + AppBarStateChangeListener.State.COLLAPSED -> { + setLightStatusbar(ColorUtil.isColorLight(ThemeStore.primaryColor(this@AlbumDetailsActivity))) + color = ThemeStore.primaryColor(this@AlbumDetailsActivity) + } + AppBarStateChangeListener.State.EXPANDED, AppBarStateChangeListener.State.IDLE -> { + setLightStatusbar(false) + color = Color.TRANSPARENT + } + else -> { + setLightStatusbar(false) + color = Color.TRANSPARENT + } + } + ToolbarContentTintHelper.setToolbarContentColorBasedOnToolbarColor(this@AlbumDetailsActivity, toolbar, color) + } + }) + } + } + + @OnClick(R.id.action_shuffle_all, R.id.artist_image) + fun onViewClicked(view: View) { + when (view.id) { + R.id.artist_image -> { + val artistPairs = arrayOf>(Pair.create(image, resources.getString(R.string.transition_artist_image))) + NavigationUtil.goToArtist(this, album!!.artistId, *artistPairs) + } + R.id.action_shuffle_all -> if (album!!.songs != null) { + MusicPlayerRemote.openAndShuffleQueue(album!!.songs!!, true) + } + } + } + + override fun onPause() { + super.onPause() + albumDetailsPresenter!!.unsubscribe() + } + + override fun loading() { + + } + + override fun showEmptyView() { + + } + + override fun completed() { + ActivityCompat.startPostponedEnterTransition(this) + } + + override fun showData(album: Album) { + if (album.songs!!.isEmpty()) { + finish() + return + } + this.album = album + + albumTitle.text = album.title + albumText.text = String.format("%s • %s • %s", album.artistName, MusicUtil.getYearString(album.year), MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(this, album.songs))) + + loadAlbumCover() + loadMoreFrom(album) + adapter!!.swapDataSet(album.songs) + } + + private fun loadMoreFrom(album: Album) { + if (artistImage != null) { + ArtistGlideRequest.Builder.from(Glide.with(this), + ArtistLoader.getArtist(this, album.artistId).blockingFirst()) + .forceDownload(false) + .generatePalette(this).build() + .dontAnimate() + .into(object : RetroMusicColoredTarget(artistImage as CircularImageView) { + override fun onColorReady(color: Int) { + //setColors(color); + } + }) + } + + val albums = ArtistLoader.getArtist(this, album.artistId) + .blockingFirst().albums + albums!!.remove(album) + if (!albums.isEmpty()) { + moreTitle.visibility = View.VISIBLE + moreRecyclerView!!.visibility = View.VISIBLE + } else { + return + } + moreTitle.text = String.format("More from %s", album.artistName) + + val albumAdapter = HorizontalAlbumAdapter(this, albums, false, null) + moreRecyclerView!!.layoutManager = GridLayoutManager(this, 1, GridLayoutManager.HORIZONTAL, false) + moreRecyclerView!!.adapter = albumAdapter + } + + private fun loadAlbumCover() { + SongGlideRequest.Builder.from(Glide.with(this), album!!.safeGetFirstSong()) + .checkIgnoreMediaStore(this) + .generatePalette(this).build() + .dontAnimate() + .into(object : RetroMusicColoredTarget(image) { + override fun onColorReady(color: Int) { + setColors(color) + } + }) + } + + private fun setColors(color: Int) { + val themeColor = if (PreferenceUtil.getInstance().adaptiveColor) color else ThemeStore.accentColor(this) + songTitle.setTextColor(themeColor) + moreTitle.setTextColor(themeColor) + actionShuffleAll.setColor(themeColor) + } + + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + menuInflater.inflate(R.menu.menu_album_detail, menu) + val sortOrder = menu.findItem(R.id.action_sort_order) + setUpSortOrderMenu(sortOrder.subMenu) + return super.onCreateOptionsMenu(menu) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return handleSortOrderMenuItem(item) + } + + private fun handleSortOrderMenuItem(item: MenuItem): Boolean { + var sortOrder: String? = null + val songs = adapter!!.dataSet + when (item.itemId) { + R.id.action_play_next -> { + MusicPlayerRemote.playNext(songs) + return true + } + R.id.action_add_to_current_playing -> { + MusicPlayerRemote.enqueue(songs) + return true + } + R.id.action_add_to_playlist -> { + AddToPlaylistDialog.create(songs).show(supportFragmentManager, "ADD_PLAYLIST") + return true + } + R.id.action_delete_from_device -> { + DeleteSongsDialog.create(songs).show(supportFragmentManager, "DELETE_SONGS") + return true + } + android.R.id.home -> { + super.onBackPressed() + return true + } + R.id.action_tag_editor -> { + val intent = Intent(this, AlbumTagEditorActivity::class.java) + intent.putExtra(AbsTagEditorActivity.EXTRA_ID, album!!.id) + startActivityForResult(intent, TAG_EDITOR_REQUEST) + return true + } + R.id.action_go_to_artist -> { + NavigationUtil.goToArtist(this, album!!.artistId) + return true + } + /*Sort*/ + R.id.action_sort_order_title -> sortOrder = AlbumSongSortOrder.SONG_A_Z + R.id.action_sort_order_title_desc -> sortOrder = AlbumSongSortOrder.SONG_Z_A + R.id.action_sort_order_track_list -> sortOrder = AlbumSongSortOrder.SONG_TRACK_LIST + R.id.action_sort_order_artist_song_duration -> sortOrder = AlbumSongSortOrder.SONG_DURATION + } + if (sortOrder != null) { + item.isChecked = true + setSaveSortOrder(sortOrder) + } + return true + } + + private fun setUpSortOrderMenu(sortOrder: SubMenu) { + when (savedSortOrder) { + AlbumSongSortOrder.SONG_A_Z -> sortOrder.findItem(R.id.action_sort_order_title).isChecked = true + AlbumSongSortOrder.SONG_Z_A -> sortOrder.findItem(R.id.action_sort_order_title_desc).isChecked = true + AlbumSongSortOrder.SONG_TRACK_LIST -> sortOrder.findItem(R.id.action_sort_order_track_list).isChecked = true + AlbumSongSortOrder.SONG_DURATION -> sortOrder.findItem(R.id.action_sort_order_artist_song_duration).isChecked = true + } + } + + private fun setSaveSortOrder(sortOrder: String?) { + PreferenceUtil.getInstance().albumDetailSongSortOrder = sortOrder + reload() + } + + override fun onMediaStoreChanged() { + super.onMediaStoreChanged() + reload() + } + + private fun reload() { + albumDetailsPresenter!!.subscribe() + } + + companion object { + + const val EXTRA_ALBUM_ID = "extra_album_id" + private const val TAG_EDITOR_REQUEST = 2001 + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/ArtistDetailActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/ArtistDetailActivity.java index 62694222..6bf6346e 100755 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/ArtistDetailActivity.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/ArtistDetailActivity.java @@ -238,8 +238,7 @@ public class ArtistDetailActivity extends AbsSlidingMusicPanelActivity implement private void setupRecyclerView() { albumAdapter = new HorizontalAlbumAdapter(this, new ArrayList<>(), false, null); albumRecyclerView.setItemAnimator(new DefaultItemAnimator()); - albumRecyclerView - .setLayoutManager(new GridLayoutManager(this, 1, GridLayoutManager.HORIZONTAL, false)); + albumRecyclerView.setLayoutManager(new GridLayoutManager(this, 1, GridLayoutManager.HORIZONTAL, false)); albumRecyclerView.setAdapter(albumAdapter); songAdapter = new SimpleSongAdapter(this, new ArrayList<>(), R.layout.item_song); @@ -313,7 +312,7 @@ public class ArtistDetailActivity extends AbsSlidingMusicPanelActivity implement .getReadableDurationString(MusicUtil.getTotalDuration(this, artist.getSongs())))); songAdapter.swapDataSet(artist.getSongs()); - albumAdapter.swapDataSet(artist.albums); + albumAdapter.swapDataSet(artist.getAlbums()); } private void loadBiography() { @@ -398,7 +397,7 @@ public class ArtistDetailActivity extends AbsSlidingMusicPanelActivity implement public void onViewClicked(View view) { switch (view.getId()) { case R.id.action_shuffle_all: - MusicPlayerRemote.openAndShuffleQueue(getArtist().getSongs(), true); + MusicPlayerRemote.INSTANCE.openAndShuffleQueue(getArtist().getSongs(), true); break; } } @@ -415,10 +414,10 @@ public class ArtistDetailActivity extends AbsSlidingMusicPanelActivity implement super.onBackPressed(); return true; case R.id.action_play_next: - MusicPlayerRemote.playNext(songs); + MusicPlayerRemote.INSTANCE.playNext(songs); return true; case R.id.action_add_to_current_playing: - MusicPlayerRemote.enqueue(songs); + MusicPlayerRemote.INSTANCE.enqueue(songs); return true; case R.id.action_add_to_playlist: AddToPlaylistDialog.create(songs).show(getSupportFragmentManager(), "ADD_PLAYLIST"); diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/EqualizerActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/EqualizerActivity.java index 9040d2f1..65095ed2 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/EqualizerActivity.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/EqualizerActivity.java @@ -66,7 +66,7 @@ public class EqualizerActivity extends AbsMusicServiceActivity implements private CompoundButton.OnCheckedChangeListener mListener = (buttonView, isChecked) -> { switch (buttonView.getId()) { case R.id.equalizer: - EqualizerHelper.getInstance().getEqualizer().setEnabled(isChecked); + EqualizerHelper.Companion.getInstance().getEqualizer().setEnabled(isChecked); TransitionManager.beginDelayedTransition(mContent); mContent.setVisibility(isChecked ? View.VISIBLE : View.GONE); break; @@ -78,12 +78,12 @@ public class EqualizerActivity extends AbsMusicServiceActivity implements if (fromUser) { if (seekBar == mBassBoostStrength) { mBassBoost.setEnabled(progress > 0); - EqualizerHelper.getInstance().setBassBoostStrength(progress); - EqualizerHelper.getInstance().setBassBoostEnabled(progress > 0); + EqualizerHelper.Companion.getInstance().setBassBoostStrength(progress); + EqualizerHelper.Companion.getInstance().setBassBoostEnabled(progress > 0); } else if (seekBar == mVirtualizerStrength) { mVirtualizer.setEnabled(progress > 0); - EqualizerHelper.getInstance().setVirtualizerEnabled(progress > 0); - EqualizerHelper.getInstance().setVirtualizerStrength(progress); + EqualizerHelper.Companion.getInstance().setVirtualizerEnabled(progress > 0); + EqualizerHelper.Companion.getInstance().setVirtualizerStrength(progress); } } } @@ -113,17 +113,17 @@ public class EqualizerActivity extends AbsMusicServiceActivity implements setupToolbar(); - mEnable.setChecked(EqualizerHelper.getInstance().getEqualizer().getEnabled()); + mEnable.setChecked(EqualizerHelper.Companion.getInstance().getEqualizer().getEnabled()); mEnable.setOnCheckedChangeListener(mListener); mPresetsNamesAdapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1); mPresets.setAdapter(mPresetsNamesAdapter); mPresets.setOnItemSelectedListener(this); - mBassBoostStrength.setProgress(EqualizerHelper.getInstance().getBassBoostStrength()); + mBassBoostStrength.setProgress(EqualizerHelper.Companion.getInstance().getBassBoostStrength()); mBassBoostStrength.setOnSeekBarChangeListener(mSeekBarChangeListener); - mVirtualizerStrength.setProgress(EqualizerHelper.getInstance().getVirtualizerStrength()); + mVirtualizerStrength.setProgress(EqualizerHelper.Companion.getInstance().getVirtualizerStrength()); mVirtualizerStrength.setOnSeekBarChangeListener(mSeekBarChangeListener); setupUI(); @@ -153,13 +153,13 @@ public class EqualizerActivity extends AbsMusicServiceActivity implements private void addPresets() { mPresetsNamesAdapter.clear(); mPresetsNamesAdapter.add("Custom"); - for (int j = 0; j < EqualizerHelper.getInstance().getEqualizer().getNumberOfPresets(); j++) { + for (int j = 0; j < EqualizerHelper.Companion.getInstance().getEqualizer().getNumberOfPresets(); j++) { mPresetsNamesAdapter - .add(EqualizerHelper.getInstance().getEqualizer().getPresetName((short) j)); + .add(EqualizerHelper.Companion.getInstance().getEqualizer().getPresetName((short) j)); mPresetsNamesAdapter.notifyDataSetChanged(); } mPresets - .setSelection((int) EqualizerHelper.getInstance().getEqualizer().getCurrentPreset() + 1); + .setSelection((int) EqualizerHelper.Companion.getInstance().getEqualizer().getCurrentPreset() + 1); } private void setupUI() { @@ -167,7 +167,7 @@ public class EqualizerActivity extends AbsMusicServiceActivity implements short bands; try { // get number of supported bands - bands = (short) EqualizerHelper.getInstance().getNumberOfBands(); + bands = (short) EqualizerHelper.Companion.getInstance().getNumberOfBands(); // for each of the supported bands, we will set up a slider from -10dB to 10dB boost/attenuation, // as well as text labels to assist the user @@ -177,26 +177,26 @@ public class EqualizerActivity extends AbsMusicServiceActivity implements View view = LayoutInflater.from(this).inflate(R.layout.retro_seekbar, mLinearLayout, false); TextView freqTextView = view.findViewById(R.id.hurtz); freqTextView.setText( - String.format("%d Hz", EqualizerHelper.getInstance().getCenterFreq((int) band) / 1000)); + String.format("%d Hz", EqualizerHelper.Companion.getInstance().getCenterFreq((int) band) / 1000)); TextView minDbTextView = view.findViewById(R.id.minus_db); minDbTextView - .setText(String.format("%d dB", EqualizerHelper.getInstance().getBandLevelLow() / 100)); + .setText(String.format("%d dB", EqualizerHelper.Companion.getInstance().getBandLevelLow() / 100)); TextView maxDbTextView = view.findViewById(R.id.plus_db); maxDbTextView.setText( - String.format("%d dB", EqualizerHelper.getInstance().getBandLevelHigh() / 100)); + String.format("%d dB", EqualizerHelper.Companion.getInstance().getBandLevelHigh() / 100)); SeekBar bar = view.findViewById(R.id.seekbar); - bar.setMax(EqualizerHelper.getInstance().getBandLevelHigh() - EqualizerHelper.getInstance() + bar.setMax(EqualizerHelper.Companion.getInstance().getBandLevelHigh() - EqualizerHelper.Companion.getInstance() .getBandLevelLow()); bar.setProgress( - EqualizerHelper.getInstance().getBandLevel((int) band) - EqualizerHelper.getInstance() + EqualizerHelper.Companion.getInstance().getBandLevel((int) band) - EqualizerHelper.Companion.getInstance() .getBandLevelLow()); bar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - EqualizerHelper.getInstance().setBandLevel((int) band, - (int) (progress + EqualizerHelper.getInstance().getBandLevelLow())); + EqualizerHelper.Companion.getInstance().setBandLevel((int) band, + (int) (progress + EqualizerHelper.Companion.getInstance().getBandLevelLow())); if (fromUser) { mPresets.setSelection(0); } @@ -221,7 +221,7 @@ public class EqualizerActivity extends AbsMusicServiceActivity implements if (position == 0) { return; } - EqualizerHelper.getInstance().getEqualizer().usePreset((short) (position - 1)); + EqualizerHelper.Companion.getInstance().getEqualizer().usePreset((short) (position - 1)); setupUI(); } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/ErrorHandlerActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/ErrorHandlerActivity.java index 6f0de686..5a0ecd4c 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/ErrorHandlerActivity.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/ErrorHandlerActivity.java @@ -1 +1 @@ -package code.name.monkey.retromusic.ui.activities; import android.os.Bundle; import androidx.appcompat.app.AppCompatActivity; import android.view.View; import butterknife.OnClick; import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.RetroApplication; public class ErrorHandlerActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_error_handler); } @OnClick(R.id.clear_app_data) void clearAppDate(View view) { RetroApplication.deleteAppData(); } } \ No newline at end of file +package code.name.monkey.retromusic.ui.activities; import android.os.Bundle; import androidx.appcompat.app.AppCompatActivity; import android.view.View; import butterknife.OnClick; import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.RetroApplication; public class ErrorHandlerActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_error_handler); } @OnClick(R.id.clear_app_data) void clearAppDate(View view) { RetroApplication.Companion.deleteAppData(); } } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/GenreDetailsActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/GenreDetailsActivity.java index 4e92f90f..f7130704 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/GenreDetailsActivity.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/GenreDetailsActivity.java @@ -89,7 +89,7 @@ public class GenreDetailsActivity extends AbsSlidingMusicPanelActivity implement genre = getIntent().getParcelableExtra(EXTRA_GENRE_ID); - presenter = new GenreDetailsPresenter(this, genre.id); + presenter = new GenreDetailsPresenter(this, genre.getId()); setUpToolBar(); setupRecyclerView(); @@ -99,13 +99,13 @@ public class GenreDetailsActivity extends AbsSlidingMusicPanelActivity implement public void onViewClicked(View view) { switch (view.getId()) { case R.id.action_shuffle: - MusicPlayerRemote.openAndShuffleQueue(songAdapter.getDataSet(), true); + MusicPlayerRemote.INSTANCE.openAndShuffleQueue(songAdapter.getDataSet(), true); break; } } private void setUpToolBar() { - title.setText(genre.name); + title.setText(genre.getName()); title.setTextColor(ThemeStore.textColorPrimary(this)); int primaryColor = ThemeStore.primaryColor(this); @@ -162,7 +162,7 @@ public class GenreDetailsActivity extends AbsSlidingMusicPanelActivity implement if (item.getItemId() == android.R.id.home) { onBackPressed(); } - return GenreMenuHelper.handleMenuClick(this, genre, item); + return GenreMenuHelper.INSTANCE.handleMenuClick(this, genre, item); } private void setupRecyclerView() { diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/LockScreenActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/LockScreenActivity.java deleted file mode 100644 index 5aa1a30c..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/LockScreenActivity.java +++ /dev/null @@ -1,86 +0,0 @@ -package code.name.monkey.retromusic.ui.activities; - -import android.os.Bundle; -import androidx.core.view.ViewCompat; -import android.view.WindowManager; - -import com.bumptech.glide.Glide; -import com.r0adkll.slidr.Slidr; -import com.r0adkll.slidr.model.SlidrConfig; -import com.r0adkll.slidr.model.SlidrPosition; - -import butterknife.ButterKnife; -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.glide.RetroMusicColoredTarget; -import code.name.monkey.retromusic.glide.SongGlideRequest; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.ui.activities.base.AbsMusicServiceActivity; -import code.name.monkey.retromusic.ui.fragments.player.lockscreen.LockScreenPlayerControlsFragment; - -public class LockScreenActivity extends AbsMusicServiceActivity { - private LockScreenPlayerControlsFragment mFragment; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD - | WindowManager.LayoutParams.FLAG_FULLSCREEN - | WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS - | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); - - setDrawUnderStatusBar(); - setContentView(R.layout.activity_lock_screen_old_style); - - hideStatusBar(); - setStatusbarColorAuto(); - setNavigationbarColorAuto(); - setTaskDescriptionColorAuto(); - setLightNavigationBar(true); - - SlidrConfig config = new SlidrConfig.Builder() - .position(SlidrPosition.BOTTOM) - .build(); - - Slidr.attach(this, config); - - ButterKnife.bind(this); - mFragment = (LockScreenPlayerControlsFragment) getSupportFragmentManager().findFragmentById(R.id.playback_controls_fragment); - - findViewById(R.id.slide).setTranslationY(100f); - findViewById(R.id.slide).setAlpha(0f); - ViewCompat.animate(findViewById(R.id.slide)) - .translationY(0f) - .alpha(1f) - .setDuration(1500) - .start(); - - findViewById(R.id.root_layout).setBackgroundColor(ThemeStore.primaryColor(this)); - } - - @Override - public void onPlayingMetaChanged() { - super.onPlayingMetaChanged(); - updateSongs(); - } - - @Override - public void onServiceConnected() { - super.onServiceConnected(); - updateSongs(); - } - - private void updateSongs() { - Song song = MusicPlayerRemote.getCurrentSong(); - SongGlideRequest.Builder.from(Glide.with(this), song) - .checkIgnoreMediaStore(this) - .generatePalette(this) - .build().into(new RetroMusicColoredTarget(findViewById(R.id.image)) { - @Override - public void onColorReady(int color) { - mFragment.setDark(color); - } - }); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/LyricsActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/LyricsActivity.java index f4be3c82..8f69927e 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/LyricsActivity.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/LyricsActivity.java @@ -137,8 +137,8 @@ public class LyricsActivity extends AbsMusicServiceActivity implements } private void loadLRCLyrics() { - if (LyricUtil.isLrcFileExist(song.title, song.artistName)) { - showLyricsLocal(LyricUtil.getLocalLyricFile(song.title, song.artistName)); + if (LyricUtil.isLrcFileExist(song.getTitle(), song.getArtistName())) { + showLyricsLocal(LyricUtil.getLocalLyricFile(song.getTitle(), song.getArtistName())); } } @@ -150,7 +150,7 @@ public class LyricsActivity extends AbsMusicServiceActivity implements disposable = new CompositeDisposable(); lyricView - .setOnPlayerClickListener((progress, content) -> MusicPlayerRemote.seekTo((int) progress)); + .setOnPlayerClickListener((progress, content) -> MusicPlayerRemote.INSTANCE.seekTo((int) progress)); //lyricView.setHighLightTextColor(ThemeStore.accentColor(this)); lyricView.setDefaultColor(ContextCompat.getColor(this, R.color.md_grey_400)); //lyricView.setTouchable(false); @@ -205,9 +205,9 @@ public class LyricsActivity extends AbsMusicServiceActivity implements } private void loadLrcFile() { - song = MusicPlayerRemote.getCurrentSong(); - bottomAppBar.setTitle(song.title); - bottomAppBar.setSubtitle(song.artistName); + song = MusicPlayerRemote.INSTANCE.getCurrentSong(); + bottomAppBar.setTitle(song.getTitle()); + bottomAppBar.setSubtitle(song.getArtistName()); SongGlideRequest.Builder.from(Glide.with(this), song) .checkIgnoreMediaStore(this) .generatePalette(this) @@ -254,7 +254,7 @@ public class LyricsActivity extends AbsMusicServiceActivity implements if (updateLyricsAsyncTask != null) { updateLyricsAsyncTask.cancel(false); } - final Song song = MusicPlayerRemote.getCurrentSong(); + final Song song = MusicPlayerRemote.INSTANCE.getCurrentSong(); updateLyricsAsyncTask = new AsyncTask() { @Override protected Lyrics doInBackground(Void... params) { @@ -292,7 +292,7 @@ public class LyricsActivity extends AbsMusicServiceActivity implements private void showSyncedLyrics() { String content = ""; try { - content = LyricUtil.getStringFromFile(song.title, song.artistName); + content = LyricUtil.getStringFromFile(song.getTitle(), song.getArtistName()); } catch (Exception e) { e.printStackTrace(); } @@ -302,21 +302,21 @@ public class LyricsActivity extends AbsMusicServiceActivity implements .content("Add time frame lyrics") .negativeText("Delete") .onNegative((dialog, which) -> { - LyricUtil.deleteLrcFile(song.title, song.artistName); + LyricUtil.deleteLrcFile(song.getTitle(), song.getArtistName()); loadLrcFile(); }) .onNeutral( (dialog, which) -> RetroUtil.openUrl(LyricsActivity.this, getGoogleSearchLrcUrl())) .inputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE) .input("Paste lyrics here", content, (dialog, input) -> { - LyricUtil.writeLrcToLoc(song.title, song.artistName, input.toString()); + LyricUtil.writeLrcToLoc(song.getTitle(), song.getArtistName(), input.toString()); loadLrcFile(); }).show(); } private String getGoogleSearchLrcUrl() { String baseUrl = "http://www.google.com/search?"; - String query = song.title + "+" + song.artistName; + String query = song.getTitle() + "+" + song.getArtistName(); query = "q=" + query.replace(" ", "+") + " .lrc"; baseUrl += query; return baseUrl; @@ -335,7 +335,7 @@ public class LyricsActivity extends AbsMusicServiceActivity implements .onNeutral(new MaterialDialog.SingleButtonCallback() { @Override public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) { - RetroUtil.openUrl(LyricsActivity.this, getGoogleSearchUrl(song.title, song.artistName)); + RetroUtil.openUrl(LyricsActivity.this, getGoogleSearchUrl(song.getTitle(), song.getArtistName())); } }) .inputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE) @@ -353,7 +353,7 @@ public class LyricsActivity extends AbsMusicServiceActivity implements private ArrayList getSongPaths(Song song) { ArrayList paths = new ArrayList<>(1); - paths.add(song.data); + paths.add(song.getData()); return paths; } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/MainActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/MainActivity.java deleted file mode 100644 index cc80ea55..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/MainActivity.java +++ /dev/null @@ -1,341 +0,0 @@ -package code.name.monkey.retromusic.ui.activities; - -import android.annotation.SuppressLint; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.SharedPreferences; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.net.Uri; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.provider.MediaStore; -import android.util.Log; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.FrameLayout; - -import com.afollestad.materialdialogs.MaterialDialog; -import com.google.android.material.bottomnavigation.BottomNavigationView; - -import java.util.ArrayList; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.app.ActivityCompat; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentTransaction; -import butterknife.BindView; -import butterknife.ButterKnife; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.RetroApplication; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.helper.SearchQueryHelper; -import code.name.monkey.retromusic.interfaces.MainActivityFragmentCallbacks; -import code.name.monkey.retromusic.loaders.AlbumLoader; -import code.name.monkey.retromusic.loaders.ArtistLoader; -import code.name.monkey.retromusic.loaders.PlaylistSongsLoader; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.service.MusicService; -import code.name.monkey.retromusic.ui.activities.base.AbsSlidingMusicPanelActivity; -import code.name.monkey.retromusic.ui.fragments.mainactivity.LibraryFragment; -import code.name.monkey.retromusic.ui.fragments.mainactivity.home.BannerHomeFragment; -import code.name.monkey.retromusic.util.PreferenceUtil; -import io.reactivex.disposables.CompositeDisposable; - -public class MainActivity extends AbsSlidingMusicPanelActivity implements SharedPreferences.OnSharedPreferenceChangeListener, BottomNavigationView.OnNavigationItemSelectedListener { - public static final int APP_INTRO_REQUEST = 2323; - public static final int LIBRARY = 1; - public static final int FOLDERS = 3; - public static final int HOME = 0; - private static final String TAG = "MainActivity"; - private static final int APP_USER_INFO_REQUEST = 9003; - private static final int REQUEST_CODE_THEME = 9002; - - @Nullable - MainActivityFragmentCallbacks currentFragment; - - @BindView(R.id.parent_container) - FrameLayout drawerLayout; - - - private boolean blockRequestPermissions; - private CompositeDisposable disposable = new CompositeDisposable(); - private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (action != null && action.equals(Intent.ACTION_SCREEN_OFF)) { - if (PreferenceUtil.getInstance().getLockScreen() && MusicPlayerRemote.isPlaying()) { - Intent activity = new Intent(context, LockScreenActivity.class); - activity.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - activity.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); - ActivityCompat.startActivity(context, activity, null); - } - } - } - }; - - @Override - protected View createContentView() { - @SuppressLint("InflateParams") - View contentView = getLayoutInflater().inflate(R.layout.activity_main_drawer_layout, null); - ViewGroup drawerContent = contentView.findViewById(R.id.drawer_content_container); - drawerContent.addView(wrapSlidingMusicPanel(R.layout.activity_main_content)); - return contentView; - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - setDrawUnderStatusBar(); - super.onCreate(savedInstanceState); - ButterKnife.bind(this); - - getBottomNavigationView().setOnNavigationItemSelectedListener(this); - - if (savedInstanceState == null) { - selectedFragment(PreferenceUtil.getInstance().getLastPage()); - } else { - restoreCurrentFragment(); - } - checkShowChangelog(); - - if (!RetroApplication.isProVersion() && !PreferenceManager.getDefaultSharedPreferences(this).getBoolean("shown", false)) { - showPromotionalOffer(); - } - } - - private void checkShowChangelog() { - try { - PackageInfo pInfo = getPackageManager().getPackageInfo(getPackageName(), 0); - int currentVersion = pInfo.versionCode; - if (currentVersion != PreferenceUtil.getInstance().getLastChangelogVersion()) { - startActivity(new Intent(this, WhatsNewActivity.class)); - } - } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); - } - } - - @Override - protected void onResume() { - super.onResume(); - IntentFilter screenOnOff = new IntentFilter(); - screenOnOff.addAction(Intent.ACTION_SCREEN_OFF); - registerReceiver(broadcastReceiver, screenOnOff); - - PreferenceUtil.getInstance().registerOnSharedPreferenceChangedListener(this); - - if (getIntent().hasExtra("expand")) { - if (getIntent().getBooleanExtra("expand", false)) { - //expandPanel(); - getIntent().putExtra("expand", false); - } - } - - } - - @Override - public void onDestroy() { - super.onDestroy(); - disposable.clear(); - if (broadcastReceiver == null) { - return; - } - unregisterReceiver(broadcastReceiver); - PreferenceUtil.getInstance().unregisterOnSharedPreferenceChangedListener(this); - } - - public void setCurrentFragment(@NonNull Fragment fragment, boolean isStackAdd, String tag) { - - FragmentManager fragmentManager = getSupportFragmentManager(); - FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); - fragmentTransaction.replace(R.id.fragment_container, fragment, tag); - if (isStackAdd) { - fragmentTransaction.addToBackStack(tag); - } - fragmentTransaction.commit(); - - currentFragment = (MainActivityFragmentCallbacks) fragment; - } - - private void restoreCurrentFragment() { - currentFragment = (MainActivityFragmentCallbacks) getSupportFragmentManager().findFragmentById(R.id.fragment_container); - } - - private void handlePlaybackIntent(@Nullable Intent intent) { - if (intent == null) { - return; - } - - Uri uri = intent.getData(); - String mimeType = intent.getType(); - boolean handled = false; - - if (intent.getAction() != null && intent.getAction().equals(MediaStore.INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH)) { - final ArrayList songs = SearchQueryHelper.getSongs(this, intent.getExtras()); - - if (MusicPlayerRemote.getShuffleMode() == MusicService.SHUFFLE_MODE_SHUFFLE) { - MusicPlayerRemote.openAndShuffleQueue(songs, true); - } else { - MusicPlayerRemote.openQueue(songs, 0, true); - } - handled = true; - } - - if (uri != null && uri.toString().length() > 0) { - MusicPlayerRemote.playFromUri(uri); - handled = true; - } else if (MediaStore.Audio.Playlists.CONTENT_TYPE.equals(mimeType)) { - final int id = (int) parseIdFromIntent(intent, "playlistId", "playlist"); - if (id >= 0) { - int position = intent.getIntExtra("position", 0); - ArrayList songs = new ArrayList<>( - PlaylistSongsLoader.getPlaylistSongList(this, id).blockingFirst()); - MusicPlayerRemote.openQueue(songs, position, true); - handled = true; - } - } else if (MediaStore.Audio.Albums.CONTENT_TYPE.equals(mimeType)) { - final int id = (int) parseIdFromIntent(intent, "albumId", "album"); - if (id >= 0) { - int position = intent.getIntExtra("position", 0); - MusicPlayerRemote - .openQueue(AlbumLoader.getAlbum(this, id).blockingFirst().songs, position, true); - handled = true; - } - } else if (MediaStore.Audio.Artists.CONTENT_TYPE.equals(mimeType)) { - final int id = (int) parseIdFromIntent(intent, "artistId", "artist"); - if (id >= 0) { - int position = intent.getIntExtra("position", 0); - MusicPlayerRemote - .openQueue(ArtistLoader.getArtist(this, id).blockingFirst().getSongs(), position, true); - handled = true; - } - } - if (handled) { - setIntent(new Intent()); - } - } - - private long parseIdFromIntent(@NonNull Intent intent, String longKey, String stringKey) { - long id = intent.getLongExtra(longKey, -1); - if (id < 0) { - String idString = intent.getStringExtra(stringKey); - if (idString != null) { - try { - id = Long.parseLong(idString); - } catch (NumberFormatException e) { - Log.e(TAG, e.getMessage()); - } - } - } - return id; - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - switch (requestCode) { - case APP_INTRO_REQUEST: - blockRequestPermissions = false; - if (!hasPermissions()) { - requestPermissions(); - } - - break; - case REQUEST_CODE_THEME: - case APP_USER_INFO_REQUEST: - postRecreate(); - break; - } - - } - - @Override - public boolean handleBackPress() { - return super.handleBackPress() || (currentFragment != null && - currentFragment.handleBackPress()); - } - - @Override - public void onServiceConnected() { - super.onServiceConnected(); - handlePlaybackIntent(getIntent()); - } - - @Override - protected void requestPermissions() { - if (!blockRequestPermissions) { - super.requestPermissions(); - } - } - - @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - if (key.equals(PreferenceUtil.GENERAL_THEME) || - key.equals(PreferenceUtil.ADAPTIVE_COLOR_APP) || - key.equals(PreferenceUtil.DOMINANT_COLOR) || - key.equals(PreferenceUtil.USER_NAME) || - key.equals(PreferenceUtil.TOGGLE_FULL_SCREEN) || - key.equals(PreferenceUtil.TOGGLE_VOLUME) || - key.equals(PreferenceUtil.ROUND_CORNERS) || - key.equals(PreferenceUtil.CAROUSEL_EFFECT) || - key.equals(PreferenceUtil.NOW_PLAYING_SCREEN_ID) || - key.equals(PreferenceUtil.TOGGLE_GENRE) || - key.equals(PreferenceUtil.BANNER_IMAGE_PATH) || - key.equals(PreferenceUtil.PROFILE_IMAGE_PATH) || - key.equals(PreferenceUtil.CIRCULAR_ALBUM_ART) || - key.equals(PreferenceUtil.KEEP_SCREEN_ON) || - key.equals(PreferenceUtil.TOGGLE_SEPARATE_LINE) || - key.equals(PreferenceUtil.ALBUM_GRID_STYLE) || - key.equals(PreferenceUtil.ARTIST_GRID_STYLE) || - key.equals(PreferenceUtil.TOGGLE_HOME_BANNER) || - key.equals(PreferenceUtil.TOGGLE_ADD_CONTROLS) || - key.equals(PreferenceUtil.ALBUM_COVER_STYLE) || - key.equals(PreferenceUtil.HOME_ARTIST_GRID_STYLE) || - key.equals(PreferenceUtil.ALBUM_COVER_TRANSFORM) || - key.equals(PreferenceUtil.TAB_TEXT_MODE)) postRecreate(); - } - - private void showPromotionalOffer() { - new MaterialDialog.Builder(this) - .positiveText("Buy") - .onPositive((dialog, which) -> - startActivity(new Intent(MainActivity.this, ProVersionActivity.class))) - .negativeText(android.R.string.cancel) - .customView(R.layout.dialog_promotional_offer, false) - .dismissListener(dialog -> { - PreferenceManager.getDefaultSharedPreferences(MainActivity.this) - .edit() - .putBoolean("shown", true) - .apply(); - }) - .show(); - } - - @Override - public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) { - PreferenceUtil.getInstance().setLastPage(menuItem.getItemId()); - selectedFragment(menuItem.getItemId()); - return true; - } - - private void selectedFragment(int itemId) { - switch (itemId) { - case R.id.action_album: - case R.id.action_artist: - case R.id.action_playlist: - case R.id.action_song: - setCurrentFragment(LibraryFragment.newInstance(itemId), false, LibraryFragment.TAG); - break; - case R.id.action_home: - setCurrentFragment(BannerHomeFragment.newInstance(), false, BannerHomeFragment.TAG); - break; - - } - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/MainActivity.kt b/app/src/main/java/code/name/monkey/retromusic/ui/activities/MainActivity.kt new file mode 100644 index 00000000..e875ca79 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/MainActivity.kt @@ -0,0 +1,294 @@ +package code.name.monkey.retromusic.ui.activities + +import android.annotation.SuppressLint +import android.content.* +import android.content.pm.PackageManager +import android.os.Bundle +import android.preference.PreferenceManager +import android.provider.MediaStore +import android.util.Log +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import butterknife.ButterKnife +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.RetroApplication +import code.name.monkey.retromusic.helper.MusicPlayerRemote +import code.name.monkey.retromusic.helper.SearchQueryHelper +import code.name.monkey.retromusic.interfaces.MainActivityFragmentCallbacks +import code.name.monkey.retromusic.loaders.AlbumLoader +import code.name.monkey.retromusic.loaders.ArtistLoader +import code.name.monkey.retromusic.loaders.PlaylistSongsLoader +import code.name.monkey.retromusic.service.MusicService +import code.name.monkey.retromusic.ui.activities.base.AbsSlidingMusicPanelActivity +import code.name.monkey.retromusic.ui.fragments.mainactivity.LibraryFragment +import code.name.monkey.retromusic.ui.fragments.mainactivity.home.BannerHomeFragment +import code.name.monkey.retromusic.util.PreferenceUtil +import com.afollestad.materialdialogs.MaterialDialog +import com.google.android.material.bottomnavigation.BottomNavigationView +import io.reactivex.disposables.CompositeDisposable +import java.util.* + +class MainActivity : AbsSlidingMusicPanelActivity(), SharedPreferences.OnSharedPreferenceChangeListener, BottomNavigationView.OnNavigationItemSelectedListener { + + lateinit var currentFragment: MainActivityFragmentCallbacks + + private var blockRequestPermissions: Boolean = false + private val disposable = CompositeDisposable() + private val broadcastReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + val action = intent.action + if (action != null && action == Intent.ACTION_SCREEN_OFF) { + if (PreferenceUtil.getInstance().lockScreen && MusicPlayerRemote.isPlaying) { + /*Intent activity = new Intent(context, LockScreenActivity.class); + activity.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + activity.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); + ActivityCompat.startActivity(context, activity, null);*/ + } + } + } + } + + override fun createContentView(): View { + @SuppressLint("InflateParams") + val contentView = layoutInflater.inflate(R.layout.activity_main_drawer_layout, null) + val drawerContent = contentView.findViewById(R.id.drawer_content_container) + drawerContent.addView(wrapSlidingMusicPanel(R.layout.activity_main_content)) + return contentView + } + + override fun onCreate(savedInstanceState: Bundle?) { + setDrawUnderStatusBar() + super.onCreate(savedInstanceState) + ButterKnife.bind(this) + + getBottomNavigationView()!!.setOnNavigationItemSelectedListener(this) + + if (savedInstanceState == null) { + selectedFragment(PreferenceUtil.getInstance().lastPage) + } else { + restoreCurrentFragment() + } + checkShowChangelog() + + if (!RetroApplication.isProVersion && !PreferenceManager.getDefaultSharedPreferences(this).getBoolean("shown", false)) { + showPromotionalOffer() + } + } + + private fun checkShowChangelog() { + try { + val pInfo = packageManager.getPackageInfo(packageName, 0) + val currentVersion = pInfo.versionCode + if (currentVersion != PreferenceUtil.getInstance().lastChangelogVersion) { + startActivityForResult(Intent(this, WhatsNewActivity::class.java), APP_INTRO_REQUEST) + } + } catch (e: PackageManager.NameNotFoundException) { + e.printStackTrace() + } + + } + + override fun onResume() { + super.onResume() + val screenOnOff = IntentFilter() + screenOnOff.addAction(Intent.ACTION_SCREEN_OFF) + registerReceiver(broadcastReceiver, screenOnOff) + + PreferenceUtil.getInstance().registerOnSharedPreferenceChangedListener(this) + + if (intent.hasExtra("expand")) { + if (intent.getBooleanExtra("expand", false)) { + //expandPanel(); + intent.putExtra("expand", false) + } + } + + } + + override fun onDestroy() { + super.onDestroy() + disposable.clear() + unregisterReceiver(broadcastReceiver) + PreferenceUtil.getInstance().unregisterOnSharedPreferenceChangedListener(this) + } + + fun setCurrentFragment(fragment: Fragment, isStackAdd: Boolean, tag: String) { + val fragmentTransaction = supportFragmentManager.beginTransaction() + fragmentTransaction.replace(R.id.fragment_container, fragment, tag) + if (isStackAdd) { + fragmentTransaction.addToBackStack(tag) + } + fragmentTransaction.commit() + currentFragment = fragment as MainActivityFragmentCallbacks + } + + private fun restoreCurrentFragment() { + currentFragment = supportFragmentManager.findFragmentById(R.id.fragment_container) as MainActivityFragmentCallbacks + } + + private fun handlePlaybackIntent(intent: Intent?) { + if (intent == null) { + return + } + + val uri = intent.data + val mimeType = intent.type + var handled = false + + if (intent.action != null && intent.action == MediaStore.INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH) { + val songs = SearchQueryHelper.getSongs(this, intent.extras!!) + + if (MusicPlayerRemote.shuffleMode == MusicService.SHUFFLE_MODE_SHUFFLE) { + MusicPlayerRemote.openAndShuffleQueue(songs, true) + } else { + MusicPlayerRemote.openQueue(songs, 0, true) + } + handled = true + } + + if (uri != null && uri.toString().length > 0) { + MusicPlayerRemote.playFromUri(uri) + handled = true + } else if (MediaStore.Audio.Playlists.CONTENT_TYPE == mimeType) { + val id = parseIdFromIntent(intent, "playlistId", "playlist").toInt() + if (id >= 0) { + val position = intent.getIntExtra("position", 0) + val songs = ArrayList( + PlaylistSongsLoader.getPlaylistSongList(this, id).blockingFirst()) + MusicPlayerRemote.openQueue(songs, position, true) + handled = true + } + } else if (MediaStore.Audio.Albums.CONTENT_TYPE == mimeType) { + val id = parseIdFromIntent(intent, "albumId", "album").toInt() + if (id >= 0) { + val position = intent.getIntExtra("position", 0) + MusicPlayerRemote + .openQueue(AlbumLoader.getAlbum(this, id).blockingFirst().songs!!, position, true) + handled = true + } + } else if (MediaStore.Audio.Artists.CONTENT_TYPE == mimeType) { + val id = parseIdFromIntent(intent, "artistId", "artist").toInt() + if (id >= 0) { + val position = intent.getIntExtra("position", 0) + MusicPlayerRemote + .openQueue(ArtistLoader.getArtist(this, id).blockingFirst().songs, position, true) + handled = true + } + } + if (handled) { + setIntent(Intent()) + } + } + + private fun parseIdFromIntent(intent: Intent, longKey: String, stringKey: String): Long { + var id = intent.getLongExtra(longKey, -1) + if (id < 0) { + val idString = intent.getStringExtra(stringKey) + if (idString != null) { + try { + id = java.lang.Long.parseLong(idString) + } catch (e: NumberFormatException) { + Log.e(TAG, e.message) + } + + } + } + return id + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + when (requestCode) { + APP_INTRO_REQUEST -> { + blockRequestPermissions = false + if (!hasPermissions()) { + requestPermissions() + } + } + REQUEST_CODE_THEME, APP_USER_INFO_REQUEST -> postRecreate() + } + + } + + override fun handleBackPress(): Boolean { + return super.handleBackPress() || currentFragment.handleBackPress() + } + + override fun onServiceConnected() { + super.onServiceConnected() + handlePlaybackIntent(intent) + } + + override fun requestPermissions() { + if (!blockRequestPermissions) { + super.requestPermissions() + } + } + + override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) { + if (key == PreferenceUtil.GENERAL_THEME || + key == PreferenceUtil.ADAPTIVE_COLOR_APP || + key == PreferenceUtil.DOMINANT_COLOR || + key == PreferenceUtil.USER_NAME || + key == PreferenceUtil.TOGGLE_FULL_SCREEN || + key == PreferenceUtil.TOGGLE_VOLUME || + key == PreferenceUtil.ROUND_CORNERS || + key == PreferenceUtil.CAROUSEL_EFFECT || + key == PreferenceUtil.NOW_PLAYING_SCREEN_ID || + key == PreferenceUtil.TOGGLE_GENRE || + key == PreferenceUtil.BANNER_IMAGE_PATH || + key == PreferenceUtil.PROFILE_IMAGE_PATH || + key == PreferenceUtil.CIRCULAR_ALBUM_ART || + key == PreferenceUtil.KEEP_SCREEN_ON || + key == PreferenceUtil.TOGGLE_SEPARATE_LINE || + key == PreferenceUtil.ALBUM_GRID_STYLE || + key == PreferenceUtil.ARTIST_GRID_STYLE || + key == PreferenceUtil.TOGGLE_HOME_BANNER || + key == PreferenceUtil.TOGGLE_ADD_CONTROLS || + key == PreferenceUtil.ALBUM_COVER_STYLE || + key == PreferenceUtil.HOME_ARTIST_GRID_STYLE || + key == PreferenceUtil.ALBUM_COVER_TRANSFORM || + key == PreferenceUtil.TAB_TEXT_MODE) + postRecreate() + } + + private fun showPromotionalOffer() { + MaterialDialog.Builder(this) + .positiveText("Buy") + .onPositive { dialog, which -> startActivity(Intent(this@MainActivity, ProVersionActivity::class.java)) } + .negativeText(android.R.string.cancel) + .customView(R.layout.dialog_promotional_offer, false) + .dismissListener { dialog -> + PreferenceManager.getDefaultSharedPreferences(this@MainActivity) + .edit() + .putBoolean("shown", true) + .apply() + } + .show() + } + + override fun onNavigationItemSelected(menuItem: MenuItem): Boolean { + PreferenceUtil.getInstance().lastPage = menuItem.itemId + selectedFragment(menuItem.itemId) + return true + } + + private fun selectedFragment(itemId: Int) { + when (itemId) { + R.id.action_album, R.id.action_artist, R.id.action_playlist, R.id.action_song -> setCurrentFragment(LibraryFragment.newInstance(itemId), false, LibraryFragment.TAG) + R.id.action_home -> setCurrentFragment(BannerHomeFragment.newInstance(), false, BannerHomeFragment.TAG) + } + } + + companion object { + const val APP_INTRO_REQUEST = 2323 + const val LIBRARY = 1 + const val FOLDERS = 3 + const val HOME = 0 + private const val TAG = "MainActivity" + private const val APP_USER_INFO_REQUEST = 9003 + private const val REQUEST_CODE_THEME = 9002 + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/NowPayingActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/NowPayingActivity.java deleted file mode 100644 index 5a56b8f6..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/NowPayingActivity.java +++ /dev/null @@ -1,180 +0,0 @@ -package code.name.monkey.retromusic.ui.activities; - -import android.content.SharedPreferences; -import android.graphics.Color; -import android.os.Bundle; -import android.transition.Slide; -import android.view.Gravity; -import android.view.animation.AnimationUtils; - -import androidx.fragment.app.Fragment; -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.appthemehelper.util.ColorUtil; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.ui.activities.base.AbsMusicServiceActivity; -import code.name.monkey.retromusic.ui.fragments.NowPlayingScreen; -import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerFragment; -import code.name.monkey.retromusic.ui.fragments.player.adaptive.AdaptiveFragment; -import code.name.monkey.retromusic.ui.fragments.player.blur.BlurPlayerFragment; -import code.name.monkey.retromusic.ui.fragments.player.card.CardFragment; -import code.name.monkey.retromusic.ui.fragments.player.cardblur.CardBlurFragment; -import code.name.monkey.retromusic.ui.fragments.player.color.ColorFragment; -import code.name.monkey.retromusic.ui.fragments.player.fit.FitFragment; -import code.name.monkey.retromusic.ui.fragments.player.flat.FlatPlayerFragment; -import code.name.monkey.retromusic.ui.fragments.player.full.FullPlayerFragment; -import code.name.monkey.retromusic.ui.fragments.player.hmm.HmmPlayerFragment; -import code.name.monkey.retromusic.ui.fragments.player.material.MaterialFragment; -import code.name.monkey.retromusic.ui.fragments.player.normal.PlayerFragment; -import code.name.monkey.retromusic.ui.fragments.player.plain.PlainPlayerFragment; -import code.name.monkey.retromusic.ui.fragments.player.simple.SimplePlayerFragment; -import code.name.monkey.retromusic.util.PreferenceUtil; - -public class NowPayingActivity extends AbsMusicServiceActivity implements AbsPlayerFragment.Callbacks, SharedPreferences.OnSharedPreferenceChangeListener { - - private NowPlayingScreen currentNowPlayingScreen; - private AbsPlayerFragment playerFragment; - - void setupWindowTransition() { - Slide slide = new Slide(Gravity.BOTTOM); - slide.setInterpolator( - AnimationUtils.loadInterpolator(this, android.R.interpolator.linear_out_slow_in)); - getWindow().setEnterTransition(slide); - //getWindow().setExitTransition(slide); - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - setLightNavigationBar(true); - setDrawUnderNavigationBar(); - setupWindowTransition(); - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_now_playng); - chooseFragmentForTheme(); - } - - private boolean isOneOfTheseThemes() { - return currentNowPlayingScreen == NowPlayingScreen.FLAT - || currentNowPlayingScreen == NowPlayingScreen.PLAIN - || currentNowPlayingScreen == NowPlayingScreen.SIMPLE - || currentNowPlayingScreen == NowPlayingScreen.NORMAL - || currentNowPlayingScreen == NowPlayingScreen.ADAPTIVE - || currentNowPlayingScreen == NowPlayingScreen.TINY - || currentNowPlayingScreen == NowPlayingScreen.MATERIAL; - } - - @Override - public void onPaletteColorChanged() { - int paletteColor = playerFragment.getPaletteColor(); - boolean isColorLight = ColorUtil.isColorLight(paletteColor); - super.setTaskDescriptionColor(paletteColor); - if ((currentNowPlayingScreen == NowPlayingScreen.FLAT || currentNowPlayingScreen == NowPlayingScreen.NORMAL) && PreferenceUtil.getInstance().getAdaptiveColor()) { - setLightNavigationBar(ColorUtil.isColorLight(ThemeStore.primaryColor(this))); - setLightStatusbar(isColorLight); - } else if (currentNowPlayingScreen == NowPlayingScreen.COLOR) { - setLightStatusbar(isColorLight); - setLightNavigationBar(isColorLight); - setNavigationbarColor(paletteColor); - } else if (currentNowPlayingScreen == NowPlayingScreen.BLUR || currentNowPlayingScreen == NowPlayingScreen.BLUR_CARD) { - setLightStatusbar(false); - setLightNavigationBar(false); - } else if (currentNowPlayingScreen == NowPlayingScreen.CARD || currentNowPlayingScreen == NowPlayingScreen.FULL) { - setLightStatusbar(false); - setLightNavigationBar(false); - } else if (currentNowPlayingScreen == NowPlayingScreen.FIT) { - setNavigationbarColor(ThemeStore.primaryColor(this)); - setLightNavigationBar(ColorUtil.isColorLight(ThemeStore.primaryColor(this))); - setLightStatusbar(false); - } else { - boolean isTheme = isOneOfTheseThemes() && ColorUtil.isColorLight(ThemeStore.primaryColor(this)); - setStatusbarColor(Color.TRANSPARENT); - setLightStatusbar(isTheme); - setLightNavigationBar(isTheme); - } - } - - @Override - protected void onResume() { - super.onResume(); - if (currentNowPlayingScreen != PreferenceUtil.getInstance().getNowPlayingScreen()) { - postRecreate(); - } - PreferenceUtil.getInstance().registerOnSharedPreferenceChangedListener(this); - } - - @Override - protected void onPause() { - super.onPause(); - PreferenceUtil.getInstance().unregisterOnSharedPreferenceChangedListener(this); - } - - @Override - public void onQueueChanged() { - super.onQueueChanged(); - if (MusicPlayerRemote.getPlayingQueue().isEmpty()) { - finish(); - } - } - - private void chooseFragmentForTheme() { - currentNowPlayingScreen = PreferenceUtil.getInstance().getNowPlayingScreen(); - - Fragment fragment; // must implement AbsPlayerFragment - switch (currentNowPlayingScreen) { - case MATERIAL: - fragment = new MaterialFragment(); - break; - case FIT: - fragment = new FitFragment(); - break; - case BLUR: - fragment = new BlurPlayerFragment(); - break; - case FLAT: - fragment = new FlatPlayerFragment(); - break; - case PLAIN: - fragment = new PlainPlayerFragment(); - break; - case FULL: - fragment = new FullPlayerFragment(); - break; - case COLOR: - fragment = new ColorFragment(); - break; - case CARD: - fragment = new CardFragment(); - break; - case SIMPLE: - fragment = new SimplePlayerFragment(); - break; - case TINY: - fragment = new HmmPlayerFragment(); - break; - case BLUR_CARD: - fragment = new CardBlurFragment(); - break; - case ADAPTIVE: - fragment = new AdaptiveFragment(); - break; - case NORMAL: - default: - fragment = new PlayerFragment(); - break; - } - getSupportFragmentManager().beginTransaction().replace(R.id.player_fragment_container, fragment).commit(); - getSupportFragmentManager().executePendingTransactions(); - - playerFragment = (AbsPlayerFragment) getSupportFragmentManager() - .findFragmentById(R.id.player_fragment_container); - - - } - - @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - if (key.equals(PreferenceUtil.ALBUM_COVER_STYLE) || key.equals(PreferenceUtil.ALBUM_COVER_TRANSFORM)) { - recreate(); - } - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/PlayingQueueActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/PlayingQueueActivity.java index 643e7887..4f90e5b6 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/PlayingQueueActivity.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/PlayingQueueActivity.java @@ -81,8 +81,8 @@ public class PlayingQueueActivity extends AbsMusicServiceActivity { mPlayingQueueAdapter = new PlayingQueueAdapter( this, - MusicPlayerRemote.getPlayingQueue(), - MusicPlayerRemote.getPosition(), + MusicPlayerRemote.INSTANCE.getPlayingQueue(), + MusicPlayerRemote.INSTANCE.getPosition(), R.layout.item_queue); mWrappedAdapter = mRecyclerViewDragDropManager.createWrappedAdapter(mPlayingQueueAdapter); @@ -92,7 +92,7 @@ public class PlayingQueueActivity extends AbsMusicServiceActivity { mRecyclerView.setAdapter(mWrappedAdapter); mRecyclerView.setItemAnimator(animator); mRecyclerViewDragDropManager.attachRecyclerView(mRecyclerView); - mLayoutManager.scrollToPositionWithOffset(MusicPlayerRemote.getPosition() + 1, 0); + mLayoutManager.scrollToPositionWithOffset(MusicPlayerRemote.INSTANCE.getPosition() + 1, 0); mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override @@ -109,7 +109,7 @@ public class PlayingQueueActivity extends AbsMusicServiceActivity { @Override public void onQueueChanged() { - if (MusicPlayerRemote.getPlayingQueue().isEmpty()) { + if (MusicPlayerRemote.INSTANCE.getPlayingQueue().isEmpty()) { finish(); return; } @@ -133,18 +133,18 @@ public class PlayingQueueActivity extends AbsMusicServiceActivity { } private void updateQueuePosition() { - mPlayingQueueAdapter.setCurrent(MusicPlayerRemote.getPosition()); + mPlayingQueueAdapter.setCurrent(MusicPlayerRemote.INSTANCE.getPosition()); resetToCurrentPosition(); } private void updateQueue() { - mPlayingQueueAdapter.swapDataSet(MusicPlayerRemote.getPlayingQueue(), MusicPlayerRemote.getPosition()); + mPlayingQueueAdapter.swapDataSet(MusicPlayerRemote.INSTANCE.getPlayingQueue(), MusicPlayerRemote.INSTANCE.getPosition()); resetToCurrentPosition(); } private void resetToCurrentPosition() { mRecyclerView.stopScroll(); - mLayoutManager.scrollToPositionWithOffset(MusicPlayerRemote.getPosition() + 1, 0); + mLayoutManager.scrollToPositionWithOffset(MusicPlayerRemote.INSTANCE.getPosition() + 1, 0); } @Override @@ -178,7 +178,7 @@ public class PlayingQueueActivity extends AbsMusicServiceActivity { } protected String getUpNextAndQueueTime() { - return getResources().getString(R.string.up_next) + " • " + MusicUtil.getReadableDurationString(MusicPlayerRemote.getQueueDurationMillis(MusicPlayerRemote.getPosition())); + return getResources().getString(R.string.up_next) + " • " + MusicUtil.getReadableDurationString(MusicPlayerRemote.INSTANCE.getQueueDurationMillis(MusicPlayerRemote.INSTANCE.getPosition())); } private void setupToolbar() { @@ -198,6 +198,6 @@ public class PlayingQueueActivity extends AbsMusicServiceActivity { @OnClick(R.id.clear_queue) void clearQueue() { - MusicPlayerRemote.clearQueue(); + MusicPlayerRemote.INSTANCE.clearQueue(); } } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/PlaylistDetailActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/PlaylistDetailActivity.java index 027c9a52..724b3e51 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/PlaylistDetailActivity.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/PlaylistDetailActivity.java @@ -32,7 +32,6 @@ import code.name.monkey.retromusic.interfaces.CabHolder; import code.name.monkey.retromusic.loaders.PlaylistLoader; import code.name.monkey.retromusic.model.AbsCustomPlaylist; import code.name.monkey.retromusic.model.Playlist; -import code.name.monkey.retromusic.model.PlaylistSong; import code.name.monkey.retromusic.model.Song; import code.name.monkey.retromusic.mvp.contract.PlaylistSongsContract; import code.name.monkey.retromusic.mvp.presenter.PlaylistSongsPresenter; @@ -114,10 +113,9 @@ public class PlaylistDetailActivity extends AbsSlidingMusicPanelActivity impleme } else { recyclerViewDragDropManager = new RecyclerViewDragDropManager(); final GeneralItemAnimator animator = new RefactoredDefaultItemAnimator(); - adapter = new OrderablePlaylistSongAdapter(this, new ArrayList(), + adapter = new OrderablePlaylistSongAdapter(this, new ArrayList<>(), R.layout.item_list, false, this, (fromPosition, toPosition) -> { - if (PlaylistsUtil - .moveItem(PlaylistDetailActivity.this, playlist.id, fromPosition, toPosition)) { + if (PlaylistsUtil.moveItem(PlaylistDetailActivity.this, playlist.id, fromPosition, toPosition)) { Song song = adapter.getDataSet().remove(fromPosition); adapter.getDataSet().add(toPosition, song); adapter.notifyItemMoved(fromPosition, toPosition); @@ -186,7 +184,7 @@ public class PlaylistDetailActivity extends AbsSlidingMusicPanelActivity impleme onBackPressed(); return true; } - return PlaylistMenuHelper.handleMenuClick(this, playlist, item); + return PlaylistMenuHelper.INSTANCE.handleMenuClick(this, playlist, item); } @NonNull @@ -228,7 +226,7 @@ public class PlaylistDetailActivity extends AbsSlidingMusicPanelActivity impleme // Playlist renamed final String playlistName = PlaylistsUtil.getNameForPlaylist(this, playlist.id); if (!playlistName.equals(playlist.name)) { - playlist = PlaylistLoader.getPlaylist(this, playlist.id).blockingFirst(); + playlist = PlaylistLoader.INSTANCE.getPlaylist(this, playlist.id).blockingFirst(); setToolbarTitle(playlist.name); } } @@ -306,6 +304,6 @@ public class PlaylistDetailActivity extends AbsSlidingMusicPanelActivity impleme if (adapter.getDataSet().isEmpty()) { return; } - MusicPlayerRemote.openAndShuffleQueue(adapter.getDataSet(), true); + MusicPlayerRemote.INSTANCE.openAndShuffleQueue(adapter.getDataSet(), true); } } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/ProVersionActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/ProVersionActivity.java index dfd277cf..e2a8ea95 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/ProVersionActivity.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/ProVersionActivity.java @@ -119,7 +119,7 @@ public class ProVersionActivity extends AbsBaseActivity implements @Override public void onPurchaseHistoryRestored() { - if (RetroApplication.isProVersion()) { + if (RetroApplication.Companion.isProVersion()) { Toast.makeText(this, R.string.restored_previous_purchase_please_restart, Toast.LENGTH_LONG) .show(); setResult(RESULT_OK); diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/SettingsActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/SettingsActivity.java index d09a7ccf..b0e912ab 100755 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/SettingsActivity.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/SettingsActivity.java @@ -1,7 +1,6 @@ package code.name.monkey.retromusic.ui.activities; import android.content.SharedPreferences; -import android.os.Build; import android.os.Bundle; import android.util.Log; import android.view.MenuItem; @@ -26,7 +25,6 @@ import code.name.monkey.appthemehelper.ThemeStore; import code.name.monkey.appthemehelper.util.ColorUtil; import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper; import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.appshortcuts.DynamicShortcutManager; import code.name.monkey.retromusic.ui.activities.base.AbsBaseActivity; import code.name.monkey.retromusic.ui.fragments.settings.MainSettingsFragment; import code.name.monkey.retromusic.util.PreferenceUtil; @@ -65,9 +63,6 @@ public class SettingsActivity extends AbsBaseActivity implements ColorChooserDia break; } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { - new DynamicShortcutManager(this).updateDynamicShortcuts(); - } recreate(); } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/SupportDevelopmentActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/SupportDevelopmentActivity.java index 51c24f8d..4d84ed6d 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/SupportDevelopmentActivity.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/SupportDevelopmentActivity.java @@ -115,7 +115,7 @@ public class SupportDevelopmentActivity extends AbsBaseActivity implements Billi @OnClick(R.id.donate) void donate() { - RetroUtil.openUrl(this, PAYPAL_ME_URL); + RetroUtil.openUrl(this,PAYPAL_ME_URL); } @Override diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/WhatsNewActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/WhatsNewActivity.java deleted file mode 100644 index cb98165b..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/WhatsNewActivity.java +++ /dev/null @@ -1,98 +0,0 @@ -package code.name.monkey.retromusic.ui.activities; - -import android.content.Context; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.os.Bundle; -import android.webkit.WebView; -import android.widget.TextView; - -import com.afollestad.materialdialogs.internal.ThemeSingleton; -import com.google.android.material.appbar.AppBarLayout; - -import java.io.BufferedReader; -import java.io.InputStream; -import java.io.InputStreamReader; - -import androidx.annotation.NonNull; -import androidx.appcompat.widget.Toolbar; -import butterknife.BindView; -import butterknife.ButterKnife; -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.appthemehelper.util.ColorUtil; -import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.ui.activities.base.AbsBaseActivity; -import code.name.monkey.retromusic.util.PreferenceUtil; - -public class WhatsNewActivity extends AbsBaseActivity { - @BindView(R.id.web_view) - WebView webView; - - @BindView(R.id.title) - TextView title; - - @BindView(R.id.toolbar) - Toolbar toolbar; - - @BindView(R.id.app_bar) - AppBarLayout appBarLayout; - - - private static void setChangelogRead(@NonNull Context context) { - try { - PackageInfo pInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); - int currentVersion = pInfo.versionCode; - PreferenceUtil.getInstance().setLastChangeLogVersion(currentVersion); - } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); - } - } - - private static String colorToHex(int color) { - return Integer.toHexString(color).substring(2); - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_whats_new); - ButterKnife.bind(this); - - setStatusbarColorAuto(); - setNavigationbarColorAuto(); - setTaskDescriptionColorAuto(); - - toolbar.setBackgroundColor(ThemeStore.primaryColor(this)); - appBarLayout.setBackgroundColor(ThemeStore.primaryColor(this)); - setSupportActionBar(toolbar); - setTitle(null); - toolbar.setNavigationOnClickListener(v -> onBackPressed()); - title.setTextColor(ThemeStore.textColorPrimary(this)); - ToolbarContentTintHelper.colorBackButton(toolbar, ThemeStore.accentColor(this)); - - try { - // Load from phonograph-changelog.html in the assets folder - StringBuilder buf = new StringBuilder(); - InputStream json = getAssets().open("retro-changelog.html"); - BufferedReader in = new BufferedReader(new InputStreamReader(json, "UTF-8")); - String str; - while ((str = in.readLine()) != null) - buf.append(str); - in.close(); - - // Inject color values for WebView body background and links - final String backgroundColor = colorToHex(ThemeStore.primaryColor(this)); - final String contentColor = ThemeSingleton.get().darkTheme ? "#ffffff" : "#000000"; - webView.loadData(buf.toString() - .replace("{style-placeholder}", - String.format("body { background-color: %s; color: %s; }", backgroundColor, contentColor)) - .replace("{link-color}", colorToHex(ThemeSingleton.get().positiveColor.getDefaultColor())) - .replace("{link-color-active}", colorToHex(ColorUtil.lightenColor(ThemeSingleton.get().positiveColor.getDefaultColor()))) - , "text/html", "UTF-8"); - } catch (Throwable e) { - webView.loadData("

Unable to load

" + e.getLocalizedMessage() + "

", "text/html", "UTF-8"); - } - setChangelogRead(this); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/WhatsNewActivity.kt b/app/src/main/java/code/name/monkey/retromusic/ui/activities/WhatsNewActivity.kt new file mode 100644 index 00000000..f70c5e94 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/WhatsNewActivity.kt @@ -0,0 +1,75 @@ +package code.name.monkey.retromusic.ui.activities + +import android.content.Context +import android.content.pm.PackageManager +import android.os.Bundle +import butterknife.ButterKnife +import code.name.monkey.appthemehelper.ThemeStore +import code.name.monkey.appthemehelper.util.ColorUtil +import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.ui.activities.base.AbsBaseActivity +import code.name.monkey.retromusic.util.PreferenceUtil +import com.afollestad.materialdialogs.internal.ThemeSingleton +import kotlinx.android.synthetic.main.activity_whats_new.* +import java.io.BufferedReader +import java.io.InputStreamReader + +class WhatsNewActivity : AbsBaseActivity() { + + + private fun setChangelogRead(context: Context) { + try { + val pInfo = context.packageManager.getPackageInfo(context.packageName, 0) + val currentVersion = pInfo.versionCode + PreferenceUtil.getInstance().setLastChangeLogVersion(currentVersion) + } catch (e: PackageManager.NameNotFoundException) { + e.printStackTrace() + } + + } + + private fun colorToHex(color: Int): String { + return Integer.toHexString(color).substring(2) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_whats_new) + ButterKnife.bind(this) + + setStatusbarColorAuto() + setNavigationbarColorAuto() + setTaskDescriptionColorAuto() + + toolbar.setBackgroundColor(ThemeStore.primaryColor(this)) + appBarLayout.setBackgroundColor(ThemeStore.primaryColor(this)) + setSupportActionBar(toolbar) + title = null + toolbar.setNavigationOnClickListener({ v -> onBackPressed() }) + whatNewtitle.setTextColor(ThemeStore.textColorPrimary(this)) + ToolbarContentTintHelper.colorBackButton(toolbar, ThemeStore.accentColor(this)) + + try { + val buf = StringBuilder() + val json = assets.open("retro-changelog.html") + val inputStream = BufferedReader(InputStreamReader(json, "UTF-8")) + while (inputStream.readLine() != null) { + buf.append(inputStream.readLine()) + } + inputStream.close() + // Inject color values for WebView body background and links + val backgroundColor = colorToHex(ThemeStore.primaryColor(this)) + val contentColor = if (ThemeSingleton.get().darkTheme) "#ffffff" else "#000000" + webView.loadData(buf.toString() + .replace("{style-placeholder}", + String.format("body { background-color: %s; color: %s; }", backgroundColor, contentColor)) + .replace("{link-color}", colorToHex(ThemeSingleton.get().positiveColor.defaultColor)) + .replace("{link-color-active}", colorToHex(ColorUtil.lightenColor(ThemeSingleton.get().positiveColor.defaultColor))), "text/html", "UTF-8") + } catch (e: Throwable) { + webView.loadData("

Unable to load

" + e.localizedMessage + "

", "text/html", "UTF-8") + } + + setChangelogRead(this) + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsBaseActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsBaseActivity.java deleted file mode 100644 index 1c679cff..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsBaseActivity.java +++ /dev/null @@ -1,159 +0,0 @@ -package code.name.monkey.retromusic.ui.activities.base; - -import android.Manifest; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.media.AudioManager; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; -import android.provider.Settings; -import android.view.KeyEvent; -import android.view.View; -import android.view.ViewGroup; - -import com.google.android.material.snackbar.Snackbar; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.app.ActivityCompat; -import androidx.core.view.ViewCompat; -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.retromusic.R; -import uk.co.chrisjenx.calligraphy.CalligraphyContextWrapper; - - -public abstract class AbsBaseActivity extends AbsThemeActivity { - - public static final int PERMISSION_REQUEST = 100; - private boolean hadPermissions; - private String[] permissions; - private String permissionDeniedMessage; - - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setVolumeControlStream(AudioManager.STREAM_MUSIC); - - permissions = getPermissionsToRequest(); - hadPermissions = hasPermissions(); - - setPermissionDeniedMessage(null); - } - - @Override - protected void onPostCreate(@Nullable Bundle savedInstanceState) { - super.onPostCreate(savedInstanceState); - if (!hasPermissions()) { - requestPermissions(); - } - } - - @Override - protected void onResume() { - super.onResume(); - final boolean hasPermissions = hasPermissions(); - if (hasPermissions != hadPermissions) { - hadPermissions = hasPermissions; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - onHasPermissionsChanged(hasPermissions); - } - } - } - - protected void onHasPermissionsChanged(boolean hasPermissions) { - // implemented by sub classes - } - - @Override - public boolean dispatchKeyEvent(@NonNull KeyEvent event) { - if (event.getKeyCode() == KeyEvent.KEYCODE_MENU && event.getAction() == KeyEvent.ACTION_UP) { - showOverflowMenu(); - return true; - } - return super.dispatchKeyEvent(event); - } - - protected void showOverflowMenu() { - - } - - @Override - protected void attachBaseContext(Context newBase) { - super.attachBaseContext(CalligraphyContextWrapper.wrap(newBase)); - } - - @Nullable - protected String[] getPermissionsToRequest() { - return null; - } - - protected View getSnackBarContainer() { - return getWindow().getDecorView(); - } - - private String getPermissionDeniedMessage() { - return permissionDeniedMessage == null ? getString(R.string.permissions_denied) - : permissionDeniedMessage; - } - - protected void setPermissionDeniedMessage(String message) { - permissionDeniedMessage = message; - } - - protected void requestPermissions() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && permissions != null) { - requestPermissions(permissions, PERMISSION_REQUEST); - } - } - - protected boolean hasPermissions() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && permissions != null) { - for (String permission : permissions) { - if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) { - return false; - } - } - } - return true; - } - - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, - @NonNull int[] grantResults) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - if (requestCode == PERMISSION_REQUEST) { - for (int grantResult : grantResults) { - if (grantResult != PackageManager.PERMISSION_GRANTED) { - if (ActivityCompat.shouldShowRequestPermissionRationale(AbsBaseActivity.this, - Manifest.permission.WRITE_EXTERNAL_STORAGE)) { - //User has deny from permission dialog - Snackbar.make(getSnackBarContainer(), getPermissionDeniedMessage(), - Snackbar.LENGTH_INDEFINITE) - .setAction(R.string.action_grant, view -> requestPermissions()) - .setActionTextColor(ThemeStore.accentColor(this)) - .show(); - } else { - // User has deny permission and checked never show permission dialog so you can redirect to Application settings page - Snackbar.make(getSnackBarContainer(), getPermissionDeniedMessage(), - Snackbar.LENGTH_INDEFINITE) - .setAction(R.string.action_settings, view -> { - Intent intent = new Intent(); - intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); - Uri uri = Uri.fromParts("package", AbsBaseActivity.this.getPackageName(), null); - intent.setData(uri); - startActivity(intent); - }) - .setActionTextColor(ThemeStore.accentColor(this)) - .show(); - } - return; - } - } - hadPermissions = true; - onHasPermissionsChanged(true); - } - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsBaseActivity.kt b/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsBaseActivity.kt new file mode 100644 index 00000000..25850c50 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsBaseActivity.kt @@ -0,0 +1,145 @@ +package code.name.monkey.retromusic.ui.activities.base + +import android.Manifest +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.media.AudioManager +import android.net.Uri +import android.os.Build +import android.os.Bundle +import android.provider.Settings +import android.view.KeyEvent +import android.view.View +import androidx.core.app.ActivityCompat +import code.name.monkey.appthemehelper.ThemeStore +import code.name.monkey.retromusic.R +import com.google.android.material.snackbar.Snackbar +import uk.co.chrisjenx.calligraphy.CalligraphyContextWrapper + + +abstract class AbsBaseActivity : AbsThemeActivity() { + private var hadPermissions: Boolean = false + private var permissions: Array? = null + private var permissionDeniedMessage: String? = null + + + open fun getPermissionsToRequest(): Array? { + return null + } + + protected fun setPermissionDeniedMessage(message: String) { + permissionDeniedMessage = message + } + + fun getPermissionDeniedMessage(): String { + return if (permissionDeniedMessage == null) getString(R.string.permissions_denied) else permissionDeniedMessage!! + } + + + private val snackBarContainer: View + get() = window.decorView + + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + volumeControlStream = AudioManager.STREAM_MUSIC + permissions = getPermissionsToRequest() + hadPermissions = hasPermissions() + permissionDeniedMessage = null + } + + override fun onPostCreate(savedInstanceState: Bundle?) { + super.onPostCreate(savedInstanceState) + if (!hasPermissions()) { + requestPermissions() + } + } + + override fun onResume() { + super.onResume() + val hasPermissions = hasPermissions() + if (hasPermissions != hadPermissions) { + hadPermissions = hasPermissions + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + onHasPermissionsChanged(hasPermissions) + } + } + } + + protected open fun onHasPermissionsChanged(hasPermissions: Boolean) { + // implemented by sub classes + } + + override fun dispatchKeyEvent(event: KeyEvent): Boolean { + if (event.keyCode == KeyEvent.KEYCODE_MENU && event.action == KeyEvent.ACTION_UP) { + showOverflowMenu() + return true + } + return super.dispatchKeyEvent(event) + } + + protected fun showOverflowMenu() { + + } + + override fun attachBaseContext(newBase: Context) { + super.attachBaseContext(CalligraphyContextWrapper.wrap(newBase)) + } + + protected open fun requestPermissions() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + requestPermissions(permissions!!, PERMISSION_REQUEST) + } + } + + protected fun hasPermissions(): Boolean { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + for (permission in permissions!!) { + if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) { + return false + } + } + } + return true + } + + override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + if (requestCode == PERMISSION_REQUEST) { + for (grantResult in grantResults) { + if (grantResult != PackageManager.PERMISSION_GRANTED) { + if (ActivityCompat.shouldShowRequestPermissionRationale(this@AbsBaseActivity, + Manifest.permission.WRITE_EXTERNAL_STORAGE)) { + //User has deny from permission dialog + Snackbar.make(snackBarContainer, permissionDeniedMessage!!, + Snackbar.LENGTH_INDEFINITE) + .setAction(R.string.action_grant) { requestPermissions() } + .setActionTextColor(ThemeStore.accentColor(this)) + .show() + } else { + // User has deny permission and checked never show permission dialog so you can redirect to Application settings page + Snackbar.make(snackBarContainer, permissionDeniedMessage!!, + Snackbar.LENGTH_INDEFINITE) + .setAction(R.string.action_settings) { + val intent = Intent() + intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS + val uri = Uri.fromParts("package", this@AbsBaseActivity.packageName, null) + intent.data = uri + startActivity(intent) + } + .setActionTextColor(ThemeStore.accentColor(this)) + .show() + } + return + } + } + hadPermissions = true + onHasPermissionsChanged(true) + } + } + + companion object { + const val PERMISSION_REQUEST = 100 + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsCastActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsCastActivity.java deleted file mode 100644 index 7047458b..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsCastActivity.java +++ /dev/null @@ -1,157 +0,0 @@ -package code.name.monkey.retromusic.ui.activities.base; - -import android.os.Bundle; -import android.util.Log; -import android.view.Menu; - -import com.google.android.gms.cast.framework.CastButtonFactory; -import com.google.android.gms.cast.framework.CastContext; -import com.google.android.gms.cast.framework.CastSession; -import com.google.android.gms.cast.framework.SessionManagerListener; -import com.google.android.gms.common.ConnectionResult; -import com.google.android.gms.common.GoogleApiAvailability; - -import java.io.IOException; - -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.cast.WebServer; - -public abstract class AbsCastActivity extends AbsBaseActivity { - private static final String TAG = "AbsCastActivity"; - - public boolean playServicesAvailable = false; - - private CastContext castContext; - private SessionManagerListener sessionManagerListener; - private CastSession castSession; - private WebServer castServer; - - private void setupCastListener() { - sessionManagerListener = new SessionManagerListener() { - - @Override - public void onSessionEnded(CastSession session, int error) { - onApplicationDisconnected(); - Log.i(TAG, "onSessionEnded: "); - } - - @Override - public void onSessionResumed(CastSession session, boolean wasSuspended) { - onApplicationConnected(session); - Log.i(TAG, "onSessionResumed: "); - } - - @Override - public void onSessionResumeFailed(CastSession session, int error) { - onApplicationDisconnected(); - Log.i(TAG, "onSessionResumeFailed: "); - } - - @Override - public void onSessionStarted(CastSession session, String sessionId) { - onApplicationConnected(session); - Log.i(TAG, "onSessionStarted: "); - } - - @Override - public void onSessionStartFailed(CastSession session, int error) { - onApplicationDisconnected(); - Log.i(TAG, "onSessionStartFailed: "); - } - - @Override - public void onSessionStarting(CastSession session) { - } - - @Override - public void onSessionEnding(CastSession session) { - } - - @Override - public void onSessionResuming(CastSession session, String sessionId) { - } - - @Override - public void onSessionSuspended(CastSession session, int reason) { - } - - private void onApplicationConnected(CastSession castSession) { - AbsCastActivity.this.castSession = castSession; - castServer = new WebServer(getApplicationContext()); - try { - castServer.start(); - } catch (IOException e) { - e.printStackTrace(); - } - supportInvalidateOptionsMenu(); - showCastMiniController(); - } - - private void onApplicationDisconnected() { - if (castServer != null) { - castServer.stop(); - } - supportInvalidateOptionsMenu(); - hideCastMiniController(); - } - }; - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - try { - playServicesAvailable = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(this) == ConnectionResult.SUCCESS; - } catch (Exception ignored) { - - } - - if (playServicesAvailable) - initCast(); - - - } - - @Override - protected void onResume() { - if (playServicesAvailable) { - castContext.getSessionManager().addSessionManagerListener(sessionManagerListener, CastSession.class); - } - super.onResume(); - } - - @Override - protected void onPause() { - super.onPause(); - if (playServicesAvailable) { - castContext.getSessionManager().removeSessionManagerListener(sessionManagerListener, CastSession.class); - } - } - - private void initCast() { - setupCastListener(); - castContext = CastContext.getSharedInstance(this); - castSession = castContext.getSessionManager().getCurrentCastSession(); - } - - /* @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.menu_cast, menu); - if (playServicesAvailable) { - CastButtonFactory.setUpMediaRouteButton(getApplicationContext(), menu, R.id.media_route_menu_item); - } - return true; - }*/ - - public void showCastMiniController() { - //implement by overriding in activities - } - - public void hideCastMiniController() { - //implement by overriding in activities - } - - public CastSession getCastSession() { - return castSession; - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsCastActivity.kt b/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsCastActivity.kt new file mode 100644 index 00000000..e27d61cc --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsCastActivity.kt @@ -0,0 +1,135 @@ +package code.name.monkey.retromusic.ui.activities.base + +import android.os.Bundle +import android.util.Log +import code.name.monkey.retromusic.cast.WebServer +import com.google.android.gms.cast.framework.CastContext +import com.google.android.gms.cast.framework.CastSession +import com.google.android.gms.cast.framework.SessionManagerListener +import com.google.android.gms.common.ConnectionResult +import com.google.android.gms.common.GoogleApiAvailability +import java.io.IOException + +abstract class AbsCastActivity : AbsBaseActivity() { + + var playServicesAvailable = false + + private var castContext: CastContext? = null + private var sessionManagerListener: SessionManagerListener? = null + var castSession: CastSession? = null + private set + private var castServer: WebServer? = null + + private fun setupCastListener() { + sessionManagerListener = object : SessionManagerListener { + + override fun onSessionEnded(session: CastSession, error: Int) { + onApplicationDisconnected() + Log.i(TAG, "onSessionEnded: ") + } + + override fun onSessionResumed(session: CastSession, wasSuspended: Boolean) { + onApplicationConnected(session) + Log.i(TAG, "onSessionResumed: ") + } + + override fun onSessionResumeFailed(session: CastSession, error: Int) { + onApplicationDisconnected() + Log.i(TAG, "onSessionResumeFailed: ") + } + + override fun onSessionStarted(session: CastSession, sessionId: String) { + onApplicationConnected(session) + Log.i(TAG, "onSessionStarted: ") + } + + override fun onSessionStartFailed(session: CastSession, error: Int) { + onApplicationDisconnected() + Log.i(TAG, "onSessionStartFailed: ") + } + + override fun onSessionStarting(session: CastSession) {} + + override fun onSessionEnding(session: CastSession) {} + + override fun onSessionResuming(session: CastSession, sessionId: String) {} + + override fun onSessionSuspended(session: CastSession, reason: Int) {} + + private fun onApplicationConnected(castSession: CastSession) { + this@AbsCastActivity.castSession = castSession + castServer = WebServer(applicationContext) + try { + castServer!!.start() + } catch (e: IOException) { + e.printStackTrace() + } + invalidateOptionsMenu() + showCastMiniController() + } + + private fun onApplicationDisconnected() { + if (castServer != null) { + castServer!!.stop() + } + invalidateOptionsMenu() + hideCastMiniController() + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + try { + playServicesAvailable = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(this) == ConnectionResult.SUCCESS + } catch (ignored: Exception) { + + } + + if (playServicesAvailable) + initCast() + + + } + + override fun onResume() { + if (playServicesAvailable) { + castContext!!.sessionManager.addSessionManagerListener(sessionManagerListener!!, CastSession::class.java) + } + super.onResume() + } + + override fun onPause() { + super.onPause() + if (playServicesAvailable) { + castContext!!.sessionManager.removeSessionManagerListener(sessionManagerListener, CastSession::class.java) + } + } + + private fun initCast() { + setupCastListener() + castContext = CastContext.getSharedInstance(this) + castSession = castContext!!.sessionManager.currentCastSession + } + + /* @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.menu_cast, menu); + if (playServicesAvailable) { + CastButtonFactory.setUpMediaRouteButton(getApplicationContext(), menu, R.id.media_route_menu_item); + } + return true; + }*/ + + open fun showCastMiniController() { + //implement by overriding in activities + } + + open fun hideCastMiniController() { + //implement by overriding in activities + } + + companion object { + private val TAG = "AbsCastActivity" + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsMusicServiceActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsMusicServiceActivity.java deleted file mode 100644 index 4a690e68..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsMusicServiceActivity.java +++ /dev/null @@ -1,224 +0,0 @@ -package code.name.monkey.retromusic.ui.activities.base; - -import android.Manifest; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.ServiceConnection; -import android.os.Bundle; -import android.os.IBinder; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import java.lang.ref.WeakReference; -import java.util.ArrayList; - -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.interfaces.MusicServiceEventListener; - -import static code.name.monkey.retromusic.Constants.MEDIA_STORE_CHANGED; -import static code.name.monkey.retromusic.Constants.META_CHANGED; -import static code.name.monkey.retromusic.Constants.PLAY_STATE_CHANGED; -import static code.name.monkey.retromusic.Constants.QUEUE_CHANGED; -import static code.name.monkey.retromusic.Constants.REPEAT_MODE_CHANGED; -import static code.name.monkey.retromusic.Constants.SHUFFLE_MODE_CHANGED; - - -public abstract class AbsMusicServiceActivity extends AbsCastActivity implements MusicServiceEventListener { - public static final String TAG = AbsMusicServiceActivity.class.getSimpleName(); - - private final ArrayList mMusicServiceEventListeners = new ArrayList<>(); - - private MusicPlayerRemote.ServiceToken serviceToken; - private MusicStateReceiver musicStateReceiver; - private boolean receiverRegistered; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - serviceToken = MusicPlayerRemote.bindToService(this, new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - AbsMusicServiceActivity.this.onServiceConnected(); - } - - @Override - public void onServiceDisconnected(ComponentName name) { - AbsMusicServiceActivity.this.onServiceDisconnected(); - } - }); - - setPermissionDeniedMessage(getString(R.string.permission_external_storage_denied)); - - } - - @Override - public void onDestroy() { - super.onDestroy(); - MusicPlayerRemote.unbindFromService(serviceToken); - if (receiverRegistered) { - unregisterReceiver(musicStateReceiver); - receiverRegistered = false; - } - } - - public void addMusicServiceEventListener(final MusicServiceEventListener listener) { - if (listener != null) { - mMusicServiceEventListeners.add(listener); - } - } - - public void removeMusicServiceEventListener(final MusicServiceEventListener listener) { - if (listener != null) { - mMusicServiceEventListeners.remove(listener); - } - } - - @Override - public void onServiceConnected() { - if (!receiverRegistered) { - musicStateReceiver = new MusicStateReceiver(this); - - final IntentFilter filter = new IntentFilter(); - filter.addAction(PLAY_STATE_CHANGED); - filter.addAction(SHUFFLE_MODE_CHANGED); - filter.addAction(REPEAT_MODE_CHANGED); - filter.addAction(META_CHANGED); - filter.addAction(QUEUE_CHANGED); - filter.addAction(MEDIA_STORE_CHANGED); - - registerReceiver(musicStateReceiver, filter); - - receiverRegistered = true; - } - - for (MusicServiceEventListener listener : mMusicServiceEventListeners) { - if (listener != null) { - listener.onServiceConnected(); - } - } - } - - @Override - public void onServiceDisconnected() { - if (receiverRegistered) { - unregisterReceiver(musicStateReceiver); - receiverRegistered = false; - } - - for (MusicServiceEventListener listener : mMusicServiceEventListeners) { - if (listener != null) { - listener.onServiceDisconnected(); - } - } - } - - @Override - public void onPlayingMetaChanged() { - for (MusicServiceEventListener listener : mMusicServiceEventListeners) { - if (listener != null) { - listener.onPlayingMetaChanged(); - } - } - } - - @Override - public void onQueueChanged() { - for (MusicServiceEventListener listener : mMusicServiceEventListeners) { - if (listener != null) { - listener.onQueueChanged(); - } - } - } - - @Override - public void onPlayStateChanged() { - for (MusicServiceEventListener listener : mMusicServiceEventListeners) { - if (listener != null) { - listener.onPlayStateChanged(); - } - } - } - - @Override - public void onMediaStoreChanged() { - for (MusicServiceEventListener listener : mMusicServiceEventListeners) { - if (listener != null) { - listener.onMediaStoreChanged(); - } - } - } - - @Override - public void onRepeatModeChanged() { - for (MusicServiceEventListener listener : mMusicServiceEventListeners) { - if (listener != null) { - listener.onRepeatModeChanged(); - } - } - } - - @Override - public void onShuffleModeChanged() { - for (MusicServiceEventListener listener : mMusicServiceEventListeners) { - if (listener != null) { - listener.onShuffleModeChanged(); - } - } - } - - @Override - protected void onHasPermissionsChanged(boolean hasPermissions) { - super.onHasPermissionsChanged(hasPermissions); - Intent intent = new Intent(MEDIA_STORE_CHANGED); - intent.putExtra("from_permissions_changed", true); // just in case we need to know this at some point - sendBroadcast(intent); - } - - @Nullable - @Override - protected String[] getPermissionsToRequest() { - return new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}; - } - - - private static final class MusicStateReceiver extends BroadcastReceiver { - - private final WeakReference reference; - - public MusicStateReceiver(final AbsMusicServiceActivity activity) { - reference = new WeakReference<>(activity); - } - - @Override - public void onReceive(final Context context, @NonNull final Intent intent) { - final String action = intent.getAction(); - AbsMusicServiceActivity activity = reference.get(); - if (activity != null && action != null) { - switch (action) { - case META_CHANGED: - activity.onPlayingMetaChanged(); - break; - case QUEUE_CHANGED: - activity.onQueueChanged(); - break; - case PLAY_STATE_CHANGED: - activity.onPlayStateChanged(); - break; - case REPEAT_MODE_CHANGED: - activity.onRepeatModeChanged(); - break; - case SHUFFLE_MODE_CHANGED: - activity.onShuffleModeChanged(); - break; - case MEDIA_STORE_CHANGED: - activity.onMediaStoreChanged(); - break; - } - } - } - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsMusicServiceActivity.kt b/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsMusicServiceActivity.kt new file mode 100644 index 00000000..086c9786 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsMusicServiceActivity.kt @@ -0,0 +1,168 @@ +package code.name.monkey.retromusic.ui.activities.base + +import android.Manifest +import android.content.* +import android.os.Bundle +import android.os.IBinder +import code.name.monkey.retromusic.Constants.MEDIA_STORE_CHANGED +import code.name.monkey.retromusic.Constants.META_CHANGED +import code.name.monkey.retromusic.Constants.PLAY_STATE_CHANGED +import code.name.monkey.retromusic.Constants.QUEUE_CHANGED +import code.name.monkey.retromusic.Constants.REPEAT_MODE_CHANGED +import code.name.monkey.retromusic.Constants.SHUFFLE_MODE_CHANGED +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.helper.MusicPlayerRemote +import code.name.monkey.retromusic.interfaces.MusicServiceEventListener +import java.lang.ref.WeakReference +import java.util.* + + +abstract class AbsMusicServiceActivity : AbsCastActivity(), MusicServiceEventListener { + + private val mMusicServiceEventListeners = ArrayList() + + private var serviceToken: MusicPlayerRemote.ServiceToken? = null + private var musicStateReceiver: MusicStateReceiver? = null + private var receiverRegistered: Boolean = false + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + serviceToken = MusicPlayerRemote.bindToService(this, object : ServiceConnection { + override fun onServiceConnected(name: ComponentName, service: IBinder) { + this@AbsMusicServiceActivity.onServiceConnected() + } + + override fun onServiceDisconnected(name: ComponentName) { + this@AbsMusicServiceActivity.onServiceDisconnected() + } + }) + + setPermissionDeniedMessage(getString(R.string.permission_external_storage_denied)); + } + + override fun onDestroy() { + super.onDestroy() + MusicPlayerRemote.unbindFromService(serviceToken) + if (receiverRegistered) { + unregisterReceiver(musicStateReceiver) + receiverRegistered = false + } + } + + fun addMusicServiceEventListener(listener: MusicServiceEventListener?) { + if (listener != null) { + mMusicServiceEventListeners.add(listener) + } + } + + fun removeMusicServiceEventListener(listener: MusicServiceEventListener?) { + if (listener != null) { + mMusicServiceEventListeners.remove(listener) + } + } + + override fun onServiceConnected() { + if (!receiverRegistered) { + musicStateReceiver = MusicStateReceiver(this) + + val filter = IntentFilter() + filter.addAction(PLAY_STATE_CHANGED) + filter.addAction(SHUFFLE_MODE_CHANGED) + filter.addAction(REPEAT_MODE_CHANGED) + filter.addAction(META_CHANGED) + filter.addAction(QUEUE_CHANGED) + filter.addAction(MEDIA_STORE_CHANGED) + + registerReceiver(musicStateReceiver, filter) + + receiverRegistered = true + } + + for (listener in mMusicServiceEventListeners) { + listener.onServiceConnected() + } + } + + override fun onServiceDisconnected() { + if (receiverRegistered) { + unregisterReceiver(musicStateReceiver) + receiverRegistered = false + } + + for (listener in mMusicServiceEventListeners) { + listener.onServiceDisconnected() + } + } + + override fun onPlayingMetaChanged() { + for (listener in mMusicServiceEventListeners) { + listener.onPlayingMetaChanged() + } + } + + override fun onQueueChanged() { + for (listener in mMusicServiceEventListeners) { + listener.onQueueChanged() + } + } + + override fun onPlayStateChanged() { + for (listener in mMusicServiceEventListeners) { + listener.onPlayStateChanged() + } + } + + override fun onMediaStoreChanged() { + for (listener in mMusicServiceEventListeners) { + listener.onMediaStoreChanged() + } + } + + override fun onRepeatModeChanged() { + for (listener in mMusicServiceEventListeners) { + listener.onRepeatModeChanged() + } + } + + override fun onShuffleModeChanged() { + for (listener in mMusicServiceEventListeners) { + listener.onShuffleModeChanged() + } + } + + override fun onHasPermissionsChanged(hasPermissions: Boolean) { + super.onHasPermissionsChanged(hasPermissions) + val intent = Intent(MEDIA_STORE_CHANGED) + intent.putExtra("from_permissions_changed", true) // just in case we need to know this at some point + sendBroadcast(intent) + } + + + override fun getPermissionsToRequest(): Array? { + return arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE) + } + + private class MusicStateReceiver(activity: AbsMusicServiceActivity) : BroadcastReceiver() { + + private val reference: WeakReference = WeakReference(activity) + + override fun onReceive(context: Context, intent: Intent) { + val action = intent.action + val activity = reference.get() + if (activity != null && action != null) { + when (action) { + META_CHANGED -> activity.onPlayingMetaChanged() + QUEUE_CHANGED -> activity.onQueueChanged() + PLAY_STATE_CHANGED -> activity.onPlayStateChanged() + REPEAT_MODE_CHANGED -> activity.onRepeatModeChanged() + SHUFFLE_MODE_CHANGED -> activity.onShuffleModeChanged() + MEDIA_STORE_CHANGED -> activity.onMediaStoreChanged() + } + } + } + } + + companion object { + val TAG = AbsMusicServiceActivity::class.java.simpleName + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsSlidingMusicPanelActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsSlidingMusicPanelActivity.java deleted file mode 100644 index 6dc4c5b2..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsSlidingMusicPanelActivity.java +++ /dev/null @@ -1,455 +0,0 @@ -package code.name.monkey.retromusic.ui.activities.base; - -import android.animation.ArgbEvaluator; -import android.animation.ValueAnimator; -import android.annotation.SuppressLint; -import android.graphics.Color; -import android.os.Build; -import android.os.Bundle; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewTreeObserver; -import android.view.WindowManager; -import android.view.animation.PathInterpolator; - -import com.google.android.gms.cast.framework.CastSession; -import com.google.android.material.bottomnavigation.BottomNavigationView; -import com.sothree.slidinguppanel.SlidingUpPanelLayout; -import com.sothree.slidinguppanel.SlidingUpPanelLayout.PanelState; - -import androidx.annotation.FloatRange; -import androidx.annotation.LayoutRes; -import androidx.coordinatorlayout.widget.CoordinatorLayout; -import androidx.fragment.app.Fragment; -import butterknife.BindView; -import butterknife.ButterKnife; -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.appthemehelper.util.ColorUtil; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.ui.fragments.MiniPlayerFragment; -import code.name.monkey.retromusic.ui.fragments.NowPlayingScreen; -import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerFragment; -import code.name.monkey.retromusic.ui.fragments.player.adaptive.AdaptiveFragment; -import code.name.monkey.retromusic.ui.fragments.player.blur.BlurPlayerFragment; -import code.name.monkey.retromusic.ui.fragments.player.card.CardFragment; -import code.name.monkey.retromusic.ui.fragments.player.cardblur.CardBlurFragment; -import code.name.monkey.retromusic.ui.fragments.player.color.ColorFragment; -import code.name.monkey.retromusic.ui.fragments.player.fit.FitFragment; -import code.name.monkey.retromusic.ui.fragments.player.flat.FlatPlayerFragment; -import code.name.monkey.retromusic.ui.fragments.player.full.FullPlayerFragment; -import code.name.monkey.retromusic.ui.fragments.player.hmm.HmmPlayerFragment; -import code.name.monkey.retromusic.ui.fragments.player.material.MaterialFragment; -import code.name.monkey.retromusic.ui.fragments.player.normal.PlayerFragment; -import code.name.monkey.retromusic.ui.fragments.player.plain.PlainPlayerFragment; -import code.name.monkey.retromusic.ui.fragments.player.simple.SimplePlayerFragment; -import code.name.monkey.retromusic.util.NavigationUtil; -import code.name.monkey.retromusic.util.PreferenceUtil; -import code.name.monkey.retromusic.util.ViewUtil; -import code.name.monkey.retromusic.views.BottomNavigationBarTinted; - -public abstract class AbsSlidingMusicPanelActivity extends AbsMusicServiceActivity implements - SlidingUpPanelLayout.PanelSlideListener, PlayerFragment.Callbacks { - - public static final String TAG = AbsSlidingMusicPanelActivity.class.getSimpleName(); - - @BindView(R.id.sliding_layout) - SlidingUpPanelLayout slidingUpPanelLayout; - - @BindView(R.id.bottom_navigation) - BottomNavigationBarTinted bottomNavigationView; - - @BindView(R.id.main_content) - CoordinatorLayout coordinatorLayout; - - private MiniPlayerFragment miniPlayerFragment; - private AbsPlayerFragment playerFragment; - private NowPlayingScreen currentNowPlayingScreen; - - private int navigationbarColor; - private int taskColor; - private boolean lightStatusbar; - private boolean lightNavigationBar; - private ValueAnimator navigationBarColorAnimator; - private ArgbEvaluator argbEvaluator = new ArgbEvaluator(); - - protected AbsSlidingMusicPanelActivity() { - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(createContentView()); - ButterKnife.bind(this); - choosFragmentForTheme(); - setupSlidingUpPanel(); - } - - public void setBottomBarVisibility(int gone) { - if (bottomNavigationView != null) { - bottomNavigationView.setVisibility(gone); - hideBottomBar(false); - } - } - - protected abstract View createContentView(); - - @Override - public void onServiceConnected() { - super.onServiceConnected(); - if (!MusicPlayerRemote.getPlayingQueue().isEmpty()) { - slidingUpPanelLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { - @Override - public void onGlobalLayout() { - slidingUpPanelLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this); - hideBottomBar(false); - } - }); - } // don't call hideBottomBar(true) here as it causes a bug with the SlidingUpPanelLayout - } - - @Override - public void onQueueChanged() { - super.onQueueChanged(); - hideBottomBar(MusicPlayerRemote.getPlayingQueue().isEmpty()); - } - - public void hideBottomBar(final boolean hide) { - int heightOfBar = getResources().getDimensionPixelSize(R.dimen.mini_player_height); - int heightOfBarWithTabs = getResources().getDimensionPixelSize(R.dimen.mini_player_height_expanded); - - if (hide) { - slidingUpPanelLayout.setPanelHeight(0); - collapsePanel(); - } else { - if (!MusicPlayerRemote.getPlayingQueue().isEmpty()) { - slidingUpPanelLayout.setPanelHeight(bottomNavigationView.getVisibility() == View.VISIBLE ? heightOfBarWithTabs : heightOfBar); - } - } - } - - private void checkDisplayCutout() { - WindowManager.LayoutParams attrs = getWindow().getAttributes(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - attrs.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; - } - - } - - protected View wrapSlidingMusicPanel(@LayoutRes int resId) { - @SuppressLint("InflateParams") - View slidingMusicPanelLayout = getLayoutInflater().inflate(R.layout.sliding_music_panel_layout, null); - ViewGroup contentContainer = slidingMusicPanelLayout.findViewById(R.id.content_container); - getLayoutInflater().inflate(resId, contentContainer); - return slidingMusicPanelLayout; - } - - @Override - public void onBackPressed() { - if (!handleBackPress()) - super.onBackPressed(); - } - - public boolean handleBackPress() { - if (slidingUpPanelLayout.getPanelHeight() != 0 && playerFragment.onBackPressed()) - return true; - if (getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED) { - collapsePanel(); - return true; - } - return false; - } - - @Override - protected View getSnackBarContainer() { - return findViewById(R.id.content_container); - } - - @Override - public void hideCastMiniController() { - super.hideCastMiniController(); - } - - @Override - public void showCastMiniController() { - super.showCastMiniController(); - - } - - @Override - public void onPlayingMetaChanged() { - super.onPlayingMetaChanged(); - CastSession castSession = getCastSession(); - if (castSession == null) { - return; - } - //MusicPlayerRemote.setZeroVolume(); - //CastHelper.startCasting(castSession, MusicPlayerRemote.getCurrentSong()); - } - - public void toggleBottomNavigationView(boolean toggle) { - bottomNavigationView.setVisibility(toggle ? View.GONE : View.VISIBLE); - } - - public BottomNavigationView getBottomNavigationView() { - return bottomNavigationView; - } - - public SlidingUpPanelLayout getSlidingUpPanelLayout() { - return slidingUpPanelLayout; - } - - public AbsPlayerFragment getPlayerFragment() { - return playerFragment; - } - - protected void setupSlidingUpPanel() { - slidingUpPanelLayout.getViewTreeObserver() - .addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { - @Override - public void onGlobalLayout() { - slidingUpPanelLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this); - - if (getPanelState() == PanelState.EXPANDED) { - onPanelSlide(slidingUpPanelLayout, 1); - onPanelExpanded(slidingUpPanelLayout); - } else if (getPanelState() == PanelState.COLLAPSED) { - onPanelCollapsed(slidingUpPanelLayout); - } else { - playerFragment.onHide(); - } - } - }); - - slidingUpPanelLayout.addPanelSlideListener(this); - - } - - public SlidingUpPanelLayout.PanelState getPanelState() { - return slidingUpPanelLayout == null ? null : slidingUpPanelLayout.getPanelState(); - } - - @Override - public void onPanelSlide(View panel, float slideOffset) { - bottomNavigationView.setTranslationY(slideOffset * 400); - setMiniPlayerAlphaProgress(slideOffset); - //if (navigationBarColorAnimator != null) navigationBarColorAnimator.cancel(); - //super.setNavigationbarColor((int) argbEvaluator.evaluate(slideOffset, navigationbarColor, Color.TRANSPARENT)); - } - - @Override - public void onPanelStateChanged(View panel, PanelState previousState, PanelState newState) { - switch (newState) { - case COLLAPSED: - onPanelCollapsed(panel); - break; - case EXPANDED: - onPanelExpanded(panel); - break; - case ANCHORED: - collapsePanel(); // this fixes a bug where the panel would get stuck for some reason - break; - } - } - - public void onPanelCollapsed(View panel) { - // restore values - super.setLightStatusbar(lightStatusbar); - super.setTaskDescriptionColor(taskColor); - super.setNavigationbarColor(navigationbarColor); - super.setLightNavigationBar(lightNavigationBar); - - - playerFragment.setMenuVisibility(false); - playerFragment.setUserVisibleHint(false); - playerFragment.onHide(); - } - - public void onPanelExpanded(View panel) { - int playerFragmentColor = playerFragment.getPaletteColor(); - super.setTaskDescriptionColor(playerFragmentColor); - - playerFragment.setMenuVisibility(true); - playerFragment.setUserVisibleHint(true); - playerFragment.onShow(); - onPaletteColorChanged(); - } - - private void setMiniPlayerAlphaProgress(@FloatRange(from = 0, to = 1) float progress) { - if (miniPlayerFragment.getView() == null) return; - float alpha = 1 - progress; - miniPlayerFragment.getView().setAlpha(alpha); - // necessary to make the views below clickable - miniPlayerFragment.getView().setVisibility(alpha == 0 ? View.GONE : View.VISIBLE); - } - - private void choosFragmentForTheme() { - currentNowPlayingScreen = PreferenceUtil.getInstance().getNowPlayingScreen(); - - Fragment fragment; // must implement AbsPlayerFragment - switch (currentNowPlayingScreen) { - case MATERIAL: - fragment = new MaterialFragment(); - break; - case BLUR: - fragment = new BlurPlayerFragment(); - break; - case FLAT: - fragment = new FlatPlayerFragment(); - break; - case PLAIN: - fragment = new PlainPlayerFragment(); - break; - case FULL: - fragment = new FullPlayerFragment(); - break; - case COLOR: - fragment = new ColorFragment(); - break; - case CARD: - fragment = new CardFragment(); - break; - case SIMPLE: - fragment = new SimplePlayerFragment(); - break; - case TINY: - fragment = new HmmPlayerFragment(); - break; - case BLUR_CARD: - fragment = new CardBlurFragment(); - break; - case ADAPTIVE: - fragment = new AdaptiveFragment(); - break; - case FIT: - fragment = new FitFragment(); - break; - case NORMAL: - default: - fragment = new PlayerFragment(); - break; - } - getSupportFragmentManager().beginTransaction().replace(R.id.player_fragment_container, fragment).commit(); - getSupportFragmentManager().executePendingTransactions(); - - playerFragment = (AbsPlayerFragment) getSupportFragmentManager().findFragmentById(R.id.player_fragment_container); - miniPlayerFragment = (MiniPlayerFragment) getSupportFragmentManager().findFragmentById(R.id.mini_player_fragment); - - //noinspection ConstantConditions - miniPlayerFragment.getView().setOnClickListener(v -> expandPanel()); - - } - - @Override - protected void onResume() { - super.onResume(); - if (currentNowPlayingScreen != PreferenceUtil.getInstance().getNowPlayingScreen()) { - postRecreate(); - } - } - - public void setAntiDragView(View antiDragView) { - //slidingUpPanelLayout.setAntiDragView(antiDragView); - } - - public void collapsePanel() { - slidingUpPanelLayout.setPanelState(PanelState.COLLAPSED); - } - - public void expandPanel() { - slidingUpPanelLayout.setPanelState(SlidingUpPanelLayout.PanelState.EXPANDED); - } - - @Override - public void onPaletteColorChanged() { - if (getPanelState() == PanelState.EXPANDED) { - int paletteColor = playerFragment.getPaletteColor(); - boolean isColorLight = ColorUtil.isColorLight(paletteColor); - super.setTaskDescriptionColor(paletteColor); - if ((currentNowPlayingScreen == NowPlayingScreen.FLAT || currentNowPlayingScreen == NowPlayingScreen.NORMAL) && PreferenceUtil.getInstance().getAdaptiveColor()) { - super.setLightNavigationBar(true); - super.setLightStatusbar(isColorLight); - } else if (currentNowPlayingScreen == NowPlayingScreen.COLOR) { - super.setLightStatusbar(isColorLight); - super.setLightNavigationBar(isColorLight); - super.setNavigationbarColor(paletteColor); - } else if (currentNowPlayingScreen == NowPlayingScreen.BLUR || currentNowPlayingScreen == NowPlayingScreen.BLUR_CARD) { - super.setLightStatusbar(false); - super.setLightNavigationBar(true); - } else if (currentNowPlayingScreen == NowPlayingScreen.CARD || currentNowPlayingScreen == NowPlayingScreen.FULL) { - super.setLightStatusbar(false); - super.setLightNavigationBar(ColorUtil.isColorLight(ThemeStore.primaryColor(this))); - } else if (currentNowPlayingScreen == NowPlayingScreen.FIT) { - super.setNavigationbarColor(ThemeStore.primaryColor(this)); - super.setLightNavigationBar(ColorUtil.isColorLight(ThemeStore.primaryColor(this))); - super.setLightStatusbar(false); - } else { - boolean isTheme = isOneOfTheseThemes() && ColorUtil.isColorLight(ThemeStore.primaryColor(this)); - super.setStatusbarColor(Color.TRANSPARENT); - super.setLightStatusbar(isTheme); - super.setLightNavigationBar(isTheme); - } - } - } - - private boolean isOneOfTheseThemes() { - return currentNowPlayingScreen == NowPlayingScreen.FLAT - || currentNowPlayingScreen == NowPlayingScreen.PLAIN - || currentNowPlayingScreen == NowPlayingScreen.SIMPLE - || currentNowPlayingScreen == NowPlayingScreen.NORMAL - || currentNowPlayingScreen == NowPlayingScreen.ADAPTIVE - || currentNowPlayingScreen == NowPlayingScreen.TINY - || currentNowPlayingScreen == NowPlayingScreen.MATERIAL; - } - - @Override - public void setLightStatusbar(boolean enabled) { - lightStatusbar = enabled; - if (getPanelState() == PanelState.COLLAPSED) { - super.setLightStatusbar(enabled); - } - } - - @Override - public void setLightNavigationBar(boolean enabled) { - lightNavigationBar = enabled; - if (getPanelState() == PanelState.COLLAPSED) { - super.setLightNavigationBar(enabled); - } - } - - @Override - public void setNavigationbarColor(int color) { - navigationbarColor = color; - if (getPanelState() == PanelState.COLLAPSED) { - if (navigationBarColorAnimator != null) navigationBarColorAnimator.cancel(); - super.setNavigationbarColor(color); - } - } - - @Override - public void setTaskDescriptionColor(int color) { - taskColor = color; - if (getPanelState() == PanelState.COLLAPSED) { - super.setTaskDescriptionColor(color); - } - } - - private void animateNavigationBarColor(int color) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - if (navigationBarColorAnimator != null) navigationBarColorAnimator.cancel(); - navigationBarColorAnimator = ValueAnimator - .ofArgb(getWindow().getNavigationBarColor(), color) - .setDuration(ViewUtil.RETRO_MUSIC_ANIM_TIME); - navigationBarColorAnimator.setInterpolator(new PathInterpolator(0.4f, 0f, 1f, 1f)); - navigationBarColorAnimator.addUpdateListener(animation -> AbsSlidingMusicPanelActivity.super.setNavigationbarColor((Integer) animation.getAnimatedValue())); - navigationBarColorAnimator.start(); - } - } - - @Override - public void onDestroy() { - super.onDestroy(); - if (navigationBarColorAnimator != null) navigationBarColorAnimator.cancel(); // just in case - } -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsSlidingMusicPanelActivity.kt b/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsSlidingMusicPanelActivity.kt new file mode 100644 index 00000000..d41255e7 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsSlidingMusicPanelActivity.kt @@ -0,0 +1,301 @@ +package code.name.monkey.retromusic.ui.activities.base + +import android.animation.ArgbEvaluator +import android.animation.ValueAnimator +import android.annotation.SuppressLint +import android.graphics.Color +import android.os.Bundle +import android.view.View +import android.view.ViewGroup +import android.view.ViewTreeObserver +import androidx.annotation.FloatRange +import androidx.annotation.LayoutRes +import androidx.fragment.app.Fragment +import butterknife.ButterKnife +import code.name.monkey.appthemehelper.ThemeStore +import code.name.monkey.appthemehelper.util.ColorUtil +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.helper.MusicPlayerRemote +import code.name.monkey.retromusic.ui.fragments.MiniPlayerFragment +import code.name.monkey.retromusic.ui.fragments.NowPlayingScreen +import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerFragment +import code.name.monkey.retromusic.ui.fragments.player.adaptive.AdaptiveFragment +import code.name.monkey.retromusic.ui.fragments.player.blur.BlurPlayerFragment +import code.name.monkey.retromusic.util.PreferenceUtil +import code.name.monkey.retromusic.views.BottomNavigationBarTinted +import com.google.android.material.bottomnavigation.BottomNavigationView +import com.sothree.slidinguppanel.SlidingUpPanelLayout +import com.sothree.slidinguppanel.SlidingUpPanelLayout.PanelState + +abstract class AbsSlidingMusicPanelActivity protected constructor() : AbsMusicServiceActivity(), SlidingUpPanelLayout.PanelSlideListener, AbsPlayerFragment.Callbacks { + + + lateinit var slidingUpPanelLayout: SlidingUpPanelLayout + private lateinit var bottomNavigationView: BottomNavigationBarTinted + + private var miniPlayerFragment: MiniPlayerFragment? = null + var playerFragment: AbsPlayerFragment? = null + private var currentNowPlayingScreen: NowPlayingScreen? = null + + private var navigationbarColor: Int = 0 + private var taskColor: Int = 0 + private var lightStatusbar: Boolean = false + private var lightNavigationBar: Boolean = false + private var navigationBarColorAnimator: ValueAnimator? = null + private val argbEvaluator = ArgbEvaluator() + + val panelState: SlidingUpPanelLayout.PanelState? + get() = slidingUpPanelLayout.panelState + + private val isOneOfTheseThemes: Boolean + get() = (currentNowPlayingScreen == NowPlayingScreen.ADAPTIVE) + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(createContentView()) + ButterKnife.bind(this) + + slidingUpPanelLayout = findViewById(R.id.sliding_layout); + bottomNavigationView = findViewById(R.id.bottom_navigation); + + choosFragmentForTheme() + setupSlidingUpPanel() + } + + fun setBottomBarVisibility(gone: Int) { + bottomNavigationView.visibility = gone + hideBottomBar(false) + } + + protected abstract fun createContentView(): View + + override fun onServiceConnected() { + super.onServiceConnected() + if (!MusicPlayerRemote.playingQueue.isEmpty()) { + slidingUpPanelLayout.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener { + override fun onGlobalLayout() { + slidingUpPanelLayout.viewTreeObserver.removeOnGlobalLayoutListener(this) + hideBottomBar(false) + } + }) + } // don't call hideBottomBar(true) here as it causes a bug with the SlidingUpPanelLayout + } + + override fun onQueueChanged() { + super.onQueueChanged() + hideBottomBar(MusicPlayerRemote.playingQueue.isEmpty()) + } + + fun hideBottomBar(hide: Boolean) { + val heightOfBar = resources.getDimensionPixelSize(R.dimen.mini_player_height) + val heightOfBarWithTabs = resources.getDimensionPixelSize(R.dimen.mini_player_height_expanded) + + if (hide) { + slidingUpPanelLayout.panelHeight = 0 + collapsePanel() + } else { + if (!MusicPlayerRemote.playingQueue.isEmpty()) { + slidingUpPanelLayout.panelHeight = if (bottomNavigationView.visibility == View.VISIBLE) heightOfBarWithTabs else heightOfBar + } + } + } + + protected fun wrapSlidingMusicPanel(@LayoutRes resId: Int): View { + @SuppressLint("InflateParams") + val slidingMusicPanelLayout = layoutInflater.inflate(R.layout.sliding_music_panel_layout, null) + val contentContainer = slidingMusicPanelLayout.findViewById(R.id.content_container) + layoutInflater.inflate(resId, contentContainer) + return slidingMusicPanelLayout + } + + override fun onBackPressed() { + if (!handleBackPress()) + super.onBackPressed() + } + + open fun handleBackPress(): Boolean { + if (slidingUpPanelLayout.panelHeight != 0 && playerFragment!!.onBackPressed()) + return true + if (panelState == SlidingUpPanelLayout.PanelState.EXPANDED) { + collapsePanel() + return true + } + return false + } + + override fun onPlayingMetaChanged() { + super.onPlayingMetaChanged() + castSession ?: return + //MusicPlayerRemote.setZeroVolume(); + //CastHelper.startCasting(castSession, MusicPlayerRemote.getCurrentSong()); + } + + fun toggleBottomNavigationView(toggle: Boolean) { + bottomNavigationView.visibility = if (toggle) View.GONE else View.VISIBLE + } + + fun getBottomNavigationView(): BottomNavigationView? { + return bottomNavigationView + } + + private fun setupSlidingUpPanel() { + slidingUpPanelLayout.viewTreeObserver + .addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener { + override fun onGlobalLayout() { + slidingUpPanelLayout.viewTreeObserver.removeOnGlobalLayoutListener(this) + + if (panelState == PanelState.EXPANDED) { + onPanelSlide(slidingUpPanelLayout, 1f) + onPanelExpanded() + } else if (panelState == PanelState.COLLAPSED) { + onPanelCollapsed() + } else { + playerFragment!!.onHide() + } + } + }) + + slidingUpPanelLayout.addPanelSlideListener(this) + + } + + override fun onPanelSlide(panel: View?, slideOffset: Float) { + bottomNavigationView.translationY = slideOffset * 400 + setMiniPlayerAlphaProgress(slideOffset) + //if (navigationBarColorAnimator != null) navigationBarColorAnimator.cancel(); + //super.setNavigationbarColor((int) argbEvaluator.evaluate(slideOffset, navigationbarColor, Color.TRANSPARENT)); + } + + override fun onPanelStateChanged(panel: View, previousState: PanelState, newState: PanelState) { + when (newState) { + SlidingUpPanelLayout.PanelState.COLLAPSED -> onPanelCollapsed() + SlidingUpPanelLayout.PanelState.EXPANDED -> onPanelExpanded() + SlidingUpPanelLayout.PanelState.ANCHORED -> collapsePanel() // this fixes a bug where the panel would get stuck for some reason + else -> { + } + } + } + + fun onPanelCollapsed() { + // restore values + super.setLightStatusbar(lightStatusbar) + super.setTaskDescriptionColor(taskColor) + super.setNavigationbarColor(navigationbarColor) + super.setLightNavigationBar(lightNavigationBar) + + + playerFragment!!.setMenuVisibility(false) + playerFragment!!.userVisibleHint = false + playerFragment!!.onHide() + } + + fun onPanelExpanded() { + val playerFragmentColor = playerFragment!!.paletteColor + super.setTaskDescriptionColor(playerFragmentColor) + + playerFragment!!.setMenuVisibility(true) + playerFragment!!.userVisibleHint = true + playerFragment!!.onShow() + onPaletteColorChanged() + } + + private fun setMiniPlayerAlphaProgress(@FloatRange(from = 0.0, to = 1.0) progress: Float) { + if (miniPlayerFragment!!.view == null) return + val alpha = 1 - progress + miniPlayerFragment!!.view!!.alpha = alpha + // necessary to make the views below clickable + miniPlayerFragment!!.view!!.visibility = if (alpha == 0f) View.GONE else View.VISIBLE + } + + private fun choosFragmentForTheme() { + currentNowPlayingScreen = PreferenceUtil.getInstance().nowPlayingScreen + + val fragment: Fragment // must implement AbsPlayerFragment + when (currentNowPlayingScreen) { + NowPlayingScreen.BLUR -> fragment = BlurPlayerFragment() + NowPlayingScreen.ADAPTIVE -> fragment = AdaptiveFragment() + else -> fragment = AdaptiveFragment() + } + supportFragmentManager.beginTransaction().replace(R.id.player_fragment_container, fragment).commit() + supportFragmentManager.executePendingTransactions() + + playerFragment = supportFragmentManager.findFragmentById(R.id.player_fragment_container) as AbsPlayerFragment? + miniPlayerFragment = supportFragmentManager.findFragmentById(R.id.mini_player_fragment) as MiniPlayerFragment? + + + miniPlayerFragment!!.view!!.setOnClickListener { expandPanel() } + + } + + override fun onResume() { + super.onResume() + if (currentNowPlayingScreen != PreferenceUtil.getInstance().nowPlayingScreen) { + postRecreate() + } + } + + private fun collapsePanel() { + slidingUpPanelLayout.panelState = PanelState.COLLAPSED + } + + private fun expandPanel() { + slidingUpPanelLayout.panelState = PanelState.EXPANDED + } + + + override fun onPaletteColorChanged() { + if (panelState == PanelState.EXPANDED) { + val paletteColor = playerFragment!!.paletteColor + ColorUtil.isColorLight(paletteColor) + super.setTaskDescriptionColor(paletteColor) + if (currentNowPlayingScreen == NowPlayingScreen.BLUR) { + super.setLightStatusbar(false) + super.setLightNavigationBar(true) + } else { + val isTheme = isOneOfTheseThemes && ColorUtil.isColorLight(ThemeStore.primaryColor(this)) + super.setStatusbarColor(Color.TRANSPARENT) + super.setLightStatusbar(isTheme) + super.setLightNavigationBar(isTheme) + } + } + } + + override fun setLightStatusbar(enabled: Boolean) { + lightStatusbar = enabled + if (panelState == PanelState.COLLAPSED) { + super.setLightStatusbar(enabled) + } + } + + override fun setLightNavigationBar(enabled: Boolean) { + lightNavigationBar = enabled + if (panelState == PanelState.COLLAPSED) { + super.setLightNavigationBar(enabled) + } + } + + override fun setNavigationbarColor(color: Int) { + navigationbarColor = color + if (panelState == PanelState.COLLAPSED) { + if (navigationBarColorAnimator != null) navigationBarColorAnimator!!.cancel() + super.setNavigationbarColor(color) + } + } + + override fun setTaskDescriptionColor(color: Int) { + taskColor = color + if (panelState == PanelState.COLLAPSED) { + super.setTaskDescriptionColor(color) + } + } + + override fun onDestroy() { + super.onDestroy() + if (navigationBarColorAnimator != null) navigationBarColorAnimator!!.cancel() // just in case + } + + companion object { + + val TAG = AbsSlidingMusicPanelActivity::class.java.simpleName + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsThemeActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsThemeActivity.java deleted file mode 100644 index c0231627..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsThemeActivity.java +++ /dev/null @@ -1,216 +0,0 @@ -package code.name.monkey.retromusic.ui.activities.base; - -import android.graphics.Color; -import android.graphics.drawable.Drawable; -import android.os.Build; -import android.os.Bundle; -import android.os.Handler; -import android.view.KeyEvent; -import android.view.View; -import android.view.WindowManager; - -import androidx.annotation.ColorInt; -import androidx.core.content.ContextCompat; -import code.name.monkey.appthemehelper.ATH; -import code.name.monkey.appthemehelper.ATHActivity; -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.appthemehelper.util.ATHUtil; -import code.name.monkey.appthemehelper.util.ColorUtil; -import code.name.monkey.appthemehelper.util.MaterialDialogsUtil; -import code.name.monkey.appthemehelper.util.TintHelper; -import code.name.monkey.appthemehelper.util.VersionUtils; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.util.PreferenceUtil; -import code.name.monkey.retromusic.util.RetroUtil; - -public abstract class AbsThemeActivity extends ATHActivity implements Runnable { - - private Handler handler = new Handler(); - - @Override - protected void onCreate(Bundle savedInstanceState) { - setTheme(PreferenceUtil.getInstance().getGeneralTheme()); - hideStatusBar(); - super.onCreate(savedInstanceState); - MaterialDialogsUtil.updateMaterialDialogsThemeSingleton(this); - - changeBackgroundShape(); - setImmersiveFullscreen(); - registerSystemUiVisibility(); - toggleScreenOn(); - } - - private void toggleScreenOn() { - if (PreferenceUtil.getInstance().isScreenOnEnabled()) { - getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - } else { - getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - } - } - - @Override - public void onWindowFocusChanged(boolean hasFocus) { - super.onWindowFocusChanged(hasFocus); - if (hasFocus) { - hideStatusBar(); - handler.removeCallbacks(this); - handler.postDelayed(this, 300); - } else { - handler.removeCallbacks(this); - } - } - - public void hideStatusBar() { - hideStatusBar(PreferenceUtil.getInstance().getFullScreenMode()); - } - - private void hideStatusBar(boolean fullscreen) { - final View statusBar = getWindow().getDecorView().getRootView().findViewById(R.id.status_bar); - if (statusBar != null) { - statusBar.setVisibility(fullscreen ? View.GONE : View.VISIBLE); - } - } - - - private void changeBackgroundShape() { - Drawable background = PreferenceUtil.getInstance().isRoundCorners() ? - ContextCompat.getDrawable(this, R.drawable.round_window) - : ContextCompat.getDrawable(this, R.drawable.square_window); - background = TintHelper.createTintedDrawable(background, ThemeStore.primaryColor(this)); - getWindow().setBackgroundDrawable(background); - } - - public void setDrawUnderStatusBar() { - if (VersionUtils.hasLollipop()) { - RetroUtil.setAllowDrawUnderStatusBar(getWindow()); - } else if (VersionUtils.hasKitKat()) { - RetroUtil.setStatusBarTranslucent(getWindow()); - } - } - - public void setDrawUnderNavigationBar() { - RetroUtil.setAllowDrawUnderNavigationBar(getWindow()); - } - - /** - * This will set the color of the view with the id "status_bar" on KitKat and Lollipop. On - * Lollipop if no such view is found it will set the statusbar color using the native method. - * - * @param color the new statusbar color (will be shifted down on Lollipop and above) - */ - public void setStatusbarColor(int color) { - if (VersionUtils.hasKitKat()) { - final View statusBar = getWindow().getDecorView().getRootView().findViewById(R.id.status_bar); - if (statusBar != null) { - if (VersionUtils.hasMarshmallow()) { - getWindow().setStatusBarColor(color); - } else if (VersionUtils.hasLollipop()) { - statusBar.setBackgroundColor(ColorUtil.darkenColor(color)); - } else { - statusBar.setBackgroundColor(color); - } - } else if (Build.VERSION.SDK_INT >= 21) { - getWindow().setStatusBarColor(ColorUtil.darkenColor(color)); - } - } - setLightStatusbarAuto(color); - } - - public void setStatusbarColorAuto() { - // we don't want to use statusbar color because we are doing the color darkening on our own to support KitKat - setStatusbarColor(ThemeStore.primaryColor(this)); - } - - public void setTaskDescriptionColor(@ColorInt int color) { - ATH.setTaskDescriptionColor(this, color); - } - - public void setTaskDescriptionColorAuto() { - setTaskDescriptionColor(ThemeStore.primaryColor(this)); - } - - public void setNavigationbarColor(int color) { - if (ThemeStore.coloredNavigationBar(this)) { - ATH.setNavigationbarColor(this, color); - } else { - ATH.setNavigationbarColor(this, Color.BLACK); - } - } - - public void setNavigationbarColorAuto() { - setNavigationbarColor(ThemeStore.navigationBarColor(this)); - } - - public void setLightStatusbar(boolean enabled) { - ATH.setLightStatusbar(this, enabled); - } - - public void setLightStatusbarAuto(int bgColor) { - setLightStatusbar(ColorUtil.isColorLight(bgColor)); - } - - public void setLightNavigationBar(boolean enabled) { - if (!ATHUtil.isWindowBackgroundDark(this) && ThemeStore.coloredNavigationBar(this)) { - ATH.setLightNavigationbar(this, enabled); - } - } - - private void registerSystemUiVisibility() { - final View decorView = getWindow().getDecorView(); - decorView.setOnSystemUiVisibilityChangeListener(visibility -> { - if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) { - setImmersiveFullscreen(); - } - }); - } - - private void unregisterSystemUiVisibility() { - final View decorView = getWindow().getDecorView(); - decorView.setOnSystemUiVisibilityChangeListener(null); - } - - public void setImmersiveFullscreen() { - int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE - | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_FULLSCREEN - | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; - if (PreferenceUtil.getInstance().getFullScreenMode()) { - getWindow().getDecorView().setSystemUiVisibility(flags); - } - } - - public void exitFullscreen() { - getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); - } - - @Override - public void run() { - setImmersiveFullscreen(); - } - - @Override - protected void onStop() { - handler.removeCallbacks(this); - super.onStop(); - } - - @Override - public void onDestroy() { - super.onDestroy(); - unregisterSystemUiVisibility(); - exitFullscreen(); - } - - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - if ((keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP)) { - handler.removeCallbacks(this); - handler.postDelayed(this, 500); - } - return super.onKeyDown(keyCode, event); - - } -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsThemeActivity.kt b/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsThemeActivity.kt new file mode 100644 index 00000000..a88a03e6 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsThemeActivity.kt @@ -0,0 +1,206 @@ +package code.name.monkey.retromusic.ui.activities.base + +import android.graphics.Color +import android.graphics.drawable.Drawable +import android.os.Build +import android.os.Bundle +import android.os.Handler +import android.view.KeyEvent +import android.view.View +import android.view.WindowManager +import androidx.annotation.ColorInt +import androidx.core.content.ContextCompat +import code.name.monkey.appthemehelper.ATH +import code.name.monkey.appthemehelper.ATHActivity +import code.name.monkey.appthemehelper.ThemeStore +import code.name.monkey.appthemehelper.util.* +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.util.PreferenceUtil +import code.name.monkey.retromusic.util.RetroUtil + +abstract class AbsThemeActivity : ATHActivity(), Runnable { + + private val handler = Handler() + + override fun onCreate(savedInstanceState: Bundle?) { + setTheme(PreferenceUtil.getInstance().generalTheme) + hideStatusBar() + super.onCreate(savedInstanceState) + MaterialDialogsUtil.updateMaterialDialogsThemeSingleton(this) + + changeBackgroundShape() + setImmersiveFullscreen() + registerSystemUiVisibility() + toggleScreenOn() + } + + private fun toggleScreenOn() { + if (PreferenceUtil.getInstance().isScreenOnEnabled) { + window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + } else { + window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + } + } + + override fun onWindowFocusChanged(hasFocus: Boolean) { + super.onWindowFocusChanged(hasFocus) + if (hasFocus) { + hideStatusBar() + handler.removeCallbacks(this) + handler.postDelayed(this, 300) + } else { + handler.removeCallbacks(this) + } + } + + fun hideStatusBar() { + hideStatusBar(PreferenceUtil.getInstance().fullScreenMode) + } + + private fun hideStatusBar(fullscreen: Boolean) { + val statusBar = window.decorView.rootView.findViewById(R.id.status_bar) + if (statusBar != null) { + statusBar.visibility = if (fullscreen) View.GONE else View.VISIBLE + } + } + + + private fun changeBackgroundShape() { + var background: Drawable? = if (PreferenceUtil.getInstance().isRoundCorners) + ContextCompat.getDrawable(this, R.drawable.round_window) + else + ContextCompat.getDrawable(this, R.drawable.square_window) + background = TintHelper.createTintedDrawable(background, ThemeStore.primaryColor(this)) + window.setBackgroundDrawable(background) + } + + fun setDrawUnderStatusBar() { + if (VersionUtils.hasLollipop()) { + RetroUtil.setAllowDrawUnderStatusBar(window) + } else if (VersionUtils.hasKitKat()) { + RetroUtil.setStatusBarTranslucent(window) + } + } + + fun setDrawUnderNavigationBar() { + RetroUtil.setAllowDrawUnderNavigationBar(window) + } + + /** + * This will set the color of the view with the id "status_bar" on KitKat and Lollipop. On + * Lollipop if no such view is found it will set the statusbar color using the native method. + * + * @param color the new statusbar color (will be shifted down on Lollipop and above) + */ + fun setStatusbarColor(color: Int) { + if (VersionUtils.hasKitKat()) { + val statusBar = window.decorView.rootView.findViewById(R.id.status_bar) + if (statusBar != null) { + if (VersionUtils.hasMarshmallow()) { + window.statusBarColor = color + } else if (VersionUtils.hasLollipop()) { + statusBar.setBackgroundColor(ColorUtil.darkenColor(color)) + } else { + statusBar.setBackgroundColor(color) + } + } else if (Build.VERSION.SDK_INT >= 21) { + window.statusBarColor = ColorUtil.darkenColor(color) + } + } + setLightStatusbarAuto(color) + } + + fun setStatusbarColorAuto() { + // we don't want to use statusbar color because we are doing the color darkening on our own to support KitKat + setStatusbarColor(ThemeStore.primaryColor(this)) + } + + open fun setTaskDescriptionColor(@ColorInt color: Int) { + ATH.setTaskDescriptionColor(this, color) + } + + fun setTaskDescriptionColorAuto() { + setTaskDescriptionColor(ThemeStore.primaryColor(this)) + } + + open fun setNavigationbarColor(color: Int) { + if (ThemeStore.coloredNavigationBar(this)) { + ATH.setNavigationbarColor(this, color) + } else { + ATH.setNavigationbarColor(this, Color.BLACK) + } + } + + fun setNavigationbarColorAuto() { + setNavigationbarColor(ThemeStore.navigationBarColor(this)) + } + + open fun setLightStatusbar(enabled: Boolean) { + ATH.setLightStatusbar(this, enabled) + } + + fun setLightStatusbarAuto(bgColor: Int) { + setLightStatusbar(ColorUtil.isColorLight(bgColor)) + } + + open fun setLightNavigationBar(enabled: Boolean) { + if (!ATHUtil.isWindowBackgroundDark(this) && ThemeStore.coloredNavigationBar(this)) { + ATH.setLightNavigationbar(this, enabled) + } + } + + private fun registerSystemUiVisibility() { + val decorView = window.decorView + decorView.setOnSystemUiVisibilityChangeListener { visibility -> + if (visibility and View.SYSTEM_UI_FLAG_FULLSCREEN == 0) { + setImmersiveFullscreen() + } + } + } + + private fun unregisterSystemUiVisibility() { + val decorView = window.decorView + decorView.setOnSystemUiVisibilityChangeListener(null) + } + + private fun setImmersiveFullscreen() { + val flags = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE + or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + or View.SYSTEM_UI_FLAG_FULLSCREEN + or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) + if (PreferenceUtil.getInstance().fullScreenMode) { + window.decorView.systemUiVisibility = flags + } + } + + private fun exitFullscreen() { + window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE + } + + override fun run() { + setImmersiveFullscreen() + } + + override fun onStop() { + handler.removeCallbacks(this) + super.onStop() + } + + public override fun onDestroy() { + super.onDestroy() + unregisterSystemUiVisibility() + exitFullscreen() + } + + + override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean { + if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP) { + handler.removeCallbacks(this) + handler.postDelayed(this, 500) + } + return super.onKeyDown(keyCode, event) + + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/tageditor/AlbumTagEditorActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/tageditor/AlbumTagEditorActivity.java index a909cf2b..326db21e 100755 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/tageditor/AlbumTagEditorActivity.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/tageditor/AlbumTagEditorActivity.java @@ -245,10 +245,10 @@ public class AlbumTagEditorActivity extends AbsTagEditorActivity implements Text @NonNull @Override protected List getSongPaths() { - ArrayList songs = AlbumLoader.getAlbum(this, getId()).blockingFirst().songs; + ArrayList songs = AlbumLoader.Companion.getAlbum(this, getId()).blockingFirst().getSongs(); ArrayList paths = new ArrayList<>(songs.size()); for (Song song : songs) { - paths.add(song.data); + paths.add(song.getData()); } return paths; } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/tageditor/SongTagEditorActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/tageditor/SongTagEditorActivity.java index 89204692..4672b62e 100755 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/tageditor/SongTagEditorActivity.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/tageditor/SongTagEditorActivity.java @@ -147,7 +147,7 @@ public class SongTagEditorActivity extends AbsTagEditorActivity implements TextW @Override protected List getSongPaths() { ArrayList paths = new ArrayList<>(1); - paths.add(SongLoader.Companion.getSong(this, getId()).blockingFirst().data); + paths.add(SongLoader.INSTANCE.getSong(this, getId()).blockingFirst().getData()); return paths; } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/CollageSongAdapter.java b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/CollageSongAdapter.java index 2c1f2144..4e955f2c 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/CollageSongAdapter.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/CollageSongAdapter.java @@ -84,7 +84,7 @@ public class CollageSongAdapter extends RecyclerView.Adapter { - MusicPlayerRemote.openQueue(dataSet, 0, true); + MusicPlayerRemote.INSTANCE.openQueue(dataSet, 0, true); }); view.setBackgroundColor(color); view.setTextColor(MaterialValueHelper.getPrimaryTextColor(context, ColorUtil.isColorLight(color))); @@ -96,7 +96,7 @@ public class CollageSongAdapter extends RecyclerView.Adapter { - MusicPlayerRemote.playNext(dataSet.get(startPosition)); + MusicPlayerRemote.INSTANCE.playNext(dataSet.get(startPosition)); }); } } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/ContributorAdapter.java b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/ContributorAdapter.java deleted file mode 100644 index 6190c77e..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/ContributorAdapter.java +++ /dev/null @@ -1,71 +0,0 @@ -package code.name.monkey.retromusic.ui.adapter; - -import android.app.Activity; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import java.util.ArrayList; -import java.util.List; - -import androidx.annotation.NonNull; -import androidx.recyclerview.widget.RecyclerView; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.model.Contributor; -import code.name.monkey.retromusic.ui.adapter.base.MediaEntryViewHolder; -import code.name.monkey.retromusic.views.NetworkImageView; - -import static code.name.monkey.retromusic.util.RetroUtil.openUrl; - -public class ContributorAdapter extends RecyclerView.Adapter { - private List contributors = new ArrayList<>(); - - public ContributorAdapter(List contributors) { - this.contributors = contributors; - } - - public ContributorAdapter() { - } - - @NonNull - @Override - public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_contributor, parent, false)); - } - - @Override - public void onBindViewHolder(@NonNull ViewHolder holder, int position) { - Contributor contributor = contributors.get(position); - holder.bindData(contributor); - } - - @Override - public int getItemCount() { - return contributors.size(); - } - - public class ViewHolder extends MediaEntryViewHolder { - - public ViewHolder(View itemView) { - super(itemView); - } - - void bindData(Contributor contributor) { - if (title != null) { - title.setText(contributor.getName()); - } - if (text != null) { - text.setText(contributor.getSummary()); - } - if (image instanceof NetworkImageView) { - ((NetworkImageView) image).setImageUrl(contributor.getProfileImage()); - } - } - - @Override - public void onClick(View v) { - super.onClick(v); - openUrl((Activity) v.getContext(), contributors.get(getAdapterPosition()).getLink()); - } - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/ContributorAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/ContributorAdapter.kt new file mode 100644 index 00000000..b362892d --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/ContributorAdapter.kt @@ -0,0 +1,48 @@ +package code.name.monkey.retromusic.ui.adapter + +import android.app.Activity +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.model.Contributor +import code.name.monkey.retromusic.ui.adapter.base.MediaEntryViewHolder +import code.name.monkey.retromusic.util.RetroUtil.openUrl +import code.name.monkey.retromusic.views.NetworkImageView + +class ContributorAdapter(private var contributors: ArrayList) : RecyclerView.Adapter() { + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_contributor, parent, false)) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val contributor = contributors[position] + holder.bindData(contributor) + } + + override fun getItemCount(): Int { + return contributors.size + } + + inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) { + + internal fun bindData(contributor: Contributor) { + if (title != null) { + title!!.text = contributor.name + } + if (text != null) { + text!!.text = contributor.summary + } + if (image is NetworkImageView) { + (image as NetworkImageView).setImageUrl(contributor.profileImage) + } + } + + override fun onClick(v: View?) { + super.onClick(v) + openUrl(v!!.context as Activity, contributors[adapterPosition].link) + } + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/GenreAdapter.java b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/GenreAdapter.java deleted file mode 100644 index 1b9ac20e..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/GenreAdapter.java +++ /dev/null @@ -1,87 +0,0 @@ -package code.name.monkey.retromusic.ui.adapter; - -import android.app.Activity; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import java.util.ArrayList; -import java.util.Locale; - -import androidx.annotation.NonNull; -import androidx.recyclerview.widget.RecyclerView; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.model.Genre; -import code.name.monkey.retromusic.ui.adapter.base.MediaEntryViewHolder; -import code.name.monkey.retromusic.util.NavigationUtil; - -/** - * @author Hemanth S (h4h13). - */ - -public class GenreAdapter extends RecyclerView.Adapter { - private ArrayList mGenres = new ArrayList<>(); - private Activity mActivity; - private int mItemLayoutRes; - - public GenreAdapter(@NonNull Activity activity, ArrayList dataSet, int itemLayoutRes) { - mActivity = activity; - mGenres = dataSet; - mItemLayoutRes = itemLayoutRes; - } - - public ArrayList getDataSet() { - return mGenres; - } - - @NonNull - @Override - public GenreAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - return new ViewHolder(LayoutInflater.from(mActivity).inflate(mItemLayoutRes, parent, false)); - } - - @Override - public void onBindViewHolder(@NonNull GenreAdapter.ViewHolder holder, int position) { - Genre genre = mGenres.get(position); - if (holder.title != null) { - holder.title.setText(genre.name); - } - if (holder.text != null) { - holder.text.setText(String.format(Locale.getDefault(), "%d %s", genre.songCount, genre.songCount > 1 ? - mActivity.getString(R.string.songs) : - mActivity.getString(R.string.song))); - } - - if (holder.separator != null) { - holder.separator.setVisibility(View.VISIBLE); - } - } - - @Override - public int getItemCount() { - return mGenres.size(); - } - - public void swapDataSet(ArrayList list) { - mGenres = list; - notifyDataSetChanged(); - } - - public class ViewHolder extends MediaEntryViewHolder { - public ViewHolder(View itemView) { - super(itemView); - if (menu != null) { - menu.setVisibility(View.GONE); - } - assert imageContainer != null; - imageContainer.setVisibility(View.GONE); - } - - @Override - public void onClick(View v) { - super.onClick(v); - Genre genre = mGenres.get(getAdapterPosition()); - NavigationUtil.goToGenre(mActivity, genre); - } - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/GenreAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/GenreAdapter.kt new file mode 100644 index 00000000..d9f11dd9 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/GenreAdapter.kt @@ -0,0 +1,73 @@ +package code.name.monkey.retromusic.ui.adapter + +import android.app.Activity +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup + +import java.util.ArrayList +import java.util.Locale +import androidx.recyclerview.widget.RecyclerView +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.model.Genre +import code.name.monkey.retromusic.ui.adapter.base.MediaEntryViewHolder +import code.name.monkey.retromusic.util.NavigationUtil + +/** + * @author Hemanth S (h4h13). + */ + +class GenreAdapter(private val mActivity: Activity, dataSet: ArrayList, private val mItemLayoutRes: Int) : RecyclerView.Adapter() { + var dataSet = ArrayList() + private set + + init { + this.dataSet = dataSet + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GenreAdapter.ViewHolder { + return ViewHolder(LayoutInflater.from(mActivity).inflate(mItemLayoutRes, parent, false)) + } + + override fun onBindViewHolder(holder: GenreAdapter.ViewHolder, position: Int) { + val genre = dataSet[position] + if (holder.title != null) { + holder.title!!.text = genre.name + } + if (holder.text != null) { + holder.text!!.text = String.format(Locale.getDefault(), "%d %s", genre.songCount, if (genre.songCount > 1) + mActivity.getString(R.string.songs) + else + mActivity.getString(R.string.song)) + } + + if (holder.separator != null) { + holder.separator!!.visibility = View.VISIBLE + } + } + + override fun getItemCount(): Int { + return dataSet.size + } + + fun swapDataSet(list: ArrayList) { + dataSet = list + notifyDataSetChanged() + } + + inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) { + init { + if (menu != null) { + menu!!.visibility = View.GONE + } + assert(imageContainer != null) + imageContainer!!.visibility = View.GONE + } + + override fun onClick(v: View?) { + super.onClick(v) + val genre = dataSet[adapterPosition] + NavigationUtil.goToGenre(mActivity, genre) + } + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/SearchAdapter.java b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/SearchAdapter.java deleted file mode 100644 index 8198722d..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/SearchAdapter.java +++ /dev/null @@ -1,163 +0,0 @@ -package code.name.monkey.retromusic.ui.adapter; - -import androidx.annotation.NonNull; -import androidx.core.util.Pair; -import androidx.appcompat.app.AppCompatActivity; -import androidx.recyclerview.widget.RecyclerView; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import com.bumptech.glide.Glide; - -import java.util.ArrayList; -import java.util.List; - -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.glide.ArtistGlideRequest; -import code.name.monkey.retromusic.glide.SongGlideRequest; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.helper.menu.SongMenuHelper; -import code.name.monkey.retromusic.model.Album; -import code.name.monkey.retromusic.model.Artist; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.ui.adapter.base.MediaEntryViewHolder; -import code.name.monkey.retromusic.util.MusicUtil; -import code.name.monkey.retromusic.util.NavigationUtil; - - -public class SearchAdapter extends RecyclerView.Adapter { - - private static final int HEADER = 0; - private static final int ALBUM = 1; - private static final int ARTIST = 2; - private static final int SONG = 3; - - private final AppCompatActivity activity; - private List dataSet; - - public SearchAdapter(@NonNull AppCompatActivity activity, @NonNull List dataSet) { - this.activity = activity; - this.dataSet = dataSet; - } - - public void swapDataSet(@NonNull ArrayList dataSet) { - this.dataSet = dataSet; - notifyDataSetChanged(); - } - - @Override - public int getItemViewType(int position) { - if (dataSet.get(position) instanceof Album) return ALBUM; - if (dataSet.get(position) instanceof Artist) return ARTIST; - if (dataSet.get(position) instanceof Song) return SONG; - return HEADER; - } - - @NonNull - @Override - public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - if (viewType == HEADER) - return new ViewHolder(LayoutInflater.from(activity).inflate(R.layout.sub_header, parent, false), viewType); - return new ViewHolder(LayoutInflater.from(activity).inflate(R.layout.item_list, parent, false), viewType); - } - - @SuppressWarnings("ConstantConditions") - @Override - public void onBindViewHolder(@NonNull final ViewHolder holder, int position) { - switch (getItemViewType(position)) { - case ALBUM: - final Album album = (Album) dataSet.get(position); - holder.title.setText(album.getTitle()); - holder.text.setText(album.getArtistName()); - SongGlideRequest.Builder.from(Glide.with(activity), album.safeGetFirstSong()) - .checkIgnoreMediaStore(activity).build() - .into(holder.image); - break; - case ARTIST: - final Artist artist = (Artist) dataSet.get(position); - holder.title.setText(artist.getName()); - holder.text.setText(MusicUtil.getArtistInfoString(activity, artist)); - ArtistGlideRequest.Builder.from(Glide.with(activity), artist) - .build().into(holder.image); - break; - case SONG: - final Song song = (Song) dataSet.get(position); - holder.title.setText(song.title); - holder.text.setText(song.albumName); - break; - default: - holder.title.setText(dataSet.get(position).toString()); - holder.title.setTextColor(ThemeStore.accentColor(activity)); - break; - } - } - - @Override - public int getItemCount() { - return dataSet.size(); - } - - public class ViewHolder extends MediaEntryViewHolder { - public ViewHolder(@NonNull View itemView, int itemViewType) { - super(itemView); - itemView.setOnLongClickListener(null); - - if (itemViewType != HEADER) { - if (separator != null) { - separator.setVisibility(View.GONE); - } - } - - if (menu != null) { - if (itemViewType == SONG) { - menu.setVisibility(View.VISIBLE); - menu.setOnClickListener(new SongMenuHelper.OnClickSongMenu(activity) { - @Override - public Song getSong() { - return (Song) dataSet.get(getAdapterPosition()); - } - }); - } else { - menu.setVisibility(View.GONE); - } - } - - switch (itemViewType) { - case ALBUM: - setImageTransitionName(activity.getString(R.string.transition_album_art)); - break; - case ARTIST: - setImageTransitionName(activity.getString(R.string.transition_artist_image)); - break; - default: - View container = itemView.findViewById(R.id.image_container); - if (container != null) { - container.setVisibility(View.GONE); - } - break; - } - } - - @Override - public void onClick(View view) { - Object item = dataSet.get(getAdapterPosition()); - switch (getItemViewType()) { - case ALBUM: - NavigationUtil.goToAlbum(activity, - ((Album) item).getId(), Pair.create(image, activity.getResources().getString(R.string.transition_album_art))); - break; - case ARTIST: - NavigationUtil.goToArtist(activity, - ((Artist) item).getId(), Pair.create(image, activity.getResources().getString(R.string.transition_artist_image))); - break; - case SONG: - ArrayList playList = new ArrayList<>(); - playList.add((Song) item); - MusicPlayerRemote.openQueue(playList, 0, true); - break; - } - } - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/SearchAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/SearchAdapter.kt new file mode 100644 index 00000000..58d66083 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/SearchAdapter.kt @@ -0,0 +1,132 @@ +package code.name.monkey.retromusic.ui.adapter + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.app.AppCompatActivity +import androidx.core.util.Pair +import androidx.recyclerview.widget.RecyclerView +import code.name.monkey.appthemehelper.ThemeStore +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.glide.ArtistGlideRequest +import code.name.monkey.retromusic.glide.SongGlideRequest +import code.name.monkey.retromusic.helper.MusicPlayerRemote +import code.name.monkey.retromusic.helper.menu.SongMenuHelper +import code.name.monkey.retromusic.model.Album +import code.name.monkey.retromusic.model.Artist +import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.ui.adapter.base.MediaEntryViewHolder +import code.name.monkey.retromusic.util.MusicUtil +import code.name.monkey.retromusic.util.NavigationUtil +import com.bumptech.glide.Glide +import java.util.* + + +class SearchAdapter(private val activity: AppCompatActivity, private var dataSet: List?) : RecyclerView.Adapter() { + + fun swapDataSet(dataSet: ArrayList) { + this.dataSet = dataSet + notifyDataSetChanged() + } + + override fun getItemViewType(position: Int): Int { + if (dataSet!![position] is Album) return ALBUM + if (dataSet!![position] is Artist) return ARTIST + return if (dataSet!![position] is Song) SONG else HEADER + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return if (viewType == HEADER) ViewHolder(LayoutInflater.from(activity).inflate(R.layout.sub_header, parent, false), viewType) else ViewHolder(LayoutInflater.from(activity).inflate(R.layout.item_list, parent, false), viewType) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + when (getItemViewType(position)) { + ALBUM -> { + val album = dataSet!![position] as Album + holder.title!!.text = album.title + holder.text!!.text = album.artistName + SongGlideRequest.Builder.from(Glide.with(activity), album.safeGetFirstSong()) + .checkIgnoreMediaStore(activity).build() + .into(holder.image!!) + } + ARTIST -> { + val artist = dataSet!![position] as Artist + holder.title!!.text = artist.name + holder.text!!.text = MusicUtil.getArtistInfoString(activity, artist) + ArtistGlideRequest.Builder.from(Glide.with(activity), artist) + .build().into(holder.image!!) + } + SONG -> { + val song = dataSet!![position] as Song + holder.title!!.text = song.title + holder.text!!.text = song.albumName + } + else -> { + holder.title!!.text = dataSet!![position].toString() + holder.title!!.setTextColor(ThemeStore.accentColor(activity)) + } + } + } + + override fun getItemCount(): Int { + return dataSet!!.size + } + + inner class ViewHolder(itemView: View, itemViewType: Int) : MediaEntryViewHolder(itemView) { + init { + itemView.setOnLongClickListener(null) + + if (itemViewType != HEADER) { + if (separator != null) { + separator!!.visibility = View.GONE + } + } + + if (menu != null) { + if (itemViewType == SONG) { + menu!!.visibility = View.VISIBLE + menu!!.setOnClickListener(object : SongMenuHelper.OnClickSongMenu(activity) { + override val song: Song + get() = dataSet!![adapterPosition] as Song + }) + } else { + menu!!.visibility = View.GONE + } + } + + when (itemViewType) { + ALBUM -> setImageTransitionName(activity.getString(R.string.transition_album_art)) + ARTIST -> setImageTransitionName(activity.getString(R.string.transition_artist_image)) + else -> { + val container = itemView.findViewById(R.id.image_container) + if (container != null) { + container.visibility = View.GONE + } + } + } + } + + override fun onClick(v: View?) { + val item = dataSet!![adapterPosition] + when (itemViewType) { + ALBUM -> NavigationUtil.goToAlbum(activity, + (item as Album).id, Pair.create(image, activity.resources.getString(R.string.transition_album_art))) + ARTIST -> NavigationUtil.goToArtist(activity, + (item as Artist).id, Pair.create(image, activity.resources.getString(R.string.transition_artist_image))) + SONG -> { + val playList = ArrayList() + playList.add(item as Song) + MusicPlayerRemote.openQueue(playList, 0, true) + } + } + } + } + + companion object { + + private val HEADER = 0 + private val ALBUM = 1 + private val ARTIST = 2 + private val SONG = 3 + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/SongFileAdapter.java b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/SongFileAdapter.java deleted file mode 100644 index 8a110673..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/SongFileAdapter.java +++ /dev/null @@ -1,215 +0,0 @@ -package code.name.monkey.retromusic.ui.adapter; - -import android.graphics.PorterDuff; -import android.graphics.drawable.Drawable; -import androidx.annotation.LayoutRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; -import android.view.LayoutInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; - -import com.bumptech.glide.Glide; -import com.bumptech.glide.load.engine.DiskCacheStrategy; -import com.bumptech.glide.signature.MediaStoreSignature; -import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView; - -import java.io.File; -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.List; - -import code.name.monkey.appthemehelper.util.ATHUtil; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.glide.audiocover.AudioFileCover; -import code.name.monkey.retromusic.interfaces.CabHolder; -import code.name.monkey.retromusic.ui.adapter.base.AbsMultiSelectAdapter; -import code.name.monkey.retromusic.ui.adapter.base.MediaEntryViewHolder; -import code.name.monkey.retromusic.util.RetroUtil; - -public class SongFileAdapter extends AbsMultiSelectAdapter implements FastScrollRecyclerView.SectionedAdapter { - - private static final int FILE = 0; - private static final int FOLDER = 1; - - private final AppCompatActivity activity; - private final int itemLayoutRes; - @Nullable - private final Callbacks callbacks; - private List dataSet; - - public SongFileAdapter(@NonNull AppCompatActivity activity, @NonNull List songFiles, @LayoutRes int itemLayoutRes, @Nullable Callbacks callback, @Nullable CabHolder cabHolder) { - super(activity, cabHolder, R.menu.menu_media_selection); - this.activity = activity; - this.dataSet = songFiles; - this.itemLayoutRes = itemLayoutRes; - this.callbacks = callback; - setHasStableIds(true); - } - - public static String readableFileSize(long size) { - if (size <= 0) return size + " B"; - final String[] units = new String[]{"B", "KB", "MB", "GB", "TB"}; - int digitGroups = (int) (Math.log10(size) / Math.log10(1024)); - return new DecimalFormat("#,##0.##").format(size / Math.pow(1024, digitGroups)) + " " + units[digitGroups]; - } - - @Override - public int getItemViewType(int position) { - return dataSet.get(position).isDirectory() ? FOLDER : FILE; - } - - @Override - public long getItemId(int position) { - return dataSet.get(position).hashCode(); - } - - public void swapDataSet(@NonNull List songFiles) { - this.dataSet = songFiles; - notifyDataSetChanged(); - } - - @NonNull - @Override - public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - return new ViewHolder(LayoutInflater.from(activity).inflate(itemLayoutRes, parent, false)); - } - - @Override - public void onBindViewHolder(@NonNull final ViewHolder holder, int index) { - final File file = dataSet.get(index); - - holder.itemView.setActivated(isChecked(file)); - - if (holder.getAdapterPosition() == getItemCount() - 1) { - if (holder.shortSeparator != null) { - holder.shortSeparator.setVisibility(View.GONE); - } - } else { - if (holder.shortSeparator != null) { - holder.shortSeparator.setVisibility(View.VISIBLE); - } - } - - if (holder.title != null) { - holder.title.setText(getFileTitle(file)); - } - if (holder.text != null) { - if (holder.getItemViewType() == FILE) { - holder.text.setText(getFileText(file)); - } else { - holder.text.setVisibility(View.GONE); - } - } - - if (holder.image != null) { - loadFileImage(file, holder); - } - } - - protected String getFileTitle(File file) { - return file.getName(); - } - - protected String getFileText(File file) { - return file.isDirectory() ? null : readableFileSize(file.length()); - } - - @SuppressWarnings("ConstantConditions") - protected void loadFileImage(File file, final ViewHolder holder) { - final int iconColor = ATHUtil.resolveColor(activity, R.attr.iconColor); - if (file.isDirectory()) { - holder.image.setColorFilter(iconColor, PorterDuff.Mode.SRC_IN); - holder.image.setImageResource(R.drawable.ic_folder_white_24dp); - } else { - Drawable error = RetroUtil.getTintedVectorDrawable(activity, R.drawable.ic_file_music_white_24dp, iconColor); - Glide.with(activity) - .load(new AudioFileCover(file.getPath())) - .diskCacheStrategy(DiskCacheStrategy.NONE) - .error(error) - .placeholder(error) - .animate(android.R.anim.fade_in) - .signature(new MediaStoreSignature("", file.lastModified(), 0)) - .into(holder.image); - } - } - - @Override - public int getItemCount() { - return dataSet.size(); - } - - @Override - protected File getIdentifier(int position) { - return dataSet.get(position); - } - - @Override - protected String getName(File object) { - return getFileTitle(object); - } - - @Override - protected void onMultipleItemAction(MenuItem menuItem, ArrayList selection) { - if (callbacks == null) return; - callbacks.onMultipleItemAction(menuItem, selection); - } - - @NonNull - @Override - public String getSectionName(int position) { - return String.valueOf(dataSet.get(position).getName().charAt(0)).toUpperCase(); - } - - public interface Callbacks { - void onFileSelected(File file); - - void onFileMenuClicked(File file, View view); - - void onMultipleItemAction(MenuItem item, ArrayList files); - } - - public class ViewHolder extends MediaEntryViewHolder { - - public ViewHolder(View itemView) { - super(itemView); - if (menu != null && callbacks != null) { - menu.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - int position = getAdapterPosition(); - if (isPositionInRange(position)) { - callbacks.onFileMenuClicked(dataSet.get(position), v); - } - } - }); - } - } - - @Override - public void onClick(View v) { - int position = getAdapterPosition(); - if (isPositionInRange(position)) { - if (isInQuickSelectMode()) { - toggleChecked(position); - } else { - if (callbacks != null) { - callbacks.onFileSelected(dataSet.get(position)); - } - } - } - } - - @Override - public boolean onLongClick(View view) { - int position = getAdapterPosition(); - return isPositionInRange(position) && toggleChecked(position); - } - - private boolean isPositionInRange(int position) { - return position >= 0 && position < dataSet.size(); - } - } -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/SongFileAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/SongFileAdapter.kt new file mode 100644 index 00000000..0bad9a4f --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/SongFileAdapter.kt @@ -0,0 +1,180 @@ +package code.name.monkey.retromusic.ui.adapter + +import android.graphics.PorterDuff +import android.view.LayoutInflater +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup +import androidx.annotation.LayoutRes +import androidx.appcompat.app.AppCompatActivity +import code.name.monkey.appthemehelper.util.ATHUtil +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.glide.audiocover.AudioFileCover +import code.name.monkey.retromusic.interfaces.CabHolder +import code.name.monkey.retromusic.ui.adapter.base.AbsMultiSelectAdapter +import code.name.monkey.retromusic.ui.adapter.base.MediaEntryViewHolder +import code.name.monkey.retromusic.util.RetroUtil +import com.bumptech.glide.Glide +import com.bumptech.glide.load.engine.DiskCacheStrategy +import com.bumptech.glide.signature.MediaStoreSignature +import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView +import java.io.File +import java.text.DecimalFormat +import java.util.* + +class SongFileAdapter(private val activity: AppCompatActivity, private var dataSet: List?, @param:LayoutRes private val itemLayoutRes: Int, private val callbacks: Callbacks?, cabHolder: CabHolder?) : AbsMultiSelectAdapter(activity, cabHolder, R.menu.menu_media_selection), FastScrollRecyclerView.SectionedAdapter { + + init { + this.setHasStableIds(true) + } + + override fun getItemViewType(position: Int): Int { + return if (dataSet!![position].isDirectory) FOLDER else FILE + } + + override fun getItemId(position: Int): Long { + return dataSet!![position].hashCode().toLong() + } + + fun swapDataSet(songFiles: List) { + this.dataSet = songFiles + notifyDataSetChanged() + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return ViewHolder(LayoutInflater.from(activity).inflate(itemLayoutRes, parent, false)) + } + + override fun onBindViewHolder(holder: ViewHolder, index: Int) { + val file = dataSet!![index] + + holder.itemView.isActivated = isChecked(file) + + if (holder.adapterPosition == itemCount - 1) { + if (holder.shortSeparator != null) { + holder.shortSeparator!!.visibility = View.GONE + } + } else { + if (holder.shortSeparator != null) { + holder.shortSeparator!!.visibility = View.VISIBLE + } + } + + if (holder.title != null) { + holder.title!!.text = getFileTitle(file) + } + if (holder.text != null) { + if (holder.itemViewType == FILE) { + holder.text!!.text = getFileText(file) + } else { + holder.text!!.visibility = View.GONE + } + } + + if (holder.image != null) { + loadFileImage(file, holder) + } + } + + private fun getFileTitle(file: File): String { + return file.name + } + + private fun getFileText(file: File): String? { + return if (file.isDirectory) null else readableFileSize(file.length()) + } + + private fun loadFileImage(file: File, holder: ViewHolder) { + val iconColor = ATHUtil.resolveColor(activity, R.attr.iconColor) + if (file.isDirectory) { + holder.image!!.setColorFilter(iconColor, PorterDuff.Mode.SRC_IN) + holder.image!!.setImageResource(R.drawable.ic_folder_white_24dp) + } else { + val error = RetroUtil.getTintedVectorDrawable(activity, R.drawable.ic_file_music_white_24dp, iconColor) + Glide.with(activity) + .load(AudioFileCover(file.path)) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .error(error) + .placeholder(error) + .animate(android.R.anim.fade_in) + .signature(MediaStoreSignature("", file.lastModified(), 0)) + .into(holder.image!!) + } + } + + override fun getItemCount(): Int { + return dataSet!!.size + } + + override fun getIdentifier(position: Int): File? { + return dataSet!![position] + } + + override fun getName(`object`: File): String { + return getFileTitle(`object`) + } + + override fun onMultipleItemAction(menuItem: MenuItem, selection: ArrayList) { + if (callbacks == null) return + callbacks.onMultipleItemAction(menuItem, selection) + } + + override fun getSectionName(position: Int): String { + return dataSet!![position].name[0].toString().toUpperCase() + } + + interface Callbacks { + fun onFileSelected(file: File) + + fun onFileMenuClicked(file: File, view: View) + + fun onMultipleItemAction(item: MenuItem, files: ArrayList) + } + + inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) { + + init { + if (menu != null && callbacks != null) { + menu!!.setOnClickListener { v -> + val position = adapterPosition + if (isPositionInRange(position)) { + callbacks.onFileMenuClicked(dataSet!![position], v) + } + } + } + } + + override fun onClick(v: View?) { + val position = adapterPosition + if (isPositionInRange(position)) { + if (isInQuickSelectMode) { + toggleChecked(position) + } else { + callbacks?.onFileSelected(dataSet!![position]) + } + } + } + + override fun onLongClick(v: View?): Boolean { + val position = adapterPosition + return isPositionInRange(position) && toggleChecked(position) + } + + private fun isPositionInRange(position: Int): Boolean { + return position >= 0 && position < dataSet!!.size + } + } + + companion object { + + private const val FILE = 0 + private const val FOLDER = 1 + + fun readableFileSize(size: Long): String { + if (size <= 0) return size.toString() + " B" + val units = arrayOf("B", "KB", "MB", "GB", "TB") + val digitGroups = (Math.log10(size.toDouble()) / Math.log10(1024.0)).toInt() + return DecimalFormat("#,##0.##").format(size / Math.pow(1024.0, digitGroups.toDouble())) + " " + units[digitGroups] + } + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/album/AlbumAdapter.java b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/album/AlbumAdapter.java deleted file mode 100644 index 90bfd72d..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/album/AlbumAdapter.java +++ /dev/null @@ -1,249 +0,0 @@ -package code.name.monkey.retromusic.ui.adapter.album; - -import android.content.res.ColorStateList; -import android.graphics.drawable.Drawable; -import android.view.LayoutInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; - -import com.bumptech.glide.Glide; -import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView; - -import java.util.ArrayList; -import java.util.List; - -import androidx.annotation.LayoutRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; -import androidx.core.util.Pair; -import code.name.monkey.appthemehelper.util.ColorUtil; -import code.name.monkey.appthemehelper.util.MaterialValueHelper; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.glide.RetroMusicColoredTarget; -import code.name.monkey.retromusic.glide.SongGlideRequest; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.helper.SortOrder; -import code.name.monkey.retromusic.helper.menu.SongsMenuHelper; -import code.name.monkey.retromusic.interfaces.CabHolder; -import code.name.monkey.retromusic.model.Album; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.ui.adapter.base.AbsMultiSelectAdapter; -import code.name.monkey.retromusic.ui.adapter.base.MediaEntryViewHolder; -import code.name.monkey.retromusic.util.MusicUtil; -import code.name.monkey.retromusic.util.NavigationUtil; -import code.name.monkey.retromusic.util.PreferenceUtil; - - -public class AlbumAdapter extends AbsMultiSelectAdapter implements - FastScrollRecyclerView.SectionedAdapter { - - public static final String TAG = AlbumAdapter.class.getSimpleName(); - - protected final AppCompatActivity activity; - protected ArrayList dataSet; - - protected int itemLayoutRes; - - protected boolean usePalette = false; - - - public AlbumAdapter(@NonNull AppCompatActivity activity, ArrayList dataSet, - @LayoutRes int itemLayoutRes, boolean usePalette, - @Nullable CabHolder cabHolder) { - super(activity, cabHolder, R.menu.menu_media_selection); - this.activity = activity; - this.dataSet = dataSet; - this.itemLayoutRes = itemLayoutRes; - this.usePalette = usePalette; - setHasStableIds(true); - - } - - - public void useItemLayout(int itemLayoutRes) { - this.itemLayoutRes = itemLayoutRes; - notifyDataSetChanged(); - } - - public void usePalette(boolean usePalette) { - this.usePalette = usePalette; - notifyDataSetChanged(); - } - - public void swapDataSet(ArrayList dataSet) { - this.dataSet = dataSet; - notifyDataSetChanged(); - } - - public ArrayList getDataSet() { - return dataSet; - } - - @NonNull - @Override - public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - View view = LayoutInflater.from(activity).inflate(itemLayoutRes, parent, false); - return createViewHolder(view, viewType); - } - - protected ViewHolder createViewHolder(View view, int viewType) { - return new ViewHolder(view); - } - - private String getAlbumTitle(Album album) { - return album.getTitle(); - } - - protected String getAlbumText(Album album) { - return album.getArtistName(); - } - - @Override - public void onBindViewHolder(@NonNull final ViewHolder holder, int position) { - final Album album = dataSet.get(position); - - final boolean isChecked = isChecked(album); - holder.itemView.setActivated(isChecked); - - if (holder.getAdapterPosition() == getItemCount() - 1) { - if (holder.shortSeparator != null) { - holder.shortSeparator.setVisibility(View.GONE); - } - } else { - if (holder.shortSeparator != null) { - holder.shortSeparator.setVisibility(View.VISIBLE); - } - } - - if (holder.title != null) { - holder.title.setText(getAlbumTitle(album)); - } - if (holder.text != null) { - holder.text.setText(getAlbumText(album)); - } - if (holder.playSongs != null) { - holder.playSongs.setOnClickListener(v -> MusicPlayerRemote.openQueue(album.songs, 0, true)); - } - loadAlbumCover(album, holder); - } - - protected void setColors(int color, ViewHolder holder) { - if (holder.paletteColorContainer != null) { - holder.paletteColorContainer.setBackgroundColor(color); - if (holder.title != null) { - holder.title.setTextColor(MaterialValueHelper.getPrimaryTextColor(activity, ColorUtil.isColorLight(color))); - } - if (holder.text != null) { - holder.text.setTextColor(MaterialValueHelper.getSecondaryTextColor(activity, ColorUtil.isColorLight(color))); - } - } - if (holder.mask != null) { - holder.mask.setBackgroundTintList(ColorStateList.valueOf(color)); - } - } - - protected void loadAlbumCover(Album album, final ViewHolder holder) { - if (holder.image == null) { - return; - } - - SongGlideRequest.Builder.from(Glide.with(activity), album.safeGetFirstSong()) - .checkIgnoreMediaStore(activity) - .generatePalette(activity).build() - .into(new RetroMusicColoredTarget(holder.image) { - @Override - public void onLoadCleared(Drawable placeholder) { - super.onLoadCleared(placeholder); - setColors(getDefaultFooterColor(), holder); - } - - @Override - public void onColorReady(int color) { - setColors(color, holder); - } - }); - } - - @Override - public int getItemCount() { - return dataSet.size(); - } - - @Override - public long getItemId(int position) { - return dataSet.get(position).getId(); - } - - @Override - protected Album getIdentifier(int position) { - return dataSet.get(position); - } - - @Override - protected String getName(Album album) { - return album.getTitle(); - } - - @Override - protected void onMultipleItemAction(@NonNull MenuItem menuItem, - @NonNull ArrayList selection) { - SongsMenuHelper.handleMenuClick(activity, getSongList(selection), menuItem.getItemId()); - } - - @NonNull - private ArrayList getSongList(@NonNull List albums) { - final ArrayList songs = new ArrayList<>(); - for (Album album : albums) { - songs.addAll(album.songs); - } - return songs; - } - - @NonNull - @Override - public String getSectionName(int position) { - @Nullable String sectionName = null; - switch (PreferenceUtil.getInstance().getAlbumSortOrder()) { - case SortOrder.AlbumSortOrder.ALBUM_A_Z: - case SortOrder.AlbumSortOrder.ALBUM_Z_A: - sectionName = dataSet.get(position).getTitle(); - break; - case SortOrder.AlbumSortOrder.ALBUM_ARTIST: - sectionName = dataSet.get(position).getArtistName(); - break; - case SortOrder.AlbumSortOrder.ALBUM_YEAR: - return MusicUtil.getYearString(dataSet.get(position).getYear()); - } - - return MusicUtil.getSectionName(sectionName); - } - - public class ViewHolder extends MediaEntryViewHolder { - - public ViewHolder(@NonNull final View itemView) { - super(itemView); - setImageTransitionName(activity.getString(R.string.transition_album_art)); - if (menu != null) { - menu.setVisibility(View.GONE); - } - } - - @Override - public void onClick(View v) { - if (isInQuickSelectMode()) { - toggleChecked(getAdapterPosition()); - } else { - Pair[] albumPairs = new Pair[]{Pair.create(image, activity.getResources().getString(R.string.transition_album_art))}; - NavigationUtil.goToAlbum(activity, dataSet.get(getAdapterPosition()).getId(), albumPairs); - } - } - - @Override - public boolean onLongClick(View view) { - toggleChecked(getAdapterPosition()); - return true; - } - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/album/AlbumAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/album/AlbumAdapter.kt new file mode 100644 index 00000000..301a335e --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/album/AlbumAdapter.kt @@ -0,0 +1,214 @@ +package code.name.monkey.retromusic.ui.adapter.album + +import android.content.res.ColorStateList +import android.graphics.drawable.Drawable +import android.view.LayoutInflater +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup +import androidx.annotation.LayoutRes +import androidx.appcompat.app.AppCompatActivity +import androidx.core.util.Pair +import code.name.monkey.appthemehelper.util.ColorUtil +import code.name.monkey.appthemehelper.util.MaterialValueHelper +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.glide.RetroMusicColoredTarget +import code.name.monkey.retromusic.glide.SongGlideRequest +import code.name.monkey.retromusic.helper.MusicPlayerRemote +import code.name.monkey.retromusic.helper.SortOrder +import code.name.monkey.retromusic.helper.menu.SongsMenuHelper +import code.name.monkey.retromusic.interfaces.CabHolder +import code.name.monkey.retromusic.model.Album +import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.ui.adapter.base.AbsMultiSelectAdapter +import code.name.monkey.retromusic.ui.adapter.base.MediaEntryViewHolder +import code.name.monkey.retromusic.util.MusicUtil +import code.name.monkey.retromusic.util.NavigationUtil +import code.name.monkey.retromusic.util.PreferenceUtil +import com.bumptech.glide.Glide +import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView +import java.util.* + + +open class AlbumAdapter(protected val activity: AppCompatActivity, + dataSet: ArrayList, + @param:LayoutRes protected var itemLayoutRes: Int, + usePalette: Boolean, + cabHolder: CabHolder?) : AbsMultiSelectAdapter(activity, cabHolder, R.menu.menu_media_selection), FastScrollRecyclerView.SectionedAdapter { + var dataSet: ArrayList + protected set + + protected var usePalette = false + + + init { + this.dataSet = dataSet + this.usePalette = usePalette + this.setHasStableIds(true) + } + + fun useItemLayout(itemLayoutRes: Int) { + this.itemLayoutRes = itemLayoutRes + notifyDataSetChanged() + } + + fun usePalette(usePalette: Boolean) { + this.usePalette = usePalette + notifyDataSetChanged() + } + + fun swapDataSet(dataSet: ArrayList) { + this.dataSet = dataSet + notifyDataSetChanged() + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + val view = LayoutInflater.from(activity).inflate(itemLayoutRes, parent, false) + return createViewHolder(view, viewType) + } + + protected open fun createViewHolder(view: View, viewType: Int): ViewHolder { + return ViewHolder(view) + } + + private fun getAlbumTitle(album: Album): String? { + return album.title + } + + protected open fun getAlbumText(album: Album): String? { + return album.artistName + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val album = dataSet[position] + + val isChecked = isChecked(album) + holder.itemView.isActivated = isChecked + + if (holder.adapterPosition == itemCount - 1) { + if (holder.shortSeparator != null) { + holder.shortSeparator!!.visibility = View.GONE + } + } else { + if (holder.shortSeparator != null) { + holder.shortSeparator!!.visibility = View.VISIBLE + } + } + + if (holder.title != null) { + holder.title!!.text = getAlbumTitle(album) + } + if (holder.text != null) { + holder.text!!.text = getAlbumText(album) + } + if (holder.playSongs != null) { + holder.playSongs!!.setOnClickListener { MusicPlayerRemote.openQueue(album.songs!!, 0, true) } + } + loadAlbumCover(album, holder) + } + + protected open fun setColors(color: Int, holder: ViewHolder) { + if (holder.paletteColorContainer != null) { + holder.paletteColorContainer!!.setBackgroundColor(color) + if (holder.title != null) { + holder.title!!.setTextColor(MaterialValueHelper.getPrimaryTextColor(activity, ColorUtil.isColorLight(color))) + } + if (holder.text != null) { + holder.text!!.setTextColor(MaterialValueHelper.getSecondaryTextColor(activity, ColorUtil.isColorLight(color))) + } + } + if (holder.mask != null) { + holder.mask!!.backgroundTintList = ColorStateList.valueOf(color) + } + } + + protected open fun loadAlbumCover(album: Album, holder: ViewHolder) { + if (holder.image == null) { + return + } + + SongGlideRequest.Builder.from(Glide.with(activity), album.safeGetFirstSong()) + .checkIgnoreMediaStore(activity) + .generatePalette(activity).build() + .into(object : RetroMusicColoredTarget(holder.image!!) { + override fun onLoadCleared(placeholder: Drawable?) { + super.onLoadCleared(placeholder) + setColors(defaultFooterColor, holder) + } + + override fun onColorReady(color: Int) { + setColors(color, holder) + } + }) + } + + override fun getItemCount(): Int { + return dataSet.size + } + + override fun getItemId(position: Int): Long { + return dataSet[position].id.toLong() + } + + override fun getIdentifier(position: Int): Album? { + return dataSet[position] + } + + override fun getName(album: Album): String { + return album.title!! + } + + override fun onMultipleItemAction(menuItem: MenuItem, + selection: ArrayList) { + SongsMenuHelper.handleMenuClick(activity, getSongList(selection), menuItem.itemId) + } + + private fun getSongList(albums: List): ArrayList { + val songs = ArrayList() + for (album in albums) { + songs.addAll(album.songs!!) + } + return songs + } + + override fun getSectionName(position: Int): String { + var sectionName: String? = null + when (PreferenceUtil.getInstance().albumSortOrder) { + SortOrder.AlbumSortOrder.ALBUM_A_Z, SortOrder.AlbumSortOrder.ALBUM_Z_A -> sectionName = dataSet[position].title + SortOrder.AlbumSortOrder.ALBUM_ARTIST -> sectionName = dataSet[position].artistName + SortOrder.AlbumSortOrder.ALBUM_YEAR -> return MusicUtil.getYearString(dataSet[position].year) + } + + return MusicUtil.getSectionName(sectionName) + } + + inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) { + + init { + setImageTransitionName(activity.getString(R.string.transition_album_art)) + if (menu != null) { + menu!!.visibility = View.GONE + } + } + + override fun onClick(v: View?) { + super.onClick(v) + if (isInQuickSelectMode) { + toggleChecked(adapterPosition) + } else { + val albumPairs = arrayOf>(Pair.create(image, activity.resources.getString(R.string.transition_album_art))) + NavigationUtil.goToAlbum(activity, dataSet[adapterPosition].id, *albumPairs) + } + } + + override fun onLongClick(v: View?): Boolean { + toggleChecked(adapterPosition) + return super.onLongClick(v) + } + } + + companion object { + + val TAG = AlbumAdapter::class.java.simpleName + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/album/AlbumCoverPagerAdapter.java b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/album/AlbumCoverPagerAdapter.java deleted file mode 100644 index 6fe11538..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/album/AlbumCoverPagerAdapter.java +++ /dev/null @@ -1,200 +0,0 @@ -package code.name.monkey.retromusic.ui.adapter.album; - -import android.content.Intent; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; - -import com.bumptech.glide.Glide; - -import java.util.ArrayList; - -import androidx.annotation.NonNull; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import butterknife.Unbinder; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.glide.RetroMusicColoredTarget; -import code.name.monkey.retromusic.glide.SongGlideRequest; -import code.name.monkey.retromusic.misc.CustomFragmentStatePagerAdapter; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.ui.activities.LyricsActivity; -import code.name.monkey.retromusic.util.PreferenceUtil; - - -public class AlbumCoverPagerAdapter extends CustomFragmentStatePagerAdapter { - - public static final String TAG = AlbumCoverPagerAdapter.class.getSimpleName(); - - private ArrayList dataSet; - - private AlbumCoverFragment.ColorReceiver currentColorReceiver; - private int currentColorReceiverPosition = -1; - - public AlbumCoverPagerAdapter(FragmentManager fm, ArrayList dataSet) { - super(fm); - this.dataSet = dataSet; - } - - @Override - public Fragment getItem(final int position) { - return AlbumCoverFragment.newInstance(dataSet.get(position)); - } - - @Override - public int getCount() { - return dataSet.size(); - } - - @NonNull - @Override - public Object instantiateItem(ViewGroup container, int position) { - Object o = super.instantiateItem(container, position); - if (currentColorReceiver != null && currentColorReceiverPosition == position) { - receiveColor(currentColorReceiver, currentColorReceiverPosition); - } - return o; - } - - /** - * Only the latest passed {@link AlbumCoverFragment.ColorReceiver} is guaranteed to receive a - * response - */ - public void receiveColor(AlbumCoverFragment.ColorReceiver colorReceiver, int position) { - AlbumCoverFragment fragment = (AlbumCoverFragment) getFragment(position); - if (fragment != null) { - currentColorReceiver = null; - currentColorReceiverPosition = -1; - fragment.receiveColor(colorReceiver, position); - } else { - currentColorReceiver = colorReceiver; - currentColorReceiverPosition = position; - } - } - - public static class AlbumCoverFragment extends Fragment { - - private static final String SONG_ARG = "song"; - @BindView(R.id.player_image) - ImageView albumCover; - private Unbinder unbinder; - private boolean isColorReady; - private int color; - private Song song; - private ColorReceiver colorReceiver; - private int request; - - public static AlbumCoverFragment newInstance(final Song song) { - AlbumCoverFragment frag = new AlbumCoverFragment(); - final Bundle args = new Bundle(); - args.putParcelable(SONG_ARG, song); - frag.setArguments(args); - return frag; - } - - @Override - public void onCreate(final Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (getArguments() != null) { - song = getArguments().getParcelable(SONG_ARG); - } - } - - @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - int layout = getLayout(); - View view = inflater.inflate(layout, container, false); - unbinder = ButterKnife.bind(this, view); - return view; - } - - private int getLayout() { - int layout; - switch (PreferenceUtil.getInstance().getAlbumCoverStyle()) { - default: - case NORMAL: - layout = R.layout.fragment_album_cover; - break; - case FLAT: - layout = R.layout.fragment_album_flat_cover; - break; - case CIRCLE: - layout = R.layout.fragment_album_circle_cover; - break; - case CARD: - layout = R.layout.fragment_album_card_cover; - break; - case MATERIAL: - layout = R.layout.fragment_album_material_cover; - break; - case FULL: - layout = R.layout.fragment_album_full_cover; - break; - case FULL_CARD: - layout = R.layout.fragment_album_full_card_cover; - break; - } - return layout; - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - loadAlbumCover(); - } - - @OnClick(R.id.player_image) - void showLyrics() { - startActivity(new Intent(getContext(), LyricsActivity.class)); - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - unbinder.unbind(); - colorReceiver = null; - } - - private void loadAlbumCover() { - SongGlideRequest.Builder.from(Glide.with(getContext()), song) - .checkIgnoreMediaStore(getActivity()) - .generatePalette(getActivity()).build() - .into(new RetroMusicColoredTarget(albumCover) { - @Override - public void onColorReady(int color) { - setColor(color); - } - }); - } - - private void setColor(int color) { - this.color = color; - isColorReady = true; - if (colorReceiver != null) { - colorReceiver.onColorReady(color, request); - colorReceiver = null; - } - } - - void receiveColor(ColorReceiver colorReceiver, int request) { - if (isColorReady) { - colorReceiver.onColorReady(color, request); - } else { - this.colorReceiver = colorReceiver; - this.request = request; - } - } - - public interface ColorReceiver { - - void onColorReady(int color, int request); - } - } -} - diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/album/AlbumCoverPagerAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/album/AlbumCoverPagerAdapter.kt new file mode 100644 index 00000000..fc902b49 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/album/AlbumCoverPagerAdapter.kt @@ -0,0 +1,172 @@ +package code.name.monkey.retromusic.ui.adapter.album + +import android.content.Intent +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.glide.RetroMusicColoredTarget +import code.name.monkey.retromusic.glide.SongGlideRequest +import code.name.monkey.retromusic.misc.CustomFragmentStatePagerAdapter +import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.ui.activities.LyricsActivity +import code.name.monkey.retromusic.ui.fragments.AlbumCoverStyle +import code.name.monkey.retromusic.util.PreferenceUtil +import com.bumptech.glide.Glide +import java.util.* + + +class AlbumCoverPagerAdapter(fm: FragmentManager, private val dataSet: ArrayList) : CustomFragmentStatePagerAdapter(fm) { + + private var currentColorReceiver: AlbumCoverFragment.ColorReceiver? = null + private var currentColorReceiverPosition = -1 + + override fun getItem(position: Int): Fragment { + return AlbumCoverFragment.newInstance(dataSet[position]) + } + + override fun getCount(): Int { + return dataSet.size + } + + override fun instantiateItem(container: ViewGroup, position: Int): Any { + val o = super.instantiateItem(container, position) + if (currentColorReceiver != null && currentColorReceiverPosition == position) { + receiveColor(currentColorReceiver!!, currentColorReceiverPosition) + } + return o + } + + /** + * Only the latest passed [AlbumCoverFragment.ColorReceiver] is guaranteed to receive a + * response + */ + fun receiveColor(colorReceiver: AlbumCoverFragment.ColorReceiver, position: Int) { + + if (getFragment(position) is AlbumCoverFragment) { + val fragment = getFragment(position) as AlbumCoverFragment + currentColorReceiver = null + currentColorReceiverPosition = -1 + fragment.receiveColor(colorReceiver, position) + } else { + currentColorReceiver = colorReceiver + currentColorReceiverPosition = position + } + + /*val fragment = getFragment(position) as AlbumCoverFragment + if (fragment != null) { + currentColorReceiver = null + currentColorReceiverPosition = -1 + fragment.receiveColor(colorReceiver, position) + } else { + currentColorReceiver = colorReceiver + currentColorReceiverPosition = position + }*/ + } + + class AlbumCoverFragment : Fragment() { + + lateinit var albumCover: ImageView + private var isColorReady: Boolean = false + private var color: Int = 0 + private var song: Song? = null + private var colorReceiver: ColorReceiver? = null + private var request: Int = 0 + + private val layout: Int + get() { + return when (PreferenceUtil.getInstance().albumCoverStyle) { + AlbumCoverStyle.NORMAL -> R.layout.fragment_album_cover + AlbumCoverStyle.FLAT -> R.layout.fragment_album_flat_cover + AlbumCoverStyle.CIRCLE -> R.layout.fragment_album_circle_cover + AlbumCoverStyle.CARD -> R.layout.fragment_album_card_cover + AlbumCoverStyle.MATERIAL -> R.layout.fragment_album_material_cover + AlbumCoverStyle.FULL -> R.layout.fragment_album_full_cover + AlbumCoverStyle.FULL_CARD -> R.layout.fragment_album_full_card_cover + else -> R.layout.fragment_album_cover + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + if (arguments != null) { + song = arguments!!.getParcelable(SONG_ARG) + } + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + val layout = layout + val view = inflater.inflate(layout, container, false) + albumCover = view.findViewById(R.id.player_image) + albumCover.setOnClickListener { startActivity(Intent(context, LyricsActivity::class.java)) } + return view + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + loadAlbumCover() + } + + + override fun onDestroyView() { + super.onDestroyView() + colorReceiver = null + } + + private fun loadAlbumCover() { + SongGlideRequest.Builder.from(Glide.with(context), song) + .checkIgnoreMediaStore(activity) + .generatePalette(activity).build() + .into(object : RetroMusicColoredTarget(albumCover) { + override fun onColorReady(color: Int) { + setColor(color) + } + }) + } + + private fun setColor(color: Int) { + this.color = color + isColorReady = true + if (colorReceiver != null) { + colorReceiver!!.onColorReady(color, request) + colorReceiver = null + } + } + + internal fun receiveColor(colorReceiver: ColorReceiver, request: Int) { + if (isColorReady) { + colorReceiver.onColorReady(color, request) + } else { + this.colorReceiver = colorReceiver + this.request = request + } + } + + interface ColorReceiver { + + fun onColorReady(color: Int, request: Int) + } + + companion object { + + private const val SONG_ARG = "song" + + fun newInstance(song: Song): AlbumCoverFragment { + val frag = AlbumCoverFragment() + val args = Bundle() + args.putParcelable(SONG_ARG, song) + frag.arguments = args + return frag + } + } + } + + companion object { + val TAG: String = AlbumCoverPagerAdapter::class.java.simpleName + } +} + diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/album/AlbumFullWithAdapter.java b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/album/AlbumFullWithAdapter.java deleted file mode 100644 index 348619b3..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/album/AlbumFullWithAdapter.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (C) 2017. Alexander Bilchuk - * - * 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 code.name.monkey.retromusic.ui.adapter.album; - -import android.app.Activity; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import androidx.annotation.NonNull; -import androidx.core.util.Pair; -import android.util.DisplayMetrics; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import com.bumptech.glide.Glide; - -import java.util.ArrayList; -import java.util.List; - -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.glide.RetroMusicColoredTarget; -import code.name.monkey.retromusic.glide.SongGlideRequest; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.model.Album; -import code.name.monkey.retromusic.util.NavigationUtil; -import code.name.monkey.retromusic.views.MetalRecyclerViewPager; - -public class AlbumFullWithAdapter extends - MetalRecyclerViewPager.MetalAdapter { - - private Activity activity; - private List dataSet = new ArrayList<>(); - - public AlbumFullWithAdapter(@NonNull Activity activity, - @NonNull DisplayMetrics metrics) { - super(metrics); - this.activity = activity; - } - - public void swapData(ArrayList list) { - dataSet = list; - notifyDataSetChanged(); - } - - @NonNull - @Override - public FullMetalViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - View viewItem = LayoutInflater.from(parent.getContext()) - .inflate(R.layout.pager_item, parent, false); - return new FullMetalViewHolder(viewItem); - } - - private Bitmap combineImageIntoOne(ArrayList bitmap) { - int w = 0, h = 0; - for (int i = 0; i < bitmap.size(); i++) { - if (i < bitmap.size() - 1) { - h = bitmap.get(i).getWidth() > bitmap.get(i + 1).getWidth() ? bitmap.get(i).getWidth() - : bitmap.get(i + 1).getWidth(); - } - w += bitmap.get(i).getHeight(); - } - - Bitmap temp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(temp); - int top = 0, left = 0; - for (int i = 0; i < bitmap.size(); i++) { - Log.d("HTML", "Combine: " + i + "/" + bitmap.size() + 1); - - top = (i == 0 ? 0 : top + bitmap.get(i).getHeight()); - left = (i == 0 ? 0 : top + bitmap.get(i).getWidth()); - canvas.drawBitmap(bitmap.get(i), left, 0f, null); - } - return temp; - } - - @Override - public void onBindViewHolder(@NonNull FullMetalViewHolder holder, int position) { - // don't forget about calling supper.onBindViewHolder! - super.onBindViewHolder(holder, position); - - final Album album = dataSet.get(position); - - if (holder.title != null) { - holder.title.setText(getAlbumTitle(album)); - } - if (holder.text != null) { - holder.text.setText(getAlbumText(album)); - } - if (holder.playSongs != null) { - holder.playSongs.setOnClickListener(v -> MusicPlayerRemote.openQueue(album.songs, 0, true)); - } - loadAlbumCover(album, holder); - } - - private String getAlbumTitle(Album album) { - return album.getTitle(); - } - - private String getAlbumText(Album album) { - return album.getArtistName(); - } - - private void loadAlbumCover(Album album, FullMetalViewHolder holder) { - if (holder.image == null) { - return; - } - - SongGlideRequest.Builder.from(Glide.with(activity), album.safeGetFirstSong()) - .checkIgnoreMediaStore(activity) - .generatePalette(activity).build() - .into(new RetroMusicColoredTarget(holder.image) { - @Override - public void onColorReady(int color) { - - } - }); - } - - @Override - public int getItemCount() { - return dataSet.size(); - } - - class FullMetalViewHolder extends MetalRecyclerViewPager.MetalViewHolder { - - FullMetalViewHolder(View itemView) { - super(itemView); - } - - @Override - public void onClick(View v) { - Pair[] albumPairs = new Pair[]{ - Pair.create(image, activity.getResources().getString(R.string.transition_album_art))}; - NavigationUtil.goToAlbum(activity, dataSet.get(getAdapterPosition()).getId(), albumPairs); - } - } -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/album/AlbumFullWithAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/album/AlbumFullWithAdapter.kt new file mode 100644 index 00000000..36de3f85 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/album/AlbumFullWithAdapter.kt @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2017. Alexander Bilchuk + * + * 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 code.name.monkey.retromusic.ui.adapter.album + +import android.app.Activity +import android.graphics.Bitmap +import android.graphics.Canvas +import android.util.DisplayMetrics +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import androidx.core.util.Pair +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.glide.RetroMusicColoredTarget +import code.name.monkey.retromusic.glide.SongGlideRequest +import code.name.monkey.retromusic.helper.MusicPlayerRemote +import code.name.monkey.retromusic.model.Album +import code.name.monkey.retromusic.util.NavigationUtil +import code.name.monkey.retromusic.views.MetalRecyclerViewPager +import com.bumptech.glide.Glide +import java.util.* + +class AlbumFullWithAdapter(private val activity: Activity, + metrics: DisplayMetrics) : MetalRecyclerViewPager.MetalAdapter(metrics) { + private var dataSet: List = ArrayList() + + fun swapData(list: ArrayList) { + dataSet = list + notifyDataSetChanged() + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FullMetalViewHolder { + val viewItem = LayoutInflater.from(parent.context) + .inflate(R.layout.pager_item, parent, false) + return FullMetalViewHolder(viewItem) + } + + override fun onBindViewHolder(holder: FullMetalViewHolder, position: Int) { + // don't forget about calling supper.onBindViewHolder! + super.onBindViewHolder(holder, position) + + val album = dataSet[position] + + if (holder.title != null) { + holder.title!!.text = getAlbumTitle(album) + } + if (holder.text != null) { + holder.text!!.text = getAlbumText(album) + } + if (holder.playSongs != null) { + holder.playSongs!!.setOnClickListener { MusicPlayerRemote.openQueue(album.songs!!, 0, true) } + } + loadAlbumCover(album, holder) + } + + private fun getAlbumTitle(album: Album): String? { + return album.title + } + + private fun getAlbumText(album: Album): String? { + return album.artistName + } + + private fun loadAlbumCover(album: Album, holder: FullMetalViewHolder) { + if (holder.image == null) { + return + } + + SongGlideRequest.Builder.from(Glide.with(activity), album.safeGetFirstSong()) + .checkIgnoreMediaStore(activity) + .generatePalette(activity).build() + .into(object : RetroMusicColoredTarget(holder.image!!) { + override fun onColorReady(color: Int) { + + } + }) + } + + override fun getItemCount(): Int { + return dataSet.size + } + + inner class FullMetalViewHolder(itemView: View) : MetalRecyclerViewPager.MetalViewHolder(itemView) { + + override fun onClick(v: View?) { + val albumPairs = arrayOf>(Pair.create(image, activity.resources.getString(R.string.transition_album_art))) + NavigationUtil.goToAlbum(activity, dataSet[adapterPosition].id, *albumPairs) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/album/HorizontalAlbumAdapter.java b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/album/HorizontalAlbumAdapter.java deleted file mode 100644 index 1d28988e..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/album/HorizontalAlbumAdapter.java +++ /dev/null @@ -1,83 +0,0 @@ -package code.name.monkey.retromusic.ui.adapter.album; - -import android.graphics.drawable.Drawable; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; -import android.view.View; -import android.view.ViewGroup; - -import com.bumptech.glide.Glide; - -import java.util.ArrayList; - -import code.name.monkey.appthemehelper.util.ColorUtil; -import code.name.monkey.appthemehelper.util.MaterialValueHelper; -import code.name.monkey.retromusic.glide.RetroMusicColoredTarget; -import code.name.monkey.retromusic.glide.SongGlideRequest; -import code.name.monkey.retromusic.helper.HorizontalAdapterHelper; -import code.name.monkey.retromusic.interfaces.CabHolder; -import code.name.monkey.retromusic.model.Album; -import code.name.monkey.retromusic.util.MusicUtil; - - -public class HorizontalAlbumAdapter extends AlbumAdapter { - public static final String TAG = AlbumAdapter.class.getSimpleName(); - - public HorizontalAlbumAdapter(@NonNull AppCompatActivity activity, ArrayList dataSet, boolean usePalette, @Nullable CabHolder cabHolder) { - super(activity, dataSet, HorizontalAdapterHelper.LAYOUT_RES, usePalette, cabHolder); - } - - @Override - protected ViewHolder createViewHolder(View view, int viewType) { - ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) view.getLayoutParams(); - HorizontalAdapterHelper.applyMarginToLayoutParams(activity, params, viewType); - return new ViewHolder(view); - } - - @Override - protected void setColors(int color, ViewHolder holder) { - if (holder.itemView != null) { - if (holder.title != null) { - holder.title.setTextColor(MaterialValueHelper.getPrimaryTextColor(activity, ColorUtil.isColorLight(color))); - } - if (holder.text != null) { - holder.text.setTextColor(MaterialValueHelper.getSecondaryTextColor(activity, ColorUtil.isColorLight(color))); - } - } - } - - @Override - protected void loadAlbumCover(Album album, final ViewHolder holder) { - if (holder.image == null) return; - - SongGlideRequest.Builder.from(Glide.with(activity), album.safeGetFirstSong()) - .checkIgnoreMediaStore(activity) - .generatePalette(activity).build() - .into(new RetroMusicColoredTarget(holder.image) { - @Override - public void onLoadCleared(Drawable placeholder) { - super.onLoadCleared(placeholder); - setColors(getAlbumArtistFooterColor(), holder); - } - - @Override - public void onColorReady(int color) { - if (usePalette) - setColors(color, holder); - else - setColors(getAlbumArtistFooterColor(), holder); - } - }); - } - - @Override - protected String getAlbumText(Album album) { - return MusicUtil.getYearString(album.getYear()); - } - - @Override - public int getItemViewType(int position) { - return HorizontalAdapterHelper.getItemViewtype(position, getItemCount()); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/album/HorizontalAlbumAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/album/HorizontalAlbumAdapter.kt new file mode 100644 index 00000000..84bb8f5c --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/album/HorizontalAlbumAdapter.kt @@ -0,0 +1,68 @@ +package code.name.monkey.retromusic.ui.adapter.album + +import android.graphics.drawable.Drawable +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.app.AppCompatActivity +import code.name.monkey.appthemehelper.util.ColorUtil +import code.name.monkey.appthemehelper.util.MaterialValueHelper +import code.name.monkey.retromusic.glide.RetroMusicColoredTarget +import code.name.monkey.retromusic.glide.SongGlideRequest +import code.name.monkey.retromusic.helper.HorizontalAdapterHelper +import code.name.monkey.retromusic.interfaces.CabHolder +import code.name.monkey.retromusic.model.Album +import code.name.monkey.retromusic.util.MusicUtil +import com.bumptech.glide.Glide +import java.util.* + + +class HorizontalAlbumAdapter(activity: AppCompatActivity, dataSet: ArrayList, usePalette: Boolean, cabHolder: CabHolder?) : AlbumAdapter(activity, dataSet, HorizontalAdapterHelper.LAYOUT_RES, usePalette, cabHolder) { + + override fun createViewHolder(view: View, viewType: Int): ViewHolder { + val params = view.layoutParams as ViewGroup.MarginLayoutParams + HorizontalAdapterHelper.applyMarginToLayoutParams(activity, params, viewType) + return ViewHolder(view) + } + + override fun setColors(color: Int, holder: ViewHolder) { + if (holder.title != null) { + holder.title!!.setTextColor(MaterialValueHelper.getPrimaryTextColor(activity, ColorUtil.isColorLight(color))) + } + if (holder.text != null) { + holder.text!!.setTextColor(MaterialValueHelper.getSecondaryTextColor(activity, ColorUtil.isColorLight(color))) + } + } + + override fun loadAlbumCover(album: Album, holder: ViewHolder) { + if (holder.image == null) return + + SongGlideRequest.Builder.from(Glide.with(activity), album.safeGetFirstSong()) + .checkIgnoreMediaStore(activity) + .generatePalette(activity).build() + .into(object : RetroMusicColoredTarget(holder.image!!) { + override fun onLoadCleared(placeholder: Drawable?) { + super.onLoadCleared(placeholder) + setColors(albumArtistFooterColor, holder) + } + + override fun onColorReady(color: Int) { + if (usePalette) + setColors(color, holder) + else + setColors(albumArtistFooterColor, holder) + } + }) + } + + override fun getAlbumText(album: Album): String? { + return MusicUtil.getYearString(album.year) + } + + override fun getItemViewType(position: Int): Int { + return HorizontalAdapterHelper.getItemViewtype(position, itemCount) + } + + companion object { + val TAG: String = AlbumAdapter::class.java.simpleName + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/artist/ArtistAdapter.java b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/artist/ArtistAdapter.java deleted file mode 100644 index ac08ec33..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/artist/ArtistAdapter.java +++ /dev/null @@ -1,201 +0,0 @@ -package code.name.monkey.retromusic.ui.adapter.artist; - -import android.content.res.ColorStateList; -import android.view.LayoutInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; - -import com.bumptech.glide.Glide; -import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView; - -import java.util.ArrayList; -import java.util.List; - -import androidx.annotation.LayoutRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; -import androidx.core.util.Pair; -import code.name.monkey.appthemehelper.util.ColorUtil; -import code.name.monkey.appthemehelper.util.MaterialValueHelper; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.glide.ArtistGlideRequest; -import code.name.monkey.retromusic.glide.RetroMusicColoredTarget; -import code.name.monkey.retromusic.helper.menu.SongsMenuHelper; -import code.name.monkey.retromusic.interfaces.CabHolder; -import code.name.monkey.retromusic.model.Artist; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.ui.adapter.base.AbsMultiSelectAdapter; -import code.name.monkey.retromusic.ui.adapter.base.MediaEntryViewHolder; -import code.name.monkey.retromusic.util.MusicUtil; -import code.name.monkey.retromusic.util.NavigationUtil; - - -public class ArtistAdapter extends AbsMultiSelectAdapter implements FastScrollRecyclerView.SectionedAdapter { - - protected final AppCompatActivity activity; - protected ArrayList dataSet; - - protected int itemLayoutRes; - - protected boolean usePalette = false; - - - public ArtistAdapter(@NonNull AppCompatActivity activity, ArrayList dataSet, - @LayoutRes int itemLayoutRes, boolean usePalette, @Nullable CabHolder cabHolder) { - super(activity, cabHolder, R.menu.menu_media_selection); - this.activity = activity; - this.dataSet = dataSet; - this.itemLayoutRes = itemLayoutRes; - this.usePalette = usePalette; - //setHasStableIds(true); - } - - public ArtistAdapter(@NonNull AppCompatActivity activity, ArrayList dataSet, - @LayoutRes int itemLayoutRes) { - super(activity, null, R.menu.menu_media_selection); - this.activity = activity; - this.dataSet = dataSet; - this.itemLayoutRes = itemLayoutRes; - } - - public void swapDataSet(ArrayList dataSet) { - this.dataSet = dataSet; - notifyDataSetChanged(); - } - - public ArrayList getDataSet() { - return dataSet; - } - - public void usePalette(boolean usePalette) { - this.usePalette = usePalette; - notifyDataSetChanged(); - } - - @Override - public long getItemId(int position) { - return dataSet.get(position).getId(); - } - - @Override - public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - View view = LayoutInflater.from(activity).inflate(itemLayoutRes, parent, false); - return createViewHolder(view); - } - - protected ViewHolder createViewHolder(View view) { - return new ViewHolder(view); - } - - @Override - public void onBindViewHolder(@NonNull final ViewHolder holder, int position) { - final Artist artist = dataSet.get(position); - - boolean isChecked = isChecked(artist); - holder.itemView.setActivated(isChecked); - - if (holder.title != null) { - holder.title.setText(artist.getName()); - } - if (holder.text != null) { - holder.text.setVisibility(View.GONE); - } - if (holder.shortSeparator != null) { - holder.shortSeparator.setVisibility(View.VISIBLE); - } - loadArtistImage(artist, holder); - } - - protected void setColors(int color, ViewHolder holder) { - if (holder.paletteColorContainer != null) { - holder.paletteColorContainer.setBackgroundColor(color); - if (holder.title != null) { - holder.title.setTextColor( - MaterialValueHelper.getPrimaryTextColor(activity, ColorUtil.isColorLight(color))); - } - } - if (holder.mask != null) { - holder.mask.setBackgroundTintList(ColorStateList.valueOf(color)); - } - } - - private void loadArtistImage(Artist artist, final ViewHolder holder) { - if (holder.image == null) { - return; - } - ArtistGlideRequest.Builder.from(Glide.with(activity), artist) - .generatePalette(activity).build() - .into(new RetroMusicColoredTarget(holder.image) { - @Override - public void onColorReady(int color) { - setColors(color, holder); - } - }); - } - - @Override - public int getItemCount() { - return dataSet.size(); - } - - @Override - protected Artist getIdentifier(int position) { - return dataSet.get(position); - } - - @Override - protected String getName(Artist artist) { - return artist.getName(); - } - - @Override - protected void onMultipleItemAction(@NonNull MenuItem menuItem, - @NonNull ArrayList selection) { - SongsMenuHelper.handleMenuClick(activity, getSongList(selection), menuItem.getItemId()); - } - - @NonNull - private ArrayList getSongList(@NonNull List artists) { - final ArrayList songs = new ArrayList<>(); - for (Artist artist : artists) { - songs.addAll(artist.getSongs()); // maybe async in future? - } - return songs; - } - - @NonNull - @Override - public String getSectionName(int position) { - return MusicUtil.getSectionName(dataSet.get(position).getName()); - } - - public class ViewHolder extends MediaEntryViewHolder { - - public ViewHolder(@NonNull View itemView) { - super(itemView); - setImageTransitionName(activity.getString(R.string.transition_artist_image)); - if (menu != null) { - menu.setVisibility(View.GONE); - } - } - - @Override - public void onClick(View v) { - if (isInQuickSelectMode()) { - toggleChecked(getAdapterPosition()); - } else { - Pair[] artistPairs = new Pair[]{Pair.create(image, - activity.getResources().getString(R.string.transition_artist_image))}; - NavigationUtil.goToArtist(activity, dataSet.get(getAdapterPosition()).getId(), artistPairs); - } - } - - @Override - public boolean onLongClick(View view) { - toggleChecked(getAdapterPosition()); - return true; - } - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/artist/ArtistAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/artist/ArtistAdapter.kt new file mode 100644 index 00000000..876b244e --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/artist/ArtistAdapter.kt @@ -0,0 +1,157 @@ +package code.name.monkey.retromusic.ui.adapter.artist + +import android.content.res.ColorStateList +import android.view.LayoutInflater +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import androidx.annotation.LayoutRes +import androidx.appcompat.app.AppCompatActivity +import androidx.core.util.Pair +import code.name.monkey.appthemehelper.util.ColorUtil +import code.name.monkey.appthemehelper.util.MaterialValueHelper +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.glide.ArtistGlideRequest +import code.name.monkey.retromusic.glide.RetroMusicColoredTarget +import code.name.monkey.retromusic.helper.menu.SongsMenuHelper +import code.name.monkey.retromusic.interfaces.CabHolder +import code.name.monkey.retromusic.model.Artist +import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.ui.adapter.base.AbsMultiSelectAdapter +import code.name.monkey.retromusic.ui.adapter.base.MediaEntryViewHolder +import code.name.monkey.retromusic.util.MusicUtil +import code.name.monkey.retromusic.util.NavigationUtil +import com.bumptech.glide.Glide +import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView +import java.util.* + + +class ArtistAdapter(val activity: AppCompatActivity, + var dataSet: ArrayList, + @LayoutRes var itemLayoutRes: Int, + var usePalette: Boolean, + cabHolder: CabHolder?) : AbsMultiSelectAdapter(activity, cabHolder, R.menu.menu_media_selection), FastScrollRecyclerView.SectionedAdapter { + + fun swapDataSet(dataSet: ArrayList) { + this.dataSet = dataSet + notifyDataSetChanged() + } + + fun usePalette(usePalette: Boolean) { + this.usePalette = usePalette + notifyDataSetChanged() + } + + override fun getItemId(position: Int): Long { + return dataSet[position].id.toLong() + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + val view = LayoutInflater.from(activity).inflate(itemLayoutRes, parent, false) + return createViewHolder(view) + } + + protected fun createViewHolder(view: View): ViewHolder { + return ViewHolder(view) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val artist = dataSet[position] + + val isChecked = isChecked(artist) + holder.itemView.isActivated = isChecked + + if (holder.title != null) { + holder.title!!.text = artist.name + } + if (holder.text != null) { + holder.text!!.visibility = View.GONE + } + if (holder.shortSeparator != null) { + holder.shortSeparator!!.visibility = View.VISIBLE + } + loadArtistImage(artist, holder) + } + + fun setColors(color: Int, holder: ViewHolder) { + if (holder.paletteColorContainer != null) { + holder.paletteColorContainer!!.setBackgroundColor(color) + if (holder.title != null) { + holder.title!!.setTextColor( + MaterialValueHelper.getPrimaryTextColor(activity, ColorUtil.isColorLight(color))) + } + } + if (holder.mask != null) { + holder.mask!!.backgroundTintList = ColorStateList.valueOf(color) + } + } + + private fun loadArtistImage(artist: Artist, holder: ViewHolder) { + if (holder.image == null) { + return + } + ArtistGlideRequest.Builder.from(Glide.with(activity), artist) + .generatePalette(activity).build() + .into(object : RetroMusicColoredTarget(holder.image!!) { + override fun onColorReady(color: Int) { + setColors(color, holder) + } + }) + } + + override fun getItemCount(): Int { + return dataSet.size + } + + override fun getIdentifier(position: Int): Artist? { + return dataSet[position] + } + + override fun getName(artist: Artist): String { + return artist.name + } + + override fun onMultipleItemAction(menuItem: MenuItem, + selection: ArrayList) { + SongsMenuHelper.handleMenuClick(activity, getSongList(selection), menuItem.itemId) + } + + private fun getSongList(artists: List): ArrayList { + val songs = ArrayList() + for (artist in artists) { + songs.addAll(artist.songs) // maybe async in future? + } + return songs + } + + override fun getSectionName(position: Int): String { + return MusicUtil.getSectionName(dataSet[position].name) + } + + inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) { + + init { + setImageTransitionName(activity.getString(R.string.transition_artist_image)) + if (menu != null) { + menu!!.visibility = View.GONE + } + } + + override fun onClick(v: View?) { + super.onClick(v) + if (isInQuickSelectMode) { + toggleChecked(adapterPosition) + } else { + val artistPairs = arrayOf>(Pair.create(image, + activity.resources.getString(R.string.transition_artist_image))) + NavigationUtil.goToArtist(activity, dataSet[adapterPosition].id, *artistPairs) + } + } + + override fun onLongClick(v: View?): Boolean { + toggleChecked(adapterPosition) + return super.onLongClick(v) + } + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/base/MediaEntryViewHolder.java b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/base/MediaEntryViewHolder.java deleted file mode 100644 index 249f52a3..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/base/MediaEntryViewHolder.java +++ /dev/null @@ -1,101 +0,0 @@ -package code.name.monkey.retromusic.ui.adapter.base; - -import android.os.Build; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageButton; -import android.widget.ImageView; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.cardview.widget.CardView; -import androidx.recyclerview.widget.RecyclerView; -import butterknife.BindView; -import butterknife.ButterKnife; -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.retromusic.R; - - -public class MediaEntryViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener { - - @Nullable - @BindView(R.id.image) - public ImageView image; - @Nullable - @BindView(R.id.image_text) - public TextView imageText; - @Nullable - @BindView(R.id.title) - public TextView title; - @Nullable - @BindView(R.id.text) - public TextView text; - @Nullable - @BindView(R.id.image_container) - public ViewGroup imageContainer; - @Nullable - @BindView(R.id.image_container_card) - public CardView imageContainerCard; - @Nullable - @BindView(R.id.menu) - public View menu; - @Nullable - @BindView(R.id.separator) - public View separator; - @Nullable - @BindView(R.id.short_separator) - public View shortSeparator; - @Nullable - @BindView(R.id.drag_view) - public View dragView; - @Nullable - @BindView(R.id.palette_color_container) - public View paletteColorContainer; - @BindView(R.id.time) - @Nullable - public TextView time; - @BindView(R.id.recycler_view) - @Nullable - public RecyclerView recyclerView; - @BindView(R.id.play_songs) - @Nullable - public ImageButton playSongs; - @BindView(R.id.mask) - @Nullable - public View mask; - @BindView(R.id.image_text_container) - @Nullable - public CardView imageTextContainer; - - - public MediaEntryViewHolder(View itemView) { - super(itemView); - ButterKnife.bind(this, itemView); - - itemView.setOnClickListener(this); - itemView.setOnLongClickListener(this); - - if (imageTextContainer != null) { - imageTextContainer.setCardBackgroundColor(ThemeStore.primaryColor(itemView.getContext())); - } - if (imageContainerCard != null) { - imageContainerCard.setCardBackgroundColor(ThemeStore.primaryColor(itemView.getContext())); - } - } - - protected void setImageTransitionName(@NonNull String transitionName) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && image != null) { - image.setTransitionName(transitionName); - } - } - - @Override - public boolean onLongClick(View v) { - return false; - } - - @Override - public void onClick(View v) { - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/base/MediaEntryViewHolder.kt b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/base/MediaEntryViewHolder.kt new file mode 100644 index 00000000..bac037a1 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/base/MediaEntryViewHolder.kt @@ -0,0 +1,80 @@ +package code.name.monkey.retromusic.ui.adapter.base + +import android.os.Build +import android.view.View +import android.view.ViewGroup +import android.widget.ImageButton +import android.widget.ImageView +import android.widget.TextView +import androidx.cardview.widget.CardView +import androidx.recyclerview.widget.RecyclerView +import code.name.monkey.appthemehelper.ThemeStore +import code.name.monkey.retromusic.R + +open class MediaEntryViewHolder(w: View) : RecyclerView.ViewHolder(w), View.OnClickListener, View.OnLongClickListener { + override fun onLongClick(v: View?): Boolean { + return false + } + + override fun onClick(v: View?) { + + } + + var image: ImageView? = null + var imageText: TextView? = null + var title: TextView? = null + var text: TextView? = null + var imageContainer: ViewGroup? = null + var imageContainerCard: CardView? = null + var menu: View? = null + var separator: View? = null + var shortSeparator: View? = null + var dragView: View? = null + var paletteColorContainer: View? = null + var time: TextView? = null + var recyclerView: RecyclerView? = null + var playSongs: ImageButton? = null + var mask: View? = null + var imageTextContainer: CardView? = null + + init { + title = w.findViewById(R.id.title) + text = w.findViewById(R.id.text) + + image = w.findViewById(R.id.image) + imageContainer = w.findViewById(R.id.image_container) + imageTextContainer = w.findViewById(R.id.image_text_container) + imageContainerCard = w.findViewById(R.id.image_container_card) + + imageText = w.findViewById(R.id.image_text) + + menu = w.findViewById(R.id.menu) + dragView = w.findViewById(R.id.drag_view) + + separator = w.findViewById(R.id.separator) + shortSeparator = w.findViewById(R.id.short_separator) + paletteColorContainer = w.findViewById(R.id.palette_color_container) + + time = w.findViewById(R.id.time); + recyclerView = w.findViewById(R.id.recycler_view) + + mask = w.findViewById(R.id.mask) + playSongs = w.findViewById(R.id.play_songs) + + w.setOnClickListener(this) + w.setOnLongClickListener(this) + + if (imageTextContainer != null) { + imageTextContainer!!.setCardBackgroundColor(ThemeStore.primaryColor(itemView.context)) + } + if (imageContainerCard != null) { + imageContainerCard!!.setCardBackgroundColor(ThemeStore.primaryColor(itemView.context)) + } + } + + fun setImageTransitionName(transitionName: String) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && image != null) { + image!!.transitionName = transitionName + } + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/playlist/AddToPlaylist.java b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/playlist/AddToPlaylist.java deleted file mode 100644 index 112a42a6..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/playlist/AddToPlaylist.java +++ /dev/null @@ -1,69 +0,0 @@ -package code.name.monkey.retromusic.ui.adapter.playlist; - -import android.app.Activity; -import android.app.Dialog; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import java.util.ArrayList; - -import androidx.annotation.NonNull; -import androidx.recyclerview.widget.RecyclerView; -import code.name.monkey.retromusic.model.Playlist; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.ui.adapter.base.MediaEntryViewHolder; -import code.name.monkey.retromusic.ui.adapter.playlist.AddToPlaylist.ViewHolder; -import code.name.monkey.retromusic.util.PlaylistsUtil; - -public class AddToPlaylist extends RecyclerView.Adapter { - - private Activity activity; - private ArrayList playlists; - private int itemLayoutRes; - private ArrayList songs; - private Dialog dialog; - - public AddToPlaylist(Activity activity, - ArrayList playlists, int itemLayoutRes, - ArrayList songs, Dialog dialog) { - this.activity = activity; - this.playlists = playlists; - this.itemLayoutRes = itemLayoutRes; - this.songs = songs; - this.dialog = dialog; - } - - @NonNull - @Override - public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - return new ViewHolder(LayoutInflater.from(activity).inflate(itemLayoutRes, parent, false)); - } - - @Override - public void onBindViewHolder(@NonNull ViewHolder holder, int position) { - Playlist playlist = playlists.get(position); - if (holder.title != null) { - holder.title.setText(playlist.name); - } - } - - @Override - public int getItemCount() { - return playlists.size(); - } - - public class ViewHolder extends MediaEntryViewHolder { - - public ViewHolder(View itemView) { - super(itemView); - } - - @Override - public void onClick(View v) { - super.onClick(v); - PlaylistsUtil.addToPlaylist(activity, songs, playlists.get(getAdapterPosition()).id, true); - dialog.dismiss(); - } - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/playlist/AddToPlaylist.kt b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/playlist/AddToPlaylist.kt new file mode 100644 index 00000000..f85746ee --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/playlist/AddToPlaylist.kt @@ -0,0 +1,44 @@ +package code.name.monkey.retromusic.ui.adapter.playlist + +import android.app.Activity +import android.app.Dialog +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup + +import java.util.ArrayList +import androidx.recyclerview.widget.RecyclerView +import code.name.monkey.retromusic.model.Playlist +import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.ui.adapter.base.MediaEntryViewHolder +import code.name.monkey.retromusic.ui.adapter.playlist.AddToPlaylist.ViewHolder +import code.name.monkey.retromusic.util.PlaylistsUtil + +class AddToPlaylist(private val activity: Activity, + private val playlists: ArrayList, private val itemLayoutRes: Int, + private val songs: ArrayList, private val dialog: Dialog) : RecyclerView.Adapter() { + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return ViewHolder(LayoutInflater.from(activity).inflate(itemLayoutRes, parent, false)) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val playlist = playlists[position] + if (holder.title != null) { + holder.title!!.text = playlist.name + } + } + + override fun getItemCount(): Int { + return playlists.size + } + + inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) { + + override fun onClick(v: View?) { + super.onClick(v) + PlaylistsUtil.addToPlaylist(activity, songs, playlists[adapterPosition].id, true) + dialog.dismiss() + } + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/playlist/PlaylistAdapter.java b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/playlist/PlaylistAdapter.java deleted file mode 100755 index 945800fe..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/playlist/PlaylistAdapter.java +++ /dev/null @@ -1,297 +0,0 @@ -package code.name.monkey.retromusic.ui.adapter.playlist; - -import android.graphics.Bitmap; -import android.graphics.PorterDuff; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; - -import com.bumptech.glide.Glide; - -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.concurrent.ExecutionException; - -import androidx.annotation.LayoutRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.widget.PopupMenu; -import butterknife.ButterKnife; -import code.name.monkey.appthemehelper.util.ATHUtil; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.dialogs.ClearSmartPlaylistDialog; -import code.name.monkey.retromusic.dialogs.DeletePlaylistDialog; -import code.name.monkey.retromusic.helper.menu.PlaylistMenuHelper; -import code.name.monkey.retromusic.helper.menu.SongsMenuHelper; -import code.name.monkey.retromusic.interfaces.CabHolder; -import code.name.monkey.retromusic.loaders.PlaylistSongsLoader; -import code.name.monkey.retromusic.model.AbsCustomPlaylist; -import code.name.monkey.retromusic.model.Playlist; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.model.smartplaylist.AbsSmartPlaylist; -import code.name.monkey.retromusic.model.smartplaylist.LastAddedPlaylist; -import code.name.monkey.retromusic.ui.adapter.base.AbsMultiSelectAdapter; -import code.name.monkey.retromusic.ui.adapter.base.MediaEntryViewHolder; -import code.name.monkey.retromusic.util.MusicUtil; -import code.name.monkey.retromusic.util.NavigationUtil; -import code.name.monkey.retromusic.util.RetroUtil; -import io.reactivex.Observable; - -public class PlaylistAdapter extends AbsMultiSelectAdapter { - - public static final String TAG = PlaylistAdapter.class.getSimpleName(); - - private static final int SMART_PLAYLIST = 0; - private static final int DEFAULT_PLAYLIST = 1; - - protected final AppCompatActivity activity; - protected ArrayList dataSet; - protected int itemLayoutRes; - private ArrayList mSongs = new ArrayList<>(); - - - public PlaylistAdapter(AppCompatActivity activity, ArrayList dataSet, - @LayoutRes int itemLayoutRes, @Nullable CabHolder cabHolder) { - super(activity, cabHolder, R.menu.menu_playlists_selection); - this.activity = activity; - this.dataSet = dataSet; - this.itemLayoutRes = itemLayoutRes; - setHasStableIds(true); - } - - public ArrayList getDataSet() { - return dataSet; - } - - public void swapDataSet(ArrayList dataSet) { - this.dataSet = dataSet; - notifyDataSetChanged(); - } - - @Override - public long getItemId(int position) { - return dataSet.get(position).id; - } - - @NonNull - @Override - public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - View view = LayoutInflater.from(activity) - .inflate(itemLayoutRes, parent, false); - return createViewHolder(view, viewType); - } - - protected ViewHolder createViewHolder(View view, int viewType) { - return new ViewHolder(view, viewType); - } - - @Override - public void onBindViewHolder(@NonNull ViewHolder holder, int position) { - /* if (getItemViewType(position) == SMART_PLAYLIST) { - if (holder.viewList != null) { - holder.viewList.get(0).setOnClickListener( - v -> NavigationUtil.goToPlaylistNew(activity, new HistoryPlaylist(activity))); - holder.viewList.get(1).setOnClickListener( - v -> NavigationUtil.goToPlaylistNew(activity, new LastAddedPlaylist(activity))); - holder.viewList.get(2).setOnClickListener( - v -> NavigationUtil.goToPlaylistNew(activity, new MyTopTracksPlaylist(activity))); - } - return; - }*/ - final Playlist playlist = dataSet.get(position); - ArrayList songs = getSongs(playlist); - holder.itemView.setActivated(isChecked(playlist)); - - if (holder.title != null) { - holder.title.setText(playlist.name); - } - if (holder.text != null) { - holder.text.setText(String.format(Locale.getDefault(), "%d Songs", songs.size())); - } - if (holder.image != null) { - holder.image.setImageResource(getIconRes(playlist)); - } - if (holder.getAdapterPosition() == getItemCount() - 1) { - if (holder.shortSeparator != null) { - holder.shortSeparator.setVisibility(View.GONE); - } - } else { - if (holder.shortSeparator != null && !(dataSet.get(position) instanceof AbsSmartPlaylist)) { - holder.shortSeparator.setVisibility(View.VISIBLE); - } - } - } - - private int getIconRes(Playlist playlist) { - if (playlist instanceof AbsSmartPlaylist) { - return ((AbsSmartPlaylist) playlist).iconRes; - } - return MusicUtil.isFavoritePlaylist(activity, playlist) ? R.drawable.ic_favorite_white_24dp - : R.drawable.ic_playlist_play_white_24dp; - } - - @Override - public int getItemViewType(int position) { - return dataSet.get(position) instanceof AbsSmartPlaylist ? SMART_PLAYLIST : DEFAULT_PLAYLIST; - } - - @Override - public int getItemCount() { - return dataSet.size(); - } - - @Override - protected Playlist getIdentifier(int position) { - return dataSet.get(position); - } - - @Override - protected String getName(Playlist playlist) { - return playlist.name; - } - - @Override - protected void onMultipleItemAction(@NonNull MenuItem menuItem, - @NonNull ArrayList selection) { - switch (menuItem.getItemId()) { - case R.id.action_delete_playlist: - for (int i = 0; i < selection.size(); i++) { - Playlist playlist = selection.get(i); - if (playlist instanceof AbsSmartPlaylist) { - AbsSmartPlaylist absSmartPlaylist = (AbsSmartPlaylist) playlist; - ClearSmartPlaylistDialog.create(absSmartPlaylist) - .show(activity.getSupportFragmentManager(), - "CLEAR_PLAYLIST_" + absSmartPlaylist.name); - selection.remove(playlist); - i--; - } - } - if (selection.size() > 0) { - DeletePlaylistDialog.create(selection) - .show(activity.getSupportFragmentManager(), "DELETE_PLAYLIST"); - } - break; - default: - SongsMenuHelper.handleMenuClick(activity, getSongList(selection), menuItem.getItemId()); - break; - } - } - - public ArrayList getSongs() { - return mSongs; - } - - public void setSongs(ArrayList songs) { - mSongs = songs; - } - - @NonNull - private ArrayList getSongList(@NonNull List playlists) { - final ArrayList songs = new ArrayList<>(); - for (Playlist playlist : playlists) { - if (playlist instanceof AbsCustomPlaylist) { - songs.addAll(((AbsCustomPlaylist) playlist).getSongs(activity).blockingFirst()); - //((AbsCustomPlaylist) playlist).getSongs(activity).subscribe(this::setSongs); - } else { - songs - .addAll(PlaylistSongsLoader.getPlaylistSongList(activity, playlist.id).blockingFirst()); - } - } - return songs; - } - - @Nullable - private ArrayList getSongs(@NonNull Playlist playlist) { - final ArrayList songs = new ArrayList<>(); - if (playlist instanceof AbsSmartPlaylist) { - songs.addAll(((AbsSmartPlaylist) playlist).getSongs(activity).blockingFirst()); - } else { - songs.addAll(PlaylistSongsLoader.getPlaylistSongList(activity, playlist.id).blockingFirst()); - } - return songs; - } - - private Observable> loadBitmaps(@NonNull ArrayList songs) { - return Observable.create(e -> { - ArrayList bitmaps = new ArrayList(); - for (Song song : songs) { - try { - Bitmap bitmap = Glide.with(activity) - .load(RetroUtil.getAlbumArtUri(song.albumId)) - .asBitmap() - .into(500, 500) - .get(); - if (bitmap != null) { - Log.i(TAG, "loadBitmaps: has"); - bitmaps.add(bitmap); - } - if (bitmaps.size() == 4) { - break; - } - } catch (InterruptedException | ExecutionException ex) { - ex.printStackTrace(); - } - } - e.onNext(bitmaps); - e.onComplete(); - }); - } - - public class ViewHolder extends MediaEntryViewHolder { - public ViewHolder(@NonNull View itemView, int itemViewType) { - super(itemView); - ButterKnife.bind(this, itemView); - if (image != null) { - int iconPadding = activity.getResources() - .getDimensionPixelSize(R.dimen.list_item_image_icon_padding); - image.setPadding(iconPadding, iconPadding, iconPadding, iconPadding); - image.setColorFilter(ATHUtil.resolveColor(activity, R.attr.iconColor), - PorterDuff.Mode.SRC_IN); - } - if (menu != null) { - menu.setOnClickListener(view -> { - final Playlist playlist = dataSet.get(getAdapterPosition()); - final PopupMenu popupMenu = new PopupMenu(activity, view); - popupMenu.inflate(getItemViewType() == SMART_PLAYLIST ? R.menu.menu_item_smart_playlist - : R.menu.menu_item_playlist); - if (playlist instanceof LastAddedPlaylist) { - popupMenu.getMenu().findItem(R.id.action_clear_playlist).setVisible(false); - } - popupMenu.setOnMenuItemClickListener(item -> { - if (item.getItemId() == R.id.action_clear_playlist) { - if (playlist instanceof AbsSmartPlaylist) { - ClearSmartPlaylistDialog.create((AbsSmartPlaylist) playlist) - .show(activity.getSupportFragmentManager(), - "CLEAR_SMART_PLAYLIST_" + playlist.name); - return true; - } - } - return PlaylistMenuHelper.handleMenuClick( - activity, dataSet.get(getAdapterPosition()), item); - }); - popupMenu.show(); - }); - } - } - - @Override - public void onClick(View view) { - if (isInQuickSelectMode()) { - toggleChecked(getAdapterPosition()); - } else { - Playlist playlist = dataSet.get(getAdapterPosition()); - NavigationUtil.goToPlaylistNew(activity, playlist); - } - } - - @Override - public boolean onLongClick(View view) { - toggleChecked(getAdapterPosition()); - return true; - } - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/playlist/PlaylistAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/playlist/PlaylistAdapter.kt new file mode 100755 index 00000000..67a3f52e --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/playlist/PlaylistAdapter.kt @@ -0,0 +1,268 @@ +package code.name.monkey.retromusic.ui.adapter.playlist + +import android.graphics.Bitmap +import android.graphics.PorterDuff +import android.util.Log +import android.view.LayoutInflater +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup +import androidx.annotation.LayoutRes +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.widget.PopupMenu +import butterknife.ButterKnife +import code.name.monkey.appthemehelper.util.ATHUtil +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.dialogs.ClearSmartPlaylistDialog +import code.name.monkey.retromusic.dialogs.DeletePlaylistDialog +import code.name.monkey.retromusic.helper.menu.PlaylistMenuHelper +import code.name.monkey.retromusic.helper.menu.SongsMenuHelper +import code.name.monkey.retromusic.interfaces.CabHolder +import code.name.monkey.retromusic.loaders.PlaylistSongsLoader +import code.name.monkey.retromusic.model.AbsCustomPlaylist +import code.name.monkey.retromusic.model.Playlist +import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.model.smartplaylist.AbsSmartPlaylist +import code.name.monkey.retromusic.model.smartplaylist.LastAddedPlaylist +import code.name.monkey.retromusic.ui.adapter.base.AbsMultiSelectAdapter +import code.name.monkey.retromusic.ui.adapter.base.MediaEntryViewHolder +import code.name.monkey.retromusic.util.MusicUtil +import code.name.monkey.retromusic.util.NavigationUtil +import code.name.monkey.retromusic.util.RetroUtil +import com.bumptech.glide.Glide +import io.reactivex.Observable +import java.util.* +import java.util.concurrent.ExecutionException + +class PlaylistAdapter(protected val activity: AppCompatActivity, dataSet: ArrayList, + @param:LayoutRes protected var itemLayoutRes: Int, cabHolder: CabHolder?) : AbsMultiSelectAdapter(activity, cabHolder, R.menu.menu_playlists_selection) { + var dataSet: ArrayList + protected set + var songs = ArrayList() + + + init { + this.dataSet = dataSet + setHasStableIds(true) + } + + fun swapDataSet(dataSet: ArrayList) { + this.dataSet = dataSet + notifyDataSetChanged() + } + + override fun getItemId(position: Int): Long { + return dataSet[position].id.toLong() + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + val view = LayoutInflater.from(activity) + .inflate(itemLayoutRes, parent, false) + return createViewHolder(view) + } + + protected fun createViewHolder(view: View): ViewHolder { + return ViewHolder(view) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + /* if (getItemViewType(position) == SMART_PLAYLIST) { + if (holder.viewList != null) { + holder.viewList.get(0).setOnClickListener( + v -> NavigationUtil.goToPlaylistNew(activity, new HistoryPlaylist(activity))); + holder.viewList.get(1).setOnClickListener( + v -> NavigationUtil.goToPlaylistNew(activity, new LastAddedPlaylist(activity))); + holder.viewList.get(2).setOnClickListener( + v -> NavigationUtil.goToPlaylistNew(activity, new MyTopTracksPlaylist(activity))); + } + return; + }*/ + val playlist = dataSet[position] + val songs = getSongs(playlist) + holder.itemView.isActivated = isChecked(playlist) + + if (holder.title != null) { + holder.title!!.text = playlist.name + } + if (holder.text != null) { + holder.text!!.text = String.format(Locale.getDefault(), "%d Songs", songs!!.size) + } + if (holder.image != null) { + holder.image!!.setImageResource(getIconRes(playlist)) + } + if (holder.adapterPosition == itemCount - 1) { + if (holder.shortSeparator != null) { + holder.shortSeparator!!.visibility = View.GONE + } + } else { + if (holder.shortSeparator != null && dataSet[position] !is AbsSmartPlaylist) { + holder.shortSeparator!!.visibility = View.VISIBLE + } + } + } + + private fun getIconRes(playlist: Playlist): Int { + if (playlist is AbsSmartPlaylist) { + return playlist.iconRes + } + return if (MusicUtil.isFavoritePlaylist(activity, playlist)) + R.drawable.ic_favorite_white_24dp + else + R.drawable.ic_playlist_play_white_24dp + } + + override fun getItemViewType(position: Int): Int { + return if (dataSet[position] is AbsSmartPlaylist) SMART_PLAYLIST else DEFAULT_PLAYLIST + } + + override fun getItemCount(): Int { + return dataSet.size + } + + override fun getIdentifier(position: Int): Playlist? { + return dataSet[position] + } + + override fun getName(playlist: Playlist): String { + return playlist.name + } + + override fun onMultipleItemAction(menuItem: MenuItem, + selection: ArrayList) { + when (menuItem.itemId) { + R.id.action_delete_playlist -> { + var i = 0 + while (i < selection.size) { + val playlist = selection[i] + if (playlist is AbsSmartPlaylist) { + ClearSmartPlaylistDialog.create(playlist) + .show(activity.supportFragmentManager, + "CLEAR_PLAYLIST_" + playlist.name) + selection.remove(playlist) + i-- + } + i++ + } + if (selection.size > 0) { + DeletePlaylistDialog.create(selection) + .show(activity.supportFragmentManager, "DELETE_PLAYLIST") + } + } + else -> SongsMenuHelper.handleMenuClick(activity, getSongList(selection), menuItem.itemId) + } + } + + private fun getSongList(playlists: List): ArrayList { + val songs = ArrayList() + for (playlist in playlists) { + if (playlist is AbsCustomPlaylist) { + songs.addAll(playlist.getSongs(activity).blockingFirst()) + //((AbsCustomPlaylist) playlist).getSongs(activity).subscribe(this::setSongs); + } else { + songs + .addAll(PlaylistSongsLoader.getPlaylistSongList(activity, playlist.id).blockingFirst()) + } + } + return songs + } + + private fun getSongs(playlist: Playlist): ArrayList? { + val songs = ArrayList() + if (playlist is AbsSmartPlaylist) { + songs.addAll(playlist.getSongs(activity).blockingFirst()) + } else { + songs.addAll(PlaylistSongsLoader.getPlaylistSongList(activity, playlist.id).blockingFirst()) + } + return songs + } + + private fun loadBitmaps(songs: ArrayList): Observable> { + return Observable.create { e -> + val bitmaps = ArrayList() + for (song in songs) { + try { + val bitmap = Glide.with(activity) + .load(RetroUtil.getAlbumArtUri(song.albumId.toLong())) + .asBitmap() + .into(500, 500) + .get() + if (bitmap != null) { + Log.i(TAG, "loadBitmaps: has") + bitmaps.add(bitmap) + } + if (bitmaps.size == 4) { + break + } + } catch (ex: InterruptedException) { + ex.printStackTrace() + } catch (ex: ExecutionException) { + ex.printStackTrace() + } + + } + e.onNext(bitmaps) + e.onComplete() + } + } + + inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) { + init { + ButterKnife.bind(this, itemView) + if (image != null) { + val iconPadding = activity.resources + .getDimensionPixelSize(R.dimen.list_item_image_icon_padding) + image!!.setPadding(iconPadding, iconPadding, iconPadding, iconPadding) + image!!.setColorFilter(ATHUtil.resolveColor(activity, R.attr.iconColor), + PorterDuff.Mode.SRC_IN) + } + if (menu != null) { + menu!!.setOnClickListener { view -> + val playlist = dataSet[adapterPosition] + val popupMenu = PopupMenu(activity, view) + popupMenu.inflate(if (getItemViewType() == SMART_PLAYLIST) + R.menu.menu_item_smart_playlist + else + R.menu.menu_item_playlist) + if (playlist is LastAddedPlaylist) { + popupMenu.menu.findItem(R.id.action_clear_playlist).isVisible = false + } + popupMenu.setOnMenuItemClickListener { item -> + if (item.itemId == R.id.action_clear_playlist) { + if (playlist is AbsSmartPlaylist) { + ClearSmartPlaylistDialog.create(playlist) + .show(activity.supportFragmentManager, + "CLEAR_SMART_PLAYLIST_" + playlist.name) + return@setOnMenuItemClickListener true + } + } + PlaylistMenuHelper.handleMenuClick( + activity, dataSet[adapterPosition], item) + } + popupMenu.show() + } + } + } + + override fun onClick(v: View?) { + if (isInQuickSelectMode) { + toggleChecked(adapterPosition) + } else { + val playlist = dataSet[adapterPosition] + NavigationUtil.goToPlaylistNew(activity, playlist) + } + } + + override fun onLongClick(v: View?): Boolean { + toggleChecked(adapterPosition) + return true + } + } + + companion object { + + val TAG: String = PlaylistAdapter::class.java.simpleName + + private const val SMART_PLAYLIST = 0 + private const val DEFAULT_PLAYLIST = 1 + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/AbsOffsetSongAdapter.java b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/AbsOffsetSongAdapter.java deleted file mode 100644 index dfe89524..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/AbsOffsetSongAdapter.java +++ /dev/null @@ -1,110 +0,0 @@ -package code.name.monkey.retromusic.ui.adapter.song; - -import androidx.annotation.LayoutRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import java.util.ArrayList; - -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.interfaces.CabHolder; -import code.name.monkey.retromusic.model.Song; - - -public abstract class AbsOffsetSongAdapter extends SongAdapter { - - protected static final int OFFSET_ITEM = 0; - protected static final int SONG = 1; - - public AbsOffsetSongAdapter(AppCompatActivity activity, ArrayList dataSet, @LayoutRes int itemLayoutRes, boolean usePalette, @Nullable CabHolder cabHolder) { - super(activity, dataSet, itemLayoutRes, usePalette, cabHolder); - } - - public AbsOffsetSongAdapter(AppCompatActivity activity, ArrayList dataSet, @LayoutRes int itemLayoutRes, boolean usePalette, @Nullable CabHolder cabHolder, boolean showSectionName) { - super(activity, dataSet, itemLayoutRes, usePalette, cabHolder, showSectionName); - } - - @NonNull - @Override - public SongAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - if (viewType == OFFSET_ITEM) { - View view = LayoutInflater.from(activity).inflate(R.layout.item_list_single_row, parent, false); - return createViewHolder(view); - } - return super.onCreateViewHolder(parent, viewType); - } - - @Override - protected SongAdapter.ViewHolder createViewHolder(View view) { - return new AbsOffsetSongAdapter.ViewHolder(view); - } - - @Override - public long getItemId(int position) { - position--; - if (position < 0) return -2; - return super.getItemId(position); - } - - @Nullable - @Override - protected Song getIdentifier(int position) { - position--; - if (position < 0) return null; - return super.getIdentifier(position); - } - - @Override - public int getItemCount() { - int superItemCount = super.getItemCount(); - return superItemCount == 0 ? 0 : superItemCount + 1; - } - - @Override - public int getItemViewType(int position) { - return position == 0 ? OFFSET_ITEM : SONG; - } - - @NonNull - @Override - public String getSectionName(int position) { - position--; - if (position < 0) return ""; - return super.getSectionName(position); - } - - public class ViewHolder extends SongAdapter.ViewHolder { - - public ViewHolder(@NonNull View itemView) { - super(itemView); - } - - @Override - protected Song getSong() { - if (getItemViewType() == OFFSET_ITEM) - return Song.EMPTY_SONG; // could also return null, just to be safe return empty song - return dataSet.get(getAdapterPosition() - 1); - } - - @Override - public void onClick(View v) { - if (isInQuickSelectMode() && getItemViewType() != OFFSET_ITEM) { - toggleChecked(getAdapterPosition()); - } else { - MusicPlayerRemote.openQueue(dataSet, getAdapterPosition() - 1, true); - } - } - - @Override - public boolean onLongClick(View view) { - if (getItemViewType() == OFFSET_ITEM) return false; - toggleChecked(getAdapterPosition()); - return true; - } - } -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/AbsOffsetSongAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/AbsOffsetSongAdapter.kt new file mode 100644 index 00000000..4329ea2b --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/AbsOffsetSongAdapter.kt @@ -0,0 +1,85 @@ +package code.name.monkey.retromusic.ui.adapter.song + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.annotation.LayoutRes +import androidx.appcompat.app.AppCompatActivity +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.helper.MusicPlayerRemote +import code.name.monkey.retromusic.interfaces.CabHolder +import code.name.monkey.retromusic.model.Song +import java.util.* + + +abstract class AbsOffsetSongAdapter : SongAdapter { + + constructor(activity: AppCompatActivity, dataSet: ArrayList, @LayoutRes itemLayoutRes: Int, usePalette: Boolean, cabHolder: CabHolder?) : super(activity, dataSet, itemLayoutRes, usePalette, cabHolder) + + constructor(activity: AppCompatActivity, dataSet: ArrayList, @LayoutRes itemLayoutRes: Int, usePalette: Boolean, cabHolder: CabHolder?, showSectionName: Boolean) : super(activity, dataSet, itemLayoutRes, usePalette, cabHolder, showSectionName) {} + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SongAdapter.ViewHolder { + if (viewType == OFFSET_ITEM) { + val view = LayoutInflater.from(activity).inflate(R.layout.item_list_single_row, parent, false) + return createViewHolder(view) + } + return super.onCreateViewHolder(parent, viewType) + } + + override fun createViewHolder(view: View): SongAdapter.ViewHolder { + return ViewHolder(view) + } + + override fun getItemId(position: Int): Long { + var positionFinal = position + positionFinal-- + return if (positionFinal < 0) -2 else super.getItemId(positionFinal) + } + + override fun getIdentifier(position: Int): Song? { + var positionFinal = position + positionFinal-- + return if (positionFinal < 0) null else super.getIdentifier(positionFinal) + } + + override fun getItemCount(): Int { + val superItemCount = super.getItemCount() + return if (superItemCount == 0) 0 else superItemCount + 1 + } + + override fun getItemViewType(position: Int): Int { + return if (position == 0) OFFSET_ITEM else SONG + } + + override fun getSectionName(position: Int): String { + var positionFinal = position + positionFinal-- + return if (position < 0) "" else super.getSectionName(positionFinal) + } + + open inner class ViewHolder(itemView: View) : SongAdapter.ViewHolder(itemView) { + + override// could also return null, just to be safe return empty song + val song: Song + get() = if (itemViewType == OFFSET_ITEM) Song.EMPTY_SONG else dataSet[adapterPosition - 1] + + override fun onClick(v: View?) { + if (isInQuickSelectMode && itemViewType != OFFSET_ITEM) { + toggleChecked(adapterPosition) + } else { + MusicPlayerRemote.openQueue(dataSet, adapterPosition - 1, true) + } + } + + override fun onLongClick(v: View?): Boolean { + if (itemViewType == OFFSET_ITEM) return false + toggleChecked(adapterPosition) + return true + } + } + + companion object { + const val OFFSET_ITEM = 0 + const val SONG = 1 + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/OrderablePlaylistSongAdapter.java b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/OrderablePlaylistSongAdapter.java deleted file mode 100644 index a5887904..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/OrderablePlaylistSongAdapter.java +++ /dev/null @@ -1,145 +0,0 @@ -package code.name.monkey.retromusic.ui.adapter.song; - -import androidx.annotation.LayoutRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; -import android.view.MenuItem; -import android.view.View; - -import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemAdapter; -import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemViewHolder; -import com.h6ah4i.android.widget.advrecyclerview.draggable.ItemDraggableRange; -import com.h6ah4i.android.widget.advrecyclerview.draggable.annotation.DraggableItemStateFlags; - -import java.util.ArrayList; -import java.util.List; - -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.dialogs.RemoveFromPlaylistDialog; -import code.name.monkey.retromusic.interfaces.CabHolder; -import code.name.monkey.retromusic.model.PlaylistSong; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.util.ViewUtil; - - -@SuppressWarnings("unchecked") -public class OrderablePlaylistSongAdapter extends PlaylistSongAdapter - implements DraggableItemAdapter { - - public static final String TAG = OrderablePlaylistSongAdapter.class.getSimpleName(); - - private OnMoveItemListener onMoveItemListener; - - public OrderablePlaylistSongAdapter(@NonNull AppCompatActivity activity, - @NonNull ArrayList dataSet, - @LayoutRes int itemLayoutRes, - boolean usePalette, - @Nullable CabHolder cabHolder, - @Nullable OnMoveItemListener onMoveItemListener) { - super(activity, (ArrayList) (List) dataSet, itemLayoutRes, usePalette, cabHolder); - setMultiSelectMenuRes(R.menu.menu_playlists_songs_selection); - this.onMoveItemListener = onMoveItemListener; - } - - @Override - protected SongAdapter.ViewHolder createViewHolder(View view) { - return new ViewHolder(view); - } - - @Override - public long getItemId(int position) { - position--; - if (position < 0) return -2; - return ((ArrayList) (List) dataSet).get(position).idInPlayList; // important! - } - - @Override - protected void onMultipleItemAction(@NonNull MenuItem menuItem, @NonNull ArrayList selection) { - switch (menuItem.getItemId()) { - case R.id.action_remove_from_playlist: - RemoveFromPlaylistDialog.create((ArrayList) (List) selection).show(activity.getSupportFragmentManager(), "ADD_PLAYLIST"); - return; - } - super.onMultipleItemAction(menuItem, selection); - } - - @Override - public boolean onCheckCanStartDrag(ViewHolder holder, int position, int x, int y) { - return onMoveItemListener != null && position > 0 && - (ViewUtil.hitTest(holder.dragView, x, y) || ViewUtil.hitTest(holder.image, x, y)); - } - - @Override - public ItemDraggableRange onGetItemDraggableRange(ViewHolder holder, int position) { - return new ItemDraggableRange(1, dataSet.size()); - } - - @Override - public void onMoveItem(int fromPosition, int toPosition) { - if (onMoveItemListener != null && fromPosition != toPosition) { - onMoveItemListener.onMoveItem(fromPosition - 1, toPosition - 1); - } - } - - @Override - public boolean onCheckCanDrop(int draggingPosition, int dropPosition) { - return dropPosition > 0; - } - - @Override - public void onItemDragStarted(int position) { - notifyDataSetChanged(); - } - - @Override - public void onItemDragFinished(int fromPosition, int toPosition, boolean result) { - notifyDataSetChanged(); - } - - public interface OnMoveItemListener { - void onMoveItem(int fromPosition, int toPosition); - } - - public class ViewHolder extends PlaylistSongAdapter.ViewHolder implements DraggableItemViewHolder { - @DraggableItemStateFlags - private int mDragStateFlags; - - public ViewHolder(@NonNull View itemView) { - super(itemView); - if (dragView != null) { - if (onMoveItemListener != null) { - dragView.setVisibility(View.VISIBLE); - } else { - dragView.setVisibility(View.GONE); - } - } - } - - @Override - protected int getSongMenuRes() { - return R.menu.menu_item_playlist_song; - } - - @Override - protected boolean onSongMenuItemClick(MenuItem item) { - switch (item.getItemId()) { - case R.id.action_remove_from_playlist: - RemoveFromPlaylistDialog.create((PlaylistSong) getSong()).show(activity.getSupportFragmentManager(), "REMOVE_FROM_PLAYLIST"); - return true; - } - return super.onSongMenuItemClick(item); - } - - @Override - @DraggableItemStateFlags - public int getDragStateFlags() { - return mDragStateFlags; - } - - @Override - public void setDragStateFlags(@DraggableItemStateFlags int flags) { - mDragStateFlags = flags; - } - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/OrderablePlaylistSongAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/OrderablePlaylistSongAdapter.kt new file mode 100644 index 00000000..a55a548f --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/OrderablePlaylistSongAdapter.kt @@ -0,0 +1,136 @@ +package code.name.monkey.retromusic.ui.adapter.song + +import android.view.MenuItem +import android.view.View +import androidx.annotation.LayoutRes +import androidx.appcompat.app.AppCompatActivity +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.dialogs.RemoveFromPlaylistDialog +import code.name.monkey.retromusic.interfaces.CabHolder +import code.name.monkey.retromusic.model.PlaylistSong +import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.util.ViewUtil +import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemAdapter +import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemViewHolder +import com.h6ah4i.android.widget.advrecyclerview.draggable.ItemDraggableRange +import com.h6ah4i.android.widget.advrecyclerview.draggable.annotation.DraggableItemStateFlags + + +class OrderablePlaylistSongAdapter(activity: AppCompatActivity, + dataSet: ArrayList, + @LayoutRes itemLayoutRes: Int, + usePalette: Boolean, + cabHolder: CabHolder?, + private val onMoveItemListener: OnMoveItemListener?) : PlaylistSongAdapter(activity, dataSet, itemLayoutRes, usePalette, cabHolder), DraggableItemAdapter { + + init { + setMultiSelectMenuRes(R.menu.menu_playlists_songs_selection) + } + + override fun createViewHolder(view: View): SongAdapter.ViewHolder { + return ViewHolder(view) + } + + override fun getItemId(position: Int): Long { + var positionFinal = position + positionFinal-- + + var long: Long = 0 + if (position < 0) { + long = -2 + } else { + if (dataSet[positionFinal] is PlaylistSong) { + long = (dataSet[positionFinal] as PlaylistSong).idInPlayList.toLong() + } + } + return long + } + + override fun onMultipleItemAction(menuItem: MenuItem, selection: ArrayList) { + when (menuItem.itemId) { + R.id.action_remove_from_playlist -> { + val songs = ArrayList() + songs.addAll(songs) + RemoveFromPlaylistDialog.create(songs).show(activity.supportFragmentManager, "ADD_PLAYLIST") + return + } + } + super.onMultipleItemAction(menuItem, selection) + } + + override fun onCheckCanStartDrag(holder: ViewHolder, position: Int, x: Int, y: Int): Boolean { + return onMoveItemListener != null && position > 0 && + (ViewUtil.hitTest(holder.dragView, x, y) || ViewUtil.hitTest(holder.image, x, y)) + } + + override fun onGetItemDraggableRange(holder: ViewHolder, position: Int): ItemDraggableRange { + return ItemDraggableRange(1, dataSet.size) + } + + override fun onMoveItem(fromPosition: Int, toPosition: Int) { + if (onMoveItemListener != null && fromPosition != toPosition) { + onMoveItemListener.onMoveItem(fromPosition - 1, toPosition - 1) + } + } + + override fun onCheckCanDrop(draggingPosition: Int, dropPosition: Int): Boolean { + return dropPosition > 0 + } + + override fun onItemDragStarted(position: Int) { + notifyDataSetChanged() + } + + override fun onItemDragFinished(fromPosition: Int, toPosition: Int, result: Boolean) { + notifyDataSetChanged() + } + + interface OnMoveItemListener { + fun onMoveItem(fromPosition: Int, toPosition: Int) + } + + inner class ViewHolder(itemView: View) : PlaylistSongAdapter.ViewHolder(itemView), DraggableItemViewHolder { + @DraggableItemStateFlags + private var mDragStateFlags: Int = 0 + + override var songMenuRes: Int + get() = R.menu.menu_item_playlist_song + set(value: Int) { + super.songMenuRes = value + } + + init { + if (dragView != null) { + if (onMoveItemListener != null) { + dragView!!.visibility = View.VISIBLE + } else { + dragView!!.visibility = View.GONE + } + } + } + + override fun onSongMenuItemClick(item: MenuItem): Boolean { + when (item.itemId) { + R.id.action_remove_from_playlist -> { + RemoveFromPlaylistDialog.create(song as PlaylistSong).show(activity.supportFragmentManager, "REMOVE_FROM_PLAYLIST") + return true + } + } + return super.onSongMenuItemClick(item) + } + + @DraggableItemStateFlags + override fun getDragStateFlags(): Int { + return mDragStateFlags + } + + override fun setDragStateFlags(@DraggableItemStateFlags flags: Int) { + mDragStateFlags = flags + } + } + + companion object { + + val TAG = OrderablePlaylistSongAdapter::class.java.simpleName + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/PlayingQueueAdapter.java b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/PlayingQueueAdapter.java deleted file mode 100644 index 9c98f5f7..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/PlayingQueueAdapter.java +++ /dev/null @@ -1,215 +0,0 @@ -package code.name.monkey.retromusic.ui.adapter.song; - -import android.graphics.Color; -import android.graphics.PorterDuff; -import android.view.MenuItem; -import android.view.View; -import android.widget.ImageView; - -import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemAdapter; -import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemViewHolder; -import com.h6ah4i.android.widget.advrecyclerview.draggable.ItemDraggableRange; -import com.h6ah4i.android.widget.advrecyclerview.draggable.annotation.DraggableItemStateFlags; - -import java.util.ArrayList; - -import androidx.annotation.ColorInt; -import androidx.annotation.LayoutRes; -import androidx.annotation.NonNull; -import androidx.appcompat.app.AppCompatActivity; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.util.MusicUtil; -import code.name.monkey.retromusic.util.ViewUtil; - - -public class PlayingQueueAdapter extends SongAdapter implements DraggableItemAdapter { - - private static final int HISTORY = 0; - private static final int CURRENT = 1; - private static final int UP_NEXT = 2; - - private int current; - private int color = -1; - - public PlayingQueueAdapter(AppCompatActivity activity, ArrayList dataSet, int current, - @LayoutRes int itemLayoutRes) { - super(activity, dataSet, itemLayoutRes, false, null); - this.current = current; - } - - public PlayingQueueAdapter(AppCompatActivity activity, ArrayList dataSet, int current, - @LayoutRes int itemLayoutRes, @ColorInt int color) { - super(activity, dataSet, itemLayoutRes, false, null); - this.current = current; - this.color = color; - } - - @Override - protected SongAdapter.ViewHolder createViewHolder(View view) { - return new ViewHolder(view); - } - - @Override - public void onBindViewHolder(@NonNull SongAdapter.ViewHolder holder, int position) { - super.onBindViewHolder(holder, position); - if (holder.imageText != null) { - holder.imageText.setText(String.valueOf(position - current)); - } - if (holder.time != null) { - holder.time.setText(MusicUtil.getReadableDurationString(getDataSet().get(position).duration)); - } - if (holder.getItemViewType() == HISTORY || holder.getItemViewType() == CURRENT) { - setAlpha(holder, 0.5f); - } - if (usePalette) { - setColor(holder, Color.WHITE); - } - } - - private void setColor(SongAdapter.ViewHolder holder, int white) { - - if (holder.title != null) { - holder.title.setTextColor(white); - if (color != -1) { - holder.title.setTextColor(color); - } - } - if (holder.text != null) { - holder.text.setTextColor(white); - } - if (holder.time != null) { - holder.time.setTextColor(white); - } - if (holder.imageText != null) { - holder.imageText.setTextColor(white); - } - if (holder.menu != null) { - ((ImageView) holder.menu).setColorFilter(white, PorterDuff.Mode.SRC_IN); - } - } - - @Override - public void usePalette(boolean color) { - super.usePalette(color); - usePalette = color; - notifyDataSetChanged(); - } - - @Override - public int getItemViewType(int position) { - if (position < current) { - return HISTORY; - } else if (position > current) { - return UP_NEXT; - } - return CURRENT; - } - - @Override - protected void loadAlbumCover(Song song, SongAdapter.ViewHolder holder) { - // We don't want to load it in this adapter - } - - public void swapDataSet(ArrayList dataSet, int position) { - this.dataSet = dataSet; - current = position; - notifyDataSetChanged(); - } - - public void setCurrent(int current) { - this.current = current; - notifyDataSetChanged(); - } - - private void setAlpha(SongAdapter.ViewHolder holder, float alpha) { - if (holder.image != null) { - holder.image.setAlpha(alpha); - } - if (holder.title != null) { - holder.title.setAlpha(alpha); - } - if (holder.text != null) { - holder.text.setAlpha(alpha); - } - if (holder.imageText != null) { - holder.imageText.setAlpha(alpha); - } - if (holder.paletteColorContainer != null) { - holder.paletteColorContainer.setAlpha(alpha); - } - } - - @Override - public boolean onCheckCanStartDrag(ViewHolder holder, int position, int x, int y) { - return ViewUtil.hitTest(holder.imageText, x, y) || (ViewUtil.hitTest(holder.dragView, x, y)); - } - - @Override - public ItemDraggableRange onGetItemDraggableRange(ViewHolder holder, int position) { - return null; - } - - @Override - public void onMoveItem(int fromPosition, int toPosition) { - MusicPlayerRemote.moveSong(fromPosition, toPosition); - } - - @Override - public boolean onCheckCanDrop(int draggingPosition, int dropPosition) { - return true; - } - - @Override - public void onItemDragStarted(int position) { - notifyDataSetChanged(); - } - - @Override - public void onItemDragFinished(int fromPosition, int toPosition, boolean result) { - notifyDataSetChanged(); - } - - public class ViewHolder extends SongAdapter.ViewHolder implements DraggableItemViewHolder { - - @DraggableItemStateFlags - private int mDragStateFlags; - - public ViewHolder(@NonNull View itemView) { - super(itemView); - if (imageText != null) { - imageText.setVisibility(View.VISIBLE); - } - if (dragView != null) { - dragView.setVisibility(View.VISIBLE); - } - } - - @Override - protected int getSongMenuRes() { - return R.menu.menu_item_playing_queue_song; - } - - @Override - protected boolean onSongMenuItemClick(MenuItem item) { - switch (item.getItemId()) { - case R.id.action_remove_from_playing_queue: - MusicPlayerRemote.removeFromQueue(getAdapterPosition()); - return true; - } - return super.onSongMenuItemClick(item); - } - - @Override - @DraggableItemStateFlags - public int getDragStateFlags() { - return mDragStateFlags; - } - - @Override - public void setDragStateFlags(@DraggableItemStateFlags int flags) { - mDragStateFlags = flags; - } - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/PlayingQueueAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/PlayingQueueAdapter.kt new file mode 100644 index 00000000..ee92dcc8 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/PlayingQueueAdapter.kt @@ -0,0 +1,199 @@ +package code.name.monkey.retromusic.ui.adapter.song + +import android.graphics.Color +import android.graphics.PorterDuff +import android.view.MenuItem +import android.view.View +import android.widget.ImageView +import androidx.annotation.ColorInt +import androidx.annotation.LayoutRes +import androidx.appcompat.app.AppCompatActivity +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.helper.MusicPlayerRemote +import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.util.MusicUtil +import code.name.monkey.retromusic.util.ViewUtil +import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemAdapter +import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemViewHolder +import com.h6ah4i.android.widget.advrecyclerview.draggable.ItemDraggableRange +import com.h6ah4i.android.widget.advrecyclerview.draggable.annotation.DraggableItemStateFlags +import java.util.* + + +class PlayingQueueAdapter : SongAdapter, DraggableItemAdapter { + + private var current: Int = 0 + private var color = -1 + + constructor(activity: AppCompatActivity, dataSet: ArrayList, current: Int, + @LayoutRes itemLayoutRes: Int) : super(activity, dataSet, itemLayoutRes, false, null) { + this.current = current + } + + constructor(activity: AppCompatActivity, dataSet: ArrayList, current: Int, + @LayoutRes itemLayoutRes: Int, @ColorInt color: Int) : super(activity, dataSet, itemLayoutRes, false, null) { + this.current = current + this.color = color + } + + override fun createViewHolder(view: View): SongAdapter.ViewHolder { + return ViewHolder(view) + } + + override fun onBindViewHolder(holder: SongAdapter.ViewHolder, position: Int) { + super.onBindViewHolder(holder, position) + if (holder.imageText != null) { + holder.imageText!!.text = (position - current).toString() + } + if (holder.time != null) { + holder.time!!.text = MusicUtil.getReadableDurationString(dataSet[position].duration) + } + if (holder.itemViewType == HISTORY || holder.itemViewType == CURRENT) { + setAlpha(holder, 0.5f) + } + if (usePalette) { + setColor(holder, Color.WHITE) + } + } + + private fun setColor(holder: SongAdapter.ViewHolder, white: Int) { + + if (holder.title != null) { + holder.title!!.setTextColor(white) + if (color != -1) { + holder.title!!.setTextColor(color) + } + } + if (holder.text != null) { + holder.text!!.setTextColor(white) + } + if (holder.time != null) { + holder.time!!.setTextColor(white) + } + if (holder.imageText != null) { + holder.imageText!!.setTextColor(white) + } + if (holder.menu != null) { + (holder.menu as ImageView).setColorFilter(white, PorterDuff.Mode.SRC_IN) + } + } + + override fun usePalette(usePalette: Boolean) { + super.usePalette(usePalette) + this.usePalette = usePalette + notifyDataSetChanged() + } + + override fun getItemViewType(position: Int): Int { + if (position < current) { + return HISTORY + } else if (position > current) { + return UP_NEXT + } + return CURRENT + } + + override fun loadAlbumCover(song: Song, holder: SongAdapter.ViewHolder) { + // We don't want to load it in this adapter + } + + fun swapDataSet(dataSet: ArrayList, position: Int) { + this.dataSet = dataSet + current = position + notifyDataSetChanged() + } + + fun setCurrent(current: Int) { + this.current = current + notifyDataSetChanged() + } + + private fun setAlpha(holder: SongAdapter.ViewHolder, alpha: Float) { + if (holder.image != null) { + holder.image!!.alpha = alpha + } + if (holder.title != null) { + holder.title!!.alpha = alpha + } + if (holder.text != null) { + holder.text!!.alpha = alpha + } + if (holder.imageText != null) { + holder.imageText!!.alpha = alpha + } + if (holder.paletteColorContainer != null) { + holder.paletteColorContainer!!.alpha = alpha + } + } + + override fun onCheckCanStartDrag(holder: ViewHolder, position: Int, x: Int, y: Int): Boolean { + return ViewUtil.hitTest(holder.imageText, x, y) || ViewUtil.hitTest(holder.dragView, x, y) + } + + override fun onGetItemDraggableRange(holder: ViewHolder, position: Int): ItemDraggableRange? { + return null + } + + override fun onMoveItem(fromPosition: Int, toPosition: Int) { + MusicPlayerRemote.moveSong(fromPosition, toPosition) + } + + override fun onCheckCanDrop(draggingPosition: Int, dropPosition: Int): Boolean { + return true + } + + override fun onItemDragStarted(position: Int) { + notifyDataSetChanged() + } + + override fun onItemDragFinished(fromPosition: Int, toPosition: Int, result: Boolean) { + notifyDataSetChanged() + } + + inner class ViewHolder(itemView: View) : SongAdapter.ViewHolder(itemView), DraggableItemViewHolder { + + @DraggableItemStateFlags + private var mDragStateFlags: Int = 0 + + override var songMenuRes: Int + get() = R.menu.menu_item_playing_queue_song + set(value: Int) { + super.songMenuRes = value + } + + init { + if (imageText != null) { + imageText!!.visibility = View.VISIBLE + } + if (dragView != null) { + dragView!!.visibility = View.VISIBLE + } + } + + override fun onSongMenuItemClick(item: MenuItem): Boolean { + when (item.itemId) { + R.id.action_remove_from_playing_queue -> { + MusicPlayerRemote.removeFromQueue(adapterPosition) + return true + } + } + return super.onSongMenuItemClick(item) + } + + @DraggableItemStateFlags + override fun getDragStateFlags(): Int { + return mDragStateFlags + } + + override fun setDragStateFlags(@DraggableItemStateFlags flags: Int) { + mDragStateFlags = flags + } + } + + companion object { + + private const val HISTORY = 0 + private const val CURRENT = 1 + private const val UP_NEXT = 2 + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/PlaylistSongAdapter.java b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/PlaylistSongAdapter.java deleted file mode 100644 index 14df6bfd..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/PlaylistSongAdapter.java +++ /dev/null @@ -1,93 +0,0 @@ -package code.name.monkey.retromusic.ui.adapter.song; - -import androidx.annotation.LayoutRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.util.Pair; -import androidx.appcompat.app.AppCompatActivity; -import android.view.MenuItem; -import android.view.View; - -import java.util.ArrayList; - -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.interfaces.CabHolder; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.util.MusicUtil; -import code.name.monkey.retromusic.util.NavigationUtil; - - -public class PlaylistSongAdapter extends AbsOffsetSongAdapter { - - public static final String TAG = PlaylistSongAdapter.class.getSimpleName(); - - public PlaylistSongAdapter(AppCompatActivity activity, @NonNull ArrayList dataSet, @LayoutRes int itemLayoutRes, boolean usePalette, @Nullable CabHolder cabHolder) { - super(activity, dataSet, itemLayoutRes, usePalette, cabHolder, false); - setMultiSelectMenuRes(R.menu.menu_cannot_delete_single_songs_playlist_songs_selection); - } - - @Override - protected SongAdapter.ViewHolder createViewHolder(View view) { - return new ViewHolder(view); - } - - @Override - public void onBindViewHolder(@NonNull final SongAdapter.ViewHolder holder, int position) { - if (holder.getItemViewType() == OFFSET_ITEM) { - int textColor = ThemeStore.textColorSecondary(activity); - if (holder.title != null) { - holder.title.setText(MusicUtil.getPlaylistInfoString(activity, dataSet)); - holder.title.setTextColor(textColor); - } - - - if (holder.text != null) { - holder.text.setVisibility(View.GONE); - } - if (holder.menu != null) { - holder.menu.setVisibility(View.GONE); - } - if (holder.image != null) { - final int padding = activity.getResources().getDimensionPixelSize(R.dimen.default_item_margin) / 2; - holder.image.setPadding(padding, padding, padding, padding); - holder.image.setColorFilter(textColor); - holder.image.setImageResource(R.drawable.ic_timer_white_24dp); - } - if (holder.dragView != null) { - holder.dragView.setVisibility(View.GONE); - } - if (holder.separator != null) { - holder.separator.setVisibility(View.VISIBLE); - } - if (holder.shortSeparator != null) { - holder.shortSeparator.setVisibility(View.GONE); - } - } else { - super.onBindViewHolder(holder, position - 1); - } - } - - public class ViewHolder extends AbsOffsetSongAdapter.ViewHolder { - public ViewHolder(@NonNull View itemView) { - super(itemView); - - } - - @Override - protected int getSongMenuRes() { - return R.menu.menu_item_cannot_delete_single_songs_playlist_song; - } - - @Override - protected boolean onSongMenuItemClick(MenuItem item) { - if (item.getItemId() == R.id.action_go_to_album) { - Pair[] albumPairs = new Pair[]{ - Pair.create(image, activity.getString(R.string.transition_album_art))}; - NavigationUtil.goToAlbum(activity, dataSet.get(getAdapterPosition() - 1).albumId, albumPairs); - return true; - } - return super.onSongMenuItemClick(item); - } - } -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/PlaylistSongAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/PlaylistSongAdapter.kt new file mode 100644 index 00000000..e9a2069a --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/PlaylistSongAdapter.kt @@ -0,0 +1,86 @@ +package code.name.monkey.retromusic.ui.adapter.song + +import android.view.MenuItem +import android.view.View +import android.widget.ImageView +import androidx.annotation.LayoutRes +import androidx.appcompat.app.AppCompatActivity +import androidx.core.util.Pair +import code.name.monkey.appthemehelper.ThemeStore +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.interfaces.CabHolder +import code.name.monkey.retromusic.model.PlaylistSong +import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.util.MusicUtil +import code.name.monkey.retromusic.util.NavigationUtil +import java.util.* + + +open class PlaylistSongAdapter(activity: AppCompatActivity, dataSet: ArrayList, @LayoutRes itemLayoutRes: Int, usePalette: Boolean, cabHolder: CabHolder?) : AbsOffsetSongAdapter(activity, dataSet, itemLayoutRes, usePalette, cabHolder, false) { + + init { + this.setMultiSelectMenuRes(R.menu.menu_cannot_delete_single_songs_playlist_songs_selection) + } + + override fun createViewHolder(view: View): SongAdapter.ViewHolder { + return ViewHolder(view) + } + + override fun onBindViewHolder(holder: SongAdapter.ViewHolder, position: Int) { + if (holder.itemViewType == AbsOffsetSongAdapter.OFFSET_ITEM) { + val textColor = ThemeStore.textColorSecondary(activity) + if (holder.title != null) { + holder.title!!.text = MusicUtil.getPlaylistInfoString(activity, dataSet) + holder.title!!.setTextColor(textColor) + } + + + if (holder.text != null) { + holder.text!!.visibility = View.GONE + } + if (holder.menu != null) { + holder.menu!!.visibility = View.GONE + } + if (holder.image != null) { + val padding = activity.resources.getDimensionPixelSize(R.dimen.default_item_margin) / 2 + holder.image!!.setPadding(padding, padding, padding, padding) + holder.image!!.setColorFilter(textColor) + holder.image!!.setImageResource(R.drawable.ic_timer_white_24dp) + } + if (holder.dragView != null) { + holder.dragView!!.visibility = View.GONE + } + if (holder.separator != null) { + holder.separator!!.visibility = View.VISIBLE + } + if (holder.shortSeparator != null) { + holder.shortSeparator!!.visibility = View.GONE + } + } else { + super.onBindViewHolder(holder, position - 1) + } + } + + open inner class ViewHolder(itemView: View) : AbsOffsetSongAdapter.ViewHolder(itemView) { + + override var songMenuRes: Int + get() = R.menu.menu_item_cannot_delete_single_songs_playlist_song + set(value) { + super.songMenuRes = value + } + + override fun onSongMenuItemClick(item: MenuItem): Boolean { + if (item.itemId == R.id.action_go_to_album) { + val albumPairs = arrayOf>(Pair.create(image, activity.getString(R.string.transition_album_art))) + NavigationUtil.goToAlbum(activity, dataSet[adapterPosition - 1].albumId, *albumPairs) + return true + } + return super.onSongMenuItemClick(item) + } + } + + companion object { + + val TAG = PlaylistSongAdapter::class.java.simpleName + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/ShuffleButtonSongAdapter.java b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/ShuffleButtonSongAdapter.java deleted file mode 100644 index c358cae6..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/ShuffleButtonSongAdapter.java +++ /dev/null @@ -1,80 +0,0 @@ -package code.name.monkey.retromusic.ui.adapter.song; - -import androidx.annotation.LayoutRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; -import android.view.View; - -import java.util.ArrayList; - -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.interfaces.CabHolder; -import code.name.monkey.retromusic.model.Song; - - -public class ShuffleButtonSongAdapter extends AbsOffsetSongAdapter { - - public ShuffleButtonSongAdapter(AppCompatActivity activity, ArrayList dataSet, - @LayoutRes int itemLayoutRes, boolean usePalette, @Nullable CabHolder cabHolder) { - super(activity, dataSet, itemLayoutRes, usePalette, cabHolder); - } - - @Override - protected SongAdapter.ViewHolder createViewHolder(View view) { - return new ViewHolder(view); - } - - @Override - public void onBindViewHolder(@NonNull final SongAdapter.ViewHolder holder, int position) { - if (holder.getItemViewType() == OFFSET_ITEM) { - int accentColor = ThemeStore.accentColor(activity); - if (holder.title != null) { - holder.title.setText(activity.getResources().getString(R.string.action_shuffle_all)); - holder.title.setTextColor(accentColor); - /*((GradientTextView) holder.title).setLinearGradient(ThemeStore.accentColor(activity), - PhonographColorUtil.getMatColor(activity, "A400"), GradientTextView.LG_VERTICAL); - */ - } - if (holder.text != null) { - holder.text.setVisibility(View.GONE); - } - if (holder.menu != null) { - holder.menu.setVisibility(View.GONE); - } - if (holder.image != null) { - final int padding = - activity.getResources().getDimensionPixelSize(R.dimen.default_item_margin) / 2; - holder.image.setPadding(padding, padding, padding, padding); - holder.image.setColorFilter(accentColor); - holder.image.setImageResource(R.drawable.ic_shuffle_white_24dp); - } - if (holder.separator != null) { - holder.separator.setVisibility(View.VISIBLE); - } - if (holder.shortSeparator != null) { - holder.shortSeparator.setVisibility(View.GONE); - } - } else { - super.onBindViewHolder(holder, position - 1); - } - } - - public class ViewHolder extends AbsOffsetSongAdapter.ViewHolder { - - public ViewHolder(@NonNull View itemView) { - super(itemView); - } - - @Override - public void onClick(View v) { - if (getItemViewType() == OFFSET_ITEM) { - MusicPlayerRemote.openAndShuffleQueue(dataSet, true); - return; - } - super.onClick(v); - } - } -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/ShuffleButtonSongAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/ShuffleButtonSongAdapter.kt new file mode 100644 index 00000000..bd344dc6 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/ShuffleButtonSongAdapter.kt @@ -0,0 +1,64 @@ +package code.name.monkey.retromusic.ui.adapter.song + +import android.view.View +import androidx.annotation.LayoutRes +import androidx.appcompat.app.AppCompatActivity +import code.name.monkey.appthemehelper.ThemeStore +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.helper.MusicPlayerRemote +import code.name.monkey.retromusic.interfaces.CabHolder +import code.name.monkey.retromusic.model.Song +import java.util.* + + +class ShuffleButtonSongAdapter(activity: AppCompatActivity, + dataSet: ArrayList, + @LayoutRes itemLayoutRes: Int, + usePalette: Boolean, + cabHolder: CabHolder?) : AbsOffsetSongAdapter(activity, dataSet, itemLayoutRes, usePalette, cabHolder) { + + override fun createViewHolder(view: View): SongAdapter.ViewHolder { + return ViewHolder(view) + } + + override fun onBindViewHolder(holder: SongAdapter.ViewHolder, position: Int) { + if (holder.itemViewType == AbsOffsetSongAdapter.OFFSET_ITEM) { + val accentColor = ThemeStore.accentColor(activity.applicationContext) + if (holder.title != null) { + holder.title!!.text = activity.resources.getString(R.string.action_shuffle_all) + holder.title!!.setTextColor(accentColor) + } + if (holder.text != null) { + holder.text!!.visibility = View.GONE + } + if (holder.menu != null) { + holder.menu!!.visibility = View.GONE + } + if (holder.image != null) { + val padding = activity.resources.getDimensionPixelSize(R.dimen.default_item_margin) / 2 + holder.image!!.setPadding(padding, padding, padding, padding) + holder.image!!.setColorFilter(accentColor) + holder.image!!.setImageResource(R.drawable.ic_shuffle_white_24dp) + } + if (holder.separator != null) { + holder.separator!!.visibility = View.VISIBLE + } + if (holder.shortSeparator != null) { + holder.shortSeparator!!.visibility = View.GONE + } + } else { + super.onBindViewHolder(holder, position - 1) + } + } + + inner class ViewHolder(itemView: View) : AbsOffsetSongAdapter.ViewHolder(itemView) { + + override fun onClick(v: View?) { + if (itemViewType == AbsOffsetSongAdapter.OFFSET_ITEM) { + MusicPlayerRemote.openAndShuffleQueue(dataSet, true) + return + } + super.onClick(v) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/SimpleSongAdapter.java b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/SimpleSongAdapter.java deleted file mode 100755 index 25923cce..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/SimpleSongAdapter.java +++ /dev/null @@ -1,69 +0,0 @@ -package code.name.monkey.retromusic.ui.adapter.song; - -import androidx.annotation.LayoutRes; -import androidx.annotation.NonNull; -import androidx.appcompat.app.AppCompatActivity; -import android.view.LayoutInflater; -import android.view.ViewGroup; - -import java.util.ArrayList; - -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.appthemehelper.util.TintHelper; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.util.MusicUtil; - -/** - * Created by Monkey D Luffy on 3/31/2016. - */ -public class SimpleSongAdapter extends SongAdapter { - - private int textColor; - - public SimpleSongAdapter(AppCompatActivity context, ArrayList songs, @LayoutRes int i) { - super(context, songs, i, false, null); - textColor = ThemeStore.textColorPrimary(context); - } - - public void swapDataSet(ArrayList arrayList) { - this.dataSet.clear(); - this.dataSet = arrayList; - notifyDataSetChanged(); - } - - @NonNull - public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - return new ViewHolder(LayoutInflater.from(activity).inflate(itemLayoutRes, parent, false)); - } - - @Override - public void onBindViewHolder(@NonNull ViewHolder holder, int position) { - super.onBindViewHolder(holder, position); - int fixedTrackNumber = MusicUtil.getFixedTrackNumber(dataSet.get(position).trackNumber); - - if (holder.imageText != null) { - holder.imageText.setText(fixedTrackNumber > 0 ? String.valueOf(fixedTrackNumber) : "-"); - holder.imageText.setTextColor(textColor); - } - - if (holder.time != null) { - holder.time.setText(MusicUtil.getReadableDurationString(dataSet.get(position).duration)); - holder.time.setTextColor(textColor); - } - if (holder.title != null) { - holder.title.setTextColor(textColor); - } - if (holder.menu != null) { - TintHelper.setTintAuto(holder.menu, textColor, false); - } - } - - public int getItemCount() { - return dataSet.size(); - } - - public void setTextColor(int textColor) { - this.textColor = textColor; - notifyDataSetChanged(); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/SimpleSongAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/SimpleSongAdapter.kt new file mode 100755 index 00000000..2d308ce6 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/SimpleSongAdapter.kt @@ -0,0 +1,63 @@ +package code.name.monkey.retromusic.ui.adapter.song + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.annotation.LayoutRes +import androidx.appcompat.app.AppCompatActivity +import code.name.monkey.appthemehelper.ThemeStore +import code.name.monkey.appthemehelper.util.TintHelper +import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.util.MusicUtil +import java.util.* + + +class SimpleSongAdapter(context: AppCompatActivity, + songs: ArrayList, + @LayoutRes i: Int) : SongAdapter(context, songs, i, false, null) { + + private var textColor: Int = 0 + + init { + textColor = ThemeStore.textColorPrimary(context) + } + + override fun swapDataSet(dataSet: ArrayList) { + this.dataSet.clear() + this.dataSet = dataSet + notifyDataSetChanged() + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return ViewHolder(LayoutInflater.from(activity).inflate(itemLayoutRes, parent, false)) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + super.onBindViewHolder(holder, position) + val fixedTrackNumber = MusicUtil.getFixedTrackNumber(dataSet[position].trackNumber) + + if (holder.imageText != null) { + holder.imageText!!.text = if (fixedTrackNumber > 0) fixedTrackNumber.toString() else "-" + holder.imageText!!.setTextColor(textColor) + } + + if (holder.time != null) { + holder.time!!.text = MusicUtil.getReadableDurationString(dataSet[position].duration) + holder.time!!.setTextColor(textColor) + } + if (holder.title != null) { + holder.title!!.setTextColor(textColor) + } + if (holder.menu != null) { + TintHelper.setTintAuto(holder.menu!!, textColor, false) + } + } + + override fun getItemCount(): Int { + return dataSet.size + } + + fun setTextColor(textColor: Int) { + this.textColor = textColor + notifyDataSetChanged() + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/SongAdapter.java b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/SongAdapter.java deleted file mode 100644 index 85623fe6..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/SongAdapter.java +++ /dev/null @@ -1,295 +0,0 @@ -package code.name.monkey.retromusic.ui.adapter.song; - -import android.graphics.drawable.Drawable; -import android.view.LayoutInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; - -import com.afollestad.materialcab.MaterialCab; -import com.bumptech.glide.Glide; -import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView; - -import java.util.ArrayList; - -import androidx.annotation.LayoutRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; -import androidx.core.util.Pair; -import code.name.monkey.appthemehelper.util.ColorUtil; -import code.name.monkey.appthemehelper.util.MaterialValueHelper; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.glide.RetroMusicColoredTarget; -import code.name.monkey.retromusic.glide.SongGlideRequest; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.helper.SortOrder; -import code.name.monkey.retromusic.helper.menu.SongMenuHelper; -import code.name.monkey.retromusic.helper.menu.SongsMenuHelper; -import code.name.monkey.retromusic.interfaces.CabHolder; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.ui.adapter.base.AbsMultiSelectAdapter; -import code.name.monkey.retromusic.ui.adapter.base.MediaEntryViewHolder; -import code.name.monkey.retromusic.util.MusicUtil; -import code.name.monkey.retromusic.util.NavigationUtil; -import code.name.monkey.retromusic.util.PreferenceUtil; - -/** - * Created by hemanths on 13/08/17. - */ - -public class SongAdapter extends AbsMultiSelectAdapter - implements MaterialCab.Callback, FastScrollRecyclerView.SectionedAdapter { - - public static final String TAG = SongAdapter.class.getSimpleName(); - - protected final AppCompatActivity activity; - protected ArrayList dataSet; - - protected int itemLayoutRes; - - protected boolean usePalette = false; - private boolean showSectionName = true; - - - public SongAdapter(AppCompatActivity activity, ArrayList dataSet, - @LayoutRes int itemLayoutRes, boolean usePalette, @Nullable CabHolder cabHolder) { - this(activity, dataSet, itemLayoutRes, usePalette, cabHolder, true); - } - - public SongAdapter(AppCompatActivity activity, ArrayList dataSet, - @LayoutRes int itemLayoutRes, boolean usePalette, @Nullable CabHolder cabHolder, - boolean showSectionName) { - super(activity, cabHolder, R.menu.menu_media_selection); - this.activity = activity; - this.dataSet = dataSet; - this.itemLayoutRes = itemLayoutRes; - this.usePalette = usePalette; - this.showSectionName = showSectionName; - setHasStableIds(true); - } - - public void swapDataSet(ArrayList dataSet) { - this.dataSet = dataSet; - notifyDataSetChanged(); - } - - public void usePalette(boolean usePalette) { - this.usePalette = usePalette; - notifyDataSetChanged(); - } - - public ArrayList getDataSet() { - return dataSet; - } - - @Override - public long getItemId(int position) { - return dataSet.get(position).id; - } - - @NonNull - @Override - public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - View view = LayoutInflater.from(activity).inflate(itemLayoutRes, parent, false); - return createViewHolder(view); - } - - protected ViewHolder createViewHolder(View view) { - return new ViewHolder(view); - } - - @Override - public void onBindViewHolder(@NonNull final ViewHolder holder, int position) { - final Song song = dataSet.get(position); - - boolean isChecked = isChecked(song); - holder.itemView.setActivated(isChecked); - - if (holder.getAdapterPosition() == getItemCount() - 1) { - if (holder.shortSeparator != null) { - holder.shortSeparator.setVisibility(View.GONE); - } - } else { - if (holder.shortSeparator != null) { - holder.shortSeparator.setVisibility(View.VISIBLE); - } - } - - if (holder.title != null) { - holder.title.setText(getSongTitle(song)); - } - if (holder.text != null) { - holder.text.setText(getSongText(song)); - } - - loadAlbumCover(song, holder); - - } - - private void setColors(int color, ViewHolder holder) { - if (holder.paletteColorContainer != null) { - holder.paletteColorContainer.setBackgroundColor(color); - if (holder.title != null) { - holder.title.setTextColor( - MaterialValueHelper.getPrimaryTextColor(activity, ColorUtil.isColorLight(color))); - } - if (holder.text != null) { - holder.text.setTextColor( - MaterialValueHelper.getSecondaryTextColor(activity, ColorUtil.isColorLight(color))); - } - } - } - - protected void loadAlbumCover(Song song, final ViewHolder holder) { - if (holder.image == null) { - return; - } - SongGlideRequest.Builder.from(Glide.with(activity), song) - .checkIgnoreMediaStore(activity) - .generatePalette(activity).build() - .into(new RetroMusicColoredTarget(holder.image) { - @Override - public void onLoadCleared(Drawable placeholder) { - super.onLoadCleared(placeholder); - setColors(getDefaultFooterColor(), holder); - } - - @Override - public void onColorReady(int color) { - setColors(color, holder); - } - }); - } - - private String getSongTitle(Song song) { - return song.title; - } - - private String getSongText(Song song) { - return song.artistName; - } - - @Override - public int getItemCount() { - return dataSet.size(); - } - - @Override - protected Song getIdentifier(int position) { - return dataSet.get(position); - } - - @Override - protected String getName(Song song) { - return song.title; - } - - @Override - protected void onMultipleItemAction(@NonNull MenuItem menuItem, - @NonNull ArrayList selection) { - SongsMenuHelper.handleMenuClick(activity, selection, menuItem.getItemId()); - } - - @NonNull - @Override - public String getSectionName(int position) { - if (!showSectionName) { - return ""; - } - @Nullable String sectionName = null; - switch (PreferenceUtil.getInstance().getSongSortOrder()) { - case SortOrder.SongSortOrder.SONG_A_Z: - case SortOrder.SongSortOrder.SONG_Z_A: - sectionName = dataSet.get(position).title; - break; - case SortOrder.SongSortOrder.SONG_ALBUM: - sectionName = dataSet.get(position).albumName; - break; - case SortOrder.SongSortOrder.SONG_ARTIST: - sectionName = dataSet.get(position).artistName; - break; - case SortOrder.SongSortOrder.SONG_YEAR: - return MusicUtil.getYearString(dataSet.get(position).year); - - } - - return MusicUtil.getSectionName(sectionName); - } - - public class ViewHolder extends MediaEntryViewHolder { - - int DEFAULT_MENU_RES = SongMenuHelper.MENU_RES; - - public ViewHolder(@NonNull View itemView) { - super(itemView); - setImageTransitionName(activity.getString(R.string.transition_album_art)); - - /*if (mItemView != null) { - mItemView.setOnMenuItemClickListener(new ListItemView.OnMenuItemClickListener() { - @Override - public void onActionMenuItemSelected(MenuItem item) { - SongMenuHelper.handleMenuClick(activity, dataSet.get(getAdapterPosition()), item.getItemId()); - } - }); - }*/ - - if (menu == null) { - return; - } - menu.setOnClickListener(new SongMenuHelper.OnClickSongMenu(activity) { - @Override - public Song getSong() { - return ViewHolder.this.getSong(); - } - - @Override - public int getMenuRes() { - return getSongMenuRes(); - } - - @Override - public boolean onMenuItemClick(MenuItem item) { - return onSongMenuItemClick(item) || super.onMenuItemClick(item); - } - }); - } - - protected Song getSong() { - return dataSet.get(getAdapterPosition()); - } - - protected int getSongMenuRes() { - return DEFAULT_MENU_RES; - } - - protected boolean onSongMenuItemClick(MenuItem item) { - if (image != null && image.getVisibility() == View.VISIBLE) { - switch (item.getItemId()) { - case R.id.action_go_to_album: - Pair[] albumPairs = new Pair[]{ - Pair.create(imageContainer, - activity.getResources().getString(R.string.transition_album_art)) - }; - NavigationUtil.goToAlbum(activity, getSong().albumId, albumPairs); - return true; - } - } - return false; - } - - @Override - public void onClick(View v) { - if (isInQuickSelectMode()) { - toggleChecked(getAdapterPosition()); - } else { - MusicPlayerRemote.openQueue(dataSet, getAdapterPosition(), true); - } - } - - @Override - public boolean onLongClick(View view) { - return toggleChecked(getAdapterPosition()); - } - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/SongAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/SongAdapter.kt new file mode 100644 index 00000000..ee15f1be --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/SongAdapter.kt @@ -0,0 +1,224 @@ +package code.name.monkey.retromusic.ui.adapter.song + +import android.graphics.drawable.Drawable +import android.view.LayoutInflater +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup +import androidx.annotation.LayoutRes +import androidx.appcompat.app.AppCompatActivity +import androidx.core.util.Pair +import code.name.monkey.appthemehelper.util.ColorUtil +import code.name.monkey.appthemehelper.util.MaterialValueHelper +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.glide.RetroMusicColoredTarget +import code.name.monkey.retromusic.glide.SongGlideRequest +import code.name.monkey.retromusic.helper.MusicPlayerRemote +import code.name.monkey.retromusic.helper.SortOrder +import code.name.monkey.retromusic.helper.menu.SongMenuHelper +import code.name.monkey.retromusic.helper.menu.SongsMenuHelper +import code.name.monkey.retromusic.interfaces.CabHolder +import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.ui.adapter.base.AbsMultiSelectAdapter +import code.name.monkey.retromusic.ui.adapter.base.MediaEntryViewHolder +import code.name.monkey.retromusic.util.MusicUtil +import code.name.monkey.retromusic.util.NavigationUtil +import code.name.monkey.retromusic.util.PreferenceUtil +import com.afollestad.materialcab.MaterialCab +import com.bumptech.glide.Glide +import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView +import java.util.* + +/** + * Created by hemanths on 13/08/17. + */ + +open class SongAdapter @JvmOverloads constructor(protected val activity: AppCompatActivity, dataSet: ArrayList, + @param:LayoutRes protected var itemLayoutRes: Int, usePalette: Boolean, cabHolder: CabHolder?, + showSectionName: Boolean = true) : AbsMultiSelectAdapter(activity, cabHolder, R.menu.menu_media_selection), MaterialCab.Callback, FastScrollRecyclerView.SectionedAdapter { + var dataSet: ArrayList + protected set + + protected var usePalette = false + private var showSectionName = true + + init { + this.dataSet = dataSet + this.usePalette = usePalette + this.showSectionName = showSectionName + this.setHasStableIds(true) + } + + open fun swapDataSet(dataSet: ArrayList) { + this.dataSet = dataSet + notifyDataSetChanged() + } + + open fun usePalette(usePalette: Boolean) { + this.usePalette = usePalette + notifyDataSetChanged() + } + + override fun getItemId(position: Int): Long { + return dataSet[position].id.toLong() + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + val view = LayoutInflater.from(activity).inflate(itemLayoutRes, parent, false) + return createViewHolder(view) + } + + protected open fun createViewHolder(view: View): ViewHolder { + return ViewHolder(view) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val song = dataSet[position] + + val isChecked = isChecked(song) + holder.itemView.isActivated = isChecked + + if (holder.adapterPosition == itemCount - 1) { + if (holder.shortSeparator != null) { + holder.shortSeparator!!.visibility = View.GONE + } + } else { + if (holder.shortSeparator != null) { + holder.shortSeparator!!.visibility = View.VISIBLE + } + } + + if (holder.title != null) { + holder.title!!.text = getSongTitle(song) + } + if (holder.text != null) { + holder.text!!.text = getSongText(song) + } + + loadAlbumCover(song, holder) + + } + + private fun setColors(color: Int, holder: ViewHolder) { + if (holder.paletteColorContainer != null) { + holder.paletteColorContainer!!.setBackgroundColor(color) + if (holder.title != null) { + holder.title!!.setTextColor(MaterialValueHelper.getPrimaryTextColor(activity, ColorUtil.isColorLight(color))) + } + if (holder.text != null) { + holder.text!!.setTextColor(MaterialValueHelper.getSecondaryTextColor(activity, ColorUtil.isColorLight(color))) + } + } + } + + protected open fun loadAlbumCover(song: Song, holder: ViewHolder) { + if (holder.image == null) { + return + } + + SongGlideRequest.Builder.from(Glide.with(activity), song) + .checkIgnoreMediaStore(activity) + .generatePalette(activity).build() + .into(object : RetroMusicColoredTarget(holder.image!!) { + override fun onLoadCleared(placeholder: Drawable?) { + super.onLoadCleared(placeholder) + setColors(defaultFooterColor, holder) + } + + override fun onColorReady(color: Int) { + setColors(color, holder) + } + }) + } + + private fun getSongTitle(song: Song): String? { + return song.title + } + + private fun getSongText(song: Song): String? { + return song.artistName + } + + override fun getItemCount(): Int { + return dataSet.size + } + + override fun getIdentifier(position: Int): Song? { + return dataSet[position] + } + + override fun getName(song: Song): String { + return song.title!! + } + + override fun onMultipleItemAction(menuItem: MenuItem, + selection: ArrayList) { + SongsMenuHelper.handleMenuClick(activity, selection, menuItem.itemId) + } + + override fun getSectionName(position: Int): String { + var sectionName: String? = null + when (PreferenceUtil.getInstance().songSortOrder) { + SortOrder.SongSortOrder.SONG_A_Z, SortOrder.SongSortOrder.SONG_Z_A -> sectionName = dataSet[position].title + SortOrder.SongSortOrder.SONG_ALBUM -> sectionName = dataSet[position].albumName + SortOrder.SongSortOrder.SONG_ARTIST -> sectionName = dataSet[position].artistName + SortOrder.SongSortOrder.SONG_YEAR -> return MusicUtil.getYearString(dataSet[position].year) + } + + return MusicUtil.getSectionName(sectionName) + } + + open inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) { + + protected open var songMenuRes = SongMenuHelper.MENU_RES + + protected open val song: Song + get() = dataSet[adapterPosition] + + init { + setImageTransitionName(activity.getString(R.string.transition_album_art)) + menu!!.setOnClickListener(object : SongMenuHelper.OnClickSongMenu(activity) { + override val song: Song + get() = this@ViewHolder.song + + override val menuRes: Int + get() = songMenuRes + + override fun onMenuItemClick(item: MenuItem): Boolean { + return onSongMenuItemClick(item) || super.onMenuItemClick(item) + } + }) + } + + protected open fun onSongMenuItemClick(item: MenuItem): Boolean { + if (image != null && image!!.visibility == View.VISIBLE) { + when (item.itemId) { + R.id.action_go_to_album -> { + val albumPairs = arrayOf>(Pair.create(imageContainer, + activity.resources.getString(R.string.transition_album_art))) + NavigationUtil.goToAlbum(activity, song.albumId, *albumPairs) + return true + } + } + } + return false + } + + override fun onClick(v: View?) { + if (isInQuickSelectMode) { + toggleChecked(adapterPosition) + } else { + MusicPlayerRemote.openQueue(dataSet, adapterPosition, true) + } + } + + override fun onLongClick(v: View?): Boolean { + return toggleChecked(adapterPosition) + } + } + + companion object { + + val TAG = SongAdapter::class.java.simpleName + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/MiniPlayerFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/MiniPlayerFragment.java deleted file mode 100644 index e87e4d34..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/MiniPlayerFragment.java +++ /dev/null @@ -1,228 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments; - -import android.animation.ObjectAnimator; -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.res.ColorStateList; -import android.os.Bundle; -import android.text.SpannableString; -import android.text.SpannableStringBuilder; -import android.text.style.ForegroundColorSpan; -import android.view.GestureDetector; -import android.view.LayoutInflater; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; -import android.view.animation.DecelerateInterpolator; -import android.widget.ImageView; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.util.Pair; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import butterknife.Unbinder; -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper; -import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.ui.fragments.base.AbsMusicServiceFragment; -import code.name.monkey.retromusic.util.NavigationUtil; -import code.name.monkey.retromusic.util.PreferenceUtil; -import code.name.monkey.retromusic.util.RetroUtil; -import me.zhanghai.android.materialprogressbar.MaterialProgressBar; - -public class MiniPlayerFragment extends AbsMusicServiceFragment implements MusicProgressViewUpdateHelper.Callback { - - @BindView(R.id.mini_player_title) - TextView miniPlayerTitle; - - @BindView(R.id.mini_player_play_pause_button) - ImageView miniPlayerPlayPauseButton; - - @BindView(R.id.action_next) - View next; - - @BindView(R.id.action_prev) - View previous; - - @BindView(R.id.action_playing_queue) - View playingQueue; - - @BindView(R.id.progress_bar) - MaterialProgressBar progressBar; - - private Unbinder unbinder; - private MusicProgressViewUpdateHelper progressViewUpdateHelper; - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - progressViewUpdateHelper = new MusicProgressViewUpdateHelper(this); - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - View layout = inflater.inflate(R.layout.fragment_mini_player, container, false); - unbinder = ButterKnife.bind(this, layout); - return layout; - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - //noinspection ConstantConditions - view.setBackgroundColor(ThemeStore.primaryColor(getContext())); - view.setOnTouchListener(new FlingPlayBackController(getActivity())); - //view.setOnClickListener(v -> NavigationUtil.gotoNowPlayingActivity(getContext())); - setUpMiniPlayer(); - - if (RetroUtil.isTablet()) { - next.setVisibility(View.VISIBLE); - previous.setVisibility(View.VISIBLE); - playingQueue.setVisibility(View.VISIBLE); - } else{ - next.setVisibility(PreferenceUtil.getInstance().isExtraMiniExtraControls() ? View.VISIBLE : View.GONE); - playingQueue.setVisibility(PreferenceUtil.getInstance().isExtraMiniExtraControls() ? View.GONE : View.VISIBLE); - previous.setVisibility(PreferenceUtil.getInstance().isExtraMiniExtraControls() ? View.VISIBLE : View.GONE); - } - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - unbinder.unbind(); - } - - @SuppressWarnings({"ConstantConditions"}) - private void setUpMiniPlayer() { - setUpPlayPauseButton(); - progressBar.setProgressTintList(ColorStateList.valueOf(ThemeStore.accentColor(getActivity()))); - } - - private void setUpPlayPauseButton() { - //noinspection ConstantConditions - miniPlayerPlayPauseButton.setOnClickListener(new PlayPauseButtonOnClickHandler()); - } - - private void updateSongTitle() { - SpannableStringBuilder builder = new SpannableStringBuilder(); - - Song song = MusicPlayerRemote.getCurrentSong(); - if (song == null) { - return; - } - SpannableString title = new SpannableString(song.title); - title.setSpan(new ForegroundColorSpan(ThemeStore.textColorPrimary(getContext())), 0, title.length(), 0); - - SpannableString text = new SpannableString(song.artistName); - text.setSpan(new ForegroundColorSpan(ThemeStore.textColorSecondary(getContext())), 0, text.length(), 0); - - builder.append(title).append(" • ").append(text); - - miniPlayerTitle.setSelected(true); - miniPlayerTitle.setText(builder); - } - - @Override - public void onServiceConnected() { - updateSongTitle(); - updatePlayPauseDrawableState(); - } - - @Override - public void onPlayingMetaChanged() { - updateSongTitle(); - } - - @Override - public void onPlayStateChanged() { - updatePlayPauseDrawableState(); - } - - @Override - public void onUpdateProgressViews(int progress, int total) { - progressBar.setMax(total); - ObjectAnimator animator = ObjectAnimator.ofInt(progressBar, "progress", progress); - animator.setDuration(1000); - animator.setInterpolator(new DecelerateInterpolator()); - animator.start(); - } - - @Override - public void onResume() { - super.onResume(); - progressViewUpdateHelper.start(); - } - - @Override - public void onPause() { - super.onPause(); - progressViewUpdateHelper.stop(); - } - - protected void updatePlayPauseDrawableState() { - if (MusicPlayerRemote.isPlaying()) { - miniPlayerPlayPauseButton.setImageResource(R.drawable.ic_pause_white_24dp); - } else { - miniPlayerPlayPauseButton.setImageResource(R.drawable.ic_play_arrow_white_24dp); - } - } - - public void setColor(int playerFragmentColor) { - //noinspection ConstantConditions - getView().setBackgroundColor(playerFragmentColor); - } - - @OnClick({R.id.action_prev, R.id.action_playing_queue, R.id.action_next}) - void onClicks(View view) { - switch (view.getId()) { - case R.id.action_playing_queue: - NavigationUtil.goToPlayingQueue(getActivity()); - break; - case R.id.action_next: - MusicPlayerRemote.playNextSong(); - break; - case R.id.action_prev: - MusicPlayerRemote.back(); - break; - } - } - - public static class FlingPlayBackController implements View.OnTouchListener { - - GestureDetector flingPlayBackController; - - public FlingPlayBackController(Context context) { - flingPlayBackController = new GestureDetector(context, - new GestureDetector.SimpleOnGestureListener() { - @Override - public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, - float velocityY) { - if (Math.abs(velocityX) > Math.abs(velocityY)) { - if (velocityX < 0) { - MusicPlayerRemote.playNextSong(); - return true; - } else if (velocityX > 0) { - MusicPlayerRemote.playPreviousSong(); - return true; - } - } - return false; - } - }); - } - - @SuppressLint("ClickableViewAccessibility") - @Override - public boolean onTouch(View v, MotionEvent event) { - return flingPlayBackController.onTouchEvent(event); - } - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/MiniPlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/MiniPlayerFragment.kt new file mode 100644 index 00000000..d627fd68 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/MiniPlayerFragment.kt @@ -0,0 +1,165 @@ +package code.name.monkey.retromusic.ui.fragments + +import android.animation.ObjectAnimator +import android.annotation.SuppressLint +import android.content.Context +import android.content.res.ColorStateList +import android.os.Bundle +import android.text.SpannableString +import android.text.SpannableStringBuilder +import android.text.style.ForegroundColorSpan +import android.view.* +import android.view.animation.DecelerateInterpolator +import butterknife.OnClick +import code.name.monkey.appthemehelper.ThemeStore +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.helper.MusicPlayerRemote +import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper +import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler +import code.name.monkey.retromusic.ui.fragments.base.AbsMusicServiceFragment +import code.name.monkey.retromusic.util.NavigationUtil +import code.name.monkey.retromusic.util.PreferenceUtil +import code.name.monkey.retromusic.util.RetroUtil +import kotlinx.android.synthetic.main.fragment_mini_player.* + +open class MiniPlayerFragment : AbsMusicServiceFragment(), MusicProgressViewUpdateHelper.Callback, View.OnClickListener { + private var progressViewUpdateHelper: MusicProgressViewUpdateHelper? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + progressViewUpdateHelper = MusicProgressViewUpdateHelper(this) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.fragment_mini_player, container, false) + } + override fun onClick(view: View) { + when (view.id) { + R.id.actionPlayingQueue -> NavigationUtil.goToPlayingQueue(activity!!) + R.id.actionNext -> MusicPlayerRemote.playNextSong() + R.id.actionPrevious -> MusicPlayerRemote.back() + } + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + view.setBackgroundColor(ThemeStore.primaryColor(context!!)) + view.setOnTouchListener(FlingPlayBackController(context!!)) + //view.setOnClickListener(v -> NavigationUtil.gotoNowPlayingActivity(getContext())); + setUpMiniPlayer() + + if (RetroUtil.isTablet()) { + actionNext!!.visibility = View.VISIBLE + actionPrevious!!.visibility = View.VISIBLE + actionPlayingQueue!!.visibility = View.VISIBLE + } else { + actionNext!!.visibility = if (PreferenceUtil.getInstance().isExtraMiniExtraControls) View.VISIBLE else View.GONE + actionPlayingQueue!!.visibility = if (PreferenceUtil.getInstance().isExtraMiniExtraControls) View.GONE else View.VISIBLE + actionPrevious!!.visibility = if (PreferenceUtil.getInstance().isExtraMiniExtraControls) View.VISIBLE else View.GONE + } + + actionPlayingQueue.setOnClickListener(this) + actionNext.setOnClickListener(this) + actionPrevious.setOnClickListener(this) + } + + private fun setUpMiniPlayer() { + setUpPlayPauseButton() + progressBar!!.progressTintList = ColorStateList.valueOf(ThemeStore.accentColor(activity!!)) + } + + private fun setUpPlayPauseButton() { + miniPlayerPlayPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler()) + } + + private fun updateSongTitle() { + val builder = SpannableStringBuilder() + + val song = MusicPlayerRemote.currentSong ?: return + val title = SpannableString(song.title) + title.setSpan(ForegroundColorSpan(ThemeStore.textColorPrimary(context!!)), 0, title.length, 0) + + val text = SpannableString(song.artistName) + text.setSpan(ForegroundColorSpan(ThemeStore.textColorSecondary(context!!)), 0, text.length, 0) + + builder.append(title).append(" • ").append(text) + + miniPlayerTitle.isSelected = true + miniPlayerTitle.text = builder + } + + override fun onServiceConnected() { + updateSongTitle() + updatePlayPauseDrawableState() + } + + override fun onPlayingMetaChanged() { + updateSongTitle() + } + + override fun onPlayStateChanged() { + updatePlayPauseDrawableState() + } + + override fun onUpdateProgressViews(progress: Int, total: Int) { + progressBar.max = total + val animator = ObjectAnimator.ofInt(progressBar, "progress", progress) + animator.duration = 1000 + animator.interpolator = DecelerateInterpolator() + animator.start() + } + + override fun onResume() { + super.onResume() + progressViewUpdateHelper!!.start() + } + + override fun onPause() { + super.onPause() + progressViewUpdateHelper!!.stop() + } + + protected fun updatePlayPauseDrawableState() { + if (MusicPlayerRemote.isPlaying) { + miniPlayerPlayPauseButton!!.setImageResource(R.drawable.ic_pause_white_24dp) + } else { + miniPlayerPlayPauseButton!!.setImageResource(R.drawable.ic_play_arrow_white_24dp) + } + } + + fun setColor(playerFragmentColor: Int) { + + view!!.setBackgroundColor(playerFragmentColor) + } + + + class FlingPlayBackController(context: Context) : View.OnTouchListener { + + internal var flingPlayBackController: GestureDetector + + init { + flingPlayBackController = GestureDetector(context, + object : GestureDetector.SimpleOnGestureListener() { + override fun onFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, + velocityY: Float): Boolean { + if (Math.abs(velocityX) > Math.abs(velocityY)) { + if (velocityX < 0) { + MusicPlayerRemote.playNextSong() + return true + } else if (velocityX > 0) { + MusicPlayerRemote.playPreviousSong() + return true + } + } + return false + } + }) + } + + @SuppressLint("ClickableViewAccessibility") + override fun onTouch(v: View, event: MotionEvent): Boolean { + return flingPlayBackController.onTouchEvent(event) + } + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/NowPlayingScreen.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/NowPlayingScreen.java index 237fcbe0..59f4288e 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/NowPlayingScreen.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/NowPlayingScreen.java @@ -8,18 +8,7 @@ import code.name.monkey.retromusic.R; public enum NowPlayingScreen { ADAPTIVE(R.string.adaptive, R.drawable.np_adaptive, 10), - BLUR(R.string.blur, R.drawable.np_blur, 4), - CARD(R.string.card, R.drawable.np_card, 6), - COLOR(R.string.color, R.drawable.np_color, 5), - BLUR_CARD(R.string.blur_card, R.drawable.np_blur_card, 9), - FIT(R.string.fit, R.drawable.np_adaptive, 12), - FLAT(R.string.flat, R.drawable.np_flat, 1), - FULL(R.string.full, R.drawable.np_full, 2), - TINY(R.string.tiny, R.drawable.np_tiny, 7), - MATERIAL(R.string.material, R.drawable.np_material, 11), - NORMAL(R.string.normal, R.drawable.np_normal, 0), - PLAIN(R.string.plain, R.drawable.np_plain, 3), - SIMPLE(R.string.simple, R.drawable.np_simple, 8); + BLUR(R.string.blur, R.drawable.np_blur, 4); @StringRes public final int titleRes; diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/PlayingQueueFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/PlayingQueueFragment.java index ab68d10e..6f6865d7 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/PlayingQueueFragment.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/PlayingQueueFragment.java @@ -58,8 +58,8 @@ public class PlayingQueueFragment extends AbsMusicServiceFragment { mPlayingQueueAdapter = new PlayingQueueAdapter( (AppCompatActivity) getActivity(), - MusicPlayerRemote.getPlayingQueue(), - MusicPlayerRemote.getPosition(), + MusicPlayerRemote.INSTANCE.getPlayingQueue(), + MusicPlayerRemote.INSTANCE.getPosition(), R.layout.item_queue); mWrappedAdapter = mRecyclerViewDragDropManager.createWrappedAdapter(mPlayingQueueAdapter); @@ -69,7 +69,7 @@ public class PlayingQueueFragment extends AbsMusicServiceFragment { mRecyclerView.setAdapter(mWrappedAdapter); mRecyclerView.setItemAnimator(animator); mRecyclerViewDragDropManager.attachRecyclerView(mRecyclerView); - mLayoutManager.scrollToPositionWithOffset(MusicPlayerRemote.getPosition() + 1, 0); + mLayoutManager.scrollToPositionWithOffset(MusicPlayerRemote.INSTANCE.getPosition() + 1, 0); } @@ -98,7 +98,7 @@ public class PlayingQueueFragment extends AbsMusicServiceFragment { } private void updateQueuePosition() { - mPlayingQueueAdapter.setCurrent(MusicPlayerRemote.getPosition()); + mPlayingQueueAdapter.setCurrent(MusicPlayerRemote.INSTANCE.getPosition()); // if (slidingUpPanelLayout.getPanelState() == SlidingUpPanelLayout.PanelState.COLLAPSED) { resetToCurrentPosition(); //} @@ -106,13 +106,13 @@ public class PlayingQueueFragment extends AbsMusicServiceFragment { private void updateQueue() { mPlayingQueueAdapter - .swapDataSet(MusicPlayerRemote.getPlayingQueue(), MusicPlayerRemote.getPosition()); + .swapDataSet(MusicPlayerRemote.INSTANCE.getPlayingQueue(), MusicPlayerRemote.INSTANCE.getPosition()); resetToCurrentPosition(); } private void resetToCurrentPosition() { mRecyclerView.stopScroll(); - mLayoutManager.scrollToPositionWithOffset(MusicPlayerRemote.getPosition() + 1, 0); + mLayoutManager.scrollToPositionWithOffset(MusicPlayerRemote.INSTANCE.getPosition() + 1, 0); } @Override diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/VolumeFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/VolumeFragment.java deleted file mode 100755 index a314fd92..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/VolumeFragment.java +++ /dev/null @@ -1,172 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments; - -import android.content.Context; -import android.graphics.Color; -import android.graphics.PorterDuff; -import android.media.AudioManager; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.SeekBar; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import butterknife.Unbinder; -import code.name.monkey.appthemehelper.util.ATHUtil; -import code.name.monkey.appthemehelper.util.TintHelper; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.util.PreferenceUtil; -import code.name.monkey.retromusic.volume.AudioVolumeObserver; -import code.name.monkey.retromusic.volume.OnAudioVolumeChangedListener; - -public class VolumeFragment extends Fragment implements SeekBar.OnSeekBarChangeListener, - OnAudioVolumeChangedListener { - - @BindView(R.id.volume_seekbar) - SeekBar volumeSeekbar; - - @BindView(R.id.volume_down) - ImageView volumeDown; - - @BindView(R.id.container) - ViewGroup viewGroup; - - @BindView(R.id.volume_up) - ImageView volumeUp; - - private Unbinder unbinder; - private AudioVolumeObserver mAudioVolumeObserver; - - public static VolumeFragment newInstance() { - return new VolumeFragment(); - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_volume, container, false); - unbinder = ButterKnife.bind(this, view); - return view; - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - //noinspection ConstantConditions - setTintable(ATHUtil.resolveColor(getContext(), R.attr.iconColor)); - } - - @Override - public void onResume() { - super.onResume(); - if (mAudioVolumeObserver == null) { - //noinspection ConstantConditions - mAudioVolumeObserver = new AudioVolumeObserver(getActivity()); - } - mAudioVolumeObserver.register(AudioManager.STREAM_MUSIC, this); - - AudioManager audioManager = getAudioManager(); - if (audioManager != null) { - volumeSeekbar.setMax(audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)); - volumeSeekbar.setProgress(audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)); - } - volumeSeekbar.setOnSeekBarChangeListener(this); - } - - @Override - public void onAudioVolumeChanged(int currentVolume, int maxVolume) { - if (volumeSeekbar == null) { - return; - } - volumeSeekbar.setMax(maxVolume); - volumeSeekbar.setProgress(currentVolume); - volumeDown.setImageResource(currentVolume == 0 ? R.drawable.ic_volume_off_white_24dp : R.drawable.ic_volume_down_white_24dp); - } - - private AudioManager getAudioManager() { - //noinspection ConstantConditions - return (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE); - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - unbinder.unbind(); - if (mAudioVolumeObserver != null) { - mAudioVolumeObserver.unregister(); - } - } - - @Override - public void onProgressChanged(SeekBar seekBar, int i, boolean b) { - AudioManager audioManager = getAudioManager(); - if (audioManager != null) { - audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, i, 0); - } - setPauseWhenZeroVolume(i < 1); - volumeDown.setImageResource(i == 0 ? R.drawable.ic_volume_off_white_24dp : R.drawable.ic_volume_down_white_24dp); - - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - - } - - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - - } - - @OnClick({R.id.volume_down, R.id.volume_up}) - public void onViewClicked(View view) { - AudioManager audioManager = getAudioManager(); - switch (view.getId()) { - case R.id.volume_down: - if (audioManager != null) { - audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_LOWER, 0); - } - break; - case R.id.volume_up: - if (audioManager != null) { - audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_RAISE, 0); - } - break; - } - } - - public void tintWhiteColor() { - setProgressBarColor(Color.WHITE); - } - - private void setProgressBarColor(int newColor) { - TintHelper.setTintAuto(volumeSeekbar, newColor, false); - volumeDown.setColorFilter(newColor, PorterDuff.Mode.SRC_IN); - volumeUp.setColorFilter(newColor, PorterDuff.Mode.SRC_IN); - } - - public void setTintable(int color) { - setProgressBarColor(color); - } - - public void removeThumb() { - volumeSeekbar.setThumb(null); - } - - private void setPauseWhenZeroVolume(boolean pauseWhenZeroVolume) { - if (PreferenceUtil.getInstance().pauseOnZeroVolume() && pauseWhenZeroVolume) - if (MusicPlayerRemote.isPlaying()) { - MusicPlayerRemote.pauseSong(); - } else { - MusicPlayerRemote.resumePlaying(); - } - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/VolumeFragment.kt b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/VolumeFragment.kt new file mode 100755 index 00000000..3fb07eea --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/VolumeFragment.kt @@ -0,0 +1,129 @@ +package code.name.monkey.retromusic.ui.fragments + +import android.content.Context +import android.graphics.Color +import android.graphics.PorterDuff +import android.media.AudioManager +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.SeekBar +import androidx.fragment.app.Fragment +import code.name.monkey.appthemehelper.util.ATHUtil +import code.name.monkey.appthemehelper.util.TintHelper +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.helper.MusicPlayerRemote +import code.name.monkey.retromusic.util.PreferenceUtil +import code.name.monkey.retromusic.volume.AudioVolumeObserver +import code.name.monkey.retromusic.volume.OnAudioVolumeChangedListener +import kotlinx.android.synthetic.main.fragment_volume.* + +class VolumeFragment : Fragment(), SeekBar.OnSeekBarChangeListener, OnAudioVolumeChangedListener, View.OnClickListener { + + private var audioVolumeObserver: AudioVolumeObserver? = null + + private val audioManager: AudioManager? + get() = context!!.getSystemService(Context.AUDIO_SERVICE) as AudioManager + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.fragment_volume, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setTintable(ATHUtil.resolveColor(context, R.attr.iconColor)) + volumeDown.setOnClickListener(this) + volumeUp.setOnClickListener(this) + } + + override fun onResume() { + super.onResume() + if (audioVolumeObserver == null) { + audioVolumeObserver = AudioVolumeObserver(activity!!) + } + audioVolumeObserver!!.register(AudioManager.STREAM_MUSIC, this) + + val audioManager = audioManager + if (audioManager != null) { + volumeSeekBar!!.max = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC) + volumeSeekBar!!.progress = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) + } + volumeSeekBar!!.setOnSeekBarChangeListener(this) + } + + override fun onAudioVolumeChanged(currentVolume: Int, maxVolume: Int) { + if (volumeSeekBar == null) { + return + } + volumeSeekBar!!.max = maxVolume + volumeSeekBar!!.progress = currentVolume + volumeDown!!.setImageResource(if (currentVolume == 0) R.drawable.ic_volume_off_white_24dp else R.drawable.ic_volume_down_white_24dp) + } + + override fun onDestroyView() { + super.onDestroyView() + if (audioVolumeObserver != null) { + audioVolumeObserver!!.unregister() + } + } + + override fun onProgressChanged(seekBar: SeekBar, i: Int, b: Boolean) { + val audioManager = audioManager + audioManager?.setStreamVolume(AudioManager.STREAM_MUSIC, i, 0) + setPauseWhenZeroVolume(i < 1) + volumeDown!!.setImageResource(if (i == 0) R.drawable.ic_volume_off_white_24dp else R.drawable.ic_volume_down_white_24dp) + + } + + override fun onStartTrackingTouch(seekBar: SeekBar) { + + } + + override fun onStopTrackingTouch(seekBar: SeekBar) { + + } + + override fun onClick(view: View) { + val audioManager = audioManager + when (view.id) { + R.id.volumeDown -> audioManager?.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_LOWER, 0) + R.id.volumeUp -> audioManager?.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_RAISE, 0) + } + } + + fun tintWhiteColor() { + setProgressBarColor(Color.WHITE) + } + + private fun setProgressBarColor(newColor: Int) { + TintHelper.setTintAuto(volumeSeekBar!!, newColor, false) + volumeDown!!.setColorFilter(newColor, PorterDuff.Mode.SRC_IN) + volumeUp!!.setColorFilter(newColor, PorterDuff.Mode.SRC_IN) + } + + private fun setTintable(color: Int) { + setProgressBarColor(color) + } + + fun removeThumb() { + volumeSeekBar!!.thumb = null + } + + private fun setPauseWhenZeroVolume(pauseWhenZeroVolume: Boolean) { + if (PreferenceUtil.getInstance().pauseOnZeroVolume() && pauseWhenZeroVolume) + if (MusicPlayerRemote.isPlaying) { + MusicPlayerRemote.pauseSong() + } else { + MusicPlayerRemote.resumePlaying() + } + } + + companion object { + + fun newInstance(): VolumeFragment { + return VolumeFragment() + } + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsLibraryPagerFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsLibraryPagerFragment.java deleted file mode 100644 index 724a78fe..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsLibraryPagerFragment.java +++ /dev/null @@ -1,19 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.base; - -import android.os.Bundle; - -import code.name.monkey.retromusic.ui.fragments.mainactivity.LibraryFragment; - -public class AbsLibraryPagerFragment extends AbsMusicServiceFragment { - - - public LibraryFragment getLibraryFragment() { - return (LibraryFragment) getParentFragment(); - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - setHasOptionsMenu(true); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsLibraryPagerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsLibraryPagerFragment.kt new file mode 100644 index 00000000..b5b5123d --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsLibraryPagerFragment.kt @@ -0,0 +1,17 @@ +package code.name.monkey.retromusic.ui.fragments.base + +import android.os.Bundle + +import code.name.monkey.retromusic.ui.fragments.mainactivity.LibraryFragment + +open class AbsLibraryPagerFragment : AbsMusicServiceFragment() { + + + val libraryFragment: LibraryFragment + get() = parentFragment as LibraryFragment + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + setHasOptionsMenu(true) + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsLibraryPagerRecyclerViewCustomGridSizeFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsLibraryPagerRecyclerViewCustomGridSizeFragment.java deleted file mode 100644 index 3c1a4dc5..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsLibraryPagerRecyclerViewCustomGridSizeFragment.java +++ /dev/null @@ -1,169 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.base; - -import android.os.Bundle; -import androidx.annotation.LayoutRes; -import androidx.annotation.NonNull; -import androidx.recyclerview.widget.RecyclerView; -import android.view.View; - -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.util.RetroUtil; - - -public abstract class AbsLibraryPagerRecyclerViewCustomGridSizeFragment extends - AbsLibraryPagerRecyclerViewFragment { - - private int gridSize; - private String sortOrder; - - private boolean usePaletteInitialized; - private boolean usePalette; - private int currentLayoutRes; - - public final int getGridSize() { - if (gridSize == 0) { - if (isLandscape()) { - gridSize = loadGridSizeLand(); - } else { - gridSize = loadGridSize(); - } - } - return gridSize; - } - - protected abstract void setGridSize(int gridSize); - - public final String getSortOrder() { - if (sortOrder == null) { - sortOrder = loadSortOrder(); - } - return sortOrder; - } - - protected abstract void setSortOrder(String sortOrder); - - public void setAndSaveSortOrder(final String sortOrder) { - this.sortOrder = sortOrder; - saveSortOrder(sortOrder); - setSortOrder(sortOrder); - } - - public int getMaxGridSize() { - if (isLandscape()) { - return getResources().getInteger(R.integer.max_columns_land); - } else { - return getResources().getInteger(R.integer.max_columns); - } - } - - /** - * @return whether the palette should be used at all or not - */ - public final boolean usePalette() { - if (!usePaletteInitialized) { - usePalette = loadUsePalette(); - usePaletteInitialized = true; - } - return usePalette; - } - - public void setAndSaveGridSize(final int gridSize) { - int oldLayoutRes = getItemLayoutRes(); - this.gridSize = gridSize; - if (isLandscape()) { - saveGridSizeLand(gridSize); - } else { - saveGridSize(gridSize); - } - // only recreate the adapter and layout manager if the layout currentLayoutRes has changed - if (oldLayoutRes != getItemLayoutRes()) { - invalidateLayoutManager(); - invalidateAdapter(); - } else { - setGridSize(gridSize); - } - } - - public void setAndSaveUsePalette(final boolean usePalette) { - this.usePalette = usePalette; - saveUsePalette(usePalette); - setUsePalette(usePalette); - } - - /** - * @return whether the palette option should be available for the current item layout or not - */ - public boolean canUsePalette() { - return getItemLayoutRes() == R.layout.item_color; - } - - /** - * Override to customize which item layout currentLayoutRes should be used. You might also want to - * override {@link #canUsePalette()} then. - * - * @see #getGridSize() - */ - @LayoutRes - protected int getItemLayoutRes() { - if (getGridSize() > getMaxGridSizeForList()) { - return R.layout.item_grid; - } - return R.layout.item_list; - } - - protected final void notifyLayoutResChanged(@LayoutRes int res) { - this.currentLayoutRes = res; - RecyclerView recyclerView = getRecyclerView(); - if (recyclerView != null) { - applyRecyclerViewPaddingForLayoutRes(recyclerView, currentLayoutRes); - } - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - applyRecyclerViewPaddingForLayoutRes(getRecyclerView(), currentLayoutRes); - } - - protected void applyRecyclerViewPaddingForLayoutRes(@NonNull RecyclerView recyclerView, - @LayoutRes int res) { - int padding; - if (res == R.layout.item_grid) { - padding = (int) (getResources().getDisplayMetrics().density * 2); - } else { - padding = 0; - } - recyclerView.setPadding(padding, padding, padding, padding); - } - - protected abstract String loadSortOrder(); - - protected abstract void saveSortOrder(String sortOrder); - - protected abstract int loadGridSize(); - - protected abstract void saveGridSize(int gridColumns); - - protected abstract int loadGridSizeLand(); - - protected abstract void saveGridSizeLand(int gridColumns); - - protected abstract void saveUsePalette(boolean usePalette); - - protected abstract boolean loadUsePalette(); - - protected abstract void setUsePalette(boolean usePalette); - - protected int getMaxGridSizeForList() { - if (isLandscape()) { - return getActivity().getResources().getInteger(R.integer.default_list_columns_land); - } - return getActivity().getResources().getInteger(R.integer.default_list_columns); - } - - protected final boolean isLandscape() { - return RetroUtil.isLandscape( ); - } - - -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsLibraryPagerRecyclerViewCustomGridSizeFragment.kt b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsLibraryPagerRecyclerViewCustomGridSizeFragment.kt new file mode 100644 index 00000000..e2c8ad05 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsLibraryPagerRecyclerViewCustomGridSizeFragment.kt @@ -0,0 +1,157 @@ +package code.name.monkey.retromusic.ui.fragments.base + +import android.os.Bundle +import android.view.View +import androidx.annotation.LayoutRes +import androidx.recyclerview.widget.RecyclerView +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.util.RetroUtil + + +abstract class AbsLibraryPagerRecyclerViewCustomGridSizeFragment, LM : RecyclerView.LayoutManager> : AbsLibraryPagerRecyclerViewFragment() { + + private var gridSize: Int = 0 + private var sortOrder: String? = null + + private var usePaletteInitialized: Boolean = false + private var usePalette: Boolean = false + private var currentLayoutRes: Int = 0 + + val maxGridSize: Int + get() = if (isLandscape) { + resources.getInteger(R.integer.max_columns_land) + } else { + resources.getInteger(R.integer.max_columns) + } + + /** + * Override to customize which item layout currentLayoutRes should be used. You might also want to + * override [.canUsePalette] then. + * + * @see .getGridSize + */ + protected val itemLayoutRes: Int + @LayoutRes + get() = if (getGridSize() > maxGridSizeForList) { + R.layout.item_grid + } else R.layout.item_list + + protected val maxGridSizeForList: Int + get() = if (isLandscape) { + activity!!.resources.getInteger(R.integer.default_list_columns_land) + } else activity!!.resources.getInteger(R.integer.default_list_columns) + + protected val isLandscape: Boolean + get() = RetroUtil.isLandscape() + + fun getGridSize(): Int { + if (gridSize == 0) { + if (isLandscape) { + gridSize = loadGridSizeLand() + } else { + gridSize = loadGridSize() + } + } + return gridSize + } + + protected abstract fun setGridSize(gridSize: Int) + + fun getSortOrder(): String? { + if (sortOrder == null) { + sortOrder = loadSortOrder() + } + return sortOrder + } + + protected abstract fun setSortOrder(sortOrder: String) + + fun setAndSaveSortOrder(sortOrder: String) { + this.sortOrder = sortOrder + saveSortOrder(sortOrder) + setSortOrder(sortOrder) + } + + /** + * @return whether the palette should be used at all or not + */ + fun usePalette(): Boolean { + if (!usePaletteInitialized) { + usePalette = loadUsePalette() + usePaletteInitialized = true + } + return usePalette + } + + fun setAndSaveGridSize(gridSize: Int) { + val oldLayoutRes = itemLayoutRes + this.gridSize = gridSize + if (isLandscape) { + saveGridSizeLand(gridSize) + } else { + saveGridSize(gridSize) + } + // only recreate the adapter and layout manager if the layout currentLayoutRes has changed + if (oldLayoutRes != itemLayoutRes) { + invalidateLayoutManager() + invalidateAdapter() + } else { + setGridSize(gridSize) + } + } + + fun setAndSaveUsePalette(usePalette: Boolean) { + this.usePalette = usePalette + saveUsePalette(usePalette) + setUsePalette(usePalette) + } + + /** + * @return whether the palette option should be available for the current item layout or not + */ + fun canUsePalette(): Boolean { + return itemLayoutRes == R.layout.item_color + } + + protected fun notifyLayoutResChanged(@LayoutRes res: Int) { + this.currentLayoutRes = res + val recyclerView = recyclerView() + applyRecyclerViewPaddingForLayoutRes(recyclerView, currentLayoutRes) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + applyRecyclerViewPaddingForLayoutRes(recyclerView(), currentLayoutRes) + } + + private fun applyRecyclerViewPaddingForLayoutRes(recyclerView: RecyclerView, + @LayoutRes res: Int) { + val padding: Int + if (res == R.layout.item_grid) { + padding = (resources.displayMetrics.density * 2).toInt() + } else { + padding = 0 + } + recyclerView.setPadding(padding, padding, padding, padding) + } + + protected abstract fun loadSortOrder(): String + + protected abstract fun saveSortOrder(sortOrder: String) + + protected abstract fun loadGridSize(): Int + + protected abstract fun saveGridSize(gridColumns: Int) + + protected abstract fun loadGridSizeLand(): Int + + protected abstract fun saveGridSizeLand(gridColumns: Int) + + protected abstract fun saveUsePalette(usePalette: Boolean) + + protected abstract fun loadUsePalette(): Boolean + + protected abstract fun setUsePalette(usePalette: Boolean) + + +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsLibraryPagerRecyclerViewFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsLibraryPagerRecyclerViewFragment.java deleted file mode 100644 index 4f96cbe4..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsLibraryPagerRecyclerViewFragment.java +++ /dev/null @@ -1,162 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.base; - -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import com.google.android.material.appbar.AppBarLayout; -import com.google.android.material.appbar.AppBarLayout.OnOffsetChangedListener; -import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView; - -import androidx.annotation.LayoutRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.StringRes; -import androidx.recyclerview.widget.RecyclerView; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.Unbinder; -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.util.ViewUtil; - - -public abstract class AbsLibraryPagerRecyclerViewFragment extends - AbsLibraryPagerFragment implements OnOffsetChangedListener { - - public static final String TAG = AbsLibraryPagerRecyclerViewFragment.class.getSimpleName(); - @BindView(R.id.container) - ViewGroup container; - @BindView(R.id.recycler_view) - RecyclerView recyclerView; - @BindView(android.R.id.empty) - TextView empty; - - private Unbinder unbinder; - private A adapter; - private LM layoutManager; - - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(getLayoutRes(), container, false); - unbinder = ButterKnife.bind(this, view); - return view; - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - getLibraryFragment().addOnAppBarOffsetChangedListener(this); - initLayoutManager(); - initAdapter(); - setUpRecyclerView(); - } - - private void setUpRecyclerView() { - if (recyclerView instanceof FastScrollRecyclerView) { - //noinspection ConstantConditions - ViewUtil.setUpFastScrollRecyclerViewColor(getActivity(), - ((FastScrollRecyclerView) recyclerView), ThemeStore.accentColor(getActivity())); - } - recyclerView.setLayoutManager(layoutManager); - recyclerView.setAdapter(adapter); - } - - @Override - public void onQueueChanged() { - super.onQueueChanged(); - checkForPadding(); - } - - @Override - public void onServiceConnected() { - super.onServiceConnected(); - checkForPadding(); - } - - private void checkForPadding() { - int height = (MusicPlayerRemote.getPlayingQueue().isEmpty() ? getResources() - .getDimensionPixelSize(R.dimen.mini_player_height) : 0); - recyclerView.setPadding(0, 0, 0, height); - } - - protected void invalidateLayoutManager() { - initLayoutManager(); - recyclerView.setLayoutManager(layoutManager); - } - - protected void invalidateAdapter() { - initAdapter(); - checkIsEmpty(); - recyclerView.setAdapter(adapter); - } - - private void initAdapter() { - adapter = createAdapter(); - adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() { - @Override - public void onChanged() { - super.onChanged(); - checkIsEmpty(); - checkForPadding(); - } - }); - } - - private void initLayoutManager() { - layoutManager = createLayoutManager(); - } - - protected A getAdapter() { - return adapter; - } - - protected LM getLayoutManager() { - return layoutManager; - } - - protected RecyclerView getRecyclerView() { - return recyclerView; - } - - public ViewGroup getContainer() { - return container; - } - - @Override - public void onOffsetChanged(AppBarLayout appBarLayout, int i) { - container.setPadding(container.getPaddingLeft(), container.getPaddingTop(), - container.getPaddingRight(), getLibraryFragment().getTotalAppBarScrollingRange() + i); - } - - private void checkIsEmpty() { - empty.setText(getEmptyMessage()); - empty.setVisibility(adapter == null || adapter.getItemCount() == 0 ? View.VISIBLE : View.GONE); - } - - @StringRes - protected int getEmptyMessage() { - return R.string.empty; - } - - @LayoutRes - protected int getLayoutRes() { - return R.layout.fragment_main_activity_recycler_view; - } - - protected abstract LM createLayoutManager(); - - @NonNull - protected abstract A createAdapter(); - - @Override - public void onDestroyView() { - super.onDestroyView(); - getLibraryFragment().removeOnAppBarOffsetChangedListener(this); - unbinder.unbind(); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsLibraryPagerRecyclerViewFragment.kt b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsLibraryPagerRecyclerViewFragment.kt new file mode 100644 index 00000000..4e833d14 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsLibraryPagerRecyclerViewFragment.kt @@ -0,0 +1,116 @@ +package code.name.monkey.retromusic.ui.fragments.base + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.annotation.NonNull +import androidx.annotation.StringRes +import androidx.recyclerview.widget.RecyclerView +import code.name.monkey.appthemehelper.ThemeStore +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.helper.MusicPlayerRemote +import code.name.monkey.retromusic.util.ViewUtil +import com.google.android.material.appbar.AppBarLayout +import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView +import kotlinx.android.synthetic.main.fragment_main_activity_recycler_view.* + +abstract class AbsLibraryPagerRecyclerViewFragment, LM : RecyclerView.LayoutManager> : AbsLibraryPagerFragment(), AppBarLayout.OnOffsetChangedListener { + + protected var adapter: A? = null + protected var layoutManager: LM? = null + + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + val view = inflater.inflate(R.layout.fragment_main_activity_recycler_view, container, false); + return view + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + libraryFragment.addOnAppBarOffsetChangedListener(this) + initLayoutManager() + initAdapter() + setUpRecyclerView() + } + + private fun setUpRecyclerView() { + if (recyclerView is FastScrollRecyclerView) { + ViewUtil.setUpFastScrollRecyclerViewColor(activity, recyclerView as FastScrollRecyclerView, ThemeStore.accentColor(activity!!)) + } + recyclerView.layoutManager = layoutManager + recyclerView.adapter = adapter + } + + private fun initAdapter() { + adapter = createAdapter() + adapter!!.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { + override fun onChanged() { + super.onChanged() + checkIsEmpty() + checkForPadding() + } + }) + } + + protected open val emptyMessage: Int + @StringRes + get() = R.string.empty + + private fun checkIsEmpty() { + empty.setText(emptyMessage) + empty.visibility = if (adapter!!.itemCount == 0) View.VISIBLE else View.GONE + } + + private fun checkForPadding() { + val height = if (MusicPlayerRemote.playingQueue.isEmpty()) + resources.getDimensionPixelSize(R.dimen.mini_player_height) + else + 0 + recyclerView.setPadding(0, 0, 0, height) + } + + private fun initLayoutManager() { + layoutManager = createLayoutManager() + } + + protected abstract fun createLayoutManager(): LM + + @NonNull + protected abstract fun createAdapter(): A + + override fun onOffsetChanged(p0: AppBarLayout?, i: Int) { + container.setPadding(container.paddingLeft, container.paddingTop, + container.paddingRight, libraryFragment.totalAppBarScrollingRange + i) + } + + override fun onQueueChanged() { + super.onQueueChanged() + checkForPadding() + } + + override fun onServiceConnected() { + super.onServiceConnected() + checkForPadding() + } + + protected fun invalidateLayoutManager() { + initLayoutManager() + recyclerView.layoutManager = layoutManager + } + + protected fun invalidateAdapter() { + initAdapter() + checkIsEmpty() + recyclerView.adapter = adapter + } + + override fun onDestroyView() { + super.onDestroyView() + libraryFragment.removeOnAppBarOffsetChangedListener(this) + } + + fun recyclerView(): RecyclerView { + return recyclerView + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsMainActivityFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsMainActivityFragment.java deleted file mode 100644 index 56d0699a..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsMainActivityFragment.java +++ /dev/null @@ -1,58 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.base; - -import android.os.Build; -import android.os.Bundle; -import android.view.View; - -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.appthemehelper.util.ColorUtil; -import code.name.monkey.appthemehelper.util.VersionUtils; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.dialogs.MainOptionsBottomSheetDialogFragment; -import code.name.monkey.retromusic.ui.activities.MainActivity; - - -public abstract class AbsMainActivityFragment extends AbsMusicServiceFragment { - - public MainActivity getMainActivity() { - return (MainActivity) getActivity(); - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - setHasOptionsMenu(true); - - getMainActivity().setNavigationbarColorAuto(); - getMainActivity().setLightNavigationBar(true); - getMainActivity().setTaskDescriptionColorAuto(); - getMainActivity().hideStatusBar(); - getMainActivity().setBottomBarVisibility(View.VISIBLE); - } - - private void setStatusbarColor(View view, int color) { - final View statusBar = view.findViewById(R.id.status_bar); - if (statusBar != null) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - statusBar.setBackgroundColor(color); - getMainActivity().setLightStatusbarAuto(color); - } else { - statusBar.setBackgroundColor(color); - } - } - } - - public void setStatusbarColorAuto(View view) { - // we don't want to use statusbar color because we are doing the color darkening on our own to support KitKat - //noinspection ConstantConditions - if (VersionUtils.hasMarshmallow()) { - setStatusbarColor(view, ThemeStore.primaryColor(getContext())); - } else { - setStatusbarColor(view, ColorUtil.darkenColor(ThemeStore.primaryColor(getContext()))); - } - } - - protected void showMainMenu() { - MainOptionsBottomSheetDialogFragment.newInstance().show(getChildFragmentManager(), "Main Menu"); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsMainActivityFragment.kt b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsMainActivityFragment.kt new file mode 100644 index 00000000..f0a231f4 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsMainActivityFragment.kt @@ -0,0 +1,56 @@ +package code.name.monkey.retromusic.ui.fragments.base + +import android.os.Build +import android.os.Bundle +import android.view.View + +import code.name.monkey.appthemehelper.ThemeStore +import code.name.monkey.appthemehelper.util.ColorUtil +import code.name.monkey.appthemehelper.util.VersionUtils +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.dialogs.MainOptionsBottomSheetDialogFragment +import code.name.monkey.retromusic.ui.activities.MainActivity + + +abstract class AbsMainActivityFragment : AbsMusicServiceFragment() { + + val mainActivity: MainActivity + get() = activity as MainActivity + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + setHasOptionsMenu(true) + + mainActivity.setNavigationbarColorAuto() + mainActivity.setLightNavigationBar(true) + mainActivity.setTaskDescriptionColorAuto() + mainActivity.hideStatusBar() + mainActivity.setBottomBarVisibility(View.VISIBLE) + } + + private fun setStatusbarColor(view: View, color: Int) { + val statusBar = view.findViewById(R.id.status_bar) + if (statusBar != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + statusBar.setBackgroundColor(color) + mainActivity.setLightStatusbarAuto(color) + } else { + statusBar.setBackgroundColor(color) + } + } + } + + fun setStatusbarColorAuto(view: View) { + // we don't want to use statusbar color because we are doing the color darkening on our own to support KitKat + + if (VersionUtils.hasMarshmallow()) { + setStatusbarColor(view, ThemeStore.primaryColor(context!!)) + } else { + setStatusbarColor(view, ColorUtil.darkenColor(ThemeStore.primaryColor(context!!))) + } + } + + protected fun showMainMenu() { + MainOptionsBottomSheetDialogFragment.newInstance().show(childFragmentManager, "Main Menu") + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsMusicServiceFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsMusicServiceFragment.java deleted file mode 100644 index 71b9d11c..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsMusicServiceFragment.java +++ /dev/null @@ -1,93 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.base; - -import android.content.Context; -import android.os.Bundle; -import android.view.View; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; -import code.name.monkey.retromusic.interfaces.MusicServiceEventListener; -import code.name.monkey.retromusic.ui.activities.base.AbsMusicServiceActivity; - -/** - * Created by hemanths on 18/08/17. - */ - -public class AbsMusicServiceFragment extends Fragment implements MusicServiceEventListener { - - private AbsMusicServiceActivity activity; - - @Nullable - public AbsMusicServiceActivity getPlayerActivity() { - return activity; - } - - @Override - public void onAttach(Context context) { - super.onAttach(context); - try { - activity = (AbsMusicServiceActivity) context; - } catch (ClassCastException e) { - throw new RuntimeException(context.getClass().getSimpleName() + " must be an instance of " + AbsMusicServiceActivity.class.getSimpleName()); - } - } - - @Override - public void onDetach() { - super.onDetach(); - activity = null; - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - activity.addMusicServiceEventListener(this); - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - activity.removeMusicServiceEventListener(this); - } - - @Override - public void onPlayingMetaChanged() { - - } - - @Override - public void onServiceConnected() { - - } - - @Override - public void onServiceDisconnected() { - - } - - @Override - public void onQueueChanged() { - - } - - @Override - public void onPlayStateChanged() { - - } - - @Override - public void onRepeatModeChanged() { - - } - - @Override - public void onShuffleModeChanged() { - - } - - @Override - public void onMediaStoreChanged() { - - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsMusicServiceFragment.kt b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsMusicServiceFragment.kt new file mode 100644 index 00000000..48778ec9 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsMusicServiceFragment.kt @@ -0,0 +1,75 @@ +package code.name.monkey.retromusic.ui.fragments.base + +import android.content.Context +import android.os.Bundle +import android.view.View +import androidx.fragment.app.Fragment +import code.name.monkey.retromusic.interfaces.MusicServiceEventListener +import code.name.monkey.retromusic.ui.activities.base.AbsMusicServiceActivity + +/** + * Created by hemanths on 18/08/17. + */ + +open class AbsMusicServiceFragment : Fragment(), MusicServiceEventListener { + + var playerActivity: AbsMusicServiceActivity? = null + private set + + override fun onAttach(context: Context?) { + super.onAttach(context) + try { + playerActivity = context as AbsMusicServiceActivity? + } catch (e: ClassCastException) { + throw RuntimeException(context!!.javaClass.simpleName + " must be an instance of " + AbsMusicServiceActivity::class.java.simpleName) + } + + } + + override fun onDetach() { + super.onDetach() + playerActivity = null + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + playerActivity!!.addMusicServiceEventListener(this) + } + + override fun onDestroyView() { + super.onDestroyView() + playerActivity!!.removeMusicServiceEventListener(this) + } + + override fun onPlayingMetaChanged() { + + } + + override fun onServiceConnected() { + + } + + override fun onServiceDisconnected() { + + } + + override fun onQueueChanged() { + + } + + override fun onPlayStateChanged() { + + } + + override fun onRepeatModeChanged() { + + } + + override fun onShuffleModeChanged() { + + } + + override fun onMediaStoreChanged() { + + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsPlayerControlsFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsPlayerControlsFragment.java deleted file mode 100644 index 8948f591..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsPlayerControlsFragment.java +++ /dev/null @@ -1,52 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.base; - -import android.view.View; -import android.view.animation.AccelerateInterpolator; -import android.view.animation.DecelerateInterpolator; - -import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper; - -/** - * Created by hemanths on 24/09/17. - */ - -public abstract class AbsPlayerControlsFragment extends AbsMusicServiceFragment - implements MusicProgressViewUpdateHelper.Callback { - - protected abstract void show(); - - protected abstract void hide(); - - protected abstract void updateShuffleState(); - - protected abstract void updateRepeatState(); - - protected abstract void setUpProgressSlider(); - - public abstract void setDark(int color); - - public void showBouceAnimation(View view) { - view.clearAnimation(); - - view.setScaleX(0.9f); - view.setScaleY(0.9f); - view.setVisibility(View.VISIBLE); - view.setPivotX(view.getWidth() / 2); - view.setPivotY(view.getHeight() / 2); - - view.animate() - .setDuration(200) - .setInterpolator(new DecelerateInterpolator()) - .scaleX(1.1f) - .scaleY(1.1f) - .withEndAction(() -> view.animate() - .setDuration(200) - .setInterpolator(new AccelerateInterpolator()) - .scaleX(1f) - .scaleY(1f) - .alpha(1f) - .start()) - .start(); - } - -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsPlayerControlsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsPlayerControlsFragment.kt new file mode 100644 index 00000000..efe3631c --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsPlayerControlsFragment.kt @@ -0,0 +1,80 @@ +package code.name.monkey.retromusic.ui.fragments.base + +import android.os.Bundle +import android.view.View +import android.view.animation.AccelerateInterpolator +import android.view.animation.DecelerateInterpolator +import android.widget.ImageButton +import android.widget.TextView +import androidx.appcompat.widget.AppCompatSeekBar +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper + +/** + * Created by hemanths on 24/09/17. + */ + +abstract class AbsPlayerControlsFragment : AbsMusicServiceFragment(), MusicProgressViewUpdateHelper.Callback { + + protected abstract fun show() + + protected abstract fun hide() + + protected abstract fun updateShuffleState() + + protected abstract fun updateRepeatState() + + protected abstract fun setUpProgressSlider() + + abstract fun setDark(color: Int) + + fun showBouceAnimation(view: View) { + view.clearAnimation() + + view.scaleX = 0.9f + view.scaleY = 0.9f + view.visibility = View.VISIBLE + view.pivotX = (view.width / 2).toFloat() + view.pivotY = (view.height / 2).toFloat() + + view.animate() + .setDuration(200) + .setInterpolator(DecelerateInterpolator()) + .scaleX(1.1f) + .scaleY(1.1f) + .withEndAction { + view.animate() + .setDuration(200) + .setInterpolator(AccelerateInterpolator()) + .scaleX(1f) + .scaleY(1f) + .alpha(1f) + .start() + } + .start() + } + + + var prevButton: ImageButton? = null + var nextButton: ImageButton? = null + var repeatButton: ImageButton? = null + var shuffleButton: ImageButton? = null + var progressSlider: AppCompatSeekBar? = null + var songTotalTime: TextView? = null + var songCurrentProgress: TextView? = null + var volumeContainer: View? = null + var playPauseFab: ImageButton? = null + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + playPauseFab = view.findViewById(R.id.player_play_pause_button) + prevButton = view.findViewById(R.id.player_prev_button) + nextButton = view.findViewById(R.id.player_next_button) + repeatButton = view.findViewById(R.id.player_repeat_button) + shuffleButton = view.findViewById(R.id.player_shuffle_button) + progressSlider = view.findViewById(R.id.player_progress_slider) + songTotalTime = view.findViewById(R.id.player_song_total_time) + songCurrentProgress = view.findViewById(R.id.player_song_current_progress) + volumeContainer = view.findViewById(R.id.volume_fragment_container) + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsPlayerFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsPlayerFragment.java deleted file mode 100644 index 6ffd741a..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsPlayerFragment.java +++ /dev/null @@ -1,231 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.base; - -import android.annotation.SuppressLint; -import android.app.Activity; -import android.content.ContentUris; -import android.content.Context; -import android.content.Intent; -import android.graphics.drawable.Drawable; -import android.media.MediaMetadataRetriever; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Bundle; -import android.provider.MediaStore; -import android.view.MenuItem; -import android.view.View; -import android.widget.Toast; - -import androidx.annotation.NonNull; -import androidx.appcompat.widget.Toolbar; -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog; -import code.name.monkey.retromusic.dialogs.CreatePlaylistDialog; -import code.name.monkey.retromusic.dialogs.DeleteSongsDialog; -import code.name.monkey.retromusic.dialogs.SleepTimerDialog; -import code.name.monkey.retromusic.dialogs.SongDetailDialog; -import code.name.monkey.retromusic.dialogs.SongShareDialog; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.interfaces.PaletteColorHolder; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.ui.activities.tageditor.AbsTagEditorActivity; -import code.name.monkey.retromusic.ui.activities.tageditor.SongTagEditorActivity; -import code.name.monkey.retromusic.util.MusicUtil; -import code.name.monkey.retromusic.util.NavigationUtil; -import code.name.monkey.retromusic.util.PreferenceUtil; -import code.name.monkey.retromusic.util.RetroUtil; -import code.name.monkey.retromusic.views.FitSystemWindowsLayout; - -public abstract class AbsPlayerFragment extends AbsMusicServiceFragment implements Toolbar.OnMenuItemClickListener, PaletteColorHolder { - public static final String TAG = AbsPlayerFragment.class.getSimpleName(); - private Callbacks callbacks; - private AsyncTask updateIsFavoriteTask; - - - @Override - public void onAttach(Context context) { - super.onAttach(context); - try { - callbacks = (Callbacks) context; - } catch (ClassCastException e) { - throw new RuntimeException(context.getClass().getSimpleName() + " must implement " + Callbacks.class.getSimpleName()); - } - } - - @Override - public void onDetach() { - super.onDetach(); - callbacks = null; - } - - @SuppressWarnings("ConstantConditions") - @Override - public boolean onMenuItemClick(MenuItem item) { - final Song song = MusicPlayerRemote.getCurrentSong(); - switch (item.getItemId()) { - case R.id.action_toggle_favorite: - toggleFavorite(song); - return true; - case R.id.action_share: - if (getFragmentManager() != null) { - SongShareDialog.create(song).show(getFragmentManager(), "SHARE_SONG"); - } - return true; - case R.id.action_delete_from_device: - DeleteSongsDialog.create(song) - .show(getActivity().getSupportFragmentManager(), "DELETE_SONGS"); - return true; - case R.id.action_add_to_playlist: - if (getFragmentManager() != null) { - AddToPlaylistDialog.create(song).show(getFragmentManager(), "ADD_PLAYLIST"); - } - return true; - case R.id.action_clear_playing_queue: - MusicPlayerRemote.clearQueue(); - return true; - case R.id.action_save_playing_queue: - CreatePlaylistDialog.create(MusicPlayerRemote.getPlayingQueue()) - .show(getActivity().getSupportFragmentManager(), "ADD_TO_PLAYLIST"); - return true; - case R.id.action_tag_editor: - Intent intent = new Intent(getActivity(), SongTagEditorActivity.class); - intent.putExtra(AbsTagEditorActivity.EXTRA_ID, song.id); - startActivity(intent); - return true; - case R.id.action_details: - if (getFragmentManager() != null) { - SongDetailDialog.create(song).show(getFragmentManager(), "SONG_DETAIL"); - } - return true; - case R.id.action_go_to_album: - NavigationUtil.goToAlbum(getActivity(), song.albumId); - return true; - case R.id.action_go_to_artist: - NavigationUtil.goToArtist(getActivity(), song.artistId); - return true; - case R.id.now_playing: - NavigationUtil.goToPlayingQueue(getActivity()); - return true; - case R.id.action_show_lyrics: - NavigationUtil.goToLyrics(getActivity()); - return true; - case R.id.action_equalizer: - NavigationUtil.openEqualizer(getActivity()); - return true; - case R.id.action_sleep_timer: - new SleepTimerDialog().show(getFragmentManager(), TAG); - return true; - case R.id.action_set_as_ringtone: - MusicUtil.setRingtone(getActivity(), song.id); - return true; - case R.id.action_settings: - NavigationUtil.goToSettings(getActivity()); - return true; - case R.id.action_go_to_genre: - MediaMetadataRetriever retriever = new MediaMetadataRetriever(); - Uri trackUri = ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, song.id); - retriever.setDataSource(getActivity(), trackUri); - String genre = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_GENRE); - if (genre == null) { - genre = "Not Specified"; - } - Toast.makeText(getContext(), genre, Toast.LENGTH_SHORT).show(); - return true; - } - return false; - } - - protected void toggleFavorite(Song song) { - MusicUtil.toggleFavorite(getActivity(), song); - } - - public abstract void onShow(); - - public abstract void onHide(); - - public abstract boolean onBackPressed(); - - public abstract Toolbar getToolbar(); - - public abstract int toolbarIconColor(); - - @Override - public void onServiceConnected() { - updateIsFavorite(); - } - - @Override - public void onPlayingMetaChanged() { - updateIsFavorite(); - } - - @Override - public void onDestroyView() { - if (updateIsFavoriteTask != null && !updateIsFavoriteTask.isCancelled()) { - updateIsFavoriteTask.cancel(true); - } - super.onDestroyView(); - } - - @SuppressLint("StaticFieldLeak") - public void updateIsFavorite() { - if (updateIsFavoriteTask != null) { - updateIsFavoriteTask.cancel(false); - } - updateIsFavoriteTask = new AsyncTask() { - @Override - protected Boolean doInBackground(Song... params) { - Activity activity = getActivity(); - if (activity != null) { - return MusicUtil.isFavorite(getActivity(), params[0]); - } else { - cancel(false); - return null; - } - } - - @Override - protected void onPostExecute(Boolean isFavorite) { - Activity activity = getActivity(); - if (activity != null) { - int res = isFavorite ? R.drawable.ic_favorite_white_24dp - : R.drawable.ic_favorite_border_white_24dp; - Drawable drawable = RetroUtil.getTintedVectorDrawable(activity, res, toolbarIconColor()); - getToolbar().getMenu().findItem(R.id.action_toggle_favorite) - .setIcon(drawable) - .setTitle(isFavorite ? getString(R.string.action_remove_from_favorites) : getString(R.string.action_add_to_favorites)); - } - } - }.execute(MusicPlayerRemote.getCurrentSong()); - } - - public Callbacks getCallbacks() { - return callbacks; - } - - - @SuppressWarnings("ConstantConditions") - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - view.setBackgroundColor(ThemeStore.primaryColor(getActivity())); - - if (PreferenceUtil.getInstance().getFullScreenMode()) { - if (view.findViewById(R.id.status_bar) != null) - view.findViewById(R.id.status_bar).setVisibility(View.GONE); - } - } - - public void setSafeArea(View safeArea) { - FitSystemWindowsLayout layout = safeArea.findViewById(R.id.safeArea); - if (layout != null) { - layout.setFit(!PreferenceUtil.getInstance().getFullScreenMode()); - } - } - - public interface Callbacks { - - void onPaletteColorChanged(); - } - -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsPlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsPlayerFragment.kt new file mode 100644 index 00000000..4eff06f4 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsPlayerFragment.kt @@ -0,0 +1,227 @@ +package code.name.monkey.retromusic.ui.fragments.base + +import android.annotation.SuppressLint +import android.content.ContentUris +import android.content.Context +import android.content.Intent +import android.media.MediaMetadataRetriever +import android.os.AsyncTask +import android.os.Bundle +import android.provider.MediaStore +import android.view.MenuItem +import android.view.View +import android.widget.Toast +import androidx.appcompat.widget.Toolbar +import code.name.monkey.appthemehelper.ThemeStore +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.dialogs.* +import code.name.monkey.retromusic.helper.MusicPlayerRemote +import code.name.monkey.retromusic.interfaces.PaletteColorHolder +import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.ui.activities.tageditor.AbsTagEditorActivity +import code.name.monkey.retromusic.ui.activities.tageditor.SongTagEditorActivity +import code.name.monkey.retromusic.ui.fragments.player.PlayerAlbumCoverFragment +import code.name.monkey.retromusic.util.MusicUtil +import code.name.monkey.retromusic.util.NavigationUtil +import code.name.monkey.retromusic.util.PreferenceUtil +import code.name.monkey.retromusic.util.RetroUtil +import code.name.monkey.retromusic.views.FitSystemWindowsLayout + +abstract class AbsPlayerFragment : AbsMusicServiceFragment(), Toolbar.OnMenuItemClickListener, PaletteColorHolder, PlayerAlbumCoverFragment.Callbacks { + var callbacks: Callbacks? = null + private set + private var updateIsFavoriteTask: AsyncTask<*, *, *>? = null + + protected var toolbar: Toolbar? = null + + + override fun onAttach(context: Context?) { + super.onAttach(context) + try { + callbacks = context as Callbacks? + } catch (e: ClassCastException) { + throw RuntimeException(context!!.javaClass.simpleName + " must implement " + Callbacks::class.java.simpleName) + } + + } + + override fun onDetach() { + super.onDetach() + callbacks = null + } + + override fun onMenuItemClick(item: MenuItem): Boolean { + val song = MusicPlayerRemote.currentSong + when (item.itemId) { + R.id.action_toggle_favorite -> { + toggleFavorite(song) + return true + } + R.id.action_share -> { + if (fragmentManager != null) { + SongShareDialog.create(song).show(fragmentManager!!, "SHARE_SONG") + } + return true + } + R.id.action_delete_from_device -> { + DeleteSongsDialog.create(song) + .show(activity!!.supportFragmentManager, "DELETE_SONGS") + return true + } + R.id.action_add_to_playlist -> { + if (fragmentManager != null) { + AddToPlaylistDialog.create(song).show(fragmentManager!!, "ADD_PLAYLIST") + } + return true + } + R.id.action_clear_playing_queue -> { + MusicPlayerRemote.clearQueue() + return true + } + R.id.action_save_playing_queue -> { + CreatePlaylistDialog.create(MusicPlayerRemote.playingQueue) + .show(activity!!.supportFragmentManager, "ADD_TO_PLAYLIST") + return true + } + R.id.action_tag_editor -> { + val intent = Intent(activity, SongTagEditorActivity::class.java) + intent.putExtra(AbsTagEditorActivity.EXTRA_ID, song.id) + startActivity(intent) + return true + } + R.id.action_details -> { + if (fragmentManager != null) { + SongDetailDialog.create(song).show(fragmentManager!!, "SONG_DETAIL") + } + return true + } + R.id.action_go_to_album -> { + NavigationUtil.goToAlbum(activity!!, song.albumId) + return true + } + R.id.action_go_to_artist -> { + NavigationUtil.goToArtist(activity!!, song.artistId) + return true + } + R.id.now_playing -> { + NavigationUtil.goToPlayingQueue(activity!!) + return true + } + R.id.action_show_lyrics -> { + NavigationUtil.goToLyrics(activity!!) + return true + } + R.id.action_equalizer -> { + NavigationUtil.openEqualizer(activity!!) + return true + } + R.id.action_sleep_timer -> { + SleepTimerDialog().show(fragmentManager!!, TAG) + return true + } + R.id.action_set_as_ringtone -> { + MusicUtil.setRingtone(activity!!, song.id) + return true + } + R.id.action_settings -> { + NavigationUtil.goToSettings(activity!!) + return true + } + R.id.action_go_to_genre -> { + val retriever = MediaMetadataRetriever() + val trackUri = ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, song.id.toLong()) + retriever.setDataSource(activity, trackUri) + var genre: String? = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_GENRE) + if (genre == null) { + genre = "Not Specified" + } + Toast.makeText(context, genre, Toast.LENGTH_SHORT).show() + return true + } + } + return false + } + + protected open fun toggleFavorite(song: Song) { + MusicUtil.toggleFavorite(activity!!, song) + } + + abstract fun onShow() + + abstract fun onHide() + + abstract fun onBackPressed(): Boolean + + abstract fun toolbarIconColor(): Int + + override fun onServiceConnected() { + updateIsFavorite() + } + + override fun onPlayingMetaChanged() { + updateIsFavorite() + } + + override fun onDestroyView() { + if (updateIsFavoriteTask != null && !updateIsFavoriteTask!!.isCancelled) { + updateIsFavoriteTask!!.cancel(true) + } + super.onDestroyView() + } + + @SuppressLint("StaticFieldLeak") + fun updateIsFavorite() { + if (updateIsFavoriteTask != null) { + updateIsFavoriteTask!!.cancel(false) + } + updateIsFavoriteTask = object : AsyncTask() { + override fun doInBackground(vararg params: Song): Boolean? { + val activity = activity + if (activity != null) { + return MusicUtil.isFavorite(getActivity()!!, params[0]) + } else { + cancel(false) + return null + } + } + + override fun onPostExecute(isFavorite: Boolean?) { + val activity = activity + if (activity != null) { + val res = if (isFavorite!!) + R.drawable.ic_favorite_white_24dp + else + R.drawable.ic_favorite_border_white_24dp + val drawable = RetroUtil.getTintedVectorDrawable(activity, res, toolbarIconColor()) + //toolbar!!.menu.findItem(R.id.action_toggle_favorite).setIcon(drawable).title = if (isFavorite) getString(R.string.action_remove_from_favorites) else getString(R.string.action_add_to_favorites) + } + } + }.execute(MusicPlayerRemote.currentSong) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + view.setBackgroundColor(ThemeStore.primaryColor(activity!!)) + if (PreferenceUtil.getInstance().fullScreenMode) { + if (view.findViewById(R.id.status_bar) != null) + view.findViewById(R.id.status_bar).visibility = View.GONE + } + } + + fun setSafeArea(safeArea: View) { + val layout = safeArea.findViewById(R.id.safeArea) + if (layout != null) { + layout.isFit = !PreferenceUtil.getInstance().fullScreenMode + } + } + + interface Callbacks { + + fun onPaletteColorChanged() + } + + companion object { + val TAG: String = AbsPlayerFragment::class.java.simpleName + } + +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/AlbumsFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/AlbumsFragment.java deleted file mode 100644 index eb70040a..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/AlbumsFragment.java +++ /dev/null @@ -1,175 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.mainactivity; - -import android.os.Bundle; - -import java.util.ArrayList; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.recyclerview.widget.GridLayoutManager; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.model.Album; -import code.name.monkey.retromusic.mvp.contract.AlbumContract; -import code.name.monkey.retromusic.mvp.presenter.AlbumPresenter; -import code.name.monkey.retromusic.ui.adapter.album.AlbumAdapter; -import code.name.monkey.retromusic.ui.fragments.base.AbsLibraryPagerRecyclerViewCustomGridSizeFragment; -import code.name.monkey.retromusic.util.PreferenceUtil; - -public class AlbumsFragment extends AbsLibraryPagerRecyclerViewCustomGridSizeFragment implements AlbumContract.AlbumView { - - public static final String TAG = AlbumsFragment.class.getSimpleName(); - - private AlbumPresenter presenter; - - public static AlbumsFragment newInstance() { - Bundle args = new Bundle(); - AlbumsFragment fragment = new AlbumsFragment(); - fragment.setArguments(args); - return fragment; - } - - @Override - protected GridLayoutManager createLayoutManager() { - return new GridLayoutManager(getActivity(), getGridSize()); - } - - @NonNull - @Override - protected AlbumAdapter createAdapter() { - int itemLayoutRes = getItemLayoutRes(); - notifyLayoutResChanged(itemLayoutRes); - if (itemLayoutRes != R.layout.item_list) { - //noinspection ConstantConditions - itemLayoutRes = PreferenceUtil.getInstance().getAlbumGridStyle(getContext()); - } - ArrayList dataSet = getAdapter() == null ? new ArrayList<>() : getAdapter().getDataSet(); - return new AlbumAdapter(getLibraryFragment().getMainActivity(), dataSet, itemLayoutRes, loadUsePalette(), getLibraryFragment()); - } - - @Override - protected int getEmptyMessage() { - return R.string.no_albums; - } - - @Override - public boolean loadUsePalette() { - //noinspection ConstantConditions - return PreferenceUtil.getInstance().albumColoredFooters(); - } - - @Override - protected void setUsePalette(boolean usePalette) { - getAdapter().usePalette(usePalette); - } - - @Override - protected void setGridSize(int gridSize) { - getLayoutManager().setSpanCount(gridSize); - getAdapter().notifyDataSetChanged(); - } - - @Override - protected void setSortOrder(String sortOrder) { - presenter.loadAlbums(); - } - - @Override - protected String loadSortOrder() { - //noinspection ConstantConditions - return PreferenceUtil.getInstance().getAlbumSortOrder(); - } - - @Override - protected void saveSortOrder(String sortOrder) { - //noinspection ConstantConditions - PreferenceUtil.getInstance().setAlbumSortOrder(sortOrder); - } - - @Override - protected int loadGridSize() { - //noinspection ConstantConditions - return PreferenceUtil.getInstance().getAlbumGridSize(getActivity()); - } - - @Override - protected void saveGridSize(int gridSize) { - //noinspection ConstantConditions - PreferenceUtil.getInstance().setAlbumGridSize(gridSize); - } - - @Override - protected int loadGridSizeLand() { - //noinspection ConstantConditions - return PreferenceUtil.getInstance().getAlbumGridSizeLand(getActivity()); - } - - @Override - protected void saveGridSizeLand(int gridSize) { - //noinspection ConstantConditions - PreferenceUtil.getInstance().setAlbumGridSizeLand(gridSize); - } - - @Override - protected void saveUsePalette(boolean usePalette) { - //noinspection ConstantConditions - PreferenceUtil.getInstance().setAlbumColoredFooters(usePalette); - } - - @Override - public void onMediaStoreChanged() { - presenter.loadAlbums(); - } - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - presenter = new AlbumPresenter(this); - } - - @Override - public void setMenuVisibility(boolean menuVisible) { - super.setMenuVisibility(menuVisible); - if (menuVisible) { - //noinspection ConstantConditions - getLibraryFragment().setTitle( - PreferenceUtil.getInstance().tabTitles() ? R.string.library - : R.string.albums); - } - } - - @Override - public void onResume() { - super.onResume(); - //noinspection ConstantConditions - getLibraryFragment().setTitle( - PreferenceUtil.getInstance().tabTitles() ? R.string.library : R.string.albums); - if (getAdapter().getDataSet().isEmpty()) { - presenter.subscribe(); - } - } - - @Override - public void onDestroy() { - super.onDestroy(); - presenter.unsubscribe(); - } - - @Override - public void loading() { - } - - @Override - public void showEmptyView() { - getAdapter().swapDataSet(new ArrayList<>()); - } - - @Override - public void completed() { - } - - @Override - public void showData(ArrayList albums) { - getAdapter().swapDataSet(albums); - } - -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/AlbumsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/AlbumsFragment.kt new file mode 100644 index 00000000..d8695449 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/AlbumsFragment.kt @@ -0,0 +1,149 @@ +package code.name.monkey.retromusic.ui.fragments.mainactivity + +import android.os.Bundle +import androidx.recyclerview.widget.GridLayoutManager +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.model.Album +import code.name.monkey.retromusic.mvp.contract.AlbumContract +import code.name.monkey.retromusic.mvp.presenter.AlbumPresenter +import code.name.monkey.retromusic.ui.adapter.album.AlbumAdapter +import code.name.monkey.retromusic.ui.fragments.base.AbsLibraryPagerRecyclerViewCustomGridSizeFragment +import code.name.monkey.retromusic.util.PreferenceUtil + +open class AlbumsFragment : AbsLibraryPagerRecyclerViewCustomGridSizeFragment(), AlbumContract.AlbumView { + + private var presenter: AlbumPresenter? = null + + + override val emptyMessage: Int + get() = R.string.no_albums + + override fun createLayoutManager(): GridLayoutManager { + return GridLayoutManager(activity, getGridSize()) + } + + override fun createAdapter(): AlbumAdapter { + var itemLayoutRes = itemLayoutRes + notifyLayoutResChanged(itemLayoutRes) + if (itemLayoutRes != R.layout.item_list) { + + itemLayoutRes = PreferenceUtil.getInstance().getAlbumGridStyle(context!!) + } + val dataSet = if (adapter == null) ArrayList() else adapter!!.dataSet + return AlbumAdapter(libraryFragment.mainActivity, dataSet, itemLayoutRes, loadUsePalette(), libraryFragment) + } + + public override fun loadUsePalette(): Boolean { + + return PreferenceUtil.getInstance().albumColoredFooters() + } + + override fun setUsePalette(usePalette: Boolean) { + adapter!!.usePalette(usePalette) + } + + override fun setGridSize(gridSize: Int) { + layoutManager!!.spanCount = gridSize + adapter!!.notifyDataSetChanged() + } + + override fun setSortOrder(sortOrder: String) { + presenter!!.loadAlbums() + } + + override fun loadSortOrder(): String { + + return PreferenceUtil.getInstance().albumSortOrder + } + + override fun saveSortOrder(sortOrder: String) { + + PreferenceUtil.getInstance().albumSortOrder = sortOrder + } + + override fun loadGridSize(): Int { + + return PreferenceUtil.getInstance().getAlbumGridSize(activity!!) + } + + override fun saveGridSize(gridColumns: Int) { + + PreferenceUtil.getInstance().setAlbumGridSize(gridColumns) + } + + override fun loadGridSizeLand(): Int { + + return PreferenceUtil.getInstance().getAlbumGridSizeLand(activity!!) + } + + override fun saveGridSizeLand(gridColumns: Int) { + + PreferenceUtil.getInstance().setAlbumGridSizeLand(gridColumns) + } + + override fun saveUsePalette(usePalette: Boolean) { + + PreferenceUtil.getInstance().setAlbumColoredFooters(usePalette) + } + + override fun onMediaStoreChanged() { + presenter!!.loadAlbums() + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + presenter = AlbumPresenter(this) + } + + override fun setMenuVisibility(menuVisible: Boolean) { + super.setMenuVisibility(menuVisible) + if (menuVisible) { + + libraryFragment.setTitle( + if (PreferenceUtil.getInstance().tabTitles()) + R.string.library + else + R.string.albums) + } + } + + override fun onResume() { + super.onResume() + + libraryFragment.setTitle( + if (PreferenceUtil.getInstance().tabTitles()) R.string.library else R.string.albums) + if (adapter!!.dataSet.isEmpty()) { + presenter!!.subscribe() + } + } + + override fun onDestroy() { + super.onDestroy() + presenter!!.unsubscribe() + } + + override fun loading() {} + + override fun showEmptyView() { + adapter!!.swapDataSet(ArrayList()) + } + + override fun completed() {} + + override fun showData(albums: ArrayList) { + adapter!!.swapDataSet(albums) + } + + companion object { + + val TAG = AlbumsFragment::class.java.simpleName + + fun newInstance(): AlbumsFragment { + val args = Bundle() + val fragment = AlbumsFragment() + fragment.arguments = args + return fragment + } + } + +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/ArtistsFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/ArtistsFragment.java deleted file mode 100644 index 5ef3f6b9..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/ArtistsFragment.java +++ /dev/null @@ -1,173 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.mainactivity; - -import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.recyclerview.widget.GridLayoutManager; - -import java.util.ArrayList; - -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.model.Artist; -import code.name.monkey.retromusic.mvp.contract.ArtistContract; -import code.name.monkey.retromusic.mvp.presenter.ArtistPresenter; -import code.name.monkey.retromusic.ui.adapter.artist.ArtistAdapter; -import code.name.monkey.retromusic.ui.fragments.base.AbsLibraryPagerRecyclerViewCustomGridSizeFragment; -import code.name.monkey.retromusic.util.PreferenceUtil; - -public class ArtistsFragment extends - AbsLibraryPagerRecyclerViewCustomGridSizeFragment implements - ArtistContract.ArtistView { - - public static final String TAG = ArtistsFragment.class.getSimpleName(); - private ArtistPresenter presenter; - - public static ArtistsFragment newInstance() { - - Bundle args = new Bundle(); - - ArtistsFragment fragment = new ArtistsFragment(); - fragment.setArguments(args); - return fragment; - } - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - presenter = new ArtistPresenter(this); - } - - @NonNull - @Override - protected GridLayoutManager createLayoutManager() { - return new GridLayoutManager(getActivity(), getGridSize()); - } - - @NonNull - @Override - protected ArtistAdapter createAdapter() { - int itemLayoutRes = getItemLayoutRes(); - notifyLayoutResChanged(itemLayoutRes); - if (itemLayoutRes != R.layout.item_list) { - itemLayoutRes = PreferenceUtil.getInstance().getArtistGridStyle(getContext()); - } - ArrayList dataSet = - getAdapter() == null ? new ArrayList<>() : getAdapter().getDataSet(); - return new ArtistAdapter(getLibraryFragment().getMainActivity(), dataSet, itemLayoutRes, - loadUsePalette(), getLibraryFragment()); - } - - @Override - protected int getEmptyMessage() { - return R.string.no_artists; - } - - @Override - public void onMediaStoreChanged() { - presenter.loadArtists(); - } - - @Override - protected int loadGridSize() { - return PreferenceUtil.getInstance().getArtistGridSize(getActivity()); - } - - @Override - protected void saveGridSize(int gridSize) { - PreferenceUtil.getInstance().setArtistGridSize(gridSize); - } - - @Override - protected int loadGridSizeLand() { - return PreferenceUtil.getInstance().getArtistGridSizeLand(getActivity()); - } - - @Override - protected void saveGridSizeLand(int gridSize) { - PreferenceUtil.getInstance().setArtistGridSizeLand(gridSize); - } - - @Override - protected void saveUsePalette(boolean usePalette) { - PreferenceUtil.getInstance().setArtistColoredFooters(usePalette); - } - - @Override - public boolean loadUsePalette() { - return PreferenceUtil.getInstance().artistColoredFooters(); - } - - @Override - protected void setUsePalette(boolean usePalette) { - getAdapter().usePalette(usePalette); - } - - @Override - protected void setGridSize(int gridSize) { - getLayoutManager().setSpanCount(gridSize); - getAdapter().notifyDataSetChanged(); - } - - - @Override - protected String loadSortOrder() { - return PreferenceUtil.getInstance().getArtistSortOrder(); - } - - @Override - protected void saveSortOrder(String sortOrder) { - PreferenceUtil.getInstance().setArtistSortOrder(sortOrder); - } - - @Override - protected void setSortOrder(String sortOrder) { - presenter.loadArtists(); - } - - - @Override - public void setMenuVisibility(boolean menuVisible) { - super.setMenuVisibility(menuVisible); - if (menuVisible) { - getLibraryFragment().setTitle( - PreferenceUtil.getInstance().tabTitles() ? R.string.library - : R.string.artists); - } - } - - @Override - public void onResume() { - super.onResume(); - getLibraryFragment().setTitle( - PreferenceUtil.getInstance().tabTitles() ? R.string.library : R.string.artists); - if (getAdapter().getDataSet().isEmpty()) { - presenter.subscribe(); - } - } - - @Override - public void onDestroy() { - super.onDestroy(); - presenter.unsubscribe(); - } - - @Override - public void loading() { - } - - @Override - public void showEmptyView() { - getAdapter().swapDataSet(new ArrayList<>()); - } - - @Override - public void completed() { - - } - - @Override - public void showData(ArrayList artists) { - getAdapter().swapDataSet(artists); - } - -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/ArtistsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/ArtistsFragment.kt new file mode 100644 index 00000000..cb67e5ba --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/ArtistsFragment.kt @@ -0,0 +1,143 @@ +package code.name.monkey.retromusic.ui.fragments.mainactivity + +import android.os.Bundle +import androidx.recyclerview.widget.GridLayoutManager +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.model.Artist +import code.name.monkey.retromusic.mvp.contract.ArtistContract +import code.name.monkey.retromusic.mvp.presenter.ArtistPresenter +import code.name.monkey.retromusic.ui.adapter.artist.ArtistAdapter +import code.name.monkey.retromusic.ui.fragments.base.AbsLibraryPagerRecyclerViewCustomGridSizeFragment +import code.name.monkey.retromusic.util.PreferenceUtil +import java.util.* + +class ArtistsFragment : AbsLibraryPagerRecyclerViewCustomGridSizeFragment(), ArtistContract.ArtistView { + private var presenter: ArtistPresenter? = null + + override val emptyMessage: Int + get() = R.string.no_artists + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + presenter = ArtistPresenter(this) + } + + override fun createLayoutManager(): GridLayoutManager { + return GridLayoutManager(activity, getGridSize()) + } + + override fun createAdapter(): ArtistAdapter { + var itemLayoutRes = itemLayoutRes + notifyLayoutResChanged(itemLayoutRes) + if (itemLayoutRes != R.layout.item_list) { + itemLayoutRes = PreferenceUtil.getInstance().getArtistGridStyle(context!!) + } + val dataSet = if (adapter == null) ArrayList() else adapter!!.dataSet + return ArtistAdapter(libraryFragment.mainActivity, dataSet, itemLayoutRes, loadUsePalette(), libraryFragment) + } + + override fun onMediaStoreChanged() { + presenter!!.loadArtists() + } + + override fun loadGridSize(): Int { + return PreferenceUtil.getInstance().getArtistGridSize(activity!!) + } + + override fun saveGridSize(gridColumns: Int) { + PreferenceUtil.getInstance().setArtistGridSize(gridColumns) + } + + override fun loadGridSizeLand(): Int { + return PreferenceUtil.getInstance().getArtistGridSizeLand(activity!!) + } + + override fun saveGridSizeLand(gridColumns: Int) { + PreferenceUtil.getInstance().setArtistGridSizeLand(gridColumns) + } + + override fun saveUsePalette(usePalette: Boolean) { + PreferenceUtil.getInstance().setArtistColoredFooters(usePalette) + } + + public override fun loadUsePalette(): Boolean { + return PreferenceUtil.getInstance().artistColoredFooters() + } + + override fun setUsePalette(usePalette: Boolean) { + adapter!!.usePalette(usePalette) + } + + override fun setGridSize(gridSize: Int) { + layoutManager!!.spanCount = gridSize + adapter!!.notifyDataSetChanged() + } + + + override fun loadSortOrder(): String { + return PreferenceUtil.getInstance().artistSortOrder + } + + override fun saveSortOrder(sortOrder: String) { + PreferenceUtil.getInstance().artistSortOrder = sortOrder + } + + override fun setSortOrder(sortOrder: String) { + presenter!!.loadArtists() + } + + + override fun setMenuVisibility(menuVisible: Boolean) { + super.setMenuVisibility(menuVisible) + if (menuVisible) { + libraryFragment.setTitle( + if (PreferenceUtil.getInstance().tabTitles()) + R.string.library + else + R.string.artists) + } + } + + override fun onResume() { + super.onResume() + libraryFragment.setTitle( + if (PreferenceUtil.getInstance().tabTitles()) R.string.library else R.string.artists) + if (adapter!!.dataSet.isEmpty()) { + presenter!!.subscribe() + } + } + + override fun onDestroy() { + super.onDestroy() + presenter!!.unsubscribe() + } + + override fun loading() {} + + override fun showEmptyView() { + adapter!!.swapDataSet(ArrayList()) + } + + override fun completed() { + + } + + override fun showData(artists: ArrayList) { + adapter!!.swapDataSet(artists) + } + + companion object { + + val TAG = ArtistsFragment::class.java.simpleName + + fun newInstance(): ArtistsFragment { + + val args = Bundle() + + val fragment = ArtistsFragment() + fragment.arguments = args + return fragment + } + } + +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/GenreFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/GenreFragment.java deleted file mode 100644 index 0ac8a9a1..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/GenreFragment.java +++ /dev/null @@ -1,110 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.mainactivity; - -import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.recyclerview.widget.LinearLayoutManager; -import android.view.Menu; -import android.view.MenuInflater; - -import java.util.ArrayList; - -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.model.Genre; -import code.name.monkey.retromusic.mvp.contract.GenreContract; -import code.name.monkey.retromusic.mvp.presenter.GenrePresenter; -import code.name.monkey.retromusic.ui.adapter.GenreAdapter; -import code.name.monkey.retromusic.ui.fragments.base.AbsLibraryPagerRecyclerViewFragment; -import code.name.monkey.retromusic.util.PreferenceUtil; - -public class GenreFragment extends - AbsLibraryPagerRecyclerViewFragment implements - GenreContract.GenreView { - - private GenrePresenter mPresenter; - - public static GenreFragment newInstance() { - Bundle args = new Bundle(); - GenreFragment fragment = new GenreFragment(); - fragment.setArguments(args); - return fragment; - } - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setHasOptionsMenu(true); - mPresenter = new GenrePresenter(this); - } - - @Override - public void setMenuVisibility(boolean menuVisible) { - super.setMenuVisibility(menuVisible); - if (menuVisible) { - getLibraryFragment().setTitle(PreferenceUtil.getInstance().tabTitles() ? R.string.library : R.string.genres); - } - } - - @Override - public void onResume() { - super.onResume(); - getLibraryFragment().setTitle(PreferenceUtil.getInstance().tabTitles() ? R.string.library : R.string.genres); - if (getAdapter().getDataSet().isEmpty()) { - mPresenter.subscribe(); - } - } - - - @Override - public void onDestroy() { - super.onDestroy(); - mPresenter.unsubscribe(); - } - - @NonNull - @Override - protected LinearLayoutManager createLayoutManager() { - return new LinearLayoutManager(getActivity()); - } - - @NonNull - @Override - protected GenreAdapter createAdapter() { - ArrayList dataSet = getAdapter() == null ? new ArrayList() : getAdapter().getDataSet(); - return new GenreAdapter(getLibraryFragment().getMainActivity(), dataSet, R.layout.item_list); - } - - @Override - public void loading() { - - } - - @Override - public void showData(ArrayList songs) { - getAdapter().swapDataSet(songs); - } - - @Override - public void showEmptyView() { - getAdapter().swapDataSet(new ArrayList()); - } - - @Override - public void completed() { - - } - - - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - super.onCreateOptionsMenu(menu, inflater); - menu.removeItem(R.id.action_sort_order); - menu.removeItem(R.id.action_grid_size); - menu.removeItem(R.id.action_new_playlist); - } - - @Override - protected int getEmptyMessage() { - return R.string.no_genres; - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/GenreFragment.kt b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/GenreFragment.kt new file mode 100644 index 00000000..84054572 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/GenreFragment.kt @@ -0,0 +1,94 @@ +package code.name.monkey.retromusic.ui.fragments.mainactivity + +import android.os.Bundle +import androidx.recyclerview.widget.LinearLayoutManager +import android.view.Menu +import android.view.MenuInflater + +import java.util.ArrayList + +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.model.Genre +import code.name.monkey.retromusic.mvp.contract.GenreContract +import code.name.monkey.retromusic.mvp.presenter.GenrePresenter +import code.name.monkey.retromusic.ui.adapter.GenreAdapter +import code.name.monkey.retromusic.ui.fragments.base.AbsLibraryPagerRecyclerViewFragment +import code.name.monkey.retromusic.util.PreferenceUtil + +class GenreFragment : AbsLibraryPagerRecyclerViewFragment(), GenreContract.GenreView { + + private var mPresenter: GenrePresenter? = null + + override val emptyMessage: Int + get() = R.string.no_genres + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setHasOptionsMenu(true) + mPresenter = GenrePresenter(this) + } + + override fun setMenuVisibility(menuVisible: Boolean) { + super.setMenuVisibility(menuVisible) + if (menuVisible) { + libraryFragment.setTitle(if (PreferenceUtil.getInstance().tabTitles()) R.string.library else R.string.genres) + } + } + + override fun onResume() { + super.onResume() + libraryFragment.setTitle(if (PreferenceUtil.getInstance().tabTitles()) R.string.library else R.string.genres) + if (adapter!!.dataSet.isEmpty()) { + mPresenter!!.subscribe() + } + } + + + override fun onDestroy() { + super.onDestroy() + mPresenter!!.unsubscribe() + } + + override fun createLayoutManager(): LinearLayoutManager { + return LinearLayoutManager(activity) + } + + override fun createAdapter(): GenreAdapter { + val dataSet = adapter!!.dataSet + return GenreAdapter(libraryFragment.mainActivity, dataSet, R.layout.item_list) + } + + override fun loading() { + + } + + override fun showData(songs: ArrayList) { + adapter!!.swapDataSet(songs) + } + + override fun showEmptyView() { + adapter!!.swapDataSet(ArrayList()) + } + + override fun completed() { + + } + + + override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { + super.onCreateOptionsMenu(menu, inflater) + menu!!.removeItem(R.id.action_sort_order) + menu.removeItem(R.id.action_grid_size) + menu.removeItem(R.id.action_new_playlist) + } + + companion object { + + fun newInstance(): GenreFragment { + val args = Bundle() + val fragment = GenreFragment() + fragment.arguments = args + return fragment + } + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/LibraryFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/LibraryFragment.java deleted file mode 100644 index efdd1d01..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/LibraryFragment.java +++ /dev/null @@ -1,476 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.mainactivity; - -import android.app.Activity; -import android.content.Intent; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.SubMenu; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import com.afollestad.materialcab.MaterialCab; -import com.google.android.material.appbar.AppBarLayout; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.StringRes; -import androidx.appcompat.widget.Toolbar; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentTransaction; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.Unbinder; -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.appthemehelper.common.ATHToolbarActivity; -import code.name.monkey.appthemehelper.util.ATHUtil; -import code.name.monkey.appthemehelper.util.TintHelper; -import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.dialogs.CreatePlaylistDialog; -import code.name.monkey.retromusic.dialogs.SleepTimerDialog; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.helper.SortOrder; -import code.name.monkey.retromusic.interfaces.CabHolder; -import code.name.monkey.retromusic.interfaces.MainActivityFragmentCallbacks; -import code.name.monkey.retromusic.loaders.SongLoader; -import code.name.monkey.retromusic.ui.activities.SettingsActivity; -import code.name.monkey.retromusic.ui.fragments.base.AbsLibraryPagerRecyclerViewCustomGridSizeFragment; -import code.name.monkey.retromusic.ui.fragments.base.AbsMainActivityFragment; -import code.name.monkey.retromusic.util.NavigationUtil; -import code.name.monkey.retromusic.util.RetroColorUtil; -import code.name.monkey.retromusic.util.RetroUtil; - -public class LibraryFragment extends AbsMainActivityFragment implements CabHolder, MainActivityFragmentCallbacks { - - public static final String TAG = "LibraryFragment"; - private static final String CURRENT_TAB_ID = "current_tab_id"; - - @BindView(R.id.toolbar) - Toolbar toolbar; - - @BindView(R.id.app_bar) - AppBarLayout appbar; - - @BindView(R.id.title) - TextView title; - - @BindView(R.id.fragment_container) - View contentContainer; - - - private Unbinder unBinder; - private MaterialCab cab; - private FragmentManager fragmentManager; - - public static Fragment newInstance(int tab) { - Bundle args = new Bundle(); - args.putInt(CURRENT_TAB_ID, tab); - LibraryFragment fragment = new LibraryFragment(); - fragment.setArguments(args); - return fragment; - } - - public static Fragment newInstance() { - return new LibraryFragment(); - } - - - public void setTitle(@StringRes int name) { - title.setText(getString(name)); - } - - public void addOnAppBarOffsetChangedListener(AppBarLayout.OnOffsetChangedListener onOffsetChangedListener) { - appbar.addOnOffsetChangedListener(onOffsetChangedListener); - } - - public void removeOnAppBarOffsetChangedListener(AppBarLayout.OnOffsetChangedListener onOffsetChangedListener) { - appbar.removeOnOffsetChangedListener(onOffsetChangedListener); - } - - public int getTotalAppBarScrollingRange() { - return appbar.getTotalScrollRange(); - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_library, container, false); - unBinder = ButterKnife.bind(this, view); - return view; - } - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - setStatusbarColorAuto(view); - setupToolbar(); - inflateFragment(); - - } - - private void inflateFragment() { - if (getArguments() == null) { - selectedFragment(SongsFragment.newInstance()); - return; - } - switch (getArguments().getInt(CURRENT_TAB_ID)) { - default: - case R.id.action_song: - selectedFragment(SongsFragment.newInstance()); - break; - case R.id.action_album: - selectedFragment(AlbumsFragment.newInstance()); - break; - case R.id.action_artist: - selectedFragment(ArtistsFragment.newInstance()); - break; - case R.id.action_playlist: - selectedFragment(PlaylistsFragment.newInstance()); - break; - } - } - - @SuppressWarnings("ConstantConditions") - private void setupToolbar() { - title.setTextColor(ThemeStore.textColorPrimary(getContext())); - - int primaryColor = ThemeStore.primaryColor(getContext()); - TintHelper.setTintAuto(contentContainer, primaryColor, true); - - toolbar.setBackgroundColor(primaryColor); - appbar.setBackgroundColor(primaryColor); - appbar.addOnOffsetChangedListener((appBarLayout, verticalOffset) -> - getMainActivity().setLightStatusbar(!ATHUtil.isWindowBackgroundDark(getContext()))); - getMainActivity().setTitle(null); - getMainActivity().setSupportActionBar(toolbar); - toolbar.setNavigationOnClickListener(v -> NavigationUtil.goToSearch(getMainActivity())); - toolbar.setOnClickListener(v -> showMainMenu()); - toolbar.setNavigationIcon(RetroUtil.getTintedDrawable(getMainActivity(), R.drawable.ic_search_white_24dp, ThemeStore.textColorPrimary(getMainActivity()))); - } - - private Fragment getCurrentFragment() { - if (fragmentManager == null) { - return SongsFragment.newInstance(); - } - return fragmentManager.findFragmentByTag(LibraryFragment.TAG); - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - unBinder.unbind(); - } - - @Override - public boolean handleBackPress() { - if (cab != null && cab.isActive()) { - cab.finish(); - return true; - } - return false; - } - - private void selectedFragment(Fragment fragment) { - fragmentManager = getChildFragmentManager(); - FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); - - fragmentTransaction - .replace(R.id.fragment_container, fragment, TAG) - .commit(); - } - - @NonNull - @Override - public MaterialCab openCab(int menuRes, MaterialCab.Callback callback) { - if (cab != null && cab.isActive()) { - cab.finish(); - } - //noinspection ConstantConditions - cab = new MaterialCab(getMainActivity(), R.id.cab_stub) - .setMenu(menuRes) - .setCloseDrawableRes(R.drawable.ic_close_white_24dp) - .setBackgroundColor( - RetroColorUtil.shiftBackgroundColorForLightText(ThemeStore.primaryColor(getActivity()))) - .start(callback); - return cab; - } - - - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - super.onCreateOptionsMenu(menu, inflater); - inflater.inflate(R.menu.menu_main, menu); - - Fragment currentFragment = getCurrentFragment(); - if (currentFragment instanceof AbsLibraryPagerRecyclerViewCustomGridSizeFragment - && currentFragment.isAdded()) { - AbsLibraryPagerRecyclerViewCustomGridSizeFragment fragment = (AbsLibraryPagerRecyclerViewCustomGridSizeFragment) currentFragment; - - MenuItem gridSizeItem = menu.findItem(R.id.action_grid_size); - if (RetroUtil.isLandscape()) { - gridSizeItem.setTitle(R.string.action_grid_size_land); - } - setUpGridSizeMenu(fragment, gridSizeItem.getSubMenu()); - - setUpSortOrderMenu(fragment, menu.findItem(R.id.action_sort_order).getSubMenu()); - - } else { - menu.add(0, R.id.action_new_playlist, 0, R.string.new_playlist_title); - menu.removeItem(R.id.action_grid_size); - } - Activity activity = getActivity(); - if (activity == null) { - return; - } - ToolbarContentTintHelper.handleOnCreateOptionsMenu(getActivity(), toolbar, menu, ATHToolbarActivity.getToolbarBackgroundColor(toolbar)); - } - - @Override - public void onPrepareOptionsMenu(Menu menu) { - super.onPrepareOptionsMenu(menu); - Activity activity = getActivity(); - if (activity == null) { - return; - } - ToolbarContentTintHelper.handleOnPrepareOptionsMenu(activity, toolbar); - } - - - private void setUpSortOrderMenu( - @NonNull AbsLibraryPagerRecyclerViewCustomGridSizeFragment fragment, - @NonNull SubMenu sortOrderMenu) { - String currentSortOrder = fragment.getSortOrder(); - sortOrderMenu.clear(); - - if (fragment instanceof AlbumsFragment) { - sortOrderMenu.add(0, R.id.action_album_sort_order_asc, 0, R.string.sort_order_a_z) - .setChecked(currentSortOrder.equals(SortOrder.AlbumSortOrder.ALBUM_A_Z)); - sortOrderMenu.add(0, R.id.action_album_sort_order_desc, 1, R.string.sort_order_z_a) - .setChecked(currentSortOrder.equals(SortOrder.AlbumSortOrder.ALBUM_Z_A)); - sortOrderMenu.add(0, R.id.action_album_sort_order_artist, 2, R.string.sort_order_artist) - .setChecked(currentSortOrder.equals(SortOrder.AlbumSortOrder.ALBUM_ARTIST)); - sortOrderMenu.add(0, R.id.action_album_sort_order_year, 3, R.string.sort_order_year) - .setChecked(currentSortOrder.equals(SortOrder.AlbumSortOrder.ALBUM_YEAR)); - } else if (fragment instanceof ArtistsFragment) { - sortOrderMenu.add(0, R.id.action_artist_sort_order_asc, 0, R.string.sort_order_a_z) - .setChecked(currentSortOrder.equals(SortOrder.ArtistSortOrder.ARTIST_A_Z)); - sortOrderMenu.add(0, R.id.action_artist_sort_order_desc, 1, R.string.sort_order_z_a) - .setChecked(currentSortOrder.equals(SortOrder.ArtistSortOrder.ARTIST_Z_A)); - } else if (fragment instanceof SongsFragment) { - sortOrderMenu.add(0, R.id.action_song_sort_order_asc, 0, R.string.sort_order_a_z) - .setChecked(currentSortOrder.equals(SortOrder.SongSortOrder.SONG_A_Z)); - sortOrderMenu.add(0, R.id.action_song_sort_order_desc, 1, R.string.sort_order_z_a) - .setChecked(currentSortOrder.equals(SortOrder.SongSortOrder.SONG_Z_A)); - sortOrderMenu.add(0, R.id.action_song_sort_order_artist, 2, R.string.sort_order_artist) - .setChecked(currentSortOrder.equals(SortOrder.SongSortOrder.SONG_ARTIST)); - sortOrderMenu.add(0, R.id.action_song_sort_order_album, 3, R.string.sort_order_album) - .setChecked(currentSortOrder.equals(SortOrder.SongSortOrder.SONG_ALBUM)); - sortOrderMenu.add(0, R.id.action_song_sort_order_year, 4, R.string.sort_order_year) - .setChecked(currentSortOrder.equals(SortOrder.SongSortOrder.SONG_YEAR)); - sortOrderMenu.add(0, R.id.action_song_sort_order_date, 4, R.string.sort_order_date) - .setChecked(currentSortOrder.equals(SortOrder.SongSortOrder.SONG_DATE)); - - } - - sortOrderMenu.setGroupCheckable(0, true, true); - } - - private boolean handleSortOrderMenuItem( - @NonNull AbsLibraryPagerRecyclerViewCustomGridSizeFragment - fragment, @NonNull MenuItem item) { - String sortOrder = null; - if (fragment instanceof AlbumsFragment) { - switch (item.getItemId()) { - case R.id.action_album_sort_order_asc: - sortOrder = SortOrder.AlbumSortOrder.ALBUM_A_Z; - break; - case R.id.action_album_sort_order_desc: - sortOrder = SortOrder.AlbumSortOrder.ALBUM_Z_A; - break; - case R.id.action_album_sort_order_artist: - sortOrder = SortOrder.AlbumSortOrder.ALBUM_ARTIST; - break; - case R.id.action_album_sort_order_year: - sortOrder = SortOrder.AlbumSortOrder.ALBUM_YEAR; - break; - } - } else if (fragment instanceof ArtistsFragment) { - switch (item.getItemId()) { - case R.id.action_artist_sort_order_asc: - sortOrder = SortOrder.ArtistSortOrder.ARTIST_A_Z; - break; - case R.id.action_artist_sort_order_desc: - sortOrder = SortOrder.ArtistSortOrder.ARTIST_Z_A; - break; - } - } else if (fragment instanceof SongsFragment) { - switch (item.getItemId()) { - case R.id.action_song_sort_order_asc: - sortOrder = SortOrder.SongSortOrder.SONG_A_Z; - break; - case R.id.action_song_sort_order_desc: - sortOrder = SortOrder.SongSortOrder.SONG_Z_A; - break; - case R.id.action_song_sort_order_artist: - sortOrder = SortOrder.SongSortOrder.SONG_ARTIST; - break; - case R.id.action_song_sort_order_album: - sortOrder = SortOrder.SongSortOrder.SONG_ALBUM; - break; - case R.id.action_song_sort_order_year: - sortOrder = SortOrder.SongSortOrder.SONG_YEAR; - break; - case R.id.action_song_sort_order_date: - sortOrder = SortOrder.SongSortOrder.SONG_DATE; - break; - - } - } - - if (sortOrder != null) { - item.setChecked(true); - fragment.setAndSaveSortOrder(sortOrder); - return true; - } - - return false; - } - - - @SuppressWarnings("ConstantConditions") - @Override - public boolean onOptionsItemSelected(@NonNull MenuItem item) { - //if (pager == null) return false; - Fragment currentFragment = getCurrentFragment(); - if (currentFragment instanceof AbsLibraryPagerRecyclerViewCustomGridSizeFragment) { - AbsLibraryPagerRecyclerViewCustomGridSizeFragment fragment = (AbsLibraryPagerRecyclerViewCustomGridSizeFragment) currentFragment; - if (handleGridSizeMenuItem(fragment, item)) { - return true; - } - if (handleSortOrderMenuItem(fragment, item)) { - return true; - } - } - int id = item.getItemId(); - switch (id) { - case R.id.action_new_playlist: - CreatePlaylistDialog.create().show(getChildFragmentManager(), "CREATE_PLAYLIST"); - return true; - case R.id.action_shuffle_all: - MusicPlayerRemote.openAndShuffleQueue(SongLoader.Companion.getAllSongs(getContext()) - .blockingFirst(), true); - return true; - case R.id.action_equalizer: - NavigationUtil.openEqualizer(getActivity()); - return true; - case R.id.action_sleep_timer: - if (getFragmentManager() != null) { - new SleepTimerDialog().show(getFragmentManager(), TAG); - } - return true; - case R.id.action_settings: - startActivity(new Intent(getContext(), SettingsActivity.class)); - break; - } - return super.onOptionsItemSelected(item); - } - - - private void setUpGridSizeMenu( - @NonNull AbsLibraryPagerRecyclerViewCustomGridSizeFragment fragment, - @NonNull SubMenu gridSizeMenu) { - switch (fragment.getGridSize()) { - case 1: - gridSizeMenu.findItem(R.id.action_grid_size_1).setChecked(true); - break; - case 2: - gridSizeMenu.findItem(R.id.action_grid_size_2).setChecked(true); - break; - case 3: - gridSizeMenu.findItem(R.id.action_grid_size_3).setChecked(true); - break; - case 4: - gridSizeMenu.findItem(R.id.action_grid_size_4).setChecked(true); - break; - case 5: - gridSizeMenu.findItem(R.id.action_grid_size_5).setChecked(true); - break; - case 6: - gridSizeMenu.findItem(R.id.action_grid_size_6).setChecked(true); - break; - case 7: - gridSizeMenu.findItem(R.id.action_grid_size_7).setChecked(true); - break; - case 8: - gridSizeMenu.findItem(R.id.action_grid_size_8).setChecked(true); - break; - } - int maxGridSize = fragment.getMaxGridSize(); - if (maxGridSize < 8) { - gridSizeMenu.findItem(R.id.action_grid_size_8).setVisible(false); - } - if (maxGridSize < 7) { - gridSizeMenu.findItem(R.id.action_grid_size_7).setVisible(false); - } - if (maxGridSize < 6) { - gridSizeMenu.findItem(R.id.action_grid_size_6).setVisible(false); - } - if (maxGridSize < 5) { - gridSizeMenu.findItem(R.id.action_grid_size_5).setVisible(false); - } - if (maxGridSize < 4) { - gridSizeMenu.findItem(R.id.action_grid_size_4).setVisible(false); - } - if (maxGridSize < 3) { - gridSizeMenu.findItem(R.id.action_grid_size_3).setVisible(false); - } - } - - - private boolean handleGridSizeMenuItem( - @NonNull AbsLibraryPagerRecyclerViewCustomGridSizeFragment - fragment, @NonNull MenuItem item) { - int gridSize = 0; - switch (item.getItemId()) { - case R.id.action_grid_size_1: - gridSize = 1; - break; - case R.id.action_grid_size_2: - gridSize = 2; - break; - case R.id.action_grid_size_3: - gridSize = 3; - break; - case R.id.action_grid_size_4: - gridSize = 4; - break; - case R.id.action_grid_size_5: - gridSize = 5; - break; - case R.id.action_grid_size_6: - gridSize = 6; - break; - case R.id.action_grid_size_7: - gridSize = 7; - break; - case R.id.action_grid_size_8: - gridSize = 8; - break; - } - - if (gridSize > 0) { - item.setChecked(true); - fragment.setAndSaveGridSize(gridSize); - return true; - } - return false; - } - - -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/LibraryFragment.kt b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/LibraryFragment.kt new file mode 100644 index 00000000..a9d4b4e0 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/LibraryFragment.kt @@ -0,0 +1,360 @@ +package code.name.monkey.retromusic.ui.fragments.mainactivity + +import android.content.Intent +import android.os.Bundle +import android.view.* +import android.widget.TextView +import androidx.annotation.StringRes +import androidx.appcompat.widget.Toolbar +import androidx.fragment.app.Fragment +import butterknife.Unbinder +import code.name.monkey.appthemehelper.ThemeStore +import code.name.monkey.appthemehelper.common.ATHToolbarActivity +import code.name.monkey.appthemehelper.util.ATHUtil +import code.name.monkey.appthemehelper.util.TintHelper +import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.dialogs.CreatePlaylistDialog +import code.name.monkey.retromusic.dialogs.SleepTimerDialog +import code.name.monkey.retromusic.helper.MusicPlayerRemote +import code.name.monkey.retromusic.helper.SortOrder +import code.name.monkey.retromusic.interfaces.CabHolder +import code.name.monkey.retromusic.interfaces.MainActivityFragmentCallbacks +import code.name.monkey.retromusic.loaders.SongLoader +import code.name.monkey.retromusic.ui.activities.SettingsActivity +import code.name.monkey.retromusic.ui.fragments.base.AbsLibraryPagerRecyclerViewCustomGridSizeFragment +import code.name.monkey.retromusic.ui.fragments.base.AbsMainActivityFragment +import code.name.monkey.retromusic.util.NavigationUtil +import code.name.monkey.retromusic.util.RetroColorUtil +import code.name.monkey.retromusic.util.RetroUtil +import com.afollestad.materialcab.MaterialCab +import com.google.android.material.appbar.AppBarLayout + +class LibraryFragment : AbsMainActivityFragment(), CabHolder, MainActivityFragmentCallbacks, AppBarLayout.OnOffsetChangedListener { + override fun onOffsetChanged(p0: AppBarLayout?, p1: Int) { + mainActivity.setLightStatusbar(!ATHUtil.isWindowBackgroundDark(context)) + } + + + lateinit var toolbar: Toolbar + lateinit var appbar: AppBarLayout + lateinit var title: TextView + lateinit var contentContainer: View + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + val view = inflater.inflate(R.layout.fragment_library, container, false) + toolbar = view.findViewById(R.id.toolbar) + appbar = view.findViewById(R.id.app_bar) + title = view.findViewById(R.id.title) + contentContainer = view.findViewById(R.id.fragment_container) + return view + } + + private var cab: MaterialCab? = null + + + val totalAppBarScrollingRange: Int + get() = appbar.totalScrollRange + + private val currentFragment: Fragment? + get() = if (fragmentManager == null) { + SongsFragment.newInstance() + } else fragmentManager!!.findFragmentByTag(LibraryFragment.TAG) + + + fun setTitle(@StringRes name: Int) { + title.text = getString(name) + } + + fun addOnAppBarOffsetChangedListener(onOffsetChangedListener: AppBarLayout.OnOffsetChangedListener) { + appbar.addOnOffsetChangedListener(onOffsetChangedListener) + } + + fun removeOnAppBarOffsetChangedListener(onOffsetChangedListener: AppBarLayout.OnOffsetChangedListener) { + appbar.removeOnOffsetChangedListener(onOffsetChangedListener) + } + + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setStatusbarColorAuto(view) + setupToolbar() + inflateFragment() + + } + + private fun inflateFragment() { + if (arguments == null) { + selectedFragment(SongsFragment.newInstance()) + return + } + when (arguments!!.getInt(CURRENT_TAB_ID)) { + R.id.action_song -> selectedFragment(SongsFragment.newInstance()) + R.id.action_album -> selectedFragment(AlbumsFragment.newInstance()) + R.id.action_artist -> selectedFragment(ArtistsFragment.newInstance()) + R.id.action_playlist -> selectedFragment(PlaylistsFragment.newInstance()) + else -> selectedFragment(SongsFragment.newInstance()) + } + } + + private fun setupToolbar() { + title.setTextColor(ThemeStore.textColorPrimary(context!!)) + + val primaryColor = ThemeStore.primaryColor(context!!) + TintHelper.setTintAuto(contentContainer, primaryColor, true) + + toolbar.setBackgroundColor(primaryColor) + appbar.setBackgroundColor(primaryColor) + appbar.addOnOffsetChangedListener(this) + mainActivity.title = null + mainActivity.setSupportActionBar(toolbar) + + toolbar.setNavigationOnClickListener { NavigationUtil.goToSearch(mainActivity) } + toolbar.setOnClickListener { showMainMenu() } + toolbar.navigationIcon = RetroUtil.getTintedDrawable(mainActivity, R.drawable.ic_search_white_24dp, ThemeStore.textColorPrimary(mainActivity)) + } + + + override fun handleBackPress(): Boolean { + if (cab != null && cab!!.isActive) { + cab!!.finish() + return true + } + return false + } + + private fun selectedFragment(fragment: Fragment) { + val fragmentManager = childFragmentManager + val fragmentTransaction = fragmentManager.beginTransaction() + + fragmentTransaction + .replace(R.id.fragment_container, fragment, TAG) + .commit() + } + + override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab { + if (cab != null && cab!!.isActive) { + cab!!.finish() + } + + cab = MaterialCab(mainActivity, R.id.cab_stub) + .setMenu(menuRes) + .setCloseDrawableRes(R.drawable.ic_close_white_24dp) + .setBackgroundColor( + RetroColorUtil.shiftBackgroundColorForLightText(ThemeStore.primaryColor(activity!!))) + .start(callback) + return cab as MaterialCab + } + + + override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { + super.onCreateOptionsMenu(menu, inflater) + inflater!!.inflate(R.menu.menu_main, menu) + + val currentFragment = currentFragment + if (currentFragment is AbsLibraryPagerRecyclerViewCustomGridSizeFragment<*, *> && currentFragment.isAdded) { + val fragment = currentFragment as AbsLibraryPagerRecyclerViewCustomGridSizeFragment<*, *>? + + val gridSizeItem = menu!!.findItem(R.id.action_grid_size) + if (RetroUtil.isLandscape()) { + gridSizeItem.setTitle(R.string.action_grid_size_land) + } + setUpGridSizeMenu(fragment!!, gridSizeItem.subMenu) + + setUpSortOrderMenu(fragment, menu.findItem(R.id.action_sort_order).subMenu) + + } else { + menu!!.add(0, R.id.action_new_playlist, 0, R.string.new_playlist_title) + menu.removeItem(R.id.action_grid_size) + } + activity ?: return + ToolbarContentTintHelper.handleOnCreateOptionsMenu(getActivity(), toolbar, menu, ATHToolbarActivity.getToolbarBackgroundColor(toolbar)) + } + + override fun onPrepareOptionsMenu(menu: Menu?) { + super.onPrepareOptionsMenu(menu) + val activity = activity ?: return + ToolbarContentTintHelper.handleOnPrepareOptionsMenu(activity, toolbar) + } + + + private fun setUpSortOrderMenu( + fragment: AbsLibraryPagerRecyclerViewCustomGridSizeFragment<*, *>, + sortOrderMenu: SubMenu) { + val currentSortOrder = fragment.getSortOrder() + sortOrderMenu.clear() + + if (fragment is AlbumsFragment) { + sortOrderMenu.add(0, R.id.action_album_sort_order_asc, 0, R.string.sort_order_a_z).isChecked = currentSortOrder == SortOrder.AlbumSortOrder.ALBUM_A_Z + sortOrderMenu.add(0, R.id.action_album_sort_order_desc, 1, R.string.sort_order_z_a).isChecked = currentSortOrder == SortOrder.AlbumSortOrder.ALBUM_Z_A + sortOrderMenu.add(0, R.id.action_album_sort_order_artist, 2, R.string.sort_order_artist).isChecked = currentSortOrder == SortOrder.AlbumSortOrder.ALBUM_ARTIST + sortOrderMenu.add(0, R.id.action_album_sort_order_year, 3, R.string.sort_order_year).isChecked = currentSortOrder == SortOrder.AlbumSortOrder.ALBUM_YEAR + } else if (fragment is ArtistsFragment) { + sortOrderMenu.add(0, R.id.action_artist_sort_order_asc, 0, R.string.sort_order_a_z).isChecked = currentSortOrder == SortOrder.ArtistSortOrder.ARTIST_A_Z + sortOrderMenu.add(0, R.id.action_artist_sort_order_desc, 1, R.string.sort_order_z_a).isChecked = currentSortOrder == SortOrder.ArtistSortOrder.ARTIST_Z_A + } else if (fragment is SongsFragment) { + sortOrderMenu.add(0, R.id.action_song_sort_order_asc, 0, R.string.sort_order_a_z).isChecked = currentSortOrder == SortOrder.SongSortOrder.SONG_A_Z + sortOrderMenu.add(0, R.id.action_song_sort_order_desc, 1, R.string.sort_order_z_a).isChecked = currentSortOrder == SortOrder.SongSortOrder.SONG_Z_A + sortOrderMenu.add(0, R.id.action_song_sort_order_artist, 2, R.string.sort_order_artist).isChecked = currentSortOrder == SortOrder.SongSortOrder.SONG_ARTIST + sortOrderMenu.add(0, R.id.action_song_sort_order_album, 3, R.string.sort_order_album).isChecked = currentSortOrder == SortOrder.SongSortOrder.SONG_ALBUM + sortOrderMenu.add(0, R.id.action_song_sort_order_year, 4, R.string.sort_order_year).isChecked = currentSortOrder == SortOrder.SongSortOrder.SONG_YEAR + sortOrderMenu.add(0, R.id.action_song_sort_order_date, 4, R.string.sort_order_date).isChecked = currentSortOrder == SortOrder.SongSortOrder.SONG_DATE + + } + + sortOrderMenu.setGroupCheckable(0, true, true) + } + + private fun handleSortOrderMenuItem( + fragment: AbsLibraryPagerRecyclerViewCustomGridSizeFragment<*, *>, item: MenuItem): Boolean { + var sortOrder: String? = null + if (fragment is AlbumsFragment) { + when (item.itemId) { + R.id.action_album_sort_order_asc -> sortOrder = SortOrder.AlbumSortOrder.ALBUM_A_Z + R.id.action_album_sort_order_desc -> sortOrder = SortOrder.AlbumSortOrder.ALBUM_Z_A + R.id.action_album_sort_order_artist -> sortOrder = SortOrder.AlbumSortOrder.ALBUM_ARTIST + R.id.action_album_sort_order_year -> sortOrder = SortOrder.AlbumSortOrder.ALBUM_YEAR + } + } else if (fragment is ArtistsFragment) { + when (item.itemId) { + R.id.action_artist_sort_order_asc -> sortOrder = SortOrder.ArtistSortOrder.ARTIST_A_Z + R.id.action_artist_sort_order_desc -> sortOrder = SortOrder.ArtistSortOrder.ARTIST_Z_A + } + } else if (fragment is SongsFragment) { + when (item.itemId) { + R.id.action_song_sort_order_asc -> sortOrder = SortOrder.SongSortOrder.SONG_A_Z + R.id.action_song_sort_order_desc -> sortOrder = SortOrder.SongSortOrder.SONG_Z_A + R.id.action_song_sort_order_artist -> sortOrder = SortOrder.SongSortOrder.SONG_ARTIST + R.id.action_song_sort_order_album -> sortOrder = SortOrder.SongSortOrder.SONG_ALBUM + R.id.action_song_sort_order_year -> sortOrder = SortOrder.SongSortOrder.SONG_YEAR + R.id.action_song_sort_order_date -> sortOrder = SortOrder.SongSortOrder.SONG_DATE + } + } + + if (sortOrder != null) { + item.isChecked = true + fragment.setAndSaveSortOrder(sortOrder) + return true + } + + return false + } + + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + //if (pager == null) return false; + val currentFragment = currentFragment + if (currentFragment is AbsLibraryPagerRecyclerViewCustomGridSizeFragment<*, *>) { + val fragment = currentFragment as AbsLibraryPagerRecyclerViewCustomGridSizeFragment<*, *>? + if (handleGridSizeMenuItem(fragment!!, item)) { + return true + } + if (handleSortOrderMenuItem(fragment, item)) { + return true + } + } + val id = item.itemId + when (id) { + R.id.action_new_playlist -> { + CreatePlaylistDialog.create().show(childFragmentManager, "CREATE_PLAYLIST") + return true + } + R.id.action_shuffle_all -> { + MusicPlayerRemote.openAndShuffleQueue(SongLoader.getAllSongs(context!!).blockingFirst(), true) + return true + } + R.id.action_equalizer -> { + NavigationUtil.openEqualizer(activity!!) + return true + } + R.id.action_sleep_timer -> { + if (fragmentManager != null) { + SleepTimerDialog().show(fragmentManager!!, TAG) + } + return true + } + R.id.action_settings -> startActivity(Intent(context, SettingsActivity::class.java)) + } + return super.onOptionsItemSelected(item) + } + + + private fun setUpGridSizeMenu( + fragment: AbsLibraryPagerRecyclerViewCustomGridSizeFragment<*, *>, + gridSizeMenu: SubMenu) { + when (fragment.getGridSize()) { + 1 -> gridSizeMenu.findItem(R.id.action_grid_size_1).isChecked = true + 2 -> gridSizeMenu.findItem(R.id.action_grid_size_2).isChecked = true + 3 -> gridSizeMenu.findItem(R.id.action_grid_size_3).isChecked = true + 4 -> gridSizeMenu.findItem(R.id.action_grid_size_4).isChecked = true + 5 -> gridSizeMenu.findItem(R.id.action_grid_size_5).isChecked = true + 6 -> gridSizeMenu.findItem(R.id.action_grid_size_6).isChecked = true + 7 -> gridSizeMenu.findItem(R.id.action_grid_size_7).isChecked = true + 8 -> gridSizeMenu.findItem(R.id.action_grid_size_8).isChecked = true + } + val maxGridSize = fragment.maxGridSize + if (maxGridSize < 8) { + gridSizeMenu.findItem(R.id.action_grid_size_8).isVisible = false + } + if (maxGridSize < 7) { + gridSizeMenu.findItem(R.id.action_grid_size_7).isVisible = false + } + if (maxGridSize < 6) { + gridSizeMenu.findItem(R.id.action_grid_size_6).isVisible = false + } + if (maxGridSize < 5) { + gridSizeMenu.findItem(R.id.action_grid_size_5).isVisible = false + } + if (maxGridSize < 4) { + gridSizeMenu.findItem(R.id.action_grid_size_4).isVisible = false + } + if (maxGridSize < 3) { + gridSizeMenu.findItem(R.id.action_grid_size_3).isVisible = false + } + } + + + private fun handleGridSizeMenuItem( + fragment: AbsLibraryPagerRecyclerViewCustomGridSizeFragment<*, *>, item: MenuItem): Boolean { + var gridSize = 0 + when (item.itemId) { + R.id.action_grid_size_1 -> gridSize = 1 + R.id.action_grid_size_2 -> gridSize = 2 + R.id.action_grid_size_3 -> gridSize = 3 + R.id.action_grid_size_4 -> gridSize = 4 + R.id.action_grid_size_5 -> gridSize = 5 + R.id.action_grid_size_6 -> gridSize = 6 + R.id.action_grid_size_7 -> gridSize = 7 + R.id.action_grid_size_8 -> gridSize = 8 + } + + if (gridSize > 0) { + item.isChecked = true + fragment.setAndSaveGridSize(gridSize) + return true + } + return false + } + + companion object { + + const val TAG: String = "LibraryFragment" + private const val CURRENT_TAB_ID = "current_tab_id" + + fun newInstance(tab: Int): Fragment { + val args = Bundle() + args.putInt(CURRENT_TAB_ID, tab) + val fragment = LibraryFragment() + fragment.arguments = args + return fragment + } + + fun newInstance(): Fragment { + return LibraryFragment() + } + } + + +} + diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/PlaylistsFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/PlaylistsFragment.java deleted file mode 100644 index 19c2d8f2..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/PlaylistsFragment.java +++ /dev/null @@ -1,120 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.mainactivity; - -import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.recyclerview.widget.LinearLayoutManager; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.View; - -import java.util.ArrayList; - -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.model.Playlist; -import code.name.monkey.retromusic.mvp.contract.PlaylistContract; -import code.name.monkey.retromusic.mvp.presenter.PlaylistPresenter; -import code.name.monkey.retromusic.ui.adapter.playlist.PlaylistAdapter; -import code.name.monkey.retromusic.ui.fragments.base.AbsLibraryPagerRecyclerViewFragment; -import code.name.monkey.retromusic.util.PreferenceUtil; - - -public class PlaylistsFragment extends AbsLibraryPagerRecyclerViewFragment implements - PlaylistContract.PlaylistView { - - private PlaylistPresenter presenter; - - public static PlaylistsFragment newInstance() { - Bundle args = new Bundle(); - PlaylistsFragment fragment = new PlaylistsFragment(); - fragment.setArguments(args); - return fragment; - } - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setHasOptionsMenu(true); - presenter = new PlaylistPresenter(this); - } - - @Override - protected LinearLayoutManager createLayoutManager() { - return new LinearLayoutManager(getActivity()); - } - - @NonNull - @Override - protected PlaylistAdapter createAdapter() { - return new PlaylistAdapter(getLibraryFragment().getMainActivity(), new ArrayList<>(), - R.layout.item_list, getLibraryFragment()); - } - - @Override - public void setMenuVisibility(boolean menuVisible) { - super.setMenuVisibility(menuVisible); - if (menuVisible) { - getLibraryFragment().setTitle(PreferenceUtil.getInstance().tabTitles() ? R.string.library : R.string.playlists); - } - } - - @Override - public void onResume() { - super.onResume(); - getLibraryFragment().setTitle(PreferenceUtil.getInstance().tabTitles() ? R.string.library : R.string.playlists); - if (getAdapter().getDataSet().isEmpty()) { - presenter.subscribe(); - } - } - - @Override - public void onDestroy() { - presenter.unsubscribe(); - super.onDestroy(); - } - - @Override - public void onMediaStoreChanged() { - super.onMediaStoreChanged(); - presenter.loadPlaylists(); - } - - @Override - public void loading() { - - } - - @Override - public void showEmptyView() { - getAdapter().swapDataSet(new ArrayList<>()); - } - - @Override - public void completed() { - - } - - @Override - public void showData(ArrayList playlists) { - getAdapter().swapDataSet(playlists); - } - - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - super.onCreateOptionsMenu(menu, inflater); - menu.removeItem(R.id.action_shuffle_all); - menu.removeItem(R.id.action_sort_order); - menu.removeItem(R.id.action_grid_size); - } - - @Override - protected int getEmptyMessage() { - return R.string.no_playlists; - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/PlaylistsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/PlaylistsFragment.kt new file mode 100644 index 00000000..76181522 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/PlaylistsFragment.kt @@ -0,0 +1,99 @@ +package code.name.monkey.retromusic.ui.fragments.mainactivity + +import android.os.Bundle +import androidx.recyclerview.widget.LinearLayoutManager +import android.view.Menu +import android.view.MenuInflater +import android.view.View + +import java.util.ArrayList + +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.model.Playlist +import code.name.monkey.retromusic.mvp.contract.PlaylistContract +import code.name.monkey.retromusic.mvp.presenter.PlaylistPresenter +import code.name.monkey.retromusic.ui.adapter.playlist.PlaylistAdapter +import code.name.monkey.retromusic.ui.fragments.base.AbsLibraryPagerRecyclerViewFragment +import code.name.monkey.retromusic.util.PreferenceUtil + + +class PlaylistsFragment : AbsLibraryPagerRecyclerViewFragment(), PlaylistContract.PlaylistView { + + private var presenter: PlaylistPresenter? = null + + override val emptyMessage: Int + get() = R.string.no_playlists + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setHasOptionsMenu(true) + presenter = PlaylistPresenter(this) + } + + override fun createLayoutManager(): LinearLayoutManager { + return LinearLayoutManager(activity) + } + + override fun createAdapter(): PlaylistAdapter { + return PlaylistAdapter(libraryFragment.mainActivity, ArrayList(), + R.layout.item_list, libraryFragment) + } + + override fun setMenuVisibility(menuVisible: Boolean) { + super.setMenuVisibility(menuVisible) + if (menuVisible) { + libraryFragment.setTitle(if (PreferenceUtil.getInstance().tabTitles()) R.string.library else R.string.playlists) + } + } + + override fun onResume() { + super.onResume() + libraryFragment.setTitle(if (PreferenceUtil.getInstance().tabTitles()) R.string.library else R.string.playlists) + if (adapter!!.dataSet.isEmpty()) { + presenter!!.subscribe() + } + } + + override fun onDestroy() { + presenter!!.unsubscribe() + super.onDestroy() + } + + override fun onMediaStoreChanged() { + super.onMediaStoreChanged() + presenter!!.loadPlaylists() + } + + override fun loading() { + + } + + override fun showEmptyView() { + adapter!!.swapDataSet(ArrayList()) + } + + override fun completed() { + + } + + override fun showData(playlists: ArrayList) { + adapter!!.swapDataSet(playlists) + } + + override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { + super.onCreateOptionsMenu(menu, inflater) + menu!!.removeItem(R.id.action_shuffle_all) + menu.removeItem(R.id.action_sort_order) + menu.removeItem(R.id.action_grid_size) + } + + companion object { + + fun newInstance(): PlaylistsFragment { + val args = Bundle() + val fragment = PlaylistsFragment() + fragment.arguments = args + return fragment + } + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/SongsFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/SongsFragment.java deleted file mode 100644 index 69fe954d..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/SongsFragment.java +++ /dev/null @@ -1,177 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.mainactivity; - -import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.recyclerview.widget.GridLayoutManager; - -import java.util.ArrayList; - -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.mvp.contract.SongContract; -import code.name.monkey.retromusic.mvp.presenter.SongPresenter; -import code.name.monkey.retromusic.ui.adapter.song.ShuffleButtonSongAdapter; -import code.name.monkey.retromusic.ui.adapter.song.SongAdapter; -import code.name.monkey.retromusic.ui.fragments.base.AbsLibraryPagerRecyclerViewCustomGridSizeFragment; -import code.name.monkey.retromusic.util.PreferenceUtil; - -@SuppressWarnings("ConstantConditions") -public class SongsFragment extends - AbsLibraryPagerRecyclerViewCustomGridSizeFragment implements - SongContract.SongView { - - private SongPresenter presenter; - - public SongsFragment() { - // Required empty public constructor - } - - public static SongsFragment newInstance() { - Bundle args = new Bundle(); - SongsFragment fragment = new SongsFragment(); - fragment.setArguments(args); - return fragment; - } - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - presenter = new SongPresenter(this); - } - - @NonNull - @Override - protected GridLayoutManager createLayoutManager() { - return new GridLayoutManager(getActivity(), getGridSize()); - } - - @Override - protected int getEmptyMessage() { - return R.string.no_songs; - } - - @NonNull - @Override - protected SongAdapter createAdapter() { - int itemLayoutRes = getItemLayoutRes(); - notifyLayoutResChanged(itemLayoutRes); - boolean usePalette = loadUsePalette(); - ArrayList dataSet = - getAdapter() == null ? new ArrayList() : getAdapter().getDataSet(); - - if (getGridSize() <= getMaxGridSizeForList()) { - return new ShuffleButtonSongAdapter(getLibraryFragment().getMainActivity(), dataSet, - itemLayoutRes, usePalette, getLibraryFragment()); - } - return new SongAdapter(getLibraryFragment().getMainActivity(), dataSet, itemLayoutRes, - usePalette, getLibraryFragment()); - } - - @Override - public void onMediaStoreChanged() { - presenter.loadSongs(); - } - - @Override - protected int loadGridSize() { - return PreferenceUtil.getInstance().getSongGridSize(getActivity()); - } - - @Override - protected void saveGridSize(int gridSize) { - PreferenceUtil.getInstance().setSongGridSize(gridSize); - } - - @Override - protected int loadGridSizeLand() { - return PreferenceUtil.getInstance().getSongGridSizeLand(getActivity()); - } - - @Override - protected void saveGridSizeLand(int gridSize) { - PreferenceUtil.getInstance().setSongGridSizeLand(gridSize); - } - - @Override - public void saveUsePalette(boolean usePalette) { - PreferenceUtil.getInstance().setSongColoredFooters(usePalette); - } - - @Override - public boolean loadUsePalette() { - return PreferenceUtil.getInstance().songColoredFooters(); - } - - @Override - public void setUsePalette(boolean usePalette) { - getAdapter().usePalette(usePalette); - } - - @Override - protected void setGridSize(int gridSize) { - getLayoutManager().setSpanCount(gridSize); - getAdapter().notifyDataSetChanged(); - } - - @Override - public void onResume() { - super.onResume(); - getLibraryFragment().setTitle( - PreferenceUtil.getInstance().tabTitles() ? R.string.library : R.string.songs); - if (getAdapter().getDataSet().isEmpty()) { - presenter.subscribe(); - } - } - - @Override - public void setMenuVisibility(boolean menuVisible) { - super.setMenuVisibility(menuVisible); - if (menuVisible) { - getLibraryFragment().setTitle( - PreferenceUtil.getInstance().tabTitles() ? R.string.library - : R.string.songs); - } - } - - @Override - public void onDestroy() { - presenter.unsubscribe(); - super.onDestroy(); - } - - @Override - public void loading() { - - } - - @Override - public void showData(ArrayList songs) { - getAdapter().swapDataSet(songs); - } - - @Override - public void showEmptyView() { - getAdapter().swapDataSet(new ArrayList()); - } - - @Override - public void completed() { - - } - - @Override - protected String loadSortOrder() { - return PreferenceUtil.getInstance().getSongSortOrder(); - } - - @Override - protected void saveSortOrder(String sortOrder) { - PreferenceUtil.getInstance().setSongSortOrder(sortOrder); - } - - @Override - protected void setSortOrder(String sortOrder) { - presenter.loadSongs(); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/SongsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/SongsFragment.kt new file mode 100644 index 00000000..63bfc9d6 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/SongsFragment.kt @@ -0,0 +1,141 @@ +package code.name.monkey.retromusic.ui.fragments.mainactivity + +import android.os.Bundle +import androidx.recyclerview.widget.GridLayoutManager +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.mvp.contract.SongContract +import code.name.monkey.retromusic.mvp.presenter.SongPresenter +import code.name.monkey.retromusic.ui.adapter.song.ShuffleButtonSongAdapter +import code.name.monkey.retromusic.ui.adapter.song.SongAdapter +import code.name.monkey.retromusic.ui.fragments.base.AbsLibraryPagerRecyclerViewCustomGridSizeFragment +import code.name.monkey.retromusic.util.PreferenceUtil +import java.util.* + +class SongsFragment : AbsLibraryPagerRecyclerViewCustomGridSizeFragment(), SongContract.SongView { + + private var presenter: SongPresenter? = null + + override val emptyMessage: Int + get() = R.string.no_songs + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + presenter = SongPresenter(this) + } + + override fun createLayoutManager(): GridLayoutManager { + return GridLayoutManager(activity, getGridSize()) + } + + override fun createAdapter(): SongAdapter { + val itemLayoutRes = itemLayoutRes + notifyLayoutResChanged(itemLayoutRes) + val usePalette = loadUsePalette() + + val dataSet = if (adapter == null) ArrayList() else adapter!!.dataSet + + return if (getGridSize() <= maxGridSizeForList) { + ShuffleButtonSongAdapter(libraryFragment.mainActivity, dataSet, itemLayoutRes, usePalette, libraryFragment) + } else SongAdapter(libraryFragment.mainActivity, dataSet, itemLayoutRes, usePalette, libraryFragment) + } + + override fun onMediaStoreChanged() { + presenter!!.loadSongs() + } + + override fun loadGridSize(): Int { + return PreferenceUtil.getInstance().getSongGridSize(activity!!) + } + + override fun saveGridSize(gridColumns: Int) { + PreferenceUtil.getInstance().setSongGridSize(gridColumns) + } + + override fun loadGridSizeLand(): Int { + return PreferenceUtil.getInstance().getSongGridSizeLand(activity!!) + } + + override fun saveGridSizeLand(gridColumns: Int) { + PreferenceUtil.getInstance().setSongGridSizeLand(gridColumns) + } + + public override fun saveUsePalette(usePalette: Boolean) { + PreferenceUtil.getInstance().setSongColoredFooters(usePalette) + } + + public override fun loadUsePalette(): Boolean { + return PreferenceUtil.getInstance().songColoredFooters() + } + + public override fun setUsePalette(usePalette: Boolean) { + adapter!!.usePalette(usePalette) + } + + override fun setGridSize(gridSize: Int) { + layoutManager!!.spanCount = gridSize + adapter!!.notifyDataSetChanged() + } + + override fun onResume() { + super.onResume() + libraryFragment.setTitle(if (PreferenceUtil.getInstance().tabTitles()) R.string.library else R.string.songs) + if (adapter!!.dataSet.isEmpty()) { + presenter!!.subscribe() + } + } + + override fun setMenuVisibility(menuVisible: Boolean) { + super.setMenuVisibility(menuVisible) + if (menuVisible) { + libraryFragment.setTitle( + if (PreferenceUtil.getInstance().tabTitles()) + R.string.library + else + R.string.songs) + } + } + + override fun onDestroy() { + presenter!!.unsubscribe() + super.onDestroy() + } + + override fun loading() { + + } + + override fun showData(songs: ArrayList) { + adapter!!.swapDataSet(songs) + } + + override fun showEmptyView() { + adapter!!.swapDataSet(ArrayList()) + } + + override fun completed() { + + } + + override fun loadSortOrder(): String { + return PreferenceUtil.getInstance().songSortOrder + } + + override fun saveSortOrder(sortOrder: String) { + PreferenceUtil.getInstance().songSortOrder = sortOrder + } + + override fun setSortOrder(sortOrder: String) { + presenter!!.loadSongs() + } + + companion object { + + fun newInstance(): SongsFragment { + val args = Bundle() + val fragment = SongsFragment() + fragment.arguments = args + return fragment + } + } +}// Required empty public constructor diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/folders/FoldersFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/folders/FoldersFragment.java index a9e57f44..1fd8b121 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/folders/FoldersFragment.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/folders/FoldersFragment.java @@ -82,7 +82,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements private static final String PATH = "path"; private static final String CRUMBS = "crumbs"; - private static final int LOADER_ID = LoaderIds.FOLDERS_FRAGMENT; + private static final int LOADER_ID = LoaderIds.Companion.getFOLDERS_FRAGMENT(); @BindView(R.id.coordinator_layout) View coordinatorLayout; @@ -370,13 +370,13 @@ public class FoldersFragment extends AbsMainActivityFragment implements File file1 = (File) extra; int startIndex = -1; for (int i = 0; i < songs.size(); i++) { - if (file1.getPath().equals(songs.get(i).data)) { // path is already canonical here + if (file1.getPath().equals(songs.get(i).getData())) { // path is already canonical here startIndex = i; break; } } if (startIndex > -1) { - MusicPlayerRemote.openQueue(songs, startIndex, true); + MusicPlayerRemote.INSTANCE.openQueue(songs, startIndex, true); } else { final File finalFile = file1; Snackbar.make(coordinatorLayout, Html.fromHtml( @@ -397,7 +397,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements public void onMultipleItemAction(MenuItem item, ArrayList files) { final int itemId = item.getItemId(); new ListSongsAsyncTask(getActivity(), null, - (songs, extra) -> SongsMenuHelper.handleMenuClick(getActivity(), songs, itemId)) + (songs, extra) -> SongsMenuHelper.INSTANCE.handleMenuClick(getActivity(), songs, itemId)) .execute(new ListSongsAsyncTask.LoadingInfo(files, AUDIO_FILE_FILTER, getFileComparator())); } @@ -425,7 +425,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements case R.id.action_delete_from_device: new ListSongsAsyncTask(getActivity(), null, (songs, extra) -> { if (!songs.isEmpty()) { - SongsMenuHelper.handleMenuClick(getActivity(), songs, itemId); + SongsMenuHelper.INSTANCE.handleMenuClick(getActivity(), songs, itemId); } }).execute(new ListSongsAsyncTask.LoadingInfo(toList(file), AUDIO_FILE_FILTER, getFileComparator())); @@ -458,7 +458,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements case R.id.action_details: case R.id.action_set_as_ringtone: case R.id.action_delete_from_device: - new ListSongsAsyncTask(getActivity(), null, (songs, extra) -> SongMenuHelper.handleMenuClick(getActivity(), songs.get(0), itemId)).execute(new ListSongsAsyncTask.LoadingInfo(toList(file), AUDIO_FILE_FILTER, getFileComparator())); + new ListSongsAsyncTask(getActivity(), null, (songs, extra) -> SongMenuHelper.INSTANCE.handleMenuClick(getActivity(), songs.get(0), itemId)).execute(new ListSongsAsyncTask.LoadingInfo(toList(file), AUDIO_FILE_FILTER, getFileComparator())); return true; case R.id.action_scan: new ListPathsAsyncTask(getActivity(), this::scanPaths).execute(new ListPathsAsyncTask.LoadingInfo(file, AUDIO_FILE_FILTER)); diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/home/BannerHomeFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/home/BannerHomeFragment.java deleted file mode 100644 index 5bb83674..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/home/BannerHomeFragment.java +++ /dev/null @@ -1,361 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.mainactivity.home; - -import android.app.Activity; -import android.graphics.Bitmap; -import android.os.Bundle; -import android.util.DisplayMetrics; -import android.view.Display; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.LinearLayout; - -import com.bumptech.glide.Glide; -import com.bumptech.glide.load.engine.DiskCacheStrategy; - -import java.io.File; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Random; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.content.ContextCompat; -import androidx.recyclerview.widget.GridLayoutManager; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import butterknife.Unbinder; -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.interfaces.MainActivityFragmentCallbacks; -import code.name.monkey.retromusic.loaders.SongLoader; -import code.name.monkey.retromusic.model.Album; -import code.name.monkey.retromusic.model.Artist; -import code.name.monkey.retromusic.model.Genre; -import code.name.monkey.retromusic.model.Playlist; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.model.smartplaylist.HistoryPlaylist; -import code.name.monkey.retromusic.model.smartplaylist.LastAddedPlaylist; -import code.name.monkey.retromusic.model.smartplaylist.MyTopTracksPlaylist; -import code.name.monkey.retromusic.mvp.contract.HomeContract; -import code.name.monkey.retromusic.mvp.presenter.HomePresenter; -import code.name.monkey.retromusic.ui.adapter.CollageSongAdapter; -import code.name.monkey.retromusic.ui.adapter.GenreAdapter; -import code.name.monkey.retromusic.ui.adapter.album.AlbumFullWithAdapter; -import code.name.monkey.retromusic.ui.adapter.artist.ArtistAdapter; -import code.name.monkey.retromusic.ui.fragments.base.AbsMainActivityFragment; -import code.name.monkey.retromusic.util.Compressor; -import code.name.monkey.retromusic.util.NavigationUtil; -import code.name.monkey.retromusic.util.PreferenceUtil; -import code.name.monkey.retromusic.util.RetroUtil; -import code.name.monkey.retromusic.views.CircularImageView; -import code.name.monkey.retromusic.views.MetalRecyclerViewPager; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.CompositeDisposable; -import io.reactivex.schedulers.Schedulers; - -import static code.name.monkey.retromusic.Constants.USER_BANNER; -import static code.name.monkey.retromusic.Constants.USER_PROFILE; - -public class BannerHomeFragment extends AbsMainActivityFragment implements MainActivityFragmentCallbacks, HomeContract.HomeView { - - public static final String TAG = "BannerHomeFragment"; - - @BindView(R.id.image) - @Nullable - ImageView imageView; - - @BindView(R.id.user_image) - CircularImageView userImage; - - @BindView(R.id.recycler_view) - RecyclerView recentArtistRV; - - @BindView(R.id.recent_album) - RecyclerView recentAlbumRV; - - @BindView(R.id.top_artist) - RecyclerView topArtistRV; - - @BindView(R.id.top_album) - MetalRecyclerViewPager topAlbumRV; - - @BindView(R.id.recent_artist_container) - View recentArtistContainer; - - @BindView(R.id.recent_albums_container) - View recentAlbumsContainer; - - @BindView(R.id.top_artist_container) - View topArtistContainer; - - @BindView(R.id.top_albums_container) - View topAlbumContainer; - - @BindView(R.id.genres) - RecyclerView genresRecyclerView; - - @BindView(R.id.genre_container) - LinearLayout genreContainer; - - @BindView(R.id.container) - View container; - - @BindView(R.id.content_container) - View contentContainer; - - @BindView(R.id.suggestion_songs) - RecyclerView suggestionsSongs; - - @BindView(R.id.suggestion_container) - LinearLayout suggestionsContainer; - - private Unbinder unbinder; - private HomePresenter homePresenter; - private CompositeDisposable disposable; - - public static BannerHomeFragment newInstance() { - Bundle args = new Bundle(); - BannerHomeFragment fragment = new BannerHomeFragment(); - fragment.setArguments(args); - return fragment; - } - - private void getTimeOfTheDay(boolean b) { - Calendar c = Calendar.getInstance(); - int timeOfDay = c.get(Calendar.HOUR_OF_DAY); - - String[] images = new String[]{}; - if (timeOfDay >= 0 && timeOfDay < 6) { - images = getResources().getStringArray(R.array.night); - } else if (timeOfDay >= 6 && timeOfDay < 12) { - images = getResources().getStringArray(R.array.morning); - } else if (timeOfDay >= 12 && timeOfDay < 16) { - images = getResources().getStringArray(R.array.after_noon); - } else if (timeOfDay >= 16 && timeOfDay < 20) { - images = getResources().getStringArray(R.array.evening); - } else if (timeOfDay >= 20 && timeOfDay < 24) { - images = getResources().getStringArray(R.array.night); - } - - String day = images[new Random().nextInt(images.length)]; - loadTimeImage(day); - } - - - private void loadTimeImage(String day) { - //noinspection ConstantConditions - if (imageView != null) { - if (PreferenceUtil.getInstance().getBannerImage().isEmpty()) { - Glide.with(getActivity()).load(day) - .asBitmap() - .placeholder(R.drawable.material_design_default) - .diskCacheStrategy(DiskCacheStrategy.SOURCE) - .into(imageView); - } else { - loadBannerFromStorage(); - } - } - } - - private void loadBannerFromStorage() { - //noinspection ConstantConditions - disposable.add(new Compressor(getContext()) - .setQuality(100) - .setCompressFormat(Bitmap.CompressFormat.WEBP) - .compressToBitmapAsFlowable( - new File(PreferenceUtil.getInstance().getBannerImage(), USER_BANNER)) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(imageView::setImageBitmap)); - } - - private void loadImageFromStorage(ImageView imageView) { - //noinspection ConstantConditions - disposable.add(new Compressor(getContext()) - .setMaxHeight(300) - .setMaxWidth(300) - .setQuality(75) - .setCompressFormat(Bitmap.CompressFormat.WEBP) - .compressToBitmapAsFlowable( - new File(PreferenceUtil.getInstance().getProfileImage(), USER_PROFILE)) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(imageView::setImageBitmap, - throwable -> imageView.setImageDrawable(ContextCompat - .getDrawable(getContext(), R.drawable.ic_person_flat)))); - } - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - disposable = new CompositeDisposable(); - //noinspection ConstantConditions - homePresenter = new HomePresenter(this); - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(PreferenceUtil.getInstance().isHomeBanner() ? R.layout.fragment_banner_home : R.layout.fragment_home, - container, false); - unbinder = ButterKnife.bind(this, view); - if (!PreferenceUtil.getInstance().isHomeBanner()) - setStatusbarColorAuto(view); - return view; - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - setupToolbar(); - loadImageFromStorage(userImage); - homePresenter.subscribe(); - getTimeOfTheDay(PreferenceUtil.getInstance().isHomeBanner()); - } - - @SuppressWarnings("ConstantConditions") - private void setupToolbar() { - userImage.setOnClickListener(v -> showMainMenu()); - contentContainer.setBackgroundColor(ThemeStore.primaryColor(getMainActivity())); - } - - @OnClick(R.id.searchIcon) - void search() { - NavigationUtil.goToSearch(getMainActivity()); - } - - @Override - public boolean handleBackPress() { - return false; - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - unbinder.unbind(); - disposable.clear(); - homePresenter.unsubscribe(); - } - - @Override - public void loading() { - - } - - @Override - public void showEmptyView() { - - } - - @Override - public void completed() { - - } - - @Override - public void showData(ArrayList homes) { - //homeAdapter.swapDataSet(homes); - } - - @Override - public void recentArtist(ArrayList artists) { - recentArtistContainer.setVisibility(View.VISIBLE); - recentArtistRV.setLayoutManager(new GridLayoutManager(getMainActivity(), - 1, GridLayoutManager.HORIZONTAL, false)); - ArtistAdapter artistAdapter = new ArtistAdapter(getMainActivity(), artists, - PreferenceUtil.getInstance().getHomeGridStyle(getContext()), false, null); - recentArtistRV.setAdapter(artistAdapter); - } - - @Override - public void recentAlbum(ArrayList albums) { - recentAlbumsContainer.setVisibility(View.VISIBLE); - AlbumFullWithAdapter artistAdapter = new AlbumFullWithAdapter(getMainActivity(), - getDisplayMetrics()); - artistAdapter.swapData(albums); - recentAlbumRV.setAdapter(artistAdapter); - } - - @Override - public void topArtists(ArrayList artists) { - topArtistContainer.setVisibility(View.VISIBLE); - topArtistRV.setLayoutManager(new GridLayoutManager(getMainActivity(), - 1, GridLayoutManager.HORIZONTAL, false)); - ArtistAdapter artistAdapter = new ArtistAdapter(getMainActivity(), artists, - PreferenceUtil.getInstance().getHomeGridStyle(getContext()), false, null); - topArtistRV.setAdapter(artistAdapter); - - } - - @Override - public void topAlbums(ArrayList albums) { - topAlbumContainer.setVisibility(View.VISIBLE); - AlbumFullWithAdapter artistAdapter = new AlbumFullWithAdapter(getMainActivity(), - getDisplayMetrics()); - artistAdapter.swapData(albums); - topAlbumRV.setAdapter(artistAdapter); - } - - @Override - public void suggestions(ArrayList songs) { - if (!songs.isEmpty()) { - suggestionsContainer.setVisibility(View.VISIBLE); - CollageSongAdapter artistAdapter = new CollageSongAdapter(getMainActivity(), songs); - suggestionsSongs.setLayoutManager(RetroUtil.isTablet() ? new GridLayoutManager(getMainActivity(), 2) : new LinearLayoutManager(getMainActivity())); - suggestionsSongs.setAdapter(artistAdapter); - } - } - - @Override - public void playlists(ArrayList playlists) { - - } - - private DisplayMetrics getDisplayMetrics() { - Display display = getMainActivity().getWindowManager().getDefaultDisplay(); - DisplayMetrics metrics = new DisplayMetrics(); - display.getMetrics(metrics); - return metrics; - } - - @Override - public void geners(ArrayList genres) { - genreContainer.setVisibility(View.VISIBLE); - genresRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); - //noinspection ConstantConditions - GenreAdapter genreAdapter = new GenreAdapter(getActivity(), genres, R.layout.item_list); - genresRecyclerView.setAdapter(genreAdapter); - } - - @OnClick({R.id.last_added, R.id.top_played, R.id.action_shuffle, R.id.history, - R.id.user_image}) - void startUserInfo(View view) { - Activity activity = getActivity(); - if (activity != null) { - switch (view.getId()) { - case R.id.action_shuffle: - MusicPlayerRemote.openAndShuffleQueue(SongLoader.Companion.getAllSongs(activity).blockingFirst(), true); - break; - case R.id.last_added: - NavigationUtil.goToPlaylistNew(activity, new LastAddedPlaylist(activity)); - break; - case R.id.top_played: - NavigationUtil.goToPlaylistNew(activity, new MyTopTracksPlaylist(activity)); - break; - case R.id.history: - NavigationUtil.goToPlaylistNew(activity, new HistoryPlaylist(activity)); - break; - case R.id.user_image: - NavigationUtil.goToUserInfo(getActivity()); - break; - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/home/BannerHomeFragment.kt b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/home/BannerHomeFragment.kt new file mode 100644 index 00000000..4b56b5b4 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/home/BannerHomeFragment.kt @@ -0,0 +1,302 @@ +package code.name.monkey.retromusic.ui.fragments.mainactivity.home + +import android.graphics.Bitmap +import android.os.Bundle +import android.util.DisplayMetrics +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import androidx.core.content.ContextCompat +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import butterknife.OnClick +import code.name.monkey.appthemehelper.ThemeStore +import code.name.monkey.retromusic.Constants.USER_BANNER +import code.name.monkey.retromusic.Constants.USER_PROFILE +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.helper.MusicPlayerRemote +import code.name.monkey.retromusic.interfaces.MainActivityFragmentCallbacks +import code.name.monkey.retromusic.loaders.SongLoader +import code.name.monkey.retromusic.model.* +import code.name.monkey.retromusic.model.smartplaylist.HistoryPlaylist +import code.name.monkey.retromusic.model.smartplaylist.LastAddedPlaylist +import code.name.monkey.retromusic.model.smartplaylist.MyTopTracksPlaylist +import code.name.monkey.retromusic.mvp.contract.HomeContract +import code.name.monkey.retromusic.mvp.presenter.HomePresenter +import code.name.monkey.retromusic.ui.adapter.CollageSongAdapter +import code.name.monkey.retromusic.ui.adapter.GenreAdapter +import code.name.monkey.retromusic.ui.adapter.album.AlbumFullWithAdapter +import code.name.monkey.retromusic.ui.adapter.artist.ArtistAdapter +import code.name.monkey.retromusic.ui.fragments.base.AbsMainActivityFragment +import code.name.monkey.retromusic.util.Compressor +import code.name.monkey.retromusic.util.NavigationUtil +import code.name.monkey.retromusic.util.PreferenceUtil +import code.name.monkey.retromusic.util.RetroUtil +import code.name.monkey.retromusic.views.CircularImageView +import code.name.monkey.retromusic.views.MetalRecyclerViewPager +import com.bumptech.glide.Glide +import com.bumptech.glide.load.engine.DiskCacheStrategy +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.schedulers.Schedulers +import kotlinx.android.synthetic.main.abs_playlists.* +import java.io.File +import java.util.* + +class BannerHomeFragment : AbsMainActivityFragment(), MainActivityFragmentCallbacks, HomeContract.HomeView { + + + override fun onCreateView(inflater: LayoutInflater, viewGroup: ViewGroup?, + savedInstanceState: Bundle?): View? { + val view = inflater.inflate(if (PreferenceUtil.getInstance().isHomeBanner) R.layout.fragment_banner_home else R.layout.fragment_home, viewGroup, false) + + if (!PreferenceUtil.getInstance().isHomeBanner) + setStatusbarColorAuto(view) + + imageView = view.findViewById(R.id.image) + userImage = view.findViewById(R.id.user_image) + recentArtistRV = view.findViewById(R.id.recycler_view) + recentAlbumRV = view.findViewById(R.id.recent_album) + topArtistRV = view.findViewById(R.id.top_artist) + topAlbumRV = view.findViewById(R.id.top_album) + recentArtistContainer = view.findViewById(R.id.recent_artist_container) + recentAlbumsContainer = view.findViewById(R.id.recent_albums_container) + topArtistContainer = view.findViewById(R.id.top_artist_container) + topAlbumContainer = view.findViewById(R.id.top_albums_container) + genresRecyclerView = view.findViewById(R.id.genres) + genreContainer = view.findViewById(R.id.genre_container) + contentContainer = view.findViewById(R.id.content_container) + container = view.findViewById(R.id.container) + suggestionsSongs = view.findViewById(R.id.suggestion_songs) + suggestionsContainer = view.findViewById(R.id.suggestion_container) + + /* lastAdded.setOnClickListener { + NavigationUtil.goToPlaylistNew(activity!!, LastAddedPlaylist(activity!!)) + } + topPlayed.setOnClickListener { + NavigationUtil.goToPlaylistNew(activity!!, MyTopTracksPlaylist(activity!!)) + } + actionShuffle.setOnClickListener { + MusicPlayerRemote.openAndShuffleQueue(SongLoader.getAllSongs(activity!!).blockingFirst(), true) + } + history.setOnClickListener { + NavigationUtil.goToPlaylistNew(activity!!, HistoryPlaylist(activity!!)) + }*/ + userImage.setOnClickListener { + NavigationUtil.goToUserInfo(activity!!) + } + return view + } + + private var imageView: ImageView? = null + private lateinit var userImage: CircularImageView + private lateinit var recentAlbumRV: MetalRecyclerViewPager + private lateinit var topAlbumRV: MetalRecyclerViewPager + private lateinit var topArtistRV: RecyclerView + private lateinit var genresRecyclerView: RecyclerView + private lateinit var suggestionsSongs: RecyclerView + private lateinit var recentArtistRV: RecyclerView + private lateinit var recentArtistContainer: View + private lateinit var recentAlbumsContainer: View + private lateinit var topArtistContainer: View + private lateinit var topAlbumContainer: View + private lateinit var genreContainer: View + private lateinit var container: View + private lateinit var contentContainer: View + private lateinit var suggestionsContainer: View + private lateinit var homePresenter: HomePresenter + val disposable: CompositeDisposable = CompositeDisposable() + + private val displayMetrics: DisplayMetrics + get() { + val display = mainActivity.windowManager.defaultDisplay + val metrics = DisplayMetrics() + display.getMetrics(metrics) + return metrics + } + + private fun getTimeOfTheDay() { + val c = Calendar.getInstance() + val timeOfDay = c.get(Calendar.HOUR_OF_DAY) + + var images = arrayOf() + if (timeOfDay in 0..5) { + images = resources.getStringArray(R.array.night) + } else if (timeOfDay in 6..11) { + images = resources.getStringArray(R.array.morning) + } else if (timeOfDay in 12..15) { + images = resources.getStringArray(R.array.after_noon) + } else if (timeOfDay in 16..19) { + images = resources.getStringArray(R.array.evening) + } else if (timeOfDay in 20..23) { + images = resources.getStringArray(R.array.night) + } + + val day = images[Random().nextInt(images.size)] + loadTimeImage(day) + } + + + private fun loadTimeImage(day: String) { + + if (imageView != null) { + if (PreferenceUtil.getInstance().bannerImage.isEmpty()) { + Glide.with(activity).load(day) + .asBitmap() + .placeholder(R.drawable.material_design_default) + .diskCacheStrategy(DiskCacheStrategy.SOURCE) + .into(imageView!!) + } else { + loadBannerFromStorage() + } + } + } + + private fun loadBannerFromStorage() { + + disposable.add(Compressor(context!!) + .setQuality(100) + .setCompressFormat(Bitmap.CompressFormat.WEBP) + .compressToBitmapAsFlowable( + File(PreferenceUtil.getInstance().bannerImage, USER_BANNER)) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { imageView!!.setImageBitmap(it) }) + } + + private fun loadImageFromStorage(imageView: ImageView) { + + disposable.add(Compressor(context!!) + .setMaxHeight(300) + .setMaxWidth(300) + .setQuality(75) + .setCompressFormat(Bitmap.CompressFormat.WEBP) + .compressToBitmapAsFlowable( + File(PreferenceUtil.getInstance().profileImage, USER_PROFILE)) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ + imageView.setImageBitmap(it) + }) { + imageView.setImageDrawable(ContextCompat + .getDrawable(context!!, R.drawable.ic_person_flat)) + }) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + homePresenter = HomePresenter(this) + } + + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setupToolbar() + loadImageFromStorage(userImage) + homePresenter.subscribe() + getTimeOfTheDay() + } + + private fun setupToolbar() { + userImage.setOnClickListener { showMainMenu() } + contentContainer.setBackgroundColor(ThemeStore.primaryColor(mainActivity)) + } + + @OnClick(R.id.searchIcon) + internal fun search() { + NavigationUtil.goToSearch(mainActivity) + } + + override fun handleBackPress(): Boolean { + return false + } + + override fun onDestroyView() { + super.onDestroyView() + disposable.clear() + homePresenter.unsubscribe() + } + + override fun loading() { + + } + + override fun showEmptyView() { + + } + + override fun completed() { + + } + + override fun showData(homes: ArrayList) { + //homeAdapter.swapDataSet(homes); + } + + override fun recentArtist(artists: ArrayList) { + recentArtistContainer.visibility = View.VISIBLE + recentArtistRV.layoutManager = GridLayoutManager(mainActivity, 1, GridLayoutManager.HORIZONTAL, false) + val artistAdapter = ArtistAdapter(mainActivity, artists, PreferenceUtil.getInstance().getHomeGridStyle(context!!), false, null) + recentArtistRV.adapter = artistAdapter + } + + override fun recentAlbum(albums: ArrayList) { + recentAlbumsContainer.visibility = View.VISIBLE + val artistAdapter = AlbumFullWithAdapter(mainActivity, + displayMetrics) + artistAdapter.swapData(albums) + recentAlbumRV.adapter = artistAdapter + } + + override fun topArtists(artists: ArrayList) { + topArtistContainer.visibility = View.VISIBLE + topArtistRV.layoutManager = GridLayoutManager(mainActivity, 1, GridLayoutManager.HORIZONTAL, false) + val artistAdapter = ArtistAdapter(mainActivity, artists, PreferenceUtil.getInstance().getHomeGridStyle(context!!), false, null) + topArtistRV.adapter = artistAdapter + + } + + override fun topAlbums(albums: ArrayList) { + topAlbumContainer.visibility = View.VISIBLE + val artistAdapter = AlbumFullWithAdapter(mainActivity, + displayMetrics) + artistAdapter.swapData(albums) + topAlbumRV.adapter = artistAdapter + } + + override fun suggestions(songs: ArrayList) { + if (!songs.isEmpty()) { + suggestionsContainer.visibility = View.VISIBLE + val artistAdapter = CollageSongAdapter(mainActivity, songs) + suggestionsSongs.layoutManager = if (RetroUtil.isTablet()) GridLayoutManager(mainActivity, 2) else LinearLayoutManager(mainActivity) + suggestionsSongs.adapter = artistAdapter + } + } + + override fun playlists(playlists: ArrayList) { + + } + + override fun geners(genres: ArrayList) { + genreContainer.visibility = View.VISIBLE + genresRecyclerView.layoutManager = LinearLayoutManager(context) + + val genreAdapter = GenreAdapter(activity!!, genres, R.layout.item_list) + genresRecyclerView.adapter = genreAdapter + } + + + companion object { + + const val TAG: String = "BannerHomeFragment" + + fun newInstance(): BannerHomeFragment { + val args = Bundle() + val fragment = BannerHomeFragment() + fragment.arguments = args + return fragment + } + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/PlayerAlbumCoverFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/PlayerAlbumCoverFragment.java deleted file mode 100644 index 280c3e60..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/PlayerAlbumCoverFragment.java +++ /dev/null @@ -1,151 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.player; - -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; -import androidx.viewpager.widget.ViewPager; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.Unbinder; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.transform.CarousalPagerTransformer; -import code.name.monkey.retromusic.transform.ParallaxPagerTransformer; -import code.name.monkey.retromusic.ui.adapter.album.AlbumCoverPagerAdapter; -import code.name.monkey.retromusic.ui.fragments.NowPlayingScreen; -import code.name.monkey.retromusic.ui.fragments.base.AbsMusicServiceFragment; -import code.name.monkey.retromusic.util.PreferenceUtil; - - -public class PlayerAlbumCoverFragment extends AbsMusicServiceFragment implements - ViewPager.OnPageChangeListener { - - public static final String TAG = PlayerAlbumCoverFragment.class.getSimpleName(); - public static final long VISIBILITY_ANIM_DURATION = 300; - @BindView(R.id.player_album_cover_viewpager) - ViewPager viewPager; - private Unbinder unbinder; - private Callbacks callbacks; - private int currentPosition; - private AlbumCoverPagerAdapter.AlbumCoverFragment.ColorReceiver colorReceiver = - new AlbumCoverPagerAdapter.AlbumCoverFragment.ColorReceiver() { - @Override - public void onColorReady(int color, int requestCode) { - if (currentPosition == requestCode) { - notifyColorChange(color); - } - } - }; - - public void removeSlideEffect() { - ParallaxPagerTransformer transformer = new ParallaxPagerTransformer(R.id.player_image); - transformer.setSpeed(0.3f); - viewPager.setPageTransformer(true, transformer); - - } - - @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_player_album_cover, container, false); - unbinder = ButterKnife.bind(this, view); - return view; - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - viewPager.addOnPageChangeListener(this); - - //noinspection ConstantConditions - if (PreferenceUtil.getInstance().carouselEffect() && - !((PreferenceUtil.getInstance().getNowPlayingScreen() == NowPlayingScreen.FULL) || (PreferenceUtil.getInstance().getNowPlayingScreen() == NowPlayingScreen.FIT))) { - viewPager.setClipToPadding(false); - viewPager.setPadding(96, 0, 96, 0); - viewPager.setPageMargin(18); - viewPager.setPageTransformer(false, new CarousalPagerTransformer(getContext())); - } else { - viewPager.setPageTransformer(true, PreferenceUtil.getInstance().getAlbumCoverTransform(getContext())); - } - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - viewPager.removeOnPageChangeListener(this); - unbinder.unbind(); - } - - @Override - public void onServiceConnected() { - updatePlayingQueue(); - } - - @Override - public void onPlayingMetaChanged() { - viewPager.setCurrentItem(MusicPlayerRemote.getPosition()); - } - - @Override - public void onQueueChanged() { - updatePlayingQueue(); - } - - private void updatePlayingQueue() { - viewPager.setAdapter( - new AlbumCoverPagerAdapter(getFragmentManager(), MusicPlayerRemote.getPlayingQueue())); - //noinspection ConstantConditions - viewPager.getAdapter().notifyDataSetChanged(); - viewPager.setCurrentItem(MusicPlayerRemote.getPosition()); - onPageSelected(MusicPlayerRemote.getPosition()); - - } - - @Override - public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { - - } - - @Override - public void onPageSelected(int position) { - currentPosition = position; - if (viewPager.getAdapter() != null) { - ((AlbumCoverPagerAdapter) viewPager.getAdapter()).receiveColor(colorReceiver, position); - } - if (position != MusicPlayerRemote.getPosition()) { - MusicPlayerRemote.playSongAt(position); - } - } - - @Override - public void onPageScrollStateChanged(int state) { - - } - - - private void notifyColorChange(int color) { - if (callbacks != null) { - callbacks.onColorChanged(color); - } - } - - public void setCallbacks(Callbacks listener) { - callbacks = listener; - } - - public void removeEffect() { - viewPager.setPageTransformer(false, null); - } - - - public interface Callbacks { - - void onColorChanged(int color); - - void onFavoriteToggled(); - - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/PlayerAlbumCoverFragment.kt b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/PlayerAlbumCoverFragment.kt new file mode 100644 index 00000000..6739497f --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/PlayerAlbumCoverFragment.kt @@ -0,0 +1,119 @@ +package code.name.monkey.retromusic.ui.fragments.player + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.viewpager.widget.ViewPager +import butterknife.Unbinder +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.helper.MusicPlayerRemote +import code.name.monkey.retromusic.transform.ParallaxPagerTransformer +import code.name.monkey.retromusic.ui.adapter.album.AlbumCoverPagerAdapter +import code.name.monkey.retromusic.ui.fragments.base.AbsMusicServiceFragment +import code.name.monkey.retromusic.util.PreferenceUtil +import kotlinx.android.synthetic.main.fragment_player_album_cover.* + + +class PlayerAlbumCoverFragment : AbsMusicServiceFragment(), ViewPager.OnPageChangeListener { + private var callbacks: Callbacks? = null + private var currentPosition: Int = 0 + private val colorReceiver = object : AlbumCoverPagerAdapter.AlbumCoverFragment.ColorReceiver { + override fun onColorReady(color: Int, request: Int) { + if (currentPosition == request) { + notifyColorChange(color) + } + } + } + + fun removeSlideEffect() { + val transformer = ParallaxPagerTransformer(R.id.player_image) + transformer.setSpeed(0.3f) + viewPager.setPageTransformer(true, transformer) + + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.fragment_player_album_cover, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + viewPager.addOnPageChangeListener(this) + viewPager.setPageTransformer(true, PreferenceUtil.getInstance().getAlbumCoverTransform(context)) + } + + override fun onDestroyView() { + super.onDestroyView() + viewPager.removeOnPageChangeListener(this) + } + + override fun onServiceConnected() { + updatePlayingQueue() + } + + override fun onPlayingMetaChanged() { + viewPager.currentItem = MusicPlayerRemote.position + } + + override fun onQueueChanged() { + updatePlayingQueue() + } + + private fun updatePlayingQueue() { + viewPager.apply { + adapter = AlbumCoverPagerAdapter(fragmentManager!!, MusicPlayerRemote.playingQueue) + viewPager.adapter!!.notifyDataSetChanged() + viewPager.currentItem = MusicPlayerRemote.position + onPageSelected(MusicPlayerRemote.position) + } + } + + override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { + + } + + override fun onPageSelected(position: Int) { + currentPosition = position + if (viewPager.adapter != null) { + (viewPager.adapter as AlbumCoverPagerAdapter).receiveColor(colorReceiver, position) + } + if (position != MusicPlayerRemote.position) { + MusicPlayerRemote.playSongAt(position) + } + } + + override fun onPageScrollStateChanged(state: Int) { + + } + + + private fun notifyColorChange(color: Int) { + if (callbacks != null) { + callbacks!!.onColorChanged(color) + } + } + + fun setCallbacks(listener: Callbacks) { + callbacks = listener + } + + fun removeEffect() { + viewPager.setPageTransformer(false, null) + } + + + interface Callbacks { + + fun onColorChanged(color: Int) + + fun onFavoriteToggled() + + } + + companion object { + val TAG: String = PlayerAlbumCoverFragment::class.java.simpleName + const val VISIBILITY_ANIM_DURATION: Long = 300 + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/adaptive/AdaptiveFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/adaptive/AdaptiveFragment.java deleted file mode 100644 index 28c4cbda..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/adaptive/AdaptiveFragment.java +++ /dev/null @@ -1,160 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.player.adaptive; - -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.ColorInt; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.widget.Toolbar; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.Unbinder; -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.appthemehelper.util.ATHUtil; -import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerFragment; -import code.name.monkey.retromusic.ui.fragments.player.PlayerAlbumCoverFragment; -import code.name.monkey.retromusic.ui.fragments.player.PlayerAlbumCoverFragment.Callbacks; -import code.name.monkey.retromusic.ui.fragments.player.normal.PlayerFragment; - -public class AdaptiveFragment extends AbsPlayerFragment implements Callbacks { - - @BindView(R.id.player_toolbar) - Toolbar toolbar; - - private int lastColor; - private AdaptivePlaybackControlsFragment playbackControlsFragment; - private Unbinder unbinder; - - public static PlayerFragment newInstance() { - Bundle args = new Bundle(); - PlayerFragment fragment = new PlayerFragment(); - fragment.setArguments(args); - return fragment; - } - - @Override - @ColorInt - public int getPaletteColor() { - return lastColor; - } - - @Override - public void onShow() { - playbackControlsFragment.show(); - } - - @Override - public void onHide() { - playbackControlsFragment.hide(); - onBackPressed(); - } - - @Override - public boolean onBackPressed() { - return false; - } - - @Override - public Toolbar getToolbar() { - return toolbar; - } - - @Override - public int toolbarIconColor() { - return ATHUtil.resolveColor(getContext(), R.attr.iconColor); - } - - @Override - public void onColorChanged(int color) { - playbackControlsFragment.setDark(color); - lastColor = color; - getCallbacks().onPaletteColorChanged(); - - ToolbarContentTintHelper.colorizeToolbar(toolbar, - ATHUtil.resolveColor(getContext(), R.attr.iconColor), getActivity()); - - } - - @Override - protected void toggleFavorite(Song song) { - super.toggleFavorite(song); - if (song.id == MusicPlayerRemote.getCurrentSong().id) { - updateIsFavorite(); - } - } - - @Override - public void onFavoriteToggled() { - toggleFavorite(MusicPlayerRemote.getCurrentSong()); - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - unbinder.unbind(); - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_adaptive_player, container, false); - unbinder = ButterKnife.bind(this, view); - return view; - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - setUpSubFragments(); - setUpPlayerToolbar(); - } - - private void setUpSubFragments() { - playbackControlsFragment = - (AdaptivePlaybackControlsFragment) getChildFragmentManager() - .findFragmentById(R.id.playback_controls_fragment); - - PlayerAlbumCoverFragment playerAlbumCoverFragment = - (PlayerAlbumCoverFragment) getChildFragmentManager() - .findFragmentById(R.id.player_album_cover_fragment); - playerAlbumCoverFragment.setCallbacks(this); - playerAlbumCoverFragment.removeSlideEffect(); - } - - private void setUpPlayerToolbar() { - int primaryColor = ATHUtil.resolveColor(getContext(), R.attr.iconColor); - toolbar.inflateMenu(R.menu.menu_player); - toolbar.setNavigationOnClickListener(v -> getActivity().onBackPressed()); - toolbar.setOnMenuItemClickListener(this); - - ToolbarContentTintHelper.colorizeToolbar(toolbar, primaryColor, getActivity()); - toolbar.setTitleTextColor(primaryColor); - toolbar.setSubtitleTextColor(ThemeStore.textColorSecondary(getContext())); - } - - @Override - public void onServiceConnected() { - updateIsFavorite(); - updateSong(); - } - - private void updateSong() { - Song song = MusicPlayerRemote.getCurrentSong(); - toolbar.setTitle(song.title); - toolbar.setSubtitle(song.artistName); - } - - @Override - public void onPlayingMetaChanged() { - updateIsFavorite(); - updateSong(); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/adaptive/AdaptiveFragment.kt b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/adaptive/AdaptiveFragment.kt new file mode 100644 index 00000000..eaa45107 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/adaptive/AdaptiveFragment.kt @@ -0,0 +1,106 @@ +package code.name.monkey.retromusic.ui.fragments.player.adaptive + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import code.name.monkey.appthemehelper.ThemeStore +import code.name.monkey.appthemehelper.util.ATHUtil +import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.helper.MusicPlayerRemote +import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerFragment +import code.name.monkey.retromusic.ui.fragments.player.PlayerAlbumCoverFragment + +class AdaptiveFragment : AbsPlayerFragment(), PlayerAlbumCoverFragment.Callbacks { + private var lastColor: Int = 0 + lateinit var playbackControlsFragment: AdaptivePlaybackControlsFragment + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.fragment_adaptive_player, container, false); + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + toolbar = view.findViewById(R.id.toolbar) + setUpSubFragments() + setUpPlayerToolbar() + } + + private fun setUpSubFragments() { + playbackControlsFragment = childFragmentManager.findFragmentById(R.id.playback_controls_fragment) as AdaptivePlaybackControlsFragment + val playerAlbumCoverFragment = childFragmentManager.findFragmentById(R.id.player_album_cover_fragment) as PlayerAlbumCoverFragment + playerAlbumCoverFragment.setCallbacks(this) + playerAlbumCoverFragment.removeSlideEffect() + } + + private fun setUpPlayerToolbar() { + val primaryColor = ATHUtil.resolveColor(context, R.attr.iconColor) + /*toolbar!!.apply { + inflateMenu(R.menu.menu_player) + setNavigationOnClickListener { activity!!.onBackPressed() } + ToolbarContentTintHelper.colorizeToolbar(this, primaryColor, activity) + setTitleTextColor(primaryColor) + setSubtitleTextColor(ThemeStore.textColorSecondary(context!!)) + }.setOnMenuItemClickListener(this)*/ + } + + override fun onServiceConnected() { + super.onServiceConnected() + updateIsFavorite() + updateSong() + } + + override fun onPlayingMetaChanged() { + updateIsFavorite() + updateSong() + } + + private fun updateSong() { + val song = MusicPlayerRemote.currentSong + /*toolbar!!.apply { + title = song.title + subtitle = song.artistName + }*/ + } + + override fun toggleFavorite(song: Song) { + super.toggleFavorite(song) + if (song.id == MusicPlayerRemote.currentSong.id) { + updateIsFavorite() + } + } + + override fun onFavoriteToggled() { + toggleFavorite(MusicPlayerRemote.currentSong) + } + + override fun onColorChanged(color: Int) { + playbackControlsFragment.setDark(color) + lastColor = color + callbacks!!.onPaletteColorChanged() + //ToolbarContentTintHelper.colorizeToolbar(toolbar, ATHUtil.resolveColor(context, R.attr.iconColor), activity) + } + + override fun onShow() { + playbackControlsFragment.show() + } + + override fun onHide() { + playbackControlsFragment.hide() + onBackPressed() + } + + override fun onBackPressed(): Boolean { + return false + } + + override fun toolbarIconColor(): Int { + return ATHUtil.resolveColor(context, R.attr.iconColor) + } + + override val paletteColor: Int + get() = lastColor + +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/adaptive/AdaptivePlaybackControlsFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/adaptive/AdaptivePlaybackControlsFragment.java deleted file mode 100644 index e1be14a7..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/adaptive/AdaptivePlaybackControlsFragment.java +++ /dev/null @@ -1,285 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.player.adaptive; - -import android.animation.ObjectAnimator; -import android.graphics.PorterDuff; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.animation.LinearInterpolator; -import android.widget.ImageButton; -import android.widget.SeekBar; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.widget.AppCompatSeekBar; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import butterknife.Unbinder; -import code.name.monkey.appthemehelper.util.ATHUtil; -import code.name.monkey.appthemehelper.util.ColorUtil; -import code.name.monkey.appthemehelper.util.MaterialValueHelper; -import code.name.monkey.appthemehelper.util.TintHelper; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper; -import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler; -import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener; -import code.name.monkey.retromusic.service.MusicService; -import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerControlsFragment; -import code.name.monkey.retromusic.util.MusicUtil; -import code.name.monkey.retromusic.util.PreferenceUtil; -import code.name.monkey.retromusic.views.PlayPauseDrawable; - -public class AdaptivePlaybackControlsFragment extends AbsPlayerControlsFragment { - - @BindView(R.id.player_play_pause_button) - ImageButton playPauseFab; - - @BindView(R.id.player_prev_button) - ImageButton prevButton; - - @BindView(R.id.player_next_button) - ImageButton nextButton; - - @BindView(R.id.player_repeat_button) - ImageButton repeatButton; - - @BindView(R.id.player_shuffle_button) - ImageButton shuffleButton; - - @BindView(R.id.player_progress_slider) - AppCompatSeekBar progressSlider; - - @BindView(R.id.player_song_total_time) - TextView songTotalTime; - - @BindView(R.id.player_song_current_progress) - TextView songCurrentProgress; - - @BindView(R.id.volume_fragment_container) - View volumeContainer; - - - private Unbinder unbinder; - private PlayPauseDrawable playPauseDrawable; - private int lastPlaybackControlsColor; - private int lastDisabledPlaybackControlsColor; - private MusicProgressViewUpdateHelper progressViewUpdateHelper; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - progressViewUpdateHelper = new MusicProgressViewUpdateHelper(this); - } - - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View view = inflater - .inflate(R.layout.fragment_adaptive_player_playback_controls, container, false); - unbinder = ButterKnife.bind(this, view); - return view; - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - unbinder = ButterKnife.bind(this, view); - setUpMusicControllers(); - - hideVolumeIfAvailable(); - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - unbinder.unbind(); - } - - - @Override - public void onResume() { - super.onResume(); - progressViewUpdateHelper.start(); - } - - @Override - public void onPause() { - super.onPause(); - progressViewUpdateHelper.stop(); - } - - @Override - public void onServiceConnected() { - updatePlayPauseDrawableState(); - updateRepeatState(); - updateShuffleState(); - - } - - @Override - public void onPlayStateChanged() { - updatePlayPauseDrawableState(); - } - - @Override - public void onRepeatModeChanged() { - updateRepeatState(); - } - - @Override - public void onShuffleModeChanged() { - updateShuffleState(); - } - - @Override - public void setDark(int dark) { - - if (ColorUtil.isColorLight(ATHUtil.resolveColor(getContext(), android.R.attr.windowBackground))) { - lastPlaybackControlsColor = MaterialValueHelper.getSecondaryTextColor(getActivity(), true); - lastDisabledPlaybackControlsColor = MaterialValueHelper.getSecondaryDisabledTextColor(getActivity(), true); - } else { - lastPlaybackControlsColor = MaterialValueHelper.getPrimaryTextColor(getActivity(), false); - lastDisabledPlaybackControlsColor = MaterialValueHelper.getPrimaryDisabledTextColor(getActivity(), false); - } - - updateRepeatState(); - updateShuffleState(); - updatePrevNextColor(); - updatePlayPauseColor(); - - TintHelper.setTintAuto(playPauseFab, MaterialValueHelper.getPrimaryTextColor(getContext(), ColorUtil.isColorLight(dark)), false); - TintHelper.setTintAuto(playPauseFab, dark, true); - TintHelper.setTintAuto(progressSlider, dark, false); - } - - private void updatePlayPauseColor() { - //playPauseButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - } - - private void setUpPlayPauseFab() { - playPauseFab.setOnClickListener(new PlayPauseButtonOnClickHandler()); - } - - protected void updatePlayPauseDrawableState() { - if (MusicPlayerRemote.isPlaying()) { - playPauseFab.setImageResource(R.drawable.ic_pause_white_24dp); - } else { - playPauseFab.setImageResource(R.drawable.ic_play_arrow_white_24dp); - } - } - - - private void setUpMusicControllers() { - setUpPlayPauseFab(); - setUpPrevNext(); - setUpRepeatButton(); - setUpShuffleButton(); - setUpProgressSlider(); - } - - private void setUpPrevNext() { - updatePrevNextColor(); - nextButton.setOnClickListener(v -> MusicPlayerRemote.playNextSong()); - prevButton.setOnClickListener(v -> MusicPlayerRemote.back()); - } - - private void updatePrevNextColor() { - nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - prevButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - } - - private void setUpShuffleButton() { - shuffleButton.setOnClickListener(v -> MusicPlayerRemote.toggleShuffleMode()); - } - - @Override - protected void updateShuffleState() { - switch (MusicPlayerRemote.getShuffleMode()) { - case MusicService.SHUFFLE_MODE_SHUFFLE: - shuffleButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - default: - shuffleButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - } - } - - private void setUpRepeatButton() { - repeatButton.setOnClickListener(v -> MusicPlayerRemote.cycleRepeatMode()); - } - - @Override - protected void updateRepeatState() { - switch (MusicPlayerRemote.getRepeatMode()) { - case MusicService.REPEAT_MODE_NONE: - repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp); - repeatButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - case MusicService.REPEAT_MODE_ALL: - repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp); - repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - case MusicService.REPEAT_MODE_THIS: - repeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp); - repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - } - } - - - @Override - protected void show() { - //Ignore - } - - @Override - protected void hide() { - //Ignore - } - - @Override - protected void setUpProgressSlider() { - progressSlider.setOnSeekBarChangeListener(new SimpleOnSeekbarChangeListener() { - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - if (fromUser) { - MusicPlayerRemote.seekTo(progress); - onUpdateProgressViews(MusicPlayerRemote.getSongProgressMillis(), - MusicPlayerRemote.getSongDurationMillis()); - } - } - }); - } - - - @OnClick(R.id.player_play_pause_button) - void showAnimation() { - if (MusicPlayerRemote.isPlaying()) { - MusicPlayerRemote.pauseSong(); - } else { - MusicPlayerRemote.resumePlaying(); - } - showBouceAnimation(playPauseFab); - } - - @Override - public void onUpdateProgressViews(int progress, int total) { - progressSlider.setMax(total); - - ObjectAnimator animator = ObjectAnimator.ofInt(progressSlider, "progress", progress); - animator.setDuration(1500); - animator.setInterpolator(new LinearInterpolator()); - animator.start(); - - songTotalTime.setText(MusicUtil.getReadableDurationString(total)); - songCurrentProgress.setText(MusicUtil.getReadableDurationString(progress)); - } - - private void hideVolumeIfAvailable() { - volumeContainer.setVisibility(PreferenceUtil.getInstance().getVolumeToggle() ? View.VISIBLE : View.GONE); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/adaptive/AdaptivePlaybackControlsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/adaptive/AdaptivePlaybackControlsFragment.kt new file mode 100644 index 00000000..4ef44759 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/adaptive/AdaptivePlaybackControlsFragment.kt @@ -0,0 +1,209 @@ +package code.name.monkey.retromusic.ui.fragments.player.adaptive + +import android.animation.ObjectAnimator +import android.graphics.PorterDuff +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.animation.LinearInterpolator +import android.widget.SeekBar +import code.name.monkey.appthemehelper.util.ATHUtil +import code.name.monkey.appthemehelper.util.ColorUtil +import code.name.monkey.appthemehelper.util.MaterialValueHelper +import code.name.monkey.appthemehelper.util.TintHelper +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.helper.MusicPlayerRemote +import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper +import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler +import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener +import code.name.monkey.retromusic.service.MusicService +import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerControlsFragment +import code.name.monkey.retromusic.util.MusicUtil +import code.name.monkey.retromusic.util.PreferenceUtil +import code.name.monkey.retromusic.views.PlayPauseDrawable + +class AdaptivePlaybackControlsFragment : AbsPlayerControlsFragment() { + + private val playPauseDrawable: PlayPauseDrawable? = null + private var lastPlaybackControlsColor: Int = 0 + private var lastDisabledPlaybackControlsColor: Int = 0 + private var progressViewUpdateHelper: MusicProgressViewUpdateHelper? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + progressViewUpdateHelper = MusicProgressViewUpdateHelper(this) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.fragment_adaptive_player_playback_controls, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setUpMusicControllers() + hideVolumeIfAvailable() + + playPauseFab!!.setOnClickListener { + if (MusicPlayerRemote.isPlaying) { + MusicPlayerRemote.pauseSong() + } else { + MusicPlayerRemote.resumePlaying() + } + showBouceAnimation(playPauseFab!!) + } + } + + override fun onResume() { + super.onResume() + progressViewUpdateHelper!!.start() + } + + override fun onPause() { + super.onPause() + progressViewUpdateHelper!!.stop() + } + + override fun onServiceConnected() { + updatePlayPauseDrawableState() + updateRepeatState() + updateShuffleState() + } + + override fun onPlayStateChanged() { + updatePlayPauseDrawableState() + } + + override fun onRepeatModeChanged() { + updateRepeatState() + } + + override fun onShuffleModeChanged() { + updateShuffleState() + } + + override fun setDark(color: Int) { + if (ColorUtil.isColorLight(ATHUtil.resolveColor(context, android.R.attr.windowBackground))) { + lastPlaybackControlsColor = MaterialValueHelper.getSecondaryTextColor(activity, true) + lastDisabledPlaybackControlsColor = MaterialValueHelper.getSecondaryDisabledTextColor(activity, true) + } else { + lastPlaybackControlsColor = MaterialValueHelper.getPrimaryTextColor(activity, false) + lastDisabledPlaybackControlsColor = MaterialValueHelper.getPrimaryDisabledTextColor(activity, false) + } + + updateRepeatState() + updateShuffleState() + updatePrevNextColor() + updatePlayPauseColor() + + TintHelper.setTintAuto(playPauseFab!!, MaterialValueHelper.getPrimaryTextColor(context, ColorUtil.isColorLight(color)), false) + TintHelper.setTintAuto(playPauseFab!!, color, true) + TintHelper.setTintAuto(progressSlider!!, color, false) + } + + private fun updatePlayPauseColor() { + //playPauseButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); + } + + private fun setUpPlayPauseFab() { + playPauseFab!!.setOnClickListener(PlayPauseButtonOnClickHandler()) + } + + protected fun updatePlayPauseDrawableState() { + if (MusicPlayerRemote.isPlaying) { + playPauseFab!!.setImageResource(R.drawable.ic_pause_white_24dp) + } else { + playPauseFab!!.setImageResource(R.drawable.ic_play_arrow_white_24dp) + } + } + + + private fun setUpMusicControllers() { + setUpPlayPauseFab() + setUpPrevNext() + setUpRepeatButton() + setUpShuffleButton() + setUpProgressSlider() + } + + private fun setUpPrevNext() { + updatePrevNextColor() + nextButton!!.setOnClickListener { MusicPlayerRemote.playNextSong() } + prevButton!!.setOnClickListener { MusicPlayerRemote.back() } + } + + private fun updatePrevNextColor() { + nextButton!!.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + prevButton!!.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + } + + private fun setUpShuffleButton() { + shuffleButton!!.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() } + } + + override fun updateShuffleState() { + when (MusicPlayerRemote.shuffleMode) { + MusicService.SHUFFLE_MODE_SHUFFLE -> shuffleButton!!.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + else -> shuffleButton!!.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + } + } + + private fun setUpRepeatButton() { + repeatButton!!.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() } + } + + override fun updateRepeatState() { + when (MusicPlayerRemote.repeatMode) { + MusicService.REPEAT_MODE_NONE -> { + repeatButton!!.setImageResource(R.drawable.ic_repeat_white_24dp) + repeatButton!!.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + } + MusicService.REPEAT_MODE_ALL -> { + repeatButton!!.setImageResource(R.drawable.ic_repeat_white_24dp) + repeatButton!!.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + } + MusicService.REPEAT_MODE_THIS -> { + repeatButton!!.setImageResource(R.drawable.ic_repeat_one_white_24dp) + repeatButton!!.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + } + } + } + + + public override fun show() { + //Ignore + } + + public override fun hide() { + //Ignore + } + + override fun setUpProgressSlider() { + progressSlider!!.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() { + override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { + if (fromUser) { + MusicPlayerRemote.seekTo(progress) + onUpdateProgressViews(MusicPlayerRemote.songProgressMillis, + MusicPlayerRemote.songDurationMillis) + } + } + }) + } + + override fun onUpdateProgressViews(progress: Int, total: Int) { + progressSlider!!.max = total + + val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress) + animator.duration = 1500 + animator.interpolator = LinearInterpolator() + animator.start() + + songTotalTime!!.text = MusicUtil.getReadableDurationString(total.toLong()) + songCurrentProgress!!.text = MusicUtil.getReadableDurationString(progress.toLong()) + } + + private fun hideVolumeIfAvailable() { + volumeContainer!!.visibility = if (PreferenceUtil.getInstance().volumeToggle) View.VISIBLE else View.GONE + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/blur/BlurPlaybackControlsFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/blur/BlurPlaybackControlsFragment.java deleted file mode 100644 index eec9433c..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/blur/BlurPlaybackControlsFragment.java +++ /dev/null @@ -1,299 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.player.blur; - -import android.animation.ObjectAnimator; -import android.graphics.Color; -import android.graphics.PorterDuff; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.animation.DecelerateInterpolator; -import android.view.animation.LinearInterpolator; -import android.widget.ImageButton; -import android.widget.SeekBar; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.widget.AppCompatSeekBar; -import androidx.appcompat.widget.AppCompatTextView; -import androidx.core.content.ContextCompat; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.Unbinder; -import code.name.monkey.appthemehelper.util.TintHelper; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper; -import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler; -import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.service.MusicService; -import code.name.monkey.retromusic.ui.fragments.VolumeFragment; -import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerControlsFragment; -import code.name.monkey.retromusic.util.MusicUtil; -import code.name.monkey.retromusic.util.PreferenceUtil; - - -public class BlurPlaybackControlsFragment extends AbsPlayerControlsFragment { - - @BindView(R.id.player_play_pause_button) - ImageButton playPauseFab; - - @BindView(R.id.player_prev_button) - ImageButton prevButton; - - @BindView(R.id.player_next_button) - ImageButton nextButton; - - @BindView(R.id.player_repeat_button) - ImageButton repeatButton; - - @BindView(R.id.player_shuffle_button) - ImageButton shuffleButton; - - @BindView(R.id.player_progress_slider) - AppCompatSeekBar progressSlider; - - @BindView(R.id.player_song_total_time) - TextView songTotalTime; - - @BindView(R.id.player_song_current_progress) - TextView songCurrentProgress; - - @BindView(R.id.title) - AppCompatTextView songTitle; - - @BindView(R.id.text) - TextView text; - - @BindView(R.id.volume_fragment_container) - View mVolumeContainer; - - private Unbinder unbinder; - private int lastPlaybackControlsColor; - private int lastDisabledPlaybackControlsColor; - private MusicProgressViewUpdateHelper progressViewUpdateHelper; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - progressViewUpdateHelper = new MusicProgressViewUpdateHelper(this); - } - - @Override - public View onCreateView(@NonNull LayoutInflater inflater, - @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_blur_playback_controls, container, false); - unbinder = ButterKnife.bind(this, view); - return view; - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - unbinder = ButterKnife.bind(this, view); - setUpMusicControllers(); - - mVolumeContainer.setVisibility(PreferenceUtil.getInstance().getVolumeToggle() ? View.VISIBLE : View.GONE); - - VolumeFragment mVolumeFragment = (VolumeFragment) getChildFragmentManager().findFragmentById(R.id.volume_fragment); - if (mVolumeFragment != null) { - mVolumeFragment.tintWhiteColor(); - } - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - unbinder.unbind(); - } - - private void updateSong() { - Song song = MusicPlayerRemote.getCurrentSong(); - songTitle.setText(song.title); - text.setText(song.artistName); - } - - @Override - public void onResume() { - super.onResume(); - progressViewUpdateHelper.start(); - } - - @Override - public void onPause() { - super.onPause(); - progressViewUpdateHelper.stop(); - } - - @Override - public void onServiceConnected() { - updatePlayPauseDrawableState(); - updateRepeatState(); - updateShuffleState(); - updateSong(); - } - - @Override - public void onPlayingMetaChanged() { - super.onPlayingMetaChanged(); - updateSong(); - } - - @Override - public void onPlayStateChanged() { - updatePlayPauseDrawableState(); - } - - @Override - public void onRepeatModeChanged() { - updateRepeatState(); - } - - @Override - public void onShuffleModeChanged() { - updateShuffleState(); - } - - @Override - public void setDark(int dark) { - lastPlaybackControlsColor = Color.WHITE; - lastDisabledPlaybackControlsColor = ContextCompat.getColor(getContext(), R.color.md_grey_500); - - songTitle.setTextColor(lastPlaybackControlsColor); - text.setTextColor(lastDisabledPlaybackControlsColor); - - setProgressBarColor(); - - songCurrentProgress.setTextColor(lastPlaybackControlsColor); - songTotalTime.setTextColor(lastPlaybackControlsColor); - - updateRepeatState(); - updateShuffleState(); - updatePrevNextColor(); - } - - private void setProgressBarColor() { - TintHelper.setTintAuto(progressSlider, Color.WHITE, false); - } - - private void setUpPlayPauseFab() { - TintHelper.setTintAuto(playPauseFab, Color.WHITE, true); - TintHelper.setTintAuto(playPauseFab, Color.BLACK, false); - playPauseFab.setOnClickListener(new PlayPauseButtonOnClickHandler()); - } - - protected void updatePlayPauseDrawableState() { - if (MusicPlayerRemote.isPlaying()) { - playPauseFab.setImageResource(R.drawable.ic_pause_white_24dp); - } else { - playPauseFab.setImageResource(R.drawable.ic_play_arrow_white_24dp); - } - } - - - private void setUpMusicControllers() { - setUpPlayPauseFab(); - setUpPrevNext(); - setUpRepeatButton(); - setUpShuffleButton(); - setUpProgressSlider(); - } - - private void setUpPrevNext() { - updatePrevNextColor(); - nextButton.setOnClickListener(v -> MusicPlayerRemote.playNextSong()); - prevButton.setOnClickListener(v -> MusicPlayerRemote.back()); - } - - private void updatePrevNextColor() { - nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - prevButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - } - - private void setUpShuffleButton() { - shuffleButton.setOnClickListener(v -> MusicPlayerRemote.toggleShuffleMode()); - } - - @Override - protected void updateShuffleState() { - switch (MusicPlayerRemote.getShuffleMode()) { - case MusicService.SHUFFLE_MODE_SHUFFLE: - shuffleButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - default: - shuffleButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - } - } - - private void setUpRepeatButton() { - repeatButton.setOnClickListener(v -> MusicPlayerRemote.cycleRepeatMode()); - } - - @Override - protected void updateRepeatState() { - switch (MusicPlayerRemote.getRepeatMode()) { - case MusicService.REPEAT_MODE_NONE: - repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp); - repeatButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - case MusicService.REPEAT_MODE_ALL: - repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp); - repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - case MusicService.REPEAT_MODE_THIS: - repeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp); - repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - } - } - - - @Override - protected void show() { - playPauseFab.animate() - .scaleX(1f) - .scaleY(1f) - .rotation(360f) - .setInterpolator(new DecelerateInterpolator()) - .start(); - } - - @Override - protected void hide() { - if (playPauseFab != null) { - playPauseFab.setScaleX(0f); - playPauseFab.setScaleY(0f); - playPauseFab.setRotation(0f); - } - } - - @Override - protected void setUpProgressSlider() { - progressSlider.setOnSeekBarChangeListener(new SimpleOnSeekbarChangeListener() { - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - if (fromUser) { - MusicPlayerRemote.seekTo(progress); - onUpdateProgressViews(MusicPlayerRemote.getSongProgressMillis(), MusicPlayerRemote.getSongDurationMillis()); - } - } - }); - } - - @Override - public void onUpdateProgressViews(int progress, int total) { - progressSlider.setMax(total); - - ObjectAnimator animator = ObjectAnimator.ofInt(progressSlider, "progress", progress); - animator.setDuration(1500); - animator.setInterpolator(new LinearInterpolator()); - animator.start(); - - songTotalTime.setText(MusicUtil.getReadableDurationString(total)); - songCurrentProgress.setText(MusicUtil.getReadableDurationString(progress)); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/blur/BlurPlaybackControlsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/blur/BlurPlaybackControlsFragment.kt new file mode 100644 index 00000000..f643ad47 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/blur/BlurPlaybackControlsFragment.kt @@ -0,0 +1,231 @@ +package code.name.monkey.retromusic.ui.fragments.player.blur + +import android.animation.ObjectAnimator +import android.graphics.Color +import android.graphics.PorterDuff +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.animation.DecelerateInterpolator +import android.view.animation.LinearInterpolator +import android.widget.SeekBar +import android.widget.TextView +import androidx.appcompat.widget.AppCompatTextView +import androidx.core.content.ContextCompat +import butterknife.BindView +import butterknife.ButterKnife +import butterknife.Unbinder +import code.name.monkey.appthemehelper.util.TintHelper +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.helper.MusicPlayerRemote +import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper +import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler +import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener +import code.name.monkey.retromusic.service.MusicService +import code.name.monkey.retromusic.ui.fragments.VolumeFragment +import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerControlsFragment +import code.name.monkey.retromusic.util.MusicUtil +import code.name.monkey.retromusic.util.PreferenceUtil + + +class BlurPlaybackControlsFragment : AbsPlayerControlsFragment() { + + lateinit var songTitle: AppCompatTextView + lateinit var text: TextView + + private var lastPlaybackControlsColor: Int = 0 + private var lastDisabledPlaybackControlsColor: Int = 0 + private var progressViewUpdateHelper: MusicProgressViewUpdateHelper? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + progressViewUpdateHelper = MusicProgressViewUpdateHelper(this) + } + + override fun onCreateView(inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?): View? { + val view = inflater.inflate(R.layout.fragment_blur_playback_controls, container, false) + songTitle = view.findViewById(R.id.title) + text = view.findViewById(R.id.text) + return view + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setUpMusicControllers() + + volumeContainer!!.visibility = if (PreferenceUtil.getInstance().volumeToggle) View.VISIBLE else View.GONE + val mVolumeFragment = childFragmentManager.findFragmentById(R.id.volume_fragment) as VolumeFragment + mVolumeFragment.tintWhiteColor() + } + + private fun updateSong() { + val song = MusicPlayerRemote.currentSong + songTitle.text = song.title + text.text = song.artistName + } + + override fun onResume() { + super.onResume() + progressViewUpdateHelper!!.start() + } + + override fun onPause() { + super.onPause() + progressViewUpdateHelper!!.stop() + } + + override fun onServiceConnected() { + updatePlayPauseDrawableState() + updateRepeatState() + updateShuffleState() + updateSong() + } + + override fun onPlayingMetaChanged() { + super.onPlayingMetaChanged() + updateSong() + } + + override fun onPlayStateChanged() { + updatePlayPauseDrawableState() + } + + override fun onRepeatModeChanged() { + updateRepeatState() + } + + override fun onShuffleModeChanged() { + updateShuffleState() + } + + override fun setDark(color: Int) { + lastPlaybackControlsColor = Color.WHITE + lastDisabledPlaybackControlsColor = ContextCompat.getColor(context!!, R.color.md_grey_500) + + songTitle.setTextColor(lastPlaybackControlsColor) + text.setTextColor(lastDisabledPlaybackControlsColor) + + setProgressBarColor() + + songCurrentProgress!!.setTextColor(lastPlaybackControlsColor) + songTotalTime!!.setTextColor(lastPlaybackControlsColor) + + updateRepeatState() + updateShuffleState() + updatePrevNextColor() + } + + private fun setProgressBarColor() { + TintHelper.setTintAuto(progressSlider!!, Color.WHITE, false) + } + + private fun setUpPlayPauseFab() { + TintHelper.setTintAuto(playPauseFab!!, Color.WHITE, true) + TintHelper.setTintAuto(playPauseFab!!, Color.BLACK, false) + playPauseFab!!.setOnClickListener(PlayPauseButtonOnClickHandler()) + } + + protected fun updatePlayPauseDrawableState() { + if (MusicPlayerRemote.isPlaying) { + playPauseFab!!.setImageResource(R.drawable.ic_pause_white_24dp) + } else { + playPauseFab!!.setImageResource(R.drawable.ic_play_arrow_white_24dp) + } + } + + + private fun setUpMusicControllers() { + setUpPlayPauseFab() + setUpPrevNext() + setUpRepeatButton() + setUpShuffleButton() + setUpProgressSlider() + } + + private fun setUpPrevNext() { + updatePrevNextColor() + nextButton!!.setOnClickListener { MusicPlayerRemote.playNextSong() } + prevButton!!.setOnClickListener { MusicPlayerRemote.back() } + } + + private fun updatePrevNextColor() { + nextButton!!.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + prevButton!!.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + } + + private fun setUpShuffleButton() { + shuffleButton!!.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() } + } + + override fun updateShuffleState() { + when (MusicPlayerRemote.shuffleMode) { + MusicService.SHUFFLE_MODE_SHUFFLE -> shuffleButton!!.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + else -> shuffleButton!!.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + } + } + + private fun setUpRepeatButton() { + repeatButton!!.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() } + } + + override fun updateRepeatState() { + when (MusicPlayerRemote.repeatMode) { + MusicService.REPEAT_MODE_NONE -> { + repeatButton!!.setImageResource(R.drawable.ic_repeat_white_24dp) + repeatButton!!.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + } + MusicService.REPEAT_MODE_ALL -> { + repeatButton!!.setImageResource(R.drawable.ic_repeat_white_24dp) + repeatButton!!.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + } + MusicService.REPEAT_MODE_THIS -> { + repeatButton!!.setImageResource(R.drawable.ic_repeat_one_white_24dp) + repeatButton!!.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + } + } + } + + + override fun show() { + playPauseFab!!.animate() + .scaleX(1f) + .scaleY(1f) + .rotation(360f) + .setInterpolator(DecelerateInterpolator()) + .start() + } + + override fun hide() { + if (playPauseFab != null) { + playPauseFab!!.scaleX = 0f + playPauseFab!!.scaleY = 0f + playPauseFab!!.rotation = 0f + } + } + + override fun setUpProgressSlider() { + progressSlider!!.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() { + override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { + if (fromUser) { + MusicPlayerRemote.seekTo(progress) + onUpdateProgressViews(MusicPlayerRemote.songProgressMillis, MusicPlayerRemote.songDurationMillis) + } + } + }) + } + + override fun onUpdateProgressViews(progress: Int, total: Int) { + progressSlider!!.max = total + + val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress) + animator.duration = 1500 + animator.interpolator = LinearInterpolator() + animator.start() + + songTotalTime!!.text = MusicUtil.getReadableDurationString(total.toLong()) + songCurrentProgress!!.text = MusicUtil.getReadableDurationString(progress.toLong()) + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/blur/BlurPlayerFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/blur/BlurPlayerFragment.java deleted file mode 100644 index 01f6f108..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/blur/BlurPlayerFragment.java +++ /dev/null @@ -1,271 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.player.blur; - -import android.app.Activity; -import android.graphics.Color; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; - -import com.bumptech.glide.Glide; -import com.h6ah4i.android.widget.advrecyclerview.animator.GeneralItemAnimator; -import com.h6ah4i.android.widget.advrecyclerview.animator.RefactoredDefaultItemAnimator; - -import androidx.annotation.ColorInt; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.widget.Toolbar; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.Unbinder; -import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.glide.BlurTransformation; -import code.name.monkey.retromusic.glide.RetroMusicColoredTarget; -import code.name.monkey.retromusic.glide.SongGlideRequest; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.ui.adapter.song.PlayingQueueAdapter; -import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerFragment; -import code.name.monkey.retromusic.ui.fragments.player.PlayerAlbumCoverFragment; -import code.name.monkey.retromusic.ui.fragments.player.normal.PlayerFragment; - -/** - * @author Hemanth S (h4h13). - */ - -public class BlurPlayerFragment extends AbsPlayerFragment implements PlayerAlbumCoverFragment.Callbacks { - - @BindView(R.id.player_toolbar) - Toolbar toolbar; - - @BindView(R.id.toolbar_container) - View toolbarContainer; - - @BindView(R.id.gradient_background) - ImageView colorBackground; - - @Nullable - @BindView(R.id.recycler_view) - RecyclerView recyclerView; - - private int lastColor; - private BlurPlaybackControlsFragment playbackControlsFragment; - private Unbinder unbinder; - - private PlayingQueueAdapter playingQueueAdapter; - private LinearLayoutManager layoutManager; - - public static PlayerFragment newInstance() { - Bundle args = new Bundle(); - PlayerFragment fragment = new PlayerFragment(); - fragment.setArguments(args); - return fragment; - } - - @Override - @ColorInt - public int getPaletteColor() { - return lastColor; - } - - @Override - public void onShow() { - playbackControlsFragment.show(); - } - - @Override - public void onHide() { - playbackControlsFragment.hide(); - onBackPressed(); - } - - @Override - public boolean onBackPressed() { - return false; - } - - @Override - public Toolbar getToolbar() { - return toolbar; - } - - @Override - public int toolbarIconColor() { - return Color.WHITE; - } - - @Override - public void onColorChanged(int color) { - playbackControlsFragment.setDark(color); - lastColor = color; - getCallbacks().onPaletteColorChanged(); - ToolbarContentTintHelper.colorizeToolbar(toolbar, Color.WHITE, getActivity()); - } - - @Override - protected void toggleFavorite(Song song) { - super.toggleFavorite(song); - if (song.id == MusicPlayerRemote.getCurrentSong().id) { - updateIsFavorite(); - } - } - - @Override - public void onFavoriteToggled() { - toggleFavorite(MusicPlayerRemote.getCurrentSong()); - } - - @Override - public void onDestroyView() { - if (recyclerView != null) { - recyclerView.setItemAnimator(null); - recyclerView.setAdapter(null); - recyclerView = null; - } - playingQueueAdapter = null; - layoutManager = null; - super.onDestroyView(); - unbinder.unbind(); - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_blur, container, false); - unbinder = ButterKnife.bind(this, view); - return view; - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - setUpSubFragments(); - setUpPlayerToolbar(); - } - - private void setUpSubFragments() { - playbackControlsFragment = (BlurPlaybackControlsFragment) getChildFragmentManager().findFragmentById(R.id.playback_controls_fragment); - - PlayerAlbumCoverFragment playerAlbumCoverFragment = - (PlayerAlbumCoverFragment) getChildFragmentManager() - .findFragmentById(R.id.player_album_cover_fragment); - if (playerAlbumCoverFragment != null) { - playerAlbumCoverFragment.setCallbacks(this); - } - } - - private void setUpPlayerToolbar() { - toolbar.inflateMenu(R.menu.menu_player); - //noinspection ConstantConditions - toolbar.setNavigationOnClickListener(v -> getActivity().onBackPressed()); - toolbar.setOnMenuItemClickListener(this); - - ToolbarContentTintHelper.colorizeToolbar(toolbar, Color.WHITE, getActivity()); - } - - private void updateBlur() { - Activity activity = getActivity(); - if (activity == null) { - return; - } - - int blurAmount = PreferenceManager.getDefaultSharedPreferences(getContext()) - .getInt("new_blur_amount", 25); - - colorBackground.clearColorFilter(); - - SongGlideRequest.Builder.from(Glide.with(activity), MusicPlayerRemote.getCurrentSong()) - .checkIgnoreMediaStore(activity) - .generatePalette(activity) - .build() - .override(320, 480) - .transform(new BlurTransformation.Builder(getActivity()).blurRadius(blurAmount).build()) - .into(new RetroMusicColoredTarget(colorBackground) { - @Override - public void onColorReady(int color) { - if (color == getDefaultFooterColor()) { - colorBackground.setColorFilter(color); - } - } - }); - } - - @Override - public void onServiceConnected() { - updateIsFavorite(); - updateBlur(); - setUpRecyclerView(); - } - - @Override - public void onPlayingMetaChanged() { - updateIsFavorite(); - updateBlur(); - updateQueuePosition(); - } - - private void setUpRecyclerView() { - if (recyclerView != null) { - final GeneralItemAnimator animator = new RefactoredDefaultItemAnimator(); - - playingQueueAdapter = new PlayingQueueAdapter( - (AppCompatActivity) getActivity(), - MusicPlayerRemote.getPlayingQueue(), - MusicPlayerRemote.getPosition(), - R.layout.item_song, Color.WHITE); - layoutManager = new LinearLayoutManager(getContext()); - - recyclerView.setLayoutManager(layoutManager); - recyclerView.setAdapter(playingQueueAdapter); - recyclerView.setItemAnimator(animator); - layoutManager.scrollToPositionWithOffset(MusicPlayerRemote.getPosition() + 1, 0); - } - } - - @Override - public void onQueueChanged() { - updateQueue(); - updateCurrentSong(); - } - - @Override - public void onMediaStoreChanged() { - updateQueue(); - updateCurrentSong(); - } - - @SuppressWarnings("ConstantConditions") - private void updateCurrentSong() { - } - - private void updateQueuePosition() { - if (playingQueueAdapter != null) { - playingQueueAdapter.setCurrent(MusicPlayerRemote.getPosition()); - // if (slidingUpPanelLayout.getPanelState() == SlidingUpPanelLayout.PanelState.COLLAPSED) { - resetToCurrentPosition(); - //} - } - } - - private void updateQueue() { - if (playingQueueAdapter != null) { - playingQueueAdapter - .swapDataSet(MusicPlayerRemote.getPlayingQueue(), MusicPlayerRemote.getPosition()); - resetToCurrentPosition(); - } - } - - private void resetToCurrentPosition() { - if (recyclerView != null) { - recyclerView.stopScroll(); - layoutManager.scrollToPositionWithOffset(MusicPlayerRemote.getPosition() + 1, 0); - } - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/blur/BlurPlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/blur/BlurPlayerFragment.kt new file mode 100644 index 00000000..a29a82c3 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/blur/BlurPlayerFragment.kt @@ -0,0 +1,198 @@ +package code.name.monkey.retromusic.ui.fragments.player.blur + +import android.graphics.Color +import android.os.Bundle +import android.preference.PreferenceManager +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import androidx.appcompat.app.AppCompatActivity +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.glide.BlurTransformation +import code.name.monkey.retromusic.glide.RetroMusicColoredTarget +import code.name.monkey.retromusic.glide.SongGlideRequest +import code.name.monkey.retromusic.helper.MusicPlayerRemote +import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.ui.adapter.song.PlayingQueueAdapter +import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerFragment +import code.name.monkey.retromusic.ui.fragments.player.PlayerAlbumCoverFragment +import com.bumptech.glide.Glide +import com.h6ah4i.android.widget.advrecyclerview.animator.RefactoredDefaultItemAnimator + +class BlurPlayerFragment : AbsPlayerFragment() { + lateinit var playbackControlsFragment: BlurPlaybackControlsFragment + + private var colorBackground: ImageView? = null + private var lastColor: Int = 0 + private var playingQueueAdapter: PlayingQueueAdapter? = null + private var layoutManager: LinearLayoutManager? = null + private var recyclerView: RecyclerView? = null + + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.fragment_blur, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setUpSubFragments() + setUpPlayerToolbar() + } + + private fun setUpSubFragments() { + playbackControlsFragment = childFragmentManager.findFragmentById(R.id.playback_controls_fragment) as BlurPlaybackControlsFragment + val playerAlbumCoverFragment = childFragmentManager.findFragmentById(R.id.player_album_cover_fragment) as PlayerAlbumCoverFragment + playerAlbumCoverFragment.setCallbacks(this) + } + + private fun setUpPlayerToolbar() { + toolbar!!.apply { + inflateMenu(R.menu.menu_player) + setNavigationOnClickListener { activity!!.onBackPressed() } + ToolbarContentTintHelper.colorizeToolbar(this, Color.WHITE, activity) + }.setOnMenuItemClickListener(this) + } + + override fun onFavoriteToggled() { + toggleFavorite(MusicPlayerRemote.currentSong) + } + + override fun onColorChanged(color: Int) { + playbackControlsFragment.setDark(color) + lastColor = color + callbacks!!.onPaletteColorChanged() + ToolbarContentTintHelper.colorizeToolbar(toolbar!!, Color.WHITE, activity) + } + + override fun toggleFavorite(song: Song) { + super.toggleFavorite(song) + if (song.id == MusicPlayerRemote.currentSong.id) { + updateIsFavorite() + } + } + + override fun onShow() { + + } + + override fun onHide() { + + } + + override fun onBackPressed(): Boolean { + return false + } + + override fun toolbarIconColor(): Int { + return Color.WHITE + } + + override val paletteColor: Int + get() = lastColor + + override fun onDestroyView() { + recyclerView!!.apply { + itemAnimator = null + adapter = null + } + playingQueueAdapter = null + layoutManager = null + super.onDestroyView() + } + + private fun updateBlur() { + val activity = activity ?: return + + val blurAmount = PreferenceManager.getDefaultSharedPreferences(context).getInt("new_blur_amount", 25) + + colorBackground!!.clearColorFilter() + + SongGlideRequest.Builder.from(Glide.with(activity), MusicPlayerRemote.currentSong) + .checkIgnoreMediaStore(activity) + .generatePalette(activity) + .build() + .override(320, 480) + .transform(BlurTransformation.Builder(getActivity()!!).blurRadius(blurAmount.toFloat()).build()) + .into(object : RetroMusicColoredTarget(colorBackground!!) { + override fun onColorReady(color: Int) { + if (color == defaultFooterColor) { + colorBackground!!.setColorFilter(color) + } + } + }) + } + + override fun onServiceConnected() { + updateIsFavorite() + updateBlur() + setUpRecyclerView() + } + + override fun onPlayingMetaChanged() { + updateIsFavorite() + updateBlur() + updateQueuePosition() + } + + private fun setUpRecyclerView() { + if (recyclerView != null) { + val animator = RefactoredDefaultItemAnimator() + + playingQueueAdapter = PlayingQueueAdapter((activity as AppCompatActivity), + MusicPlayerRemote.playingQueue, + MusicPlayerRemote.position, + R.layout.item_song, + Color.WHITE) + layoutManager = LinearLayoutManager(context) + + recyclerView!!.apply { + layoutManager = layoutManager + adapter = playingQueueAdapter + itemAnimator = animator + } + layoutManager!!.scrollToPositionWithOffset(MusicPlayerRemote.position + 1, 0) + } + } + + override fun onQueueChanged() { + updateQueue() + updateCurrentSong() + } + + override fun onMediaStoreChanged() { + updateQueue() + updateCurrentSong() + } + + private fun updateCurrentSong() {} + + private fun updateQueuePosition() { + + playingQueueAdapter!!.apply { + setCurrent(MusicPlayerRemote.position) + resetToCurrentPosition() + } + + } + + private fun updateQueue() { + playingQueueAdapter!!.apply { + swapDataSet(MusicPlayerRemote.playingQueue, MusicPlayerRemote.position) + resetToCurrentPosition() + } + + } + + private fun resetToCurrentPosition() { + recyclerView!!.apply { + stopScroll() + } + layoutManager!!.scrollToPositionWithOffset(MusicPlayerRemote.position + 1, 0) + } +} + diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/card/CardFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/card/CardFragment.java deleted file mode 100644 index 0a1e9e25..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/card/CardFragment.java +++ /dev/null @@ -1,281 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.player.card; - -import android.graphics.Color; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import com.h6ah4i.android.widget.advrecyclerview.animator.GeneralItemAnimator; -import com.h6ah4i.android.widget.advrecyclerview.animator.RefactoredDefaultItemAnimator; -import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager; -import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils; - -import androidx.annotation.ColorInt; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.widget.Toolbar; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.Unbinder; -import code.name.monkey.appthemehelper.util.ColorUtil; -import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.ui.adapter.song.PlayingQueueAdapter; -import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerFragment; -import code.name.monkey.retromusic.ui.fragments.player.PlayerAlbumCoverFragment; -import code.name.monkey.retromusic.ui.fragments.player.normal.PlayerFragment; - -public class CardFragment extends AbsPlayerFragment implements PlayerAlbumCoverFragment.Callbacks { - - @BindView(R.id.player_toolbar) - Toolbar toolbar; - - @Nullable - @BindView(R.id.recycler_view) - - RecyclerView recyclerView; - @Nullable - - @BindView(R.id.title) - TextView title; - - private RecyclerView.Adapter wrappedAdapter; - private RecyclerViewDragDropManager recyclerViewDragDropManager; - private PlayingQueueAdapter playingQueueAdapter; - private LinearLayoutManager layoutManager; - private int lastColor; - private CardPlaybackControlsFragment playbackControlsFragment; - private Unbinder unbinder; - - public static PlayerFragment newInstance() { - Bundle args = new Bundle(); - PlayerFragment fragment = new PlayerFragment(); - fragment.setArguments(args); - return fragment; - } - - @Override - @ColorInt - public int getPaletteColor() { - return lastColor; - } - - @Override - public void onShow() { - playbackControlsFragment.show(); - } - - @Override - public void onHide() { - playbackControlsFragment.hide(); - onBackPressed(); - } - - @Override - public void onResume() { - super.onResume(); - } - - @Override - public boolean onBackPressed() { - return false; - } - - @Override - public Toolbar getToolbar() { - return toolbar; - } - - @Override - public int toolbarIconColor() { - return Color.WHITE; - } - - @Override - public void onColorChanged(int color) { - playbackControlsFragment.setDark(color); - lastColor = color; - getCallbacks().onPaletteColorChanged(); - - ToolbarContentTintHelper.colorizeToolbar(toolbar, Color.WHITE, getActivity()); - - if (title != null && playingQueueAdapter != null) { - if (ColorUtil.isColorLight(color)) { - title.setTextColor(Color.BLACK); - playingQueueAdapter.usePalette(false); - } else { - title.setTextColor(Color.WHITE); - playingQueueAdapter.usePalette(true); - } - } - } - - @Override - protected void toggleFavorite(Song song) { - super.toggleFavorite(song); - if (song.id == MusicPlayerRemote.getCurrentSong().id) { - updateIsFavorite(); - } - } - - @Override - public void onFavoriteToggled() { - toggleFavorite(MusicPlayerRemote.getCurrentSong()); - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - if (recyclerViewDragDropManager != null) { - recyclerViewDragDropManager.release(); - recyclerViewDragDropManager = null; - } - - if (recyclerView != null) { - recyclerView.setItemAnimator(null); - recyclerView.setAdapter(null); - recyclerView = null; - } - - if (wrappedAdapter != null) { - WrapperAdapterUtils.releaseAll(wrappedAdapter); - wrappedAdapter = null; - } - playingQueueAdapter = null; - layoutManager = null; - super.onDestroyView(); - unbinder.unbind(); - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_card_player, container, false); - unbinder = ButterKnife.bind(this, view); - return view; - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - setUpSubFragments(); - setUpPlayerToolbar(); - setUpRecyclerView(); - } - - private void setUpSubFragments() { - playbackControlsFragment = - (CardPlaybackControlsFragment) getChildFragmentManager() - .findFragmentById(R.id.playback_controls_fragment); - - PlayerAlbumCoverFragment playerAlbumCoverFragment = (PlayerAlbumCoverFragment) getChildFragmentManager() - .findFragmentById(R.id.player_album_cover_fragment); - playerAlbumCoverFragment.setCallbacks(this); - playerAlbumCoverFragment.removeSlideEffect(); - } - - private void setUpPlayerToolbar() { - toolbar.inflateMenu(R.menu.menu_player); - toolbar.setNavigationOnClickListener(v -> getActivity().onBackPressed()); - toolbar.setOnMenuItemClickListener(this); - - ToolbarContentTintHelper.colorizeToolbar(toolbar, Color.WHITE, getActivity()); - - } - - @Override - public void onServiceConnected() { - updateIsFavorite(); - //updateLyrics(); - setUpRecyclerView(); - } - - @Override - public void onPlayingMetaChanged() { - updateIsFavorite(); - //updateLyrics(); - updateQueuePosition(); - } - - private void setUpRecyclerView() { - if (recyclerView != null) { - recyclerViewDragDropManager = new RecyclerViewDragDropManager(); - final GeneralItemAnimator animator = new RefactoredDefaultItemAnimator(); - - playingQueueAdapter = new PlayingQueueAdapter( - (AppCompatActivity) getActivity(), - MusicPlayerRemote.getPlayingQueue(), - MusicPlayerRemote.getPosition(), - R.layout.item_song); - wrappedAdapter = recyclerViewDragDropManager.createWrappedAdapter(playingQueueAdapter); - - layoutManager = new LinearLayoutManager(getContext()); - - recyclerView.setLayoutManager(layoutManager); - recyclerView.setAdapter(wrappedAdapter); - recyclerView.setItemAnimator(animator); - recyclerViewDragDropManager.attachRecyclerView(recyclerView); - layoutManager.scrollToPositionWithOffset(MusicPlayerRemote.getPosition() + 1, 0); - } - } - - @Override - public void onQueueChanged() { - updateQueue(); - updateCurrentSong(); - } - - @Override - public void onMediaStoreChanged() { - updateQueue(); - updateCurrentSong(); - } - - @SuppressWarnings("ConstantConditions") - private void updateCurrentSong() { - } - - private void updateQueuePosition() { - if (playingQueueAdapter != null) { - playingQueueAdapter.setCurrent(MusicPlayerRemote.getPosition()); - } - // if (slidingUpPanelLayout.getPanelState() == SlidingUpPanelLayout.PanelState.COLLAPSED) { - resetToCurrentPosition(); - //} - } - - private void updateQueue() { - if (playingQueueAdapter != null) { - playingQueueAdapter.swapDataSet(MusicPlayerRemote.getPlayingQueue(), - MusicPlayerRemote.getPosition()); - resetToCurrentPosition(); - } - } - - private void resetToCurrentPosition() { - if (recyclerView != null) { - recyclerView.stopScroll(); - layoutManager.scrollToPositionWithOffset(MusicPlayerRemote.getPosition() + 1, 0); - } - - } - - @Override - public void onPause() { - if (recyclerViewDragDropManager != null) { - recyclerViewDragDropManager.cancelDrag(); - } - - super.onPause(); - } - - -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/card/CardPlaybackControlsFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/card/CardPlaybackControlsFragment.java deleted file mode 100644 index 0abcaff0..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/card/CardPlaybackControlsFragment.java +++ /dev/null @@ -1,334 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.player.card; - -import android.animation.ObjectAnimator; -import android.graphics.PorterDuff; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.animation.LinearInterpolator; -import android.widget.ImageButton; -import android.widget.ImageView; -import android.widget.SeekBar; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.widget.AppCompatSeekBar; -import androidx.appcompat.widget.AppCompatTextView; -import androidx.cardview.widget.CardView; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import butterknife.Unbinder; -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.appthemehelper.util.ATHUtil; -import code.name.monkey.appthemehelper.util.ColorUtil; -import code.name.monkey.appthemehelper.util.MaterialValueHelper; -import code.name.monkey.appthemehelper.util.TintHelper; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper; -import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler; -import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.service.MusicService; -import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerControlsFragment; -import code.name.monkey.retromusic.util.MusicUtil; -import code.name.monkey.retromusic.util.PreferenceUtil; -import code.name.monkey.retromusic.views.PlayPauseDrawable; - -public class CardPlaybackControlsFragment extends AbsPlayerControlsFragment { - @BindView(R.id.player_play_pause_button) - ImageButton playPauseFab; - - @BindView(R.id.player_prev_button) - ImageButton prevButton; - - @BindView(R.id.player_next_button) - ImageButton nextButton; - - @BindView(R.id.player_repeat_button) - ImageButton repeatButton; - - @BindView(R.id.player_shuffle_button) - ImageButton shuffleButton; - - @BindView(R.id.player_progress_slider) - AppCompatSeekBar progressSlider; - - @BindView(R.id.player_song_total_time) - TextView songTotalTime; - - @BindView(R.id.player_song_current_progress) - TextView songCurrentProgress; - - @BindView(R.id.title) - AppCompatTextView title; - - @BindView(R.id.text) - TextView text; - - @BindView(R.id.volume_fragment_container) - View volumeContainer; - - @BindView(R.id.menu) - View menuView; - - @BindView(R.id.image_text_container) - CardView colorContainer; - - @BindView(R.id.image) - ImageView playImageView; - - @BindView(R.id.playback_controls) - View playbackControls; - - private Unbinder unbinder; - private PlayPauseDrawable playPauseDrawable; - private int lastPlaybackControlsColor; - private int lastDisabledPlaybackControlsColor; - private MusicProgressViewUpdateHelper progressViewUpdateHelper; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - progressViewUpdateHelper = new MusicProgressViewUpdateHelper(this); - } - - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_card_player_playback_controls, container, false); - unbinder = ButterKnife.bind(this, view); - return view; - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - unbinder = ButterKnife.bind(this, view); - setUpMusicControllers(); - - hideVolumeIfAvailable(); - - setupControls(); - } - - private void setupControls() { - playImageView.setImageResource(R.drawable.ic_play_circle_filled_white_24dp); - //noinspection ConstantConditions - int iconPadding = getActivity().getResources().getDimensionPixelSize(R.dimen.list_item_image_icon_padding); - playImageView.setPadding(iconPadding, iconPadding, iconPadding, iconPadding); - - menuView.setVisibility(View.GONE); - - int primaryColor = ThemeStore.primaryColor(getContext()); - playbackControls.setBackgroundColor(primaryColor); - colorContainer.setCardBackgroundColor(primaryColor); - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - unbinder.unbind(); - } - - private void updateSong() { - Song song = MusicPlayerRemote.getCurrentSong(); - title.setText(song.title); - text.setText(song.artistName); - - } - - @Override - public void onResume() { - super.onResume(); - progressViewUpdateHelper.start(); - } - - @Override - public void onPause() { - super.onPause(); - progressViewUpdateHelper.stop(); - } - - @Override - public void onServiceConnected() { - updatePlayPauseDrawableState(); - updateRepeatState(); - updateShuffleState(); - updateSong(); - } - - @Override - public void onPlayingMetaChanged() { - super.onPlayingMetaChanged(); - updateSong(); - } - - @Override - public void onPlayStateChanged() { - updatePlayPauseDrawableState(); - } - - @Override - public void onRepeatModeChanged() { - updateRepeatState(); - } - - @Override - public void onShuffleModeChanged() { - updateShuffleState(); - } - - @Override - public void setDark(int dark) { - playImageView.setColorFilter(dark, PorterDuff.Mode.SRC_IN); - if (ColorUtil.isColorLight(ATHUtil.resolveColor(getContext(), android.R.attr.windowBackground))) { - lastPlaybackControlsColor = MaterialValueHelper.getSecondaryTextColor(getActivity(), true); - lastDisabledPlaybackControlsColor = MaterialValueHelper.getSecondaryDisabledTextColor(getActivity(), true); - } else { - lastPlaybackControlsColor = MaterialValueHelper.getPrimaryTextColor(getActivity(), false); - lastDisabledPlaybackControlsColor = MaterialValueHelper.getPrimaryDisabledTextColor(getActivity(), false); - } - - updateRepeatState(); - updateShuffleState(); - updatePrevNextColor(); - updatePlayPauseColor(); - updateProgressTextColor(); - - TintHelper.setTintAuto(playPauseFab, MaterialValueHelper.getPrimaryTextColor(getContext(), ColorUtil.isColorLight(dark)), false); - TintHelper.setTintAuto(playPauseFab, dark, true); - } - - private void updatePlayPauseColor() { - //playPauseButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - } - - private void setUpPlayPauseFab() { - playPauseFab.setOnClickListener(new PlayPauseButtonOnClickHandler()); - } - - protected void updatePlayPauseDrawableState() { - if (MusicPlayerRemote.isPlaying()) { - playPauseFab.setImageResource(R.drawable.ic_pause_white_24dp); - } else { - playPauseFab.setImageResource(R.drawable.ic_play_arrow_white_24dp); - } - } - - private void setUpMusicControllers() { - setUpPlayPauseFab(); - setUpPrevNext(); - setUpRepeatButton(); - setUpShuffleButton(); - setUpProgressSlider(); - } - - private void setUpPrevNext() { - updatePrevNextColor(); - nextButton.setOnClickListener(v -> MusicPlayerRemote.playNextSong()); - prevButton.setOnClickListener(v -> MusicPlayerRemote.back()); - } - - private void updatePrevNextColor() { - nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - prevButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - } - - private void setUpShuffleButton() { - shuffleButton.setOnClickListener(v -> MusicPlayerRemote.toggleShuffleMode()); - } - - @Override - protected void updateShuffleState() { - switch (MusicPlayerRemote.getShuffleMode()) { - case MusicService.SHUFFLE_MODE_SHUFFLE: - shuffleButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - default: - shuffleButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - } - } - - private void setUpRepeatButton() { - repeatButton.setOnClickListener(v -> MusicPlayerRemote.cycleRepeatMode()); - } - - @Override - protected void updateRepeatState() { - switch (MusicPlayerRemote.getRepeatMode()) { - case MusicService.REPEAT_MODE_NONE: - repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp); - repeatButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - case MusicService.REPEAT_MODE_ALL: - repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp); - repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - case MusicService.REPEAT_MODE_THIS: - repeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp); - repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - } - } - - private void updateProgressTextColor() { - int color = MaterialValueHelper.getPrimaryTextColor(getContext(), false); - songTotalTime.setTextColor(color); - songCurrentProgress.setTextColor(color); - } - - @Override - protected void show() { - //Ignore - } - - @Override - protected void hide() { - //Ignore - } - - @Override - protected void setUpProgressSlider() { - progressSlider.setOnSeekBarChangeListener(new SimpleOnSeekbarChangeListener() { - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - if (fromUser) { - MusicPlayerRemote.seekTo(progress); - onUpdateProgressViews(MusicPlayerRemote.getSongProgressMillis(), MusicPlayerRemote.getSongDurationMillis()); - } - } - }); - } - - @OnClick(R.id.player_play_pause_button) - void showAnimation() { - if (MusicPlayerRemote.isPlaying()) { - MusicPlayerRemote.pauseSong(); - } else { - MusicPlayerRemote.resumePlaying(); - } - showBouceAnimation(playPauseFab); - } - - @Override - public void onUpdateProgressViews(int progress, int total) { - progressSlider.setMax(total); - - ObjectAnimator animator = ObjectAnimator.ofInt(progressSlider, "progress", progress); - animator.setDuration(1500); - animator.setInterpolator(new LinearInterpolator()); - animator.start(); - - songTotalTime.setText(MusicUtil.getReadableDurationString(total)); - songCurrentProgress.setText(MusicUtil.getReadableDurationString(progress)); - } - - private void hideVolumeIfAvailable() { - volumeContainer.setVisibility(PreferenceUtil.getInstance().getVolumeToggle() ? View.VISIBLE : View.GONE); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/cardblur/CardBlurFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/cardblur/CardBlurFragment.java deleted file mode 100644 index 97e7931f..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/cardblur/CardBlurFragment.java +++ /dev/null @@ -1,201 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.player.cardblur; - -import android.app.Activity; -import android.graphics.Color; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; - -import com.bumptech.glide.Glide; - -import androidx.annotation.ColorInt; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.widget.Toolbar; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.Unbinder; -import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.glide.BlurTransformation; -import code.name.monkey.retromusic.glide.RetroMusicColoredTarget; -import code.name.monkey.retromusic.glide.SongGlideRequest; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerFragment; -import code.name.monkey.retromusic.ui.fragments.player.PlayerAlbumCoverFragment; -import code.name.monkey.retromusic.ui.fragments.player.normal.PlayerFragment; - -public class CardBlurFragment extends AbsPlayerFragment implements - PlayerAlbumCoverFragment.Callbacks { - - @BindView(R.id.player_toolbar) - Toolbar toolbar; - - @BindView(R.id.gradient_background) - ImageView colorBackground; - - private int lastColor; - private CardBlurPlaybackControlsFragment playbackControlsFragment; - private Unbinder unbinder; - - public static PlayerFragment newInstance() { - Bundle args = new Bundle(); - PlayerFragment fragment = new PlayerFragment(); - fragment.setArguments(args); - return fragment; - } - - @Override - @ColorInt - public int getPaletteColor() { - return lastColor; - } - - @Override - public void onShow() { - playbackControlsFragment.show(); - } - - @Override - public void onHide() { - playbackControlsFragment.hide(); - onBackPressed(); - } - - @Override - public void onResume() { - super.onResume(); - } - - @Override - public boolean onBackPressed() { - return false; - } - - @Override - public Toolbar getToolbar() { - return toolbar; - } - - @Override - public int toolbarIconColor() { - return Color.WHITE; - } - - @Override - public void onColorChanged(int color) { - playbackControlsFragment.setDark(color); - lastColor = color; - getCallbacks().onPaletteColorChanged(); - ToolbarContentTintHelper.colorizeToolbar(toolbar, Color.WHITE, getActivity()); - - toolbar.setTitleTextColor(Color.WHITE); - toolbar.setSubtitleTextColor(Color.WHITE); - } - - @Override - protected void toggleFavorite(Song song) { - super.toggleFavorite(song); - if (song.id == MusicPlayerRemote.getCurrentSong().id) { - updateIsFavorite(); - } - } - - @Override - public void onFavoriteToggled() { - toggleFavorite(MusicPlayerRemote.getCurrentSong()); - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - unbinder.unbind(); - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_card_blur_player, container, false); - unbinder = ButterKnife.bind(this, view); - return view; - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - setUpSubFragments(); - setUpPlayerToolbar(); - } - - private void setUpSubFragments() { - playbackControlsFragment = (CardBlurPlaybackControlsFragment) getChildFragmentManager() - .findFragmentById(R.id.playback_controls_fragment); - - PlayerAlbumCoverFragment playerAlbumCoverFragment = - (PlayerAlbumCoverFragment) getChildFragmentManager().findFragmentById(R.id.player_album_cover_fragment); - if (playerAlbumCoverFragment != null) { - playerAlbumCoverFragment.setCallbacks(this); - playerAlbumCoverFragment.removeEffect(); - } - - } - - private void setUpPlayerToolbar() { - toolbar.inflateMenu(R.menu.menu_player); - toolbar.setNavigationOnClickListener(v -> getActivity().onBackPressed()); - toolbar.setOnMenuItemClickListener(this); - - ToolbarContentTintHelper.colorizeToolbar(toolbar, Color.WHITE, getActivity()); - toolbar.setTitleTextColor(Color.WHITE); - toolbar.setSubtitleTextColor(Color.WHITE); - } - - @Override - public void onServiceConnected() { - updateIsFavorite(); - updateBlur(); - updateSong(); - } - - @Override - public void onPlayingMetaChanged() { - updateIsFavorite(); - updateBlur(); - updateSong(); - } - - private void updateSong() { - Song song = MusicPlayerRemote.getCurrentSong(); - toolbar.setTitle(song.title); - toolbar.setSubtitle(song.artistName); - } - - private void updateBlur() { - Activity activity = getActivity(); - if (activity == null) { - return; - } - int blurAmount = PreferenceManager.getDefaultSharedPreferences(getContext()) - .getInt("new_blur_amount", 25); - - colorBackground.clearColorFilter(); - SongGlideRequest.Builder.from(Glide.with(activity), MusicPlayerRemote.getCurrentSong()) - .checkIgnoreMediaStore(activity) - .generatePalette(activity) - .build() - .transform(new BlurTransformation.Builder(getActivity()).blurRadius(blurAmount).build()) - .into(new RetroMusicColoredTarget(colorBackground) { - @Override - public void onColorReady(int color) { - if (color == getDefaultFooterColor()) { - colorBackground.setColorFilter(color); - } - } - }); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/cardblur/CardBlurPlaybackControlsFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/cardblur/CardBlurPlaybackControlsFragment.java deleted file mode 100644 index 0b6acbc2..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/cardblur/CardBlurPlaybackControlsFragment.java +++ /dev/null @@ -1,309 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.player.cardblur; - -import android.animation.ObjectAnimator; -import android.content.res.ColorStateList; -import android.graphics.Color; -import android.graphics.PorterDuff; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.animation.AccelerateInterpolator; -import android.view.animation.DecelerateInterpolator; -import android.view.animation.LinearInterpolator; -import android.widget.ImageButton; -import android.widget.SeekBar; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.widget.AppCompatSeekBar; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import butterknife.Unbinder; -import code.name.monkey.appthemehelper.util.ColorUtil; -import code.name.monkey.appthemehelper.util.MaterialValueHelper; -import code.name.monkey.appthemehelper.util.TintHelper; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper; -import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler; -import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener; -import code.name.monkey.retromusic.service.MusicService; -import code.name.monkey.retromusic.ui.fragments.VolumeFragment; -import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerControlsFragment; -import code.name.monkey.retromusic.util.MusicUtil; -import code.name.monkey.retromusic.util.PreferenceUtil; - -public class CardBlurPlaybackControlsFragment extends AbsPlayerControlsFragment { - @BindView(R.id.player_play_pause_button) - ImageButton playPauseFab; - - @BindView(R.id.player_prev_button) - ImageButton prevButton; - - @BindView(R.id.player_next_button) - ImageButton nextButton; - - @BindView(R.id.player_repeat_button) - ImageButton repeatButton; - - @BindView(R.id.player_shuffle_button) - ImageButton shuffleButton; - - @BindView(R.id.player_progress_slider) - AppCompatSeekBar progressSlider; - - @BindView(R.id.player_song_total_time) - TextView songTotalTime; - - @BindView(R.id.player_song_current_progress) - TextView songCurrentProgress; - - @BindView(R.id.volume_fragment_container) - View volumeContainer; - - - private Unbinder unbinder; - private int lastPlaybackControlsColor; - private int lastDisabledPlaybackControlsColor; - private MusicProgressViewUpdateHelper progressViewUpdateHelper; - - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - progressViewUpdateHelper = new MusicProgressViewUpdateHelper(this); - } - - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_card_blur_player_playback_controls, container, false); - unbinder = ButterKnife.bind(this, view); - return view; - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - unbinder = ButterKnife.bind(this, view); - setUpMusicControllers(); - hideVolumeIfAvailable(); - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - unbinder.unbind(); - } - - @Override - public void onResume() { - super.onResume(); - progressViewUpdateHelper.start(); - } - - @Override - public void onPause() { - super.onPause(); - progressViewUpdateHelper.stop(); - } - - @Override - public void onServiceConnected() { - updatePlayPauseDrawableState(); - updateRepeatState(); - updateShuffleState(); - } - - - @Override - public void onPlayStateChanged() { - updatePlayPauseDrawableState(); - } - - @Override - public void onRepeatModeChanged() { - updateRepeatState(); - } - - @Override - public void onShuffleModeChanged() { - updateShuffleState(); - } - - @Override - public void setDark(int dark) { - lastPlaybackControlsColor = Color.WHITE; - lastDisabledPlaybackControlsColor = ColorUtil.withAlpha(Color.WHITE, 0.3f); - - updateRepeatState(); - updateShuffleState(); - updatePrevNextColor(); - updateProgressTextColor(); - } - - - private void setUpPlayPauseFab() { - TintHelper.setTintAuto(playPauseFab, Color.WHITE, true); - TintHelper.setTintAuto(playPauseFab, Color.BLACK, false); - playPauseFab.setOnClickListener(new PlayPauseButtonOnClickHandler()); - } - - protected void updatePlayPauseDrawableState() { - if (MusicPlayerRemote.isPlaying()) { - playPauseFab.setImageResource(R.drawable.ic_pause_white_24dp); - } else { - playPauseFab.setImageResource(R.drawable.ic_play_arrow_white_24dp); - } - } - - private void setUpMusicControllers() { - setUpPlayPauseFab(); - setUpPrevNext(); - setUpRepeatButton(); - setUpShuffleButton(); - setUpProgressSlider(); - setupVolumeControls(); - } - - private void setupVolumeControls() { - VolumeFragment volumeFragment = (VolumeFragment) getChildFragmentManager() - .findFragmentById(R.id.volume_fragment); - if (volumeFragment != null) { - volumeFragment.tintWhiteColor(); - } - } - - private void setUpPrevNext() { - updatePrevNextColor(); - nextButton.setOnClickListener(v -> MusicPlayerRemote.playNextSong()); - prevButton.setOnClickListener(v -> MusicPlayerRemote.back()); - } - - private void updatePrevNextColor() { - nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - prevButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - } - - private void setUpShuffleButton() { - shuffleButton.setOnClickListener(v -> MusicPlayerRemote.toggleShuffleMode()); - } - - @Override - protected void updateShuffleState() { - switch (MusicPlayerRemote.getShuffleMode()) { - case MusicService.SHUFFLE_MODE_SHUFFLE: - shuffleButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - default: - shuffleButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - } - } - - private void setUpRepeatButton() { - repeatButton.setOnClickListener(v -> MusicPlayerRemote.cycleRepeatMode()); - } - - @Override - protected void updateRepeatState() { - switch (MusicPlayerRemote.getRepeatMode()) { - case MusicService.REPEAT_MODE_NONE: - repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp); - repeatButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - case MusicService.REPEAT_MODE_ALL: - repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp); - repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - case MusicService.REPEAT_MODE_THIS: - repeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp); - repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - } - } - - private void updateProgressTextColor() { - int color = MaterialValueHelper.getPrimaryTextColor(getContext(), false); - songTotalTime.setTextColor(color); - songCurrentProgress.setTextColor(color); - } - - @Override - protected void show() { - //Ignore - } - - @Override - protected void hide() { - //Ignore - } - - @Override - protected void setUpProgressSlider() { - progressSlider.setOnSeekBarChangeListener(new SimpleOnSeekbarChangeListener() { - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - if (fromUser) { - MusicPlayerRemote.seekTo(progress); - onUpdateProgressViews(MusicPlayerRemote.getSongProgressMillis(), MusicPlayerRemote.getSongDurationMillis()); - } - } - }); - progressSlider.setBackgroundTintList(ColorStateList.valueOf(Color.WHITE)); - } - - public void showBouceAnimation() { - playPauseFab.clearAnimation(); - - playPauseFab.setScaleX(0.9f); - playPauseFab.setScaleY(0.9f); - playPauseFab.setVisibility(View.VISIBLE); - playPauseFab.setPivotX(playPauseFab.getWidth() / 2); - playPauseFab.setPivotY(playPauseFab.getHeight() / 2); - - playPauseFab.animate() - .setDuration(200) - .setInterpolator(new DecelerateInterpolator()) - .scaleX(1.1f) - .scaleY(1.1f) - .withEndAction(() -> playPauseFab.animate() - .setDuration(200) - .setInterpolator(new AccelerateInterpolator()) - .scaleX(1f) - .scaleY(1f) - .alpha(1f) - .start()) - .start(); - } - - @OnClick(R.id.player_play_pause_button) - void showAnimation() { - if (MusicPlayerRemote.isPlaying()) { - MusicPlayerRemote.pauseSong(); - } else { - MusicPlayerRemote.resumePlaying(); - } - showBouceAnimation(); - } - - @Override - public void onUpdateProgressViews(int progress, int total) { - progressSlider.setMax(total); - - ObjectAnimator animator = ObjectAnimator.ofInt(progressSlider, "progress", progress); - animator.setDuration(1500); - animator.setInterpolator(new LinearInterpolator()); - animator.start(); - - songTotalTime.setText(MusicUtil.getReadableDurationString(total)); - songCurrentProgress.setText(MusicUtil.getReadableDurationString(progress)); - } - - public void hideVolumeIfAvailable() { - volumeContainer.setVisibility(PreferenceUtil.getInstance().getVolumeToggle() ? View.VISIBLE : View.GONE); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/color/ColorFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/color/ColorFragment.java deleted file mode 100644 index 76d46883..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/color/ColorFragment.java +++ /dev/null @@ -1,309 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.player.color; - -import android.animation.ArgbEvaluator; -import android.animation.ValueAnimator; -import android.annotation.SuppressLint; -import android.app.Activity; -import android.content.Intent; -import android.graphics.drawable.Drawable; -import android.os.AsyncTask; -import android.os.Bundle; -import android.text.TextUtils; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.TextView; - -import com.bumptech.glide.Glide; -import com.bumptech.glide.request.animation.GlideAnimation; - -import androidx.annotation.ColorInt; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.widget.Toolbar; -import androidx.palette.graphics.Palette; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import butterknife.Unbinder; -import code.name.monkey.appthemehelper.util.ATHUtil; -import code.name.monkey.appthemehelper.util.ColorUtil; -import code.name.monkey.appthemehelper.util.MaterialValueHelper; -import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.glide.RetroMusicColoredTarget; -import code.name.monkey.retromusic.glide.SongGlideRequest; -import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.model.lyrics.Lyrics; -import code.name.monkey.retromusic.ui.activities.LyricsActivity; -import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerFragment; -import code.name.monkey.retromusic.util.MusicUtil; -import code.name.monkey.retromusic.util.RetroColorUtil; -import code.name.monkey.retromusic.util.ViewUtil; - -public class ColorFragment extends AbsPlayerFragment { - - @BindView(R.id.player_toolbar) - Toolbar toolbar; - - @BindView(R.id.gradient_background) - View colorBackground; - - @BindView(R.id.image) - ImageView imageView; - - @BindView(R.id.lyrics) - TextView lyricsView; - - @BindView(R.id.lyrics_container) - View lyricsViewContainer; - - @BindView(R.id.album_cover_container) - View imageViewContainer; - - private int lastColor; - private int backgroundColor; - private ColorPlaybackControlsFragment playbackControlsFragment; - private Unbinder unbinder; - private ValueAnimator valueAnimator; - private AsyncTask updateLyricsAsyncTask; - private Lyrics lyrics; - - public static ColorFragment newInstance() { - Bundle args = new Bundle(); - ColorFragment fragment = new ColorFragment(); - fragment.setArguments(args); - return fragment; - } - - @Override - public void onShow() { - playbackControlsFragment.show(); - } - - @Override - public void onHide() { - playbackControlsFragment.hide(); - onBackPressed(); - } - - @Override - public boolean onBackPressed() { - return false; - } - - @Override - @ColorInt - public int getPaletteColor() { - return backgroundColor; - } - - @Override - public Toolbar getToolbar() { - return toolbar; - } - - @Override - public int toolbarIconColor() { - return lastColor; - } - - @Override - protected void toggleFavorite(Song song) { - super.toggleFavorite(song); - if (song.id == MusicPlayerRemote.getCurrentSong().id) { - updateIsFavorite(); - } - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - unbinder.unbind(); - if (valueAnimator != null) { - valueAnimator.cancel(); - valueAnimator = null; - } - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_color_player, container, false); - unbinder = ButterKnife.bind(this, view); - return view; - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - setUpSubFragments(); - setUpPlayerToolbar(); - } - - private void setUpSubFragments() { - playbackControlsFragment = (ColorPlaybackControlsFragment) - getChildFragmentManager().findFragmentById(R.id.playback_controls_fragment); - - } - - private void setUpPlayerToolbar() { - toolbar.inflateMenu(R.menu.menu_player); - toolbar.setNavigationOnClickListener(v -> getActivity().onBackPressed()); - toolbar.setOnMenuItemClickListener(this); - - ToolbarContentTintHelper.colorizeToolbar(toolbar, - ATHUtil.resolveColor(getContext(), R.attr.iconColor), getActivity()); - } - - @Override - public void onPlayingMetaChanged() { - super.onPlayingMetaChanged(); - updateSong(); - updateLyricsLocal(); - } - - @Override - public void onServiceConnected() { - super.onServiceConnected(); - updateSong(); - updateLyricsLocal(); - } - - private void updateSong() { - Activity activity = getActivity(); - - SongGlideRequest.Builder.from(Glide.with(activity), MusicPlayerRemote.getCurrentSong()) - .checkIgnoreMediaStore(activity) - .generatePalette(activity).build().dontAnimate() - .into(new RetroMusicColoredTarget(imageView) { - @Override - public void onColorReady(int color) { - //setColors(color); - } - - @Override - public void onLoadFailed(Exception e, Drawable errorDrawable) { - super.onLoadFailed(e, errorDrawable); - - int backgroundColor = getDefaultFooterColor(); - int textColor = ColorUtil.isColorLight(getDefaultFooterColor()) ? - MaterialValueHelper.getPrimaryTextColor(getContext(), true) : - MaterialValueHelper.getPrimaryTextColor(getContext(), false); - - setColors(backgroundColor, textColor); - } - - @Override - public void onResourceReady(BitmapPaletteWrapper resource, - GlideAnimation glideAnimation) { - super.onResourceReady(resource, glideAnimation); - /* MediaNotificationProcessor processor = new MediaNotificationProcessor(getContext(), - getContext()); - Palette.Builder builder = MediaNotificationProcessor - .generatePalette(resource.getBitmap()); - - int backgroundColor = processor.getBackgroundColor(builder); - int textColor = processor.getTextColor(builder);*/ - - Palette palette = resource.getPalette(); - Palette.Swatch swatch = RetroColorUtil.getSwatch(palette); - - int textColor = RetroColorUtil.getTextColor(palette); - int backgroundColor = swatch.getRgb(); - - setColors(backgroundColor, textColor); - } - }); - } - - private void setColors(int backgroundColor, int textColor) { - playbackControlsFragment.setDark(textColor, backgroundColor); - - colorBackground.setBackgroundColor(backgroundColor); - - ToolbarContentTintHelper.colorizeToolbar(toolbar, textColor, getActivity()); - - lastColor = textColor; - - this.backgroundColor = backgroundColor; - - if (getPlayerActivity() != null) { - getPlayerActivity().setLightNavigationBar(ColorUtil.isColorLight(backgroundColor)); - } - getCallbacks().onPaletteColorChanged(); - - } - - private void colorize(int i) { - if (valueAnimator != null) { - valueAnimator.cancel(); - } - - valueAnimator = ValueAnimator.ofObject(new ArgbEvaluator(), backgroundColor, i); - valueAnimator.addUpdateListener(animation -> { - if (colorBackground != null) { - colorBackground.setBackgroundColor((Integer) animation.getAnimatedValue()); - } - }); - valueAnimator.setDuration(ViewUtil.RETRO_MUSIC_ANIM_TIME).start(); - } - - @SuppressLint("StaticFieldLeak") - private void updateLyricsLocal() { - if (updateLyricsAsyncTask != null) { - updateLyricsAsyncTask.cancel(false); - } - final Song song = MusicPlayerRemote.getCurrentSong(); - updateLyricsAsyncTask = new AsyncTask() { - @Override - protected void onPreExecute() { - super.onPreExecute(); - lyrics = null; - toolbar.getMenu().removeItem(R.id.action_show_lyrics); - } - - @Override - protected Lyrics doInBackground(Void... params) { - String data = MusicUtil.getLyrics(song); - if (TextUtils.isEmpty(data)) { - return null; - } - return Lyrics.parse(song, data); - } - - @Override - protected void onPostExecute(Lyrics l) { - lyrics = l; - if (lyrics == null) { - lyricsView.setText(R.string.no_lyrics_found); - } else { - lyricsView.setText(lyrics.getText()); - } - } - - @Override - protected void onCancelled(Lyrics s) { - onPostExecute(null); - } - }.execute(); - } - - @OnClick(R.id.expand) - void expand() { - startActivity(new Intent(getContext(), LyricsActivity.class)); - } - - @OnClick({R.id.lyrics, R.id.image}) - void toggleLyrics(View view) { - if (lyricsViewContainer.getVisibility() == View.GONE) { - lyricsViewContainer.setVisibility(View.VISIBLE); - } else { - lyricsViewContainer.setVisibility(View.GONE); - } - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/color/ColorPlaybackControlsFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/color/ColorPlaybackControlsFragment.java deleted file mode 100644 index b98dc4f5..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/color/ColorPlaybackControlsFragment.java +++ /dev/null @@ -1,311 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.player.color; - -import android.animation.ObjectAnimator; -import android.graphics.Color; -import android.graphics.PorterDuff; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.animation.DecelerateInterpolator; -import android.view.animation.LinearInterpolator; -import android.widget.ImageButton; -import android.widget.SeekBar; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.widget.AppCompatSeekBar; -import androidx.appcompat.widget.AppCompatTextView; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.Unbinder; -import code.name.monkey.appthemehelper.util.ColorUtil; -import code.name.monkey.appthemehelper.util.TintHelper; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper; -import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler; -import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.service.MusicService; -import code.name.monkey.retromusic.ui.fragments.VolumeFragment; -import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerControlsFragment; -import code.name.monkey.retromusic.util.MusicUtil; -import code.name.monkey.retromusic.util.PreferenceUtil; - -public class ColorPlaybackControlsFragment extends AbsPlayerControlsFragment { - - @BindView(R.id.player_play_pause_button) - ImageButton playPauseFab; - - @BindView(R.id.player_prev_button) - ImageButton prevButton; - - @BindView(R.id.player_next_button) - ImageButton nextButton; - - @BindView(R.id.player_repeat_button) - ImageButton repeatButton; - - @BindView(R.id.player_shuffle_button) - ImageButton shuffleButton; - - @BindView(R.id.player_progress_slider) - AppCompatSeekBar progressSlider; - - @BindView(R.id.player_song_total_time) - TextView songTotalTime; - - @BindView(R.id.player_song_current_progress) - TextView songCurrentProgress; - - @BindView(R.id.title) - AppCompatTextView title; - - @BindView(R.id.text) - TextView text; - - @BindView(R.id.volume_fragment_container) - View volumeContainer; - - private Unbinder unbinder; - private int lastPlaybackControlsColor; - private int lastDisabledPlaybackControlsColor; - private MusicProgressViewUpdateHelper progressViewUpdateHelper; - private VolumeFragment volumeFragment; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - progressViewUpdateHelper = new MusicProgressViewUpdateHelper(this); - } - - @Override - public View onCreateView(@NonNull LayoutInflater inflater, - @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_color_player_playback_controls, container, false); - unbinder = ButterKnife.bind(this, view); - return view; - } - - @Override - public void onResume() { - super.onResume(); - progressViewUpdateHelper.start(); - } - - @Override - public void onPause() { - super.onPause(); - progressViewUpdateHelper.stop(); - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - unbinder = ButterKnife.bind(this, view); - setUpMusicControllers(); - - if (PreferenceUtil.getInstance().getVolumeToggle()) { - volumeContainer.setVisibility(View.VISIBLE); - } else { - volumeContainer.setVisibility(View.GONE); - } - - volumeFragment = (VolumeFragment) getChildFragmentManager().findFragmentById(R.id.volume_fragment); - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - unbinder.unbind(); - } - - private void updateSong() { - Song song = MusicPlayerRemote.getCurrentSong(); - title.setText(song.title); - text.setText(song.artistName); - } - - @Override - public void onServiceConnected() { - updatePlayPauseDrawableState(); - updateRepeatState(); - updateShuffleState(); - updateSong(); - } - - @Override - public void onPlayingMetaChanged() { - super.onPlayingMetaChanged(); - updateSong(); - } - - @Override - public void onPlayStateChanged() { - updatePlayPauseDrawableState(); - } - - @Override - public void onRepeatModeChanged() { - updateRepeatState(); - } - - @Override - public void onShuffleModeChanged() { - updateShuffleState(); - } - - - public void setDark(int textColor, int background) { - setDark(textColor); - TintHelper.setTintAuto(playPauseFab, background, false); - TintHelper.setTintAuto(playPauseFab, textColor, true); - } - - @Override - public void setDark(int color) { - lastPlaybackControlsColor = color; - lastDisabledPlaybackControlsColor = ColorUtil.withAlpha(color, 0.5f); - - title.setTextColor(lastPlaybackControlsColor); - text.setTextColor(lastDisabledPlaybackControlsColor); - - TintHelper.setTintAuto(progressSlider, lastPlaybackControlsColor, false); - - volumeFragment.setTintable(lastPlaybackControlsColor); - - songCurrentProgress.setTextColor(lastDisabledPlaybackControlsColor); - songTotalTime.setTextColor(lastDisabledPlaybackControlsColor); - - updateRepeatState(); - updateShuffleState(); - updatePrevNextColor(); - } - - - private void setUpPlayPauseFab() { - TintHelper.setTintAuto(playPauseFab, Color.WHITE, true); - TintHelper.setTintAuto(playPauseFab, Color.BLACK, false); - playPauseFab.setOnClickListener(new PlayPauseButtonOnClickHandler()); - } - - - protected void updatePlayPauseDrawableState() { - if (MusicPlayerRemote.isPlaying()) { - playPauseFab.setImageResource(R.drawable.ic_pause_white_24dp); - } else { - playPauseFab.setImageResource(R.drawable.ic_play_arrow_white_24dp); - } - } - - - private void setUpMusicControllers() { - setUpPlayPauseFab(); - setUpPrevNext(); - setUpRepeatButton(); - setUpShuffleButton(); - setUpProgressSlider(); - } - - private void setUpPrevNext() { - updatePrevNextColor(); - nextButton.setOnClickListener(v -> MusicPlayerRemote.playNextSong()); - prevButton.setOnClickListener(v -> MusicPlayerRemote.back()); - } - - private void updatePrevNextColor() { - nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - prevButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - } - - private void setUpShuffleButton() { - shuffleButton.setOnClickListener(v -> MusicPlayerRemote.toggleShuffleMode()); - } - - @Override - protected void updateShuffleState() { - switch (MusicPlayerRemote.getShuffleMode()) { - case MusicService.SHUFFLE_MODE_SHUFFLE: - shuffleButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - default: - shuffleButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - } - } - - private void setUpRepeatButton() { - repeatButton.setOnClickListener(v -> MusicPlayerRemote.cycleRepeatMode()); - } - - @Override - protected void updateRepeatState() { - switch (MusicPlayerRemote.getRepeatMode()) { - case MusicService.REPEAT_MODE_NONE: - repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp); - repeatButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - case MusicService.REPEAT_MODE_ALL: - repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp); - repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - case MusicService.REPEAT_MODE_THIS: - repeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp); - repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - } - } - - - @Override - protected void show() { - playPauseFab.animate() - .scaleX(1f) - .scaleY(1f) - .rotation(360f) - .setInterpolator(new DecelerateInterpolator()) - .start(); - } - - @Override - protected void hide() { - if (playPauseFab != null) { - playPauseFab.setScaleX(0f); - playPauseFab.setScaleY(0f); - playPauseFab.setRotation(0f); - } - } - - @Override - protected void setUpProgressSlider() { - progressSlider.setOnSeekBarChangeListener(new SimpleOnSeekbarChangeListener() { - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - if (fromUser) { - MusicPlayerRemote.seekTo(progress); - onUpdateProgressViews(MusicPlayerRemote.getSongProgressMillis(), MusicPlayerRemote.getSongDurationMillis()); - } - } - }); - } - - @Override - public void onUpdateProgressViews(int progress, int total) { - progressSlider.setMax(total); - - ObjectAnimator animator = ObjectAnimator.ofInt(progressSlider, "progress", progress); - animator.setDuration(1500); - animator.setInterpolator(new LinearInterpolator()); - animator.start(); - - songTotalTime.setText(MusicUtil.getReadableDurationString(total)); - songCurrentProgress.setText(MusicUtil.getReadableDurationString(progress)); - } - - public void hideVolumeIfAvailable() { - volumeContainer.setVisibility(PreferenceUtil.getInstance().getVolumeToggle() ? View.VISIBLE : View.GONE); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/fit/FitFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/fit/FitFragment.java deleted file mode 100644 index 12ec30b8..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/fit/FitFragment.java +++ /dev/null @@ -1,154 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.player.fit; - -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.ColorInt; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.widget.Toolbar; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.Unbinder; -import code.name.monkey.appthemehelper.util.ATHUtil; -import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerFragment; -import code.name.monkey.retromusic.ui.fragments.player.PlayerAlbumCoverFragment; - - -public class FitFragment extends AbsPlayerFragment implements PlayerAlbumCoverFragment.Callbacks { - - @BindView(R.id.player_toolbar) - Toolbar toolbar; - - private int lastColor; - private FitPlaybackControlsFragment playbackControlsFragment; - private Unbinder unbinder; - - public FitFragment() { - } - - public static FitFragment newInstance() { - Bundle args = new Bundle(); - FitFragment fragment = new FitFragment(); - fragment.setArguments(args); - return fragment; - } - - @Override - @ColorInt - public int getPaletteColor() { - return lastColor; - } - - @Override - public void onShow() { - playbackControlsFragment.show(); - } - - @Override - public void onHide() { - playbackControlsFragment.hide(); - onBackPressed(); - } - - @Override - public boolean onBackPressed() { - return false; - } - - @Override - public Toolbar getToolbar() { - return toolbar; - } - - @Override - public int toolbarIconColor() { - return ATHUtil.resolveColor(getContext(), R.attr.iconColor); - } - - @Override - public void onColorChanged(int color) { - playbackControlsFragment.setDark(color); - lastColor = color; - getCallbacks().onPaletteColorChanged(); - - ToolbarContentTintHelper.colorizeToolbar(toolbar, ATHUtil.resolveColor(getContext(), R.attr.iconColor), getActivity()); - - } - - @Override - protected void toggleFavorite(Song song) { - super.toggleFavorite(song); - if (song.id == MusicPlayerRemote.getCurrentSong().id) { - updateIsFavorite(); - } - } - - @Override - public void onFavoriteToggled() { - toggleFavorite(MusicPlayerRemote.getCurrentSong()); - } - - - @Override - public void onDestroyView() { - super.onDestroyView(); - unbinder.unbind(); - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_fit, container, false); - unbinder = ButterKnife.bind(this, view); - if (getPlayerActivity() != null) { - getPlayerActivity().setDrawUnderStatusBar(); - getPlayerActivity().setNavigationbarColorAuto(); - } - return view; - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - setUpSubFragments(); - setUpPlayerToolbar(); - } - - private void setUpSubFragments() { - playbackControlsFragment = (FitPlaybackControlsFragment) getChildFragmentManager().findFragmentById(R.id.playback_controls_fragment); - - PlayerAlbumCoverFragment playerAlbumCoverFragment = (PlayerAlbumCoverFragment) getChildFragmentManager().findFragmentById(R.id.player_album_cover_fragment); - if (playerAlbumCoverFragment != null) { - playerAlbumCoverFragment.setCallbacks(this); - playerAlbumCoverFragment.removeEffect(); - } - } - - private void setUpPlayerToolbar() { - toolbar.inflateMenu(R.menu.menu_player); - toolbar.setNavigationOnClickListener(v -> getActivity().onBackPressed()); - toolbar.setOnMenuItemClickListener(this); - - ToolbarContentTintHelper.colorizeToolbar(toolbar, - ATHUtil.resolveColor(getContext(), R.attr.iconColor), getActivity()); - } - - @Override - public void onServiceConnected() { - updateIsFavorite(); - - } - - @Override - public void onPlayingMetaChanged() { - updateIsFavorite(); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/fit/FitPlaybackControlsFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/fit/FitPlaybackControlsFragment.java deleted file mode 100644 index 271ebe61..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/fit/FitPlaybackControlsFragment.java +++ /dev/null @@ -1,344 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.player.fit; - -import android.animation.ObjectAnimator; -import android.graphics.PorterDuff; -import android.graphics.drawable.ClipDrawable; -import android.graphics.drawable.LayerDrawable; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.animation.AccelerateInterpolator; -import android.view.animation.DecelerateInterpolator; -import android.view.animation.LinearInterpolator; -import android.widget.ImageButton; -import android.widget.SeekBar; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.widget.AppCompatSeekBar; -import androidx.appcompat.widget.AppCompatTextView; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import butterknife.Unbinder; -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.appthemehelper.util.ATHUtil; -import code.name.monkey.appthemehelper.util.ColorUtil; -import code.name.monkey.appthemehelper.util.MaterialValueHelper; -import code.name.monkey.appthemehelper.util.TintHelper; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper; -import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler; -import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.service.MusicService; -import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerControlsFragment; -import code.name.monkey.retromusic.util.MusicUtil; -import code.name.monkey.retromusic.util.PreferenceUtil; - -public class FitPlaybackControlsFragment extends AbsPlayerControlsFragment { - - @BindView(R.id.player_play_pause_button) - ImageButton playPauseFab; - - @BindView(R.id.player_prev_button) - ImageButton prevButton; - - @BindView(R.id.player_next_button) - ImageButton nextButton; - - @BindView(R.id.player_repeat_button) - ImageButton repeatButton; - - @BindView(R.id.player_shuffle_button) - ImageButton shuffleButton; - - @BindView(R.id.player_progress_slider) - AppCompatSeekBar progressSlider; - - @BindView(R.id.player_song_total_time) - TextView songTotalTime; - - @BindView(R.id.player_song_current_progress) - TextView songCurrentProgress; - - @BindView(R.id.title) - AppCompatTextView title; - - @BindView(R.id.text) - TextView text; - - @BindView(R.id.volume_fragment_container) - View mVolumeContainer; - - private Unbinder unbinder; - private int lastPlaybackControlsColor; - private int lastDisabledPlaybackControlsColor; - private MusicProgressViewUpdateHelper progressViewUpdateHelper; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - progressViewUpdateHelper = new MusicProgressViewUpdateHelper(this); - } - - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_fit_playback_controls, container, false); - unbinder = ButterKnife.bind(this, view); - return view; - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - unbinder = ButterKnife.bind(this, view); - setUpMusicControllers(); - - if (PreferenceUtil.getInstance().getVolumeToggle()) { - mVolumeContainer.setVisibility(View.VISIBLE); - } else { - mVolumeContainer.setVisibility(View.GONE); - } - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - unbinder.unbind(); - } - - private void updateSong() { - Song song = MusicPlayerRemote.getCurrentSong(); - title.setText(song.title); - text.setText(song.artistName); - } - - @Override - public void onResume() { - super.onResume(); - progressViewUpdateHelper.start(); - } - - @Override - public void onPause() { - super.onPause(); - progressViewUpdateHelper.stop(); - } - - @Override - public void onServiceConnected() { - updatePlayPauseDrawableState(); - updateRepeatState(); - updateShuffleState(); - updateSong(); - } - - @Override - public void onPlayingMetaChanged() { - super.onPlayingMetaChanged(); - updateSong(); - } - - @Override - public void onPlayStateChanged() { - updatePlayPauseDrawableState(); - } - - @Override - public void onRepeatModeChanged() { - updateRepeatState(); - } - - @Override - public void onShuffleModeChanged() { - updateShuffleState(); - } - - @Override - public void setDark(int dark) { - int color = ATHUtil.resolveColor(getActivity(), android.R.attr.colorBackground); - if (ColorUtil.isColorLight(color)) { - lastPlaybackControlsColor = MaterialValueHelper.getSecondaryTextColor(getActivity(), true); - lastDisabledPlaybackControlsColor = MaterialValueHelper.getSecondaryDisabledTextColor(getActivity(), true); - } else { - lastPlaybackControlsColor = MaterialValueHelper.getPrimaryTextColor(getActivity(), false); - lastDisabledPlaybackControlsColor = MaterialValueHelper.getPrimaryDisabledTextColor(getActivity(), false); - } - - if (PreferenceUtil.getInstance().getAdaptiveColor()) { - setFabColor(dark); - } else { - setFabColor(ThemeStore.accentColor(getContext())); - } - - updateRepeatState(); - updateShuffleState(); - updatePrevNextColor(); - } - - private void setFabColor(int i) { - TintHelper.setTintAuto(playPauseFab, MaterialValueHelper.getPrimaryTextColor(getContext(), ColorUtil.isColorLight(i)), false); - TintHelper.setTintAuto(playPauseFab, i, true); - //setProgressBarColor(i); - } - - private void setProgressBarColor(int newColor) { - LayerDrawable ld = (LayerDrawable) progressSlider.getProgressDrawable(); - ClipDrawable clipDrawable = (ClipDrawable) ld.findDrawableByLayerId(android.R.id.progress); - clipDrawable.setColorFilter(newColor, PorterDuff.Mode.SRC_IN); - } - - private void setUpPlayPauseFab() { - playPauseFab.setOnClickListener(new PlayPauseButtonOnClickHandler()); - } - - protected void updatePlayPauseDrawableState() { - if (MusicPlayerRemote.isPlaying()) { - playPauseFab.setImageResource(R.drawable.ic_pause_white_24dp); - } else { - playPauseFab.setImageResource(R.drawable.ic_play_arrow_white_24dp); - } - } - - private void setUpMusicControllers() { - setUpPlayPauseFab(); - setUpPrevNext(); - setUpRepeatButton(); - setUpShuffleButton(); - setUpProgressSlider(); - } - - private void setUpPrevNext() { - updatePrevNextColor(); - nextButton.setOnClickListener(v -> MusicPlayerRemote.playNextSong()); - prevButton.setOnClickListener(v -> MusicPlayerRemote.back()); - } - - private void updatePrevNextColor() { - nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - prevButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - } - - private void setUpShuffleButton() { - shuffleButton.setOnClickListener(v -> MusicPlayerRemote.toggleShuffleMode()); - } - - @Override - protected void updateShuffleState() { - switch (MusicPlayerRemote.getShuffleMode()) { - case MusicService.SHUFFLE_MODE_SHUFFLE: - shuffleButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - default: - shuffleButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - } - } - - private void setUpRepeatButton() { - repeatButton.setOnClickListener(v -> MusicPlayerRemote.cycleRepeatMode()); - } - - @Override - protected void updateRepeatState() { - switch (MusicPlayerRemote.getRepeatMode()) { - case MusicService.REPEAT_MODE_NONE: - repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp); - repeatButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - case MusicService.REPEAT_MODE_ALL: - repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp); - repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - case MusicService.REPEAT_MODE_THIS: - repeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp); - repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - } - } - - @Override - protected void show() { - playPauseFab.animate() - .scaleX(1f) - .scaleY(1f) - .rotation(360f) - .setInterpolator(new DecelerateInterpolator()) - .start(); - } - - @Override - protected void hide() { - if (playPauseFab != null) { - playPauseFab.setScaleX(0f); - playPauseFab.setScaleY(0f); - playPauseFab.setRotation(0f); - } - } - - @Override - protected void setUpProgressSlider() { - progressSlider.setOnSeekBarChangeListener(new SimpleOnSeekbarChangeListener() { - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - if (fromUser) { - MusicPlayerRemote.seekTo(progress); - onUpdateProgressViews(MusicPlayerRemote.getSongProgressMillis(), - MusicPlayerRemote.getSongDurationMillis()); - } - } - }); - } - - public void showBouceAnimation() { - playPauseFab.clearAnimation(); - - playPauseFab.setScaleX(0.9f); - playPauseFab.setScaleY(0.9f); - playPauseFab.setVisibility(View.VISIBLE); - playPauseFab.setPivotX(playPauseFab.getWidth() / 2); - playPauseFab.setPivotY(playPauseFab.getHeight() / 2); - - playPauseFab.animate() - .setDuration(200) - .setInterpolator(new DecelerateInterpolator()) - .scaleX(1.1f) - .scaleY(1.1f) - .withEndAction(() -> playPauseFab.animate() - .setDuration(200) - .setInterpolator(new AccelerateInterpolator()) - .scaleX(1f) - .scaleY(1f) - .alpha(1f) - .start()) - .start(); - } - - @OnClick(R.id.player_play_pause_button) - void showAnimation() { - if (MusicPlayerRemote.isPlaying()) { - MusicPlayerRemote.pauseSong(); - } else { - MusicPlayerRemote.resumePlaying(); - } - showBouceAnimation(); - } - - @Override - public void onUpdateProgressViews(int progress, int total) { - progressSlider.setMax(total); - - ObjectAnimator animator = ObjectAnimator.ofInt(progressSlider, "progress", progress); - animator.setDuration(1500); - animator.setInterpolator(new LinearInterpolator()); - animator.start(); - - songTotalTime.setText(MusicUtil.getReadableDurationString(total)); - songCurrentProgress.setText(MusicUtil.getReadableDurationString(progress)); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/flat/FlatPlaybackControlsFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/flat/FlatPlaybackControlsFragment.java deleted file mode 100644 index 2f766f3f..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/flat/FlatPlaybackControlsFragment.java +++ /dev/null @@ -1,297 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.player.flat; - -import android.animation.ObjectAnimator; -import android.graphics.PorterDuff; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.animation.DecelerateInterpolator; -import android.view.animation.LinearInterpolator; -import android.widget.ImageButton; -import android.widget.ImageView; -import android.widget.SeekBar; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.Unbinder; -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.appthemehelper.util.ATHUtil; -import code.name.monkey.appthemehelper.util.ColorUtil; -import code.name.monkey.appthemehelper.util.MaterialValueHelper; -import code.name.monkey.appthemehelper.util.TintHelper; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper; -import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper.Callback; -import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler; -import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.service.MusicService; -import code.name.monkey.retromusic.ui.fragments.base.AbsMusicServiceFragment; -import code.name.monkey.retromusic.util.MusicUtil; -import code.name.monkey.retromusic.util.PreferenceUtil; -import code.name.monkey.retromusic.views.PlayPauseDrawable; - -public class FlatPlaybackControlsFragment extends AbsMusicServiceFragment implements Callback { - - @BindView(R.id.text) - TextView mText; - - @BindView(R.id.title) - TextView mTitle; - - @BindView(R.id.playback_controls) - ViewGroup viewGroup; - - @BindView(R.id.player_song_total_time) - TextView mSongTotalTime; - - @BindView(R.id.player_song_current_progress) - TextView mPlayerSongCurrentProgress; - - @BindView(R.id.player_repeat_button) - ImageButton mPlayerRepeatButton; - - @BindView(R.id.player_shuffle_button) - ImageButton mPlayerShuffleButton; - - @BindView(R.id.player_play_pause_button) - ImageView playPauseFab; - - @BindView(R.id.player_progress_slider) - SeekBar progressSlider; - - @BindView(R.id.volume_fragment_container) - View mVolumeContainer; - - Unbinder unbinder; - private int lastPlaybackControlsColor; - private int lastDisabledPlaybackControlsColor; - private MusicProgressViewUpdateHelper progressViewUpdateHelper; - private PlayPauseDrawable playerFabPlayPauseDrawable; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - progressViewUpdateHelper = new MusicProgressViewUpdateHelper(this); - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_flat_player_playback_controls, container, false); - unbinder = ButterKnife.bind(this, view); - return view; - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - setUpMusicControllers(); - - mVolumeContainer.setVisibility(PreferenceUtil.getInstance().getVolumeToggle() ? View.VISIBLE : View.GONE); - } - - @Override - public void onResume() { - super.onResume(); - progressViewUpdateHelper.start(); - } - - @Override - public void onPause() { - super.onPause(); - progressViewUpdateHelper.stop(); - } - - @Override - public void onUpdateProgressViews(int progress, int total) { - progressSlider.setMax(total); - - ObjectAnimator animator = ObjectAnimator.ofInt(progressSlider, "progress", progress); - animator.setDuration(1500); - animator.setInterpolator(new LinearInterpolator()); - animator.start(); - - mPlayerSongCurrentProgress.setText(MusicUtil.getReadableDurationString(progress)); - mSongTotalTime.setText(MusicUtil.getReadableDurationString(total)); - } - - - public void show() { - playPauseFab.animate() - .scaleX(1f) - .scaleY(1f) - .setInterpolator(new DecelerateInterpolator()) - .start(); - } - - - public void hide() { - if (playPauseFab != null) { - playPauseFab.setScaleX(0f); - playPauseFab.setScaleY(0f); - playPauseFab.setRotation(0f); - } - } - - public void setDark(int dark) { - int color = ATHUtil.resolveColor(getActivity(), android.R.attr.colorBackground); - boolean isDark = ColorUtil.isColorLight(color); - if (isDark) { - lastPlaybackControlsColor = MaterialValueHelper.getSecondaryTextColor(getActivity(), true); - lastDisabledPlaybackControlsColor = MaterialValueHelper.getSecondaryDisabledTextColor(getActivity(), true); - } else { - lastPlaybackControlsColor = MaterialValueHelper.getPrimaryTextColor(getActivity(), false); - lastDisabledPlaybackControlsColor = MaterialValueHelper.getPrimaryDisabledTextColor(getActivity(), false); - } - int accentColor = ThemeStore.accentColor(getContext()); - boolean b = PreferenceUtil.getInstance().getAdaptiveColor(); - updateTextColors(b ? dark : accentColor); - setProgressBarColor(b ? dark : accentColor); - - - updateRepeatState(); - updateShuffleState(); - } - - private void setProgressBarColor(int dark) { - TintHelper.setTintAuto(progressSlider, dark, false); - //LayerDrawable ld = (LayerDrawable) progressSlider.getProgressDrawable(); - //ClipDrawable clipDrawable = (ClipDrawable) ld.findDrawableByLayerId(android.R.id.progress); - //clipDrawable.setColorFilter(dark, PorterDuff.Mode.SRC_IN); - } - - private void updateTextColors(int color) { - boolean isDark = ColorUtil.isColorLight(color); - int darkColor = ColorUtil.darkenColor(color); - int colorPrimary = MaterialValueHelper.getPrimaryTextColor(getContext(), isDark); - int colorSecondary = MaterialValueHelper.getSecondaryTextColor(getContext(), ColorUtil.isColorLight(darkColor)); - - TintHelper.setTintAuto(playPauseFab, colorPrimary, false); - TintHelper.setTintAuto(playPauseFab, color, true); - - mTitle.setBackgroundColor(color); - mTitle.setTextColor(colorPrimary); - mText.setBackgroundColor(darkColor); - mText.setTextColor(colorSecondary); - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - unbinder.unbind(); - } - - @Override - public void onServiceConnected() { - updatePlayPauseDrawableState(); - updateRepeatState(); - updateShuffleState(); - updateSong(); - } - - @Override - public void onPlayingMetaChanged() { - super.onPlayingMetaChanged(); - updateSong(); - } - - @Override - public void onPlayStateChanged() { - updatePlayPauseDrawableState(); - } - - private void setUpPlayPauseFab() { - playPauseFab.setOnClickListener(new PlayPauseButtonOnClickHandler()); - } - - protected void updatePlayPauseDrawableState() { - if (MusicPlayerRemote.isPlaying()) { - playPauseFab.setImageResource(R.drawable.ic_pause_white_24dp); - } else { - playPauseFab.setImageResource(R.drawable.ic_play_arrow_white_24dp); - } - } - - private void setUpMusicControllers() { - setUpPlayPauseFab(); - setUpRepeatButton(); - setUpShuffleButton(); - setUpProgressSlider(); - } - - private void updateSong() { - //TransitionManager.beginDelayedTransition(viewGroup, new ChangeText().setChangeBehavior(ChangeText.CHANGE_BEHAVIOR_OUT_IN)); - Song song = MusicPlayerRemote.getCurrentSong(); - mTitle.setText(song.title); - mText.setText(song.artistName); - - } - - private void setUpProgressSlider() { - progressSlider.setOnSeekBarChangeListener(new SimpleOnSeekbarChangeListener() { - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - if (fromUser) { - MusicPlayerRemote.seekTo(progress); - onUpdateProgressViews(MusicPlayerRemote.getSongProgressMillis(), - MusicPlayerRemote.getSongDurationMillis()); - } - } - }); - } - - @Override - public void onRepeatModeChanged() { - updateRepeatState(); - } - - @Override - public void onShuffleModeChanged() { - updateShuffleState(); - } - - private void setUpRepeatButton() { - mPlayerRepeatButton.setOnClickListener(v -> MusicPlayerRemote.cycleRepeatMode()); - } - - private void updateRepeatState() { - switch (MusicPlayerRemote.getRepeatMode()) { - case MusicService.REPEAT_MODE_NONE: - mPlayerRepeatButton.setImageResource(R.drawable.ic_repeat_white_24dp); - mPlayerRepeatButton - .setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - case MusicService.REPEAT_MODE_ALL: - mPlayerRepeatButton.setImageResource(R.drawable.ic_repeat_white_24dp); - mPlayerRepeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - case MusicService.REPEAT_MODE_THIS: - mPlayerRepeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp); - mPlayerRepeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - } - } - - private void setUpShuffleButton() { - mPlayerShuffleButton.setOnClickListener(v -> MusicPlayerRemote.toggleShuffleMode()); - } - - private void updateShuffleState() { - switch (MusicPlayerRemote.getShuffleMode()) { - case MusicService.SHUFFLE_MODE_SHUFFLE: - mPlayerShuffleButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - default: - mPlayerShuffleButton - .setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - } - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/flat/FlatPlayerFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/flat/FlatPlayerFragment.java deleted file mode 100644 index 5d302840..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/flat/FlatPlayerFragment.java +++ /dev/null @@ -1,177 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.player.flat; - -import android.animation.ArgbEvaluator; -import android.animation.ValueAnimator; -import android.graphics.drawable.GradientDrawable; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.FrameLayout; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.widget.Toolbar; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.Unbinder; -import code.name.monkey.appthemehelper.util.ATHUtil; -import code.name.monkey.appthemehelper.util.ColorUtil; -import code.name.monkey.appthemehelper.util.MaterialValueHelper; -import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerFragment; -import code.name.monkey.retromusic.ui.fragments.player.PlayerAlbumCoverFragment; -import code.name.monkey.retromusic.util.PreferenceUtil; -import code.name.monkey.retromusic.util.ViewUtil; -import code.name.monkey.retromusic.views.DrawableGradient; - -public class FlatPlayerFragment extends AbsPlayerFragment implements - PlayerAlbumCoverFragment.Callbacks { - - @BindView(R.id.player_toolbar) - Toolbar toolbar; - - @BindView(R.id.gradient_background) - View colorBackground; - - @BindView(R.id.toolbar_container) - FrameLayout toolbarContainer; - - - private Unbinder unbinder; - private ValueAnimator valueAnimator; - private FlatPlaybackControlsFragment flatPlaybackControlsFragment; - private int lastColor; - - private void setUpSubFragments() { - flatPlaybackControlsFragment = (FlatPlaybackControlsFragment) - getChildFragmentManager().findFragmentById(R.id.playback_controls_fragment); - - PlayerAlbumCoverFragment playerAlbumCoverFragment = (PlayerAlbumCoverFragment) - getChildFragmentManager().findFragmentById(R.id.player_album_cover_fragment); - if (playerAlbumCoverFragment != null) { - playerAlbumCoverFragment.setCallbacks(this); - } - } - - private void setUpPlayerToolbar() { - toolbar.inflateMenu(R.menu.menu_player); - toolbar.setNavigationOnClickListener(v -> getActivity().onBackPressed()); - toolbar.setOnMenuItemClickListener(this); - - ToolbarContentTintHelper.colorizeToolbar(toolbar, ATHUtil.resolveColor(getContext(), - R.attr.iconColor), getActivity()); - } - - private void colorize(int i) { - if (valueAnimator != null) { - valueAnimator.cancel(); - } - - valueAnimator = ValueAnimator.ofObject(new ArgbEvaluator(), android.R.color.transparent, i); - valueAnimator.addUpdateListener(animation -> { - GradientDrawable drawable = new DrawableGradient(GradientDrawable.Orientation.TOP_BOTTOM, - new int[]{(int) animation.getAnimatedValue(), android.R.color.transparent}, 0); - if (colorBackground != null) { - colorBackground.setBackground(drawable); - } - }); - valueAnimator.setDuration(ViewUtil.RETRO_MUSIC_ANIM_TIME).start(); - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_flat_player, container, false); - unbinder = ButterKnife.bind(this, view); - - return view; - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - setUpPlayerToolbar(); - setUpSubFragments(); - - } - - @Override - public int getPaletteColor() { - return lastColor; - } - - @Override - public void onShow() { - flatPlaybackControlsFragment.show(); - } - - @Override - public void onHide() { - flatPlaybackControlsFragment.hide(); - onBackPressed(); - } - - @Override - public boolean onBackPressed() { - return false; - } - - @Override - public Toolbar getToolbar() { - return toolbar; - } - - @Override - public int toolbarIconColor() { - boolean isLight = ColorUtil.isColorLight(lastColor); - return PreferenceUtil.getInstance().getAdaptiveColor() ? - MaterialValueHelper.getPrimaryTextColor(getContext(), isLight) : - ATHUtil.resolveColor(getContext(), R.attr.iconColor); - } - - @Override - public void onColorChanged(int color) { - lastColor = color; - flatPlaybackControlsFragment.setDark(color); - getCallbacks().onPaletteColorChanged(); - - boolean isLight = ColorUtil.isColorLight(color); - - //TransitionManager.beginDelayedTransition(mToolbar); - int iconColor = PreferenceUtil.getInstance().getAdaptiveColor() ? - MaterialValueHelper.getPrimaryTextColor(getContext(), isLight) : - ATHUtil.resolveColor(getContext(), R.attr.iconColor); - ToolbarContentTintHelper.colorizeToolbar(toolbar, iconColor, getActivity()); - if (PreferenceUtil.getInstance().getAdaptiveColor()) { - colorize(color); - } - } - - - @Override - public void onFavoriteToggled() { - toggleFavorite(MusicPlayerRemote.getCurrentSong()); - } - - - @Override - protected void toggleFavorite(Song song) { - super.toggleFavorite(song); - if (song.id == MusicPlayerRemote.getCurrentSong().id) { - updateIsFavorite(); - } - } - - - @Override - public void onDestroyView() { - super.onDestroyView(); - unbinder.unbind(); - } - -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/full/FullPlaybackControlsFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/full/FullPlaybackControlsFragment.java deleted file mode 100644 index b7a527f2..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/full/FullPlaybackControlsFragment.java +++ /dev/null @@ -1,313 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.player.full; - -import android.animation.ObjectAnimator; -import android.graphics.Color; -import android.graphics.PorterDuff; -import android.graphics.drawable.ClipDrawable; -import android.graphics.drawable.LayerDrawable; -import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.content.ContextCompat; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.animation.DecelerateInterpolator; -import android.view.animation.LinearInterpolator; -import android.widget.ImageButton; -import android.widget.SeekBar; -import android.widget.TextView; - -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.Unbinder; -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.appthemehelper.util.MaterialValueHelper; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper; -import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler; -import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.service.MusicService; -import code.name.monkey.retromusic.ui.fragments.VolumeFragment; -import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerControlsFragment; -import code.name.monkey.retromusic.util.MusicUtil; -import code.name.monkey.retromusic.util.PreferenceUtil; - -/** - * Created by hemanths on 20/09/17. - */ - -public class FullPlaybackControlsFragment extends AbsPlayerControlsFragment { - @BindView(R.id.player_song_current_progress) - TextView mPlayerSongCurrentProgress; - - @BindView(R.id.player_song_total_time) - TextView songTotalTime; - - @BindView(R.id.player_progress_slider) - SeekBar progressSlider; - @BindView(R.id.player_prev_button) - - ImageButton playerPrevButton; - @BindView(R.id.player_next_button) - - ImageButton playerNextButton; - @BindView(R.id.player_repeat_button) - - ImageButton playerRepeatButton; - @BindView(R.id.player_shuffle_button) - - ImageButton playerShuffleButton; - @BindView(R.id.player_play_pause_button) - - ImageButton playerPlayPauseFab; - - - @BindView(R.id.title) - TextView mTitle; - - @BindView(R.id.text) - TextView mText; - - @BindView(R.id.volume_fragment_container) - View mVolumeContainer; - - Unbinder unbinder; - private int lastPlaybackControlsColor; - private int lastDisabledPlaybackControlsColor; - private MusicProgressViewUpdateHelper progressViewUpdateHelper; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - progressViewUpdateHelper = new MusicProgressViewUpdateHelper(this); - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, - @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_full_player_controls, container, false); - unbinder = ButterKnife.bind(this, view); - return view; - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - unbinder.unbind(); - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - - setUpMusicControllers(); - - mVolumeContainer.setVisibility(PreferenceUtil.getInstance().getVolumeToggle() ? View.VISIBLE : View.GONE); - VolumeFragment volumeFragment = (VolumeFragment) getChildFragmentManager().findFragmentById(R.id.volume_fragment); - volumeFragment.tintWhiteColor(); - - } - - @Override - public void onResume() { - super.onResume(); - progressViewUpdateHelper.start(); - } - - @Override - public void onPause() { - super.onPause(); - progressViewUpdateHelper.stop(); - } - - @Override - public void onUpdateProgressViews(int progress, int total) { - progressSlider.setMax(total); - - ObjectAnimator animator = ObjectAnimator.ofInt(progressSlider, "progress", progress); - animator.setDuration(1500); - animator.setInterpolator(new LinearInterpolator()); - animator.start(); - - mPlayerSongCurrentProgress.setText(MusicUtil.getReadableDurationString(progress)); - songTotalTime.setText(MusicUtil.getReadableDurationString(total)); - } - - - public void show() { - playerPlayPauseFab.animate() - .scaleX(1f) - .scaleY(1f) - .setInterpolator(new DecelerateInterpolator()) - .start(); - } - - - public void hide() { - if (playerPlayPauseFab != null) { - playerPlayPauseFab.setScaleX(0f); - playerPlayPauseFab.setScaleY(0f); - playerPlayPauseFab.setRotation(0f); - } - } - - public void setDark(int dark) { - lastPlaybackControlsColor = Color.WHITE; - lastDisabledPlaybackControlsColor = ContextCompat.getColor(getContext(), R.color.md_grey_500); - - if (PreferenceUtil.getInstance().getAdaptiveColor()) { - setProgressBarColor(dark); - } else { - int accentColor = ThemeStore.accentColor(getContext()); - setProgressBarColor(accentColor); - } - - updateRepeatState(); - updateShuffleState(); - updatePrevNextColor(); - updateProgressTextColor(); - } - - private void setProgressBarColor(int dark) { - LayerDrawable ld = (LayerDrawable) progressSlider.getProgressDrawable(); - ClipDrawable clipDrawable = (ClipDrawable) ld.findDrawableByLayerId(android.R.id.progress); - clipDrawable.setColorFilter(dark, PorterDuff.Mode.SRC_IN); - } - - @Override - public void onServiceConnected() { - updatePlayPauseDrawableState(false); - updateRepeatState(); - updateShuffleState(); - updateSong(); - } - - private void updateSong() { - Song song = MusicPlayerRemote.getCurrentSong(); - mTitle.setText(song.title); - mText.setText(song.artistName); - } - - @Override - public void onPlayingMetaChanged() { - super.onPlayingMetaChanged(); - updateSong(); - } - - @Override - public void onPlayStateChanged() { - updatePlayPauseDrawableState(true); - } - - protected void updatePlayPauseDrawableState(boolean animate) { - - if (MusicPlayerRemote.isPlaying()) { - playerPlayPauseFab.setImageResource(R.drawable.ic_pause_white_24dp); - } else { - playerPlayPauseFab.setImageResource(R.drawable.ic_play_arrow_white_24dp); - } - } - - private void setUpPlayPauseFab() { - - playerPlayPauseFab.setOnClickListener(new PlayPauseButtonOnClickHandler()); - playerPlayPauseFab.post(() -> { - if (playerPlayPauseFab != null) { - playerPlayPauseFab.setPivotX(playerPlayPauseFab.getWidth() / 2); - playerPlayPauseFab.setPivotY(playerPlayPauseFab.getHeight() / 2); - } - }); - } - - private void setUpMusicControllers() { - setUpPlayPauseFab(); - setUpPrevNext(); - setUpRepeatButton(); - setUpShuffleButton(); - setUpProgressSlider(); - } - - private void setUpPrevNext() { - updatePrevNextColor(); - playerNextButton.setOnClickListener(v -> MusicPlayerRemote.playNextSong()); - playerPrevButton.setOnClickListener(v -> MusicPlayerRemote.back()); - } - - private void updateProgressTextColor() { - int color = MaterialValueHelper.getSecondaryTextColor(getContext(), false); - //songTotalTime.setTextColor(color); - //songCurrentProgress.setTextColor(color); - } - - private void updatePrevNextColor() { - playerNextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - playerPrevButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - } - - @Override - protected void setUpProgressSlider() { - progressSlider.setOnSeekBarChangeListener(new SimpleOnSeekbarChangeListener() { - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - if (fromUser) { - MusicPlayerRemote.seekTo(progress); - onUpdateProgressViews(MusicPlayerRemote.getSongProgressMillis(), MusicPlayerRemote.getSongDurationMillis()); - } - } - }); - } - - - @Override - public void onRepeatModeChanged() { - updateRepeatState(); - } - - @Override - public void onShuffleModeChanged() { - updateShuffleState(); - } - - private void setUpRepeatButton() { - playerRepeatButton.setOnClickListener(v -> MusicPlayerRemote.cycleRepeatMode()); - } - - @Override - protected void updateRepeatState() { - switch (MusicPlayerRemote.getRepeatMode()) { - case MusicService.REPEAT_MODE_NONE: - playerRepeatButton.setImageResource(R.drawable.ic_repeat_white_24dp); - playerRepeatButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - case MusicService.REPEAT_MODE_ALL: - playerRepeatButton.setImageResource(R.drawable.ic_repeat_white_24dp); - playerRepeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - case MusicService.REPEAT_MODE_THIS: - playerRepeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp); - playerRepeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - } - } - - private void setUpShuffleButton() { - playerShuffleButton.setOnClickListener(v -> MusicPlayerRemote.toggleShuffleMode()); - } - - @Override - public void updateShuffleState() { - switch (MusicPlayerRemote.getShuffleMode()) { - case MusicService.SHUFFLE_MODE_SHUFFLE: - playerShuffleButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - default: - playerShuffleButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - } - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/full/FullPlayerFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/full/FullPlayerFragment.java deleted file mode 100644 index 5c3daa35..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/full/FullPlayerFragment.java +++ /dev/null @@ -1,124 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.player.full; - -import android.graphics.Color; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.ColorInt; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.widget.Toolbar; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.Unbinder; -import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerFragment; -import code.name.monkey.retromusic.ui.fragments.player.PlayerAlbumCoverFragment; - -public class FullPlayerFragment extends AbsPlayerFragment implements PlayerAlbumCoverFragment.Callbacks { - @BindView(R.id.player_toolbar) - Toolbar toolbar; - - private Unbinder unbinder; - - private int lastColor; - private FullPlaybackControlsFragment fullPlaybackControlsFragment; - - private void setUpPlayerToolbar() { - toolbar.inflateMenu(R.menu.menu_player); - toolbar.setNavigationIcon(R.drawable.ic_close_white_24dp); - toolbar.setNavigationOnClickListener(v -> getActivity().onBackPressed()); - toolbar.setOnMenuItemClickListener(this); - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_full, container, false); - unbinder = ButterKnife.bind(this, view); - return view; - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - setUpSubFragments(); - setUpPlayerToolbar(); - } - - private void setUpSubFragments() { - fullPlaybackControlsFragment = (FullPlaybackControlsFragment) - getChildFragmentManager().findFragmentById(R.id.playback_controls_fragment); - - PlayerAlbumCoverFragment playerAlbumCoverFragment = (PlayerAlbumCoverFragment) - getChildFragmentManager().findFragmentById(R.id.player_album_cover_fragment); - if (playerAlbumCoverFragment != null) { - playerAlbumCoverFragment.setCallbacks(this); - playerAlbumCoverFragment.removeSlideEffect(); - } - } - - @Override - @ColorInt - public int getPaletteColor() { - return lastColor; - } - - @Override - public void onShow() { - - } - - @Override - public void onHide() { - - } - - @Override - public boolean onBackPressed() { - return false; - } - - @Override - public Toolbar getToolbar() { - return toolbar; - } - - @Override - public int toolbarIconColor() { - return Color.WHITE; - } - - @Override - public void onColorChanged(int color) { - lastColor = color; - fullPlaybackControlsFragment.setDark(color); - getCallbacks().onPaletteColorChanged(); - ToolbarContentTintHelper.colorizeToolbar(toolbar, Color.WHITE, getActivity()); - } - - @Override - public void onFavoriteToggled() { - toggleFavorite(MusicPlayerRemote.getCurrentSong()); - } - - @Override - protected void toggleFavorite(Song song) { - super.toggleFavorite(song); - if (song.id == MusicPlayerRemote.getCurrentSong().id) { - updateIsFavorite(); - } - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - unbinder.unbind(); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/hmm/HmmPlaybackControlsFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/hmm/HmmPlaybackControlsFragment.java deleted file mode 100644 index 17884afc..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/hmm/HmmPlaybackControlsFragment.java +++ /dev/null @@ -1,142 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.player.hmm; - -import android.graphics.PorterDuff; -import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageButton; - -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.Unbinder; -import code.name.monkey.appthemehelper.util.ColorUtil; -import code.name.monkey.appthemehelper.util.MaterialValueHelper; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.service.MusicService; -import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerControlsFragment; - -/** - * @author Hemanth S (h4h13). - */ - -public class HmmPlaybackControlsFragment extends AbsPlayerControlsFragment { - @BindView(R.id.player_repeat_button) - ImageButton repeatButton; - @BindView(R.id.player_shuffle_button) - ImageButton shuffleButton; - private Unbinder unbinder; - private int mLastPlaybackControlsColor; - private int mLastDisabledPlaybackControlsColor; - - @Override - public void onDestroyView() { - super.onDestroyView(); - unbinder.unbind(); - } - - @Override - public void onRepeatModeChanged() { - updateRepeatState(); - } - - @Override - public void onShuffleModeChanged() { - updateShuffleState(); - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_hmm_controls_fragment, container, false); - unbinder = ButterKnife.bind(this, view); - return view; - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - setUpMusicControllers(); - } - - private void setUpMusicControllers() { - setUpRepeatButton(); - setUpShuffleButton(); - setUpProgressSlider(); - } - - @Override - protected void show() { - - } - - @Override - protected void hide() { - - } - - private void setUpShuffleButton() { - shuffleButton.setOnClickListener(v -> MusicPlayerRemote.toggleShuffleMode()); - } - - @Override - protected void updateShuffleState() { - switch (MusicPlayerRemote.getShuffleMode()) { - case MusicService.SHUFFLE_MODE_SHUFFLE: - shuffleButton.setColorFilter(mLastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - default: - shuffleButton.setColorFilter(mLastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - } - } - - private void setUpRepeatButton() { - repeatButton.setOnClickListener(v -> MusicPlayerRemote.cycleRepeatMode()); - } - - @Override - protected void updateRepeatState() { - switch (MusicPlayerRemote.getRepeatMode()) { - case MusicService.REPEAT_MODE_NONE: - repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp); - repeatButton.setColorFilter(mLastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - case MusicService.REPEAT_MODE_ALL: - repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp); - repeatButton.setColorFilter(mLastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - case MusicService.REPEAT_MODE_THIS: - repeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp); - repeatButton.setColorFilter(mLastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - } - } - - @Override - protected void setUpProgressSlider() { - - } - - @Override - public void setDark(int dark) { - if (ColorUtil.isColorLight(dark)) { - mLastPlaybackControlsColor = MaterialValueHelper.getSecondaryTextColor(getActivity(), true); - mLastDisabledPlaybackControlsColor = MaterialValueHelper.getSecondaryDisabledTextColor(getActivity(), true); - } else { - mLastPlaybackControlsColor = MaterialValueHelper.getPrimaryTextColor(getActivity(), false); - mLastDisabledPlaybackControlsColor = MaterialValueHelper.getPrimaryDisabledTextColor(getActivity(), false); - } - - updateRepeatState(); - updateShuffleState(); - } - - @Override - public void onUpdateProgressViews(int progress, int total) { - - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/hmm/HmmPlayerFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/hmm/HmmPlayerFragment.java deleted file mode 100644 index 7197914e..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/hmm/HmmPlayerFragment.java +++ /dev/null @@ -1,222 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.player.hmm; - -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; -import android.graphics.Color; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.animation.LinearInterpolator; -import android.widget.ProgressBar; -import android.widget.TextView; - -import com.afollestad.materialdialogs.internal.MDTintHelper; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.widget.Toolbar; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.Unbinder; -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.appthemehelper.util.ColorUtil; -import code.name.monkey.appthemehelper.util.MaterialValueHelper; -import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper; -import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.ui.fragments.MiniPlayerFragment; -import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerFragment; -import code.name.monkey.retromusic.ui.fragments.player.PlayerAlbumCoverFragment; -import code.name.monkey.retromusic.util.MusicUtil; -import code.name.monkey.retromusic.util.PreferenceUtil; - -/** - * @author Hemanth S (h4h13). - */ - -public class HmmPlayerFragment extends AbsPlayerFragment implements - MusicProgressViewUpdateHelper.Callback, PlayerAlbumCoverFragment.Callbacks { - - @BindView(R.id.title) - TextView title; - - @BindView(R.id.text) - TextView text; - - @BindView(R.id.player_song_total_time) - TextView totalTime; - - @BindView(R.id.progress_bar) - ProgressBar progressBar; - - @BindView(R.id.player_toolbar) - Toolbar toolbar; - - private MusicProgressViewUpdateHelper progressViewUpdateHelper; - private Unbinder unBinder; - private int lastColor; - private HmmPlaybackControlsFragment hmmPlaybackControlsFragment; - - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - progressViewUpdateHelper = new MusicProgressViewUpdateHelper(this); - } - - @Override - public void onResume() { - super.onResume(); - progressViewUpdateHelper.start(); - } - - @Override - public void onPause() { - super.onPause(); - progressViewUpdateHelper.stop(); - } - - private void updateSong() { - Song song = MusicPlayerRemote.getCurrentSong(); - title.setText(song.title); - text.setText(String.format("%s \nby -%s", song.albumName, song.artistName)); - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - unBinder.unbind(); - - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_hmm_player, container, false); - unBinder = ButterKnife.bind(this, view); - - return view; - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - progressBar.setOnClickListener(new PlayPauseButtonOnClickHandler()); - progressBar.setOnTouchListener(new MiniPlayerFragment.FlingPlayBackController(getActivity())); - - setUpPlayerToolbar(); - setUpSubFragments(); - } - - private void setUpSubFragments() { - hmmPlaybackControlsFragment = (HmmPlaybackControlsFragment) getChildFragmentManager() - .findFragmentById(R.id.playback_controls_fragment); - PlayerAlbumCoverFragment playerAlbumCoverFragment = (PlayerAlbumCoverFragment) getChildFragmentManager() - .findFragmentById(R.id.player_album_cover_fragment); - playerAlbumCoverFragment.setCallbacks(this); - - } - - @Override - public int getPaletteColor() { - return lastColor; - } - - @Override - public void onShow() { - hmmPlaybackControlsFragment.show(); - } - - @Override - public void onHide() { - hmmPlaybackControlsFragment.hide(); - } - - @Override - public boolean onBackPressed() { - return false; - } - - @Override - public Toolbar getToolbar() { - return toolbar; - } - - @Override - public int toolbarIconColor() { - return MaterialValueHelper - .getSecondaryTextColor(getContext(), ColorUtil.isColorLight(lastColor)); - } - - @Override - public void onUpdateProgressViews(int progress, int total) { - progressBar.setMax(total); - - ObjectAnimator animator = ObjectAnimator.ofInt(progressBar, "progress", progress); - - AnimatorSet animatorSet = new AnimatorSet(); - animatorSet.playSequentially(animator); - - animatorSet.setDuration(1500); - animatorSet.setInterpolator(new LinearInterpolator()); - animatorSet.start(); - - totalTime.setText(String.format("%s/%s", MusicUtil.getReadableDurationString(total), - MusicUtil.getReadableDurationString(progress))); - } - - private void setUpPlayerToolbar() { - toolbar.inflateMenu(R.menu.menu_player); - toolbar.setNavigationOnClickListener(v -> getActivity().onBackPressed()); - toolbar.setOnMenuItemClickListener(this); - } - - - @Override - protected void toggleFavorite(Song song) { - super.toggleFavorite(song); - if (song.id == MusicPlayerRemote.getCurrentSong().id) { - updateIsFavorite(); - } - } - - @Override - public void onServiceConnected() { - super.onServiceConnected(); - updateSong(); - } - - @Override - public void onPlayingMetaChanged() { - super.onPlayingMetaChanged(); - updateSong(); - } - - @Override - public void onColorChanged(int color) { - lastColor = PreferenceUtil.getInstance().getAdaptiveColor() ? color : - ThemeStore.accentColor(getContext()); - getCallbacks().onPaletteColorChanged(); - hmmPlaybackControlsFragment.setDark(lastColor); - setProgressBarColor(lastColor); - - int iconColor = MaterialValueHelper - .getSecondaryTextColor(getContext(), ColorUtil.isColorLight(lastColor)); - ToolbarContentTintHelper.colorizeToolbar(toolbar, iconColor, getActivity()); - } - - private void setProgressBarColor(int color) { - MDTintHelper.setTint(progressBar, color); - } - - @Override - public void onFavoriteToggled() { - toggleFavorite(MusicPlayerRemote.getCurrentSong()); - } - -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/lockscreen/LockScreenPlayerControlsFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/lockscreen/LockScreenPlayerControlsFragment.java deleted file mode 100644 index b66d2e24..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/lockscreen/LockScreenPlayerControlsFragment.java +++ /dev/null @@ -1,306 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.player.lockscreen; - -import android.animation.ObjectAnimator; -import android.graphics.PorterDuff; -import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.widget.AppCompatImageButton; -import androidx.appcompat.widget.AppCompatSeekBar; -import androidx.appcompat.widget.AppCompatTextView; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.animation.AccelerateInterpolator; -import android.view.animation.DecelerateInterpolator; -import android.view.animation.LinearInterpolator; -import android.widget.ImageButton; -import android.widget.SeekBar; -import android.widget.TextView; - -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import butterknife.Unbinder; -import code.name.monkey.appthemehelper.util.ATHUtil; -import code.name.monkey.appthemehelper.util.ColorUtil; -import code.name.monkey.appthemehelper.util.MaterialValueHelper; -import code.name.monkey.appthemehelper.util.TintHelper; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper; -import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerControlsFragment; -import code.name.monkey.retromusic.util.MusicUtil; -import code.name.monkey.retromusic.util.PreferenceUtil; - -/** - * @author Hemanth S (h4h13). - */ -public class LockScreenPlayerControlsFragment extends AbsPlayerControlsFragment { - @BindView(R.id.player_play_pause_button) - AppCompatImageButton playPauseFab; - - @BindView(R.id.player_prev_button) - ImageButton prevButton; - - @BindView(R.id.player_next_button) - ImageButton nextButton; - - @BindView(R.id.player_progress_slider) - AppCompatSeekBar progressSlider; - - @BindView(R.id.player_song_total_time) - TextView songTotalTime; - - @BindView(R.id.player_song_current_progress) - TextView songCurrentProgress; - - @BindView(R.id.title) - AppCompatTextView title; - - @BindView(R.id.text) - AppCompatTextView text; - - @BindView(R.id.volume_fragment_container) - View volumeContainer; - - private Unbinder unbinder; - private MusicProgressViewUpdateHelper progressViewUpdateHelper; - private int lastPlaybackControlsColor; - - public LockScreenPlayerControlsFragment() { - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - progressViewUpdateHelper = new MusicProgressViewUpdateHelper(this); - } - - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_lock_screen_playback_controls, container, false); - unbinder = ButterKnife.bind(this, view); - return view; - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - setUpMusicControllers(); - - //noinspection ConstantConditions - volumeContainer.setVisibility(PreferenceUtil.getInstance().getVolumeToggle() - ? View.VISIBLE : View.GONE); - - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - unbinder.unbind(); - } - - private void updateSong() { - Song song = MusicPlayerRemote.getCurrentSong(); - - title.setText(song.title); - text.setText(String.format("%s - %s", song.artistName, song.albumName)); - - } - - @Override - public void onResume() { - super.onResume(); - progressViewUpdateHelper.start(); - } - - @Override - public void onPause() { - super.onPause(); - progressViewUpdateHelper.stop(); - } - - @Override - public void onServiceConnected() { - updatePlayPauseDrawableState(false); - updateRepeatState(); - updateShuffleState(); - updateSong(); - } - - @Override - public void onPlayingMetaChanged() { - super.onPlayingMetaChanged(); - updateSong(); - } - - @Override - public void onPlayStateChanged() { - updatePlayPauseDrawableState(true); - } - - @Override - public void onRepeatModeChanged() { - updateRepeatState(); - } - - @Override - public void onShuffleModeChanged() { - updateShuffleState(); - } - - @Override - public void setDark(int dark) { - setProgressBarColor(progressSlider, dark); - - if (ColorUtil.isColorLight(ATHUtil.resolveColor(getContext(), android.R.attr.windowBackground))) { - lastPlaybackControlsColor = MaterialValueHelper.getSecondaryTextColor(getActivity(), true); - } else { - lastPlaybackControlsColor = MaterialValueHelper.getPrimaryTextColor(getActivity(), false); - - } - - updatePrevNextColor(); - - boolean isDark = ColorUtil.isColorLight(dark); - text.setTextColor(dark); - TintHelper.setTintAuto(playPauseFab, MaterialValueHelper.getPrimaryTextColor(getContext(), isDark), false); - TintHelper.setTintAuto(playPauseFab, dark, true); - } - - public void setProgressBarColor(SeekBar progressBar, int newColor) { - TintHelper.setTintAuto(progressBar, newColor, false); - //LayerDrawable ld = (LayerDrawable) progressBar.getProgressDrawable(); - //ClipDrawable clipDrawable = (ClipDrawable) ld.findDrawableByLayerId(android.R.id.progress); - //clipDrawable.setColorFilter(newColor, PorterDuff.Mode.SRC_IN); - } - - private void setUpPlayPauseFab() { - playPauseFab.post(() -> { - if (playPauseFab != null) { - playPauseFab.setPivotX(playPauseFab.getWidth() / 2); - playPauseFab.setPivotY(playPauseFab.getHeight() / 2); - } - }); - } - - - private void setUpMusicControllers() { - setUpPlayPauseFab(); - setUpPrevNext(); - setUpProgressSlider(); - } - - private void setUpPrevNext() { - updatePrevNextColor(); - nextButton.setOnClickListener(v -> MusicPlayerRemote.playNextSong()); - prevButton.setOnClickListener(v -> MusicPlayerRemote.back()); - } - - private void updatePrevNextColor() { - nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - prevButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - } - - @Override - protected void show() { - playPauseFab.animate() - .scaleX(1f) - .scaleY(1f) - .rotation(360f) - .setInterpolator(new DecelerateInterpolator()) - .start(); - } - - @Override - protected void hide() { - if (playPauseFab != null) { - playPauseFab.setScaleX(0f); - playPauseFab.setScaleY(0f); - playPauseFab.setRotation(0f); - } - } - - @Override - protected void updateShuffleState() { - //TODO(Nothing to Implement) - } - - @Override - protected void updateRepeatState() { - //TODO(Nothing to Implement) - } - - @Override - protected void setUpProgressSlider() { - progressSlider.setOnSeekBarChangeListener(new SimpleOnSeekbarChangeListener() { - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - if (fromUser) { - MusicPlayerRemote.seekTo(progress); - onUpdateProgressViews(MusicPlayerRemote.getSongProgressMillis(), - MusicPlayerRemote.getSongDurationMillis()); - } - } - }); - } - - public void showBouceAnimation() { - playPauseFab.clearAnimation(); - - playPauseFab.setScaleX(0.9f); - playPauseFab.setScaleY(0.9f); - playPauseFab.setVisibility(View.VISIBLE); - playPauseFab.setPivotX(playPauseFab.getWidth() / 2); - playPauseFab.setPivotY(playPauseFab.getHeight() / 2); - - playPauseFab.animate() - .setDuration(200) - .setInterpolator(new DecelerateInterpolator()) - .scaleX(1.1f) - .scaleY(1.1f) - .withEndAction(() -> playPauseFab.animate() - .setDuration(200) - .setInterpolator(new AccelerateInterpolator()) - .scaleX(1f) - .scaleY(1f) - .alpha(1f) - .start()) - .start(); - } - - protected void updatePlayPauseDrawableState(boolean animate) { - if (MusicPlayerRemote.isPlaying()) { - playPauseFab.setImageResource(R.drawable.ic_pause_white_24dp); - } else { - playPauseFab.setImageResource(R.drawable.ic_play_arrow_white_24dp); - } - } - - @OnClick(R.id.player_play_pause_button) - void showAnimation() { - if (MusicPlayerRemote.isPlaying()) { - MusicPlayerRemote.pauseSong(); - } else { - MusicPlayerRemote.resumePlaying(); - } - showBouceAnimation(); - } - - @Override - public void onUpdateProgressViews(int progress, int total) { - progressSlider.setMax(total); - - ObjectAnimator animator = ObjectAnimator.ofInt(progressSlider, "progress", progress); - animator.setDuration(1500); - animator.setInterpolator(new LinearInterpolator()); - animator.start(); - - songTotalTime.setText(MusicUtil.getReadableDurationString(total)); - songCurrentProgress.setText(MusicUtil.getReadableDurationString(progress)); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/material/MaterialControlsFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/material/MaterialControlsFragment.java deleted file mode 100644 index 5649185b..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/material/MaterialControlsFragment.java +++ /dev/null @@ -1,290 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.player.material; - -import android.animation.ObjectAnimator; -import android.graphics.PorterDuff; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.animation.LinearInterpolator; -import android.widget.ImageButton; -import android.widget.SeekBar; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.widget.AppCompatSeekBar; -import androidx.appcompat.widget.AppCompatTextView; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.Unbinder; -import code.name.monkey.appthemehelper.util.ATHUtil; -import code.name.monkey.appthemehelper.util.ColorUtil; -import code.name.monkey.appthemehelper.util.MaterialValueHelper; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper; -import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler; -import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.service.MusicService; -import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerControlsFragment; -import code.name.monkey.retromusic.util.MusicUtil; -import code.name.monkey.retromusic.util.PreferenceUtil; - -/** - * @author Hemanth S (h4h13). - */ -public class MaterialControlsFragment extends AbsPlayerControlsFragment { - - @BindView(R.id.player_play_pause_button) - ImageButton playPauseFab; - - @BindView(R.id.player_prev_button) - ImageButton prevButton; - - @BindView(R.id.player_next_button) - ImageButton nextButton; - - @BindView(R.id.player_repeat_button) - ImageButton repeatButton; - - @BindView(R.id.player_shuffle_button) - ImageButton shuffleButton; - - @BindView(R.id.player_progress_slider) - AppCompatSeekBar progressSlider; - - @BindView(R.id.player_song_total_time) - TextView songTotalTime; - - @BindView(R.id.player_song_current_progress) - TextView songCurrentProgress; - - @BindView(R.id.title) - AppCompatTextView title; - - @BindView(R.id.text) - TextView text; - - @BindView(R.id.volume_fragment_container) - View mVolumeContainer; - - private Unbinder unbinder; - private int lastPlaybackControlsColor; - private int lastDisabledPlaybackControlsColor; - private MusicProgressViewUpdateHelper progressViewUpdateHelper; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - progressViewUpdateHelper = new MusicProgressViewUpdateHelper(this); - } - - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_material_playback_controls, container, false); - unbinder = ButterKnife.bind(this, view); - return view; - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - unbinder = ButterKnife.bind(this, view); - setUpMusicControllers(); - - if (PreferenceUtil.getInstance().getVolumeToggle()) { - mVolumeContainer.setVisibility(View.VISIBLE); - } else { - mVolumeContainer.setVisibility(View.GONE); - } - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - unbinder.unbind(); - } - - private void updateSong() { - Song song = MusicPlayerRemote.getCurrentSong(); - title.setText(song.title); - text.setText(song.artistName); - } - - @Override - public void onResume() { - super.onResume(); - progressViewUpdateHelper.start(); - } - - @Override - public void onPause() { - super.onPause(); - progressViewUpdateHelper.stop(); - } - - @Override - public void onServiceConnected() { - updatePlayPauseDrawableState(); - updateRepeatState(); - updateShuffleState(); - updateSong(); - } - - @Override - public void onPlayingMetaChanged() { - super.onPlayingMetaChanged(); - updateSong(); - } - - @Override - public void onPlayStateChanged() { - updatePlayPauseDrawableState(); - } - - @Override - public void onRepeatModeChanged() { - updateRepeatState(); - } - - @Override - public void onShuffleModeChanged() { - updateShuffleState(); - } - - @Override - public void setDark(int dark) { - int color = ATHUtil.resolveColor(getActivity(), android.R.attr.colorBackground); - if (ColorUtil.isColorLight(color)) { - lastPlaybackControlsColor = MaterialValueHelper.getSecondaryTextColor(getActivity(), true); - lastDisabledPlaybackControlsColor = MaterialValueHelper.getSecondaryDisabledTextColor(getActivity(), true); - } else { - lastPlaybackControlsColor = MaterialValueHelper.getPrimaryTextColor(getActivity(), false); - lastDisabledPlaybackControlsColor = MaterialValueHelper.getPrimaryDisabledTextColor(getActivity(), false); - } - - updateRepeatState(); - updateShuffleState(); - - if (PreferenceUtil.getInstance().getAdaptiveColor()) { - lastPlaybackControlsColor = dark; - text.setTextColor(dark); - } - - updatePlayPauseColor(); - updatePrevNextColor(); - } - - private void setUpPlayPauseFab() { - playPauseFab.setOnClickListener(new PlayPauseButtonOnClickHandler()); - } - - protected void updatePlayPauseDrawableState() { - if (MusicPlayerRemote.isPlaying()) { - playPauseFab.setImageResource(R.drawable.ic_pause_white_big); - } else { - playPauseFab.setImageResource(R.drawable.ic_play_arrow_white_big); - } - } - - private void setUpMusicControllers() { - setUpPlayPauseFab(); - setUpPrevNext(); - setUpRepeatButton(); - setUpShuffleButton(); - setUpProgressSlider(); - } - - private void setUpPrevNext() { - updatePrevNextColor(); - nextButton.setOnClickListener(v -> MusicPlayerRemote.playNextSong()); - prevButton.setOnClickListener(v -> MusicPlayerRemote.back()); - } - - private void updatePrevNextColor() { - nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - prevButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - } - - private void updatePlayPauseColor() { - playPauseFab.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - } - - private void setUpShuffleButton() { - shuffleButton.setOnClickListener(v -> MusicPlayerRemote.toggleShuffleMode()); - } - - @Override - protected void updateShuffleState() { - switch (MusicPlayerRemote.getShuffleMode()) { - case MusicService.SHUFFLE_MODE_SHUFFLE: - shuffleButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - default: - shuffleButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - } - } - - private void setUpRepeatButton() { - repeatButton.setOnClickListener(v -> MusicPlayerRemote.cycleRepeatMode()); - } - - @Override - protected void updateRepeatState() { - switch (MusicPlayerRemote.getRepeatMode()) { - case MusicService.REPEAT_MODE_NONE: - repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp); - repeatButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - case MusicService.REPEAT_MODE_ALL: - repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp); - repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - case MusicService.REPEAT_MODE_THIS: - repeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp); - repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - } - } - - @Override - protected void show() { - - } - - @Override - protected void hide() { - - } - - @Override - protected void setUpProgressSlider() { - progressSlider.setOnSeekBarChangeListener(new SimpleOnSeekbarChangeListener() { - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - if (fromUser) { - MusicPlayerRemote.seekTo(progress); - onUpdateProgressViews(MusicPlayerRemote.getSongProgressMillis(), - MusicPlayerRemote.getSongDurationMillis()); - } - } - }); - } - - @Override - public void onUpdateProgressViews(int progress, int total) { - progressSlider.setMax(total); - - ObjectAnimator animator = ObjectAnimator.ofInt(progressSlider, "progress", progress); - animator.setDuration(1500); - animator.setInterpolator(new LinearInterpolator()); - animator.start(); - - songTotalTime.setText(MusicUtil.getReadableDurationString(total)); - songCurrentProgress.setText(MusicUtil.getReadableDurationString(progress)); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/material/MaterialFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/material/MaterialFragment.java deleted file mode 100644 index 502e40a2..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/material/MaterialFragment.java +++ /dev/null @@ -1,149 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.player.material; - -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.ColorInt; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.widget.Toolbar; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.Unbinder; -import code.name.monkey.appthemehelper.util.ATHUtil; -import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerFragment; -import code.name.monkey.retromusic.ui.fragments.player.PlayerAlbumCoverFragment; -import code.name.monkey.retromusic.ui.fragments.player.normal.PlayerFragment; - -/** - * @author Hemanth S (h4h13). - */ -public class MaterialFragment extends AbsPlayerFragment implements PlayerAlbumCoverFragment.Callbacks { - - @BindView(R.id.player_toolbar) - Toolbar toolbar; - - private int lastColor; - private MaterialControlsFragment playbackControlsFragment; - private Unbinder unbinder; - - public static PlayerFragment newInstance() { - Bundle args = new Bundle(); - PlayerFragment fragment = new PlayerFragment(); - fragment.setArguments(args); - return fragment; - } - - @Override - @ColorInt - public int getPaletteColor() { - return lastColor; - } - - @Override - public void onShow() { - playbackControlsFragment.show(); - } - - @Override - public void onHide() { - playbackControlsFragment.hide(); - onBackPressed(); - } - - @Override - public boolean onBackPressed() { - return false; - } - - @Override - public Toolbar getToolbar() { - return toolbar; - } - - @Override - public int toolbarIconColor() { - return ATHUtil.resolveColor(getContext(), R.attr.iconColor); - } - - @Override - public void onColorChanged(int color) { - playbackControlsFragment.setDark(color); - lastColor = color; - getCallbacks().onPaletteColorChanged(); - - ToolbarContentTintHelper.colorizeToolbar(toolbar, - ATHUtil.resolveColor(getContext(), R.attr.iconColor), getActivity()); - } - - @Override - protected void toggleFavorite(Song song) { - super.toggleFavorite(song); - if (song.id == MusicPlayerRemote.getCurrentSong().id) { - updateIsFavorite(); - } - } - - @Override - public void onFavoriteToggled() { - toggleFavorite(MusicPlayerRemote.getCurrentSong()); - } - - - @Override - public void onDestroyView() { - super.onDestroyView(); - unbinder.unbind(); - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_material, container, false); - unbinder = ButterKnife.bind(this, view); - return view; - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - setUpSubFragments(); - setUpPlayerToolbar(); - } - - private void setUpSubFragments() { - playbackControlsFragment = (MaterialControlsFragment) getChildFragmentManager() - .findFragmentById(R.id.playback_controls_fragment); - - PlayerAlbumCoverFragment playerAlbumCoverFragment = - (PlayerAlbumCoverFragment) getChildFragmentManager() - .findFragmentById(R.id.player_album_cover_fragment); - playerAlbumCoverFragment.setCallbacks(this); - } - - private void setUpPlayerToolbar() { - toolbar.inflateMenu(R.menu.menu_player); - toolbar.setNavigationOnClickListener(v -> getActivity().onBackPressed()); - toolbar.setOnMenuItemClickListener(this); - - ToolbarContentTintHelper.colorizeToolbar(toolbar, - ATHUtil.resolveColor(getContext(), R.attr.iconColor), getActivity()); - } - - @Override - public void onServiceConnected() { - updateIsFavorite(); - } - - @Override - public void onPlayingMetaChanged() { - updateIsFavorite(); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/normal/PlayerFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/normal/PlayerFragment.java deleted file mode 100644 index 2ad2bfc9..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/normal/PlayerFragment.java +++ /dev/null @@ -1,175 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.player.normal; - -import android.animation.ArgbEvaluator; -import android.animation.ValueAnimator; -import android.graphics.drawable.GradientDrawable; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.ColorInt; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.widget.Toolbar; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.Unbinder; -import code.name.monkey.appthemehelper.util.ATHUtil; -import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerFragment; -import code.name.monkey.retromusic.ui.fragments.player.PlayerAlbumCoverFragment; -import code.name.monkey.retromusic.util.PreferenceUtil; -import code.name.monkey.retromusic.util.ViewUtil; -import code.name.monkey.retromusic.views.DrawableGradient; - - -public class PlayerFragment extends AbsPlayerFragment implements PlayerAlbumCoverFragment.Callbacks { - - @BindView(R.id.player_toolbar) - Toolbar toolbar; - - @BindView(R.id.gradient_background) - View colorBackground; - - private int lastColor; - private PlayerPlaybackControlsFragment playbackControlsFragment; - private Unbinder unbinder; - private ValueAnimator valueAnimator; - - public static PlayerFragment newInstance() { - Bundle args = new Bundle(); - PlayerFragment fragment = new PlayerFragment(); - fragment.setArguments(args); - return fragment; - } - - - private void colorize(int i) { - if (valueAnimator != null) { - valueAnimator.cancel(); - } - - valueAnimator = ValueAnimator.ofObject(new ArgbEvaluator(), android.R.color.transparent, i); - valueAnimator.addUpdateListener(animation -> { - GradientDrawable drawable = new DrawableGradient(GradientDrawable.Orientation.TOP_BOTTOM, - new int[]{(int) animation.getAnimatedValue(), android.R.color.transparent}, 0); - if (colorBackground != null) { - colorBackground.setBackground(drawable); - } - }); - valueAnimator.setDuration(ViewUtil.RETRO_MUSIC_ANIM_TIME).start(); - } - - @Override - @ColorInt - public int getPaletteColor() { - return lastColor; - } - - @Override - public void onShow() { - playbackControlsFragment.show(); - } - - @Override - public void onHide() { - playbackControlsFragment.hide(); - onBackPressed(); - } - - @Override - public boolean onBackPressed() { - return false; - } - - @Override - public Toolbar getToolbar() { - return toolbar; - } - - @Override - public int toolbarIconColor() { - return ATHUtil.resolveColor(getContext(), R.attr.iconColor); - } - - @Override - public void onColorChanged(int color) { - playbackControlsFragment.setDark(color); - lastColor = color; - getCallbacks().onPaletteColorChanged(); - - ToolbarContentTintHelper.colorizeToolbar(toolbar, ATHUtil.resolveColor(getContext(), R.attr.iconColor), getActivity()); - - if (PreferenceUtil.getInstance().getAdaptiveColor()) { - colorize(color); - } - } - - @Override - protected void toggleFavorite(Song song) { - super.toggleFavorite(song); - if (song.id == MusicPlayerRemote.getCurrentSong().id) { - updateIsFavorite(); - } - } - - @Override - public void onFavoriteToggled() { - toggleFavorite(MusicPlayerRemote.getCurrentSong()); - } - - - @Override - public void onDestroyView() { - super.onDestroyView(); - unbinder.unbind(); - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_player, container, false); - unbinder = ButterKnife.bind(this, view); - return view; - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - setUpSubFragments(); - setUpPlayerToolbar(); - } - - private void setUpSubFragments() { - playbackControlsFragment = (PlayerPlaybackControlsFragment) getChildFragmentManager().findFragmentById(R.id.playback_controls_fragment); - PlayerAlbumCoverFragment playerAlbumCoverFragment = (PlayerAlbumCoverFragment) getChildFragmentManager().findFragmentById(R.id.player_album_cover_fragment); - if (playerAlbumCoverFragment != null) { - playerAlbumCoverFragment.setCallbacks(this); - } - } - - private void setUpPlayerToolbar() { - toolbar.inflateMenu(R.menu.menu_player); - toolbar.setNavigationOnClickListener(v -> getActivity().onBackPressed()); - toolbar.setOnMenuItemClickListener(this); - - ToolbarContentTintHelper.colorizeToolbar(toolbar, - ATHUtil.resolveColor(getContext(), R.attr.iconColor), getActivity()); - } - - @Override - public void onServiceConnected() { - updateIsFavorite(); - - } - - @Override - public void onPlayingMetaChanged() { - updateIsFavorite(); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/normal/PlayerPlaybackControlsFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/normal/PlayerPlaybackControlsFragment.java deleted file mode 100644 index 28eb7789..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/normal/PlayerPlaybackControlsFragment.java +++ /dev/null @@ -1,344 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.player.normal; - -import android.animation.ObjectAnimator; -import android.graphics.PorterDuff; -import android.graphics.drawable.ClipDrawable; -import android.graphics.drawable.LayerDrawable; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.animation.AccelerateInterpolator; -import android.view.animation.DecelerateInterpolator; -import android.view.animation.LinearInterpolator; -import android.widget.ImageButton; -import android.widget.SeekBar; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.widget.AppCompatSeekBar; -import androidx.appcompat.widget.AppCompatTextView; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import butterknife.Unbinder; -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.appthemehelper.util.ATHUtil; -import code.name.monkey.appthemehelper.util.ColorUtil; -import code.name.monkey.appthemehelper.util.MaterialValueHelper; -import code.name.monkey.appthemehelper.util.TintHelper; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper; -import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler; -import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.service.MusicService; -import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerControlsFragment; -import code.name.monkey.retromusic.util.MusicUtil; -import code.name.monkey.retromusic.util.PreferenceUtil; - -public class PlayerPlaybackControlsFragment extends AbsPlayerControlsFragment { - - @BindView(R.id.player_play_pause_button) - ImageButton playPauseFab; - - @BindView(R.id.player_prev_button) - ImageButton prevButton; - - @BindView(R.id.player_next_button) - ImageButton nextButton; - - @BindView(R.id.player_repeat_button) - ImageButton repeatButton; - - @BindView(R.id.player_shuffle_button) - ImageButton shuffleButton; - - @BindView(R.id.player_progress_slider) - AppCompatSeekBar progressSlider; - - @BindView(R.id.player_song_total_time) - TextView songTotalTime; - - @BindView(R.id.player_song_current_progress) - TextView songCurrentProgress; - - @BindView(R.id.title) - AppCompatTextView title; - - @BindView(R.id.text) - TextView text; - - @BindView(R.id.volume_fragment_container) - View mVolumeContainer; - - private Unbinder unbinder; - private int lastPlaybackControlsColor; - private int lastDisabledPlaybackControlsColor; - private MusicProgressViewUpdateHelper progressViewUpdateHelper; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - progressViewUpdateHelper = new MusicProgressViewUpdateHelper(this); - } - - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_player_playback_controls, container, false); - unbinder = ButterKnife.bind(this, view); - return view; - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - unbinder = ButterKnife.bind(this, view); - setUpMusicControllers(); - - if (PreferenceUtil.getInstance().getVolumeToggle()) { - mVolumeContainer.setVisibility(View.VISIBLE); - } else { - mVolumeContainer.setVisibility(View.GONE); - } - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - unbinder.unbind(); - } - - private void updateSong() { - Song song = MusicPlayerRemote.getCurrentSong(); - title.setText(song.title); - text.setText(song.artistName); - } - - @Override - public void onResume() { - super.onResume(); - progressViewUpdateHelper.start(); - } - - @Override - public void onPause() { - super.onPause(); - progressViewUpdateHelper.stop(); - } - - @Override - public void onServiceConnected() { - updatePlayPauseDrawableState(); - updateRepeatState(); - updateShuffleState(); - updateSong(); - } - - @Override - public void onPlayingMetaChanged() { - super.onPlayingMetaChanged(); - updateSong(); - } - - @Override - public void onPlayStateChanged() { - updatePlayPauseDrawableState(); - } - - @Override - public void onRepeatModeChanged() { - updateRepeatState(); - } - - @Override - public void onShuffleModeChanged() { - updateShuffleState(); - } - - @Override - public void setDark(int dark) { - int color = ATHUtil.resolveColor(getActivity(), android.R.attr.colorBackground); - if (ColorUtil.isColorLight(color)) { - lastPlaybackControlsColor = MaterialValueHelper.getSecondaryTextColor(getActivity(), true); - lastDisabledPlaybackControlsColor = MaterialValueHelper.getSecondaryDisabledTextColor(getActivity(), true); - } else { - lastPlaybackControlsColor = MaterialValueHelper.getPrimaryTextColor(getActivity(), false); - lastDisabledPlaybackControlsColor = MaterialValueHelper.getPrimaryDisabledTextColor(getActivity(), false); - } - - if (PreferenceUtil.getInstance().getAdaptiveColor()) { - setFabColor(dark); - } else { - setFabColor(ThemeStore.accentColor(getContext())); - } - - updateRepeatState(); - updateShuffleState(); - updatePrevNextColor(); - } - - private void setFabColor(int i) { - TintHelper.setTintAuto(playPauseFab, MaterialValueHelper.getPrimaryTextColor(getContext(), ColorUtil.isColorLight(i)), false); - TintHelper.setTintAuto(playPauseFab, i, true); - setProgressBarColor(i); - } - - private void setProgressBarColor(int newColor) { - LayerDrawable ld = (LayerDrawable) progressSlider.getProgressDrawable(); - ClipDrawable clipDrawable = (ClipDrawable) ld.findDrawableByLayerId(android.R.id.progress); - clipDrawable.setColorFilter(newColor, PorterDuff.Mode.SRC_IN); - } - - private void setUpPlayPauseFab() { - playPauseFab.setOnClickListener(new PlayPauseButtonOnClickHandler()); - } - - protected void updatePlayPauseDrawableState() { - if (MusicPlayerRemote.isPlaying()) { - playPauseFab.setImageResource(R.drawable.ic_pause_white_24dp); - } else { - playPauseFab.setImageResource(R.drawable.ic_play_arrow_white_24dp); - } - } - - private void setUpMusicControllers() { - setUpPlayPauseFab(); - setUpPrevNext(); - setUpRepeatButton(); - setUpShuffleButton(); - setUpProgressSlider(); - } - - private void setUpPrevNext() { - updatePrevNextColor(); - nextButton.setOnClickListener(v -> MusicPlayerRemote.playNextSong()); - prevButton.setOnClickListener(v -> MusicPlayerRemote.back()); - } - - private void updatePrevNextColor() { - nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - prevButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - } - - private void setUpShuffleButton() { - shuffleButton.setOnClickListener(v -> MusicPlayerRemote.toggleShuffleMode()); - } - - @Override - protected void updateShuffleState() { - switch (MusicPlayerRemote.getShuffleMode()) { - case MusicService.SHUFFLE_MODE_SHUFFLE: - shuffleButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - default: - shuffleButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - } - } - - private void setUpRepeatButton() { - repeatButton.setOnClickListener(v -> MusicPlayerRemote.cycleRepeatMode()); - } - - @Override - protected void updateRepeatState() { - switch (MusicPlayerRemote.getRepeatMode()) { - case MusicService.REPEAT_MODE_NONE: - repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp); - repeatButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - case MusicService.REPEAT_MODE_ALL: - repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp); - repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - case MusicService.REPEAT_MODE_THIS: - repeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp); - repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - } - } - - @Override - protected void show() { - playPauseFab.animate() - .scaleX(1f) - .scaleY(1f) - .rotation(360f) - .setInterpolator(new DecelerateInterpolator()) - .start(); - } - - @Override - protected void hide() { - if (playPauseFab != null) { - playPauseFab.setScaleX(0f); - playPauseFab.setScaleY(0f); - playPauseFab.setRotation(0f); - } - } - - @Override - protected void setUpProgressSlider() { - progressSlider.setOnSeekBarChangeListener(new SimpleOnSeekbarChangeListener() { - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - if (fromUser) { - MusicPlayerRemote.seekTo(progress); - onUpdateProgressViews(MusicPlayerRemote.getSongProgressMillis(), - MusicPlayerRemote.getSongDurationMillis()); - } - } - }); - } - - public void showBouceAnimation() { - playPauseFab.clearAnimation(); - - playPauseFab.setScaleX(0.9f); - playPauseFab.setScaleY(0.9f); - playPauseFab.setVisibility(View.VISIBLE); - playPauseFab.setPivotX(playPauseFab.getWidth() / 2); - playPauseFab.setPivotY(playPauseFab.getHeight() / 2); - - playPauseFab.animate() - .setDuration(200) - .setInterpolator(new DecelerateInterpolator()) - .scaleX(1.1f) - .scaleY(1.1f) - .withEndAction(() -> playPauseFab.animate() - .setDuration(200) - .setInterpolator(new AccelerateInterpolator()) - .scaleX(1f) - .scaleY(1f) - .alpha(1f) - .start()) - .start(); - } - - @OnClick(R.id.player_play_pause_button) - void showAnimation() { - if (MusicPlayerRemote.isPlaying()) { - MusicPlayerRemote.pauseSong(); - } else { - MusicPlayerRemote.resumePlaying(); - } - showBouceAnimation(); - } - - @Override - public void onUpdateProgressViews(int progress, int total) { - progressSlider.setMax(total); - - ObjectAnimator animator = ObjectAnimator.ofInt(progressSlider, "progress", progress); - animator.setDuration(1500); - animator.setInterpolator(new LinearInterpolator()); - animator.start(); - - songTotalTime.setText(MusicUtil.getReadableDurationString(total)); - songCurrentProgress.setText(MusicUtil.getReadableDurationString(progress)); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/plain/PlainPlaybackControlsFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/plain/PlainPlaybackControlsFragment.java deleted file mode 100644 index e1afe5e4..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/plain/PlainPlaybackControlsFragment.java +++ /dev/null @@ -1,324 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.player.plain; - -import android.animation.ObjectAnimator; -import android.graphics.PorterDuff; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.animation.AccelerateInterpolator; -import android.view.animation.DecelerateInterpolator; -import android.view.animation.LinearInterpolator; -import android.widget.ImageButton; -import android.widget.SeekBar; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import butterknife.Unbinder; -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.appthemehelper.util.ATHUtil; -import code.name.monkey.appthemehelper.util.ColorUtil; -import code.name.monkey.appthemehelper.util.MaterialValueHelper; -import code.name.monkey.appthemehelper.util.TintHelper; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper; -import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler; -import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener; -import code.name.monkey.retromusic.service.MusicService; -import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerControlsFragment; -import code.name.monkey.retromusic.util.MusicUtil; -import code.name.monkey.retromusic.util.PreferenceUtil; -import code.name.monkey.retromusic.views.PlayPauseDrawable; - -/** - * @author Hemanth S (h4h13). - */ - -public class PlainPlaybackControlsFragment extends AbsPlayerControlsFragment { - - @BindView(R.id.player_play_pause_button) - ImageButton playPauseFab; - - @BindView(R.id.player_prev_button) - ImageButton prevButton; - - @BindView(R.id.player_next_button) - ImageButton nextButton; - - @BindView(R.id.player_repeat_button) - ImageButton repeatButton; - - @BindView(R.id.player_shuffle_button) - ImageButton shuffleButton; - - @BindView(R.id.player_progress_slider) - SeekBar progressSlider; - - @BindView(R.id.player_song_total_time) - TextView songTotalTime; - - @BindView(R.id.player_song_current_progress) - TextView songCurrentProgress; - - @BindView(R.id.volume_fragment_container) - View volumeContainer; - - private Unbinder unbinder; - private PlayPauseDrawable playerFabPlayPauseDrawable; - private int lastPlaybackControlsColor; - private int lastDisabledPlaybackControlsColor; - private MusicProgressViewUpdateHelper progressViewUpdateHelper; - - @Override - public void onPlayStateChanged() { - updatePlayPauseDrawableState(); - } - - @Override - public void onRepeatModeChanged() { - updateRepeatState(); - } - - @Override - public void onShuffleModeChanged() { - updateShuffleState(); - } - - @Override - public void onServiceConnected() { - updatePlayPauseDrawableState(); - updateRepeatState(); - updateShuffleState(); - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - progressViewUpdateHelper = new MusicProgressViewUpdateHelper(this); - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - unbinder.unbind(); - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, - @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_plain_controls_fragment, container, false); - unbinder = ButterKnife.bind(this, view); - return view; - } - - - @Override - public void onResume() { - super.onResume(); - progressViewUpdateHelper.start(); - } - - @Override - public void onPause() { - super.onPause(); - progressViewUpdateHelper.stop(); - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - setUpMusicControllers(); - if (PreferenceUtil.getInstance().getVolumeToggle()) { - volumeContainer.setVisibility(View.VISIBLE); - } else { - volumeContainer.setVisibility(View.GONE); - } - } - - private void setUpMusicControllers() { - setUpPlayPauseFab(); - setUpPrevNext(); - setUpRepeatButton(); - setUpShuffleButton(); - setUpProgressSlider(); - } - - private void setUpPrevNext() { - updatePrevNextColor(); - nextButton.setOnClickListener(v -> MusicPlayerRemote.playNextSong()); - prevButton.setOnClickListener(v -> MusicPlayerRemote.back()); - } - - private void updatePrevNextColor() { - nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - prevButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - } - - private void setUpShuffleButton() { - shuffleButton.setOnClickListener(v -> MusicPlayerRemote.toggleShuffleMode()); - } - - @Override - protected void updateShuffleState() { - switch (MusicPlayerRemote.getShuffleMode()) { - case MusicService.SHUFFLE_MODE_SHUFFLE: - shuffleButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - default: - shuffleButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - } - } - - private void setUpRepeatButton() { - repeatButton.setOnClickListener(v -> MusicPlayerRemote.cycleRepeatMode()); - } - - @Override - protected void updateRepeatState() { - switch (MusicPlayerRemote.getRepeatMode()) { - case MusicService.REPEAT_MODE_NONE: - repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp); - repeatButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - case MusicService.REPEAT_MODE_ALL: - repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp); - repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - case MusicService.REPEAT_MODE_THIS: - repeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp); - repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - } - } - - - @Override - protected void show() { - playPauseFab.animate() - .scaleX(1f) - .scaleY(1f) - .rotation(360f) - .setInterpolator(new DecelerateInterpolator()) - .start(); - } - - @Override - protected void hide() { - if (playPauseFab != null) { - playPauseFab.setScaleX(0f); - playPauseFab.setScaleY(0f); - playPauseFab.setRotation(0f); - } - } - - @Override - protected void setUpProgressSlider() { - progressSlider.setOnSeekBarChangeListener(new SimpleOnSeekbarChangeListener() { - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - if (fromUser) { - MusicPlayerRemote.seekTo(progress); - onUpdateProgressViews(MusicPlayerRemote.getSongProgressMillis(), - MusicPlayerRemote.getSongDurationMillis()); - } - } - }); - } - - public void showBouceAnimation() { - playPauseFab.clearAnimation(); - - playPauseFab.setScaleX(0.9f); - playPauseFab.setScaleY(0.9f); - playPauseFab.setVisibility(View.VISIBLE); - playPauseFab.setPivotX(playPauseFab.getWidth() / 2); - playPauseFab.setPivotY(playPauseFab.getHeight() / 2); - - playPauseFab.animate() - .setDuration(200) - .setInterpolator(new DecelerateInterpolator()) - .scaleX(1.1f) - .scaleY(1.1f) - .withEndAction(() -> playPauseFab.animate() - .setDuration(200) - .setInterpolator(new AccelerateInterpolator()) - .scaleX(1f) - .scaleY(1f) - .alpha(1f) - .start()) - .start(); - } - - @OnClick(R.id.player_play_pause_button) - void showAnimation() { - if (MusicPlayerRemote.isPlaying()) { - MusicPlayerRemote.pauseSong(); - } else { - MusicPlayerRemote.resumePlaying(); - } - showBouceAnimation(); - } - - @Override - public void onUpdateProgressViews(int progress, int total) { - progressSlider.setMax(total); - - ObjectAnimator animator = ObjectAnimator.ofInt(progressSlider, "progress", progress); - animator.setDuration(1500); - animator.setInterpolator(new LinearInterpolator()); - animator.start(); - - songTotalTime.setText(MusicUtil.getReadableDurationString(total)); - songCurrentProgress.setText(MusicUtil.getReadableDurationString(progress)); - } - - @Override - public void setDark(int dark) { - int color = ATHUtil.resolveColor(getActivity(), android.R.attr.colorBackground); - if (ColorUtil.isColorLight(color)) { - lastPlaybackControlsColor = - MaterialValueHelper.getSecondaryTextColor(getActivity(), true); - lastDisabledPlaybackControlsColor = - MaterialValueHelper.getSecondaryDisabledTextColor(getActivity(), true); - } else { - lastPlaybackControlsColor = - MaterialValueHelper.getPrimaryTextColor(getActivity(), false); - lastDisabledPlaybackControlsColor = - MaterialValueHelper.getPrimaryDisabledTextColor(getActivity(), false); - } - - int finalColor = PreferenceUtil.getInstance().getAdaptiveColor() ? dark : ThemeStore.accentColor(getContext()); - - - setProgressBarColor(finalColor); - TintHelper.setTintAuto(playPauseFab, MaterialValueHelper.getPrimaryTextColor(getContext(), ColorUtil.isColorLight(finalColor)), false); - TintHelper.setTintAuto(playPauseFab, finalColor, true); - - updateRepeatState(); - updateShuffleState(); - updatePrevNextColor(); - } - - private void setProgressBarColor(int newColor) { - TintHelper.setTintAuto(progressSlider, newColor, false); - } - - private void setUpPlayPauseFab() { - playPauseFab.setOnClickListener(new PlayPauseButtonOnClickHandler()); - } - - protected void updatePlayPauseDrawableState() { - if (MusicPlayerRemote.isPlaying()) { - playPauseFab.setImageResource(R.drawable.ic_pause_white_24dp); - } else { - playPauseFab.setImageResource(R.drawable.ic_play_arrow_white_24dp); - } - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/plain/PlainPlayerFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/plain/PlainPlayerFragment.java deleted file mode 100644 index 7a1a72ca..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/plain/PlainPlayerFragment.java +++ /dev/null @@ -1,161 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.player.plain; - -import android.graphics.Color; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.FrameLayout; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.widget.Toolbar; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.Unbinder; -import code.name.monkey.appthemehelper.util.ATHUtil; -import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerFragment; -import code.name.monkey.retromusic.ui.fragments.player.PlayerAlbumCoverFragment; - -/** - * @author Hemanth S (h4h13). - */ - -public class PlainPlayerFragment extends AbsPlayerFragment implements - PlayerAlbumCoverFragment.Callbacks { - - @BindView(R.id.title) - TextView title; - - @BindView(R.id.text) - TextView text; - - @BindView(R.id.player_toolbar) - Toolbar toolbar; - - @BindView(R.id.toolbar_container) - FrameLayout toolbarContainer; - - private Unbinder unbinder; - private PlainPlaybackControlsFragment plainPlaybackControlsFragment; - private int mLastColor; - - @Override - public void onDestroyView() { - super.onDestroyView(); - unbinder.unbind(); - } - - @Override - public void onPlayingMetaChanged() { - super.onPlayingMetaChanged(); - updateSong(); - } - - private void updateSong() { - Song song = MusicPlayerRemote.getCurrentSong(); - title.setText(song.title); - text.setText(song.artistName); - } - - @Override - public void onServiceConnected() { - super.onServiceConnected(); - updateSong(); - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_plain_player, container, false); - unbinder = ButterKnife.bind(this, view); - - return view; - } - - private void setUpPlayerToolbar() { - toolbar.inflateMenu(R.menu.menu_player); - toolbar.setNavigationOnClickListener(v -> getActivity().onBackPressed()); - toolbar.setOnMenuItemClickListener(this); - - ToolbarContentTintHelper.colorizeToolbar(toolbar, - ATHUtil.resolveColor(getContext(), R.attr.iconColor), - getActivity()); - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - - setUpSubFragments(); - setUpPlayerToolbar(); - title.setSelected(true); - } - - private void setUpSubFragments() { - plainPlaybackControlsFragment = (PlainPlaybackControlsFragment) getChildFragmentManager() - .findFragmentById(R.id.playback_controls_fragment); - PlayerAlbumCoverFragment playerAlbumCoverFragment = (PlayerAlbumCoverFragment) getChildFragmentManager() - .findFragmentById(R.id.player_album_cover_fragment); - playerAlbumCoverFragment.setCallbacks(this); - } - - @Override - public int getPaletteColor() { - return mLastColor; - } - - @Override - public void onShow() { - plainPlaybackControlsFragment.show(); - } - - @Override - public void onHide() { - plainPlaybackControlsFragment.hide(); - onBackPressed(); - } - - @Override - public boolean onBackPressed() { - return false; - } - - @Override - public Toolbar getToolbar() { - return toolbar; - } - - @Override - public int toolbarIconColor() { - return ATHUtil.resolveColor(getContext(), R.attr.iconColor); - } - - @Override - public void onColorChanged(int color) { - plainPlaybackControlsFragment.setDark(color); - mLastColor = color; - getCallbacks().onPaletteColorChanged(); - ToolbarContentTintHelper.colorizeToolbar(toolbar, - ATHUtil.resolveColor(getContext(), R.attr.iconColor), getActivity()); - } - - @Override - public void onFavoriteToggled() { - toggleFavorite(MusicPlayerRemote.getCurrentSong()); - } - - @Override - protected void toggleFavorite(Song song) { - super.toggleFavorite(song); - if (song.id == MusicPlayerRemote.getCurrentSong().id) { - updateIsFavorite(); - } - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/simple/SimplePlaybackControlsFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/simple/SimplePlaybackControlsFragment.java deleted file mode 100644 index 657b0155..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/simple/SimplePlaybackControlsFragment.java +++ /dev/null @@ -1,312 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.player.simple; - -import android.graphics.PorterDuff; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.animation.AccelerateInterpolator; -import android.view.animation.DecelerateInterpolator; -import android.widget.ImageButton; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import butterknife.Unbinder; -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.appthemehelper.util.ATHUtil; -import code.name.monkey.appthemehelper.util.ColorUtil; -import code.name.monkey.appthemehelper.util.MaterialValueHelper; -import code.name.monkey.appthemehelper.util.TintHelper; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper; -import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.service.MusicService; -import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerControlsFragment; -import code.name.monkey.retromusic.util.MusicUtil; -import code.name.monkey.retromusic.util.PreferenceUtil; -import code.name.monkey.retromusic.views.PlayPauseDrawable; - -/** - * @author Hemanth S (h4h13). - */ - -public class SimplePlaybackControlsFragment extends AbsPlayerControlsFragment { - - @BindView(R.id.player_play_pause_button) - ImageButton playPauseFab; - - @BindView(R.id.player_prev_button) - ImageButton prevButton; - - @BindView(R.id.player_next_button) - ImageButton nextButton; - - @BindView(R.id.player_repeat_button) - ImageButton repeatButton; - - @BindView(R.id.player_shuffle_button) - ImageButton shuffleButton; - - @BindView(R.id.player_song_current_progress) - TextView songCurrentProgress; - - @BindView(R.id.volume_fragment_container) - View volumeContainer; - - @BindView(R.id.title) - TextView title; - - @BindView(R.id.text) - TextView text; - private Unbinder unbinder; - private PlayPauseDrawable playerFabPlayPauseDrawable; - private int lastPlaybackControlsColor; - private int lastDisabledPlaybackControlsColor; - private MusicProgressViewUpdateHelper progressViewUpdateHelper; - - - @Override - public void onPlayStateChanged() { - updatePlayPauseDrawableState(); - } - - @Override - public void onRepeatModeChanged() { - updateRepeatState(); - } - - @Override - public void onShuffleModeChanged() { - updateShuffleState(); - } - - @Override - public void onServiceConnected() { - updatePlayPauseDrawableState(); - updateRepeatState(); - updateShuffleState(); - updateSong(); - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - progressViewUpdateHelper = new MusicProgressViewUpdateHelper(this); - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - unbinder.unbind(); - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_simple_controls_fragment, container, false); - unbinder = ButterKnife.bind(this, view); - return view; - } - - @Override - public void onResume() { - super.onResume(); - progressViewUpdateHelper.start(); - } - - @Override - public void onPause() { - super.onPause(); - progressViewUpdateHelper.stop(); - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - setUpMusicControllers(); - volumeContainer.setVisibility( - PreferenceUtil.getInstance().getVolumeToggle() ? View.VISIBLE : View.GONE); - } - - private void setUpMusicControllers() { - setUpPlayPauseFab(); - setUpPrevNext(); - setUpRepeatButton(); - setUpShuffleButton(); - setUpProgressSlider(); - } - - private void setUpPrevNext() { - updatePrevNextColor(); - nextButton.setOnClickListener(v -> MusicPlayerRemote.playNextSong()); - prevButton.setOnClickListener(v -> MusicPlayerRemote.back()); - } - - private void updatePrevNextColor() { - nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - prevButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - } - - private void setUpShuffleButton() { - shuffleButton.setOnClickListener(v -> MusicPlayerRemote.toggleShuffleMode()); - } - - @Override - protected void updateShuffleState() { - switch (MusicPlayerRemote.getShuffleMode()) { - case MusicService.SHUFFLE_MODE_SHUFFLE: - shuffleButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - default: - shuffleButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - } - } - - private void setUpRepeatButton() { - repeatButton.setOnClickListener(v -> MusicPlayerRemote.cycleRepeatMode()); - } - - @Override - protected void updateRepeatState() { - switch (MusicPlayerRemote.getRepeatMode()) { - case MusicService.REPEAT_MODE_NONE: - repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp); - repeatButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - case MusicService.REPEAT_MODE_ALL: - repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp); - repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - case MusicService.REPEAT_MODE_THIS: - repeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp); - repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - } - } - - private void updateSong() { - Song song = MusicPlayerRemote.getCurrentSong(); - title.setText(song.title); - text.setText(song.artistName); - } - - @Override - public void onPlayingMetaChanged() { - super.onPlayingMetaChanged(); - updateSong(); - - } - - @Override - protected void setUpProgressSlider() { - - } - - @Override - protected void show() { - playPauseFab.animate() - .scaleX(1f) - .scaleY(1f) - .rotation(360f) - .setInterpolator(new DecelerateInterpolator()) - .start(); - } - - @Override - protected void hide() { - if (playPauseFab != null) { - playPauseFab.setScaleX(0f); - playPauseFab.setScaleY(0f); - playPauseFab.setRotation(0f); - } - } - - - public void showBouceAnimation() { - playPauseFab.clearAnimation(); - - playPauseFab.setScaleX(0.9f); - playPauseFab.setScaleY(0.9f); - playPauseFab.setVisibility(View.VISIBLE); - playPauseFab.setPivotX(playPauseFab.getWidth() / 2); - playPauseFab.setPivotY(playPauseFab.getHeight() / 2); - - playPauseFab.animate() - .setDuration(200) - .setInterpolator(new DecelerateInterpolator()) - .scaleX(1.1f) - .scaleY(1.1f) - .withEndAction(() -> playPauseFab.animate() - .setDuration(200) - .setInterpolator(new AccelerateInterpolator()) - .scaleX(1f) - .scaleY(1f) - .alpha(1f) - .start()) - .start(); - } - - @OnClick(R.id.player_play_pause_button) - void showAnimation() { - if (MusicPlayerRemote.isPlaying()) { - MusicPlayerRemote.pauseSong(); - } else { - MusicPlayerRemote.resumePlaying(); - } - showBouceAnimation(); - } - - @Override - public void onUpdateProgressViews(int progress, int total) { - songCurrentProgress - .setText(String.format("%s / %s", MusicUtil.getReadableDurationString(progress), - MusicUtil.getReadableDurationString(total))); - } - - @Override - public void setDark(int dark) { - int color = ATHUtil.resolveColor(getActivity(), android.R.attr.colorBackground); - if (ColorUtil.isColorLight(color)) { - lastPlaybackControlsColor = MaterialValueHelper - .getSecondaryTextColor(getActivity(), true); - lastDisabledPlaybackControlsColor = MaterialValueHelper - .getSecondaryDisabledTextColor(getActivity(), true); - } else { - lastPlaybackControlsColor = MaterialValueHelper - .getPrimaryTextColor(getActivity(), false); - lastDisabledPlaybackControlsColor = MaterialValueHelper - .getPrimaryDisabledTextColor(getActivity(), false); - } - - int finalColor = PreferenceUtil.getInstance().getAdaptiveColor() ? dark : ThemeStore.accentColor(getContext()); - text.setTextColor(finalColor); - TintHelper.setTintAuto(playPauseFab, MaterialValueHelper.getPrimaryTextColor(getContext(), ColorUtil.isColorLight(finalColor)), false); - TintHelper.setTintAuto(playPauseFab, finalColor, true); - - - updateRepeatState(); - updateShuffleState(); - updatePrevNextColor(); - } - - - private void setUpPlayPauseFab() { - playPauseFab.setOnClickListener(new PlayPauseButtonOnClickHandler()); - } - - protected void updatePlayPauseDrawableState() { - if (MusicPlayerRemote.isPlaying()) { - playPauseFab.setImageResource(R.drawable.ic_pause_white_24dp); - } else { - playPauseFab.setImageResource(R.drawable.ic_play_arrow_white_24dp); - } - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/simple/SimplePlayerFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/simple/SimplePlayerFragment.java deleted file mode 100644 index 302ae16a..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/simple/SimplePlayerFragment.java +++ /dev/null @@ -1,130 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.player.simple; - -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.widget.Toolbar; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.Unbinder; -import code.name.monkey.appthemehelper.util.ATHUtil; -import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerFragment; -import code.name.monkey.retromusic.ui.fragments.player.PlayerAlbumCoverFragment; - -/** - * @author Hemanth S (h4h13). - */ - -public class SimplePlayerFragment extends AbsPlayerFragment implements - PlayerAlbumCoverFragment.Callbacks { - - @BindView(R.id.player_toolbar) - Toolbar toolbar; - - private Unbinder unbinder; - private SimplePlaybackControlsFragment simplePlaybackControlsFragment; - private int lastColor; - - - @Override - public void onDestroyView() { - super.onDestroyView(); - unbinder.unbind(); - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_simple_player, container, false); - unbinder = ButterKnife.bind(this, view); - return view; - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - setUpSubFragments(); - setUpPlayerToolbar(); - } - - private void setUpSubFragments() { - PlayerAlbumCoverFragment playerAlbumCoverFragment = (PlayerAlbumCoverFragment) - getChildFragmentManager().findFragmentById(R.id.player_album_cover_fragment); - playerAlbumCoverFragment.setCallbacks(this); - simplePlaybackControlsFragment = (SimplePlaybackControlsFragment) - getChildFragmentManager().findFragmentById(R.id.playback_controls_fragment); - - } - - @Override - public int getPaletteColor() { - return lastColor; - } - - @Override - public void onShow() { - simplePlaybackControlsFragment.show(); - } - - @Override - public void onHide() { - simplePlaybackControlsFragment.hide(); - } - - @Override - public boolean onBackPressed() { - return false; - } - - @Override - public Toolbar getToolbar() { - return toolbar; - } - - @Override - public int toolbarIconColor() { - return ATHUtil.resolveColor(getContext(), R.attr.iconColor); - } - - @Override - public void onColorChanged(int color) { - lastColor = color; - getCallbacks().onPaletteColorChanged(); - simplePlaybackControlsFragment.setDark(color); - ToolbarContentTintHelper.colorizeToolbar(toolbar, - ATHUtil.resolveColor(getContext(), R.attr.iconColor), getActivity()); - - } - - @Override - public void onFavoriteToggled() { - toggleFavorite(MusicPlayerRemote.getCurrentSong()); - } - - @Override - protected void toggleFavorite(Song song) { - super.toggleFavorite(song); - if (song.id == MusicPlayerRemote.getCurrentSong().id) { - updateIsFavorite(); - } - } - - private void setUpPlayerToolbar() { - toolbar.inflateMenu(R.menu.menu_player); - toolbar.setNavigationOnClickListener(v -> getActivity().onBackPressed()); - toolbar.setOnMenuItemClickListener(this); - - ToolbarContentTintHelper.colorizeToolbar(toolbar, - ATHUtil.resolveColor(getContext(), R.attr.iconColor), - getActivity()); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/MainSettingsFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/MainSettingsFragment.java index 0843b248..20b352a4 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/MainSettingsFragment.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/MainSettingsFragment.java @@ -151,7 +151,7 @@ public class MainSettingsFragment extends Fragment { .setQuality(75) .setCompressFormat(Bitmap.CompressFormat.WEBP) .compressToBitmapAsFlowable( - new File(PreferenceUtil.getInstance().getProfileImage(), USER_PROFILE)) + new File(PreferenceUtil.getInstance().getProfileImage(),USER_PROFILE)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(userImageBottom::setImageBitmap, diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/NotificationSettingsFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/NotificationSettingsFragment.java index 1739f4b5..6eec07ab 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/NotificationSettingsFragment.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/NotificationSettingsFragment.java @@ -25,7 +25,7 @@ public class NotificationSettingsFragment extends AbsSettingsFragment { // Save preference PreferenceUtil.getInstance().setClassicNotification((Boolean) newValue); - final MusicService service = MusicPlayerRemote.musicService; + final MusicService service = MusicPlayerRemote.INSTANCE.getMusicService(); if (service != null) { service.initNotification(); service.updateNotification(); diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/NowPlayingSettingsFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/NowPlayingSettingsFragment.java index cf3b8805..ee67454f 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/NowPlayingSettingsFragment.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/NowPlayingSettingsFragment.java @@ -25,7 +25,7 @@ public class NowPlayingSettingsFragment extends AbsSettingsFragment implements final TwoStatePreference carouselEffect = (TwoStatePreference) findPreference("carousel_effect"); carouselEffect.setOnPreferenceChangeListener((preference, newValue) -> { - if ((Boolean) newValue && !RetroApplication.isProVersion()) { + if ((Boolean) newValue && !RetroApplication.Companion.isProVersion()) { showProToastAndNavigate(getActivity().getString(R.string.pref_title_toggle_carousel_effect)); return false; } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/PersonaizeSettingsFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/PersonaizeSettingsFragment.java index 0bb677f3..39dc558d 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/PersonaizeSettingsFragment.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/PersonaizeSettingsFragment.java @@ -16,7 +16,7 @@ public class PersonaizeSettingsFragment extends AbsSettingsFragment implements S public void invalidateSettings() { final TwoStatePreference cornerWindow = (TwoStatePreference) findPreference("corner_window"); cornerWindow.setOnPreferenceChangeListener((preference, newValue) -> { - if ((Boolean) newValue && !RetroApplication.isProVersion()) { + if ((Boolean) newValue && !RetroApplication.Companion.isProVersion()) { showProToastAndNavigate(getActivity().getString(R.string.pref_title_round_corners)); return false; } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/ThemeSettingsFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/ThemeSettingsFragment.java index 5f6cc4d2..2a4e0095 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/ThemeSettingsFragment.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/ThemeSettingsFragment.java @@ -3,19 +3,18 @@ package code.name.monkey.retromusic.ui.fragments.settings; import android.graphics.Color; import android.os.Build; import android.os.Bundle; -import androidx.core.content.ContextCompat; -import androidx.preference.Preference; -import androidx.preference.TwoStatePreference; import com.afollestad.materialdialogs.color.ColorChooserDialog; +import androidx.core.content.ContextCompat; +import androidx.preference.Preference; +import androidx.preference.TwoStatePreference; import code.name.monkey.appthemehelper.ThemeStore; import code.name.monkey.appthemehelper.common.prefs.supportv7.ATEColorPreference; import code.name.monkey.appthemehelper.util.ColorUtil; import code.name.monkey.appthemehelper.util.VersionUtils; import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.RetroApplication; -import code.name.monkey.retromusic.appshortcuts.DynamicShortcutManager; import code.name.monkey.retromusic.ui.activities.SettingsActivity; import code.name.monkey.retromusic.util.PreferenceUtil; @@ -49,7 +48,7 @@ public class ThemeSettingsFragment extends AbsSettingsFragment { generalTheme.setOnPreferenceChangeListener((preference, newValue) -> { String theme = (String) newValue; - if (theme.equals("color") && !RetroApplication.isProVersion()) { + if (theme.equals("color") && !RetroApplication.Companion.isProVersion()) { primaryColorPref.setVisible(false); showProToastAndNavigate("Color theme"); return false; @@ -81,7 +80,6 @@ public class ThemeSettingsFragment extends AbsSettingsFragment { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { getActivity().setTheme(PreferenceUtil.getThemeResFromPrefValue(theme)); - new DynamicShortcutManager(getActivity()).updateDynamicShortcuts(); } getActivity().recreate(); //invalidateSettings(); @@ -111,9 +109,6 @@ public class ThemeSettingsFragment extends AbsSettingsFragment { colorAppShortcuts.setOnPreferenceChangeListener((preference, newValue) -> { // Save preference PreferenceUtil.getInstance().setColoredAppShortcuts((Boolean) newValue); - // Update app shortcuts - new DynamicShortcutManager(getActivity()).updateDynamicShortcuts(); - return true; }); } diff --git a/app/src/main/java/code/name/monkey/retromusic/util/CustomArtistImageUtil.java b/app/src/main/java/code/name/monkey/retromusic/util/CustomArtistImageUtil.java index 752b4871..34406ea7 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/CustomArtistImageUtil.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/CustomArtistImageUtil.java @@ -7,7 +7,6 @@ import android.graphics.Bitmap; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.AsyncTask; -import androidx.annotation.NonNull; import android.widget.Toast; import com.bumptech.glide.Glide; @@ -22,6 +21,7 @@ import java.io.IOException; import java.io.OutputStream; import java.util.Locale; +import androidx.annotation.NonNull; import code.name.monkey.retromusic.RetroApplication; import code.name.monkey.retromusic.model.Artist; @@ -55,12 +55,12 @@ public class CustomArtistImageUtil { } public static File getFile(Artist artist) { - File dir = new File(RetroApplication.getInstance().getFilesDir(), FOLDER_NAME); + File dir = new File(RetroApplication.Companion.getInstance().getFilesDir(), FOLDER_NAME); return new File(dir, getFileName(artist)); } public void setCustomArtistImage(final Artist artist, Uri uri) { - Glide.with(RetroApplication.getInstance()) + Glide.with(RetroApplication.Companion.getInstance()) .load(uri) .asBitmap() .diskCacheStrategy(DiskCacheStrategy.NONE) @@ -70,7 +70,7 @@ public class CustomArtistImageUtil { public void onLoadFailed(Exception e, Drawable errorDrawable) { super.onLoadFailed(e, errorDrawable); e.printStackTrace(); - Toast.makeText(RetroApplication.getInstance(), e.toString(), Toast.LENGTH_LONG).show(); + Toast.makeText(RetroApplication.Companion.getInstance(), e.toString(), Toast.LENGTH_LONG).show(); } @SuppressLint("StaticFieldLeak") @@ -80,7 +80,7 @@ public class CustomArtistImageUtil { @SuppressLint("ApplySharedPref") @Override protected Void doInBackground(Void... params) { - File dir = new File(RetroApplication.getInstance().getFilesDir(), FOLDER_NAME); + File dir = new File(RetroApplication.Companion.getInstance().getFilesDir(), FOLDER_NAME); if (!dir.exists()) { if (!dir.mkdirs()) { // create the folder return null; @@ -94,13 +94,13 @@ public class CustomArtistImageUtil { succesful = ImageUtil.resizeBitmap(resource, 2048).compress(Bitmap.CompressFormat.JPEG, 100, os); os.close(); } catch (IOException e) { - Toast.makeText(RetroApplication.getInstance(), e.toString(), Toast.LENGTH_LONG).show(); + Toast.makeText(RetroApplication.Companion.getInstance(), e.toString(), Toast.LENGTH_LONG).show(); } if (succesful) { mPreferences.edit().putBoolean(getFileName(artist), true).commit(); - ArtistSignatureUtil.getInstance(RetroApplication.getInstance()).updateArtistSignature(artist.getName()); - RetroApplication.getInstance().getContentResolver().notifyChange(Uri.parse("content://media"), null); // trigger media store changed to force artist image reload + ArtistSignatureUtil.getInstance(RetroApplication.Companion.getInstance()).updateArtistSignature(artist.getName()); + RetroApplication.Companion.getInstance().getContentResolver().notifyChange(Uri.parse("content://media"), null); // trigger media store changed to force artist image reload } return null; } @@ -116,8 +116,8 @@ public class CustomArtistImageUtil { @Override protected Void doInBackground(Void... params) { mPreferences.edit().putBoolean(getFileName(artist), false).commit(); - ArtistSignatureUtil.getInstance(RetroApplication.getInstance()).updateArtistSignature(artist.getName()); - RetroApplication.getInstance().getContentResolver().notifyChange(Uri.parse("content://media"), null); // trigger media store changed to force artist image reload + ArtistSignatureUtil.getInstance(RetroApplication.Companion.getInstance()).updateArtistSignature(artist.getName()); + RetroApplication.Companion.getInstance().getContentResolver().notifyChange(Uri.parse("content://media"), null); // trigger media store changed to force artist image reload File file = getFile(artist); if (!file.exists()) { diff --git a/app/src/main/java/code/name/monkey/retromusic/util/FileUtil.java b/app/src/main/java/code/name/monkey/retromusic/util/FileUtil.java index 103df288..fae4f13f 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/FileUtil.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/FileUtil.java @@ -47,7 +47,7 @@ public final class FileUtil { @NonNull public static Observable> matchFilesWithMediaStore(@NonNull Context context, @Nullable List files) { - return SongLoader.Companion.getSongs(makeSongCursor(context, files)); + return SongLoader.INSTANCE.getSongs(makeSongCursor(context, files)); } public static String safeGetCanonicalPath(File file) { @@ -75,7 +75,7 @@ public final class FileUtil { } } - Cursor songCursor = SongLoader.Companion.makeSongCursor(context, selection, selection == null ? null : paths); + Cursor songCursor = SongLoader.INSTANCE.makeSongCursor(context, selection, selection == null ? null : paths); return songCursor == null ? null : new SortedCursor(songCursor, paths, MediaStore.Audio.AudioColumns.DATA); diff --git a/app/src/main/java/code/name/monkey/retromusic/util/MusicUtil.java b/app/src/main/java/code/name/monkey/retromusic/util/MusicUtil.java index 1c082beb..bbbf7e77 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/MusicUtil.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/MusicUtil.java @@ -65,7 +65,7 @@ public class MusicUtil { .putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName(), - new File(song.data))) + new File(song.getData()))) .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) .setType("audio/*"); } catch (IllegalArgumentException e) { @@ -142,7 +142,7 @@ public class MusicUtil { long duration = 0; for (int i = 0; i < songs.size(); i++) { - duration += songs.get(i).duration; + duration += songs.get(i).getDuration(); } return songCount + " " + songString + " • " + MusicUtil.getReadableDurationString(duration); @@ -207,7 +207,7 @@ public class MusicUtil { final StringBuilder selection = new StringBuilder(); selection.append(BaseColumns._ID + " IN ("); for (int i = 0; i < songs.size(); i++) { - selection.append(songs.get(i).id); + selection.append(songs.get(i).getId()); if (i < songs.size() - 1) { selection.append(","); } @@ -224,8 +224,8 @@ public class MusicUtil { cursor.moveToFirst(); while (!cursor.isAfterLast()) { final int id = cursor.getInt(0); - Song song = SongLoader.Companion.getSong(activity, id).blockingFirst(); - MusicPlayerRemote.removeFromQueue(song); + Song song = SongLoader.INSTANCE.getSong(activity, id).blockingFirst(); + MusicPlayerRemote.INSTANCE.removeFromQueue(song); cursor.moveToNext(); } @@ -271,7 +271,7 @@ public class MusicUtil { public static String getLyrics(Song song) { String lyrics = null; - File file = new File(song.data); + File file = new File(song.getData()); try { lyrics = AudioFileIO.read(file).getTagOrCreateDefault().getFirst(FieldKey.LYRICS); @@ -286,7 +286,7 @@ public class MusicUtil { if (dir != null && dir.exists() && dir.isDirectory()) { String format = ".*%s.*\\.(lrc|txt)"; String filename = Pattern.quote(FileUtil.stripExtension(file.getName())); - String songtitle = Pattern.quote(song.title); + String songtitle = Pattern.quote(song.getTitle()); final ArrayList patterns = new ArrayList<>(); patterns.add(Pattern.compile(String.format(format, filename), @@ -341,11 +341,11 @@ public class MusicUtil { } private static Observable getFavoritesPlaylist(@NonNull final Context context) { - return PlaylistLoader.getPlaylist(context, context.getString(R.string.favorites)); + return PlaylistLoader.INSTANCE.getPlaylist(context, context.getString(R.string.favorites)); } private static Observable getOrCreateFavoritesPlaylist(@NonNull final Context context) { - return PlaylistLoader.getPlaylist(context, + return PlaylistLoader.INSTANCE.getPlaylist(context, PlaylistsUtil.createPlaylist(context, context.getString(R.string.favorites))); } @@ -359,7 +359,7 @@ public class MusicUtil { //getFavoritesPlaylist(context).blockingFirst().id.subscribe(MusicUtil::setPlaylist); //return PlaylistsUtil.doPlaylistContains(context, getFavoritesPlaylist(context).blockingFirst().id, song.id); return PlaylistsUtil - .doPlaylistContains(context, getFavoritesPlaylist(context).blockingFirst().id, song.id); + .doPlaylistContains(context, getFavoritesPlaylist(context).blockingFirst().id, song.getId()); } public static boolean isArtistNameUnknown(@Nullable String artistName) { @@ -401,7 +401,7 @@ public class MusicUtil { public static long getTotalDuration(@NonNull final Context context, @NonNull List songs) { long duration = 0; for (int i = 0; i < songs.size(); i++) { - duration += songs.get(i).duration; + duration += songs.get(i).getDuration(); } return duration; } diff --git a/app/src/main/java/code/name/monkey/retromusic/util/NavigationUtil.java b/app/src/main/java/code/name/monkey/retromusic/util/NavigationUtil.java index 5308b169..44ad3fcb 100755 --- a/app/src/main/java/code/name/monkey/retromusic/util/NavigationUtil.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/NavigationUtil.java @@ -18,12 +18,12 @@ import code.name.monkey.retromusic.helper.MusicPlayerRemote; import code.name.monkey.retromusic.model.Genre; import code.name.monkey.retromusic.model.Playlist; import code.name.monkey.retromusic.ui.activities.AboutActivity; +import code.name.monkey.retromusic.ui.activities.AlbumDetailsActivity; import code.name.monkey.retromusic.ui.activities.ArtistDetailActivity; import code.name.monkey.retromusic.ui.activities.EqualizerActivity; import code.name.monkey.retromusic.ui.activities.GenreDetailsActivity; import code.name.monkey.retromusic.ui.activities.LicenseActivity; import code.name.monkey.retromusic.ui.activities.LyricsActivity; -import code.name.monkey.retromusic.ui.activities.NowPayingActivity; import code.name.monkey.retromusic.ui.activities.PlayingQueueActivity; import code.name.monkey.retromusic.ui.activities.PlaylistDetailActivity; import code.name.monkey.retromusic.ui.activities.ProVersionActivity; @@ -32,8 +32,6 @@ import code.name.monkey.retromusic.ui.activities.SettingsActivity; import code.name.monkey.retromusic.ui.activities.SupportDevelopmentActivity; import code.name.monkey.retromusic.ui.activities.UserInfoActivity; import code.name.monkey.retromusic.ui.activities.WhatsNewActivity; -import code.name.monkey.retromusic.ui.activities.AlbumDetailsActivity; -import code.name.monkey.retromusic.ui.activities.base.AbsSlidingMusicPanelActivity; import static code.name.monkey.retromusic.Constants.RATE_ON_GOOGLE_PLAY; import static code.name.monkey.retromusic.ui.activities.GenreDetailsActivity.EXTRA_GENRE_ID; @@ -75,7 +73,7 @@ public class NavigationUtil { } private static void stockEqalizer(@NonNull Activity activity) { - final int sessionId = MusicPlayerRemote.getAudioSessionId(); + final int sessionId = MusicPlayerRemote.INSTANCE.getAudioSessionId(); if (sessionId == AudioEffect.ERROR_BAD_VALUE) { Toast.makeText(activity, activity.getResources().getString(R.string.no_audio_ID), Toast.LENGTH_LONG).show(); @@ -149,12 +147,11 @@ public class NavigationUtil { } public static void gotoNowPlayingActivity(Context context, @Nullable Pair... sharedElements) { - ActivityCompat.startActivity(context, new Intent(context, NowPayingActivity.class), - ActivityOptionsCompat.makeSceneTransitionAnimation((Activity) context, sharedElements).toBundle()); + //ActivityCompat.startActivity(context, new Intent(context, NowPayingActivity.class), ActivityOptionsCompat.makeSceneTransitionAnimation((Activity) context, sharedElements).toBundle()); } public static void gotoNowPlaying(Activity activity) { - ActivityCompat.startActivity(activity, new Intent(activity, NowPayingActivity.class),null); + //ActivityCompat.startActivity(activity, new Intent(activity, NowPayingActivity.class),null); } } diff --git a/app/src/main/java/code/name/monkey/retromusic/util/PlaylistsUtil.java b/app/src/main/java/code/name/monkey/retromusic/util/PlaylistsUtil.java index 920224f4..c016d6de 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/PlaylistsUtil.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/PlaylistsUtil.java @@ -149,7 +149,7 @@ public class PlaylistsUtil { for (int i = 0; i < len; i++) { contentValues[i] = new ContentValues(); contentValues[i].put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, base + offset + i); - contentValues[i].put(MediaStore.Audio.Playlists.Members.AUDIO_ID, songs.get(offset + i).id); + contentValues[i].put(MediaStore.Audio.Playlists.Members.AUDIO_ID, songs.get(offset + i).getId()); } return contentValues; } @@ -158,7 +158,7 @@ public class PlaylistsUtil { Uri uri = MediaStore.Audio.Playlists.Members.getContentUri( "external", playlistId); String selection = MediaStore.Audio.Playlists.Members.AUDIO_ID + " =?"; - String[] selectionArgs = new String[]{String.valueOf(song.id)}; + String[] selectionArgs = new String[]{String.valueOf(song.getId())}; try { context.getContentResolver().delete(uri, selection, selectionArgs); @@ -244,7 +244,7 @@ public class PlaylistsUtil { } public static Observable savePlaylist(Context context, Playlist playlist) { - return M3UWriter.write(context, new File(Environment.getExternalStorageDirectory(), "Playlists"), playlist); + return M3UWriter.Companion.write(context, new File(Environment.getExternalStorageDirectory(), "Playlists"), playlist); } } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/util/PreferenceUtil.java b/app/src/main/java/code/name/monkey/retromusic/util/PreferenceUtil.java index 2697890e..9b93568b 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/PreferenceUtil.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/PreferenceUtil.java @@ -122,7 +122,7 @@ public final class PreferenceUtil { public static PreferenceUtil getInstance() { if (sInstance == null) { - sInstance = new PreferenceUtil(RetroApplication.getContext()); + sInstance = new PreferenceUtil(RetroApplication.Companion.getContext()); } return sInstance; } @@ -145,7 +145,7 @@ public final class PreferenceUtil { } public final String getArtistSortOrder() { - return mPreferences.getString(ARTIST_SORT_ORDER, SortOrder.ArtistSortOrder.ARTIST_A_Z); + return mPreferences.getString(ARTIST_SORT_ORDER, SortOrder.ArtistSortOrder.Companion.getARTIST_A_Z()); } public void setArtistSortOrder(final String sortOrder) { @@ -155,7 +155,7 @@ public final class PreferenceUtil { } public final String getArtistSongSortOrder() { - return mPreferences.getString(ARTIST_SONG_SORT_ORDER, SortOrder.ArtistSongSortOrder.SONG_A_Z); + return mPreferences.getString(ARTIST_SONG_SORT_ORDER, SortOrder.ArtistSongSortOrder.Companion.getSONG_A_Z()); } public final boolean isHomeBanner() { @@ -164,11 +164,11 @@ public final class PreferenceUtil { public final String getArtistAlbumSortOrder() { return mPreferences - .getString(ARTIST_ALBUM_SORT_ORDER, SortOrder.ArtistAlbumSortOrder.ALBUM_YEAR); + .getString(ARTIST_ALBUM_SORT_ORDER, SortOrder.ArtistAlbumSortOrder.Companion.getALBUM_YEAR()); } public final String getAlbumSortOrder() { - return mPreferences.getString(ALBUM_SORT_ORDER, SortOrder.AlbumSortOrder.ALBUM_A_Z); + return mPreferences.getString(ALBUM_SORT_ORDER, SortOrder.AlbumSortOrder.Companion.getALBUM_A_Z()); } public void setAlbumSortOrder(final String sortOrder) { @@ -179,11 +179,11 @@ public final class PreferenceUtil { public final String getAlbumSongSortOrder() { return mPreferences - .getString(ALBUM_SONG_SORT_ORDER, SortOrder.AlbumSongSortOrder.SONG_TRACK_LIST); + .getString(ALBUM_SONG_SORT_ORDER, SortOrder.AlbumSongSortOrder.Companion.getSONG_TRACK_LIST()); } public final String getSongSortOrder() { - return mPreferences.getString(SONG_SORT_ORDER, SortOrder.SongSortOrder.SONG_A_Z); + return mPreferences.getString(SONG_SORT_ORDER, SortOrder.SongSortOrder.Companion.getSONG_A_Z()); } public void setSongSortOrder(final String sortOrder) { @@ -193,7 +193,7 @@ public final class PreferenceUtil { } public final String getGenreSortOrder() { - return mPreferences.getString(GENRE_SORT_ORDER, SortOrder.GenreSortOrder.GENRE_A_Z); + return mPreferences.getString(GENRE_SORT_ORDER, SortOrder.GenreSortOrder.Companion.getGENRE_A_Z()); } public boolean isScreenOnEnabled() { @@ -327,7 +327,7 @@ public final class PreferenceUtil { return nowPlayingScreen; } } - return NowPlayingScreen.NORMAL; + return NowPlayingScreen.ADAPTIVE; } @SuppressLint("CommitPrefEdits") @@ -632,7 +632,7 @@ public final class PreferenceUtil { public String getAlbumDetailSongSortOrder() { return mPreferences - .getString(ALBUM_DETAIL_SONG_SORT_ORDER, SortOrder.AlbumSongSortOrder.SONG_TRACK_LIST); + .getString(ALBUM_DETAIL_SONG_SORT_ORDER, SortOrder.AlbumSongSortOrder.Companion.getSONG_TRACK_LIST()); } public void setAlbumDetailSongSortOrder(String sortOrder) { @@ -643,7 +643,7 @@ public final class PreferenceUtil { public String getArtistDetailSongSortOrder() { return mPreferences - .getString(ARTIST_DETAIL_SONG_SORT_ORDER, SortOrder.ArtistSongSortOrder.SONG_A_Z); + .getString(ARTIST_DETAIL_SONG_SORT_ORDER, SortOrder.ArtistSongSortOrder.Companion.getSONG_A_Z()); } public void setArtistDetailSongSortOrder(String sortOrder) { diff --git a/app/src/main/java/code/name/monkey/retromusic/util/RetroUtil.java b/app/src/main/java/code/name/monkey/retromusic/util/RetroUtil.java index 30cb0e0a..01bfe30b 100755 --- a/app/src/main/java/code/name/monkey/retromusic/util/RetroUtil.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/RetroUtil.java @@ -84,11 +84,11 @@ public class RetroUtil { } public static boolean isTablet() { - return RetroApplication.getContext().getResources().getConfiguration().smallestScreenWidthDp >= 600; + return RetroApplication.Companion.getContext().getResources().getConfiguration().smallestScreenWidthDp >= 600; } public static boolean isLandscape() { - return RetroApplication.getContext().getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; + return RetroApplication.Companion.getContext().getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; } @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) @@ -199,8 +199,8 @@ public class RetroUtil { public static Drawable getTintedDrawable(@DrawableRes int id) { return TintHelper - .createTintedDrawable(ContextCompat.getDrawable(RetroApplication.getInstance(), id), - ThemeStore.accentColor(RetroApplication.getInstance())); + .createTintedDrawable(ContextCompat.getDrawable(RetroApplication.Companion.getInstance(), id), + ThemeStore.accentColor(RetroApplication.Companion.getInstance())); } public static Bitmap createBitmap(Drawable drawable, float sizeMultiplier) { @@ -297,9 +297,9 @@ public class RetroUtil { public static int getStatusBarHeight() { int result = 0; - int resourceId = RetroApplication.getContext().getResources().getIdentifier("status_bar_height", "dimen", "android"); + int resourceId = RetroApplication.Companion.getContext().getResources().getIdentifier("status_bar_height", "dimen", "android"); if (resourceId > 0) { - result = RetroApplication.getContext().getResources().getDimensionPixelSize(resourceId); + result = RetroApplication.Companion.getContext().getResources().getDimensionPixelSize(resourceId); } return result; } @@ -415,7 +415,7 @@ public class RetroUtil { } public static boolean checkNavigationBarHeight() { - Resources resources = RetroApplication.getContext().getResources(); + Resources resources = RetroApplication.Companion.getContext().getResources(); int orientation = resources.getConfiguration().orientation; if (!hasNavBar(resources)) { return false; diff --git a/app/src/main/java/code/name/monkey/retromusic/util/SystemUtils.java b/app/src/main/java/code/name/monkey/retromusic/util/SystemUtils.java index 62eaf510..2d4b7199 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/SystemUtils.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/SystemUtils.java @@ -38,9 +38,9 @@ public class SystemUtils { public static int getNavigationBarHeight() { int result = 0; - int resourceId = RetroApplication.getContext().getResources().getIdentifier("navigation_bar_height", "dimen", "android"); + int resourceId = RetroApplication.Companion.getContext().getResources().getIdentifier("navigation_bar_height", "dimen", "android"); if (resourceId > 0) { - result = RetroApplication.getContext().getResources().getDimensionPixelSize(resourceId); + result = RetroApplication.Companion.getContext().getResources().getDimensionPixelSize(resourceId); } return result; } diff --git a/app/src/main/java/code/name/monkey/retromusic/views/MetalRecyclerViewPager.java b/app/src/main/java/code/name/monkey/retromusic/views/MetalRecyclerViewPager.java deleted file mode 100644 index ae117ce0..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/views/MetalRecyclerViewPager.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (C) 2017. Alexander Bilchuk - * - * 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 code.name.monkey.retromusic.views; - -import android.content.Context; -import android.content.res.TypedArray; -import android.util.AttributeSet; -import android.util.DisplayMetrics; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.PagerSnapHelper; -import androidx.recyclerview.widget.RecyclerView; -import androidx.recyclerview.widget.SnapHelper; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.ui.adapter.base.MediaEntryViewHolder; -import code.name.monkey.retromusic.util.RetroUtil; - -public class MetalRecyclerViewPager extends RecyclerView { - - private int itemMargin; - - public MetalRecyclerViewPager(Context context) { - super(context); - init(context, null); - } - - public MetalRecyclerViewPager(Context context, @Nullable AttributeSet attrs) { - super(context, attrs); - init(context, attrs); - } - - public MetalRecyclerViewPager(Context context, @Nullable AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - init(context, attrs); - } - - private void init(Context context, @Nullable AttributeSet attrs) { - if (attrs != null) { - TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MetalRecyclerViewPager, 0, 0); - itemMargin = (int) typedArray.getDimension(R.styleable.MetalRecyclerViewPager_itemMargin, 0f); - typedArray.recycle(); - } - - setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)); - SnapHelper snapHelper = new PagerSnapHelper(); - snapHelper.attachToRecyclerView(this); - } - - public void setAdapter(Adapter adapter) { - if (MetalAdapter.class.isInstance(adapter)) { - MetalAdapter metalAdapter = (MetalAdapter) adapter; - metalAdapter.setItemMargin(itemMargin); - metalAdapter.updateDisplayMetrics(); - } else { - throw new IllegalArgumentException("Only MetalAdapter is allowed here"); - } - super.setAdapter(adapter); - } - - public static abstract class MetalAdapter extends RecyclerView.Adapter { - - private DisplayMetrics metrics; - private int itemMargin; - private int itemWidth; - - public MetalAdapter(@NonNull DisplayMetrics metrics) { - this.metrics = metrics; - } - - void setItemMargin(int itemMargin) { - this.itemMargin = itemMargin; - } - - void updateDisplayMetrics() { - if (RetroUtil.isTablet()) { - itemWidth = (metrics.widthPixels / 2) - itemMargin * 3; - } else { - itemWidth = metrics.widthPixels - itemMargin ; - } - } - - @Override - public void onBindViewHolder(@NonNull VH holder, int position) { - int currentItemWidth = itemWidth; - - if (position == 0) { - currentItemWidth += itemMargin; - holder.rootLayout.setPadding(0, 0, 0, 0); - } else if (position == getItemCount() - 1) { - currentItemWidth += itemMargin; - holder.rootLayout.setPadding(0, 0, 0, 0); - } - - int height = holder.rootLayout.getLayoutParams().height; - holder.rootLayout.setLayoutParams(new ViewGroup.LayoutParams(currentItemWidth, height)); - } - - - } - - public static abstract class MetalViewHolder extends MediaEntryViewHolder { - - ViewGroup rootLayout; - - public MetalViewHolder(View itemView) { - super(itemView); - rootLayout = (ViewGroup) itemView.findViewById(R.id.root_layout); - } - } -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/views/MetalRecyclerViewPager.kt b/app/src/main/java/code/name/monkey/retromusic/views/MetalRecyclerViewPager.kt new file mode 100644 index 00000000..958637a1 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/views/MetalRecyclerViewPager.kt @@ -0,0 +1,86 @@ +package code.name.monkey.retromusic.views + +import android.content.Context +import android.util.AttributeSet +import android.util.DisplayMetrics +import android.view.View +import android.view.ViewGroup +import androidx.annotation.NonNull +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.PagerSnapHelper +import androidx.recyclerview.widget.RecyclerView +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.ui.adapter.base.MediaEntryViewHolder +import code.name.monkey.retromusic.util.RetroUtil + +class MetalRecyclerViewPager : RecyclerView { + constructor(context: Context) : super(context) { + init(context, null) + } + + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) { + init(context, attrs) + } + + constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { + init(context, attrs) + } + + private var itemMargin: Int = 0 + + fun init(context: Context, attrs: AttributeSet?) { + val typedArray = context.obtainStyledAttributes(attrs, R.styleable.MetalRecyclerViewPager, 0, 0) + itemMargin = typedArray.getDimension(R.styleable.MetalRecyclerViewPager_itemMargin, 0f).toInt() + typedArray.recycle() + + layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) + val snapHelper = PagerSnapHelper() + snapHelper.attachToRecyclerView(this) + } + + override fun setAdapter(adapter: Adapter<*>?) { + if (adapter is MetalAdapter) { + adapter.setItemMargin(itemMargin) + adapter.updateDisplayMetrics() + } else { + throw IllegalArgumentException("Only MetalAdapter is allowed here") + } + super.setAdapter(adapter) + } + + abstract class MetalAdapter(@NonNull val displayMetrics: DisplayMetrics) : RecyclerView.Adapter() { + private var itemMargin: Int = 0 + private var itemWidth: Int = 0 + + fun setItemMargin(itemMargin: Int) { + this.itemMargin = itemMargin + } + + fun updateDisplayMetrics() { + itemWidth = if (RetroUtil.isTablet()) { + displayMetrics.widthPixels / 2 - itemMargin * 3 + } else { + displayMetrics.widthPixels - itemMargin + } + } + + override fun onBindViewHolder(holder: VH, position: Int) { + var currentItemWidth = itemWidth + + if (position == 0) { + currentItemWidth += itemMargin + holder.rootLayout.setPadding(0, 0, 0, 0) + } else if (position == itemCount - 1) { + currentItemWidth += itemMargin + holder.rootLayout.setPadding(0, 0, 0, 0) + } + + val height = holder.rootLayout.layoutParams.height + holder.rootLayout.layoutParams = ViewGroup.LayoutParams(currentItemWidth, height) + } + } + + abstract class MetalViewHolder(itemView: View) : MediaEntryViewHolder(itemView) { + var rootLayout: ViewGroup = itemView.findViewById(R.id.root_layout) + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/views/TintIconColorToolbar.java b/app/src/main/java/code/name/monkey/retromusic/views/TintIconColorToolbar.java deleted file mode 100644 index eb3d190f..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/views/TintIconColorToolbar.java +++ /dev/null @@ -1,33 +0,0 @@ -package code.name.monkey.retromusic.views; - -import android.content.Context; -import android.graphics.PorterDuff; -import android.graphics.drawable.Drawable; -import android.util.AttributeSet; - -import androidx.annotation.Nullable; -import androidx.appcompat.widget.Toolbar; -import code.name.monkey.appthemehelper.ThemeStore; - -public class TintIconColorToolbar extends Toolbar { - public TintIconColorToolbar(Context context) { - super(context); - } - - public TintIconColorToolbar(Context context, @Nullable AttributeSet attrs) { - super(context, attrs); - } - - public TintIconColorToolbar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - - @Override - public void setNavigationIcon(@Nullable Drawable icon) { - super.setNavigationIcon(icon); - if (icon != null) { - icon.setColorFilter(ThemeStore.accentColor(getContext()), PorterDuff.Mode.SRC_IN); - } - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/views/TintIconColorToolbar.kt b/app/src/main/java/code/name/monkey/retromusic/views/TintIconColorToolbar.kt new file mode 100644 index 00000000..ee5ae245 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/views/TintIconColorToolbar.kt @@ -0,0 +1,22 @@ +package code.name.monkey.retromusic.views + +import android.content.Context +import android.graphics.PorterDuff +import android.graphics.drawable.Drawable +import android.util.AttributeSet +import androidx.appcompat.widget.Toolbar +import code.name.monkey.appthemehelper.ThemeStore + +class TintIconColorToolbar : Toolbar { + constructor(context: Context) : super(context) + + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) + + constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) + + + override fun setNavigationIcon(icon: Drawable?) { + super.setNavigationIcon(icon) + icon?.setColorFilter(ThemeStore.accentColor(context), PorterDuff.Mode.SRC_IN) + } +} diff --git a/app/src/main/res/layout-land/activity_album.xml b/app/src/main/res/layout-land/activity_album.xml index f3cefaa8..a1078fab 100644 --- a/app/src/main/res/layout-land/activity_album.xml +++ b/app/src/main/res/layout-land/activity_album.xml @@ -30,7 +30,7 @@ @@ -16,12 +16,14 @@ @@ -40,7 +42,7 @@ diff --git a/app/src/main/res/layout/activity_whats_new.xml b/app/src/main/res/layout/activity_whats_new.xml index 719c143a..b4086b12 100644 --- a/app/src/main/res/layout/activity_whats_new.xml +++ b/app/src/main/res/layout/activity_whats_new.xml @@ -7,7 +7,7 @@ tools:context="code.name.monkey.retromusic.ui.activities.WhatsNewActivity"> @@ -34,7 +34,7 @@ diff --git a/app/src/main/res/layout/fragment_volume.xml b/app/src/main/res/layout/fragment_volume.xml index 8f160c01..ebe69c00 100755 --- a/app/src/main/res/layout/fragment_volume.xml +++ b/app/src/main/res/layout/fragment_volume.xml @@ -1,14 +1,14 @@ Date: Tue, 4 Dec 2018 21:04:26 +0530 Subject: [PATCH 2/3] toolbar height fix --- .../main/res/layout-land/activity_album.xml | 4 +- .../layout-land/activity_album_tag_editor.xml | 4 +- .../layout-land/activity_artist_details.xml | 14 +- .../res/layout-land/activity_settings.xml | 11 +- .../res/layout-land/fragment_banner_home.xml | 34 +-- .../main/res/layout-land/fragment_blur.xml | 6 +- .../layout-land/fragment_card_blur_player.xml | 2 +- .../res/layout-land/fragment_card_player.xml | 2 +- .../res/layout-land/fragment_color_player.xml | 3 +- .../main/res/layout-land/fragment_home.xml | 72 +++--- .../res/layout-land/fragment_material.xml | 3 +- .../res/layout-land/fragment_plain_player.xml | 5 +- .../main/res/layout-land/fragment_player.xml | 3 +- .../layout-land/fragment_simple_player.xml | 3 +- app/src/main/res/layout-land/pager_item.xml | 2 +- .../res/layout-xlarge-land/activity_album.xml | 4 +- .../activity_artist_details.xml | 14 +- .../fragment_banner_home.xml | 64 ++--- .../res/layout-xlarge-land/fragment_blur.xml | 88 ++----- .../res/layout-xlarge-land/fragment_home.xml | 25 +- .../layout-xlarge-land/fragment_player.xml | 2 +- .../res/layout-xlarge-land/pager_item.xml | 2 +- .../abs_playlists.xml | 4 - .../main/res/layout-xlarge/activity_album.xml | 4 +- .../activity_artist_content.xml | 18 +- .../layout-xlarge/activity_artist_details.xml | 18 +- .../activity_user_info.xml | 2 +- .../layout-xlarge/fragment_banner_home.xml | 110 ++++++-- .../main/res/layout-xlarge/fragment_blur.xml | 6 +- .../main/res/layout-xlarge/fragment_home.xml | 53 ++-- .../fragment_mini_player.xml | 0 .../res/layout-xlarge/fragment_player.xml | 3 +- app/src/main/res/layout-xlarge/pager_item.xml | 2 +- app/src/main/res/layout/activity_about.xml | 6 +- app/src/main/res/layout/activity_album.xml | 4 +- .../main/res/layout/activity_album_small.xml | 173 ------------- .../res/layout/activity_album_style_2.xml | 240 ------------------ .../res/layout/activity_album_tag_editor.xml | 2 +- .../res/layout/activity_artist_content.xml | 12 +- .../res/layout/activity_artist_details.xml | 16 +- app/src/main/res/layout/activity_donation.xml | 2 +- .../main/res/layout/activity_equalizer.xml | 4 +- .../res/layout/activity_error_handler.xml | 2 +- app/src/main/res/layout/activity_license.xml | 2 +- .../layout/activity_main_drawer_layout.xml | 19 +- .../res/layout/activity_playing_queue.xml | 2 +- .../res/layout/activity_playlist_detail.xml | 12 +- .../main/res/layout/activity_pro_version.xml | 2 +- app/src/main/res/layout/activity_settings.xml | 10 +- .../res/layout/activity_song_tag_editor.xml | 4 +- .../main/res/layout/activity_user_info.xml | 16 +- .../main/res/layout/activity_whats_new.xml | 2 +- .../collapsing_floating_action_button.xml | 1 + .../res/layout/dialog_add_to_playlist.xml | 6 +- .../main/res/layout/dialog_file_details.xml | 2 +- app/src/main/res/layout/dialog_file_share.xml | 2 +- ...reate_playlist.xml => dialog_playlist.xml} | 19 +- .../res/layout/dialog_playlist_rename.xml | 5 +- .../layout/dialog_remove_from_playlist.xml | 7 +- .../res/layout/fragment_adaptive_player.xml | 2 +- .../main/res/layout/fragment_album_cover.xml | 6 +- .../main/res/layout/fragment_banner_home.xml | 37 ++- app/src/main/res/layout/fragment_blur.xml | 29 ++- .../res/layout/fragment_card_blur_player.xml | 2 +- .../main/res/layout/fragment_card_player.xml | 2 +- .../main/res/layout/fragment_color_player.xml | 3 +- app/src/main/res/layout/fragment_fit.xml | 3 +- ...fragment_flat_player_playback_controls.xml | 17 +- app/src/main/res/layout/fragment_folder.xml | 18 +- .../main/res/layout/fragment_hmm_player.xml | 4 +- app/src/main/res/layout/fragment_home.xml | 31 +-- app/src/main/res/layout/fragment_library.xml | 9 +- .../main/res/layout/fragment_main_options.xml | 66 ++--- .../res/layout/fragment_main_settings.xml | 63 +---- app/src/main/res/layout/fragment_material.xml | 3 +- .../main/res/layout/fragment_plain_player.xml | 5 +- app/src/main/res/layout/fragment_player.xml | 3 +- .../fragment_simple_controls_fragment.xml | 2 +- .../res/layout/fragment_simple_player.xml | 3 +- .../main/res/layout/home_section_content.xml | 24 +- app/src/main/res/layout/item_album_card.xml | 2 +- app/src/main/res/layout/item_card.xml | 2 +- app/src/main/res/layout/item_card_color.xml | 2 +- app/src/main/res/layout/item_color.xml | 2 +- .../main/res/layout/item_donation_option.xml | 1 - app/src/main/res/layout/item_grid.xml | 2 +- app/src/main/res/layout/item_grid_circle.xml | 2 +- app/src/main/res/layout/item_list.xml | 18 +- .../res/layout/navigation_drawer_header.xml | 6 + app/src/main/res/layout/pager_item.xml | 2 +- 90 files changed, 515 insertions(+), 1015 deletions(-) rename app/src/main/res/{layout-sw600dp => layout-xlarge}/abs_playlists.xml (96%) rename app/src/main/res/{layout-sw600dp => layout-xlarge}/activity_artist_content.xml (86%) rename app/src/main/res/{layout-sw600dp => layout-xlarge}/activity_user_info.xml (98%) rename app/src/main/res/{layout-sw600dp => layout-xlarge}/fragment_mini_player.xml (100%) delete mode 100644 app/src/main/res/layout/activity_album_small.xml delete mode 100644 app/src/main/res/layout/activity_album_style_2.xml rename app/src/main/res/layout/{dialog_create_playlist.xml => dialog_playlist.xml} (86%) create mode 100644 app/src/main/res/layout/navigation_drawer_header.xml diff --git a/app/src/main/res/layout-land/activity_album.xml b/app/src/main/res/layout-land/activity_album.xml index a1078fab..124a9c3f 100644 --- a/app/src/main/res/layout-land/activity_album.xml +++ b/app/src/main/res/layout-land/activity_album.xml @@ -26,7 +26,7 @@ + style="@style/Toolbar" /> - + style="@style/Toolbar" /> @@ -45,13 +44,13 @@ app:layout_behavior="@string/appbar_scrolling_view_behavior"> diff --git a/app/src/main/res/layout-land/fragment_banner_home.xml b/app/src/main/res/layout-land/fragment_banner_home.xml index 11ebac07..a500740b 100644 --- a/app/src/main/res/layout-land/fragment_banner_home.xml +++ b/app/src/main/res/layout-land/fragment_banner_home.xml @@ -2,18 +2,15 @@ - - - - + tools:ignore="MissingPrefix" /> - + @@ -83,8 +83,8 @@ tools:background="@color/md_red_A700"> diff --git a/app/src/main/res/layout-land/fragment_card_blur_player.xml b/app/src/main/res/layout-land/fragment_card_blur_player.xml index 51e28471..565480bc 100644 --- a/app/src/main/res/layout-land/fragment_card_blur_player.xml +++ b/app/src/main/res/layout-land/fragment_card_blur_player.xml @@ -41,7 +41,7 @@ diff --git a/app/src/main/res/layout-land/fragment_color_player.xml b/app/src/main/res/layout-land/fragment_color_player.xml index d0cb7c67..79e1de1e 100644 --- a/app/src/main/res/layout-land/fragment_color_player.xml +++ b/app/src/main/res/layout-land/fragment_color_player.xml @@ -119,8 +119,7 @@ diff --git a/app/src/main/res/layout-land/fragment_home.xml b/app/src/main/res/layout-land/fragment_home.xml index eaa08ee5..1b7a7a3a 100644 --- a/app/src/main/res/layout-land/fragment_home.xml +++ b/app/src/main/res/layout-land/fragment_home.xml @@ -19,18 +19,50 @@ android:layout_width="match_parent" android:layout_height="match_parent"> + + + + + + + + + + + + - - - - - - - - - - - diff --git a/app/src/main/res/layout-land/fragment_material.xml b/app/src/main/res/layout-land/fragment_material.xml index c5c0c10f..67ffead8 100644 --- a/app/src/main/res/layout-land/fragment_material.xml +++ b/app/src/main/res/layout-land/fragment_material.xml @@ -70,8 +70,7 @@ diff --git a/app/src/main/res/layout-land/fragment_plain_player.xml b/app/src/main/res/layout-land/fragment_plain_player.xml index 6a01f762..8aef0523 100644 --- a/app/src/main/res/layout-land/fragment_plain_player.xml +++ b/app/src/main/res/layout-land/fragment_plain_player.xml @@ -62,7 +62,7 @@ diff --git a/app/src/main/res/layout-land/fragment_player.xml b/app/src/main/res/layout-land/fragment_player.xml index fa165fe3..1283ea59 100755 --- a/app/src/main/res/layout-land/fragment_player.xml +++ b/app/src/main/res/layout-land/fragment_player.xml @@ -62,8 +62,7 @@ diff --git a/app/src/main/res/layout-land/fragment_simple_player.xml b/app/src/main/res/layout-land/fragment_simple_player.xml index 0c90a1f8..c0ddd8ea 100644 --- a/app/src/main/res/layout-land/fragment_simple_player.xml +++ b/app/src/main/res/layout-land/fragment_simple_player.xml @@ -73,8 +73,7 @@ diff --git a/app/src/main/res/layout-land/pager_item.xml b/app/src/main/res/layout-land/pager_item.xml index 48c9d60c..3609aa9c 100644 --- a/app/src/main/res/layout-land/pager_item.xml +++ b/app/src/main/res/layout-land/pager_item.xml @@ -48,7 +48,7 @@ + style="@style/Toolbar" /> @@ -92,7 +92,7 @@ @@ -97,7 +97,7 @@ + app:layout_collapseMode="parallax"> - - - + tools:ignore="MissingPrefix" /> - + - - + android:layout_marginStart="128dp" + android:layout_marginEnd="128dp" + app:cardCornerRadius="12dp"> - + - - + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout-xlarge-land/fragment_blur.xml b/app/src/main/res/layout-xlarge-land/fragment_blur.xml index c4c1dac7..e98b5454 100644 --- a/app/src/main/res/layout-xlarge-land/fragment_blur.xml +++ b/app/src/main/res/layout-xlarge-land/fragment_blur.xml @@ -8,7 +8,7 @@ android:focusable="true"> @@ -41,73 +41,41 @@ android:layout_above="@id/toolbar_container" android:orientation="horizontal"> - - - - - - - - - - - - - - - - + android:layout_gravity="center_horizontal" + android:orientation="vertical"> - + android:layout_weight="0"> - + + + + android:layout_weight="1" + tools:background="@color/md_white_1000"> + + + + @@ -119,8 +87,8 @@ tools:background="@color/md_red_A700"> diff --git a/app/src/main/res/layout-xlarge-land/fragment_home.xml b/app/src/main/res/layout-xlarge-land/fragment_home.xml index e409ac6d..db53cbb0 100644 --- a/app/src/main/res/layout-xlarge-land/fragment_home.xml +++ b/app/src/main/res/layout-xlarge-land/fragment_home.xml @@ -21,7 +21,6 @@ android:layout_height="match_parent"> - + android:padding="16dp" + app:srcCompat="@drawable/ic_search_white_24dp" + app:tint="@color/md_white_1000" /> - + android:text="@string/app_name" + android:textColor="@color/md_white_1000" /> diff --git a/app/src/main/res/layout-xlarge-land/pager_item.xml b/app/src/main/res/layout-xlarge-land/pager_item.xml index 48c9d60c..3609aa9c 100644 --- a/app/src/main/res/layout-xlarge-land/pager_item.xml +++ b/app/src/main/res/layout-xlarge-land/pager_item.xml @@ -48,7 +48,7 @@ diff --git a/app/src/main/res/layout-xlarge/activity_album.xml b/app/src/main/res/layout-xlarge/activity_album.xml index 64bb04a2..fad6d301 100644 --- a/app/src/main/res/layout-xlarge/activity_album.xml +++ b/app/src/main/res/layout-xlarge/activity_album.xml @@ -35,7 +35,7 @@ @@ -94,7 +94,7 @@ diff --git a/app/src/main/res/layout-xlarge/activity_artist_details.xml b/app/src/main/res/layout-xlarge/activity_artist_details.xml index d16bc99b..c308a1c9 100644 --- a/app/src/main/res/layout-xlarge/activity_artist_details.xml +++ b/app/src/main/res/layout-xlarge/activity_artist_details.xml @@ -7,7 +7,7 @@ android:layout_height="match_parent"> @@ -97,17 +96,16 @@ - diff --git a/app/src/main/res/layout-sw600dp/activity_user_info.xml b/app/src/main/res/layout-xlarge/activity_user_info.xml similarity index 98% rename from app/src/main/res/layout-sw600dp/activity_user_info.xml rename to app/src/main/res/layout-xlarge/activity_user_info.xml index 941fe121..bd2facf6 100644 --- a/app/src/main/res/layout-sw600dp/activity_user_info.xml +++ b/app/src/main/res/layout-xlarge/activity_user_info.xml @@ -14,7 +14,7 @@ - + android:layout_height="wrap_content" + android:background="@android:color/transparent" + android:elevation="0dp" + app:elevation="0dp"> - + android:background="@android:color/transparent" + app:contentScrim="@android:color/transparent" + app:layout_scrollFlags="scroll|exitUntilCollapsed" + app:statusBarScrim="@color/md_black_1000" + app:titleEnabled="false"> - + android:layout_height="196dp" + app:layout_collapseMode="parallax"> - + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout-xlarge/fragment_blur.xml b/app/src/main/res/layout-xlarge/fragment_blur.xml index 8a8b4517..60a52bb0 100644 --- a/app/src/main/res/layout-xlarge/fragment_blur.xml +++ b/app/src/main/res/layout-xlarge/fragment_blur.xml @@ -8,7 +8,7 @@ android:focusable="true"> @@ -73,8 +73,8 @@ tools:background="@color/md_red_A700"> diff --git a/app/src/main/res/layout-xlarge/fragment_home.xml b/app/src/main/res/layout-xlarge/fragment_home.xml index 789aa438..5bc9820c 100644 --- a/app/src/main/res/layout-xlarge/fragment_home.xml +++ b/app/src/main/res/layout-xlarge/fragment_home.xml @@ -22,48 +22,39 @@ android:layout_height="match_parent"> - + tools:ignore="UnusedAttribute"> - + - + - - - - + + diff --git a/app/src/main/res/layout-xlarge/pager_item.xml b/app/src/main/res/layout-xlarge/pager_item.xml index 6c067a3f..118dcf16 100644 --- a/app/src/main/res/layout-xlarge/pager_item.xml +++ b/app/src/main/res/layout-xlarge/pager_item.xml @@ -48,7 +48,7 @@ diff --git a/app/src/main/res/layout/activity_album.xml b/app/src/main/res/layout/activity_album.xml index 61c21a87..4d4d4727 100755 --- a/app/src/main/res/layout/activity_album.xml +++ b/app/src/main/res/layout/activity_album.xml @@ -40,7 +40,7 @@ @@ -82,7 +82,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_album_style_2.xml b/app/src/main/res/layout/activity_album_style_2.xml deleted file mode 100644 index e5258762..00000000 --- a/app/src/main/res/layout/activity_album_style_2.xml +++ /dev/null @@ -1,240 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_album_tag_editor.xml b/app/src/main/res/layout/activity_album_tag_editor.xml index 204d1012..42bc70a5 100755 --- a/app/src/main/res/layout/activity_album_tag_editor.xml +++ b/app/src/main/res/layout/activity_album_tag_editor.xml @@ -59,7 +59,7 @@ diff --git a/app/src/main/res/layout/activity_artist_content.xml b/app/src/main/res/layout/activity_artist_content.xml index d858129f..3e4dfcf5 100644 --- a/app/src/main/res/layout/activity_artist_content.xml +++ b/app/src/main/res/layout/activity_artist_content.xml @@ -6,14 +6,14 @@ android:orientation="vertical"> diff --git a/app/src/main/res/layout/activity_artist_details.xml b/app/src/main/res/layout/activity_artist_details.xml index fd837a3a..3ab4c98a 100755 --- a/app/src/main/res/layout/activity_artist_details.xml +++ b/app/src/main/res/layout/activity_artist_details.xml @@ -7,12 +7,12 @@ android:layout_height="match_parent"> diff --git a/app/src/main/res/layout/activity_equalizer.xml b/app/src/main/res/layout/activity_equalizer.xml index eefa588b..f04ae09c 100644 --- a/app/src/main/res/layout/activity_equalizer.xml +++ b/app/src/main/res/layout/activity_equalizer.xml @@ -21,7 +21,7 @@ @@ -29,7 +29,7 @@ \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_license.xml b/app/src/main/res/layout/activity_license.xml index d1ba7dbd..2439a90f 100644 --- a/app/src/main/res/layout/activity_license.xml +++ b/app/src/main/res/layout/activity_license.xml @@ -23,7 +23,7 @@ diff --git a/app/src/main/res/layout/activity_main_drawer_layout.xml b/app/src/main/res/layout/activity_main_drawer_layout.xml index deac29bc..1ffcc1ee 100644 --- a/app/src/main/res/layout/activity_main_drawer_layout.xml +++ b/app/src/main/res/layout/activity_main_drawer_layout.xml @@ -1,13 +1,24 @@ - + android:layout_height="match_parent"> - \ No newline at end of file + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_playing_queue.xml b/app/src/main/res/layout/activity_playing_queue.xml index 149449d3..d1862206 100755 --- a/app/src/main/res/layout/activity_playing_queue.xml +++ b/app/src/main/res/layout/activity_playing_queue.xml @@ -23,7 +23,7 @@ diff --git a/app/src/main/res/layout/activity_playlist_detail.xml b/app/src/main/res/layout/activity_playlist_detail.xml index dd236dd0..4b7e426b 100644 --- a/app/src/main/res/layout/activity_playlist_detail.xml +++ b/app/src/main/res/layout/activity_playlist_detail.xml @@ -20,14 +20,14 @@ android:layout_height="match_parent"> @@ -65,7 +65,7 @@ diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml index 668409e8..3760ef42 100755 --- a/app/src/main/res/layout/activity_settings.xml +++ b/app/src/main/res/layout/activity_settings.xml @@ -5,27 +5,27 @@ android:layout_height="match_parent"> @@ -40,7 +40,7 @@ diff --git a/app/src/main/res/layout/activity_song_tag_editor.xml b/app/src/main/res/layout/activity_song_tag_editor.xml index 6cac0df4..937a2814 100755 --- a/app/src/main/res/layout/activity_song_tag_editor.xml +++ b/app/src/main/res/layout/activity_song_tag_editor.xml @@ -34,14 +34,14 @@ @@ -43,19 +43,19 @@ app:cardUseCompatPadding="true"> @@ -75,7 +75,7 @@ android:layout_margin="8dp"> diff --git a/app/src/main/res/layout/collapsing_floating_action_button.xml b/app/src/main/res/layout/collapsing_floating_action_button.xml index 8acb7c0a..c29a7938 100644 --- a/app/src/main/res/layout/collapsing_floating_action_button.xml +++ b/app/src/main/res/layout/collapsing_floating_action_button.xml @@ -4,6 +4,7 @@ xmlns:tools="http://schemas.android.com/tools" android:id="@+id/container" android:layout_width="wrap_content" + android:layout_gravity="center" android:layout_height="wrap_content" app:cardCornerRadius="26dp" app:cardUseCompatPadding="true" diff --git a/app/src/main/res/layout/dialog_add_to_playlist.xml b/app/src/main/res/layout/dialog_add_to_playlist.xml index d2f6f8cc..85626eff 100644 --- a/app/src/main/res/layout/dialog_add_to_playlist.xml +++ b/app/src/main/res/layout/dialog_add_to_playlist.xml @@ -12,13 +12,13 @@ android:orientation="horizontal"> \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_file_details.xml b/app/src/main/res/layout/dialog_file_details.xml index 5d5d2b21..e30b911e 100644 --- a/app/src/main/res/layout/dialog_file_details.xml +++ b/app/src/main/res/layout/dialog_file_details.xml @@ -9,7 +9,7 @@ - - + android:layout_height="match_parent"> + app:cardElevation="8dp" + app:cardUseCompatPadding="true"> - - - - + tools:ignore="MissingPrefix" /> - + @@ -40,19 +40,6 @@ - - - - + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_card_blur_player.xml b/app/src/main/res/layout/fragment_card_blur_player.xml index 24c4581b..54fd0e99 100644 --- a/app/src/main/res/layout/fragment_card_blur_player.xml +++ b/app/src/main/res/layout/fragment_card_blur_player.xml @@ -43,7 +43,7 @@ diff --git a/app/src/main/res/layout/fragment_color_player.xml b/app/src/main/res/layout/fragment_color_player.xml index 6eb67786..422bbd50 100644 --- a/app/src/main/res/layout/fragment_color_player.xml +++ b/app/src/main/res/layout/fragment_color_player.xml @@ -116,8 +116,7 @@ diff --git a/app/src/main/res/layout/fragment_fit.xml b/app/src/main/res/layout/fragment_fit.xml index d31f16c6..b45af0d6 100644 --- a/app/src/main/res/layout/fragment_fit.xml +++ b/app/src/main/res/layout/fragment_fit.xml @@ -48,8 +48,7 @@ diff --git a/app/src/main/res/layout/fragment_flat_player_playback_controls.xml b/app/src/main/res/layout/fragment_flat_player_playback_controls.xml index 45da3457..66473c3c 100644 --- a/app/src/main/res/layout/fragment_flat_player_playback_controls.xml +++ b/app/src/main/res/layout/fragment_flat_player_playback_controls.xml @@ -23,16 +23,16 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" - android:paddingEnd="16dp" - android:paddingStart="16dp"> + android:paddingStart="16dp" + android:paddingEnd="16dp"> + android:paddingStart="16dp" + android:paddingEnd="16dp"> @@ -18,14 +17,14 @@ - + android:text="@string/folders" + tools:ignore="MissingPrefix" /> @@ -65,7 +65,7 @@ app:layout_behavior="@string/appbar_scrolling_view_behavior"> - - - - + android:text="@string/app_name" + tools:ignore="MissingPrefix" /> - + @@ -47,13 +47,6 @@ android:text="@string/app_name" tools:ignore="MissingPrefix" /> - + + + android:orientation="vertical" + android:paddingStart="12dp" + android:paddingEnd="12dp"> + android:padding="0dp" /> - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_main_settings.xml b/app/src/main/res/layout/fragment_main_settings.xml index 2a9dca1f..31436e21 100644 --- a/app/src/main/res/layout/fragment_main_settings.xml +++ b/app/src/main/res/layout/fragment_main_settings.xml @@ -13,56 +13,7 @@ android:orientation="vertical"> - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/fragment_plain_player.xml b/app/src/main/res/layout/fragment_plain_player.xml index 0b78367e..a78847fe 100644 --- a/app/src/main/res/layout/fragment_plain_player.xml +++ b/app/src/main/res/layout/fragment_plain_player.xml @@ -37,7 +37,7 @@ diff --git a/app/src/main/res/layout/fragment_player.xml b/app/src/main/res/layout/fragment_player.xml index e2ca2221..c9f51eb7 100644 --- a/app/src/main/res/layout/fragment_player.xml +++ b/app/src/main/res/layout/fragment_player.xml @@ -65,8 +65,7 @@ diff --git a/app/src/main/res/layout/fragment_simple_controls_fragment.xml b/app/src/main/res/layout/fragment_simple_controls_fragment.xml index 4375c7f8..0804abf2 100644 --- a/app/src/main/res/layout/fragment_simple_controls_fragment.xml +++ b/app/src/main/res/layout/fragment_simple_controls_fragment.xml @@ -22,7 +22,7 @@ diff --git a/app/src/main/res/layout/home_section_content.xml b/app/src/main/res/layout/home_section_content.xml index 56d37e3b..71103b18 100644 --- a/app/src/main/res/layout/home_section_content.xml +++ b/app/src/main/res/layout/home_section_content.xml @@ -7,7 +7,7 @@ android:orientation="vertical"> @@ -32,7 +32,7 @@ @@ -83,7 +83,7 @@ @@ -134,7 +134,7 @@ diff --git a/app/src/main/res/layout/item_album_card.xml b/app/src/main/res/layout/item_album_card.xml index ed085678..ccab06d2 100644 --- a/app/src/main/res/layout/item_album_card.xml +++ b/app/src/main/res/layout/item_album_card.xml @@ -22,7 +22,7 @@ diff --git a/app/src/main/res/layout/item_color.xml b/app/src/main/res/layout/item_color.xml index de687034..530485c9 100644 --- a/app/src/main/res/layout/item_color.xml +++ b/app/src/main/res/layout/item_color.xml @@ -38,7 +38,7 @@ + android:visibility="gone" + tools:text="1" + tools:visibility="visible" /> @@ -75,25 +76,24 @@ android:paddingStart="16dp" android:paddingEnd="16dp"> - + android:textSize="16sp"/> - + android:maxLines="1" /> + + + \ No newline at end of file diff --git a/app/src/main/res/layout/pager_item.xml b/app/src/main/res/layout/pager_item.xml index 1f5d06fa..6d899d9b 100644 --- a/app/src/main/res/layout/pager_item.xml +++ b/app/src/main/res/layout/pager_item.xml @@ -48,7 +48,7 @@ Date: Wed, 5 Dec 2018 09:59:55 +0530 Subject: [PATCH 3/3] Adding now playing themes and KOTLIN conversion --- app/app.iml | 268 ++-- app/build.gradle | 1 - app/src/main/AndroidManifest.xml | 4 +- app/src/main/assets/retro-changelog.html | 2 +- .../{RetroApplication.kt => App.kt} | 5 +- .../dialogs/AddToPlaylistDialog.java | 84 - .../retromusic/dialogs/AddToPlaylistDialog.kt | 68 + .../dialogs/ClearSmartPlaylistDialog.java | 52 - .../dialogs/ClearSmartPlaylistDialog.kt | 45 + .../dialogs/CreatePlaylistDialog.java | 123 -- .../dialogs/CreatePlaylistDialog.kt | 83 + .../dialogs/DeletePlaylistDialog.java | 102 -- .../dialogs/DeletePlaylistDialog.kt | 70 + .../MainOptionsBottomSheetDialogFragment.java | 167 -- .../MainOptionsBottomSheetDialogFragment.kt | 117 ++ .../dialogs/RemoveFromPlaylistDialog.kt | 40 +- .../dialogs/RenamePlaylistDialog.java | 98 -- .../dialogs/RenamePlaylistDialog.kt | 65 + .../retromusic/glide/ArtistGlideRequest.java | 6 +- .../glide/RetroMusicColoredTarget.kt | 1 - .../retromusic/helper/MusicPlayerRemote.kt | 6 +- .../retromusic/helper/menu/GenreMenuHelper.kt | 16 +- .../helper/menu/PlaylistMenuHelper.kt | 6 +- .../monkey/retromusic/loaders/SongLoader.kt | 2 +- .../name/monkey/retromusic/model/Album.kt | 2 +- .../name/monkey/retromusic/model/Genre.java | 86 + .../name/monkey/retromusic/model/Genre.kt | 26 - .../code/name/monkey/retromusic/model/Song.kt | 4 +- .../mvp/presenter/ArtistDetailsPresenter.java | 7 +- .../NowPlayingScreenPreferenceDialog.java | 6 +- .../retromusic/providers/RepositoryImpl.java | 4 +- .../monkey/retromusic/rest/KogouClient.java | 4 +- .../retromusic/service/MultiPlayer.java | 334 ++++ .../monkey/retromusic/service/MultiPlayer.kt | 333 ---- .../retromusic/service/MusicService.java | 1420 +++++++++++++++++ .../monkey/retromusic/service/MusicService.kt | 1247 --------------- .../ui/activities/AlbumDetailsActivity.kt | 103 +- .../ui/activities/ArtistDetailActivity.java | 458 ------ .../ui/activities/ArtistDetailActivity.kt | 363 +++++ .../ui/activities/ErrorHandlerActivity.java | 2 +- .../ui/activities/GenreDetailsActivity.java | 226 --- .../ui/activities/GenreDetailsActivity.kt | 174 ++ .../retromusic/ui/activities/MainActivity.kt | 141 +- .../ui/activities/PlaylistDetailActivity.java | 309 ---- .../ui/activities/PlaylistDetailActivity.kt | 254 +++ .../ui/activities/ProVersionActivity.java | 6 +- .../ui/activities/SettingsActivity.java | 163 -- .../ui/activities/SettingsActivity.kt | 124 ++ .../ui/activities/WhatsNewActivity.kt | 2 +- .../ui/activities/base/AbsBaseActivity.java | 159 -- .../ui/activities/base/AbsBaseActivity.kt | 18 +- .../base/AbsMusicServiceActivity.kt | 4 +- .../base/AbsSlidingMusicPanelActivity.kt | 31 +- .../ui/activities/base/AbsThemeActivity.kt | 15 +- .../ui/adapter/CollageSongAdapter.java | 17 +- .../ui/adapter/song/AbsOffsetSongAdapter.kt | 2 +- .../song/OrderablePlaylistSongAdapter.kt | 2 +- .../ui/fragments/MiniPlayerFragment.kt | 3 +- .../ui/fragments/NowPlayingScreen.java | 14 +- .../ui/fragments/PlayingQueueFragment.java | 148 -- .../retromusic/ui/fragments/VolumeFragment.kt | 20 +- .../base/AbsPlayerControlsFragment.kt | 9 +- .../ui/fragments/base/AbsPlayerFragment.kt | 6 +- .../fragments/mainactivity/GenreFragment.kt | 6 +- .../fragments/mainactivity/LibraryFragment.kt | 6 +- .../mainactivity/folders/FoldersFragment.java | 59 +- .../mainactivity/home/BannerHomeFragment.kt | 225 ++- .../player/PlayerAlbumCoverFragment.kt | 1 - .../player/adaptive/AdaptiveFragment.kt | 29 +- .../AdaptivePlaybackControlsFragment.kt | 81 +- .../blur/BlurPlaybackControlsFragment.kt | 170 +- .../player/blur/BlurPlayerFragment.kt | 89 +- .../player/cardblur/CardBlurFragment.kt | 151 ++ .../CardBlurPlaybackControlsFragment.kt | 222 +++ .../ui/fragments/player/fit/FitFragment.kt | 110 ++ .../player/fit/FitPlaybackControlsFragment.kt | 271 ++++ .../flat/FlatPlaybackControlsFragment.kt | 230 +++ .../player/flat/FlatPlayerFragment.kt | 128 ++ .../settings/MainSettingsFragment.java | 162 -- .../settings/MainSettingsFragment.kt | 54 + .../settings/NowPlayingSettingsFragment.java | 4 +- .../settings/PersonaizeSettingsFragment.java | 4 +- .../settings/ThemeSettingsFragment.java | 4 +- .../util/CustomArtistImageUtil.java | 20 +- .../retromusic/util/NavigationUtil.java | 5 +- .../retromusic/util/PreferenceUtil.java | 6 +- .../monkey/retromusic/util/RetroUtil.java | 20 +- .../monkey/retromusic/util/SystemUtils.java | 6 +- .../views/MaterialButtonTextColor.java | 25 + app/src/main/res/drawable/navigation_item.xml | 5 + .../res/drawable/navigation_view_item.xml | 7 + app/src/main/res/font/circular_std_black.otf | Bin 74500 -> 0 bytes app/src/main/res/font/circular_std_book.otf | Bin 68940 -> 0 bytes app/src/main/res/font/font.xml | 4 +- app/src/main/res/font/sans_bold.ttf | Bin 0 -> 117916 bytes app/src/main/res/font/sans_regular.ttf | Bin 0 -> 119984 bytes .../layout-land/fragment_card_blur_player.xml | 18 +- .../res/layout-land/fragment_card_player.xml | 13 +- .../res/layout-land/fragment_flat_player.xml | 12 +- .../main/res/layout-land/fragment_home.xml | 23 +- .../main/res/layout-land/fragment_player.xml | 4 +- .../res/layout-xlarge-land/fragment_home.xml | 23 +- .../layout-xlarge-land/fragment_player.xml | 5 +- .../main/res/layout-xlarge/fragment_home.xml | 23 +- .../res/layout-xlarge/fragment_player.xml | 5 +- .../res/layout/fragment_adaptive_player.xml | 6 +- ...ment_adaptive_player_playback_controls.xml | 24 +- .../res/layout/fragment_card_blur_player.xml | 17 +- ...ent_card_blur_player_playback_controls.xml | 133 +- .../main/res/layout/fragment_card_player.xml | 11 +- ...fragment_card_player_playback_controls.xml | 158 +- app/src/main/res/layout/fragment_fit.xml | 8 +- .../layout/fragment_fit_playback_controls.xml | 114 +- .../main/res/layout/fragment_flat_player.xml | 14 +- ...fragment_flat_player_playback_controls.xml | 58 +- app/src/main/res/layout/fragment_full.xml | 2 +- .../main/res/layout/fragment_hmm_player.xml | 3 +- app/src/main/res/layout/fragment_home.xml | 4 +- app/src/main/res/layout/fragment_player.xml | 11 +- .../fragment_player_playback_controls.xml | 130 +- app/src/main/res/layout/fragment_volume.xml | 2 +- .../main/res/menu/bottom_navigation_main.xml | 6 - app/src/main/res/menu/menu_drawer.xml | 29 + app/src/main/res/values/arrays.xml | 2 - .../res/values/ic_launcher_background.xml | 2 - app/src/main/res/values/md_colors.xml | 4 - app/src/main/res/values/strings.xml | 1 + app/src/main/res/values/styles.xml | 21 +- .../main/res/values/swipe_button_attrs.xml | 31 - app/src/sans/res/font/font.xml | 4 + app/src/sans/res/font/product_sans_bold.ttf | Bin 0 -> 55548 bytes 131 files changed, 5398 insertions(+), 5304 deletions(-) rename app/src/main/java/code/name/monkey/retromusic/{RetroApplication.kt => App.kt} (94%) delete mode 100644 app/src/main/java/code/name/monkey/retromusic/dialogs/AddToPlaylistDialog.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/dialogs/AddToPlaylistDialog.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/dialogs/ClearSmartPlaylistDialog.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/dialogs/ClearSmartPlaylistDialog.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/dialogs/CreatePlaylistDialog.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/dialogs/CreatePlaylistDialog.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/dialogs/DeletePlaylistDialog.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/dialogs/DeletePlaylistDialog.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/dialogs/MainOptionsBottomSheetDialogFragment.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/dialogs/MainOptionsBottomSheetDialogFragment.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/dialogs/RenamePlaylistDialog.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/dialogs/RenamePlaylistDialog.kt create mode 100644 app/src/main/java/code/name/monkey/retromusic/model/Genre.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/model/Genre.kt create mode 100644 app/src/main/java/code/name/monkey/retromusic/service/MultiPlayer.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/service/MultiPlayer.kt create mode 100644 app/src/main/java/code/name/monkey/retromusic/service/MusicService.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/service/MusicService.kt delete mode 100755 app/src/main/java/code/name/monkey/retromusic/ui/activities/ArtistDetailActivity.java create mode 100755 app/src/main/java/code/name/monkey/retromusic/ui/activities/ArtistDetailActivity.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/activities/GenreDetailsActivity.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/activities/GenreDetailsActivity.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/activities/PlaylistDetailActivity.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/activities/PlaylistDetailActivity.kt delete mode 100755 app/src/main/java/code/name/monkey/retromusic/ui/activities/SettingsActivity.java create mode 100755 app/src/main/java/code/name/monkey/retromusic/ui/activities/SettingsActivity.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsBaseActivity.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/PlayingQueueFragment.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/cardblur/CardBlurFragment.kt create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/cardblur/CardBlurPlaybackControlsFragment.kt create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/fit/FitFragment.kt create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/fit/FitPlaybackControlsFragment.kt create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/flat/FlatPlaybackControlsFragment.kt create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/flat/FlatPlayerFragment.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/MainSettingsFragment.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/MainSettingsFragment.kt create mode 100644 app/src/main/java/code/name/monkey/retromusic/views/MaterialButtonTextColor.java create mode 100644 app/src/main/res/drawable/navigation_item.xml create mode 100644 app/src/main/res/drawable/navigation_view_item.xml delete mode 100755 app/src/main/res/font/circular_std_black.otf delete mode 100755 app/src/main/res/font/circular_std_book.otf create mode 100755 app/src/main/res/font/sans_bold.ttf create mode 100755 app/src/main/res/font/sans_regular.ttf create mode 100644 app/src/main/res/menu/menu_drawer.xml delete mode 100644 app/src/main/res/values/ic_launcher_background.xml delete mode 100644 app/src/main/res/values/md_colors.xml delete mode 100644 app/src/main/res/values/swipe_button_attrs.xml create mode 100755 app/src/sans/res/font/product_sans_bold.ttf diff --git a/app/app.iml b/app/app.iml index 2566dab7..e41380a5 100644 --- a/app/app.iml +++ b/app/app.iml @@ -8,16 +8,16 @@ - @@ -25,87 +25,90 @@ - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -148,13 +151,16 @@ + + + - + @@ -166,7 +172,6 @@ - @@ -189,87 +194,86 @@ - + - - + + + + + + + + - - - - - - - - - - + + + + + + + + + + - - - - - - - - - + + + + - - - + + - - - - - + + + + + + + + - - - - + + + + - - - - - + + - + + - - - + + - - + + + + + - - - - - - - - + + + - + + - - - - - + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 32115020..9b55926c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -142,7 +142,6 @@ dependencies { implementation 'com.anjlab.android.iab.v3:library:1.0.44' /*UI Library*/ - implementation 'uk.co.chrisjenx:calligraphy:2.3.0' implementation 'me.zhanghai.android.materialprogressbar:library:1.4.2' implementation 'com.r0adkll:slidableactivity:2.0.6' /*Backend all*/ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ca258b34..f9c70b14 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -14,7 +14,7 @@ + android:windowSoftInputMode="stateVisible"/> diff --git a/app/src/main/assets/retro-changelog.html b/app/src/main/assets/retro-changelog.html index 281b5626..9f5329b3 100644 --- a/app/src/main/assets/retro-changelog.html +++ b/app/src/main/assets/retro-changelog.html @@ -1 +1 @@ -

Version 2.2.100

  • Click new music mix to play songs
  • Gradient image option for gird list
  • Clear button for playing queue
  • Click toolbar (Library) to open options
  • Folder list back button
  • New theme Fit
  • On library click on toolbar for accessing main menu
  • On home click on toolbar for accessing search
  • BottomSheetDialogue is now adaptable to screens, background colour and text size consistency.
  • Removed coloured navigation bar option to making app adapt the primary colour
  • Swipe up gesture for now playing removed, replaced with "tap to open", To achieve transparent navigation bar for desired themes.
  • Improved tablet UI and home screen by adding suggestions toggle banner issues.
  • Improving lyrics page

FAQ's

*If you face any UI related issues you clear app data and cache, if its not working try to uninstall and install again.

\ No newline at end of file +

Version 2.2.100

  • Click new music mix to play songs
  • Gradient image option for gird list
  • Clear button for playing queue
  • Click toolbar (Library) to open options
  • Folder list back button
  • New theme Fit
  • On library click on toolbar for accessing main menu
  • On home click on toolbar for accessing search
  • BottomSheetDialogue is now adaptable to screens, background colour and text size consistency.
  • Removed coloured navigation bar option to making app adapt the primary colour
  • Swipe up gesture for now playing removed, replaced with "tap to open", To achieve transparent navigation bar for desired themes.
  • Improved tablet UI and home screen by adding suggestions toggle banner issues.
  • Improving lyrics page

FAQ's

*If you face any UI related issues you clear app data and cache, if its not working try to uninstall and install again.

\ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/RetroApplication.kt b/app/src/main/java/code/name/monkey/retromusic/App.kt similarity index 94% rename from app/src/main/java/code/name/monkey/retromusic/RetroApplication.kt rename to app/src/main/java/code/name/monkey/retromusic/App.kt index 176e6a03..0c3d590d 100644 --- a/app/src/main/java/code/name/monkey/retromusic/RetroApplication.kt +++ b/app/src/main/java/code/name/monkey/retromusic/App.kt @@ -6,9 +6,8 @@ import code.name.monkey.appthemehelper.ThemeStore import com.anjlab.android.iab.v3.BillingProcessor import com.anjlab.android.iab.v3.TransactionDetails import com.bumptech.glide.Glide -import uk.co.chrisjenx.calligraphy.CalligraphyConfig -class RetroApplication : MultiDexApplication() { +class App : MultiDexApplication() { lateinit var billingProcessor: BillingProcessor @@ -67,7 +66,7 @@ class RetroApplication : MultiDexApplication() { const val PRO_VERSION_PRODUCT_ID = "pro_version" - lateinit var instance: RetroApplication + lateinit var instance: App private set val context: Context diff --git a/app/src/main/java/code/name/monkey/retromusic/dialogs/AddToPlaylistDialog.java b/app/src/main/java/code/name/monkey/retromusic/dialogs/AddToPlaylistDialog.java deleted file mode 100644 index 2a8cfa7c..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/dialogs/AddToPlaylistDialog.java +++ /dev/null @@ -1,84 +0,0 @@ -package code.name.monkey.retromusic.dialogs; - -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import java.util.ArrayList; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.recyclerview.widget.DefaultItemAnimator; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.loaders.PlaylistLoader; -import code.name.monkey.retromusic.model.Playlist; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.ui.adapter.playlist.AddToPlaylist; -import code.name.monkey.retromusic.views.RoundedBottomSheetDialogFragment; - -/** - * @author Karim Abou Zeid (kabouzeid), Aidan Follestad (afollestad) - */ -public class AddToPlaylistDialog extends RoundedBottomSheetDialogFragment { - - @BindView(R.id.playlists) - RecyclerView playlist; - - @BindView(R.id.title) - TextView title; - - @NonNull - public static AddToPlaylistDialog create(Song song) { - ArrayList list = new ArrayList<>(); - list.add(song); - return create(list); - } - - @NonNull - public static AddToPlaylistDialog create(ArrayList songs) { - AddToPlaylistDialog dialog = new AddToPlaylistDialog(); - Bundle args = new Bundle(); - args.putParcelableArrayList("songs", songs); - dialog.setArguments(args); - return dialog; - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View layout = inflater.inflate(R.layout.dialog_add_to_playlist, container, false); - ButterKnife.bind(this, layout); - return layout; - } - - @SuppressWarnings("ConstantConditions") - @OnClick(R.id.action_add_playlist) - void newPlaylist() { - final ArrayList songs = getArguments().getParcelableArrayList("songs"); - CreatePlaylistDialog.create(songs) - .show(getActivity().getSupportFragmentManager(), "ADD_TO_PLAYLIST"); - dismiss(); - } - - @SuppressWarnings("ConstantConditions") - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - title.setTextColor(ThemeStore.textColorPrimary(getContext())); - final ArrayList songs = getArguments().getParcelableArrayList("songs"); - final ArrayList playlists = PlaylistLoader.INSTANCE.getAllPlaylists(getActivity()).blockingFirst(); - final AddToPlaylist playlistAdapter = new AddToPlaylist(getActivity(), playlists, R.layout.item_playlist, songs, getDialog()); - playlist.setLayoutManager(new LinearLayoutManager(getContext())); - playlist.setItemAnimator(new DefaultItemAnimator()); - playlist.setAdapter(playlistAdapter); - } -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/dialogs/AddToPlaylistDialog.kt b/app/src/main/java/code/name/monkey/retromusic/dialogs/AddToPlaylistDialog.kt new file mode 100644 index 00000000..b8b68391 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/dialogs/AddToPlaylistDialog.kt @@ -0,0 +1,68 @@ +package code.name.monkey.retromusic.dialogs + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.DefaultItemAnimator +import androidx.recyclerview.widget.LinearLayoutManager +import butterknife.ButterKnife +import code.name.monkey.appthemehelper.ThemeStore +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.loaders.PlaylistLoader +import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.ui.adapter.playlist.AddToPlaylist +import code.name.monkey.retromusic.views.RoundedBottomSheetDialogFragment +import kotlinx.android.synthetic.main.dialog_add_to_playlist.* +import java.util.* + +/** + * @author Karim Abou Zeid (kabouzeid), Aidan Follestad (afollestad) + */ +class AddToPlaylistDialog : RoundedBottomSheetDialogFragment() { + + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + val layout = inflater.inflate(R.layout.dialog_add_to_playlist, container, false) + ButterKnife.bind(this, layout) + return layout + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + actionAddPlaylist.setOnClickListener { + val songs = arguments!!.getParcelableArrayList("songs") + CreatePlaylistDialog.create(songs).show(activity!!.supportFragmentManager, "ADD_TO_PLAYLIST") + dismiss() + } + + bannerTitle.setTextColor(ThemeStore.textColorPrimary(context!!)) + val songs = arguments!!.getParcelableArrayList("songs") + val playlists = PlaylistLoader.getAllPlaylists(activity!!).blockingFirst() + val playlistAdapter = AddToPlaylist(activity!!, playlists, R.layout.item_playlist, songs!!, dialog) + recyclerView.apply { + layoutManager = LinearLayoutManager(context) + itemAnimator = DefaultItemAnimator() + adapter = playlistAdapter + } + } + + companion object { + + fun create(song: Song): AddToPlaylistDialog { + val list = ArrayList() + list.add(song) + return create(list) + } + + fun create(songs: ArrayList): AddToPlaylistDialog { + val dialog = AddToPlaylistDialog() + val args = Bundle() + args.putParcelableArrayList("songs", songs) + dialog.arguments = args + return dialog + } + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/dialogs/ClearSmartPlaylistDialog.java b/app/src/main/java/code/name/monkey/retromusic/dialogs/ClearSmartPlaylistDialog.java deleted file mode 100644 index 735b0f9d..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/dialogs/ClearSmartPlaylistDialog.java +++ /dev/null @@ -1,52 +0,0 @@ -package code.name.monkey.retromusic.dialogs; - -import android.app.Dialog; -import android.os.Bundle; -import android.text.Html; - -import com.afollestad.materialdialogs.DialogAction; -import com.afollestad.materialdialogs.MaterialDialog; - -import androidx.annotation.NonNull; -import androidx.fragment.app.DialogFragment; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.model.smartplaylist.AbsSmartPlaylist; - - -public class ClearSmartPlaylistDialog extends DialogFragment { - - @NonNull - public static ClearSmartPlaylistDialog create(AbsSmartPlaylist playlist) { - ClearSmartPlaylistDialog dialog = new ClearSmartPlaylistDialog(); - Bundle args = new Bundle(); - args.putParcelable("playlist", playlist); - dialog.setArguments(args); - return dialog; - } - - @NonNull - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - //noinspection unchecked - final AbsSmartPlaylist playlist = getArguments().getParcelable("playlist"); - int title = R.string.clear_playlist_title; - //noinspection ConstantConditions - CharSequence content = Html.fromHtml(getString(R.string.clear_playlist_x, playlist.name)); - - return new MaterialDialog.Builder(getActivity()) - .title(title) - .content(content) - .positiveText(R.string.clear_action) - .negativeText(android.R.string.cancel) - .onPositive(new MaterialDialog.SingleButtonCallback() { - @Override - public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) { - if (getActivity() == null) { - return; - } - playlist.clear(getActivity()); - } - }) - .build(); - } -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/dialogs/ClearSmartPlaylistDialog.kt b/app/src/main/java/code/name/monkey/retromusic/dialogs/ClearSmartPlaylistDialog.kt new file mode 100644 index 00000000..500d95dc --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/dialogs/ClearSmartPlaylistDialog.kt @@ -0,0 +1,45 @@ +package code.name.monkey.retromusic.dialogs + +import android.app.Dialog +import android.os.Bundle +import android.text.Html +import androidx.fragment.app.DialogFragment +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.model.smartplaylist.AbsSmartPlaylist +import com.afollestad.materialdialogs.MaterialDialog + + +class ClearSmartPlaylistDialog : DialogFragment() { + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + + val playlist = arguments!!.getParcelable("playlist") + val title = R.string.clear_playlist_title + + val content = Html.fromHtml(getString(R.string.clear_playlist_x, playlist!!.name)) + + return MaterialDialog.Builder(activity!!) + .title(title) + .content(content) + .positiveText(R.string.clear_action) + .negativeText(android.R.string.cancel) + .onPositive { _, _ -> + if (activity == null) { + return@onPositive + } + playlist.clear(activity) + } + .build() + } + + companion object { + + fun create(playlist: AbsSmartPlaylist): ClearSmartPlaylistDialog { + val dialog = ClearSmartPlaylistDialog() + val args = Bundle() + args.putParcelable("playlist", playlist) + dialog.arguments = args + return dialog + } + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/dialogs/CreatePlaylistDialog.java b/app/src/main/java/code/name/monkey/retromusic/dialogs/CreatePlaylistDialog.java deleted file mode 100644 index b1b7cf45..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/dialogs/CreatePlaylistDialog.java +++ /dev/null @@ -1,123 +0,0 @@ -package code.name.monkey.retromusic.dialogs; - -import android.content.res.ColorStateList; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import com.google.android.material.button.MaterialButton; -import com.google.android.material.textfield.TextInputEditText; -import com.google.android.material.textfield.TextInputLayout; - -import java.util.ArrayList; -import java.util.Objects; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.appthemehelper.util.MaterialUtil; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.util.PlaylistsUtil; -import code.name.monkey.retromusic.views.RoundedBottomSheetDialogFragment; - -/** - * @author Karim Abou Zeid (kabouzeid), Aidan Follestad (afollestad) - */ -public class CreatePlaylistDialog extends RoundedBottomSheetDialogFragment { - - @BindView(R.id.option_1) - TextInputEditText playlistName; - - @BindView(R.id.action_new_playlist) - TextInputLayout textInputLayout; - - @BindView(R.id.action_cancel) - MaterialButton actionCancel; - - @BindView(R.id.action_create) - MaterialButton actionCreate; - - @BindView(R.id.title) - TextView title; - - @NonNull - public static CreatePlaylistDialog create() { - return create((Song) null); - } - - @NonNull - public static CreatePlaylistDialog create(@Nullable Song song) { - ArrayList list = new ArrayList<>(); - if (song != null) { - list.add(song); - } - return create(list); - } - - @NonNull - public static CreatePlaylistDialog create(ArrayList songs) { - CreatePlaylistDialog dialog = new CreatePlaylistDialog(); - Bundle args = new Bundle(); - args.putParcelableArrayList("songs", songs); - dialog.setArguments(args); - return dialog; - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View layout = inflater.inflate(R.layout.dialog_create_playlist, container, false); - ButterKnife.bind(this, layout); - return layout; - } - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - int accentColor = ThemeStore.accentColor(Objects.requireNonNull(getContext())); - - MaterialUtil.setTint(actionCreate, true); - - MaterialUtil.setTint(actionCancel, false); - - MaterialUtil.setTint(textInputLayout, true); - - - playlistName.setHintTextColor(ColorStateList.valueOf(accentColor)); - playlistName.setTextColor(ThemeStore.textColorPrimary(getContext())); - - title.setTextColor(ThemeStore.textColorPrimary(getContext())); - } - - @OnClick({R.id.action_cancel, R.id.action_create}) - void actions(View view) { - switch (view.getId()) { - case R.id.action_cancel: - dismiss(); - break; - case R.id.action_create: - if (getActivity() == null) { - return; - } - if (!playlistName.getText().toString().trim().isEmpty()) { - final int playlistId = PlaylistsUtil - .createPlaylist(getActivity(), playlistName.getText().toString()); - if (playlistId != -1 && getActivity() != null) { - //noinspection unchecked - ArrayList songs = getArguments().getParcelableArrayList("songs"); - if (songs != null) { - PlaylistsUtil.addToPlaylist(getActivity(), songs, playlistId, true); - } - } - } - break; - } - dismiss(); - } -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/dialogs/CreatePlaylistDialog.kt b/app/src/main/java/code/name/monkey/retromusic/dialogs/CreatePlaylistDialog.kt new file mode 100644 index 00000000..f2245eba --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/dialogs/CreatePlaylistDialog.kt @@ -0,0 +1,83 @@ +package code.name.monkey.retromusic.dialogs + +import android.content.Context +import android.content.res.ColorStateList +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import butterknife.ButterKnife +import code.name.monkey.appthemehelper.ThemeStore +import code.name.monkey.appthemehelper.util.MaterialUtil +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.util.PlaylistsUtil +import code.name.monkey.retromusic.views.RoundedBottomSheetDialogFragment +import kotlinx.android.synthetic.main.dialog_playlist.* +import java.util.* + +/** + * @author Karim Abou Zeid (kabouzeid), Aidan Follestad (afollestad) + */ +class CreatePlaylistDialog : RoundedBottomSheetDialogFragment() { + + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + val layout = inflater.inflate(R.layout.dialog_playlist, container, false) + ButterKnife.bind(this, layout) + return layout + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + val accentColor = ThemeStore.accentColor(Objects.requireNonNull(context)) + + MaterialUtil.setTint(actionCreate, true) + MaterialUtil.setTint(actionCancel, false) + MaterialUtil.setTint(actionNewPlaylistContainer, true) + + actionNewPlaylist.setHintTextColor(ColorStateList.valueOf(accentColor)) + actionNewPlaylist.setTextColor(ThemeStore.textColorPrimary(context!!)) + bannerTitle.setTextColor(ThemeStore.textColorPrimary(context!!)) + + + actionCancel.setOnClickListener { dismiss() } + actionCreate.setOnClickListener { + if (activity == null) { + return@setOnClickListener + } + if (!actionNewPlaylist!!.text!!.toString().trim { it <= ' ' }.isEmpty()) { + val playlistId = PlaylistsUtil + .createPlaylist(activity!!, actionNewPlaylist!!.text!!.toString()) + if (playlistId != -1 && activity != null) { + + val songs = arguments!!.getParcelableArrayList("songs") + if (songs != null) { + PlaylistsUtil.addToPlaylist(activity!!, songs, playlistId, true) + } + } + } + dismiss() + } + } + + companion object { + + @JvmOverloads + fun create(song: Song? = null): CreatePlaylistDialog { + val list = ArrayList() + if (song != null) { + list.add(song) + } + return create(list) + } + + fun create(songs: ArrayList): CreatePlaylistDialog { + val dialog = CreatePlaylistDialog() + val args = Bundle() + args.putParcelableArrayList("songs", songs) + dialog.arguments = args + return dialog + } + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/dialogs/DeletePlaylistDialog.java b/app/src/main/java/code/name/monkey/retromusic/dialogs/DeletePlaylistDialog.java deleted file mode 100644 index a7681518..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/dialogs/DeletePlaylistDialog.java +++ /dev/null @@ -1,102 +0,0 @@ -package code.name.monkey.retromusic.dialogs; - -import android.os.Bundle; -import android.text.Html; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import com.google.android.material.button.MaterialButton; - -import java.util.ArrayList; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.appthemehelper.util.MaterialUtil; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.model.Playlist; -import code.name.monkey.retromusic.util.PlaylistsUtil; -import code.name.monkey.retromusic.views.RoundedBottomSheetDialogFragment; - - -public class DeletePlaylistDialog extends RoundedBottomSheetDialogFragment { - - @BindView(R.id.action_delete) - MaterialButton actionDelete; - - @BindView(R.id.title) - TextView title; - - @BindView(R.id.action_cancel) - MaterialButton actionCancel; - - @NonNull - public static DeletePlaylistDialog create(Playlist playlist) { - ArrayList list = new ArrayList<>(); - list.add(playlist); - return create(list); - } - - @NonNull - public static DeletePlaylistDialog create(ArrayList playlists) { - DeletePlaylistDialog dialog = new DeletePlaylistDialog(); - Bundle args = new Bundle(); - args.putParcelableArrayList("playlists", playlists); - dialog.setArguments(args); - return dialog; - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View layout = inflater.inflate(R.layout.dialog_delete, container, false); - ButterKnife.bind(this, layout); - return layout; - } - - @SuppressWarnings("ConstantConditions") - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - //noinspection unchecked - final ArrayList playlists = getArguments().getParcelableArrayList("playlists"); - int title; - CharSequence content; - //noinspection ConstantConditions - if (playlists.size() > 1) { - content = Html.fromHtml(getString(R.string.delete_x_playlists, playlists.size())); - } else { - content = Html.fromHtml(getString(R.string.delete_playlist_x, playlists.get(0).name)); - } - this.title.setText(content); - this.title.setTextColor(ThemeStore.textColorPrimary(getContext())); - - actionDelete.setText(R.string.action_delete); - - MaterialUtil.setTint(actionDelete, true); - MaterialUtil.setTint(actionCancel, false); - - - } - - @OnClick({R.id.action_cancel, R.id.action_delete}) - void actions(View view) { - final ArrayList playlists = getArguments().getParcelableArrayList("playlists"); - switch (view.getId()) { - case R.id.action_delete: - if (getActivity() == null) - return; - PlaylistsUtil.deletePlaylists(getActivity(), playlists); - break; - default: - } - dismiss(); - } - -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/dialogs/DeletePlaylistDialog.kt b/app/src/main/java/code/name/monkey/retromusic/dialogs/DeletePlaylistDialog.kt new file mode 100644 index 00000000..78a2969b --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/dialogs/DeletePlaylistDialog.kt @@ -0,0 +1,70 @@ +package code.name.monkey.retromusic.dialogs + +import android.os.Bundle +import android.text.Html +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import butterknife.ButterKnife +import code.name.monkey.appthemehelper.ThemeStore +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.model.Playlist +import code.name.monkey.retromusic.util.PlaylistsUtil +import code.name.monkey.retromusic.views.RoundedBottomSheetDialogFragment +import kotlinx.android.synthetic.main.dialog_remove_from_playlist.* +import java.util.* + + +class DeletePlaylistDialog : RoundedBottomSheetDialogFragment() { + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + val layout = inflater.inflate(R.layout.dialog_remove_from_playlist, container, false) + ButterKnife.bind(this, layout) + return layout + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + val playlists = arguments!!.getParcelableArrayList("playlists") + val content: CharSequence + + content = if (playlists!!.size > 1) { + Html.fromHtml(getString(R.string.delete_x_playlists, playlists.size)) + } else { + Html.fromHtml(getString(R.string.delete_playlist_x, playlists[0].name)) + } + bannerTitle.text = content + bannerTitle.setTextColor(ThemeStore.textColorPrimary(context!!)) + + actionRemove.setText(R.string.action_delete) + actionRemove.setTextColor(ThemeStore.textColorSecondary(context!!)) + actionCancel.setTextColor(ThemeStore.textColorSecondary(context!!)) + + actionCancel.setOnClickListener { dismiss() } + actionRemove.setOnClickListener { + PlaylistsUtil.deletePlaylists(activity!!, playlists) + dismiss() + } + } + + + companion object { + + fun create(playlist: Playlist): DeletePlaylistDialog { + val list = ArrayList() + list.add(playlist) + return create(list) + } + + fun create(playlist: ArrayList): DeletePlaylistDialog { + val dialog = DeletePlaylistDialog() + val args = Bundle() + args.putParcelableArrayList("playlist", playlist) + dialog.arguments = args + return dialog + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/dialogs/MainOptionsBottomSheetDialogFragment.java b/app/src/main/java/code/name/monkey/retromusic/dialogs/MainOptionsBottomSheetDialogFragment.java deleted file mode 100644 index 0374f023..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/dialogs/MainOptionsBottomSheetDialogFragment.java +++ /dev/null @@ -1,167 +0,0 @@ -package code.name.monkey.retromusic.dialogs; - -import android.content.res.ColorStateList; -import android.graphics.Bitmap; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import com.google.android.material.button.MaterialButton; - -import java.io.File; -import java.util.Calendar; -import java.util.List; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.widget.AppCompatTextView; -import androidx.core.content.ContextCompat; -import butterknife.BindView; -import butterknife.BindViews; -import butterknife.ButterKnife; -import butterknife.OnClick; -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.RetroApplication; -import code.name.monkey.retromusic.ui.activities.MainActivity; -import code.name.monkey.retromusic.ui.fragments.mainactivity.folders.FoldersFragment; -import code.name.monkey.retromusic.util.Compressor; -import code.name.monkey.retromusic.util.NavigationUtil; -import code.name.monkey.retromusic.util.PreferenceUtil; -import code.name.monkey.retromusic.views.CircularImageView; -import code.name.monkey.retromusic.views.RoundedBottomSheetDialogFragment; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.CompositeDisposable; -import io.reactivex.schedulers.Schedulers; - -import static code.name.monkey.retromusic.Constants.USER_PROFILE; - -public class MainOptionsBottomSheetDialogFragment extends RoundedBottomSheetDialogFragment { - - private static final String TAG = "MainOptionsBottomSheetD"; - private static ButterKnife.Setter textColor = (view, value, index) -> view.setTextColor(ColorStateList.valueOf(value)); - - - @BindViews({R.id.action_folders, R.id.action_about, R.id.action_buy_pro, R.id.action_rate, - R.id.action_sleep_timer}) - List materialButtons; - - @BindView(R.id.user_image_bottom) - CircularImageView userImageBottom; - - @BindView(R.id.title_welcome) - AppCompatTextView titleWelcome; - - @BindView(R.id.text) - AppCompatTextView text; - - private CompositeDisposable disposable = new CompositeDisposable(); - - public static MainOptionsBottomSheetDialogFragment newInstance(int selected_id) { - Bundle bundle = new Bundle(); - bundle.putInt("selected_id", selected_id); - MainOptionsBottomSheetDialogFragment fragment = new MainOptionsBottomSheetDialogFragment(); - fragment.setArguments(bundle); - return fragment; - } - - public static MainOptionsBottomSheetDialogFragment newInstance() { - return new MainOptionsBottomSheetDialogFragment(); - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - disposable.clear(); - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View layout = inflater.inflate(R.layout.fragment_main_options, container, false); - ButterKnife.bind(this, layout); - layout.findViewById(R.id.action_buy_pro).setVisibility(RetroApplication.Companion.isProVersion() ? View.GONE : View.VISIBLE); - //ButterKnife.apply(materialButtons, textColor, ThemeStore.textColorPrimary(getContext())); - return layout; - } - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - text.setTextColor(ThemeStore.textColorSecondary(getContext())); - titleWelcome.setTextColor(ThemeStore.textColorPrimary(getContext())); - titleWelcome.setText(String.format("%s %s!", getTimeOfTheDay(), PreferenceUtil.getInstance().getUserName())); - loadImageFromStorage(); - } - - @OnClick({R.id.action_folders, R.id.user_info_container, R.id.action_settings, R.id.action_sleep_timer, R.id.action_rate, - R.id.action_buy_pro, R.id.action_about}) - void onClick(View view) { - MainActivity mainActivity = (MainActivity) getActivity(); - if (mainActivity == null) { - return; - } - switch (view.getId()) { - case R.id.action_folders: - mainActivity.setCurrentFragment(FoldersFragment.newInstance(getContext()), true, FoldersFragment.TAG); - break; - case R.id.action_settings: - NavigationUtil.goToSettings(mainActivity); - break; - case R.id.action_about: - NavigationUtil.goToAbout(mainActivity); - break; - case R.id.action_buy_pro: - NavigationUtil.goToProVersion(mainActivity); - break; - case R.id.action_sleep_timer: - if (getFragmentManager() != null) { - new SleepTimerDialog().show(getFragmentManager(), TAG); - } - break; - case R.id.user_info_container: - NavigationUtil.goToUserInfo(getActivity()); - break; - case R.id.action_rate: - NavigationUtil.goToPlayStore(mainActivity); - break; - } - dismiss(); - } - - private String getTimeOfTheDay() { - String message = getString(R.string.title_good_day); - Calendar c = Calendar.getInstance(); - int timeOfDay = c.get(Calendar.HOUR_OF_DAY); - - if (timeOfDay >= 0 && timeOfDay < 6) { - message = getString(R.string.title_good_night); - } else if (timeOfDay >= 6 && timeOfDay < 12) { - message = getString(R.string.title_good_morning); - } else if (timeOfDay >= 12 && timeOfDay < 16) { - message = getString(R.string.title_good_afternoon); - } else if (timeOfDay >= 16 && timeOfDay < 20) { - message = getString(R.string.title_good_evening); - } else if (timeOfDay >= 20 && timeOfDay < 24) { - message = getString(R.string.title_good_night); - } - return message; - } - - private void loadImageFromStorage() { - //noinspection ConstantConditions - disposable.add(new Compressor(getContext()) - .setMaxHeight(300) - .setMaxWidth(300) - .setQuality(75) - .setCompressFormat(Bitmap.CompressFormat.WEBP) - .compressToBitmapAsFlowable( - new File(PreferenceUtil.getInstance().getProfileImage(),USER_PROFILE)) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(userImageBottom::setImageBitmap, - throwable -> userImageBottom.setImageDrawable(ContextCompat - .getDrawable(getContext(), R.drawable.ic_person_flat)))); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/dialogs/MainOptionsBottomSheetDialogFragment.kt b/app/src/main/java/code/name/monkey/retromusic/dialogs/MainOptionsBottomSheetDialogFragment.kt new file mode 100644 index 00000000..50f76b85 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/dialogs/MainOptionsBottomSheetDialogFragment.kt @@ -0,0 +1,117 @@ +package code.name.monkey.retromusic.dialogs + +import android.graphics.Bitmap +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.content.ContextCompat +import code.name.monkey.appthemehelper.ThemeStore +import code.name.monkey.retromusic.Constants.USER_PROFILE +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.ui.activities.MainActivity +import code.name.monkey.retromusic.util.Compressor +import code.name.monkey.retromusic.util.NavigationUtil +import code.name.monkey.retromusic.util.PreferenceUtil +import code.name.monkey.retromusic.views.RoundedBottomSheetDialogFragment +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.schedulers.Schedulers +import kotlinx.android.synthetic.main.fragment_main_options.* +import java.io.File +import java.util.* + +class MainOptionsBottomSheetDialogFragment : RoundedBottomSheetDialogFragment(), View.OnClickListener { + + private val disposable = CompositeDisposable() + + private val timeOfTheDay: String + get() { + var message = getString(R.string.title_good_day) + val c = Calendar.getInstance() + val timeOfDay = c.get(Calendar.HOUR_OF_DAY) + + when (timeOfDay) { + in 0..5 -> message = getString(R.string.title_good_night) + in 6..11 -> message = getString(R.string.title_good_morning) + in 12..15 -> message = getString(R.string.title_good_afternoon) + in 16..19 -> message = getString(R.string.title_good_evening) + in 20..23 -> message = getString(R.string.title_good_night) + } + return message + } + + override fun onDestroyView() { + super.onDestroyView() + disposable.clear() + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.fragment_main_options, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + text!!.setTextColor(ThemeStore.textColorSecondary(context!!)) + titleWelcome!!.setTextColor(ThemeStore.textColorPrimary(context!!)) + titleWelcome!!.text = String.format("%s %s!", timeOfTheDay, PreferenceUtil.getInstance().userName) + loadImageFromStorage() + + actionSettings.setOnClickListener(this) + actionAbout.setOnClickListener(this) + actionSleepTimer.setOnClickListener(this) + userInfoContainer.setOnClickListener(this) + actionRate.setOnClickListener(this) + } + + + override fun onClick(view: View) { + val mainActivity = activity as MainActivity? ?: return + when (view.id) { + R.id.actionSettings -> NavigationUtil.goToSettings(mainActivity) + R.id.actionAbout -> NavigationUtil.goToAbout(mainActivity) + R.id.actionSleepTimer -> if (fragmentManager != null) { + SleepTimerDialog().show(fragmentManager!!, TAG) + } + R.id.userInfoContainer -> NavigationUtil.goToUserInfo(activity!!) + R.id.actionRate -> NavigationUtil.goToPlayStore(mainActivity) + } + dismiss() + } + + private fun loadImageFromStorage() { + + disposable.add(Compressor(context!!) + .setMaxHeight(300) + .setMaxWidth(300) + .setQuality(75) + .setCompressFormat(Bitmap.CompressFormat.WEBP) + .compressToBitmapAsFlowable( + File(PreferenceUtil.getInstance().profileImage, USER_PROFILE)) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ userImage!!.setImageBitmap(it) }, { + userImage!!.setImageDrawable(ContextCompat + .getDrawable(context!!, R.drawable.ic_person_flat)) + }, { + + })) + } + + companion object { + + private const val TAG: String = "MainOptionsBottomSheetD" + + fun newInstance(selected_id: Int): MainOptionsBottomSheetDialogFragment { + val bundle = Bundle() + bundle.putInt("selected_id", selected_id) + val fragment = MainOptionsBottomSheetDialogFragment() + fragment.arguments = bundle + return fragment + } + + fun newInstance(): MainOptionsBottomSheetDialogFragment { + return MainOptionsBottomSheetDialogFragment() + } + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/dialogs/RemoveFromPlaylistDialog.kt b/app/src/main/java/code/name/monkey/retromusic/dialogs/RemoveFromPlaylistDialog.kt index 38726666..94988c5a 100644 --- a/app/src/main/java/code/name/monkey/retromusic/dialogs/RemoveFromPlaylistDialog.kt +++ b/app/src/main/java/code/name/monkey/retromusic/dialogs/RemoveFromPlaylistDialog.kt @@ -6,40 +6,17 @@ import android.text.Html import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.TextView -import butterknife.BindView import butterknife.ButterKnife -import butterknife.OnClick import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.retromusic.R import code.name.monkey.retromusic.model.PlaylistSong import code.name.monkey.retromusic.util.PlaylistsUtil import code.name.monkey.retromusic.views.RoundedBottomSheetDialogFragment +import kotlinx.android.synthetic.main.dialog_remove_from_playlist.* import java.util.* class RemoveFromPlaylistDialog : RoundedBottomSheetDialogFragment() { - @BindView(R.id.action_remove) - internal var remove: TextView? = null - - @BindView(R.id.title) - internal var title: TextView? = null - - @BindView(R.id.action_cancel) - internal var cancel: TextView? = null - - @OnClick(R.id.action_cancel, R.id.action_remove) - internal fun actions(view: View) { - val songs = arguments!!.getParcelableArrayList("songs") - when (view.id) { - R.id.action_remove -> { - if (activity == null) - return - PlaylistsUtil.removeFromPlaylist(activity!!, songs!!) - } - } - dismiss() - } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { val layout = inflater.inflate(R.layout.dialog_remove_from_playlist, container, false) @@ -64,12 +41,17 @@ class RemoveFromPlaylistDialog : RoundedBottomSheetDialogFragment() { title = R.string.remove_song_from_playlist_title content = Html.fromHtml(getString(R.string.remove_song_x_from_playlist, songs!![0].title)) } - this.remove!!.text = content - this.title!!.setText(title) + actionRemove.text = content + bannerTitle.setText(title) - this.title!!.setTextColor(ThemeStore.textColorPrimary(context!!)) - this.remove!!.setTextColor(ThemeStore.textColorSecondary(context!!)) - this.cancel!!.setTextColor(ThemeStore.textColorSecondary(context!!)) + bannerTitle.setTextColor(ThemeStore.textColorPrimary(context!!)) + actionRemove.setTextColor(ThemeStore.textColorSecondary(context!!)) + actionCancel.setTextColor(ThemeStore.textColorSecondary(context!!)) + + actionRemove.setOnClickListener { + PlaylistsUtil.removeFromPlaylist(activity!!, songs) + } + actionCancel.setOnClickListener { dismiss() } } companion object { diff --git a/app/src/main/java/code/name/monkey/retromusic/dialogs/RenamePlaylistDialog.java b/app/src/main/java/code/name/monkey/retromusic/dialogs/RenamePlaylistDialog.java deleted file mode 100644 index 19d32f6c..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/dialogs/RenamePlaylistDialog.java +++ /dev/null @@ -1,98 +0,0 @@ -package code.name.monkey.retromusic.dialogs; - -import android.content.res.ColorStateList; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import com.google.android.material.button.MaterialButton; -import com.google.android.material.textfield.TextInputEditText; -import com.google.android.material.textfield.TextInputLayout; - -import java.util.Objects; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.appthemehelper.util.MaterialUtil; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.util.PlaylistsUtil; -import code.name.monkey.retromusic.views.RoundedBottomSheetDialogFragment; - -public class RenamePlaylistDialog extends RoundedBottomSheetDialogFragment { - - @BindView(R.id.title) - TextView title; - - @BindView(R.id.option_1) - TextInputEditText playlistName; - - @BindView(R.id.action_new_playlist) - TextInputLayout textInputLayout; - - @BindView(R.id.action_cancel) - MaterialButton actionCancel; - - @BindView(R.id.action_rename) - MaterialButton rename; - - @NonNull - public static RenamePlaylistDialog create(long playlistId) { - RenamePlaylistDialog dialog = new RenamePlaylistDialog(); - Bundle args = new Bundle(); - args.putLong("playlist_id", playlistId); - dialog.setArguments(args); - return dialog; - } - - @OnClick({R.id.action_cancel, R.id.action_rename}) - void actions(View view) { - switch (view.getId()) { - case R.id.action_cancel: - dismiss(); - break; - case R.id.action_rename: - if (!playlistName.toString().trim().equals("")) { - long playlistId = getArguments().getLong("playlist_id"); - PlaylistsUtil.renamePlaylist(getActivity(), playlistId, - playlistName.getText().toString()); - } - break; - } - dismiss(); - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View layout = inflater.inflate(R.layout.dialog_playlist_rename, container, false); - ButterKnife.bind(this, layout); - return layout; - } - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - int accentColor = ThemeStore.accentColor(Objects.requireNonNull(getContext())); - MaterialUtil.setTint(rename,true); - MaterialUtil.setTint(actionCancel,false); - MaterialUtil.setTint(textInputLayout,false); - - playlistName.setHintTextColor(ColorStateList.valueOf(accentColor)); - playlistName.setTextColor(ThemeStore.textColorPrimary(getContext())); - - title.setTextColor(ThemeStore.textColorPrimary(getContext())); - - long playlistId = 0; - if (getArguments() != null) { - playlistId = getArguments().getLong("playlist_id"); - } - playlistName.setText(PlaylistsUtil.getNameForPlaylist(getActivity(), playlistId)); - } -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/dialogs/RenamePlaylistDialog.kt b/app/src/main/java/code/name/monkey/retromusic/dialogs/RenamePlaylistDialog.kt new file mode 100644 index 00000000..53bc6cb5 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/dialogs/RenamePlaylistDialog.kt @@ -0,0 +1,65 @@ +package code.name.monkey.retromusic.dialogs + +import android.content.res.ColorStateList +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import butterknife.ButterKnife +import code.name.monkey.appthemehelper.ThemeStore +import code.name.monkey.appthemehelper.util.MaterialUtil +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.util.PlaylistsUtil +import code.name.monkey.retromusic.views.RoundedBottomSheetDialogFragment +import kotlinx.android.synthetic.main.dialog_playlist.* + +class RenamePlaylistDialog : RoundedBottomSheetDialogFragment() { + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + val layout = inflater.inflate(R.layout.dialog_playlist, container, false) + ButterKnife.bind(this, layout) + return layout + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + val accentColor = ThemeStore.accentColor(context!!) + actionCreate.setText(R.string.action_rename) + + MaterialUtil.setTint(actionCreate, true) + MaterialUtil.setTint(actionCancel, false) + MaterialUtil.setTint(actionNewPlaylistContainer, false) + + actionNewPlaylist.apply { + var playlistId: Long = 0 + if (arguments != null) { + playlistId = arguments!!.getLong("playlist_id") + } + setText(PlaylistsUtil.getNameForPlaylist(activity!!, playlistId)) + setHintTextColor(ColorStateList.valueOf(accentColor)) + setTextColor(ThemeStore.textColorPrimary(context!!)) + } + + bannerTitle.setTextColor(ThemeStore.textColorPrimary(context!!)) + bannerTitle.setText(R.string.rename_playlist_title) + actionCancel.setOnClickListener { dismiss() } + actionCreate.setOnClickListener { + if (actionNewPlaylist.toString().trim { it <= ' ' } != "") { + val playlistId = arguments!!.getLong("playlist_id") + PlaylistsUtil.renamePlaylist(context!!, playlistId, actionNewPlaylist.text!!.toString()) + } + } + } + + companion object { + + fun create(playlistId: Long): RenamePlaylistDialog { + val dialog = RenamePlaylistDialog() + val args = Bundle() + args.putLong("playlist_id", playlistId) + dialog.arguments = args + return dialog + } + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/glide/ArtistGlideRequest.java b/app/src/main/java/code/name/monkey/retromusic/glide/ArtistGlideRequest.java index 7bf2c1e9..2a779a2a 100644 --- a/app/src/main/java/code/name/monkey/retromusic/glide/ArtistGlideRequest.java +++ b/app/src/main/java/code/name/monkey/retromusic/glide/ArtistGlideRequest.java @@ -15,7 +15,7 @@ import com.bumptech.glide.load.resource.drawable.GlideDrawable; import com.bumptech.glide.request.target.Target; import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.RetroApplication; +import code.name.monkey.retromusic.App; import code.name.monkey.retromusic.glide.artistimage.ArtistImage; import code.name.monkey.retromusic.glide.palette.BitmapPaletteTranscoder; import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper; @@ -31,7 +31,7 @@ public class ArtistGlideRequest { private static DrawableTypeRequest createBaseRequest(RequestManager requestManager, Artist artist, boolean noCustomImage, boolean forceDownload) { - boolean hasCustomImage = CustomArtistImageUtil.getInstance(RetroApplication.Companion.getInstance()) + boolean hasCustomImage = CustomArtistImageUtil.getInstance(App.Companion.getInstance()) .hasCustomArtistImage(artist); if (noCustomImage || !hasCustomImage) { return requestManager.load(new ArtistImage(artist.getName(), forceDownload)); @@ -41,7 +41,7 @@ public class ArtistGlideRequest { } private static Key createSignature(Artist artist) { - return ArtistSignatureUtil.getInstance(RetroApplication.Companion.getInstance()) + return ArtistSignatureUtil.getInstance(App.Companion.getInstance()) .getArtistSignature(artist.getName()); } diff --git a/app/src/main/java/code/name/monkey/retromusic/glide/RetroMusicColoredTarget.kt b/app/src/main/java/code/name/monkey/retromusic/glide/RetroMusicColoredTarget.kt index 281dc96a..5defa713 100644 --- a/app/src/main/java/code/name/monkey/retromusic/glide/RetroMusicColoredTarget.kt +++ b/app/src/main/java/code/name/monkey/retromusic/glide/RetroMusicColoredTarget.kt @@ -7,7 +7,6 @@ import com.bumptech.glide.request.animation.GlideAnimation import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.retromusic.R -import code.name.monkey.retromusic.RetroApplication import code.name.monkey.retromusic.glide.palette.BitmapPaletteTarget import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper import code.name.monkey.retromusic.util.PreferenceUtil diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/MusicPlayerRemote.kt b/app/src/main/java/code/name/monkey/retromusic/helper/MusicPlayerRemote.kt index cf685bb2..cec5bd45 100644 --- a/app/src/main/java/code/name/monkey/retromusic/helper/MusicPlayerRemote.kt +++ b/app/src/main/java/code/name/monkey/retromusic/helper/MusicPlayerRemote.kt @@ -12,7 +12,7 @@ import android.provider.MediaStore import android.util.Log import android.widget.Toast import code.name.monkey.retromusic.R -import code.name.monkey.retromusic.RetroApplication +import code.name.monkey.retromusic.App import code.name.monkey.retromusic.loaders.SongLoader import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.service.MusicService @@ -33,7 +33,7 @@ object MusicPlayerRemote { private val castSession: CastSession? get() { - val castSession = CastContext.getSharedInstance(RetroApplication.instance).sessionManager.currentCastSession + val castSession = CastContext.getSharedInstance(App.instance).sessionManager.currentCastSession if (castSession != null) { playbackLocation = PlaybackLocation.REMOTE } else { @@ -48,7 +48,7 @@ object MusicPlayerRemote { val currentSong: Song get() = if (musicService != null) { musicService!!.currentSong - } else Song.EMPTY_SONG + } else Song.emptySong /** * Async diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/menu/GenreMenuHelper.kt b/app/src/main/java/code/name/monkey/retromusic/helper/menu/GenreMenuHelper.kt index 6796361f..f9c127ce 100644 --- a/app/src/main/java/code/name/monkey/retromusic/helper/menu/GenreMenuHelper.kt +++ b/app/src/main/java/code/name/monkey/retromusic/helper/menu/GenreMenuHelper.kt @@ -1,26 +1,22 @@ package code.name.monkey.retromusic.helper.menu import android.app.Activity -import androidx.appcompat.app.AppCompatActivity import android.view.MenuItem - -import java.util.ArrayList - -import code.name.monkey.retromusic.loaders.GenreLoader -import code.name.monkey.retromusic.model.Genre -import code.name.monkey.retromusic.model.Song +import androidx.appcompat.app.AppCompatActivity import code.name.monkey.retromusic.R import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog import code.name.monkey.retromusic.helper.MusicPlayerRemote +import code.name.monkey.retromusic.loaders.GenreLoader +import code.name.monkey.retromusic.model.Genre +import code.name.monkey.retromusic.model.Song +import java.util.* /** * @author Hemanth S (h4h13). */ object GenreMenuHelper { - fun handleMenuClick(activity: AppCompatActivity, - genre: Genre, - item: MenuItem): Boolean { + fun handleMenuClick(activity: AppCompatActivity, genre: Genre, item: MenuItem): Boolean { when (item.itemId) { R.id.action_play -> { MusicPlayerRemote.openQueue(getGenreSongs(activity, genre), 0, true) diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/menu/PlaylistMenuHelper.kt b/app/src/main/java/code/name/monkey/retromusic/helper/menu/PlaylistMenuHelper.kt index 5289ad31..44481a36 100644 --- a/app/src/main/java/code/name/monkey/retromusic/helper/menu/PlaylistMenuHelper.kt +++ b/app/src/main/java/code/name/monkey/retromusic/helper/menu/PlaylistMenuHelper.kt @@ -9,7 +9,7 @@ import android.widget.Toast import java.util.ArrayList import code.name.monkey.retromusic.R -import code.name.monkey.retromusic.RetroApplication +import code.name.monkey.retromusic.App import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog import code.name.monkey.retromusic.dialogs.DeletePlaylistDialog import code.name.monkey.retromusic.dialogs.RenamePlaylistDialog @@ -76,8 +76,8 @@ object PlaylistMenuHelper { private class SavePlaylistAsyncTask internal constructor(context: Context) : WeakContextAsyncTask(context) { override fun doInBackground(vararg params: Playlist): String { - return String.format(RetroApplication.instance.applicationContext.getString(R.string - .saved_playlist_to), PlaylistsUtil.savePlaylist(RetroApplication.instance.applicationContext, params[0]).blockingFirst()) + return String.format(App.instance.applicationContext.getString(R.string + .saved_playlist_to), PlaylistsUtil.savePlaylist(App.instance.applicationContext, params[0]).blockingFirst()) } override fun onPostExecute(string: String) { diff --git a/app/src/main/java/code/name/monkey/retromusic/loaders/SongLoader.kt b/app/src/main/java/code/name/monkey/retromusic/loaders/SongLoader.kt index c6e7f40c..9359e339 100644 --- a/app/src/main/java/code/name/monkey/retromusic/loaders/SongLoader.kt +++ b/app/src/main/java/code/name/monkey/retromusic/loaders/SongLoader.kt @@ -119,7 +119,7 @@ object SongLoader { val song: Song = if (cursor != null && cursor.moveToFirst()) { getSongFromCursorImpl(cursor) } else { - Song.EMPTY_SONG + Song.emptySong } cursor?.close() e.onNext(song) diff --git a/app/src/main/java/code/name/monkey/retromusic/model/Album.kt b/app/src/main/java/code/name/monkey/retromusic/model/Album.kt index 27405c44..dc9df05c 100644 --- a/app/src/main/java/code/name/monkey/retromusic/model/Album.kt +++ b/app/src/main/java/code/name/monkey/retromusic/model/Album.kt @@ -36,6 +36,6 @@ class Album { } fun safeGetFirstSong(): Song { - return if (songs!!.isEmpty()) Song.EMPTY_SONG else songs[0] + return if (songs!!.isEmpty()) Song.emptySong else songs[0] } } diff --git a/app/src/main/java/code/name/monkey/retromusic/model/Genre.java b/app/src/main/java/code/name/monkey/retromusic/model/Genre.java new file mode 100644 index 00000000..9522536d --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/model/Genre.java @@ -0,0 +1,86 @@ +package code.name.monkey.retromusic.model; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * @author Hemanth S (h4h13). + */ + +public class Genre implements Parcelable { + + public static final Creator CREATOR = new Creator() { + @Override + public Genre createFromParcel(Parcel in) { + return new Genre(in); + } + + @Override + public Genre[] newArray(int size) { + return new Genre[size]; + } + }; + public final int id; + public final String name; + public final int songCount; + + public Genre(final int id, final String name, int songCount) { + this.id = id; + this.name = name; + this.songCount = songCount; + } + + + // For unknown genre + public Genre(final String name, final int songCount) { + this.id = -1; + this.name = name; + this.songCount = songCount; + } + + protected Genre(Parcel in) { + id = in.readInt(); + name = in.readString(); + songCount = in.readInt(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(id); + dest.writeString(name); + dest.writeInt(songCount); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Genre genre = (Genre) o; + + if (id != genre.id) return false; + return name != null ? name.equals(genre.name) : genre.name == null; + } + + @Override + public int hashCode() { + int result = id; + result = 31 * result + (name != null ? name.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "Genre{" + + "id=" + id + + ", name='" + name + '\'' + + '}'; + } + + +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/model/Genre.kt b/app/src/main/java/code/name/monkey/retromusic/model/Genre.kt deleted file mode 100644 index 9448e18e..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/model/Genre.kt +++ /dev/null @@ -1,26 +0,0 @@ -package code.name.monkey.retromusic.model - -import java.io.Serializable - -/** - * @author Hemanth S (h4h13). - */ - -class Genre : Serializable { - val id: Int - val name: String? - val songCount: Int - - constructor(id: Int, name: String, songCount: Int) { - this.id = id - this.name = name - this.songCount = songCount - } - - // For unknown genre - constructor(name: String, songCount: Int) { - this.id = -1 - this.name = name - this.songCount = songCount - } -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/model/Song.kt b/app/src/main/java/code/name/monkey/retromusic/model/Song.kt index fab260e2..a395b699 100644 --- a/app/src/main/java/code/name/monkey/retromusic/model/Song.kt +++ b/app/src/main/java/code/name/monkey/retromusic/model/Song.kt @@ -64,7 +64,9 @@ open class Song : Parcelable { } companion object { - val EMPTY_SONG = Song(-1, "", -1, -1, -1, "", -1, -1, "", -1, "") + + var emptySong = Song(-1, "", -1, -1, -1, "", -1, -1, "", -1, "") + @JvmField val CREATOR: Parcelable.Creator = object : Parcelable.Creator { override fun createFromParcel(source: Parcel): Song { diff --git a/app/src/main/java/code/name/monkey/retromusic/mvp/presenter/ArtistDetailsPresenter.java b/app/src/main/java/code/name/monkey/retromusic/mvp/presenter/ArtistDetailsPresenter.java index 0885f68a..c09e12fc 100644 --- a/app/src/main/java/code/name/monkey/retromusic/mvp/presenter/ArtistDetailsPresenter.java +++ b/app/src/main/java/code/name/monkey/retromusic/mvp/presenter/ArtistDetailsPresenter.java @@ -1,13 +1,12 @@ package code.name.monkey.retromusic.mvp.presenter; import android.os.Bundle; -import androidx.annotation.NonNull; +import androidx.annotation.NonNull; import code.name.monkey.retromusic.model.Artist; import code.name.monkey.retromusic.mvp.Presenter; import code.name.monkey.retromusic.mvp.contract.ArtistDetailContract; - -import static code.name.monkey.retromusic.ui.activities.ArtistDetailActivity.EXTRA_ARTIST_ID; +import code.name.monkey.retromusic.ui.activities.ArtistDetailActivity; /** @@ -38,7 +37,7 @@ public class ArtistDetailsPresenter extends Presenter implements ArtistDetailCon @Override public void loadArtistById() { - disposable.add(repository.getArtistById(bundle.getInt(EXTRA_ARTIST_ID)) + disposable.add(repository.getArtistById(bundle.getInt(ArtistDetailActivity.EXTRA_ARTIST_ID)) .subscribeOn(schedulerProvider.computation()) .observeOn(schedulerProvider.ui()) .doOnSubscribe(disposable1 -> view.loading()) diff --git a/app/src/main/java/code/name/monkey/retromusic/preferences/NowPlayingScreenPreferenceDialog.java b/app/src/main/java/code/name/monkey/retromusic/preferences/NowPlayingScreenPreferenceDialog.java index 6c68c8ca..59d7fde5 100644 --- a/app/src/main/java/code/name/monkey/retromusic/preferences/NowPlayingScreenPreferenceDialog.java +++ b/app/src/main/java/code/name/monkey/retromusic/preferences/NowPlayingScreenPreferenceDialog.java @@ -21,7 +21,7 @@ import androidx.fragment.app.DialogFragment; import androidx.viewpager.widget.PagerAdapter; import androidx.viewpager.widget.ViewPager; import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.RetroApplication; +import code.name.monkey.retromusic.App; import code.name.monkey.retromusic.ui.fragments.NowPlayingScreen; import code.name.monkey.retromusic.util.NavigationUtil; import code.name.monkey.retromusic.util.PreferenceUtil; @@ -97,8 +97,8 @@ public class NowPlayingScreenPreferenceDialog extends DialogFragment implements nowPlayingScreen.equals(NowPlayingScreen.TINY) || nowPlayingScreen.equals(NowPlayingScreen.BLUR_CARD)|| nowPlayingScreen.equals(NowPlayingScreen.ADAPTIVE)) - && !RetroApplication.Companion.isProVersion();*/ - return !RetroApplication.Companion.isProVersion(); + && !App.Companion.isProVersion();*/ + return !App.Companion.isProVersion(); } @Override diff --git a/app/src/main/java/code/name/monkey/retromusic/providers/RepositoryImpl.java b/app/src/main/java/code/name/monkey/retromusic/providers/RepositoryImpl.java index 7deda91e..d65c7b6b 100644 --- a/app/src/main/java/code/name/monkey/retromusic/providers/RepositoryImpl.java +++ b/app/src/main/java/code/name/monkey/retromusic/providers/RepositoryImpl.java @@ -6,7 +6,7 @@ import java.io.File; import java.util.ArrayList; import code.name.monkey.retromusic.Injection; -import code.name.monkey.retromusic.RetroApplication; +import code.name.monkey.retromusic.App; import code.name.monkey.retromusic.loaders.AlbumLoader; import code.name.monkey.retromusic.loaders.ArtistLoader; import code.name.monkey.retromusic.loaders.GenreLoader; @@ -41,7 +41,7 @@ public class RepositoryImpl implements Repository { public static synchronized RepositoryImpl getInstance() { if (INSTANCE == null) { - INSTANCE = new RepositoryImpl(RetroApplication.Companion.getInstance()); + INSTANCE = new RepositoryImpl(App.Companion.getInstance()); } return INSTANCE; } diff --git a/app/src/main/java/code/name/monkey/retromusic/rest/KogouClient.java b/app/src/main/java/code/name/monkey/retromusic/rest/KogouClient.java index fcf0c7cd..dca902fa 100644 --- a/app/src/main/java/code/name/monkey/retromusic/rest/KogouClient.java +++ b/app/src/main/java/code/name/monkey/retromusic/rest/KogouClient.java @@ -6,7 +6,7 @@ import java.io.File; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import code.name.monkey.retromusic.RetroApplication; +import code.name.monkey.retromusic.App; import code.name.monkey.retromusic.rest.service.KuGouApiService; import okhttp3.Cache; import okhttp3.Call; @@ -69,7 +69,7 @@ public class KogouClient { return new OkHttpClient.Builder() .addInterceptor(interceptor) - .cache(createDefaultCache(RetroApplication.Companion.getInstance())) + .cache(createDefaultCache(App.Companion.getInstance())) .addInterceptor(createCacheControlInterceptor()); } diff --git a/app/src/main/java/code/name/monkey/retromusic/service/MultiPlayer.java b/app/src/main/java/code/name/monkey/retromusic/service/MultiPlayer.java new file mode 100644 index 00000000..d725f40b --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/service/MultiPlayer.java @@ -0,0 +1,334 @@ +package code.name.monkey.retromusic.service; + +import android.content.Context; +import android.content.Intent; +import android.media.AudioManager; +import android.media.MediaPlayer; +import android.media.audiofx.AudioEffect; +import android.net.Uri; +import android.os.PowerManager; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import android.util.Log; +import android.widget.Toast; + +import code.name.monkey.retromusic.R; +import code.name.monkey.retromusic.service.playback.Playback; +import code.name.monkey.retromusic.util.PreferenceUtil; + +/** + * @author Andrew Neal, Karim Abou Zeid (kabouzeid) + */ +public class MultiPlayer implements Playback, MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener { + public static final String TAG = MultiPlayer.class.getSimpleName(); + + private MediaPlayer mCurrentMediaPlayer = new MediaPlayer(); + private MediaPlayer mNextMediaPlayer; + + private Context context; + @Nullable + private Playback.PlaybackCallbacks callbacks; + + private boolean mIsInitialized = false; + + /** + * Constructor of MultiPlayer + */ + MultiPlayer(final Context context) { + this.context = context; + mCurrentMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK); + } + + /** + * @param path The path of the file, or the http/rtsp URL of the stream + * you want to play + * @return True if the player has been prepared and is + * ready to play, false otherwise + */ + @Override + public boolean setDataSource(@NonNull final String path) { + mIsInitialized = false; + mIsInitialized = setDataSourceImpl(mCurrentMediaPlayer, path); + if (mIsInitialized) { + setNextDataSource(null); + } + return mIsInitialized; + } + + /** + * @param player The {@link MediaPlayer} to use + * @param path The path of the file, or the http/rtsp URL of the stream + * you want to play + * @return True if the player has been prepared and is + * ready to play, false otherwise + */ + private boolean setDataSourceImpl(@NonNull final MediaPlayer player, @NonNull final String path) { + if (context == null) { + return false; + } + try { + player.reset(); + player.setOnPreparedListener(null); + if (path.startsWith("content://")) { + player.setDataSource(context, Uri.parse(path)); + } else { + player.setDataSource(path); + } + player.setAudioStreamType(AudioManager.STREAM_MUSIC); + player.prepare(); + } catch (Exception e) { + return false; + } + player.setOnCompletionListener(this); + player.setOnErrorListener(this); + final Intent intent = new Intent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION); + intent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, getAudioSessionId()); + intent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, context.getPackageName()); + intent.putExtra(AudioEffect.EXTRA_CONTENT_TYPE, AudioEffect.CONTENT_TYPE_MUSIC); + context.sendBroadcast(intent); + return true; + } + + /** + * Set the MediaPlayer to start when this MediaPlayer finishes playback. + * + * @param path The path of the file, or the http/rtsp URL of the stream + * you want to play + */ + @Override + public void setNextDataSource(@Nullable final String path) { + if (context == null) { + return; + } + try { + mCurrentMediaPlayer.setNextMediaPlayer(null); + } catch (IllegalArgumentException e) { + Log.i(TAG, "Next media player is current one, continuing"); + } catch (IllegalStateException e) { + Log.e(TAG, "Media player not initialized!"); + return; + } + if (mNextMediaPlayer != null) { + mNextMediaPlayer.release(); + mNextMediaPlayer = null; + } + if (path == null) { + return; + } + if (PreferenceUtil.getInstance().gaplessPlayback()) { + mNextMediaPlayer = new MediaPlayer(); + mNextMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK); + mNextMediaPlayer.setAudioSessionId(getAudioSessionId()); + if (setDataSourceImpl(mNextMediaPlayer, path)) { + try { + mCurrentMediaPlayer.setNextMediaPlayer(mNextMediaPlayer); + } catch (@NonNull IllegalArgumentException | IllegalStateException e) { + Log.e(TAG, "setNextDataSource: setNextMediaPlayer()", e); + if (mNextMediaPlayer != null) { + mNextMediaPlayer.release(); + mNextMediaPlayer = null; + } + } + } else { + if (mNextMediaPlayer != null) { + mNextMediaPlayer.release(); + mNextMediaPlayer = null; + } + } + } + } + + /** + * Sets the callbacks + * + * @param callbacks The callbacks to use + */ + @Override + public void setCallbacks(@Nullable final Playback.PlaybackCallbacks callbacks) { + this.callbacks = callbacks; + } + + /** + * @return True if the player is ready to go, false otherwise + */ + @Override + public boolean isInitialized() { + return mIsInitialized; + } + + /** + * Starts or resumes playback. + */ + @Override + public boolean start() { + try { + mCurrentMediaPlayer.start(); + return true; + } catch (IllegalStateException e) { + return false; + } + } + + /** + * Resets the MediaPlayer to its uninitialized state. + */ + @Override + public void stop() { + mCurrentMediaPlayer.reset(); + mIsInitialized = false; + } + + /** + * Releases resources associated with this MediaPlayer object. + */ + @Override + public void release() { + stop(); + mCurrentMediaPlayer.release(); + if (mNextMediaPlayer != null) { + mNextMediaPlayer.release(); + } + } + + /** + * Pauses playback. Call start() to resume. + */ + @Override + public boolean pause() { + try { + mCurrentMediaPlayer.pause(); + return true; + } catch (IllegalStateException e) { + return false; + } + } + + /** + * Checks whether the MultiPlayer is playing. + */ + @Override + public boolean isPlaying() { + return mIsInitialized && mCurrentMediaPlayer.isPlaying(); + } + + /** + * Gets the duration of the file. + * + * @return The duration in milliseconds + */ + @Override + public int duration() { + if (!mIsInitialized) { + return -1; + } + try { + return mCurrentMediaPlayer.getDuration(); + } catch (IllegalStateException e) { + return -1; + } + } + + /** + * Gets the current playback position. + * + * @return The current position in milliseconds + */ + @Override + public int position() { + if (!mIsInitialized) { + return -1; + } + try { + return mCurrentMediaPlayer.getCurrentPosition(); + } catch (IllegalStateException e) { + return -1; + } + } + + /** + * Gets the current playback position. + * + * @param whereto The offset in milliseconds from the start to seek to + * @return The offset in milliseconds from the start to seek to + */ + @Override + public int seek(final int whereto) { + try { + mCurrentMediaPlayer.seekTo(whereto); + return whereto; + } catch (IllegalStateException e) { + return -1; + } + } + + @Override + public boolean setVolume(final float vol) { + try { + mCurrentMediaPlayer.setVolume(vol, vol); + return true; + } catch (IllegalStateException e) { + return false; + } + } + + /** + * Sets the audio session ID. + * + * @param sessionId The audio session ID + */ + @Override + public boolean setAudioSessionId(final int sessionId) { + try { + mCurrentMediaPlayer.setAudioSessionId(sessionId); + return true; + } catch (@NonNull IllegalArgumentException | IllegalStateException e) { + return false; + } + } + + /** + * Returns the audio session ID. + * + * @return The current audio session ID. + */ + @Override + public int getAudioSessionId() { + return mCurrentMediaPlayer.getAudioSessionId(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean onError(final MediaPlayer mp, final int what, final int extra) { + mIsInitialized = false; + mCurrentMediaPlayer.release(); + mCurrentMediaPlayer = new MediaPlayer(); + mCurrentMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK); + if (context != null) { + Toast.makeText(context, context.getResources().getString(R.string.unplayable_file), Toast.LENGTH_SHORT).show(); + } + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public void onCompletion(final MediaPlayer mp) { + if (mp == mCurrentMediaPlayer && mNextMediaPlayer != null) { + mIsInitialized = false; + mCurrentMediaPlayer.release(); + mCurrentMediaPlayer = mNextMediaPlayer; + mIsInitialized = true; + mNextMediaPlayer = null; + if (callbacks != null) + callbacks.onTrackWentToNext(); + } else { + if (callbacks != null) + callbacks.onTrackEnded(); + } + } + + +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/service/MultiPlayer.kt b/app/src/main/java/code/name/monkey/retromusic/service/MultiPlayer.kt deleted file mode 100644 index ba24b958..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/service/MultiPlayer.kt +++ /dev/null @@ -1,333 +0,0 @@ -package code.name.monkey.retromusic.service - -import android.content.Context -import android.content.Intent -import android.media.AudioManager -import android.media.MediaPlayer -import android.media.audiofx.AudioEffect -import android.net.Uri -import android.os.PowerManager -import android.util.Log -import android.widget.Toast - -import code.name.monkey.retromusic.R -import code.name.monkey.retromusic.service.playback.Playback -import code.name.monkey.retromusic.util.PreferenceUtil - -/** - * @author Andrew Neal, Karim Abou Zeid (kabouzeid) - */ -class MultiPlayer -/** - * Constructor of `MultiPlayer` - */ -internal constructor(private val context: Context?) : Playback, MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener { - - private var mCurrentMediaPlayer = MediaPlayer() - private var mNextMediaPlayer: MediaPlayer? = null - private var callbacks: Playback.PlaybackCallbacks? = null - - private var mIsInitialized = false - - init { - mCurrentMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK) - } - - /** - * @param path The path of the file, or the http/rtsp URL of the stream - * you want to play - * @return True if the `player` has been prepared and is - * ready to play, false otherwise - */ - override fun setDataSource(path: String): Boolean { - mIsInitialized = false - mIsInitialized = setDataSourceImpl(mCurrentMediaPlayer, path) - if (mIsInitialized) { - setNextDataSource(null) - } - return mIsInitialized - } - - /** - * @param player The [MediaPlayer] to use - * @param path The path of the file, or the http/rtsp URL of the stream - * you want to play - * @return True if the `player` has been prepared and is - * ready to play, false otherwise - */ - private fun setDataSourceImpl(player: MediaPlayer, path: String): Boolean { - if (context == null) { - return false - } - try { - player.reset() - player.setOnPreparedListener(null) - if (path.startsWith("content://")) { - player.setDataSource(context, Uri.parse(path)) - } else { - player.setDataSource(path) - } - player.setAudioStreamType(AudioManager.STREAM_MUSIC) - player.prepare() - } catch (e: Exception) { - return false - } - - player.setOnCompletionListener(this) - player.setOnErrorListener(this) - val intent = Intent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION) - intent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, audioSessionId) - intent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, context.packageName) - intent.putExtra(AudioEffect.EXTRA_CONTENT_TYPE, AudioEffect.CONTENT_TYPE_MUSIC) - context.sendBroadcast(intent) - return true - } - - /** - * Set the MediaPlayer to start when this MediaPlayer finishes playback. - * - * @param path The path of the file, or the http/rtsp URL of the stream - * you want to play - */ - override fun setNextDataSource(path: String?) { - if (context == null) { - return - } - try { - mCurrentMediaPlayer.setNextMediaPlayer(null) - } catch (e: IllegalArgumentException) { - Log.i(TAG, "Next media player is current one, continuing") - } catch (e: IllegalStateException) { - Log.e(TAG, "Media player not initialized!") - return - } - - if (mNextMediaPlayer != null) { - mNextMediaPlayer!!.release() - mNextMediaPlayer = null - } - if (path == null) { - return - } - if (PreferenceUtil.getInstance().gaplessPlayback()) { - mNextMediaPlayer = MediaPlayer() - mNextMediaPlayer!!.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK) - mNextMediaPlayer!!.audioSessionId = audioSessionId - if (setDataSourceImpl(mNextMediaPlayer!!, path)) { - try { - mCurrentMediaPlayer.setNextMediaPlayer(mNextMediaPlayer) - } catch (e: IllegalArgumentException) { - Log.e(TAG, "setNextDataSource: setNextMediaPlayer()", e) - if (mNextMediaPlayer != null) { - mNextMediaPlayer!!.release() - mNextMediaPlayer = null - } - } catch (e: IllegalStateException) { - Log.e(TAG, "setNextDataSource: setNextMediaPlayer()", e) - if (mNextMediaPlayer != null) { - mNextMediaPlayer!!.release() - mNextMediaPlayer = null - } - } - - } else { - if (mNextMediaPlayer != null) { - mNextMediaPlayer!!.release() - mNextMediaPlayer = null - } - } - } - } - - /** - * Sets the callbacks - * - * @param callbacks The callbacks to use - */ - override fun setCallbacks(callbacks: Playback.PlaybackCallbacks?) { - this.callbacks = callbacks - } - - /** - * @return True if the player is ready to go, false otherwise - */ - override fun isInitialized(): Boolean { - return mIsInitialized - } - - /** - * Starts or resumes playback. - */ - override fun start(): Boolean { - try { - mCurrentMediaPlayer.start() - return true - } catch (e: IllegalStateException) { - return false - } - - } - - /** - * Resets the MediaPlayer to its uninitialized state. - */ - override fun stop() { - mCurrentMediaPlayer.reset() - mIsInitialized = false - } - - /** - * Releases resources associated with this MediaPlayer object. - */ - override fun release() { - stop() - mCurrentMediaPlayer.release() - if (mNextMediaPlayer != null) { - mNextMediaPlayer!!.release() - } - } - - /** - * Pauses playback. Call start() to resume. - */ - override fun pause(): Boolean { - try { - mCurrentMediaPlayer.pause() - return true - } catch (e: IllegalStateException) { - return false - } - - } - - /** - * Checks whether the MultiPlayer is playing. - */ - override fun isPlaying(): Boolean { - return mIsInitialized && mCurrentMediaPlayer.isPlaying - } - - /** - * Gets the duration of the file. - * - * @return The duration in milliseconds - */ - override fun duration(): Int { - if (!mIsInitialized) { - return -1 - } - try { - return mCurrentMediaPlayer.duration - } catch (e: IllegalStateException) { - return -1 - } - - } - - /** - * Gets the current playback position. - * - * @return The current position in milliseconds - */ - override fun position(): Int { - if (!mIsInitialized) { - return -1 - } - try { - return mCurrentMediaPlayer.currentPosition - } catch (e: IllegalStateException) { - return -1 - } - - } - - /** - * Gets the current playback position. - * - * @param whereto The offset in milliseconds from the start to seek to - * @return The offset in milliseconds from the start to seek to - */ - override fun seek(whereto: Int): Int { - try { - mCurrentMediaPlayer.seekTo(whereto) - return whereto - } catch (e: IllegalStateException) { - return -1 - } - - } - - override fun setVolume(vol: Float): Boolean { - try { - mCurrentMediaPlayer.setVolume(vol, vol) - return true - } catch (e: IllegalStateException) { - return false - } - - } - - /** - * Sets the audio session ID. - * - * @param sessionId The audio session ID - */ - override fun setAudioSessionId(sessionId: Int): Boolean { - try { - mCurrentMediaPlayer.audioSessionId = sessionId - return true - } catch (e: IllegalArgumentException) { - return false - } catch (e: IllegalStateException) { - return false - } - - } - - /** - * Returns the audio session ID. - * - * @return The current audio session ID. - */ - override fun getAudioSessionId(): Int { - return mCurrentMediaPlayer.audioSessionId - } - - /** - * {@inheritDoc} - */ - override fun onError(mp: MediaPlayer, what: Int, extra: Int): Boolean { - mIsInitialized = false - mCurrentMediaPlayer.release() - mCurrentMediaPlayer = MediaPlayer() - mCurrentMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK) - if (context != null) { - Toast.makeText(context, context.resources.getString(R.string.unplayable_file), Toast.LENGTH_SHORT).show() - } - return false - } - - /** - * {@inheritDoc} - */ - override fun onCompletion(mp: MediaPlayer) { - if (mp === mCurrentMediaPlayer && mNextMediaPlayer != null) { - mIsInitialized = false - mCurrentMediaPlayer.release() - mCurrentMediaPlayer = mNextMediaPlayer as MediaPlayer - mIsInitialized = true - mNextMediaPlayer = null - if (callbacks != null) - callbacks!!.onTrackWentToNext() - } else { - if (callbacks != null) - callbacks!!.onTrackEnded() - } - } - - companion object { - val TAG: String = MultiPlayer::class.java.simpleName - } - - -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/service/MusicService.java b/app/src/main/java/code/name/monkey/retromusic/service/MusicService.java new file mode 100644 index 00000000..39d4e0f0 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/service/MusicService.java @@ -0,0 +1,1420 @@ +package code.name.monkey.retromusic.service; + +import android.app.PendingIntent; +import android.app.Service; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.SharedPreferences; +import android.database.ContentObserver; +import android.graphics.Bitmap; +import android.graphics.Point; +import android.graphics.drawable.Drawable; +import android.media.AudioManager; +import android.media.audiofx.AudioEffect; +import android.media.session.MediaSession; +import android.os.Binder; +import android.os.Build; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.PowerManager; +import android.os.Process; +import android.preference.PreferenceManager; +import android.provider.MediaStore; +import android.support.v4.media.MediaMetadataCompat; +import android.support.v4.media.session.MediaSessionCompat; +import android.support.v4.media.session.PlaybackStateCompat; +import android.telephony.PhoneStateListener; +import android.telephony.TelephonyManager; +import android.util.Log; +import android.widget.Toast; + +import com.bumptech.glide.BitmapRequestBuilder; +import com.bumptech.glide.Glide; +import com.bumptech.glide.request.animation.GlideAnimation; +import com.bumptech.glide.request.target.SimpleTarget; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import code.name.monkey.retromusic.R; +import code.name.monkey.retromusic.glide.BlurTransformation; +import code.name.monkey.retromusic.glide.SongGlideRequest; +import code.name.monkey.retromusic.helper.ShuffleHelper; +import code.name.monkey.retromusic.helper.StopWatch; +import code.name.monkey.retromusic.loaders.PlaylistSongsLoader; +import code.name.monkey.retromusic.model.AbsCustomPlaylist; +import code.name.monkey.retromusic.model.Playlist; +import code.name.monkey.retromusic.model.Song; +import code.name.monkey.retromusic.providers.HistoryStore; +import code.name.monkey.retromusic.providers.MusicPlaybackQueueStore; +import code.name.monkey.retromusic.providers.SongPlayCountStore; +import code.name.monkey.retromusic.service.notification.PlayingNotification; +import code.name.monkey.retromusic.service.notification.PlayingNotificationImpl24; +import code.name.monkey.retromusic.service.notification.PlayingNotificationOreo; +import code.name.monkey.retromusic.service.playback.Playback; +import code.name.monkey.retromusic.util.MusicUtil; +import code.name.monkey.retromusic.util.PreferenceUtil; +import code.name.monkey.retromusic.util.RetroUtil; + +import static code.name.monkey.retromusic.Constants.ACTION_PAUSE; +import static code.name.monkey.retromusic.Constants.ACTION_PLAY; +import static code.name.monkey.retromusic.Constants.ACTION_PLAY_PLAYLIST; +import static code.name.monkey.retromusic.Constants.ACTION_QUIT; +import static code.name.monkey.retromusic.Constants.ACTION_REWIND; +import static code.name.monkey.retromusic.Constants.ACTION_SKIP; +import static code.name.monkey.retromusic.Constants.ACTION_STOP; +import static code.name.monkey.retromusic.Constants.ACTION_TOGGLE_PAUSE; +import static code.name.monkey.retromusic.Constants.APP_WIDGET_UPDATE; +import static code.name.monkey.retromusic.Constants.EXTRA_APP_WIDGET_NAME; +import static code.name.monkey.retromusic.Constants.INTENT_EXTRA_PLAYLIST; +import static code.name.monkey.retromusic.Constants.INTENT_EXTRA_SHUFFLE_MODE; +import static code.name.monkey.retromusic.Constants.MEDIA_STORE_CHANGED; +import static code.name.monkey.retromusic.Constants.META_CHANGED; +import static code.name.monkey.retromusic.Constants.MUSIC_PACKAGE_NAME; +import static code.name.monkey.retromusic.Constants.PLAY_STATE_CHANGED; +import static code.name.monkey.retromusic.Constants.QUEUE_CHANGED; +import static code.name.monkey.retromusic.Constants.REPEAT_MODE_CHANGED; +import static code.name.monkey.retromusic.Constants.RETRO_MUSIC_PACKAGE_NAME; +import static code.name.monkey.retromusic.Constants.SHUFFLE_MODE_CHANGED; + +/** + * @author Karim Abou Zeid (kabouzeid), Andrew Neal + */ +public class MusicService extends Service implements SharedPreferences.OnSharedPreferenceChangeListener, Playback.PlaybackCallbacks { + public static final String TAG = MusicService.class.getSimpleName(); + + public static final String SAVED_POSITION = "POSITION"; + public static final String SAVED_POSITION_IN_TRACK = "POSITION_IN_TRACK"; + public static final String SAVED_SHUFFLE_MODE = "SHUFFLE_MODE"; + public static final String SAVED_REPEAT_MODE = "REPEAT_MODE"; + + public static final int RELEASE_WAKELOCK = 0; + public static final int TRACK_ENDED = 1; + public static final int TRACK_WENT_TO_NEXT = 2; + public static final int PLAY_SONG = 3; + public static final int PREPARE_NEXT = 4; + public static final int SET_POSITION = 5; + public static final int RESTORE_QUEUES = 9; + public static final int SHUFFLE_MODE_NONE = 0; + public static final int SHUFFLE_MODE_SHUFFLE = 1; + public static final int REPEAT_MODE_NONE = 0; + public static final int REPEAT_MODE_ALL = 1; + public static final int REPEAT_MODE_THIS = 2; + public static final int SAVE_QUEUES = 0; + private static final int FOCUS_CHANGE = 6; + private static final int DUCK = 7; + private static final int UNDUCK = 8; + private static final long MEDIA_SESSION_ACTIONS = PlaybackStateCompat.ACTION_PLAY + | PlaybackStateCompat.ACTION_PAUSE + | PlaybackStateCompat.ACTION_PLAY_PAUSE + | PlaybackStateCompat.ACTION_SKIP_TO_NEXT + | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS + | PlaybackStateCompat.ACTION_STOP + | PlaybackStateCompat.ACTION_SEEK_TO; + private final IBinder musicBind = new MusicBinder(); + private final BroadcastReceiver widgetIntentReceiver = new BroadcastReceiver() { + @Override + public void onReceive(final Context context, final Intent intent) { + final String command = intent.getStringExtra(EXTRA_APP_WIDGET_NAME); + + } + }; + private Playback playback; + private ArrayList playingQueue = new ArrayList<>(); + private ArrayList originalPlayingQueue = new ArrayList<>(); + private int position = -1; + private int nextPosition = -1; + private int shuffleMode; + private int repeatMode; + private boolean queuesRestored; + private boolean pausedByTransientLossOfFocus; + private final BroadcastReceiver becomingNoisyReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, @NonNull Intent intent) { + if (intent.getAction() != null && intent.getAction().equals(AudioManager.ACTION_AUDIO_BECOMING_NOISY)) { + pause(); + } + } + }; + private PlayingNotification playingNotification; + private AudioManager audioManager; + @SuppressWarnings("deprecation") + private MediaSessionCompat mediaSession; + private PowerManager.WakeLock wakeLock; + private PlaybackHandler playerHandler; + private final AudioManager.OnAudioFocusChangeListener audioFocusListener = new AudioManager.OnAudioFocusChangeListener() { + @Override + public void onAudioFocusChange(final int focusChange) { + playerHandler.obtainMessage(FOCUS_CHANGE, focusChange, 0).sendToTarget(); + } + }; + private QueueSaveHandler queueSaveHandler; + private HandlerThread musicPlayerHandlerThread; + private HandlerThread queueSaveHandlerThread; + private SongPlayCountHelper songPlayCountHelper = new SongPlayCountHelper(); + private ThrottledSeekHandler throttledSeekHandler; + private boolean becomingNoisyReceiverRegistered; + private IntentFilter becomingNoisyReceiverIntentFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY); + private ContentObserver mediaStoreObserver; + private boolean notHandledMetaChangedForCurrentTrack; + private PhoneStateListener phoneStateListener = new PhoneStateListener() { + @Override + public void onCallStateChanged(int state, String incomingNumber) { + switch (state) { + case TelephonyManager.CALL_STATE_IDLE: + //Not in call: Play music + play(); + break; + case TelephonyManager.CALL_STATE_RINGING: + case TelephonyManager.CALL_STATE_OFFHOOK: + //A call is dialing, active or on hold + pause(); + break; + default: + } + super.onCallStateChanged(state, incomingNumber); + } + }; + private boolean isServiceBound; + private Handler uiThreadHandler; + private IntentFilter headsetReceiverIntentFilter = new IntentFilter(Intent.ACTION_HEADSET_PLUG); + private boolean headsetReceiverRegistered = false; + private BroadcastReceiver headsetReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (action != null) { + switch (action) { + case Intent.ACTION_HEADSET_PLUG: + int state = intent.getIntExtra("state", -1); + switch (state) { + case 0: + Log.d(TAG, "Headset unplugged"); + pause(); + break; + case 1: + Log.d(TAG, "Headset plugged"); + play(); + break; + } + break; + } + } + } + }; + + private static String getTrackUri(@NonNull Song song) { + return MusicUtil.getSongFileUri(song.getId()).toString(); + } + + private static Bitmap copy(Bitmap bitmap) { + Bitmap.Config config = bitmap.getConfig(); + if (config == null) { + config = Bitmap.Config.RGB_565; + } + try { + return bitmap.copy(config, false); + } catch (OutOfMemoryError e) { + e.printStackTrace(); + return null; + } + } + + + @Override + public void onCreate() { + super.onCreate(); + + final TelephonyManager telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE); + if (telephonyManager != null) { + telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE); + } + + final PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); + if (powerManager != null) { + wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getClass().getName()); + } + wakeLock.setReferenceCounted(false); + + musicPlayerHandlerThread = new HandlerThread("PlaybackHandler"); + musicPlayerHandlerThread.start(); + playerHandler = new PlaybackHandler(this, musicPlayerHandlerThread.getLooper()); + + playback = new MultiPlayer(this); + playback.setCallbacks(this); + + setupMediaSession(); + + // queue saving needs to run on a separate thread so that it doesn't block the playback handler events + queueSaveHandlerThread = new HandlerThread("QueueSaveHandler", Process.THREAD_PRIORITY_BACKGROUND); + queueSaveHandlerThread.start(); + queueSaveHandler = new QueueSaveHandler(this, queueSaveHandlerThread.getLooper()); + + uiThreadHandler = new Handler(); + + registerReceiver(widgetIntentReceiver, new IntentFilter(APP_WIDGET_UPDATE)); + + initNotification(); + + mediaStoreObserver = new MediaStoreObserver(playerHandler); + throttledSeekHandler = new ThrottledSeekHandler(playerHandler); + getContentResolver().registerContentObserver( + MediaStore.Audio.Media.INTERNAL_CONTENT_URI, true, mediaStoreObserver); + getContentResolver().registerContentObserver( + MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, true, mediaStoreObserver); + + PreferenceUtil.getInstance().registerOnSharedPreferenceChangedListener(this); + + restoreState(); + + mediaSession.setActive(true); + + sendBroadcast(new Intent("code.name.monkey.retromusic.RETRO_MUSIC_SERVICE_CREATED")); + + registerHeadsetEvents(); + + + } + + private AudioManager getAudioManager() { + if (audioManager == null) { + audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); + } + return audioManager; + } + + private void setupMediaSession() { + ComponentName mediaButtonReceiverComponentName = new ComponentName(getApplicationContext(), MediaButtonIntentReceiver.class); + + Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); + mediaButtonIntent.setComponent(mediaButtonReceiverComponentName); + + + PendingIntent mediaButtonReceiverPendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, mediaButtonIntent, 0); + + mediaSession = new MediaSessionCompat(this, "RetroMusicPlayer", mediaButtonReceiverComponentName, mediaButtonReceiverPendingIntent); + mediaSession.setCallback(new MediaSessionCompat.Callback() { + @Override + public void onPlay() { + play(); + } + + @Override + public void onPause() { + pause(); + } + + @Override + public void onSkipToNext() { + playNextSong(true); + } + + @Override + public void onSkipToPrevious() { + back(true); + } + + @Override + public void onStop() { + quit(); + } + + @Override + public void onSeekTo(long pos) { + seek((int) pos); + } + + @Override + public boolean onMediaButtonEvent(Intent mediaButtonEvent) { + return MediaButtonIntentReceiver.Companion.handleIntent(MusicService.this, mediaButtonEvent); + } + }); + + mediaSession.setFlags(MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS + | MediaSession.FLAG_HANDLES_MEDIA_BUTTONS); + + mediaSession.setMediaButtonReceiver(mediaButtonReceiverPendingIntent); + } + + @Override + public int onStartCommand(@Nullable Intent intent, int flags, int startId) { + if (intent != null) { + if (intent.getAction() != null) { + restoreQueuesAndPositionIfNecessary(); + String action = intent.getAction(); + switch (action) { + case ACTION_TOGGLE_PAUSE: + if (isPlaying()) { + pause(); + } else { + play(); + } + break; + case ACTION_PAUSE: + pause(); + break; + case ACTION_PLAY: + play(); + break; + case ACTION_PLAY_PLAYLIST: + Playlist playlist = intent.getParcelableExtra(INTENT_EXTRA_PLAYLIST); + int shuffleMode = intent.getIntExtra(INTENT_EXTRA_SHUFFLE_MODE, getShuffleMode()); + if (playlist != null) { + ArrayList playlistSongs; + if (playlist instanceof AbsCustomPlaylist) { + playlistSongs = ((AbsCustomPlaylist) playlist).getSongs(getApplicationContext()).blockingFirst(); + } else { + //noinspection unchecked + playlistSongs = PlaylistSongsLoader.INSTANCE.getPlaylistSongList(getApplicationContext(), playlist.id).blockingFirst(); + } + if (!playlistSongs.isEmpty()) { + if (shuffleMode == SHUFFLE_MODE_SHUFFLE) { + int startPosition; + startPosition = new Random().nextInt(playlistSongs.size()); + openQueue(playlistSongs, startPosition, true); + setShuffleMode(shuffleMode); + } else { + openQueue(playlistSongs, 0, true); + } + } else { + Toast.makeText(getApplicationContext(), R.string.playlist_is_empty, Toast.LENGTH_LONG).show(); + } + } else { + Toast.makeText(getApplicationContext(), R.string.playlist_is_empty, Toast.LENGTH_LONG).show(); + } + break; + case ACTION_REWIND: + back(true); + break; + case ACTION_SKIP: + playNextSong(true); + break; + case ACTION_STOP: + case ACTION_QUIT: + return quit(); + } + } + } + + return START_STICKY; + } + + + @Override + public void onDestroy() { + unregisterReceiver(widgetIntentReceiver); + if (becomingNoisyReceiverRegistered) { + unregisterReceiver(becomingNoisyReceiver); + becomingNoisyReceiverRegistered = false; + } + if (headsetReceiverRegistered) { + unregisterReceiver(headsetReceiver); + headsetReceiverRegistered = false; + } + mediaSession.setActive(false); + quit(); + releaseResources(); + getContentResolver().unregisterContentObserver(mediaStoreObserver); + PreferenceUtil.getInstance().unregisterOnSharedPreferenceChangedListener(this); + wakeLock.release(); + + sendBroadcast(new Intent("code.name.monkey.retromusic.RETRO_MUSIC_MUSIC_SERVICE_DESTROYED")); + } + + @Override + public IBinder onBind(Intent intent) { + isServiceBound = true; + return musicBind; + } + + @Override + public void onRebind(Intent intent) { + isServiceBound = true; + } + + @Override + public boolean onUnbind(Intent intent) { + isServiceBound = false; + if (!isPlaying()) { + stopSelf(); + } + return true; + } + + private void saveQueuesImpl() { + MusicPlaybackQueueStore.getInstance(this).saveQueues(playingQueue, originalPlayingQueue); + } + + private void savePosition() { + PreferenceManager.getDefaultSharedPreferences(this).edit().putInt(SAVED_POSITION, getPosition()).apply(); + } + + private void savePositionInTrack() { + PreferenceManager.getDefaultSharedPreferences(this).edit().putInt(SAVED_POSITION_IN_TRACK, getSongProgressMillis()).apply(); + } + + public void saveState() { + saveQueues(); + savePosition(); + savePositionInTrack(); + } + + private void saveQueues() { + queueSaveHandler.removeMessages(SAVE_QUEUES); + queueSaveHandler.sendEmptyMessage(SAVE_QUEUES); + } + + private void restoreState() { + shuffleMode = PreferenceManager.getDefaultSharedPreferences(this).getInt(SAVED_SHUFFLE_MODE, 0); + repeatMode = PreferenceManager.getDefaultSharedPreferences(this).getInt(SAVED_REPEAT_MODE, 0); + handleAndSendChangeInternal(SHUFFLE_MODE_CHANGED); + handleAndSendChangeInternal(REPEAT_MODE_CHANGED); + + playerHandler.removeMessages(RESTORE_QUEUES); + playerHandler.sendEmptyMessage(RESTORE_QUEUES); + } + + private synchronized void restoreQueuesAndPositionIfNecessary() { + if (!queuesRestored && playingQueue.isEmpty()) { + ArrayList restoredQueue = MusicPlaybackQueueStore.getInstance(this).getSavedPlayingQueue() + .blockingFirst(); + + ArrayList restoredOriginalQueue = MusicPlaybackQueueStore.getInstance(this).getSavedOriginalPlayingQueue() + .blockingFirst(); + + int restoredPosition = PreferenceManager.getDefaultSharedPreferences(this).getInt(SAVED_POSITION, -1); + int restoredPositionInTrack = PreferenceManager.getDefaultSharedPreferences(this).getInt(SAVED_POSITION_IN_TRACK, -1); + + if (restoredQueue.size() > 0 && restoredQueue.size() == restoredOriginalQueue.size() && restoredPosition != -1) { + this.originalPlayingQueue = restoredOriginalQueue; + this.playingQueue = restoredQueue; + + position = restoredPosition; + openCurrent(); + prepareNext(); + + if (restoredPositionInTrack > 0) seek(restoredPositionInTrack); + + notHandledMetaChangedForCurrentTrack = true; + sendChangeInternal(META_CHANGED); + sendChangeInternal(QUEUE_CHANGED); + } + } + queuesRestored = true; + } + + private int quit() { + pause(); + playingNotification.stop(); + + if (isServiceBound) { + return START_STICKY; + } else { + closeAudioEffectSession(); + getAudioManager().abandonAudioFocus(audioFocusListener); + stopSelf(); + return START_NOT_STICKY; + } + } + + private void releaseResources() { + playerHandler.removeCallbacksAndMessages(null); + musicPlayerHandlerThread.quitSafely(); + queueSaveHandler.removeCallbacksAndMessages(null); + queueSaveHandlerThread.quitSafely(); + playback.release(); + playback = null; + mediaSession.release(); + } + + public boolean isPlaying() { + return playback != null && playback.isPlaying(); + } + + public int getPosition() { + return position; + } + + public void setPosition(final int position) { + // handle this on the handlers thread to avoid blocking the ui thread + playerHandler.removeMessages(SET_POSITION); + playerHandler.obtainMessage(SET_POSITION, position, 0).sendToTarget(); + } + + public void playNextSong(boolean force) { + playSongAt(getNextPosition(force)); + } + + private boolean openTrackAndPrepareNextAt(int position) { + synchronized (this) { + this.position = position; + boolean prepared = openCurrent(); + if (prepared) prepareNextImpl(); + notifyChange(META_CHANGED); + notHandledMetaChangedForCurrentTrack = false; + return prepared; + } + } + + private boolean openCurrent() { + synchronized (this) { + try { + return playback.setDataSource(getTrackUri(getCurrentSong())); + } catch (Exception e) { + return false; + } + } + } + + private void prepareNext() { + playerHandler.removeMessages(PREPARE_NEXT); + playerHandler.obtainMessage(PREPARE_NEXT).sendToTarget(); + } + + private boolean prepareNextImpl() { + synchronized (this) { + try { + int nextPosition = getNextPosition(false); + playback.setNextDataSource(getTrackUri(getSongAt(nextPosition))); + this.nextPosition = nextPosition; + return true; + } catch (Exception e) { + return false; + } + } + } + + private void closeAudioEffectSession() { + final Intent audioEffectsIntent = new Intent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION); + audioEffectsIntent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, playback.getAudioSessionId()); + audioEffectsIntent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, getPackageName()); + sendBroadcast(audioEffectsIntent); + } + + private boolean requestFocus() { + return (getAudioManager().requestAudioFocus(audioFocusListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN) == AudioManager.AUDIOFOCUS_REQUEST_GRANTED); + } + + public void initNotification() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && !PreferenceUtil.getInstance().classicNotification()) { + playingNotification = new PlayingNotificationImpl24(); + } else { + playingNotification = new PlayingNotificationOreo(); + } + playingNotification.init(this); + } + + public void updateNotification() { + if (playingNotification != null && getCurrentSong().getId() != -1) { + playingNotification.update(); + } + } + + private void updateMediaSessionPlaybackState() { + mediaSession.setPlaybackState( + new PlaybackStateCompat.Builder() + .setActions(MEDIA_SESSION_ACTIONS) + .setState(isPlaying() ? PlaybackStateCompat.STATE_PLAYING : PlaybackStateCompat.STATE_PAUSED, + getPosition(), 1) + .build()); + } + + private void updateMediaSessionMetaData() { + final Song song = getCurrentSong(); + + if (song.getId() == -1) { + mediaSession.setMetadata(null); + return; + } + + final MediaMetadataCompat.Builder metaData = new MediaMetadataCompat.Builder() + .putString(MediaMetadataCompat.METADATA_KEY_ARTIST, song.getArtistName()) + .putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ARTIST, song.getArtistName()) + .putString(MediaMetadataCompat.METADATA_KEY_ALBUM, song.getAlbumName()) + .putString(MediaMetadataCompat.METADATA_KEY_TITLE, song.getTitle()) + .putLong(MediaMetadataCompat.METADATA_KEY_DURATION, song.getDuration()) + .putLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER, getPosition() + 1) + .putLong(MediaMetadataCompat.METADATA_KEY_YEAR, song.getYear()) + .putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, null); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + metaData.putLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS, getPlayingQueue().size()); + } + + if (PreferenceUtil.getInstance().albumArtOnLockscreen()) { + final Point screenSize = RetroUtil.getScreenSize(MusicService.this); + final BitmapRequestBuilder request = SongGlideRequest.Builder.from(Glide.with(MusicService.this), song) + .checkIgnoreMediaStore(MusicService.this) + .asBitmap().build(); + if (PreferenceUtil.getInstance().blurredAlbumArt()) { + request.transform(new BlurTransformation.Builder(MusicService.this).build()); + } + runOnUiThread(new Runnable() { + @Override + public void run() { + request.into(new SimpleTarget(screenSize.x, screenSize.y) { + @Override + public void onLoadFailed(Exception e, Drawable errorDrawable) { + super.onLoadFailed(e, errorDrawable); + mediaSession.setMetadata(metaData.build()); + } + + @Override + public void onResourceReady(Bitmap resource, GlideAnimation glideAnimation) { + metaData.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, copy(resource)); + mediaSession.setMetadata(metaData.build()); + } + }); + } + }); + } else { + mediaSession.setMetadata(metaData.build()); + } + } + + public void runOnUiThread(Runnable runnable) { + uiThreadHandler.post(runnable); + } + + public Song getCurrentSong() { + return getSongAt(getPosition()); + } + + public Song getSongAt(int position) { + if (position >= 0 && position < getPlayingQueue().size()) { + return getPlayingQueue().get(position); + } else { + return Song.Companion.getEmptySong(); + } + } + + public int getNextPosition(boolean force) { + int position = getPosition() + 1; + switch (getRepeatMode()) { + case REPEAT_MODE_ALL: + if (isLastTrack()) { + position = 0; + } + break; + case REPEAT_MODE_THIS: + if (force) { + if (isLastTrack()) { + position = 0; + } + } else { + position -= 1; + } + break; + default: + case REPEAT_MODE_NONE: + if (isLastTrack()) { + position -= 1; + } + break; + } + return position; + } + + private boolean isLastTrack() { + return getPosition() == getPlayingQueue().size() - 1; + } + + public ArrayList getPlayingQueue() { + return playingQueue; + } + + public int getRepeatMode() { + return repeatMode; + } + + public void setRepeatMode(final int repeatMode) { + switch (repeatMode) { + case REPEAT_MODE_NONE: + case REPEAT_MODE_ALL: + case REPEAT_MODE_THIS: + this.repeatMode = repeatMode; + PreferenceManager.getDefaultSharedPreferences(this).edit() + .putInt(SAVED_REPEAT_MODE, repeatMode) + .apply(); + prepareNext(); + handleAndSendChangeInternal(REPEAT_MODE_CHANGED); + break; + } + } + + public void openQueue(@Nullable final ArrayList playingQueue, final int startPosition, final boolean startPlaying) { + if (playingQueue != null && !playingQueue.isEmpty() && startPosition >= 0 && startPosition < playingQueue.size()) { + // it is important to copy the playing queue here first as we might add/remove songs later + originalPlayingQueue = new ArrayList<>(playingQueue); + this.playingQueue = new ArrayList<>(originalPlayingQueue); + + int position = startPosition; + if (shuffleMode == SHUFFLE_MODE_SHUFFLE) { + ShuffleHelper.INSTANCE.makeShuffleList(this.playingQueue, startPosition); + position = 0; + } + if (startPlaying) { + playSongAt(position); + } else { + setPosition(position); + } + notifyChange(QUEUE_CHANGED); + } + } + + public void addSong(int position, Song song) { + playingQueue.add(position, song); + originalPlayingQueue.add(position, song); + notifyChange(QUEUE_CHANGED); + } + + public void addSong(Song song) { + playingQueue.add(song); + originalPlayingQueue.add(song); + notifyChange(QUEUE_CHANGED); + } + + public void addSongs(int position, List songs) { + playingQueue.addAll(position, songs); + originalPlayingQueue.addAll(position, songs); + notifyChange(QUEUE_CHANGED); + } + + public void addSongs(List songs) { + playingQueue.addAll(songs); + originalPlayingQueue.addAll(songs); + notifyChange(QUEUE_CHANGED); + } + + public void removeSong(int position) { + if (getShuffleMode() == SHUFFLE_MODE_NONE) { + playingQueue.remove(position); + originalPlayingQueue.remove(position); + } else { + originalPlayingQueue.remove(playingQueue.remove(position)); + } + + rePosition(position); + + notifyChange(QUEUE_CHANGED); + } + + public void removeSong(@NonNull Song song) { + for (int i = 0; i < playingQueue.size(); i++) { + if (playingQueue.get(i).getId() == song.getId()) { + playingQueue.remove(i); + rePosition(i); + } + } + for (int i = 0; i < originalPlayingQueue.size(); i++) { + if (originalPlayingQueue.get(i).getId() == song.getId()) { + originalPlayingQueue.remove(i); + } + } + notifyChange(QUEUE_CHANGED); + } + + private void rePosition(int deletedPosition) { + int currentPosition = getPosition(); + if (deletedPosition < currentPosition) { + position = currentPosition - 1; + } else if (deletedPosition == currentPosition) { + if (playingQueue.size() > deletedPosition) { + setPosition(position); + } else { + setPosition(position - 1); + } + } + } + + public void moveSong(int from, int to) { + if (from == to) return; + final int currentPosition = getPosition(); + Song songToMove = playingQueue.remove(from); + playingQueue.add(to, songToMove); + if (getShuffleMode() == SHUFFLE_MODE_NONE) { + Song tmpSong = originalPlayingQueue.remove(from); + originalPlayingQueue.add(to, tmpSong); + } + if (from > currentPosition && to <= currentPosition) { + position = currentPosition + 1; + } else if (from < currentPosition && to >= currentPosition) { + position = currentPosition - 1; + } else if (from == currentPosition) { + position = to; + } + notifyChange(QUEUE_CHANGED); + } + + public void clearQueue() { + playingQueue.clear(); + originalPlayingQueue.clear(); + + setPosition(-1); + notifyChange(QUEUE_CHANGED); + } + + public void playSongAt(final int position) { + // handle this on the handlers thread to avoid blocking the ui thread + playerHandler.removeMessages(PLAY_SONG); + playerHandler.obtainMessage(PLAY_SONG, position, 0).sendToTarget(); + } + + private void playSongAtImpl(int position) { + if (openTrackAndPrepareNextAt(position)) { + play(); + } else { + Toast.makeText(this, getResources().getString(R.string.unplayable_file), Toast.LENGTH_SHORT).show(); + } + } + + public void pause() { + pausedByTransientLossOfFocus = false; + if (playback.isPlaying()) { + playback.pause(); + notifyChange(PLAY_STATE_CHANGED); + } + } + + public void play() { + synchronized (this) { + if (requestFocus()) { + if (!playback.isPlaying()) { + if (!playback.isInitialized()) { + playSongAt(getPosition()); + } else { + playback.start(); + if (!becomingNoisyReceiverRegistered) { + registerReceiver(becomingNoisyReceiver, becomingNoisyReceiverIntentFilter); + becomingNoisyReceiverRegistered = true; + } + if (notHandledMetaChangedForCurrentTrack) { + handleChangeInternal(META_CHANGED); + notHandledMetaChangedForCurrentTrack = false; + } + notifyChange(PLAY_STATE_CHANGED); + + // fixes a bug where the volume would stay ducked because the AudioManager.AUDIOFOCUS_GAIN event is not sent + playerHandler.removeMessages(DUCK); + playerHandler.sendEmptyMessage(UNDUCK); + } + } + } else { + Toast.makeText(this, getResources().getString(R.string.audio_focus_denied), Toast.LENGTH_SHORT).show(); + } + } + } + + public void playSongs(ArrayList songs, int shuffleMode) { + if (songs != null && !songs.isEmpty()) { + if (shuffleMode == SHUFFLE_MODE_SHUFFLE) { + int startPosition = 0; + if (!songs.isEmpty()) { + startPosition = new Random().nextInt(songs.size()); + } + openQueue(songs, startPosition, false); + setShuffleMode(shuffleMode); + } else { + openQueue(songs, 0, false); + } + play(); + } else { + Toast.makeText(getApplicationContext(), R.string.playlist_is_empty, Toast.LENGTH_LONG).show(); + } + } + + public void playPreviousSong(boolean force) { + playSongAt(getPreviousPosition(force)); + } + + public void back(boolean force) { + if (getSongProgressMillis() > 2000) { + seek(0); + } else { + playPreviousSong(force); + } + } + + public int getPreviousPosition(boolean force) { + int newPosition = getPosition() - 1; + switch (repeatMode) { + case REPEAT_MODE_ALL: + if (newPosition < 0) { + newPosition = getPlayingQueue().size() - 1; + } + break; + case REPEAT_MODE_THIS: + if (force) { + if (newPosition < 0) { + newPosition = getPlayingQueue().size() - 1; + } + } else { + newPosition = getPosition(); + } + break; + default: + case REPEAT_MODE_NONE: + if (newPosition < 0) { + newPosition = 0; + } + break; + } + return newPosition; + } + + public int getSongProgressMillis() { + return playback.position(); + } + + public int getSongDurationMillis() { + return playback.duration(); + } + + public long getQueueDurationMillis(int position) { + long duration = 0; + for (int i = position + 1; i < playingQueue.size(); i++) + duration += playingQueue.get(i).getDuration(); + return duration; + } + + public int seek(int millis) { + synchronized (this) { + try { + int newPosition = playback.seek(millis); + throttledSeekHandler.notifySeek(); + return newPosition; + } catch (Exception e) { + return -1; + } + } + } + + public void cycleRepeatMode() { + switch (getRepeatMode()) { + case REPEAT_MODE_NONE: + setRepeatMode(REPEAT_MODE_ALL); + break; + case REPEAT_MODE_ALL: + setRepeatMode(REPEAT_MODE_THIS); + break; + default: + setRepeatMode(REPEAT_MODE_NONE); + break; + } + } + + public void toggleShuffle() { + if (getShuffleMode() == SHUFFLE_MODE_NONE) { + setShuffleMode(SHUFFLE_MODE_SHUFFLE); + } else { + setShuffleMode(SHUFFLE_MODE_NONE); + } + } + + public int getShuffleMode() { + return shuffleMode; + } + + public void setShuffleMode(final int shuffleMode) { + PreferenceManager.getDefaultSharedPreferences(this).edit() + .putInt(SAVED_SHUFFLE_MODE, shuffleMode) + .apply(); + switch (shuffleMode) { + case SHUFFLE_MODE_SHUFFLE: + this.shuffleMode = shuffleMode; + ShuffleHelper.INSTANCE.makeShuffleList(this.getPlayingQueue(), getPosition()); + position = 0; + break; + case SHUFFLE_MODE_NONE: + this.shuffleMode = shuffleMode; + int currentSongId = getCurrentSong().getId(); + playingQueue = new ArrayList<>(originalPlayingQueue); + int newPosition = 0; + for (Song song : getPlayingQueue()) { + if (song.getId() == currentSongId) { + newPosition = getPlayingQueue().indexOf(song); + } + } + position = newPosition; + break; + } + handleAndSendChangeInternal(SHUFFLE_MODE_CHANGED); + notifyChange(QUEUE_CHANGED); + } + + private void notifyChange(@NonNull final String what) { + handleAndSendChangeInternal(what); + sendPublicIntent(what); + } + + private void handleAndSendChangeInternal(@NonNull final String what) { + handleChangeInternal(what); + sendChangeInternal(what); + } + + // to let other apps know whats playing. i.E. last.fm (scrobbling) or musixmatch + private void sendPublicIntent(@NonNull final String what) { + final Intent intent = new Intent(what.replace(RETRO_MUSIC_PACKAGE_NAME, MUSIC_PACKAGE_NAME)); + + final Song song = getCurrentSong(); + + intent.putExtra("id", song.getId()); + intent.putExtra("artist", song.getArtistName()); + intent.putExtra("album", song.getAlbumName()); + intent.putExtra("track", song.getTitle()); + intent.putExtra("duration", song.getDuration()); + intent.putExtra("position", (long) getSongProgressMillis()); + intent.putExtra("playing", isPlaying()); + intent.putExtra("scrobbling_source", RETRO_MUSIC_PACKAGE_NAME); + + sendStickyBroadcast(intent); + + } + + private void sendChangeInternal(final String what) { + sendBroadcast(new Intent(what)); + + } + + private void handleChangeInternal(@NonNull final String what) { + switch (what) { + case PLAY_STATE_CHANGED: + updateNotification(); + updateMediaSessionPlaybackState(); + final boolean isPlaying = isPlaying(); + if (!isPlaying && getSongProgressMillis() > 0) { + savePositionInTrack(); + } + songPlayCountHelper.notifyPlayStateChanged(isPlaying); + break; + case META_CHANGED: + updateNotification(); + updateMediaSessionMetaData(); + savePosition(); + savePositionInTrack(); + final Song currentSong = getCurrentSong(); + HistoryStore.getInstance(this).addSongId(currentSong.getId()); + if (songPlayCountHelper.shouldBumpPlayCount()) { + SongPlayCountStore.getInstance(this).bumpPlayCount(songPlayCountHelper.getSong().getId()); + } + songPlayCountHelper.notifySongChanged(currentSong); + break; + case QUEUE_CHANGED: + updateMediaSessionMetaData(); // because playing queue size might have changed + saveState(); + if (playingQueue.size() > 0) { + prepareNext(); + } else { + playingNotification.stop(); + } + break; + } + } + + public int getAudioSessionId() { + return playback.getAudioSessionId(); + } + + public MediaSessionCompat getMediaSession() { + return mediaSession; + } + + public void releaseWakeLock() { + if (wakeLock.isHeld()) { + wakeLock.release(); + } + } + + public void acquireWakeLock(long milli) { + wakeLock.acquire(milli); + } + + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + switch (key) { + case PreferenceUtil.GAPLESS_PLAYBACK: + if (sharedPreferences.getBoolean(key, false)) { + prepareNext(); + } else { + playback.setNextDataSource(null); + } + break; + case PreferenceUtil.ALBUM_ART_ON_LOCKSCREEN: + case PreferenceUtil.BLURRED_ALBUM_ART: + updateMediaSessionMetaData(); + break; + case PreferenceUtil.COLORED_NOTIFICATION: + case PreferenceUtil.DOMINANT_COLOR: + updateNotification(); + break; + case PreferenceUtil.CLASSIC_NOTIFICATION: + initNotification(); + updateNotification(); + break; + case PreferenceUtil.TOGGLE_HEADSET: + registerHeadsetEvents(); + break; + } + } + + private void registerHeadsetEvents() { + if (!headsetReceiverRegistered && PreferenceUtil.getInstance().getHeadsetPlugged()) { + registerReceiver(headsetReceiver, headsetReceiverIntentFilter); + headsetReceiverRegistered = true; + } + } + + @Override + public void onTrackWentToNext() { + playerHandler.sendEmptyMessage(TRACK_WENT_TO_NEXT); + } + + @Override + public void onTrackEnded() { + acquireWakeLock(30000); + playerHandler.sendEmptyMessage(TRACK_ENDED); + } + + + private static final class QueueSaveHandler extends Handler { + @NonNull + private final WeakReference mService; + + QueueSaveHandler(final MusicService service, @NonNull final Looper looper) { + super(looper); + mService = new WeakReference<>(service); + } + + @Override + public void handleMessage(@NonNull Message msg) { + final MusicService service = mService.get(); + switch (msg.what) { + case SAVE_QUEUES: + service.saveQueuesImpl(); + break; + } + } + } + + private static final class PlaybackHandler extends Handler { + @NonNull + private final WeakReference mService; + private float currentDuckVolume = 1.0f; + + PlaybackHandler(final MusicService service, @NonNull final Looper looper) { + super(looper); + mService = new WeakReference<>(service); + } + + @Override + public void handleMessage(@NonNull final Message msg) { + final MusicService service = mService.get(); + if (service == null) { + return; + } + + switch (msg.what) { + case DUCK: + if (PreferenceUtil.getInstance().audioDucking()) { + currentDuckVolume -= .05f; + if (currentDuckVolume > .2f) { + sendEmptyMessageDelayed(DUCK, 10); + } else { + currentDuckVolume = .2f; + } + } else { + currentDuckVolume = 1f; + } + service.playback.setVolume(currentDuckVolume); + break; + + case UNDUCK: + if (PreferenceUtil.getInstance().audioDucking()) { + currentDuckVolume += .03f; + if (currentDuckVolume < 1f) { + sendEmptyMessageDelayed(UNDUCK, 10); + } else { + currentDuckVolume = 1f; + } + } else { + currentDuckVolume = 1f; + } + service.playback.setVolume(currentDuckVolume); + break; + + case TRACK_WENT_TO_NEXT: + if (service.getRepeatMode() == REPEAT_MODE_NONE && service.isLastTrack()) { + service.pause(); + service.seek(0); + } else { + service.position = service.nextPosition; + service.prepareNextImpl(); + service.notifyChange(META_CHANGED); + } + break; + + case TRACK_ENDED: + if (service.getRepeatMode() == REPEAT_MODE_NONE && service.isLastTrack()) { + service.notifyChange(PLAY_STATE_CHANGED); + service.seek(0); + } else { + service.playNextSong(false); + } + sendEmptyMessage(RELEASE_WAKELOCK); + break; + + case RELEASE_WAKELOCK: + service.releaseWakeLock(); + break; + + case PLAY_SONG: + service.playSongAtImpl(msg.arg1); + break; + + case SET_POSITION: + service.openTrackAndPrepareNextAt(msg.arg1); + service.notifyChange(PLAY_STATE_CHANGED); + break; + + case PREPARE_NEXT: + service.prepareNextImpl(); + break; + + case RESTORE_QUEUES: + service.restoreQueuesAndPositionIfNecessary(); + break; + + case FOCUS_CHANGE: + switch (msg.arg1) { + case AudioManager.AUDIOFOCUS_GAIN: + if (!service.isPlaying() && service.pausedByTransientLossOfFocus) { + service.play(); + service.pausedByTransientLossOfFocus = false; + } + removeMessages(DUCK); + sendEmptyMessage(UNDUCK); + break; + + case AudioManager.AUDIOFOCUS_LOSS: + // Lost focus for an unbounded amount of time: stop playback and release media playback + service.pause(); + break; + + case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: + // Lost focus for a short time, but we have to stop + // playback. We don't release the media playback because playback + // is likely to resume + boolean wasPlaying = service.isPlaying(); + service.pause(); + service.pausedByTransientLossOfFocus = wasPlaying; + break; + + case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: + // Lost focus for a short time, but it's ok to keep playing + // at an attenuated level + removeMessages(UNDUCK); + sendEmptyMessage(DUCK); + break; + } + break; + } + } + } + + private static class SongPlayCountHelper { + public static final String TAG = SongPlayCountHelper.class.getSimpleName(); + + private StopWatch stopWatch = new StopWatch(); + private Song song = Song.Companion.getEmptySong(); + + public Song getSong() { + return song; + } + + boolean shouldBumpPlayCount() { + return song.getDuration() * 0.5d < stopWatch.getElapsedTime(); + } + + void notifySongChanged(Song song) { + synchronized (this) { + stopWatch.reset(); + this.song = song; + } + } + + void notifyPlayStateChanged(boolean isPlaying) { + synchronized (this) { + if (isPlaying) { + stopWatch.start(); + } else { + stopWatch.pause(); + } + } + } + } + + public class MusicBinder extends Binder { + @NonNull + public MusicService getService() { + return MusicService.this; + } + } + + private class MediaStoreObserver extends ContentObserver implements Runnable { + // milliseconds to delay before calling refresh to aggregate events + private static final long REFRESH_DELAY = 500; + private Handler mHandler; + + MediaStoreObserver(Handler handler) { + super(handler); + mHandler = handler; + } + + @Override + public void onChange(boolean selfChange) { + // if a change is detected, remove any scheduled callback + // then post a new one. This is intended to prevent closely + // spaced events from generating multiple refresh calls + mHandler.removeCallbacks(this); + mHandler.postDelayed(this, REFRESH_DELAY); + } + + @Override + public void run() { + // actually call refresh when the delayed callback fires + // do not send a sticky broadcast here + handleAndSendChangeInternal(MEDIA_STORE_CHANGED); + } + } + + private class ThrottledSeekHandler implements Runnable { + // milliseconds to throttle before calling run() to aggregate events + private static final long THROTTLE = 500; + private Handler mHandler; + + ThrottledSeekHandler(Handler handler) { + mHandler = handler; + } + + void notifySeek() { + mHandler.removeCallbacks(this); + mHandler.postDelayed(this, THROTTLE); + } + + @Override + public void run() { + savePositionInTrack(); + sendPublicIntent(PLAY_STATE_CHANGED); // for musixmatch synced lyrics + } + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/service/MusicService.kt b/app/src/main/java/code/name/monkey/retromusic/service/MusicService.kt deleted file mode 100644 index 813385cf..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/service/MusicService.kt +++ /dev/null @@ -1,1247 +0,0 @@ -package code.name.monkey.retromusic.service - -import android.annotation.SuppressLint -import android.app.PendingIntent -import android.app.Service -import android.content.* -import android.database.ContentObserver -import android.graphics.Bitmap -import android.graphics.drawable.Drawable -import android.media.AudioManager -import android.media.audiofx.AudioEffect -import android.media.session.MediaSession -import android.os.* -import android.preference.PreferenceManager -import android.provider.MediaStore -import android.support.v4.media.MediaMetadataCompat -import android.support.v4.media.session.MediaSessionCompat -import android.support.v4.media.session.PlaybackStateCompat -import android.telephony.PhoneStateListener -import android.telephony.TelephonyManager -import android.util.Log -import android.widget.Toast -import code.name.monkey.retromusic.Constants.ACTION_PAUSE -import code.name.monkey.retromusic.Constants.ACTION_PLAY -import code.name.monkey.retromusic.Constants.ACTION_PLAY_PLAYLIST -import code.name.monkey.retromusic.Constants.ACTION_QUIT -import code.name.monkey.retromusic.Constants.ACTION_SKIP -import code.name.monkey.retromusic.Constants.ACTION_STOP -import code.name.monkey.retromusic.Constants.ACTION_TOGGLE_PAUSE -import code.name.monkey.retromusic.Constants.APP_WIDGET_UPDATE -import code.name.monkey.retromusic.Constants.EXTRA_APP_WIDGET_NAME -import code.name.monkey.retromusic.Constants.INTENT_EXTRA_PLAYLIST -import code.name.monkey.retromusic.Constants.INTENT_EXTRA_SHUFFLE_MODE -import code.name.monkey.retromusic.Constants.MEDIA_STORE_CHANGED -import code.name.monkey.retromusic.Constants.META_CHANGED -import code.name.monkey.retromusic.Constants.MUSIC_PACKAGE_NAME -import code.name.monkey.retromusic.Constants.PLAY_STATE_CHANGED -import code.name.monkey.retromusic.Constants.QUEUE_CHANGED -import code.name.monkey.retromusic.Constants.REPEAT_MODE_CHANGED -import code.name.monkey.retromusic.Constants.RETRO_MUSIC_PACKAGE_NAME -import code.name.monkey.retromusic.Constants.SHUFFLE_MODE_CHANGED -import code.name.monkey.retromusic.R -import code.name.monkey.retromusic.glide.BlurTransformation -import code.name.monkey.retromusic.glide.SongGlideRequest -import code.name.monkey.retromusic.helper.MusicPlayerRemote.setShuffleMode -import code.name.monkey.retromusic.helper.ShuffleHelper -import code.name.monkey.retromusic.helper.StopWatch -import code.name.monkey.retromusic.loaders.PlaylistSongsLoader -import code.name.monkey.retromusic.model.AbsCustomPlaylist -import code.name.monkey.retromusic.model.Playlist -import code.name.monkey.retromusic.model.Song -import code.name.monkey.retromusic.providers.HistoryStore -import code.name.monkey.retromusic.providers.MusicPlaybackQueueStore -import code.name.monkey.retromusic.providers.SongPlayCountStore -import code.name.monkey.retromusic.service.notification.PlayingNotification -import code.name.monkey.retromusic.service.notification.PlayingNotificationImpl24 -import code.name.monkey.retromusic.service.notification.PlayingNotificationOreo -import code.name.monkey.retromusic.service.playback.Playback -import code.name.monkey.retromusic.util.MusicUtil -import code.name.monkey.retromusic.util.PreferenceUtil -import code.name.monkey.retromusic.util.RetroUtil -import com.bumptech.glide.Glide -import com.bumptech.glide.request.animation.GlideAnimation -import com.bumptech.glide.request.target.SimpleTarget -import com.google.android.gms.cast.framework.media.MediaIntentReceiver.ACTION_REWIND -import java.lang.ref.WeakReference -import java.util.* - -/** - * @author Karim Abou Zeid (kabouzeid), Andrew Neal - */ -class MusicService : Service(), SharedPreferences.OnSharedPreferenceChangeListener, Playback.PlaybackCallbacks { - private val musicBind = MusicBinder() - private var playback: Playback? = null - var playingQueue = ArrayList() - private set - private var originalPlayingQueue = ArrayList() - var position = -1 - set(value) { - playerHandler!!.removeMessages(SET_POSITION) - playerHandler!!.obtainMessage(SET_POSITION, value, 0).sendToTarget() - } - private var nextPosition = -1 - var shuffleMode: Int = 0 - set(value) { - PreferenceManager.getDefaultSharedPreferences(this).edit() - .putInt(SAVED_SHUFFLE_MODE, value) - .apply() - when (value) { - SHUFFLE_MODE_SHUFFLE -> { - field = value - ShuffleHelper.makeShuffleList(this.playingQueue, position) - position = 0 - } - SHUFFLE_MODE_NONE -> { - field = value - val currentSongId = currentSong.id - playingQueue = ArrayList(originalPlayingQueue) - var newPosition = 0 - for (song in playingQueue) { - if (song.id == currentSongId) { - newPosition = playingQueue.indexOf(song) - } - } - position = newPosition - } - } - handleAndSendChangeInternal(SHUFFLE_MODE_CHANGED) - notifyChange(QUEUE_CHANGED) - } - var repeatMode: Int = 0 - set(value) { - when (value) { - REPEAT_MODE_NONE, REPEAT_MODE_ALL, REPEAT_MODE_THIS -> { - field = value - PreferenceManager.getDefaultSharedPreferences(this).edit() - .putInt(SAVED_REPEAT_MODE, repeatMode) - .apply() - prepareNext() - handleAndSendChangeInternal(REPEAT_MODE_CHANGED) - } - } - } - private var queuesRestored: Boolean = false - private var pausedByTransientLossOfFocus: Boolean = false - private val becomingNoisyReceiver = object : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - if (intent.action != null && intent.action == AudioManager.ACTION_AUDIO_BECOMING_NOISY) { - pause() - } - } - } - private var playingNotification: PlayingNotification? = null - private var audioManager: AudioManager? = null - var mediaSession: MediaSessionCompat? = null - private set - private var wakeLock: PowerManager.WakeLock? = null - private var playerHandler: PlaybackHandler? = null - private val audioFocusListener = AudioManager.OnAudioFocusChangeListener { focusChange -> playerHandler!!.obtainMessage(FOCUS_CHANGE, focusChange, 0).sendToTarget() } - private var queueSaveHandler: QueueSaveHandler? = null - private var musicPlayerHandlerThread: HandlerThread? = null - private var queueSaveHandlerThread: HandlerThread? = null - private val songPlayCountHelper = SongPlayCountHelper() - private var throttledSeekHandler: ThrottledSeekHandler? = null - private var becomingNoisyReceiverRegistered: Boolean = false - private val becomingNoisyReceiverIntentFilter = IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY) - private var mediaStoreObserver: ContentObserver? = null - private var notHandledMetaChangedForCurrentTrack: Boolean = false - private val phoneStateListener = object : PhoneStateListener() { - override fun onCallStateChanged(state: Int, incomingNumber: String) { - when (state) { - TelephonyManager.CALL_STATE_IDLE -> - //Not in call: Play music - play() - TelephonyManager.CALL_STATE_RINGING, TelephonyManager.CALL_STATE_OFFHOOK -> - //A call is dialing, active or on hold - pause() - } - super.onCallStateChanged(state, incomingNumber) - } - } - private var isServiceBound: Boolean = false - private var uiThreadHandler: Handler? = null - private val widgetIntentReceiver = object : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - val command = intent.getStringExtra(EXTRA_APP_WIDGET_NAME) - } - } - private val headsetReceiverIntentFilter = IntentFilter(Intent.ACTION_HEADSET_PLUG) - private var headsetReceiverRegistered = false - private val headsetReceiver = object : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - val action = intent.action - if (action != null) { - when (action) { - Intent.ACTION_HEADSET_PLUG -> { - val state = intent.getIntExtra("state", -1) - when (state) { - 0 -> { - Log.d(TAG, "Headset unplugged") - pause() - } - 1 -> { - Log.d(TAG, "Headset plugged") - play() - } - } - } - } - } - } - } - - val isPlaying: Boolean - get() = playback != null && playback!!.isPlaying - - val currentSong: Song - get() = getSongAt(position) - - private val isLastTrack: Boolean - get() = position == playingQueue.size - 1 - - val songProgressMillis: Int - get() = playback!!.position() - - val songDurationMillis: Int - get() = playback!!.duration() - - val audioSessionId: Int - get() = playback!!.audioSessionId - - - override fun onCreate() { - super.onCreate() - - val telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager - telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE) - - val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager - wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, javaClass.name) - wakeLock!!.setReferenceCounted(false) - - musicPlayerHandlerThread = HandlerThread("PlaybackHandler") - musicPlayerHandlerThread!!.start() - playerHandler = PlaybackHandler(this, musicPlayerHandlerThread!!.looper) - - playback = MultiPlayer(this) - playback!!.setCallbacks(this) - - setupMediaSession() - - // queue saving needs to run on a separate thread so that it doesn't block the playback handler events - queueSaveHandlerThread = HandlerThread("QueueSaveHandler", Process.THREAD_PRIORITY_BACKGROUND) - queueSaveHandlerThread!!.start() - queueSaveHandler = QueueSaveHandler(this, queueSaveHandlerThread!!.looper) - - uiThreadHandler = Handler() - - registerReceiver(widgetIntentReceiver, IntentFilter(APP_WIDGET_UPDATE)) - - initNotification() - - mediaStoreObserver = MediaStoreObserver(playerHandler!!) - throttledSeekHandler = ThrottledSeekHandler(playerHandler!!) - contentResolver.registerContentObserver( - MediaStore.Audio.Media.INTERNAL_CONTENT_URI, true, mediaStoreObserver!!) - contentResolver.registerContentObserver( - MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, true, mediaStoreObserver!!) - - PreferenceUtil.getInstance().registerOnSharedPreferenceChangedListener(this) - - restoreState() - - mediaSession!!.isActive = true - - sendBroadcast(Intent("code.name.monkey.retromusic.RETRO_MUSIC_SERVICE_CREATED")) - - registerHeadsetEvents() - - - } - - private fun getAudioManager(): AudioManager? { - if (audioManager == null) { - audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager - } - return audioManager - } - - @SuppressLint("WrongConstant") - private fun setupMediaSession() { - val mediaButtonReceiverComponentName = ComponentName(applicationContext, MediaButtonIntentReceiver::class.java) - - val mediaButtonIntent = Intent(Intent.ACTION_MEDIA_BUTTON) - mediaButtonIntent.component = mediaButtonReceiverComponentName - - - val mediaButtonReceiverPendingIntent = PendingIntent.getBroadcast(applicationContext, 0, mediaButtonIntent, 0) - - mediaSession = MediaSessionCompat(this, "RetroMusicPlayer", mediaButtonReceiverComponentName, mediaButtonReceiverPendingIntent) - mediaSession!!.setCallback(object : MediaSessionCompat.Callback() { - override fun onPlay() { - play() - } - - override fun onPause() { - pause() - } - - override fun onSkipToNext() { - playNextSong(true) - } - - override fun onSkipToPrevious() { - back(true) - } - - override fun onStop() { - quit() - } - - override fun onSeekTo(pos: Long) { - seek(pos.toInt()) - } - - override fun onMediaButtonEvent(mediaButtonEvent: Intent): Boolean { - return MediaButtonIntentReceiver.handleIntent(this@MusicService, mediaButtonEvent) - } - }) - - mediaSession!!.setFlags(MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS or MediaSession.FLAG_HANDLES_MEDIA_BUTTONS) - - mediaSession!!.setMediaButtonReceiver(mediaButtonReceiverPendingIntent) - } - - override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { - if (intent != null) { - if (intent.action != null) { - restoreQueuesAndPositionIfNecessary() - val action = intent.action - when (action) { - ACTION_TOGGLE_PAUSE -> if (isPlaying) { - pause() - } else { - play() - } - ACTION_PAUSE -> pause() - ACTION_PLAY -> play() - ACTION_PLAY_PLAYLIST -> { - val playlist = intent.getParcelableExtra(INTENT_EXTRA_PLAYLIST) - val shuffleMode = intent.getIntExtra(INTENT_EXTRA_SHUFFLE_MODE, shuffleMode) - if (playlist != null) { - val playlistSongs: ArrayList - if (playlist is AbsCustomPlaylist) { - playlistSongs = playlist.getSongs(applicationContext).blockingFirst() - } else { - - playlistSongs = PlaylistSongsLoader.getPlaylistSongList(applicationContext, playlist.id).blockingFirst() - } - if (!playlistSongs.isEmpty()) { - if (shuffleMode == SHUFFLE_MODE_SHUFFLE) { - val startPosition: Int = Random().nextInt(playlistSongs.size) - openQueue(playlistSongs, startPosition, true) - setShuffleMode(shuffleMode) - } else { - openQueue(playlistSongs, 0, true) - } - } else { - Toast.makeText(applicationContext, R.string.playlist_is_empty, Toast.LENGTH_LONG).show() - } - } else { - Toast.makeText(applicationContext, R.string.playlist_is_empty, Toast.LENGTH_LONG).show() - } - } - ACTION_REWIND -> back(true) - ACTION_SKIP -> playNextSong(true) - ACTION_STOP, ACTION_QUIT -> return quit() - } - } - } - - return Service.START_STICKY - } - - - override fun onDestroy() { - unregisterReceiver(widgetIntentReceiver) - if (becomingNoisyReceiverRegistered) { - unregisterReceiver(becomingNoisyReceiver) - becomingNoisyReceiverRegistered = false - } - if (headsetReceiverRegistered) { - unregisterReceiver(headsetReceiver) - headsetReceiverRegistered = false - } - mediaSession!!.isActive = false - quit() - releaseResources() - contentResolver.unregisterContentObserver(mediaStoreObserver!!) - PreferenceUtil.getInstance().unregisterOnSharedPreferenceChangedListener(this) - wakeLock!!.release() - - sendBroadcast(Intent("code.name.monkey.retromusic.RETRO_MUSIC_MUSIC_SERVICE_DESTROYED")) - } - - override fun onBind(intent: Intent): IBinder? { - isServiceBound = true - return musicBind - } - - override fun onRebind(intent: Intent) { - isServiceBound = true - } - - override fun onUnbind(intent: Intent): Boolean { - isServiceBound = false - if (!isPlaying) { - stopSelf() - } - return true - } - - private fun saveQueuesImpl() { - MusicPlaybackQueueStore.getInstance(this).saveQueues(playingQueue, originalPlayingQueue) - } - - private fun savePosition() { - PreferenceManager.getDefaultSharedPreferences(this).edit().putInt(SAVED_POSITION, position).apply() - } - - private fun savePositionInTrack() { - PreferenceManager.getDefaultSharedPreferences(this).edit().putInt(SAVED_POSITION_IN_TRACK, songProgressMillis).apply() - } - - private fun saveState() { - saveQueues() - savePosition() - savePositionInTrack() - } - - private fun saveQueues() { - queueSaveHandler!!.removeMessages(SAVE_QUEUES) - queueSaveHandler!!.sendEmptyMessage(SAVE_QUEUES) - } - - private fun restoreState() { - shuffleMode = PreferenceManager.getDefaultSharedPreferences(this).getInt(SAVED_SHUFFLE_MODE, 0) - repeatMode = PreferenceManager.getDefaultSharedPreferences(this).getInt(SAVED_REPEAT_MODE, 0) - handleAndSendChangeInternal(SHUFFLE_MODE_CHANGED) - handleAndSendChangeInternal(REPEAT_MODE_CHANGED) - - playerHandler!!.removeMessages(RESTORE_QUEUES) - playerHandler!!.sendEmptyMessage(RESTORE_QUEUES) - } - - @Synchronized - private fun restoreQueuesAndPositionIfNecessary() { - if (!queuesRestored && playingQueue.isEmpty()) { - val restoredQueue = MusicPlaybackQueueStore.getInstance(this).savedPlayingQueue - .blockingFirst() - - val restoredOriginalQueue = MusicPlaybackQueueStore.getInstance(this).savedOriginalPlayingQueue - .blockingFirst() - - val restoredPosition = PreferenceManager.getDefaultSharedPreferences(this).getInt(SAVED_POSITION, -1) - val restoredPositionInTrack = PreferenceManager.getDefaultSharedPreferences(this).getInt(SAVED_POSITION_IN_TRACK, -1) - - if (restoredQueue.size > 0 && restoredQueue.size == restoredOriginalQueue.size && restoredPosition != -1) { - this.originalPlayingQueue = restoredOriginalQueue - this.playingQueue = restoredQueue - - position = restoredPosition - openCurrent() - prepareNext() - - if (restoredPositionInTrack > 0) seek(restoredPositionInTrack) - - notHandledMetaChangedForCurrentTrack = true - sendChangeInternal(META_CHANGED) - sendChangeInternal(QUEUE_CHANGED) - } - } - queuesRestored = true - } - - @SuppressLint("WrongConstant") - private fun quit(): Int { - pause() - playingNotification!!.stop() - - if (isServiceBound) { - return Service.START_STICKY - } else { - closeAudioEffectSession() - getAudioManager()!!.abandonAudioFocus(audioFocusListener) - stopSelf() - return Service.START_NOT_STICKY - } - } - - private fun releaseResources() { - playerHandler!!.removeCallbacksAndMessages(null) - musicPlayerHandlerThread!!.quitSafely() - queueSaveHandler!!.removeCallbacksAndMessages(null) - queueSaveHandlerThread!!.quitSafely() - playback!!.release() - playback = null - mediaSession!!.release() - } - - fun playNextSong(force: Boolean) { - playSongAt(getNextPosition(force)) - } - - private fun openTrackAndPrepareNextAt(position: Int): Boolean { - synchronized(this) { - this.position = position - val prepared = openCurrent() - if (prepared) prepareNextImpl() - notifyChange(META_CHANGED) - notHandledMetaChangedForCurrentTrack = false - return prepared - } - } - - private fun openCurrent(): Boolean { - synchronized(this) { - try { - return playback!!.setDataSource(getTrackUri(currentSong)) - } catch (e: Exception) { - return false - } - - } - } - - private fun prepareNext() { - playerHandler!!.removeMessages(PREPARE_NEXT) - playerHandler!!.obtainMessage(PREPARE_NEXT).sendToTarget() - } - - private fun prepareNextImpl(): Boolean { - synchronized(this) { - return try { - val nextPosition = getNextPosition(false) - playback!!.setNextDataSource(getTrackUri(getSongAt(nextPosition))) - this.nextPosition = nextPosition - true - } catch (e: Exception) { - false - } - - } - } - - private fun closeAudioEffectSession() { - val audioEffectsIntent = Intent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION) - audioEffectsIntent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, playback!!.audioSessionId) - audioEffectsIntent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, packageName) - sendBroadcast(audioEffectsIntent) - } - - private fun requestFocus(): Boolean { - return getAudioManager()!!.requestAudioFocus(audioFocusListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN) == AudioManager.AUDIOFOCUS_REQUEST_GRANTED - } - - fun initNotification() { - playingNotification = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && !PreferenceUtil.getInstance().classicNotification()) { - PlayingNotificationImpl24() - } else { - PlayingNotificationOreo() - } - playingNotification!!.init(this) - } - - fun updateNotification() { - if (playingNotification != null && currentSong.id != -1) { - playingNotification!!.update() - } - } - - private fun updateMediaSessionPlaybackState() { - mediaSession!!.setPlaybackState( - PlaybackStateCompat.Builder() - .setActions(MEDIA_SESSION_ACTIONS) - .setState(if (isPlaying) PlaybackStateCompat.STATE_PLAYING else PlaybackStateCompat.STATE_PAUSED, - position.toLong(), 1f) - .build()) - } - - private fun updateMediaSessionMetaData() { - val song = currentSong - - if (song.id == -1) { - mediaSession!!.setMetadata(null) - return - } - - val metaData = MediaMetadataCompat.Builder() - .putString(MediaMetadataCompat.METADATA_KEY_ARTIST, song.artistName) - .putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ARTIST, song.artistName) - .putString(MediaMetadataCompat.METADATA_KEY_ALBUM, song.albumName) - .putString(MediaMetadataCompat.METADATA_KEY_TITLE, song.title) - .putLong(MediaMetadataCompat.METADATA_KEY_DURATION, song.duration) - .putLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER, (position + 1).toLong()) - .putLong(MediaMetadataCompat.METADATA_KEY_YEAR, song.year.toLong()) - .putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, null) - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - metaData.putLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS, playingQueue.size.toLong()) - } - - if (PreferenceUtil.getInstance().albumArtOnLockscreen()) { - val screenSize = RetroUtil.getScreenSize(this@MusicService) - val request = SongGlideRequest.Builder.from(Glide.with(this@MusicService), song) - .checkIgnoreMediaStore(this@MusicService) - .asBitmap().build() - if (PreferenceUtil.getInstance().blurredAlbumArt()) { - request.transform(BlurTransformation.Builder(this@MusicService).build()) - } - runOnUiThread(Runnable { - request.into(object : SimpleTarget(screenSize.x, screenSize.y) { - override fun onLoadFailed(e: Exception?, errorDrawable: Drawable?) { - super.onLoadFailed(e, errorDrawable) - mediaSession!!.setMetadata(metaData.build()) - } - - override fun onResourceReady(resource: Bitmap, glideAnimation: GlideAnimation) { - metaData.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, copy(resource)) - mediaSession!!.setMetadata(metaData.build()) - } - }) - }) - } else { - mediaSession!!.setMetadata(metaData.build()) - } - } - - fun runOnUiThread(runnable: Runnable) { - uiThreadHandler!!.post(runnable) - } - - private fun getSongAt(position: Int): Song { - return if (position >= 0 && position < playingQueue.size) { - playingQueue[position] - } else { - Song.EMPTY_SONG - } - } - - private fun getNextPosition(force: Boolean): Int { - var position = position + 1 - when (repeatMode) { - REPEAT_MODE_ALL -> if (isLastTrack) { - position = 0 - } - REPEAT_MODE_THIS -> if (force) { - if (isLastTrack) { - position = 0 - } - } else { - position -= 1 - } - REPEAT_MODE_NONE -> if (isLastTrack) { - position -= 1 - } - else -> if (isLastTrack) { - position -= 1 - } - } - return position - } - - - fun openQueue(playingQueue: ArrayList?, startPosition: Int, startPlaying: Boolean) { - if (playingQueue != null && !playingQueue.isEmpty() && startPosition >= 0 && startPosition < playingQueue.size) { - // it is important to copy the playing queue here first as we might add/remove songs later - originalPlayingQueue = ArrayList(playingQueue) - this.playingQueue = ArrayList(originalPlayingQueue) - - var position = startPosition - if (shuffleMode == SHUFFLE_MODE_SHUFFLE) { - ShuffleHelper.makeShuffleList(this.playingQueue, startPosition) - position = 0 - } - if (startPlaying) { - playSongAt(position) - } else { - this.position = position - } - notifyChange(QUEUE_CHANGED) - } - } - - fun addSong(position: Int, song: Song) { - playingQueue.add(position, song) - originalPlayingQueue.add(position, song) - notifyChange(QUEUE_CHANGED) - } - - fun addSong(song: Song) { - playingQueue.add(song) - originalPlayingQueue.add(song) - notifyChange(QUEUE_CHANGED) - } - - fun addSongs(position: Int, songs: List) { - playingQueue.addAll(position, songs) - originalPlayingQueue.addAll(position, songs) - notifyChange(QUEUE_CHANGED) - } - - fun addSongs(songs: List) { - playingQueue.addAll(songs) - originalPlayingQueue.addAll(songs) - notifyChange(QUEUE_CHANGED) - } - - fun removeSong(position: Int) { - if (shuffleMode == SHUFFLE_MODE_NONE) { - playingQueue.removeAt(position) - originalPlayingQueue.removeAt(position) - } else { - originalPlayingQueue.remove(playingQueue.removeAt(position)) - } - - rePosition(position) - - notifyChange(QUEUE_CHANGED) - } - - fun removeSong(song: Song) { - for (i in playingQueue.indices) { - if (playingQueue[i].id == song.id) { - playingQueue.removeAt(i) - rePosition(i) - } - } - for (i in originalPlayingQueue.indices) { - if (originalPlayingQueue[i].id == song.id) { - originalPlayingQueue.removeAt(i) - } - } - notifyChange(QUEUE_CHANGED) - } - - private fun rePosition(deletedPosition: Int) { - val currentPosition = position - if (deletedPosition < currentPosition) { - position = currentPosition - 1 - } else if (deletedPosition == currentPosition) { - if (playingQueue.size > deletedPosition) { - this.position = position - } else { - this.position = position - 1 - } - } - } - - fun moveSong(from: Int, to: Int) { - if (from == to) return - val currentPosition = position - val songToMove = playingQueue.removeAt(from) - playingQueue.add(to, songToMove) - if (shuffleMode == SHUFFLE_MODE_NONE) { - val tmpSong = originalPlayingQueue.removeAt(from) - originalPlayingQueue.add(to, tmpSong) - } - if (currentPosition in to..(from - 1)) { - position = currentPosition + 1 - } else if (currentPosition in (from + 1)..to) { - position = currentPosition - 1 - } else if (from == currentPosition) { - position = to - } - notifyChange(QUEUE_CHANGED) - } - - fun clearQueue() { - playingQueue.clear() - originalPlayingQueue.clear() - - position = -1 - notifyChange(QUEUE_CHANGED) - } - - fun playSongAt(position: Int) { - // handle this on the handlers thread to avoid blocking the ui thread - playerHandler!!.removeMessages(PLAY_SONG) - playerHandler!!.obtainMessage(PLAY_SONG, position, 0).sendToTarget() - } - - private fun playSongAtImpl(position: Int) { - if (openTrackAndPrepareNextAt(position)) { - play() - } else { - Toast.makeText(this, resources.getString(R.string.unplayable_file), Toast.LENGTH_SHORT).show() - } - } - - fun pause() { - pausedByTransientLossOfFocus = false - if (playback!!.isPlaying) { - playback!!.pause() - notifyChange(PLAY_STATE_CHANGED) - } - } - - fun play() { - synchronized(this) { - if (requestFocus()) { - if (!playback!!.isPlaying) { - if (!playback!!.isInitialized) { - playSongAt(position) - } else { - playback!!.start() - if (!becomingNoisyReceiverRegistered) { - registerReceiver(becomingNoisyReceiver, becomingNoisyReceiverIntentFilter) - becomingNoisyReceiverRegistered = true - } - if (notHandledMetaChangedForCurrentTrack) { - handleChangeInternal(META_CHANGED) - notHandledMetaChangedForCurrentTrack = false - } - notifyChange(PLAY_STATE_CHANGED) - - // fixes a bug where the volume would stay ducked because the AudioManager.AUDIOFOCUS_GAIN event is not sent - playerHandler!!.removeMessages(DUCK) - playerHandler!!.sendEmptyMessage(UN_DUCK) - } - } - } else { - Toast.makeText(this, resources.getString(R.string.audio_focus_denied), Toast.LENGTH_SHORT).show() - } - } - } - - fun playSongs(songs: ArrayList?, shuffleMode: Int) { - if (songs != null && !songs.isEmpty()) { - if (shuffleMode == SHUFFLE_MODE_SHUFFLE) { - var startPosition = 0 - if (!songs.isEmpty()) { - startPosition = Random().nextInt(songs.size) - } - openQueue(songs, startPosition, false) - setShuffleMode(shuffleMode) - } else { - openQueue(songs, 0, false) - } - play() - } else { - Toast.makeText(applicationContext, R.string.playlist_is_empty, Toast.LENGTH_LONG).show() - } - } - - fun playPreviousSong(force: Boolean) { - playSongAt(getPreviousPosition(force)) - } - - fun back(force: Boolean) { - if (songProgressMillis > 2000) { - seek(0) - } else { - playPreviousSong(force) - } - } - - private fun getPreviousPosition(force: Boolean): Int { - var newPosition = position - 1 - when (repeatMode) { - REPEAT_MODE_ALL -> if (newPosition < 0) { - newPosition = playingQueue.size - 1 - } - REPEAT_MODE_THIS -> if (force) { - if (newPosition < 0) { - newPosition = playingQueue.size - 1 - } - } else { - newPosition = position - } - REPEAT_MODE_NONE -> if (newPosition < 0) { - newPosition = 0 - } - else -> if (newPosition < 0) { - newPosition = 0 - } - } - return newPosition - } - - fun getQueueDurationMillis(position: Int): Long { - var duration: Long = 0 - for (i in position + 1 until playingQueue.size) - duration += playingQueue[i].duration - return duration - } - - fun seek(millis: Int): Int { - synchronized(this) { - try { - val newPosition = playback!!.seek(millis) - throttledSeekHandler!!.notifySeek() - return newPosition - } catch (e: Exception) { - return -1 - } - - } - } - - fun cycleRepeatMode() { - repeatMode = when (repeatMode) { - REPEAT_MODE_NONE -> REPEAT_MODE_ALL - REPEAT_MODE_ALL -> REPEAT_MODE_THIS - else -> REPEAT_MODE_NONE - } - } - - fun toggleShuffle() { - if (shuffleMode == SHUFFLE_MODE_NONE) { - setShuffleMode(SHUFFLE_MODE_SHUFFLE) - } else { - setShuffleMode(SHUFFLE_MODE_NONE) - } - } - - - private fun notifyChange(what: String) { - handleAndSendChangeInternal(what) - sendPublicIntent(what) - } - - private fun handleAndSendChangeInternal(what: String) { - handleChangeInternal(what) - sendChangeInternal(what) - } - - // to let other apps know whats playing. i.E. last.fm (scrobbling) or musixmatch - @SuppressLint("WrongConstant") - private fun sendPublicIntent(what: String) { - val intent = Intent(what.replace(RETRO_MUSIC_PACKAGE_NAME, MUSIC_PACKAGE_NAME)) - - val song = currentSong - - intent.putExtra("id", song.id) - intent.putExtra("artist", song.artistName) - intent.putExtra("album", song.albumName) - intent.putExtra("track", song.title) - intent.putExtra("duration", song.duration) - intent.putExtra("position", songProgressMillis.toLong()) - intent.putExtra("playing", isPlaying) - intent.putExtra("scrobbling_source", RETRO_MUSIC_PACKAGE_NAME) - - sendStickyBroadcast(intent) - - } - - private fun sendChangeInternal(what: String) { - sendBroadcast(Intent(what)) - } - - private fun handleChangeInternal(what: String) { - when (what) { - PLAY_STATE_CHANGED -> { - updateNotification() - updateMediaSessionPlaybackState() - val isPlaying = isPlaying - if (!isPlaying && songProgressMillis > 0) { - savePositionInTrack() - } - songPlayCountHelper.notifyPlayStateChanged(isPlaying) - } - META_CHANGED -> { - updateNotification() - updateMediaSessionMetaData() - savePosition() - savePositionInTrack() - val currentSong = currentSong - HistoryStore.getInstance(this).addSongId(currentSong.id.toLong()) - if (songPlayCountHelper.shouldBumpPlayCount()) { - SongPlayCountStore.getInstance(this).bumpPlayCount(songPlayCountHelper.song.id.toLong()) - } - songPlayCountHelper.notifySongChanged(currentSong) - } - QUEUE_CHANGED -> { - updateMediaSessionMetaData() // because playing queue size might have changed - saveState() - if (playingQueue.size > 0) { - prepareNext() - } else { - playingNotification!!.stop() - } - } - } - } - - fun releaseWakeLock() { - if (wakeLock!!.isHeld) { - wakeLock!!.release() - } - } - - private fun acquireWakeLock(milli: Long) { - wakeLock!!.acquire(milli) - } - - override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) { - when (key) { - PreferenceUtil.GAPLESS_PLAYBACK -> if (sharedPreferences.getBoolean(key, false)) { - prepareNext() - } else { - playback!!.setNextDataSource(null) - } - PreferenceUtil.ALBUM_ART_ON_LOCKSCREEN, PreferenceUtil.BLURRED_ALBUM_ART -> updateMediaSessionMetaData() - PreferenceUtil.COLORED_NOTIFICATION, PreferenceUtil.DOMINANT_COLOR -> updateNotification() - PreferenceUtil.CLASSIC_NOTIFICATION -> { - initNotification() - updateNotification() - } - PreferenceUtil.TOGGLE_HEADSET -> registerHeadsetEvents() - } - } - - private fun registerHeadsetEvents() { - if (!headsetReceiverRegistered && PreferenceUtil.getInstance().headsetPlugged) { - registerReceiver(headsetReceiver, headsetReceiverIntentFilter) - headsetReceiverRegistered = true - } - } - - override fun onTrackWentToNext() { - playerHandler!!.sendEmptyMessage(TRACK_WENT_TO_NEXT) - } - - override fun onTrackEnded() { - acquireWakeLock(30000) - playerHandler!!.sendEmptyMessage(TRACK_ENDED) - } - - - inner class QueueSaveHandler internal constructor(service: MusicService, looper: Looper) : Handler(looper) { - val mService: WeakReference = WeakReference(service) - - override fun handleMessage(msg: Message) { - val service = mService.get() - when (msg.what) { - SAVE_QUEUES -> service!!.saveQueuesImpl() - } - } - } - - inner class PlaybackHandler internal constructor(service: MusicService, looper: Looper) : Handler(looper) { - val mService: WeakReference = WeakReference(service) - var currentDuckVolume = 1.0f - - override fun handleMessage(msg: Message) { - val service = mService.get() ?: return - - when (msg.what) { - DUCK -> { - if (PreferenceUtil.getInstance().audioDucking()) { - currentDuckVolume -= .05f - if (currentDuckVolume > .2f) { - sendEmptyMessageDelayed(DUCK, 10) - } else { - currentDuckVolume = .2f - } - } else { - currentDuckVolume = 1f - } - service.playback!!.setVolume(currentDuckVolume) - } - - UN_DUCK -> { - if (PreferenceUtil.getInstance().audioDucking()) { - currentDuckVolume += .03f - if (currentDuckVolume < 1f) { - sendEmptyMessageDelayed(UN_DUCK, 10) - } else { - currentDuckVolume = 1f - } - } else { - currentDuckVolume = 1f - } - service.playback!!.setVolume(currentDuckVolume) - } - - TRACK_WENT_TO_NEXT -> if (service.repeatMode == REPEAT_MODE_NONE && service.isLastTrack) { - service.pause() - service.seek(0) - } else { - service.position = service.nextPosition - service.prepareNextImpl() - service.notifyChange(META_CHANGED) - } - - TRACK_ENDED -> { - if (service.repeatMode == REPEAT_MODE_NONE && service.isLastTrack) { - service.notifyChange(PLAY_STATE_CHANGED) - service.seek(0) - } else { - service.playNextSong(false) - } - sendEmptyMessage(RELEASE_WAKELOCK) - } - - RELEASE_WAKELOCK -> service.releaseWakeLock() - - PLAY_SONG -> service.playSongAtImpl(msg.arg1) - - SET_POSITION -> { - service.openTrackAndPrepareNextAt(msg.arg1) - service.notifyChange(PLAY_STATE_CHANGED) - } - - PREPARE_NEXT -> service.prepareNextImpl() - - RESTORE_QUEUES -> service.restoreQueuesAndPositionIfNecessary() - - FOCUS_CHANGE -> when (msg.arg1) { - AudioManager.AUDIOFOCUS_GAIN -> { - if (!service.isPlaying && service.pausedByTransientLossOfFocus) { - service.play() - service.pausedByTransientLossOfFocus = false - } - removeMessages(DUCK) - sendEmptyMessage(UN_DUCK) - } - - AudioManager.AUDIOFOCUS_LOSS -> - // Lost focus for an unbounded amount of time: stop playback and release media playback - service.pause() - - AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> { - // Lost focus for a short time, but we have to stop - // playback. We don't release the media playback because playback - // is likely to resume - val wasPlaying = service.isPlaying - service.pause() - service.pausedByTransientLossOfFocus = wasPlaying - } - - AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> { - // Lost focus for a short time, but it's ok to keep playing - // at an attenuated level - removeMessages(UN_DUCK) - sendEmptyMessage(DUCK) - } - } - } - } - } - - inner class SongPlayCountHelper { - - private val stopWatch = StopWatch() - var song = Song.EMPTY_SONG - private set - - internal fun shouldBumpPlayCount(): Boolean { - return song.duration * 0.5 < stopWatch.elapsedTime - } - - internal fun notifySongChanged(song: Song) { - synchronized(this) { - stopWatch.reset() - this.song = song - } - } - - internal fun notifyPlayStateChanged(isPlaying: Boolean) { - synchronized(this) { - if (isPlaying) { - stopWatch.start() - } else { - stopWatch.pause() - } - } - } - } - - inner class MusicBinder : Binder() { - val service: MusicService - get() = this@MusicService - } - - inner class MediaStoreObserver internal constructor(private val mHandler: Handler) : ContentObserver(mHandler), Runnable { - - override fun onChange(selfChange: Boolean) { - // if a change is detected, remove any scheduled callback - // then post a new one. This is intended to prevent closely - // spaced events from generating multiple refresh calls - mHandler.removeCallbacks(this) - mHandler.postDelayed(this, 500) - } - - override fun run() { - // actually call refresh when the delayed callback fires - // do not send a sticky broadcast here - handleAndSendChangeInternal(MEDIA_STORE_CHANGED) - } - } - - inner class ThrottledSeekHandler internal constructor(private val mHandler: Handler) : Runnable { - - internal fun notifySeek() { - mHandler.removeCallbacks(this) - mHandler.postDelayed(this, 500) - } - - override fun run() { - savePositionInTrack() - sendPublicIntent(PLAY_STATE_CHANGED) // for musixmatch synced lyrics - } - } - - companion object { - val TAG: String = MusicService::class.java.simpleName - - const val SAVED_POSITION = "POSITION" - const val SAVED_POSITION_IN_TRACK = "POSITION_IN_TRACK" - const val SAVED_SHUFFLE_MODE = "SHUFFLE_MODE" - const val SAVED_REPEAT_MODE = "REPEAT_MODE" - - const val RELEASE_WAKELOCK = 0 - const val TRACK_ENDED = 1 - const val TRACK_WENT_TO_NEXT = 2 - const val PLAY_SONG = 3 - const val PREPARE_NEXT = 4 - const val SET_POSITION = 5 - const val RESTORE_QUEUES = 9 - const val SHUFFLE_MODE_NONE = 0 - const val SHUFFLE_MODE_SHUFFLE = 1 - const val REPEAT_MODE_NONE = 0 - const val REPEAT_MODE_ALL = 1 - const val REPEAT_MODE_THIS = 2 - const val SAVE_QUEUES = 0 - const val FOCUS_CHANGE = 6 - const val DUCK = 7 - const val UN_DUCK = 8 - const val MEDIA_SESSION_ACTIONS = (PlaybackStateCompat.ACTION_PLAY - or PlaybackStateCompat.ACTION_PAUSE - or PlaybackStateCompat.ACTION_PLAY_PAUSE - or PlaybackStateCompat.ACTION_SKIP_TO_NEXT - or PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS - or PlaybackStateCompat.ACTION_STOP - or PlaybackStateCompat.ACTION_SEEK_TO) - - private fun getTrackUri(song: Song): String { - return MusicUtil.getSongFileUri(song.id).toString() - } - - private fun copy(bitmap: Bitmap): Bitmap? { - var config: Bitmap.Config? = bitmap.config - if (config == null) { - config = Bitmap.Config.RGB_565 - } - try { - return bitmap.copy(config, false) - } catch (e: OutOfMemoryError) { - e.printStackTrace() - return null - } - - } - } -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/AlbumDetailsActivity.kt b/app/src/main/java/code/name/monkey/retromusic/ui/activities/AlbumDetailsActivity.kt index af628a2c..1876a809 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/AlbumDetailsActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/AlbumDetailsActivity.kt @@ -13,7 +13,6 @@ import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager import butterknife.ButterKnife -import butterknife.OnClick import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.util.ColorUtil import code.name.monkey.appthemehelper.util.TintHelper @@ -49,9 +48,9 @@ import java.util.* class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), AlbumDetailsContract.AlbumDetailsView { - private var albumDetailsPresenter: AlbumDetailsPresenter? = null + private lateinit var albumDetailsPresenter: AlbumDetailsPresenter + private lateinit var simpleSongAdapter: SimpleSongAdapter - private var adapter: SimpleSongAdapter? = null var album: Album? = null private set @@ -82,13 +81,12 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), AlbumDetailsContrac val albumId = intent.getIntExtra(EXTRA_ALBUM_ID, -1) albumDetailsPresenter = AlbumDetailsPresenter(this, albumId) - albumDetailsPresenter!!.subscribe() + albumDetailsPresenter.subscribe() setupRecyclerView() setupToolbarMarginHeight() - - contentContainer.setOnScrollChangeListener { v: NestedScrollView?, scrollX: Int, scrollY: Int, oldScrollX: Int, oldScrollY: Int -> + contentContainer.setOnScrollChangeListener { _: NestedScrollView?, _: Int, scrollY: Int, _: Int, oldScrollY: Int -> run { if (scrollY > oldScrollY) { actionShuffleAll!!.setShowTitle(false) @@ -98,31 +96,40 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), AlbumDetailsContrac } } } + + actionShuffleAll.setOnClickListener { MusicPlayerRemote.openAndShuffleQueue(album!!.songs!!, true) } + artistImage.setOnClickListener { + val artistPairs = arrayOf>(Pair.create(image, resources.getString(R.string.transition_artist_image))) + NavigationUtil.goToArtist(this, album!!.artistId, *artistPairs) + } } private fun setupRecyclerView() { - adapter = SimpleSongAdapter(this, ArrayList(), R.layout.item_song) + simpleSongAdapter = SimpleSongAdapter(this, ArrayList(), R.layout.item_song) recyclerView.apply { layoutManager = LinearLayoutManager(this@AlbumDetailsActivity) itemAnimator = DefaultItemAnimator() isNestedScrollingEnabled = false - adapter = adapter + adapter = simpleSongAdapter } } private fun setupToolbarMarginHeight() { - val primaryColor = ThemeStore.primaryColor(this) - TintHelper.setTintAuto(contentContainer!!, primaryColor, true) - if (collapsingToolbarLayout != null) { - collapsingToolbarLayout!!.setContentScrimColor(primaryColor) - collapsingToolbarLayout!!.setStatusBarScrimColor(ColorUtil.darkenColor(primaryColor)) - } - - toolbar.setNavigationIcon(R.drawable.ic_keyboard_backspace_black_24dp) setSupportActionBar(toolbar) - supportActionBar!!.title = null + val primaryColor = ThemeStore.primaryColor(this) + TintHelper.setTintAuto(contentContainer!!, primaryColor, true) + + if (collapsingToolbarLayout != null) { + collapsingToolbarLayout!!.apply { + setContentScrimColor(primaryColor) + setStatusBarScrimColor(ColorUtil.darkenColor(primaryColor)) + } + } + + + toolbar.setNavigationIcon(R.drawable.ic_keyboard_backspace_black_24dp) if (toolbar != null && !PreferenceUtil.getInstance().fullScreenMode) { val params = toolbar!!.layoutParams as ViewGroup.MarginLayoutParams @@ -131,45 +138,29 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), AlbumDetailsContrac } if (appBarLayout != null) { - appBarLayout!!.addOnOffsetChangedListener(object : AppBarStateChangeListener() { - override fun onStateChanged(appBarLayout: AppBarLayout, state: AppBarStateChangeListener.State) { - val color: Int - when (state) { - AppBarStateChangeListener.State.COLLAPSED -> { - setLightStatusbar(ColorUtil.isColorLight(ThemeStore.primaryColor(this@AlbumDetailsActivity))) - color = ThemeStore.primaryColor(this@AlbumDetailsActivity) - } - AppBarStateChangeListener.State.EXPANDED, AppBarStateChangeListener.State.IDLE -> { - setLightStatusbar(false) - color = Color.TRANSPARENT - } - else -> { - setLightStatusbar(false) - color = Color.TRANSPARENT + appBarLayout!!.apply { + addOnOffsetChangedListener(object : AppBarStateChangeListener() { + override fun onStateChanged(appBarLayout: AppBarLayout, state: AppBarStateChangeListener.State) { + val color: Int = when (state) { + AppBarStateChangeListener.State.COLLAPSED -> { + setLightStatusbar(ColorUtil.isColorLight(ThemeStore.primaryColor(this@AlbumDetailsActivity))) + ThemeStore.primaryColor(this@AlbumDetailsActivity) + } + AppBarStateChangeListener.State.EXPANDED, AppBarStateChangeListener.State.IDLE -> { + setLightStatusbar(false) + Color.TRANSPARENT + } } + ToolbarContentTintHelper.setToolbarContentColorBasedOnToolbarColor(this@AlbumDetailsActivity, toolbar, color) } - ToolbarContentTintHelper.setToolbarContentColorBasedOnToolbarColor(this@AlbumDetailsActivity, toolbar, color) - } - }) - } - } - - @OnClick(R.id.action_shuffle_all, R.id.artist_image) - fun onViewClicked(view: View) { - when (view.id) { - R.id.artist_image -> { - val artistPairs = arrayOf>(Pair.create(image, resources.getString(R.string.transition_artist_image))) - NavigationUtil.goToArtist(this, album!!.artistId, *artistPairs) - } - R.id.action_shuffle_all -> if (album!!.songs != null) { - MusicPlayerRemote.openAndShuffleQueue(album!!.songs!!, true) + }) } } } override fun onPause() { super.onPause() - albumDetailsPresenter!!.unsubscribe() + albumDetailsPresenter.unsubscribe() } override fun loading() { @@ -196,7 +187,8 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), AlbumDetailsContrac loadAlbumCover() loadMoreFrom(album) - adapter!!.swapDataSet(album.songs) + + simpleSongAdapter.swapDataSet(album.songs) } private fun loadMoreFrom(album: Album) { @@ -213,20 +205,19 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), AlbumDetailsContrac }) } - val albums = ArtistLoader.getArtist(this, album.artistId) - .blockingFirst().albums + val albums = ArtistLoader.getArtist(this, album.artistId).blockingFirst().albums albums!!.remove(album) if (!albums.isEmpty()) { moreTitle.visibility = View.VISIBLE - moreRecyclerView!!.visibility = View.VISIBLE + moreRecyclerView.visibility = View.VISIBLE } else { return } moreTitle.text = String.format("More from %s", album.artistName) val albumAdapter = HorizontalAlbumAdapter(this, albums, false, null) - moreRecyclerView!!.layoutManager = GridLayoutManager(this, 1, GridLayoutManager.HORIZONTAL, false) - moreRecyclerView!!.adapter = albumAdapter + moreRecyclerView.layoutManager = GridLayoutManager(this, 1, GridLayoutManager.HORIZONTAL, false) + moreRecyclerView.adapter = albumAdapter } private fun loadAlbumCover() { @@ -262,7 +253,7 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), AlbumDetailsContrac private fun handleSortOrderMenuItem(item: MenuItem): Boolean { var sortOrder: String? = null - val songs = adapter!!.dataSet + val songs = simpleSongAdapter.dataSet when (item.itemId) { R.id.action_play_next -> { MusicPlayerRemote.playNext(songs) @@ -327,7 +318,7 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), AlbumDetailsContrac } private fun reload() { - albumDetailsPresenter!!.subscribe() + albumDetailsPresenter.subscribe() } companion object { diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/ArtistDetailActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/ArtistDetailActivity.java deleted file mode 100755 index 6bf6346e..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/ArtistDetailActivity.java +++ /dev/null @@ -1,458 +0,0 @@ -package code.name.monkey.retromusic.ui.activities; - -import android.content.Intent; -import android.content.res.ColorStateList; -import android.graphics.Color; -import android.os.Bundle; -import android.text.Html; -import android.text.Spanned; -import android.transition.Slide; -import android.view.Gravity; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewGroup.LayoutParams; -import android.view.animation.AnimationUtils; -import android.widget.ImageView; -import android.widget.TextView; -import android.widget.Toast; - -import com.bumptech.glide.Glide; -import com.google.android.material.appbar.AppBarLayout; -import com.google.android.material.appbar.CollapsingToolbarLayout; - -import java.util.ArrayList; -import java.util.Locale; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.widget.AppCompatTextView; -import androidx.appcompat.widget.Toolbar; -import androidx.core.app.ActivityCompat; -import androidx.core.widget.NestedScrollView; -import androidx.recyclerview.widget.DefaultItemAnimator; -import androidx.recyclerview.widget.GridLayoutManager; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.appthemehelper.util.ColorUtil; -import code.name.monkey.appthemehelper.util.TintHelper; -import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog; -import code.name.monkey.retromusic.glide.ArtistGlideRequest; -import code.name.monkey.retromusic.glide.RetroMusicColoredTarget; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.misc.AppBarStateChangeListener; -import code.name.monkey.retromusic.model.Artist; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.mvp.contract.ArtistDetailContract; -import code.name.monkey.retromusic.mvp.presenter.ArtistDetailsPresenter; -import code.name.monkey.retromusic.rest.LastFMRestClient; -import code.name.monkey.retromusic.rest.model.LastFmArtist; -import code.name.monkey.retromusic.ui.activities.base.AbsSlidingMusicPanelActivity; -import code.name.monkey.retromusic.ui.adapter.album.AlbumAdapter; -import code.name.monkey.retromusic.ui.adapter.album.HorizontalAlbumAdapter; -import code.name.monkey.retromusic.ui.adapter.song.SimpleSongAdapter; -import code.name.monkey.retromusic.util.CustomArtistImageUtil; -import code.name.monkey.retromusic.util.DensityUtil; -import code.name.monkey.retromusic.util.MusicUtil; -import code.name.monkey.retromusic.util.PreferenceUtil; -import code.name.monkey.retromusic.util.RetroUtil; -import code.name.monkey.retromusic.views.CollapsingFAB; -import retrofit2.Call; -import retrofit2.Callback; -import retrofit2.Response; - -public class ArtistDetailActivity extends AbsSlidingMusicPanelActivity implements - ArtistDetailContract.ArtistsDetailsView { - - public static final String EXTRA_ARTIST_ID = "extra_artist_id"; - private static final int REQUEST_CODE_SELECT_IMAGE = 9003; - @BindView(R.id.app_bar) - @Nullable - AppBarLayout appBarLayout; - - @BindView(R.id.collapsing_toolbar) - @Nullable - CollapsingToolbarLayout collapsingToolbarLayout; - - @BindView(R.id.image) - ImageView image; - - @BindView(R.id.biography) - TextView biographyTextView; - - @BindView(R.id.recycler_view) - RecyclerView recyclerView; - - @BindView(R.id.album_recycler_view) - RecyclerView albumRecyclerView; - - @BindView(R.id.album_title) - AppCompatTextView albumTitle; - - @BindView(R.id.song_title) - AppCompatTextView songTitle; - - @BindView(R.id.biography_title) - AppCompatTextView biographyTitle; - - @BindView(R.id.title) - TextView title; - - @BindView(R.id.text) - TextView text; - - @BindView(R.id.action_shuffle_all) - CollapsingFAB shuffleButton; - - @BindView(R.id.gradient_background) - @Nullable - View background; - - @BindView(R.id.image_container) - @Nullable - View imageContainer; - - @BindView(R.id.content) - NestedScrollView contentContainer; - - @BindView(R.id.toolbar) - Toolbar toolbar; - - @Nullable - private Spanned biography; - private Artist artist; - private LastFMRestClient lastFMRestClient; - private ArtistDetailsPresenter artistDetailsPresenter; - private SimpleSongAdapter songAdapter; - private AlbumAdapter albumAdapter; - private boolean forceDownload; - - void setupWindowTransistion() { - Slide slide = new Slide(Gravity.BOTTOM); - slide.setInterpolator( - AnimationUtils.loadInterpolator(this, android.R.interpolator.linear_out_slow_in)); - getWindow().setEnterTransition(slide); - - } - - - @Override - protected View createContentView() { - return wrapSlidingMusicPanel(R.layout.activity_artist_details); - } - - @Override - protected void onCreate(Bundle bundle) { - setDrawUnderStatusBar(); - setupWindowTransistion(); - super.onCreate(bundle); - ButterKnife.bind(this); - - toggleBottomNavigationView(true); - setNavigationbarColorAuto(); - setLightNavigationBar(true); - - ActivityCompat.postponeEnterTransition(this); - - lastFMRestClient = new LastFMRestClient(this); - - setUpViews(); - - artistDetailsPresenter = new ArtistDetailsPresenter(this, getIntent().getExtras()); - artistDetailsPresenter.subscribe(); - - contentContainer.setOnScrollChangeListener((NestedScrollView.OnScrollChangeListener) (v, scrollX, scrollY, oldScrollX, oldScrollY) -> { - if (scrollY > oldScrollY) { - shuffleButton.setShowTitle(false); - } - if (scrollY < oldScrollY) { - shuffleButton.setShowTitle(true); - } - }); - } - - private void setUpViews() { - setupRecyclerView(); - setupToolbarMarginHeight(); - setupContainerHeight(); - } - - private void setupContainerHeight() { - if (imageContainer != null) { - LayoutParams params = imageContainer.getLayoutParams(); - params.width = DensityUtil.getScreenHeight(this) / 2; - imageContainer.setLayoutParams(params); - } - } - - private void setupToolbarMarginHeight() { - int primaryColor = ThemeStore.primaryColor(this); - TintHelper.setTintAuto(contentContainer, primaryColor, true); - if (collapsingToolbarLayout != null) { - collapsingToolbarLayout.setContentScrimColor(primaryColor); - collapsingToolbarLayout.setStatusBarScrimColor(ColorUtil.darkenColor(primaryColor)); - } - - toolbar.setNavigationIcon(R.drawable.ic_keyboard_backspace_black_24dp); - setSupportActionBar(toolbar); - //noinspection ConstantConditions - getSupportActionBar().setTitle(null); - - - if (toolbar != null && !PreferenceUtil.getInstance().getFullScreenMode()) { - ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) toolbar.getLayoutParams(); - params.topMargin = RetroUtil.getStatusBarHeight( ); - toolbar.setLayoutParams(params); - } - - if (appBarLayout != null) { - appBarLayout.addOnOffsetChangedListener(new AppBarStateChangeListener() { - @Override - public void onStateChanged(AppBarLayout appBarLayout, State state) { - int color; - switch (state) { - case COLLAPSED: - setLightStatusbar(ColorUtil.isColorLight(ThemeStore.primaryColor(appBarLayout.getContext()))); - color = ThemeStore.primaryColor(appBarLayout.getContext()); - break; - default: - case EXPANDED: - case IDLE: - setLightStatusbar(false); - color = Color.TRANSPARENT; - break; - } - ToolbarContentTintHelper.setToolbarContentColorBasedOnToolbarColor(appBarLayout.getContext(), toolbar, color); - } - }); - } - } - - private void setupRecyclerView() { - albumAdapter = new HorizontalAlbumAdapter(this, new ArrayList<>(), false, null); - albumRecyclerView.setItemAnimator(new DefaultItemAnimator()); - albumRecyclerView.setLayoutManager(new GridLayoutManager(this, 1, GridLayoutManager.HORIZONTAL, false)); - albumRecyclerView.setAdapter(albumAdapter); - - songAdapter = new SimpleSongAdapter(this, new ArrayList<>(), R.layout.item_song); - recyclerView.setItemAnimator(new DefaultItemAnimator()); - recyclerView.setLayoutManager(new LinearLayoutManager(this)); - recyclerView.setAdapter(songAdapter); - } - - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - switch (requestCode) { - case REQUEST_CODE_SELECT_IMAGE: - if (resultCode == RESULT_OK) { - CustomArtistImageUtil.getInstance(this).setCustomArtistImage(artist, data.getData()); - } - break; - default: - if (resultCode == RESULT_OK) { - reload(); - } - break; - } - } - - @Override - protected void onPause() { - super.onPause(); - artistDetailsPresenter.unsubscribe(); - } - - @Override - public void loading() { - } - - @Override - public void showEmptyView() { - - } - - @Override - public void completed() { - ActivityCompat.startPostponedEnterTransition(this); - } - - @Override - public void showData(Artist artist) { - setArtist(artist); - } - - private Artist getArtist() { - if (artist == null) { - artist = new Artist(); - } - return artist; - } - - private void setArtist(Artist artist) { - if (artist.getSongCount() <= 0) { - finish(); - } - this.artist = artist; - loadArtistImage(); - - if (RetroUtil.isAllowedToDownloadMetadata(this)) { - loadBiography(); - } - title.setText(artist.getName()); - text.setText(String.format("%s • %s", MusicUtil.getArtistInfoString(this, artist), MusicUtil - .getReadableDurationString(MusicUtil.getTotalDuration(this, artist.getSongs())))); - - songAdapter.swapDataSet(artist.getSongs()); - albumAdapter.swapDataSet(artist.getAlbums()); - } - - private void loadBiography() { - loadBiography(Locale.getDefault().getLanguage()); - } - - private void loadBiography(@Nullable final String lang) { - biography = null; - - lastFMRestClient.getApiService() - .getArtistInfo(getArtist().getName(), lang, null) - .enqueue(new Callback() { - @Override - public void onResponse(@NonNull Call call, - @NonNull Response response) { - final LastFmArtist lastFmArtist = response.body(); - if (lastFmArtist != null && lastFmArtist.getArtist() != null) { - final String bioContent = lastFmArtist.getArtist().getBio().getContent(); - if (bioContent != null && !bioContent.trim().isEmpty()) { - //TransitionManager.beginDelayedTransition(titleContainer); - biographyTextView.setVisibility(View.VISIBLE); - biographyTitle.setVisibility(View.VISIBLE); - biography = Html.fromHtml(bioContent); - biographyTextView.setText(biography); - } - } - - // If the "lang" parameter is set and no biography is given, retry with default language - if (biography == null && lang != null) { - loadBiography(null); - } - } - - @Override - public void onFailure(@NonNull Call call, @NonNull Throwable t) { - t.printStackTrace(); - biography = null; - } - }); - } - - @OnClick(R.id.biography) - void toggleArtistBiography() { - if (biographyTextView.getMaxLines() == 4) { - biographyTextView.setMaxLines(Integer.MAX_VALUE); - } else { - biographyTextView.setMaxLines(4); - } - } - - private void loadArtistImage() { - ArtistGlideRequest.Builder.from(Glide.with(this), artist) - .forceDownload(forceDownload) - .generatePalette(this).build() - .dontAnimate() - .into(new RetroMusicColoredTarget(image) { - @Override - public void onColorReady(int color) { - setColors(color); - } - }); - forceDownload = false; - } - - private void setColors(int color) { - - int textColor = PreferenceUtil.getInstance().getAdaptiveColor() ? color : ThemeStore.accentColor(this); - - albumTitle.setTextColor(textColor); - songTitle.setTextColor(textColor); - biographyTitle.setTextColor(textColor); - - shuffleButton.setColor(textColor); - - if (background != null) { - background.setBackgroundTintList(ColorStateList.valueOf(color)); - } - findViewById(R.id.root).setBackgroundColor(ThemeStore.primaryColor(this)); - } - - @OnClick({R.id.action_shuffle_all}) - public void onViewClicked(View view) { - switch (view.getId()) { - case R.id.action_shuffle_all: - MusicPlayerRemote.INSTANCE.openAndShuffleQueue(getArtist().getSongs(), true); - break; - } - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - return handleSortOrderMenuItem(item); - } - - private boolean handleSortOrderMenuItem(@NonNull MenuItem item) { - final ArrayList songs = getArtist().getSongs(); - switch (item.getItemId()) { - case android.R.id.home: - super.onBackPressed(); - return true; - case R.id.action_play_next: - MusicPlayerRemote.INSTANCE.playNext(songs); - return true; - case R.id.action_add_to_current_playing: - MusicPlayerRemote.INSTANCE.enqueue(songs); - return true; - case R.id.action_add_to_playlist: - AddToPlaylistDialog.create(songs).show(getSupportFragmentManager(), "ADD_PLAYLIST"); - return true; - case R.id.action_set_artist_image: - Intent intent = new Intent(Intent.ACTION_GET_CONTENT); - intent.setType("image/*"); - startActivityForResult( - Intent.createChooser(intent, getString(R.string.pick_from_local_storage)), - REQUEST_CODE_SELECT_IMAGE); - return true; - case R.id.action_reset_artist_image: - Toast.makeText(ArtistDetailActivity.this, getResources().getString(R.string.updating), - Toast.LENGTH_SHORT).show(); - CustomArtistImageUtil.getInstance(ArtistDetailActivity.this).resetCustomArtistImage(artist); - forceDownload = true; - return true; - } - return true; - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.menu_artist_detail, menu); - return super.onCreateOptionsMenu(menu); - } - - @Override - public void onMediaStoreChanged() { - super.onMediaStoreChanged(); - reload(); - } - - private void reload() { - artistDetailsPresenter.unsubscribe(); - artistDetailsPresenter.subscribe(); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/ArtistDetailActivity.kt b/app/src/main/java/code/name/monkey/retromusic/ui/activities/ArtistDetailActivity.kt new file mode 100755 index 00000000..c47a6074 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/ArtistDetailActivity.kt @@ -0,0 +1,363 @@ +package code.name.monkey.retromusic.ui.activities + +import android.app.Activity +import android.content.Intent +import android.graphics.Color +import android.os.Build +import android.os.Bundle +import android.text.Html +import android.text.Spanned +import android.transition.Slide +import android.view.* +import android.view.animation.AnimationUtils +import android.widget.Toast +import androidx.core.app.ActivityCompat +import androidx.core.widget.NestedScrollView +import androidx.recyclerview.widget.DefaultItemAnimator +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.LinearLayoutManager +import butterknife.ButterKnife +import code.name.monkey.appthemehelper.ThemeStore +import code.name.monkey.appthemehelper.util.ColorUtil +import code.name.monkey.appthemehelper.util.TintHelper +import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog +import code.name.monkey.retromusic.glide.ArtistGlideRequest +import code.name.monkey.retromusic.glide.RetroMusicColoredTarget +import code.name.monkey.retromusic.helper.MusicPlayerRemote +import code.name.monkey.retromusic.misc.AppBarStateChangeListener +import code.name.monkey.retromusic.model.Artist +import code.name.monkey.retromusic.mvp.contract.ArtistDetailContract +import code.name.monkey.retromusic.mvp.presenter.ArtistDetailsPresenter +import code.name.monkey.retromusic.rest.LastFMRestClient +import code.name.monkey.retromusic.rest.model.LastFmArtist +import code.name.monkey.retromusic.ui.activities.base.AbsSlidingMusicPanelActivity +import code.name.monkey.retromusic.ui.adapter.album.AlbumAdapter +import code.name.monkey.retromusic.ui.adapter.album.HorizontalAlbumAdapter +import code.name.monkey.retromusic.ui.adapter.song.SimpleSongAdapter +import code.name.monkey.retromusic.util.* +import com.bumptech.glide.Glide +import com.google.android.material.appbar.AppBarLayout +import kotlinx.android.synthetic.main.activity_artist_content.* +import kotlinx.android.synthetic.main.activity_artist_details.* +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response +import java.util.* + +class ArtistDetailActivity : AbsSlidingMusicPanelActivity(), ArtistDetailContract.ArtistsDetailsView { + + private var biography: Spanned? = null + private var artist: Artist? = null + private var lastFMRestClient: LastFMRestClient? = null + private var artistDetailsPresenter: ArtistDetailsPresenter? = null + private var songAdapter: SimpleSongAdapter? = null + private var albumAdapter: AlbumAdapter? = null + private var forceDownload: Boolean = false + + private fun setupWindowTransistion() { + val slide = Slide(Gravity.BOTTOM) + slide.interpolator = AnimationUtils.loadInterpolator(this, android.R.interpolator.linear_out_slow_in) + window.enterTransition = slide + } + + + override fun createContentView(): View { + return wrapSlidingMusicPanel(R.layout.activity_artist_details) + } + + override fun onCreate(savedInstanceState: Bundle?) { + setDrawUnderStatusBar() + setupWindowTransistion() + super.onCreate(savedInstanceState) + ButterKnife.bind(this) + + toggleBottomNavigationView(true) + setNavigationbarColorAuto() + setLightNavigationBar(true) + + ActivityCompat.postponeEnterTransition(this) + + lastFMRestClient = LastFMRestClient(this) + + setUpViews() + + artistDetailsPresenter = ArtistDetailsPresenter(this, intent.extras) + artistDetailsPresenter!!.subscribe() + + contentContainer.setOnScrollChangeListener { _: NestedScrollView?, _: Int, scrollY: Int, _: Int, oldScrollY: Int -> + run { + if (scrollY > oldScrollY) { + actionShuffleAll!!.setShowTitle(false) + } + if (scrollY < oldScrollY) { + actionShuffleAll!!.setShowTitle(true) + } + } + } + + biographyText.setOnClickListener { + if (biographyText.maxLines == 4) { + biographyText.maxLines = Integer.MAX_VALUE + } else { + biographyText.maxLines = 4 + } + } + actionShuffleAll.setOnClickListener { MusicPlayerRemote.openAndShuffleQueue(getArtist().songs, true) } + } + + private fun setUpViews() { + setupRecyclerView() + setupToolbarMarginHeight() + setupContainerHeight() + } + + private fun setupContainerHeight() { + if (imageContainer != null) { + val params = imageContainer!!.layoutParams + params.width = DensityUtil.getScreenHeight(this) / 2 + imageContainer!!.layoutParams = params + } + } + + private fun setupToolbarMarginHeight() { + val primaryColor = ThemeStore.primaryColor(this) + TintHelper.setTintAuto(contentContainer!!, primaryColor, true) + if (collapsingToolbar != null) { + collapsingToolbar!!.setContentScrimColor(primaryColor) + collapsingToolbar!!.setStatusBarScrimColor(ColorUtil.darkenColor(primaryColor)) + } + + toolbar!!.setNavigationIcon(R.drawable.ic_keyboard_backspace_black_24dp) + setSupportActionBar(toolbar) + + supportActionBar!!.title = null + + + if (toolbar != null && !PreferenceUtil.getInstance().fullScreenMode) { + val params = toolbar!!.layoutParams as ViewGroup.MarginLayoutParams + params.topMargin = RetroUtil.getStatusBarHeight() + toolbar!!.layoutParams = params + } + + if (appBarLayout != null) { + appBarLayout!!.addOnOffsetChangedListener(object : AppBarStateChangeListener() { + override fun onStateChanged(appBarLayout: AppBarLayout, state: AppBarStateChangeListener.State) { + val color: Int + when (state) { + AppBarStateChangeListener.State.COLLAPSED -> { + setLightStatusbar(ColorUtil.isColorLight(ThemeStore.primaryColor(appBarLayout.context))) + color = ThemeStore.primaryColor(appBarLayout.context) + } + AppBarStateChangeListener.State.EXPANDED, AppBarStateChangeListener.State.IDLE -> { + setLightStatusbar(false) + color = Color.TRANSPARENT + } + + } + ToolbarContentTintHelper.setToolbarContentColorBasedOnToolbarColor(appBarLayout.context, toolbar, color) + } + }) + } + } + + private fun setupRecyclerView() { + albumAdapter = HorizontalAlbumAdapter(this, ArrayList(), false, null) + albumRecyclerView.apply { + itemAnimator = DefaultItemAnimator() + layoutManager = GridLayoutManager(this.context, 1, GridLayoutManager.HORIZONTAL, false) + adapter = albumAdapter + } + songAdapter = SimpleSongAdapter(this, ArrayList(), R.layout.item_song) + recyclerView.apply { + itemAnimator = DefaultItemAnimator() + layoutManager = LinearLayoutManager(this.context) + adapter = songAdapter + } + } + + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + when (requestCode) { + REQUEST_CODE_SELECT_IMAGE -> if (resultCode == Activity.RESULT_OK) { + CustomArtistImageUtil.getInstance(this).setCustomArtistImage(artist, data!!.data) + } + else -> if (resultCode == Activity.RESULT_OK) { + reload() + } + } + } + + override fun onPause() { + super.onPause() + artistDetailsPresenter!!.unsubscribe() + } + + override fun loading() {} + + override fun showEmptyView() { + + } + + override fun completed() { + ActivityCompat.startPostponedEnterTransition(this) + } + + override fun showData(artist: Artist) { + setArtist(artist) + } + + private fun getArtist(): Artist { + if (artist == null) { + artist = Artist() + } + return this.artist!! + } + + private fun setArtist(artist: Artist) { + if (artist.songCount <= 0) { + finish() + } + this.artist = artist + loadArtistImage() + + if (RetroUtil.isAllowedToDownloadMetadata(this)) { + loadBiography() + } + artistTitle.text = artist.name + text.text = String.format("%s • %s", MusicUtil.getArtistInfoString(this, artist), MusicUtil + .getReadableDurationString(MusicUtil.getTotalDuration(this, artist.songs))) + + songAdapter!!.swapDataSet(artist.songs) + albumAdapter!!.swapDataSet(artist.albums!!) + } + + private fun loadBiography(lang: String? = Locale.getDefault().language) { + biography = null + + lastFMRestClient!!.apiService + .getArtistInfo(getArtist().name, lang, null) + .enqueue(object : Callback { + override fun onResponse(call: Call, + response: Response) { + val lastFmArtist = response.body() + if (lastFmArtist != null && lastFmArtist.artist != null) { + val bioContent = lastFmArtist.artist.bio.content + if (bioContent != null && !bioContent.trim { it <= ' ' }.isEmpty()) { + //TransitionManager.beginDelayedTransition(titleContainer); + biographyText.visibility = View.VISIBLE + biographyTitle.visibility = View.VISIBLE + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + biography = Html.fromHtml(bioContent, Html.FROM_HTML_MODE_LEGACY) + } else { + biography = Html.fromHtml(bioContent) + } + biographyText!!.text = biography + } + } + + // If the "lang" parameter is set and no biography is given, retry with default language + if (biography == null && lang != null) { + loadBiography(null) + } + } + + override fun onFailure(call: Call, t: Throwable) { + t.printStackTrace() + biography = null + } + }) + } + + + private fun loadArtistImage() { + ArtistGlideRequest.Builder.from(Glide.with(this), artist) + .forceDownload(forceDownload) + .generatePalette(this).build() + .dontAnimate() + .into(object : RetroMusicColoredTarget(artistImage) { + override fun onColorReady(color: Int) { + setColors(color) + } + }) + forceDownload = false + } + + private fun setColors(color: Int) { + + val textColor = if (PreferenceUtil.getInstance().adaptiveColor) color else ThemeStore.accentColor(this) + + albumTitle.setTextColor(textColor) + songTitle.setTextColor(textColor) + biographyTitle.setTextColor(textColor) + + actionShuffleAll.setColor(textColor) + + + findViewById(R.id.root).setBackgroundColor(ThemeStore.primaryColor(this)) + } + + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return handleSortOrderMenuItem(item) + } + + private fun handleSortOrderMenuItem(item: MenuItem): Boolean { + val songs = getArtist().songs + when (item.itemId) { + android.R.id.home -> { + super.onBackPressed() + return true + } + R.id.action_play_next -> { + MusicPlayerRemote.playNext(songs) + return true + } + R.id.action_add_to_current_playing -> { + MusicPlayerRemote.enqueue(songs) + return true + } + R.id.action_add_to_playlist -> { + AddToPlaylistDialog.create(songs).show(supportFragmentManager, "ADD_PLAYLIST") + return true + } + R.id.action_set_artist_image -> { + val intent = Intent(Intent.ACTION_GET_CONTENT) + intent.type = "image/*" + startActivityForResult(Intent.createChooser(intent, getString(R.string.pick_from_local_storage)), REQUEST_CODE_SELECT_IMAGE) + return true + } + R.id.action_reset_artist_image -> { + Toast.makeText(this@ArtistDetailActivity, resources.getString(R.string.updating), + Toast.LENGTH_SHORT).show() + CustomArtistImageUtil.getInstance(this@ArtistDetailActivity).resetCustomArtistImage(artist) + forceDownload = true + return true + } + } + return true + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + menuInflater.inflate(R.menu.menu_artist_detail, menu) + return super.onCreateOptionsMenu(menu) + } + + override fun onMediaStoreChanged() { + super.onMediaStoreChanged() + reload() + } + + private fun reload() { + artistDetailsPresenter!!.unsubscribe() + artistDetailsPresenter!!.subscribe() + } + + companion object { + + const val EXTRA_ARTIST_ID = "extra_artist_id" + const val REQUEST_CODE_SELECT_IMAGE = 9003 + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/ErrorHandlerActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/ErrorHandlerActivity.java index 5a0ecd4c..10253ff9 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/ErrorHandlerActivity.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/ErrorHandlerActivity.java @@ -1 +1 @@ -package code.name.monkey.retromusic.ui.activities; import android.os.Bundle; import androidx.appcompat.app.AppCompatActivity; import android.view.View; import butterknife.OnClick; import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.RetroApplication; public class ErrorHandlerActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_error_handler); } @OnClick(R.id.clear_app_data) void clearAppDate(View view) { RetroApplication.Companion.deleteAppData(); } } \ No newline at end of file +package code.name.monkey.retromusic.ui.activities; import android.os.Bundle; import androidx.appcompat.app.AppCompatActivity; import android.view.View; import butterknife.OnClick; import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.App; public class ErrorHandlerActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_error_handler); } @OnClick(R.id.clear_app_data) void clearAppDate(View view) { App.Companion.deleteAppData(); } } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/GenreDetailsActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/GenreDetailsActivity.java deleted file mode 100644 index f7130704..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/GenreDetailsActivity.java +++ /dev/null @@ -1,226 +0,0 @@ -package code.name.monkey.retromusic.ui.activities; - -import android.os.Bundle; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.widget.ProgressBar; -import android.widget.TextView; - -import com.afollestad.materialcab.MaterialCab; -import com.google.android.material.appbar.AppBarLayout; -import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView; - -import java.util.ArrayList; - -import androidx.annotation.NonNull; -import androidx.appcompat.widget.Toolbar; -import androidx.recyclerview.widget.DefaultItemAnimator; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.helper.menu.GenreMenuHelper; -import code.name.monkey.retromusic.interfaces.CabHolder; -import code.name.monkey.retromusic.model.Genre; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.mvp.contract.GenreDetailsContract; -import code.name.monkey.retromusic.mvp.presenter.GenreDetailsPresenter; -import code.name.monkey.retromusic.ui.activities.base.AbsSlidingMusicPanelActivity; -import code.name.monkey.retromusic.ui.adapter.song.SongAdapter; -import code.name.monkey.retromusic.util.RetroColorUtil; -import code.name.monkey.retromusic.util.ViewUtil; -import code.name.monkey.retromusic.views.CollapsingFAB; - -/** - * @author Hemanth S (h4h13). - */ - -public class GenreDetailsActivity extends AbsSlidingMusicPanelActivity implements GenreDetailsContract.GenreDetailsView, CabHolder { - public static final String EXTRA_GENRE_ID = "extra_genre_id"; - - @BindView(R.id.recycler_view) - RecyclerView recyclerView; - - @BindView(R.id.toolbar) - Toolbar toolbar; - - @BindView(android.R.id.empty) - TextView empty; - - @BindView(R.id.action_shuffle) - CollapsingFAB shuffleButton; - - @BindView(R.id.progress_bar) - ProgressBar progressBar; - - @BindView(R.id.app_bar) - AppBarLayout appBarLayout; - - @BindView(R.id.title) - TextView title; - - private Genre genre; - private GenreDetailsPresenter presenter; - private SongAdapter songAdapter; - private MaterialCab cab; - - private void checkIsEmpty() { - empty.setVisibility( - songAdapter.getItemCount() == 0 ? View.VISIBLE : View.GONE - ); - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - setDrawUnderStatusBar(); - super.onCreate(savedInstanceState); - ButterKnife.bind(this); - - setNavigationbarColorAuto(); - setTaskDescriptionColorAuto(); - toggleBottomNavigationView(true); - setLightNavigationBar(true); - - - genre = getIntent().getParcelableExtra(EXTRA_GENRE_ID); - presenter = new GenreDetailsPresenter(this, genre.getId()); - - setUpToolBar(); - setupRecyclerView(); - } - - @OnClick({R.id.action_shuffle}) - public void onViewClicked(View view) { - switch (view.getId()) { - case R.id.action_shuffle: - MusicPlayerRemote.INSTANCE.openAndShuffleQueue(songAdapter.getDataSet(), true); - break; - } - } - - private void setUpToolBar() { - title.setText(genre.getName()); - title.setTextColor(ThemeStore.textColorPrimary(this)); - - int primaryColor = ThemeStore.primaryColor(this); - toolbar.setNavigationIcon(R.drawable.ic_keyboard_backspace_black_24dp); - toolbar.setBackgroundColor(primaryColor); - appBarLayout.setBackgroundColor(primaryColor); - ToolbarContentTintHelper.colorBackButton(toolbar, ThemeStore.accentColor(this)); - setTitle(null); - setSupportActionBar(toolbar); - shuffleButton.setColor(ThemeStore.accentColor(this)); - } - - @Override - protected void onResume() { - super.onResume(); - presenter.subscribe(); - } - - @Override - protected void onPause() { - super.onPause(); - presenter.unsubscribe(); - } - - @Override - protected View createContentView() { - return wrapSlidingMusicPanel(R.layout.activity_playlist_detail); - } - - - @Override - public void loading() { - - } - - @Override - public void showEmptyView() { - - } - - @Override - public void completed() { - - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.menu_genre_detail, menu); - return super.onCreateOptionsMenu(menu); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (item.getItemId() == android.R.id.home) { - onBackPressed(); - } - return GenreMenuHelper.INSTANCE.handleMenuClick(this, genre, item); - } - - private void setupRecyclerView() { - ViewUtil.setUpFastScrollRecyclerViewColor(this, - ((FastScrollRecyclerView) recyclerView), ThemeStore.accentColor(this)); - songAdapter = new SongAdapter(this, new ArrayList<>(), R.layout.item_list, false, this); - recyclerView.setItemAnimator(new DefaultItemAnimator()); - recyclerView.setLayoutManager(new LinearLayoutManager(this)); - recyclerView.setAdapter(songAdapter); - recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { - @Override - public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { - super.onScrolled(recyclerView, dx, dy); - if (dy > 0) { - shuffleButton.setShowTitle(false); - } else if (dy < 0) { - shuffleButton.setShowTitle(true); - } - } - }); - songAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() { - @Override - public void onChanged() { - super.onChanged(); - checkIsEmpty(); - } - }); - } - - @Override - public void showData(ArrayList songs) { - songAdapter.swapDataSet(songs); - } - - @NonNull - @Override - public MaterialCab openCab(final int menu, final MaterialCab.Callback callback) { - if (cab != null && cab.isActive()) cab.finish(); - cab = new MaterialCab(this, R.id.cab_stub) - .setMenu(menu) - .setCloseDrawableRes(R.drawable.ic_close_white_24dp) - .setBackgroundColor(RetroColorUtil.shiftBackgroundColorForLightText(ThemeStore.primaryColor(this))) - .start(callback); - return cab; - } - - @Override - public void onBackPressed() { - if (cab != null && cab.isActive()) cab.finish(); - else { - recyclerView.stopScroll(); - super.onBackPressed(); - } - } - - @Override - public void onMediaStoreChanged() { - super.onMediaStoreChanged(); - presenter.subscribe(); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/GenreDetailsActivity.kt b/app/src/main/java/code/name/monkey/retromusic/ui/activities/GenreDetailsActivity.kt new file mode 100644 index 00000000..b1178cc1 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/GenreDetailsActivity.kt @@ -0,0 +1,174 @@ +package code.name.monkey.retromusic.ui.activities + +import android.os.Bundle +import android.view.Menu +import android.view.MenuItem +import android.view.View +import androidx.recyclerview.widget.DefaultItemAnimator +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import butterknife.ButterKnife +import code.name.monkey.appthemehelper.ThemeStore +import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.helper.MusicPlayerRemote +import code.name.monkey.retromusic.helper.menu.GenreMenuHelper +import code.name.monkey.retromusic.interfaces.CabHolder +import code.name.monkey.retromusic.model.Genre +import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.mvp.contract.GenreDetailsContract +import code.name.monkey.retromusic.mvp.presenter.GenreDetailsPresenter +import code.name.monkey.retromusic.ui.activities.base.AbsSlidingMusicPanelActivity +import code.name.monkey.retromusic.ui.adapter.song.SongAdapter +import code.name.monkey.retromusic.util.RetroColorUtil +import code.name.monkey.retromusic.util.ViewUtil +import com.afollestad.materialcab.MaterialCab +import kotlinx.android.synthetic.main.activity_playlist_detail.* +import java.util.* + +/** + * @author Hemanth S (h4h13). + */ + +class GenreDetailsActivity : AbsSlidingMusicPanelActivity(), GenreDetailsContract.GenreDetailsView, CabHolder { + + private var genre: Genre? = null + private var presenter: GenreDetailsPresenter? = null + private var songAdapter: SongAdapter? = null + private var cab: MaterialCab? = null + + private fun checkIsEmpty() { + empty!!.visibility = if (songAdapter!!.itemCount == 0) View.VISIBLE else View.GONE + } + + override fun onCreate(savedInstanceState: Bundle?) { + setDrawUnderStatusBar() + super.onCreate(savedInstanceState) + ButterKnife.bind(this) + + setStatusbarColorAuto() + setNavigationbarColorAuto() + setTaskDescriptionColorAuto() + + toggleBottomNavigationView(true) + setLightNavigationBar(true) + + + genre = intent.extras!!.getParcelable(EXTRA_GENRE_ID) + presenter = GenreDetailsPresenter(this, genre!!.id) + + setUpToolBar() + setupRecyclerView() + actionShuffle.setOnClickListener { MusicPlayerRemote.openAndShuffleQueue(songAdapter!!.dataSet, true) } + } + + private fun setUpToolBar() { + bannerTitle!!.text = genre!!.name + bannerTitle!!.setTextColor(ThemeStore.textColorPrimary(this)) + + val primaryColor = ThemeStore.primaryColor(this) + toolbar.setNavigationIcon(R.drawable.ic_keyboard_backspace_black_24dp) + toolbar.setBackgroundColor(primaryColor) + appBarLayout.setBackgroundColor(primaryColor) + ToolbarContentTintHelper.colorBackButton(toolbar!!, ThemeStore.accentColor(this)) + title = null + setSupportActionBar(toolbar) + actionShuffle.setColor(ThemeStore.accentColor(this)) + } + + override fun onResume() { + super.onResume() + presenter!!.subscribe() + } + + override fun onPause() { + super.onPause() + presenter!!.unsubscribe() + } + + override fun createContentView(): View { + return wrapSlidingMusicPanel(R.layout.activity_playlist_detail) + } + + + override fun loading() { + + } + + override fun showEmptyView() { + + } + + override fun completed() { + + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + menuInflater.inflate(R.menu.menu_genre_detail, menu) + return super.onCreateOptionsMenu(menu) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + if (item.itemId == android.R.id.home) { + onBackPressed() + } + return GenreMenuHelper.handleMenuClick(this, genre!!, item) + } + + private fun setupRecyclerView() { + ViewUtil.setUpFastScrollRecyclerViewColor(this, recyclerView, ThemeStore.accentColor(this)) + songAdapter = SongAdapter(this, ArrayList(), R.layout.item_list, false, this) + recyclerView.apply { + itemAnimator = DefaultItemAnimator() + layoutManager = LinearLayoutManager(this@GenreDetailsActivity) + adapter = songAdapter + }.addOnScrollListener(object : RecyclerView.OnScrollListener() { + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + super.onScrolled(recyclerView, dx, dy) + if (dy > 0) { + actionShuffle.setShowTitle(false) + } else if (dy < 0) { + actionShuffle.setShowTitle(true) + } + } + }) + songAdapter!!.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { + override fun onChanged() { + super.onChanged() + checkIsEmpty() + } + }) + } + + override fun showData(songs: ArrayList) { + songAdapter!!.swapDataSet(songs) + } + + override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab { + if (cab != null && cab!!.isActive) cab!!.finish() + cab = MaterialCab(this, R.id.cab_stub) + .setMenu(menuRes) + .setCloseDrawableRes(R.drawable.ic_close_white_24dp) + .setBackgroundColor(RetroColorUtil.shiftBackgroundColorForLightText(ThemeStore.primaryColor(this))) + .start(callback) + return cab!! + } + + override fun onBackPressed() { + if (cab != null && cab!!.isActive) + cab!!.finish() + else { + recyclerView!!.stopScroll() + super.onBackPressed() + } + } + + override fun onMediaStoreChanged() { + super.onMediaStoreChanged() + presenter!!.subscribe() + } + + companion object { + const val EXTRA_GENRE_ID = "extra_genre_id" + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/MainActivity.kt b/app/src/main/java/code/name/monkey/retromusic/ui/activities/MainActivity.kt index e875ca79..81f934b6 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/MainActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/MainActivity.kt @@ -4,6 +4,7 @@ import android.annotation.SuppressLint import android.content.* import android.content.pm.PackageManager import android.os.Bundle +import android.os.Handler import android.preference.PreferenceManager import android.provider.MediaStore import android.util.Log @@ -12,8 +13,11 @@ import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment import butterknife.ButterKnife +import code.name.monkey.appthemehelper.ThemeStore +import code.name.monkey.appthemehelper.util.ATHUtil +import code.name.monkey.appthemehelper.util.NavigationViewUtil +import code.name.monkey.retromusic.App import code.name.monkey.retromusic.R -import code.name.monkey.retromusic.RetroApplication import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.SearchQueryHelper import code.name.monkey.retromusic.interfaces.MainActivityFragmentCallbacks @@ -23,16 +27,18 @@ import code.name.monkey.retromusic.loaders.PlaylistSongsLoader import code.name.monkey.retromusic.service.MusicService import code.name.monkey.retromusic.ui.activities.base.AbsSlidingMusicPanelActivity import code.name.monkey.retromusic.ui.fragments.mainactivity.LibraryFragment +import code.name.monkey.retromusic.ui.fragments.mainactivity.folders.FoldersFragment import code.name.monkey.retromusic.ui.fragments.mainactivity.home.BannerHomeFragment import code.name.monkey.retromusic.util.PreferenceUtil import com.afollestad.materialdialogs.MaterialDialog -import com.google.android.material.bottomnavigation.BottomNavigationView import io.reactivex.disposables.CompositeDisposable +import kotlinx.android.synthetic.main.activity_main_drawer_layout.* import java.util.* -class MainActivity : AbsSlidingMusicPanelActivity(), SharedPreferences.OnSharedPreferenceChangeListener, BottomNavigationView.OnNavigationItemSelectedListener { - lateinit var currentFragment: MainActivityFragmentCallbacks +class MainActivity : AbsSlidingMusicPanelActivity(), SharedPreferences.OnSharedPreferenceChangeListener { + + private lateinit var currentFragment: MainActivityFragmentCallbacks private var blockRequestPermissions: Boolean = false private val disposable = CompositeDisposable() @@ -63,16 +69,23 @@ class MainActivity : AbsSlidingMusicPanelActivity(), SharedPreferences.OnSharedP super.onCreate(savedInstanceState) ButterKnife.bind(this) - getBottomNavigationView()!!.setOnNavigationItemSelectedListener(this) + getBottomNavigationView()!!.setOnNavigationItemSelectedListener { + PreferenceUtil.getInstance().lastPage = it.itemId + selectedFragment(it.itemId) + true + } + + setUpDrawerLayout() if (savedInstanceState == null) { - selectedFragment(PreferenceUtil.getInstance().lastPage) + setMusicChooser(PreferenceUtil.getInstance().lastMusicChooser) } else { - restoreCurrentFragment() + restoreCurrentFragment(); } + checkShowChangelog() - if (!RetroApplication.isProVersion && !PreferenceManager.getDefaultSharedPreferences(this).getBoolean("shown", false)) { + if (!App.isProVersion && !PreferenceManager.getDefaultSharedPreferences(this).getBoolean("shown", false)) { showPromotionalOffer() } } @@ -104,7 +117,6 @@ class MainActivity : AbsSlidingMusicPanelActivity(), SharedPreferences.OnSharedP intent.putExtra("expand", false) } } - } override fun onDestroy() { @@ -114,16 +126,12 @@ class MainActivity : AbsSlidingMusicPanelActivity(), SharedPreferences.OnSharedP PreferenceUtil.getInstance().unregisterOnSharedPreferenceChangedListener(this) } - fun setCurrentFragment(fragment: Fragment, isStackAdd: Boolean, tag: String) { - val fragmentTransaction = supportFragmentManager.beginTransaction() - fragmentTransaction.replace(R.id.fragment_container, fragment, tag) - if (isStackAdd) { - fragmentTransaction.addToBackStack(tag) - } - fragmentTransaction.commit() + private fun setCurrentFragment(fragment: Fragment) { + supportFragmentManager.beginTransaction().replace(R.id.fragment_container, fragment, null).commit() currentFragment = fragment as MainActivityFragmentCallbacks } + private fun restoreCurrentFragment() { currentFragment = supportFragmentManager.findFragmentById(R.id.fragment_container) as MainActivityFragmentCallbacks } @@ -139,7 +147,6 @@ class MainActivity : AbsSlidingMusicPanelActivity(), SharedPreferences.OnSharedP if (intent.action != null && intent.action == MediaStore.INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH) { val songs = SearchQueryHelper.getSongs(this, intent.extras!!) - if (MusicPlayerRemote.shuffleMode == MusicService.SHUFFLE_MODE_SHUFFLE) { MusicPlayerRemote.openAndShuffleQueue(songs, true) } else { @@ -148,15 +155,14 @@ class MainActivity : AbsSlidingMusicPanelActivity(), SharedPreferences.OnSharedP handled = true } - if (uri != null && uri.toString().length > 0) { + if (uri != null && uri.toString().isNotEmpty()) { MusicPlayerRemote.playFromUri(uri) handled = true } else if (MediaStore.Audio.Playlists.CONTENT_TYPE == mimeType) { val id = parseIdFromIntent(intent, "playlistId", "playlist").toInt() if (id >= 0) { val position = intent.getIntExtra("position", 0) - val songs = ArrayList( - PlaylistSongsLoader.getPlaylistSongList(this, id).blockingFirst()) + val songs = ArrayList(PlaylistSongsLoader.getPlaylistSongList(this, id).blockingFirst()) MusicPlayerRemote.openQueue(songs, position, true) handled = true } @@ -164,16 +170,14 @@ class MainActivity : AbsSlidingMusicPanelActivity(), SharedPreferences.OnSharedP val id = parseIdFromIntent(intent, "albumId", "album").toInt() if (id >= 0) { val position = intent.getIntExtra("position", 0) - MusicPlayerRemote - .openQueue(AlbumLoader.getAlbum(this, id).blockingFirst().songs!!, position, true) + MusicPlayerRemote.openQueue(AlbumLoader.getAlbum(this, id).blockingFirst().songs!!, position, true) handled = true } } else if (MediaStore.Audio.Artists.CONTENT_TYPE == mimeType) { val id = parseIdFromIntent(intent, "artistId", "artist").toInt() if (id >= 0) { val position = intent.getIntExtra("position", 0) - MusicPlayerRemote - .openQueue(ArtistLoader.getArtist(this, id).blockingFirst().songs, position, true) + MusicPlayerRemote.openQueue(ArtistLoader.getArtist(this, id).blockingFirst().songs, position, true) handled = true } } @@ -192,7 +196,6 @@ class MainActivity : AbsSlidingMusicPanelActivity(), SharedPreferences.OnSharedP } catch (e: NumberFormatException) { Log.e(TAG, e.message) } - } } return id @@ -213,6 +216,10 @@ class MainActivity : AbsSlidingMusicPanelActivity(), SharedPreferences.OnSharedP } override fun handleBackPress(): Boolean { + if (drawerLayout.isDrawerOpen(navigationView)) { + drawerLayout.closeDrawers() + return true + } return super.handleBackPress() || currentFragment.handleBackPress() } @@ -257,10 +264,10 @@ class MainActivity : AbsSlidingMusicPanelActivity(), SharedPreferences.OnSharedP private fun showPromotionalOffer() { MaterialDialog.Builder(this) .positiveText("Buy") - .onPositive { dialog, which -> startActivity(Intent(this@MainActivity, ProVersionActivity::class.java)) } + .onPositive { _, _ -> startActivity(Intent(this@MainActivity, ProVersionActivity::class.java)) } .negativeText(android.R.string.cancel) .customView(R.layout.dialog_promotional_offer, false) - .dismissListener { dialog -> + .dismissListener { PreferenceManager.getDefaultSharedPreferences(this@MainActivity) .edit() .putBoolean("shown", true) @@ -269,19 +276,82 @@ class MainActivity : AbsSlidingMusicPanelActivity(), SharedPreferences.OnSharedP .show() } - override fun onNavigationItemSelected(menuItem: MenuItem): Boolean { - PreferenceUtil.getInstance().lastPage = menuItem.itemId - selectedFragment(menuItem.itemId) - return true - } - private fun selectedFragment(itemId: Int) { when (itemId) { - R.id.action_album, R.id.action_artist, R.id.action_playlist, R.id.action_song -> setCurrentFragment(LibraryFragment.newInstance(itemId), false, LibraryFragment.TAG) - R.id.action_home -> setCurrentFragment(BannerHomeFragment.newInstance(), false, BannerHomeFragment.TAG) + R.id.action_album, + R.id.action_artist, + R.id.action_playlist, + R.id.action_song -> setCurrentFragment(LibraryFragment.newInstance(itemId)) } } + private fun setUpNavigationView() { + val accentColor = ThemeStore.accentColor(this) + NavigationViewUtil.setItemIconColors(navigationView, ATHUtil.resolveColor(this, R.attr.iconColor, ThemeStore.textColorSecondary(this)), accentColor) + NavigationViewUtil.setItemTextColors(navigationView, ThemeStore.textColorPrimary(this), accentColor) + + checkSetUpPro() + navigationView.setNavigationItemSelectedListener { menuItem -> + drawerLayout.closeDrawers() + when (menuItem.itemId) { + R.id.nav_library -> Handler().postDelayed({ setMusicChooser(LIBRARY) }, 200) + R.id.nav_home -> Handler().postDelayed({ setMusicChooser(HOME) }, 200) + R.id.nav_folders -> Handler().postDelayed({ setMusicChooser(FOLDERS) }, 200) + R.id.buy_pro -> Handler().postDelayed({ startActivityForResult(Intent(this@MainActivity, ProVersionActivity::class.java), PURCHASE_REQUEST) }, 200) + } + true + } + } + + private fun setMusicChooser(key: Int) { + PreferenceUtil.getInstance().lastMusicChooser = key + when (key) { + LIBRARY -> { + navigationView.setCheckedItem(R.id.nav_library) + setCurrentFragment(LibraryFragment.newInstance()) + } + FOLDERS -> { + navigationView.setCheckedItem(R.id.nav_folders) + setCurrentFragment(FoldersFragment.newInstance(this)) + } + HOME -> { + navigationView.setCheckedItem(R.id.nav_home) + setCurrentFragment(BannerHomeFragment()) + } + } + } + + + private fun checkSetUpPro() { + if (App.isProVersion) { + setUpPro() + } + } + + private fun setUpPro() { + navigationView.menu.removeGroup(R.id.navigation_drawer_menu_category_buy_pro) + } + + private fun setUpDrawerLayout() { + setUpNavigationView() + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + if (item.itemId == android.R.id.home) { + if (drawerLayout.isDrawerOpen(navigationView)) { + drawerLayout.closeDrawer(navigationView) + } else { + drawerLayout.openDrawer(navigationView) + } + return true + } + return super.onOptionsItemSelected(item) + } + + private fun updateNavigationDrawerHeader() { + + } + companion object { const val APP_INTRO_REQUEST = 2323 const val LIBRARY = 1 @@ -290,5 +360,6 @@ class MainActivity : AbsSlidingMusicPanelActivity(), SharedPreferences.OnSharedP private const val TAG = "MainActivity" private const val APP_USER_INFO_REQUEST = 9003 private const val REQUEST_CODE_THEME = 9002 + private const val PURCHASE_REQUEST = 101 } } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/PlaylistDetailActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/PlaylistDetailActivity.java deleted file mode 100644 index 724b3e51..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/PlaylistDetailActivity.java +++ /dev/null @@ -1,309 +0,0 @@ -package code.name.monkey.retromusic.ui.activities; - -import android.os.Bundle; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.widget.TextView; - -import com.afollestad.materialcab.MaterialCab; -import com.google.android.material.appbar.AppBarLayout; -import com.h6ah4i.android.widget.advrecyclerview.animator.GeneralItemAnimator; -import com.h6ah4i.android.widget.advrecyclerview.animator.RefactoredDefaultItemAnimator; -import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager; -import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils; -import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView; - -import java.util.ArrayList; - -import androidx.annotation.NonNull; -import androidx.appcompat.widget.Toolbar; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.helper.menu.PlaylistMenuHelper; -import code.name.monkey.retromusic.interfaces.CabHolder; -import code.name.monkey.retromusic.loaders.PlaylistLoader; -import code.name.monkey.retromusic.model.AbsCustomPlaylist; -import code.name.monkey.retromusic.model.Playlist; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.mvp.contract.PlaylistSongsContract; -import code.name.monkey.retromusic.mvp.presenter.PlaylistSongsPresenter; -import code.name.monkey.retromusic.ui.activities.base.AbsSlidingMusicPanelActivity; -import code.name.monkey.retromusic.ui.adapter.song.OrderablePlaylistSongAdapter; -import code.name.monkey.retromusic.ui.adapter.song.PlaylistSongAdapter; -import code.name.monkey.retromusic.ui.adapter.song.SongAdapter; -import code.name.monkey.retromusic.util.PlaylistsUtil; -import code.name.monkey.retromusic.util.RetroColorUtil; -import code.name.monkey.retromusic.util.ViewUtil; -import code.name.monkey.retromusic.views.CollapsingFAB; - -public class PlaylistDetailActivity extends AbsSlidingMusicPanelActivity implements CabHolder, - PlaylistSongsContract.PlaylistSongsView { - - @NonNull - public static String EXTRA_PLAYLIST = "extra_playlist"; - - @BindView(R.id.recycler_view) - RecyclerView recyclerView; - - @BindView(R.id.toolbar) - Toolbar toolbar; - - @BindView(android.R.id.empty) - TextView empty; - - @BindView(R.id.action_shuffle) - CollapsingFAB shuffleButton; - - @BindView(R.id.app_bar) - AppBarLayout appBarLayout; - - @BindView(R.id.title) - TextView title; - - private Playlist playlist; - private MaterialCab cab; - private SongAdapter adapter; - private RecyclerView.Adapter wrappedAdapter; - private RecyclerViewDragDropManager recyclerViewDragDropManager; - private PlaylistSongsPresenter songsPresenter; - - @Override - protected void onCreate(Bundle savedInstanceState) { - setDrawUnderStatusBar(); - super.onCreate(savedInstanceState); - ButterKnife.bind(this); - - setStatusbarColorAuto(); - setNavigationbarColorAuto(); - setTaskDescriptionColorAuto(); - setLightNavigationBar(true); - toggleBottomNavigationView(true); - - playlist = getIntent().getExtras().getParcelable(EXTRA_PLAYLIST); - - if (playlist != null) { - songsPresenter = new PlaylistSongsPresenter(this, playlist); - } - - setUpToolBar(); - setUpRecyclerView(); - } - - @Override - protected View createContentView() { - return wrapSlidingMusicPanel(R.layout.activity_playlist_detail); - } - - private void setUpRecyclerView() { - ViewUtil.setUpFastScrollRecyclerViewColor(this, ((FastScrollRecyclerView) recyclerView), - ThemeStore.accentColor(this)); - recyclerView.setLayoutManager(new LinearLayoutManager(this)); - if (playlist instanceof AbsCustomPlaylist) { - adapter = new PlaylistSongAdapter(this, new ArrayList(), R.layout.item_list, false, - this); - recyclerView.setAdapter(adapter); - } else { - recyclerViewDragDropManager = new RecyclerViewDragDropManager(); - final GeneralItemAnimator animator = new RefactoredDefaultItemAnimator(); - adapter = new OrderablePlaylistSongAdapter(this, new ArrayList<>(), - R.layout.item_list, false, this, (fromPosition, toPosition) -> { - if (PlaylistsUtil.moveItem(PlaylistDetailActivity.this, playlist.id, fromPosition, toPosition)) { - Song song = adapter.getDataSet().remove(fromPosition); - adapter.getDataSet().add(toPosition, song); - adapter.notifyItemMoved(fromPosition, toPosition); - } - }); - wrappedAdapter = recyclerViewDragDropManager.createWrappedAdapter(adapter); - - recyclerView.setAdapter(wrappedAdapter); - recyclerView.setItemAnimator(animator); - - recyclerViewDragDropManager.attachRecyclerView(recyclerView); - } - adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() { - @Override - public void onChanged() { - super.onChanged(); - checkIsEmpty(); - } - }); - recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { - @Override - public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { - super.onScrolled(recyclerView, dx, dy); - if (dy > 0) { - shuffleButton.setShowTitle(false); - } else if (dy < 0) { - shuffleButton.setShowTitle(true); - } - } - }); - } - - @Override - protected void onResume() { - super.onResume(); - songsPresenter.subscribe(); - } - - private void setUpToolBar() { - title.setText(playlist.name); - title.setTextColor(ThemeStore.textColorPrimary(this)); - shuffleButton.setColor(ThemeStore.accentColor(this)); - - int primaryColor = ThemeStore.primaryColor(this); - toolbar.setBackgroundColor(primaryColor); - appBarLayout.setBackgroundColor(primaryColor); - - setTitle(null); - setSupportActionBar(toolbar); - toolbar.setNavigationIcon(R.drawable.ic_keyboard_backspace_black_24dp); - - ToolbarContentTintHelper.colorBackButton(toolbar, ThemeStore.accentColor(this)); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(playlist instanceof AbsCustomPlaylist ? R.menu.menu_smart_playlist_detail : R.menu.menu_playlist_detail, menu); - return super.onCreateOptionsMenu(menu); - } - - @Override - public boolean onOptionsItemSelected(@NonNull MenuItem item) { - int id = item.getItemId(); - switch (id) { - case android.R.id.home: - onBackPressed(); - return true; - } - return PlaylistMenuHelper.INSTANCE.handleMenuClick(this, playlist, item); - } - - @NonNull - @Override - public MaterialCab openCab(final int menu, final MaterialCab.Callback callback) { - if (cab != null && cab.isActive()) { - cab.finish(); - } - cab = new MaterialCab(this, R.id.cab_stub) - .setMenu(menu) - .setCloseDrawableRes(R.drawable.ic_close_white_24dp) - .setBackgroundColor( - RetroColorUtil.shiftBackgroundColorForLightText(ThemeStore.primaryColor(this))) - .start(callback); - return cab; - } - - @Override - public void onBackPressed() { - if (cab != null && cab.isActive()) { - cab.finish(); - } else { - recyclerView.stopScroll(); - super.onBackPressed(); - } - } - - @Override - public void onMediaStoreChanged() { - super.onMediaStoreChanged(); - - if (!(playlist instanceof AbsCustomPlaylist)) { - // Playlist deleted - if (!PlaylistsUtil.doesPlaylistExist(this, playlist.id)) { - finish(); - return; - } - - // Playlist renamed - final String playlistName = PlaylistsUtil.getNameForPlaylist(this, playlist.id); - if (!playlistName.equals(playlist.name)) { - playlist = PlaylistLoader.INSTANCE.getPlaylist(this, playlist.id).blockingFirst(); - setToolbarTitle(playlist.name); - } - } - songsPresenter.subscribe(); - } - - private void setToolbarTitle(String title) { - //noinspection ConstantConditions - getSupportActionBar().setTitle(title); - } - - private void checkIsEmpty() { - empty.setVisibility( - adapter.getItemCount() == 0 ? View.VISIBLE : View.GONE - ); - } - - @Override - public void onPause() { - if (recyclerViewDragDropManager != null) { - recyclerViewDragDropManager.cancelDrag(); - } - super.onPause(); - songsPresenter.unsubscribe(); - } - - @Override - public void onDestroy() { - if (recyclerViewDragDropManager != null) { - recyclerViewDragDropManager.release(); - recyclerViewDragDropManager = null; - } - - if (recyclerView != null) { - recyclerView.setItemAnimator(null); - recyclerView.setAdapter(null); - recyclerView = null; - } - - if (wrappedAdapter != null) { - WrapperAdapterUtils.releaseAll(wrappedAdapter); - wrappedAdapter = null; - } - adapter = null; - - super.onDestroy(); - } - - @Override - public void onPlayingMetaChanged() { - super.onPlayingMetaChanged(); - songsPresenter.subscribe(); - } - - @Override - public void loading() { - } - - @Override - public void showEmptyView() { - empty.setVisibility(View.VISIBLE); - } - - @Override - public void completed() { - } - - @Override - public void showData(ArrayList songs) { - adapter.swapDataSet(songs); - } - - @OnClick(R.id.action_shuffle) - public void onViewClicked() { - if (adapter.getDataSet().isEmpty()) { - return; - } - MusicPlayerRemote.INSTANCE.openAndShuffleQueue(adapter.getDataSet(), true); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/PlaylistDetailActivity.kt b/app/src/main/java/code/name/monkey/retromusic/ui/activities/PlaylistDetailActivity.kt new file mode 100644 index 00000000..07ca6e0e --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/PlaylistDetailActivity.kt @@ -0,0 +1,254 @@ +package code.name.monkey.retromusic.ui.activities + +import android.os.Bundle +import android.view.Menu +import android.view.MenuItem +import android.view.View +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import butterknife.ButterKnife +import code.name.monkey.appthemehelper.ThemeStore +import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.helper.MusicPlayerRemote +import code.name.monkey.retromusic.helper.menu.PlaylistMenuHelper +import code.name.monkey.retromusic.interfaces.CabHolder +import code.name.monkey.retromusic.loaders.PlaylistLoader +import code.name.monkey.retromusic.model.AbsCustomPlaylist +import code.name.monkey.retromusic.model.Playlist +import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.mvp.contract.PlaylistSongsContract +import code.name.monkey.retromusic.mvp.presenter.PlaylistSongsPresenter +import code.name.monkey.retromusic.ui.activities.base.AbsSlidingMusicPanelActivity +import code.name.monkey.retromusic.ui.adapter.song.OrderablePlaylistSongAdapter +import code.name.monkey.retromusic.ui.adapter.song.PlaylistSongAdapter +import code.name.monkey.retromusic.ui.adapter.song.SongAdapter +import code.name.monkey.retromusic.util.PlaylistsUtil +import code.name.monkey.retromusic.util.RetroColorUtil +import code.name.monkey.retromusic.util.ViewUtil +import com.afollestad.materialcab.MaterialCab +import com.h6ah4i.android.widget.advrecyclerview.animator.RefactoredDefaultItemAnimator +import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager +import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils +import kotlinx.android.synthetic.main.activity_playlist_detail.* +import java.util.* + +class PlaylistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder, PlaylistSongsContract.PlaylistSongsView { + + + private var playlist: Playlist? = null + private var cab: MaterialCab? = null + private lateinit var adapter: SongAdapter + private var wrappedAdapter: RecyclerView.Adapter<*>? = null + private var recyclerViewDragDropManager: RecyclerViewDragDropManager? = null + private var songsPresenter: PlaylistSongsPresenter? = null + + override fun onCreate(savedInstanceState: Bundle?) { + setDrawUnderStatusBar() + super.onCreate(savedInstanceState) + ButterKnife.bind(this) + + setStatusbarColorAuto() + setNavigationbarColorAuto() + setTaskDescriptionColorAuto() + setLightNavigationBar(true) + toggleBottomNavigationView(true) + + + playlist = intent.extras!!.getParcelable(EXTRA_PLAYLIST) + songsPresenter = PlaylistSongsPresenter(this, playlist!!) + + setUpToolBar() + setUpRecyclerView() + } + + override fun createContentView(): View { + return wrapSlidingMusicPanel(R.layout.activity_playlist_detail) + } + + private fun setUpRecyclerView() { + ViewUtil.setUpFastScrollRecyclerViewColor(this, recyclerView, ThemeStore.accentColor(this)) + recyclerView.layoutManager = LinearLayoutManager(this) + if (playlist is AbsCustomPlaylist) { + adapter = PlaylistSongAdapter(this, ArrayList(), R.layout.item_list, false, this) + recyclerView!!.adapter = adapter + } else { + recyclerViewDragDropManager = RecyclerViewDragDropManager() + val animator = RefactoredDefaultItemAnimator() + adapter = OrderablePlaylistSongAdapter(this, ArrayList(), R.layout.item_list, false, this, + object : OrderablePlaylistSongAdapter.OnMoveItemListener { + override fun onMoveItem(fromPosition: Int, toPosition: Int) { + if (PlaylistsUtil.moveItem(this@PlaylistDetailActivity, playlist!!.id, fromPosition, toPosition)) { + val song = adapter.dataSet.removeAt(fromPosition) + adapter.dataSet.add(toPosition, song) + adapter.notifyItemMoved(fromPosition, toPosition) + } + } + }) + wrappedAdapter = recyclerViewDragDropManager!!.createWrappedAdapter(adapter) + + recyclerView.adapter = wrappedAdapter + recyclerView.itemAnimator = animator + + recyclerViewDragDropManager!!.attachRecyclerView(recyclerView!!) + } + adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { + override fun onChanged() { + super.onChanged() + checkIsEmpty() + } + }) + recyclerView!!.addOnScrollListener(object : RecyclerView.OnScrollListener() { + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + super.onScrolled(recyclerView, dx, dy) + if (dy > 0) { + actionShuffle.setShowTitle(false) + } else if (dy < 0) { + actionShuffle.setShowTitle(true) + } + } + }) + actionShuffle.setOnClickListener { + if (adapter.dataSet.isEmpty()) { + return@setOnClickListener + } + MusicPlayerRemote.openAndShuffleQueue(adapter.dataSet, true) + } + } + + override fun onResume() { + super.onResume() + songsPresenter!!.subscribe() + } + + private fun setUpToolBar() { + bannerTitle.text = playlist!!.name + bannerTitle.setTextColor(ThemeStore.textColorPrimary(this)) + actionShuffle.setColor(ThemeStore.accentColor(this)) + + val primaryColor = ThemeStore.primaryColor(this) + toolbar!!.setBackgroundColor(primaryColor) + appBarLayout!!.setBackgroundColor(primaryColor) + + title = null + setSupportActionBar(toolbar) + toolbar!!.setNavigationIcon(R.drawable.ic_keyboard_backspace_black_24dp) + + ToolbarContentTintHelper.colorBackButton(toolbar!!, ThemeStore.accentColor(this)) + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + menuInflater.inflate(if (playlist is AbsCustomPlaylist) R.menu.menu_smart_playlist_detail else R.menu.menu_playlist_detail, menu) + return super.onCreateOptionsMenu(menu) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + val id = item.itemId + when (id) { + android.R.id.home -> { + onBackPressed() + return true + } + } + return PlaylistMenuHelper.handleMenuClick(this, playlist!!, item) + } + + override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab { + if (cab != null && cab!!.isActive) { + cab!!.finish() + } + cab = MaterialCab(this, R.id.cab_stub) + .setMenu(menuRes) + .setCloseDrawableRes(R.drawable.ic_close_white_24dp) + .setBackgroundColor( + RetroColorUtil.shiftBackgroundColorForLightText(ThemeStore.primaryColor(this))) + .start(callback) + return cab!! + } + + override fun onBackPressed() { + if (cab != null && cab!!.isActive) { + cab!!.finish() + } else { + recyclerView!!.stopScroll() + super.onBackPressed() + } + } + + override fun onMediaStoreChanged() { + super.onMediaStoreChanged() + + if (playlist !is AbsCustomPlaylist) { + // Playlist deleted + if (!PlaylistsUtil.doesPlaylistExist(this, playlist!!.id)) { + finish() + return + } + + // Playlist renamed + val playlistName = PlaylistsUtil.getNameForPlaylist(this, playlist!!.id.toLong()) + if (playlistName != playlist!!.name) { + playlist = PlaylistLoader.getPlaylist(this, playlist!!.id).blockingFirst() + setToolbarTitle(playlist!!.name) + } + } + songsPresenter!!.subscribe() + } + + private fun setToolbarTitle(title: String) { + + supportActionBar!!.title = title + } + + private fun checkIsEmpty() { + empty.visibility = if (adapter.itemCount == 0) View.VISIBLE else View.GONE + } + + public override fun onPause() { + if (recyclerViewDragDropManager != null) { + recyclerViewDragDropManager!!.cancelDrag() + } + super.onPause() + songsPresenter!!.unsubscribe() + } + + override fun onDestroy() { + if (recyclerViewDragDropManager != null) { + recyclerViewDragDropManager!!.release() + recyclerViewDragDropManager = null + } + + if (recyclerView != null) { + recyclerView!!.itemAnimator = null + recyclerView!!.adapter = null + } + + if (wrappedAdapter != null) { + WrapperAdapterUtils.releaseAll(wrappedAdapter) + wrappedAdapter = null + } + super.onDestroy() + } + + override fun onPlayingMetaChanged() { + super.onPlayingMetaChanged() + songsPresenter!!.subscribe() + } + + override fun loading() {} + + override fun showEmptyView() { + empty!!.visibility = View.VISIBLE + } + + override fun completed() {} + + override fun showData(songs: ArrayList) { + adapter.swapDataSet(songs) + } + + companion object { + + var EXTRA_PLAYLIST = "extra_playlist" + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/ProVersionActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/ProVersionActivity.java index e2a8ea95..cd425bd6 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/ProVersionActivity.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/ProVersionActivity.java @@ -27,7 +27,7 @@ import code.name.monkey.appthemehelper.util.MaterialUtil; import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper; import code.name.monkey.retromusic.BuildConfig; import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.RetroApplication; +import code.name.monkey.retromusic.App; import code.name.monkey.retromusic.ui.activities.base.AbsBaseActivity; /** @@ -106,7 +106,7 @@ public class ProVersionActivity extends AbsBaseActivity implements } break; case R.id.purchase_button: - billingProcessor.purchase(ProVersionActivity.this, RetroApplication.PRO_VERSION_PRODUCT_ID); + billingProcessor.purchase(ProVersionActivity.this, App.PRO_VERSION_PRODUCT_ID); break; } } @@ -119,7 +119,7 @@ public class ProVersionActivity extends AbsBaseActivity implements @Override public void onPurchaseHistoryRestored() { - if (RetroApplication.Companion.isProVersion()) { + if (App.Companion.isProVersion()) { Toast.makeText(this, R.string.restored_previous_purchase_please_restart, Toast.LENGTH_LONG) .show(); setResult(RESULT_OK); diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/SettingsActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/SettingsActivity.java deleted file mode 100755 index b0e912ab..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/SettingsActivity.java +++ /dev/null @@ -1,163 +0,0 @@ -package code.name.monkey.retromusic.ui.activities; - -import android.content.SharedPreferences; -import android.os.Bundle; -import android.util.Log; -import android.view.MenuItem; -import android.widget.FrameLayout; -import android.widget.TextView; - -import com.afollestad.materialdialogs.color.ColorChooserDialog; -import com.google.android.material.appbar.AppBarLayout; - -import androidx.annotation.ColorInt; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.StringRes; -import androidx.appcompat.widget.Toolbar; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentTransaction; -import androidx.transition.TransitionManager; -import butterknife.BindView; -import butterknife.ButterKnife; -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.appthemehelper.util.ColorUtil; -import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.ui.activities.base.AbsBaseActivity; -import code.name.monkey.retromusic.ui.fragments.settings.MainSettingsFragment; -import code.name.monkey.retromusic.util.PreferenceUtil; - -public class SettingsActivity extends AbsBaseActivity implements ColorChooserDialog.ColorCallback, SharedPreferences.OnSharedPreferenceChangeListener { - - private static final String TAG = "SettingsActivity"; - - @BindView(R.id.toolbar) - Toolbar toolbar; - - @BindView(R.id.app_bar) - AppBarLayout appBarLayout; - - @BindView(R.id.title) - TextView title; - - @BindView(R.id.detail_content_frame) - @Nullable - FrameLayout detailsFrame; - - private FragmentManager fragmentManager = getSupportFragmentManager(); - - @Override - public void onColorSelection(@NonNull ColorChooserDialog dialog, @ColorInt int selectedColor) { - switch (dialog.getTitle()) { - case R.string.primary_color: - int theme = ColorUtil.isColorLight(selectedColor) ? - PreferenceUtil.getThemeResFromPrefValue("light") : - PreferenceUtil.getThemeResFromPrefValue("dark"); - - ThemeStore.editTheme(this).activityTheme(theme).primaryColor(selectedColor).commit(); - break; - case R.string.accent_color: - ThemeStore.editTheme(this).accentColor(selectedColor).commit(); - break; - } - - recreate(); - } - - @Override - public void onColorChooserDismissed(@NonNull ColorChooserDialog dialog) { - - } - - @Override - protected void onCreate(@Nullable Bundle bundle) { - super.onCreate(bundle); - setContentView(R.layout.activity_settings); - ButterKnife.bind(this); - - setStatusbarColorAuto(); - setNavigationbarColorAuto(); - - setLightNavigationBar(true); - - setupToolbar(); - - if (bundle == null) { - fragmentManager.beginTransaction().replace(R.id.content_frame, new MainSettingsFragment()) - .commit(); - } - } - - private void setupToolbar() { - toolbar.setBackgroundColor(ThemeStore.primaryColor(this)); - appBarLayout.setBackgroundColor(ThemeStore.primaryColor(this)); - setSupportActionBar(toolbar); - setTitle(null); - toolbar.setNavigationOnClickListener(v -> onBackPressed()); - title.setTextColor(ThemeStore.textColorPrimary(this)); - ToolbarContentTintHelper.colorBackButton(toolbar, ThemeStore.accentColor(this)); - } - - public void setupFragment(Fragment fragment, @StringRes int titleName) { - FragmentTransaction fragmentTransaction = fragmentManager - .beginTransaction() - .setCustomAnimations(R.anim.sliding_in_left, R.anim.sliding_out_right, android.R.anim.slide_in_left, android.R.anim.slide_out_right); - - title.setText(titleName); - - if (detailsFrame == null) { - fragmentTransaction.replace(R.id.content_frame, fragment, fragment.getTag()); - fragmentTransaction.addToBackStack(null); - fragmentTransaction.commit(); - } else { - fragmentTransaction.replace(R.id.detail_content_frame, fragment, fragment.getTag()); - fragmentTransaction.commit(); - } - } - - @Override - public void onBackPressed() { - if (fragmentManager.getBackStackEntryCount() == 0) { - super.onBackPressed(); - } else { - title.setText(R.string.action_settings); - fragmentManager.popBackStack(); - } - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (item.getItemId() == android.R.id.home) { - onBackPressed(); - return true; - } - return super.onOptionsItemSelected(item); - } - - public void addAppbarLayoutElevation(float v) { - TransitionManager.beginDelayedTransition(appBarLayout); - appBarLayout.setElevation(v); - } - - @Override - public void onPause() { - super.onPause(); - PreferenceUtil.getInstance().unregisterOnSharedPreferenceChangedListener(this); - } - - @Override - public void onResume() { - super.onResume(); - PreferenceUtil.getInstance().registerOnSharedPreferenceChangedListener(this); - } - - @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - Log.i(TAG, "onSharedPreferenceChanged: "); - if (key.equals(PreferenceUtil.PROFILE_IMAGE_PATH)) { - recreate(); - } - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/SettingsActivity.kt b/app/src/main/java/code/name/monkey/retromusic/ui/activities/SettingsActivity.kt new file mode 100755 index 00000000..e0fe1e00 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/SettingsActivity.kt @@ -0,0 +1,124 @@ +package code.name.monkey.retromusic.ui.activities + +import android.content.SharedPreferences +import android.os.Bundle +import android.view.MenuItem +import androidx.annotation.ColorInt +import androidx.annotation.StringRes +import androidx.fragment.app.Fragment +import butterknife.ButterKnife +import code.name.monkey.appthemehelper.ThemeStore +import code.name.monkey.appthemehelper.util.ColorUtil +import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.ui.activities.base.AbsBaseActivity +import code.name.monkey.retromusic.ui.fragments.settings.MainSettingsFragment +import code.name.monkey.retromusic.util.PreferenceUtil +import com.afollestad.materialdialogs.color.ColorChooserDialog +import kotlinx.android.synthetic.main.activity_settings.* + +class SettingsActivity : AbsBaseActivity(), ColorChooserDialog.ColorCallback, SharedPreferences.OnSharedPreferenceChangeListener { + + private val fragmentManager = supportFragmentManager + + override fun onColorSelection(dialog: ColorChooserDialog, @ColorInt selectedColor: Int) { + when (dialog.title) { + R.string.primary_color -> { + val theme = if (ColorUtil.isColorLight(selectedColor)) + PreferenceUtil.getThemeResFromPrefValue("light") + else + PreferenceUtil.getThemeResFromPrefValue("dark") + + ThemeStore.editTheme(this).activityTheme(theme).primaryColor(selectedColor).commit() + } + R.string.accent_color -> ThemeStore.editTheme(this).accentColor(selectedColor).commit() + } + recreate() + } + + override fun onColorChooserDismissed(dialog: ColorChooserDialog) { + + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_settings) + ButterKnife.bind(this) + + setStatusbarColorAuto() + setNavigationbarColorAuto() + + setLightNavigationBar(true) + + setupToolbar() + + if (savedInstanceState == null) { + fragmentManager.beginTransaction().replace(R.id.contentFrame, MainSettingsFragment()) + .commit() + } + } + + private fun setupToolbar() { + setSupportActionBar(toolbar) + title = null + toolbar.setBackgroundColor(ThemeStore.primaryColor(this)) + appBarLayout.setBackgroundColor(ThemeStore.primaryColor(this)) + toolbar.setNavigationOnClickListener { onBackPressed() } + settingsTitle.setTextColor(ThemeStore.textColorPrimary(this)) + ToolbarContentTintHelper.colorBackButton(toolbar, ThemeStore.accentColor(this)) + } + + fun setupFragment(fragment: Fragment, @StringRes titleName: Int) { + val fragmentTransaction = fragmentManager + .beginTransaction() + .setCustomAnimations(R.anim.sliding_in_left, R.anim.sliding_out_right, android.R.anim.slide_in_left, android.R.anim.slide_out_right) + + settingsTitle.setText(titleName) + + if (detailContentFrame == null) { + fragmentTransaction.replace(R.id.contentFrame, fragment, fragment.tag) + fragmentTransaction.addToBackStack(null) + fragmentTransaction.commit() + } else { + fragmentTransaction.replace(R.id.detailContentFrame, fragment, fragment.tag) + fragmentTransaction.commit() + } + } + + override fun onBackPressed() { + if (fragmentManager.backStackEntryCount == 0) { + super.onBackPressed() + } else { + settingsTitle.setText(R.string.action_settings) + fragmentManager.popBackStack() + } + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + if (item.itemId == android.R.id.home) { + onBackPressed() + return true + } + return super.onOptionsItemSelected(item) + } + + public override fun onPause() { + super.onPause() + PreferenceUtil.getInstance().unregisterOnSharedPreferenceChangedListener(this) + } + + public override fun onResume() { + super.onResume() + PreferenceUtil.getInstance().registerOnSharedPreferenceChangedListener(this) + } + + override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) { + if (key == PreferenceUtil.PROFILE_IMAGE_PATH) { + recreate() + } + } + + companion object { + const val TAG: String = "SettingsActivity" + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/WhatsNewActivity.kt b/app/src/main/java/code/name/monkey/retromusic/ui/activities/WhatsNewActivity.kt index f70c5e94..d8a6d492 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/WhatsNewActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/WhatsNewActivity.kt @@ -46,7 +46,7 @@ class WhatsNewActivity : AbsBaseActivity() { appBarLayout.setBackgroundColor(ThemeStore.primaryColor(this)) setSupportActionBar(toolbar) title = null - toolbar.setNavigationOnClickListener({ v -> onBackPressed() }) + toolbar.setNavigationOnClickListener { onBackPressed() } whatNewtitle.setTextColor(ThemeStore.textColorPrimary(this)) ToolbarContentTintHelper.colorBackButton(toolbar, ThemeStore.accentColor(this)) diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsBaseActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsBaseActivity.java deleted file mode 100644 index 1c679cff..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsBaseActivity.java +++ /dev/null @@ -1,159 +0,0 @@ -package code.name.monkey.retromusic.ui.activities.base; - -import android.Manifest; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.media.AudioManager; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; -import android.provider.Settings; -import android.view.KeyEvent; -import android.view.View; -import android.view.ViewGroup; - -import com.google.android.material.snackbar.Snackbar; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.app.ActivityCompat; -import androidx.core.view.ViewCompat; -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.retromusic.R; -import uk.co.chrisjenx.calligraphy.CalligraphyContextWrapper; - - -public abstract class AbsBaseActivity extends AbsThemeActivity { - - public static final int PERMISSION_REQUEST = 100; - private boolean hadPermissions; - private String[] permissions; - private String permissionDeniedMessage; - - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setVolumeControlStream(AudioManager.STREAM_MUSIC); - - permissions = getPermissionsToRequest(); - hadPermissions = hasPermissions(); - - setPermissionDeniedMessage(null); - } - - @Override - protected void onPostCreate(@Nullable Bundle savedInstanceState) { - super.onPostCreate(savedInstanceState); - if (!hasPermissions()) { - requestPermissions(); - } - } - - @Override - protected void onResume() { - super.onResume(); - final boolean hasPermissions = hasPermissions(); - if (hasPermissions != hadPermissions) { - hadPermissions = hasPermissions; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - onHasPermissionsChanged(hasPermissions); - } - } - } - - protected void onHasPermissionsChanged(boolean hasPermissions) { - // implemented by sub classes - } - - @Override - public boolean dispatchKeyEvent(@NonNull KeyEvent event) { - if (event.getKeyCode() == KeyEvent.KEYCODE_MENU && event.getAction() == KeyEvent.ACTION_UP) { - showOverflowMenu(); - return true; - } - return super.dispatchKeyEvent(event); - } - - protected void showOverflowMenu() { - - } - - @Override - protected void attachBaseContext(Context newBase) { - super.attachBaseContext(CalligraphyContextWrapper.wrap(newBase)); - } - - @Nullable - protected String[] getPermissionsToRequest() { - return null; - } - - protected View getSnackBarContainer() { - return getWindow().getDecorView(); - } - - private String getPermissionDeniedMessage() { - return permissionDeniedMessage == null ? getString(R.string.permissions_denied) - : permissionDeniedMessage; - } - - protected void setPermissionDeniedMessage(String message) { - permissionDeniedMessage = message; - } - - protected void requestPermissions() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && permissions != null) { - requestPermissions(permissions, PERMISSION_REQUEST); - } - } - - protected boolean hasPermissions() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && permissions != null) { - for (String permission : permissions) { - if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) { - return false; - } - } - } - return true; - } - - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, - @NonNull int[] grantResults) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - if (requestCode == PERMISSION_REQUEST) { - for (int grantResult : grantResults) { - if (grantResult != PackageManager.PERMISSION_GRANTED) { - if (ActivityCompat.shouldShowRequestPermissionRationale(AbsBaseActivity.this, - Manifest.permission.WRITE_EXTERNAL_STORAGE)) { - //User has deny from permission dialog - Snackbar.make(getSnackBarContainer(), getPermissionDeniedMessage(), - Snackbar.LENGTH_INDEFINITE) - .setAction(R.string.action_grant, view -> requestPermissions()) - .setActionTextColor(ThemeStore.accentColor(this)) - .show(); - } else { - // User has deny permission and checked never show permission dialog so you can redirect to Application settings page - Snackbar.make(getSnackBarContainer(), getPermissionDeniedMessage(), - Snackbar.LENGTH_INDEFINITE) - .setAction(R.string.action_settings, view -> { - Intent intent = new Intent(); - intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); - Uri uri = Uri.fromParts("package", AbsBaseActivity.this.getPackageName(), null); - intent.setData(uri); - startActivity(intent); - }) - .setActionTextColor(ThemeStore.accentColor(this)) - .show(); - } - return; - } - } - hadPermissions = true; - onHasPermissionsChanged(true); - } - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsBaseActivity.kt b/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsBaseActivity.kt index 25850c50..5bbafb05 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsBaseActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsBaseActivity.kt @@ -1,7 +1,6 @@ package code.name.monkey.retromusic.ui.activities.base import android.Manifest -import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.media.AudioManager @@ -15,17 +14,16 @@ import androidx.core.app.ActivityCompat import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.retromusic.R import com.google.android.material.snackbar.Snackbar -import uk.co.chrisjenx.calligraphy.CalligraphyContextWrapper abstract class AbsBaseActivity : AbsThemeActivity() { private var hadPermissions: Boolean = false - private var permissions: Array? = null + private lateinit var permissions: Array private var permissionDeniedMessage: String? = null - open fun getPermissionsToRequest(): Array? { - return null + open fun getPermissionsToRequest(): Array { + return arrayOf() } protected fun setPermissionDeniedMessage(message: String) { @@ -82,20 +80,16 @@ abstract class AbsBaseActivity : AbsThemeActivity() { protected fun showOverflowMenu() { } - - override fun attachBaseContext(newBase: Context) { - super.attachBaseContext(CalligraphyContextWrapper.wrap(newBase)) - } - + protected open fun requestPermissions() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - requestPermissions(permissions!!, PERMISSION_REQUEST) + requestPermissions(permissions, PERMISSION_REQUEST) } } protected fun hasPermissions(): Boolean { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - for (permission in permissions!!) { + for (permission in permissions) { if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) { return false } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsMusicServiceActivity.kt b/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsMusicServiceActivity.kt index 086c9786..e22fb801 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsMusicServiceActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsMusicServiceActivity.kt @@ -138,7 +138,7 @@ abstract class AbsMusicServiceActivity : AbsCastActivity(), MusicServiceEventLis } - override fun getPermissionsToRequest(): Array? { + override fun getPermissionsToRequest(): Array { return arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE) } @@ -163,6 +163,6 @@ abstract class AbsMusicServiceActivity : AbsCastActivity(), MusicServiceEventLis } companion object { - val TAG = AbsMusicServiceActivity::class.java.simpleName + val TAG: String = AbsMusicServiceActivity::class.java.simpleName } } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsSlidingMusicPanelActivity.kt b/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsSlidingMusicPanelActivity.kt index d41255e7..6035d437 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsSlidingMusicPanelActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsSlidingMusicPanelActivity.kt @@ -21,6 +21,10 @@ import code.name.monkey.retromusic.ui.fragments.NowPlayingScreen import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerFragment import code.name.monkey.retromusic.ui.fragments.player.adaptive.AdaptiveFragment import code.name.monkey.retromusic.ui.fragments.player.blur.BlurPlayerFragment +import code.name.monkey.retromusic.ui.fragments.player.card.CardFragment +import code.name.monkey.retromusic.ui.fragments.player.cardblur.CardBlurFragment +import code.name.monkey.retromusic.ui.fragments.player.fit.FitFragment +import code.name.monkey.retromusic.ui.fragments.player.normal.PlayerFragment import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.views.BottomNavigationBarTinted import com.google.android.material.bottomnavigation.BottomNavigationView @@ -58,7 +62,7 @@ abstract class AbsSlidingMusicPanelActivity protected constructor() : AbsMusicSe slidingUpPanelLayout = findViewById(R.id.sliding_layout); bottomNavigationView = findViewById(R.id.bottom_navigation); - choosFragmentForTheme() + chooseFragmentForTheme() setupSlidingUpPanel() } @@ -207,22 +211,23 @@ abstract class AbsSlidingMusicPanelActivity protected constructor() : AbsMusicSe miniPlayerFragment!!.view!!.visibility = if (alpha == 0f) View.GONE else View.VISIBLE } - private fun choosFragmentForTheme() { + private fun chooseFragmentForTheme() { currentNowPlayingScreen = PreferenceUtil.getInstance().nowPlayingScreen - val fragment: Fragment // must implement AbsPlayerFragment - when (currentNowPlayingScreen) { - NowPlayingScreen.BLUR -> fragment = BlurPlayerFragment() - NowPlayingScreen.ADAPTIVE -> fragment = AdaptiveFragment() - else -> fragment = AdaptiveFragment() - } + val fragment: Fragment = when (currentNowPlayingScreen) { + NowPlayingScreen.BLUR -> BlurPlayerFragment() + NowPlayingScreen.ADAPTIVE -> AdaptiveFragment() + NowPlayingScreen.NORMAL -> PlayerFragment() + NowPlayingScreen.CARD -> CardFragment() + NowPlayingScreen.BLUR_CARD -> CardBlurFragment() + NowPlayingScreen.FIT -> FitFragment() + else -> PlayerFragment() + } // must implement AbsPlayerFragment supportFragmentManager.beginTransaction().replace(R.id.player_fragment_container, fragment).commit() supportFragmentManager.executePendingTransactions() - playerFragment = supportFragmentManager.findFragmentById(R.id.player_fragment_container) as AbsPlayerFragment? - miniPlayerFragment = supportFragmentManager.findFragmentById(R.id.mini_player_fragment) as MiniPlayerFragment? - - + playerFragment = supportFragmentManager.findFragmentById(R.id.player_fragment_container) as AbsPlayerFragment + miniPlayerFragment = supportFragmentManager.findFragmentById(R.id.mini_player_fragment) as MiniPlayerFragment miniPlayerFragment!!.view!!.setOnClickListener { expandPanel() } } @@ -296,6 +301,6 @@ abstract class AbsSlidingMusicPanelActivity protected constructor() : AbsMusicSe companion object { - val TAG = AbsSlidingMusicPanelActivity::class.java.simpleName + val TAG: String = AbsSlidingMusicPanelActivity::class.java.simpleName } } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsThemeActivity.kt b/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsThemeActivity.kt index a88a03e6..fb99ef1b 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsThemeActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsThemeActivity.kt @@ -96,15 +96,16 @@ abstract class AbsThemeActivity : ATHActivity(), Runnable { if (VersionUtils.hasKitKat()) { val statusBar = window.decorView.rootView.findViewById(R.id.status_bar) if (statusBar != null) { - if (VersionUtils.hasMarshmallow()) { - window.statusBarColor = color - } else if (VersionUtils.hasLollipop()) { - statusBar.setBackgroundColor(ColorUtil.darkenColor(color)) - } else { - statusBar.setBackgroundColor(color) + when { + VersionUtils.hasMarshmallow() -> window.statusBarColor = color + VersionUtils.hasLollipop() -> statusBar.setBackgroundColor(ColorUtil.darkenColor(color)) + else -> statusBar.setBackgroundColor(color) } } else if (Build.VERSION.SDK_INT >= 21) { - window.statusBarColor = ColorUtil.darkenColor(color) + when { + VersionUtils.hasMarshmallow() -> window.statusBarColor = color + else -> window.statusBarColor = ColorUtil.darkenColor(color) + } } } setLightStatusbarAuto(color) diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/CollageSongAdapter.java b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/CollageSongAdapter.java index 4e955f2c..166f7f65 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/CollageSongAdapter.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/CollageSongAdapter.java @@ -70,8 +70,7 @@ public class CollageSongAdapter extends RecyclerView.Adapter imageViews; @@ -80,14 +79,12 @@ public class CollageSongAdapter extends RecyclerView.Adapter { - MusicPlayerRemote.INSTANCE.openQueue(dataSet, 0, true); - }); - view.setBackgroundColor(color); - view.setTextColor(MaterialValueHelper.getPrimaryTextColor(context, ColorUtil.isColorLight(color))); + //ButterKnife.bind(this, itemView); + //Context context = itemView.getContext(); + //int color = ThemeStore.accentColor(context); + //view.setOnClickListener(v -> MusicPlayerRemote.INSTANCE.openQueue(dataSet, 0, true)); + //view.setBackgroundColor(color); + //view.setTextColor(MaterialValueHelper.getPrimaryTextColor(context, ColorUtil.isColorLight(color))); } void bindSongs() { diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/AbsOffsetSongAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/AbsOffsetSongAdapter.kt index 4329ea2b..92e25f58 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/AbsOffsetSongAdapter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/AbsOffsetSongAdapter.kt @@ -61,7 +61,7 @@ abstract class AbsOffsetSongAdapter : SongAdapter { override// could also return null, just to be safe return empty song val song: Song - get() = if (itemViewType == OFFSET_ITEM) Song.EMPTY_SONG else dataSet[adapterPosition - 1] + get() = if (itemViewType == OFFSET_ITEM) Song.emptySong else dataSet[adapterPosition - 1] override fun onClick(v: View?) { if (isInQuickSelectMode && itemViewType != OFFSET_ITEM) { diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/OrderablePlaylistSongAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/OrderablePlaylistSongAdapter.kt index a55a548f..8889c516 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/OrderablePlaylistSongAdapter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/OrderablePlaylistSongAdapter.kt @@ -36,7 +36,7 @@ class OrderablePlaylistSongAdapter(activity: AppCompatActivity, positionFinal-- var long: Long = 0 - if (position < 0) { + if (positionFinal < 0) { long = -2 } else { if (dataSet[positionFinal] is PlaylistSong) { diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/MiniPlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/MiniPlayerFragment.kt index d627fd68..34d90346 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/MiniPlayerFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/MiniPlayerFragment.kt @@ -10,7 +10,6 @@ import android.text.SpannableStringBuilder import android.text.style.ForegroundColorSpan import android.view.* import android.view.animation.DecelerateInterpolator -import butterknife.OnClick import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.retromusic.R import code.name.monkey.retromusic.helper.MusicPlayerRemote @@ -76,7 +75,7 @@ open class MiniPlayerFragment : AbsMusicServiceFragment(), MusicProgressViewUpda private fun updateSongTitle() { val builder = SpannableStringBuilder() - val song = MusicPlayerRemote.currentSong ?: return + val song = MusicPlayerRemote.currentSong val title = SpannableString(song.title) title.setSpan(ForegroundColorSpan(ThemeStore.textColorPrimary(context!!)), 0, title.length, 0) diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/NowPlayingScreen.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/NowPlayingScreen.java index 59f4288e..5b74e577 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/NowPlayingScreen.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/NowPlayingScreen.java @@ -6,9 +6,19 @@ import code.name.monkey.retromusic.R; public enum NowPlayingScreen { - + NORMAL(R.string.normal, R.drawable.np_normal, 0), + FLAT(R.string.flat, R.drawable.np_flat, 1), + //FULL(R.string.full, R.drawable.np_full, 2), + //PLAIN(R.string.plain, R.drawable.np_plain, 3), + BLUR(R.string.blur, R.drawable.np_blur, 4), + //COLOR(R.string.color, R.drawable.np_color, 5), + CARD(R.string.card, R.drawable.np_card, 6), + //TINY(R.string.tiny, R.drawable.np_tiny, 7), + //SIMPLE(R.string.simple, R.drawable.np_simple, 8), + BLUR_CARD(R.string.blur_card, R.drawable.np_blur_card, 9), ADAPTIVE(R.string.adaptive, R.drawable.np_adaptive, 10), - BLUR(R.string.blur, R.drawable.np_blur, 4); + //MATERIAL(R.string.material, R.drawable.np_material, 11), + FIT(R.string.fit, R.drawable.np_adaptive, 12); @StringRes public final int titleRes; diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/PlayingQueueFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/PlayingQueueFragment.java deleted file mode 100644 index 6f6865d7..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/PlayingQueueFragment.java +++ /dev/null @@ -1,148 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments; - -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import com.h6ah4i.android.widget.advrecyclerview.animator.GeneralItemAnimator; -import com.h6ah4i.android.widget.advrecyclerview.animator.RefactoredDefaultItemAnimator; -import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager; -import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.Unbinder; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.ui.adapter.song.PlayingQueueAdapter; -import code.name.monkey.retromusic.ui.fragments.base.AbsMusicServiceFragment; -import code.name.monkey.retromusic.views.CollapsingFAB; - -public class PlayingQueueFragment extends AbsMusicServiceFragment { - - @BindView(R.id.recycler_view) - RecyclerView mRecyclerView; - - - Unbinder unbinder; - - private RecyclerView.Adapter mWrappedAdapter; - private RecyclerViewDragDropManager mRecyclerViewDragDropManager; - private PlayingQueueAdapter mPlayingQueueAdapter; - private LinearLayoutManager mLayoutManager; - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_main_activity_recycler_view, container, false); - unbinder = ButterKnife.bind(this, view); - return view; - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - setUpRecyclerView(); - } - - private void setUpRecyclerView() { - mRecyclerViewDragDropManager = new RecyclerViewDragDropManager(); - final GeneralItemAnimator animator = new RefactoredDefaultItemAnimator(); - - mPlayingQueueAdapter = new PlayingQueueAdapter( - (AppCompatActivity) getActivity(), - MusicPlayerRemote.INSTANCE.getPlayingQueue(), - MusicPlayerRemote.INSTANCE.getPosition(), - R.layout.item_queue); - mWrappedAdapter = mRecyclerViewDragDropManager.createWrappedAdapter(mPlayingQueueAdapter); - - mLayoutManager = new LinearLayoutManager(getContext()); - - mRecyclerView.setLayoutManager(mLayoutManager); - mRecyclerView.setAdapter(mWrappedAdapter); - mRecyclerView.setItemAnimator(animator); - mRecyclerViewDragDropManager.attachRecyclerView(mRecyclerView); - mLayoutManager.scrollToPositionWithOffset(MusicPlayerRemote.INSTANCE.getPosition() + 1, 0); - - } - - @Override - public void onQueueChanged() { - updateQueue(); - updateCurrentSong(); - } - - @Override - public void onMediaStoreChanged() { - updateQueue(); - updateCurrentSong(); - } - - @SuppressWarnings("ConstantConditions") - private void updateCurrentSong() { - } - - @Override - public void onPlayingMetaChanged() { - //updateCurrentSong(); - //updateIsFavorite(); - updateQueuePosition(); - //updateLyrics(); - } - - private void updateQueuePosition() { - mPlayingQueueAdapter.setCurrent(MusicPlayerRemote.INSTANCE.getPosition()); - // if (slidingUpPanelLayout.getPanelState() == SlidingUpPanelLayout.PanelState.COLLAPSED) { - resetToCurrentPosition(); - //} - } - - private void updateQueue() { - mPlayingQueueAdapter - .swapDataSet(MusicPlayerRemote.INSTANCE.getPlayingQueue(), MusicPlayerRemote.INSTANCE.getPosition()); - resetToCurrentPosition(); - } - - private void resetToCurrentPosition() { - mRecyclerView.stopScroll(); - mLayoutManager.scrollToPositionWithOffset(MusicPlayerRemote.INSTANCE.getPosition() + 1, 0); - } - - @Override - public void onPause() { - if (mRecyclerViewDragDropManager != null) { - mRecyclerViewDragDropManager.cancelDrag(); - } - super.onPause(); - } - - @Override - public void onDestroyView() { - if (mRecyclerViewDragDropManager != null) { - mRecyclerViewDragDropManager.release(); - mRecyclerViewDragDropManager = null; - } - - if (mRecyclerView != null) { - mRecyclerView.setItemAnimator(null); - mRecyclerView.setAdapter(null); - mRecyclerView = null; - } - - if (mWrappedAdapter != null) { - WrapperAdapterUtils.releaseAll(mWrappedAdapter); - mWrappedAdapter = null; - } - mPlayingQueueAdapter = null; - mLayoutManager = null; - super.onDestroyView(); - unbinder.unbind(); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/VolumeFragment.kt b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/VolumeFragment.kt index 3fb07eea..5202281e 100755 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/VolumeFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/VolumeFragment.kt @@ -47,19 +47,19 @@ class VolumeFragment : Fragment(), SeekBar.OnSeekBarChangeListener, OnAudioVolum val audioManager = audioManager if (audioManager != null) { - volumeSeekBar!!.max = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC) - volumeSeekBar!!.progress = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) + volumeSeekBar.max = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC) + volumeSeekBar.progress = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) } - volumeSeekBar!!.setOnSeekBarChangeListener(this) + volumeSeekBar.setOnSeekBarChangeListener(this) } override fun onAudioVolumeChanged(currentVolume: Int, maxVolume: Int) { if (volumeSeekBar == null) { return } - volumeSeekBar!!.max = maxVolume - volumeSeekBar!!.progress = currentVolume - volumeDown!!.setImageResource(if (currentVolume == 0) R.drawable.ic_volume_off_white_24dp else R.drawable.ic_volume_down_white_24dp) + volumeSeekBar.max = maxVolume + volumeSeekBar.progress = currentVolume + volumeDown.setImageResource(if (currentVolume == 0) R.drawable.ic_volume_off_white_24dp else R.drawable.ic_volume_down_white_24dp) } override fun onDestroyView() { @@ -98,9 +98,9 @@ class VolumeFragment : Fragment(), SeekBar.OnSeekBarChangeListener, OnAudioVolum } private fun setProgressBarColor(newColor: Int) { - TintHelper.setTintAuto(volumeSeekBar!!, newColor, false) - volumeDown!!.setColorFilter(newColor, PorterDuff.Mode.SRC_IN) - volumeUp!!.setColorFilter(newColor, PorterDuff.Mode.SRC_IN) + TintHelper.setTintAuto(volumeSeekBar, newColor, false) + volumeDown.setColorFilter(newColor, PorterDuff.Mode.SRC_IN) + volumeUp.setColorFilter(newColor, PorterDuff.Mode.SRC_IN) } private fun setTintable(color: Int) { @@ -108,7 +108,7 @@ class VolumeFragment : Fragment(), SeekBar.OnSeekBarChangeListener, OnAudioVolum } fun removeThumb() { - volumeSeekBar!!.thumb = null + volumeSeekBar.thumb = null } private fun setPauseWhenZeroVolume(pauseWhenZeroVolume: Boolean) { diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsPlayerControlsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsPlayerControlsFragment.kt index efe3631c..239e3103 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsPlayerControlsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsPlayerControlsFragment.kt @@ -55,7 +55,7 @@ abstract class AbsPlayerControlsFragment : AbsMusicServiceFragment(), MusicProgr } - var prevButton: ImageButton? = null + /* var prevButton: ImageButton? = null var nextButton: ImageButton? = null var repeatButton: ImageButton? = null var shuffleButton: ImageButton? = null @@ -63,11 +63,12 @@ abstract class AbsPlayerControlsFragment : AbsMusicServiceFragment(), MusicProgr var songTotalTime: TextView? = null var songCurrentProgress: TextView? = null var volumeContainer: View? = null - var playPauseFab: ImageButton? = null + var playPauseFab: ImageButton? = null*/ override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - playPauseFab = view.findViewById(R.id.player_play_pause_button) + + /* playPauseFab = view.findViewById(R.id.player_play_pause_button) prevButton = view.findViewById(R.id.player_prev_button) nextButton = view.findViewById(R.id.player_next_button) repeatButton = view.findViewById(R.id.player_repeat_button) @@ -75,6 +76,6 @@ abstract class AbsPlayerControlsFragment : AbsMusicServiceFragment(), MusicProgr progressSlider = view.findViewById(R.id.player_progress_slider) songTotalTime = view.findViewById(R.id.player_song_total_time) songCurrentProgress = view.findViewById(R.id.player_song_current_progress) - volumeContainer = view.findViewById(R.id.volume_fragment_container) + volumeContainer = view.findViewById(R.id.volume_fragment_container)*/ } } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsPlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsPlayerFragment.kt index 4eff06f4..80286a76 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsPlayerFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsPlayerFragment.kt @@ -32,8 +32,6 @@ abstract class AbsPlayerFragment : AbsMusicServiceFragment(), Toolbar.OnMenuItem private set private var updateIsFavoriteTask: AsyncTask<*, *, *>? = null - protected var toolbar: Toolbar? = null - override fun onAttach(context: Context?) { super.onAttach(context) @@ -146,6 +144,8 @@ abstract class AbsPlayerFragment : AbsMusicServiceFragment(), Toolbar.OnMenuItem MusicUtil.toggleFavorite(activity!!, song) } + abstract fun toolbarGet(): Toolbar + abstract fun onShow() abstract fun onHide() @@ -193,7 +193,7 @@ abstract class AbsPlayerFragment : AbsMusicServiceFragment(), Toolbar.OnMenuItem else R.drawable.ic_favorite_border_white_24dp val drawable = RetroUtil.getTintedVectorDrawable(activity, res, toolbarIconColor()) - //toolbar!!.menu.findItem(R.id.action_toggle_favorite).setIcon(drawable).title = if (isFavorite) getString(R.string.action_remove_from_favorites) else getString(R.string.action_add_to_favorites) + toolbarGet().menu.findItem(R.id.action_toggle_favorite).setIcon(drawable).title = if (isFavorite) getString(R.string.action_remove_from_favorites) else getString(R.string.action_add_to_favorites) } } }.execute(MusicPlayerRemote.currentSong) diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/GenreFragment.kt b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/GenreFragment.kt index 84054572..519cdc3f 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/GenreFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/GenreFragment.kt @@ -1,12 +1,9 @@ package code.name.monkey.retromusic.ui.fragments.mainactivity import android.os.Bundle -import androidx.recyclerview.widget.LinearLayoutManager import android.view.Menu import android.view.MenuInflater - -import java.util.ArrayList - +import androidx.recyclerview.widget.LinearLayoutManager import code.name.monkey.retromusic.R import code.name.monkey.retromusic.model.Genre import code.name.monkey.retromusic.mvp.contract.GenreContract @@ -14,6 +11,7 @@ import code.name.monkey.retromusic.mvp.presenter.GenrePresenter import code.name.monkey.retromusic.ui.adapter.GenreAdapter import code.name.monkey.retromusic.ui.fragments.base.AbsLibraryPagerRecyclerViewFragment import code.name.monkey.retromusic.util.PreferenceUtil +import java.util.* class GenreFragment : AbsLibraryPagerRecyclerViewFragment(), GenreContract.GenreView { diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/LibraryFragment.kt b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/LibraryFragment.kt index a9d4b4e0..9ea0ad93 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/LibraryFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/LibraryFragment.kt @@ -7,7 +7,6 @@ import android.widget.TextView import androidx.annotation.StringRes import androidx.appcompat.widget.Toolbar import androidx.fragment.app.Fragment -import butterknife.Unbinder import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.common.ATHToolbarActivity import code.name.monkey.appthemehelper.util.ATHUtil @@ -109,10 +108,7 @@ class LibraryFragment : AbsMainActivityFragment(), CabHolder, MainActivityFragme appbar.addOnOffsetChangedListener(this) mainActivity.title = null mainActivity.setSupportActionBar(toolbar) - - toolbar.setNavigationOnClickListener { NavigationUtil.goToSearch(mainActivity) } - toolbar.setOnClickListener { showMainMenu() } - toolbar.navigationIcon = RetroUtil.getTintedDrawable(mainActivity, R.drawable.ic_search_white_24dp, ThemeStore.textColorPrimary(mainActivity)) + toolbar.navigationIcon = RetroUtil.getTintedDrawable(mainActivity, R.drawable.ic_menu_white_24dp, ThemeStore.textColorPrimary(mainActivity)) } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/folders/FoldersFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/folders/FoldersFragment.java index 1fd8b121..5ab19f72 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/folders/FoldersFragment.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/folders/FoldersFragment.java @@ -40,7 +40,6 @@ import androidx.loader.app.LoaderManager; import androidx.loader.content.Loader; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import butterknife.BindView; import butterknife.ButterKnife; import butterknife.Unbinder; import code.name.monkey.appthemehelper.ThemeStore; @@ -74,8 +73,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements AppBarLayout.OnOffsetChangedListener, LoaderManager.LoaderCallbacks> { public static final String TAG = FoldersFragment.class.getSimpleName(); - public static final FileFilter AUDIO_FILE_FILTER = file -> !file.isHidden() && (file.isDirectory() - || + public static final FileFilter AUDIO_FILE_FILTER = file -> !file.isHidden() && (file.isDirectory() || FileUtil.fileIsMimeType(file, "audio/*", MimeTypeMap.getSingleton()) || FileUtil.fileIsMimeType(file, "application/opus", MimeTypeMap.getSingleton()) || FileUtil.fileIsMimeType(file, "application/ogg", MimeTypeMap.getSingleton())); @@ -83,30 +81,18 @@ public class FoldersFragment extends AbsMainActivityFragment implements private static final String PATH = "path"; private static final String CRUMBS = "crumbs"; private static final int LOADER_ID = LoaderIds.Companion.getFOLDERS_FRAGMENT(); - @BindView(R.id.coordinator_layout) - View coordinatorLayout; - @BindView(R.id.container) - View container; + private View coordinatorLayout, container, empty; - @BindView(R.id.title) - TextView title; + private TextView title; - @BindView(android.R.id.empty) - View empty; + private Toolbar toolbar; - @BindView(R.id.toolbar) - Toolbar toolbar; + private BreadCrumbLayout breadCrumbs; - @BindView(R.id.bread_crumbs) - BreadCrumbLayout breadCrumbs; - - @BindView(R.id.app_bar) - AppBarLayout appbar; - - @BindView(R.id.recycler_view) - FastScrollRecyclerView recyclerView; + private AppBarLayout appBarLayout; + private FastScrollRecyclerView recyclerView; private Comparator fileComparator = (lhs, rhs) -> { if (lhs.isDirectory() && !rhs.isDirectory()) { @@ -118,10 +104,8 @@ public class FoldersFragment extends AbsMainActivityFragment implements (rhs.getName()); } }; - private Unbinder unbinder; private SongFileAdapter adapter; private MaterialCab cab; - public FoldersFragment() { } @@ -162,6 +146,17 @@ public class FoldersFragment extends AbsMainActivityFragment implements } } + private void initViews(View view) { + coordinatorLayout = view.findViewById(R.id.coordinatorLayout); + recyclerView = view.findViewById(R.id.recyclerView); + appBarLayout = view.findViewById(R.id.appBarLayout); + breadCrumbs = view.findViewById(R.id.breadCrumbs); + toolbar = view.findViewById(R.id.toolbar); + empty = view.findViewById(android.R.id.empty); + title = view.findViewById(R.id.bannerTitle); + container = view.findViewById(R.id.container); + } + private void setCrumb(BreadCrumbLayout.Crumb crumb, boolean addToHistory) { if (crumb == null) { return; @@ -215,15 +210,13 @@ public class FoldersFragment extends AbsMainActivityFragment implements public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_folder, container, false); - unbinder = ButterKnife.bind(this, view); + initViews(view); return view; } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { setStatusbarColorAuto(view); - //getMainActivity().getSlidingUpPanelLayout().setShadowHeight(0); - setUpAppbarColor(); setUpBreadCrumbs(); setUpRecyclerView(); @@ -238,18 +231,17 @@ public class FoldersFragment extends AbsMainActivityFragment implements int primaryColor = ThemeStore.primaryColor(getContext()); - toolbar.setNavigationIcon(R.drawable.ic_keyboard_backspace_black_24dp); + toolbar.setNavigationIcon(R.drawable.ic_menu_white_24dp); //noinspection ConstantConditions getActivity().setTitle(null); getMainActivity().setSupportActionBar(toolbar); - toolbar.setNavigationOnClickListener(v -> getActivity().onBackPressed()); TintHelper.setTintAuto(container, primaryColor, true); - appbar.setBackgroundColor(primaryColor); + appBarLayout.setBackgroundColor(primaryColor); toolbar.setBackgroundColor(primaryColor); breadCrumbs.setActivatedContentColor(ToolbarContentTintHelper.toolbarTitleColor(getActivity(), ColorUtil.darkenColor(primaryColor))); breadCrumbs.setDeactivatedContentColor(ToolbarContentTintHelper.toolbarSubtitleColor(getActivity(), ColorUtil.darkenColor(primaryColor))); - appbar.addOnOffsetChangedListener((appBarLayout, verticalOffset) -> getMainActivity().setLightStatusbar(!ATHUtil.isWindowBackgroundDark(getContext()))); + appBarLayout.addOnOffsetChangedListener((appBarLayout, verticalOffset) -> getMainActivity().setLightStatusbar(!ATHUtil.isWindowBackgroundDark(getContext()))); } private void setUpBreadCrumbs() { @@ -261,7 +253,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements ViewUtil.setUpFastScrollRecyclerViewColor(getActivity(), recyclerView, ThemeStore.accentColor(getActivity())); recyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); - appbar.addOnOffsetChangedListener(this); + appBarLayout.addOnOffsetChangedListener(this); } private void setUpAdapter() { @@ -286,8 +278,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements @Override public void onDestroyView() { - appbar.removeOnOffsetChangedListener(this); - unbinder.unbind(); + appBarLayout.removeOnOffsetChangedListener(this); super.onDestroyView(); } @@ -473,7 +464,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements @Override public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) { container.setPadding(container.getPaddingLeft(), container.getPaddingTop(), - container.getPaddingRight(), appbar.getTotalScrollRange() + verticalOffset); + container.getPaddingRight(), this.appBarLayout.getTotalScrollRange() + verticalOffset); } private void checkIsEmpty() { diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/home/BannerHomeFragment.kt b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/home/BannerHomeFragment.kt index 4b56b5b4..273ab2a9 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/home/BannerHomeFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/home/BannerHomeFragment.kt @@ -1,18 +1,19 @@ package code.name.monkey.retromusic.ui.fragments.mainactivity.home import android.graphics.Bitmap +import android.graphics.Color import android.os.Bundle import android.util.DisplayMetrics import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.ImageView +import androidx.appcompat.widget.Toolbar import androidx.core.content.ContextCompat import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import butterknife.OnClick import code.name.monkey.appthemehelper.ThemeStore +import code.name.monkey.appthemehelper.util.TintHelper import code.name.monkey.retromusic.Constants.USER_BANNER import code.name.monkey.retromusic.Constants.USER_PROFILE import code.name.monkey.retromusic.R @@ -34,80 +35,37 @@ import code.name.monkey.retromusic.util.Compressor import code.name.monkey.retromusic.util.NavigationUtil import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.RetroUtil -import code.name.monkey.retromusic.views.CircularImageView -import code.name.monkey.retromusic.views.MetalRecyclerViewPager import com.bumptech.glide.Glide import com.bumptech.glide.load.engine.DiskCacheStrategy import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable import io.reactivex.schedulers.Schedulers -import kotlinx.android.synthetic.main.abs_playlists.* +import kotlinx.android.synthetic.main.fragment_banner_home.* +import kotlinx.android.synthetic.main.home_section_content.* import java.io.File import java.util.* class BannerHomeFragment : AbsMainActivityFragment(), MainActivityFragmentCallbacks, HomeContract.HomeView { + val disposable: CompositeDisposable = CompositeDisposable() + private lateinit var homePresenter: HomePresenter + private lateinit var contentContainerView: View + private lateinit var lastAdded: View + private lateinit var topPlayed: View + private lateinit var actionShuffle: View + private lateinit var history: View + private lateinit var userImage: ImageView + private lateinit var toolbar: Toolbar - override fun onCreateView(inflater: LayoutInflater, viewGroup: ViewGroup?, - savedInstanceState: Bundle?): View? { - val view = inflater.inflate(if (PreferenceUtil.getInstance().isHomeBanner) R.layout.fragment_banner_home else R.layout.fragment_home, viewGroup, false) - - if (!PreferenceUtil.getInstance().isHomeBanner) - setStatusbarColorAuto(view) - - imageView = view.findViewById(R.id.image) - userImage = view.findViewById(R.id.user_image) - recentArtistRV = view.findViewById(R.id.recycler_view) - recentAlbumRV = view.findViewById(R.id.recent_album) - topArtistRV = view.findViewById(R.id.top_artist) - topAlbumRV = view.findViewById(R.id.top_album) - recentArtistContainer = view.findViewById(R.id.recent_artist_container) - recentAlbumsContainer = view.findViewById(R.id.recent_albums_container) - topArtistContainer = view.findViewById(R.id.top_artist_container) - topAlbumContainer = view.findViewById(R.id.top_albums_container) - genresRecyclerView = view.findViewById(R.id.genres) - genreContainer = view.findViewById(R.id.genre_container) - contentContainer = view.findViewById(R.id.content_container) - container = view.findViewById(R.id.container) - suggestionsSongs = view.findViewById(R.id.suggestion_songs) - suggestionsContainer = view.findViewById(R.id.suggestion_container) - - /* lastAdded.setOnClickListener { - NavigationUtil.goToPlaylistNew(activity!!, LastAddedPlaylist(activity!!)) - } - topPlayed.setOnClickListener { - NavigationUtil.goToPlaylistNew(activity!!, MyTopTracksPlaylist(activity!!)) - } - actionShuffle.setOnClickListener { - MusicPlayerRemote.openAndShuffleQueue(SongLoader.getAllSongs(activity!!).blockingFirst(), true) - } - history.setOnClickListener { - NavigationUtil.goToPlaylistNew(activity!!, HistoryPlaylist(activity!!)) - }*/ - userImage.setOnClickListener { - NavigationUtil.goToUserInfo(activity!!) - } - return view + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + mainActivity.setBottomBarVisibility(View.GONE) + } + + override fun onCreateView(inflater: LayoutInflater, viewGroup: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(if (PreferenceUtil.getInstance().isHomeBanner) R.layout.fragment_banner_home else R.layout.fragment_home, viewGroup, false) } - private var imageView: ImageView? = null - private lateinit var userImage: CircularImageView - private lateinit var recentAlbumRV: MetalRecyclerViewPager - private lateinit var topAlbumRV: MetalRecyclerViewPager - private lateinit var topArtistRV: RecyclerView - private lateinit var genresRecyclerView: RecyclerView - private lateinit var suggestionsSongs: RecyclerView - private lateinit var recentArtistRV: RecyclerView - private lateinit var recentArtistContainer: View - private lateinit var recentAlbumsContainer: View - private lateinit var topArtistContainer: View - private lateinit var topAlbumContainer: View - private lateinit var genreContainer: View - private lateinit var container: View - private lateinit var contentContainer: View - private lateinit var suggestionsContainer: View - private lateinit var homePresenter: HomePresenter - val disposable: CompositeDisposable = CompositeDisposable() private val displayMetrics: DisplayMetrics get() { @@ -122,16 +80,12 @@ class BannerHomeFragment : AbsMainActivityFragment(), MainActivityFragmentCallba val timeOfDay = c.get(Calendar.HOUR_OF_DAY) var images = arrayOf() - if (timeOfDay in 0..5) { - images = resources.getStringArray(R.array.night) - } else if (timeOfDay in 6..11) { - images = resources.getStringArray(R.array.morning) - } else if (timeOfDay in 12..15) { - images = resources.getStringArray(R.array.after_noon) - } else if (timeOfDay in 16..19) { - images = resources.getStringArray(R.array.evening) - } else if (timeOfDay in 20..23) { - images = resources.getStringArray(R.array.night) + when (timeOfDay) { + in 0..5 -> images = resources.getStringArray(R.array.night) + in 6..11 -> images = resources.getStringArray(R.array.morning) + in 12..15 -> images = resources.getStringArray(R.array.after_noon) + in 16..19 -> images = resources.getStringArray(R.array.evening) + in 20..23 -> images = resources.getStringArray(R.array.night) } val day = images[Random().nextInt(images.size)] @@ -141,47 +95,39 @@ class BannerHomeFragment : AbsMainActivityFragment(), MainActivityFragmentCallba private fun loadTimeImage(day: String) { - if (imageView != null) { + if (bannerImage != null) { if (PreferenceUtil.getInstance().bannerImage.isEmpty()) { Glide.with(activity).load(day) .asBitmap() .placeholder(R.drawable.material_design_default) .diskCacheStrategy(DiskCacheStrategy.SOURCE) - .into(imageView!!) + .into(bannerImage!!) } else { - loadBannerFromStorage() + + disposable.add(Compressor(context!!) + .setQuality(100) + .setCompressFormat(Bitmap.CompressFormat.WEBP) + .compressToBitmapAsFlowable(File(PreferenceUtil.getInstance().bannerImage, USER_BANNER)) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { bannerImage!!.setImageBitmap(it) }) } } } - private fun loadBannerFromStorage() { - - disposable.add(Compressor(context!!) - .setQuality(100) - .setCompressFormat(Bitmap.CompressFormat.WEBP) - .compressToBitmapAsFlowable( - File(PreferenceUtil.getInstance().bannerImage, USER_BANNER)) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe { imageView!!.setImageBitmap(it) }) - } - private fun loadImageFromStorage(imageView: ImageView) { - disposable.add(Compressor(context!!) .setMaxHeight(300) .setMaxWidth(300) .setQuality(75) .setCompressFormat(Bitmap.CompressFormat.WEBP) - .compressToBitmapAsFlowable( - File(PreferenceUtil.getInstance().profileImage, USER_PROFILE)) + .compressToBitmapAsFlowable(File(PreferenceUtil.getInstance().profileImage, USER_PROFILE)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe({ imageView.setImageBitmap(it) }) { - imageView.setImageDrawable(ContextCompat - .getDrawable(context!!, R.drawable.ic_person_flat)) + imageView.setImageDrawable(ContextCompat.getDrawable(context!!, R.drawable.ic_person_flat)) }) } @@ -193,20 +139,55 @@ class BannerHomeFragment : AbsMainActivityFragment(), MainActivityFragmentCallba override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + mainActivity.setBottomBarVisibility(View.GONE) + + toolbar = view.findViewById(R.id.toolbar) + + if (!PreferenceUtil.getInstance().isHomeBanner) + setStatusbarColorAuto(view) + + lastAdded = view.findViewById(R.id.lastAdded) + lastAdded.setOnClickListener { + NavigationUtil.goToPlaylistNew(activity!!, LastAddedPlaylist(activity!!)) + } + + topPlayed = view.findViewById(R.id.topPlayed) + topPlayed.setOnClickListener { + NavigationUtil.goToPlaylistNew(activity!!, MyTopTracksPlaylist(activity!!)) + } + + actionShuffle = view.findViewById(R.id.actionShuffle) + actionShuffle.setOnClickListener { + MusicPlayerRemote.openAndShuffleQueue(SongLoader.getAllSongs(activity!!).blockingFirst(), true) + } + + history = view.findViewById(R.id.history) + history.setOnClickListener { + NavigationUtil.goToPlaylistNew(activity!!, HistoryPlaylist(activity!!)) + } + + userImage = view.findViewById(R.id.userImage) + userImage.setOnClickListener { showMainMenu() } + + homePresenter = HomePresenter(this) + + contentContainerView = view.findViewById(R.id.contentContainer) + contentContainerView.setBackgroundColor(ThemeStore.primaryColor(context!!)) + + //bannerTitle.setTextColor(ThemeStore.textColorPrimary(context!!)) + setupToolbar() - loadImageFromStorage(userImage) homePresenter.subscribe() + + loadImageFromStorage(userImage) getTimeOfTheDay() } private fun setupToolbar() { - userImage.setOnClickListener { showMainMenu() } - contentContainer.setBackgroundColor(ThemeStore.primaryColor(mainActivity)) - } - - @OnClick(R.id.searchIcon) - internal fun search() { - NavigationUtil.goToSearch(mainActivity) + toolbar.navigationIcon = TintHelper.createTintedDrawable(ContextCompat.getDrawable(context!!, R.drawable.ic_menu_white_24dp), ThemeStore.textColorPrimary(context!!)) + mainActivity.title = null + mainActivity.setSupportActionBar(toolbar) + toolbar.setBackgroundColor(Color.TRANSPARENT) } override fun handleBackPress(): Boolean { @@ -237,41 +218,46 @@ class BannerHomeFragment : AbsMainActivityFragment(), MainActivityFragmentCallba override fun recentArtist(artists: ArrayList) { recentArtistContainer.visibility = View.VISIBLE - recentArtistRV.layoutManager = GridLayoutManager(mainActivity, 1, GridLayoutManager.HORIZONTAL, false) - val artistAdapter = ArtistAdapter(mainActivity, artists, PreferenceUtil.getInstance().getHomeGridStyle(context!!), false, null) - recentArtistRV.adapter = artistAdapter + recentArtist.apply { + val artistAdapter = ArtistAdapter(mainActivity, artists, PreferenceUtil.getInstance().getHomeGridStyle(context!!), false, null) + layoutManager = GridLayoutManager(mainActivity, 1, GridLayoutManager.HORIZONTAL, false) + adapter = artistAdapter + } } override fun recentAlbum(albums: ArrayList) { recentAlbumsContainer.visibility = View.VISIBLE - val artistAdapter = AlbumFullWithAdapter(mainActivity, - displayMetrics) + val artistAdapter = AlbumFullWithAdapter(mainActivity, displayMetrics) artistAdapter.swapData(albums) - recentAlbumRV.adapter = artistAdapter + recentAlbum.adapter = artistAdapter } override fun topArtists(artists: ArrayList) { topArtistContainer.visibility = View.VISIBLE - topArtistRV.layoutManager = GridLayoutManager(mainActivity, 1, GridLayoutManager.HORIZONTAL, false) - val artistAdapter = ArtistAdapter(mainActivity, artists, PreferenceUtil.getInstance().getHomeGridStyle(context!!), false, null) - topArtistRV.adapter = artistAdapter + topArtist.apply { + layoutManager = GridLayoutManager(mainActivity, 1, GridLayoutManager.HORIZONTAL, false) + val artistAdapter = ArtistAdapter(mainActivity, artists, PreferenceUtil.getInstance().getHomeGridStyle(context!!), false, null) + adapter = artistAdapter + } } override fun topAlbums(albums: ArrayList) { - topAlbumContainer.visibility = View.VISIBLE + topAlbumsContainer.visibility = View.VISIBLE val artistAdapter = AlbumFullWithAdapter(mainActivity, displayMetrics) artistAdapter.swapData(albums) - topAlbumRV.adapter = artistAdapter + topAlbum.adapter = artistAdapter } override fun suggestions(songs: ArrayList) { if (!songs.isEmpty()) { - suggestionsContainer.visibility = View.VISIBLE + suggestionContainer.visibility = View.VISIBLE val artistAdapter = CollageSongAdapter(mainActivity, songs) - suggestionsSongs.layoutManager = if (RetroUtil.isTablet()) GridLayoutManager(mainActivity, 2) else LinearLayoutManager(mainActivity) - suggestionsSongs.adapter = artistAdapter + suggestionSongs.apply { + layoutManager = if (RetroUtil.isTablet()) GridLayoutManager(mainActivity, 2) else LinearLayoutManager(mainActivity) + adapter = artistAdapter + } } } @@ -281,10 +267,11 @@ class BannerHomeFragment : AbsMainActivityFragment(), MainActivityFragmentCallba override fun geners(genres: ArrayList) { genreContainer.visibility = View.VISIBLE - genresRecyclerView.layoutManager = LinearLayoutManager(context) - - val genreAdapter = GenreAdapter(activity!!, genres, R.layout.item_list) - genresRecyclerView.adapter = genreAdapter + genresRecyclerView.apply { + val genreAdapter = GenreAdapter(activity!!, genres, R.layout.item_list) + layoutManager = LinearLayoutManager(context) + adapter = genreAdapter + } } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/PlayerAlbumCoverFragment.kt b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/PlayerAlbumCoverFragment.kt index 6739497f..008fa552 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/PlayerAlbumCoverFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/PlayerAlbumCoverFragment.kt @@ -5,7 +5,6 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.viewpager.widget.ViewPager -import butterknife.Unbinder import code.name.monkey.retromusic.R import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.transform.ParallaxPagerTransformer diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/adaptive/AdaptiveFragment.kt b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/adaptive/AdaptiveFragment.kt index eaa45107..5735ebfd 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/adaptive/AdaptiveFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/adaptive/AdaptiveFragment.kt @@ -4,6 +4,7 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.appcompat.widget.Toolbar import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper @@ -12,18 +13,22 @@ import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerFragment import code.name.monkey.retromusic.ui.fragments.player.PlayerAlbumCoverFragment +import kotlinx.android.synthetic.main.fragment_adaptive_player.* class AdaptiveFragment : AbsPlayerFragment(), PlayerAlbumCoverFragment.Callbacks { + override fun toolbarGet(): Toolbar { + return playerToolbar + } + private var lastColor: Int = 0 - lateinit var playbackControlsFragment: AdaptivePlaybackControlsFragment + private lateinit var playbackControlsFragment: AdaptivePlaybackControlsFragment override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_adaptive_player, container, false); + return inflater.inflate(R.layout.fragment_adaptive_player, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - toolbar = view.findViewById(R.id.toolbar) setUpSubFragments() setUpPlayerToolbar() } @@ -31,19 +36,21 @@ class AdaptiveFragment : AbsPlayerFragment(), PlayerAlbumCoverFragment.Callbacks private fun setUpSubFragments() { playbackControlsFragment = childFragmentManager.findFragmentById(R.id.playback_controls_fragment) as AdaptivePlaybackControlsFragment val playerAlbumCoverFragment = childFragmentManager.findFragmentById(R.id.player_album_cover_fragment) as PlayerAlbumCoverFragment - playerAlbumCoverFragment.setCallbacks(this) - playerAlbumCoverFragment.removeSlideEffect() + playerAlbumCoverFragment.apply { + removeSlideEffect() + }.setCallbacks(this) } private fun setUpPlayerToolbar() { - val primaryColor = ATHUtil.resolveColor(context, R.attr.iconColor) - /*toolbar!!.apply { + ATHUtil.resolveColor(context, R.attr.iconColor) + val primaryColor = ThemeStore.primaryColor(context!!) + playerToolbar.apply { inflateMenu(R.menu.menu_player) setNavigationOnClickListener { activity!!.onBackPressed() } ToolbarContentTintHelper.colorizeToolbar(this, primaryColor, activity) setTitleTextColor(primaryColor) setSubtitleTextColor(ThemeStore.textColorSecondary(context!!)) - }.setOnMenuItemClickListener(this)*/ + }.setOnMenuItemClickListener(this) } override fun onServiceConnected() { @@ -59,10 +66,10 @@ class AdaptiveFragment : AbsPlayerFragment(), PlayerAlbumCoverFragment.Callbacks private fun updateSong() { val song = MusicPlayerRemote.currentSong - /*toolbar!!.apply { + playerToolbar.apply { title = song.title subtitle = song.artistName - }*/ + } } override fun toggleFavorite(song: Song) { @@ -80,7 +87,7 @@ class AdaptiveFragment : AbsPlayerFragment(), PlayerAlbumCoverFragment.Callbacks playbackControlsFragment.setDark(color) lastColor = color callbacks!!.onPaletteColorChanged() - //ToolbarContentTintHelper.colorizeToolbar(toolbar, ATHUtil.resolveColor(context, R.attr.iconColor), activity) + ToolbarContentTintHelper.colorizeToolbar(playerToolbar, ATHUtil.resolveColor(context, R.attr.iconColor), activity) } override fun onShow() { diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/adaptive/AdaptivePlaybackControlsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/adaptive/AdaptivePlaybackControlsFragment.kt index 4ef44759..e607b269 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/adaptive/AdaptivePlaybackControlsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/adaptive/AdaptivePlaybackControlsFragment.kt @@ -21,11 +21,10 @@ import code.name.monkey.retromusic.service.MusicService import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerControlsFragment import code.name.monkey.retromusic.util.MusicUtil import code.name.monkey.retromusic.util.PreferenceUtil -import code.name.monkey.retromusic.views.PlayPauseDrawable +import kotlinx.android.synthetic.main.fragment_adaptive_player_playback_controls.* class AdaptivePlaybackControlsFragment : AbsPlayerControlsFragment() { - private val playPauseDrawable: PlayPauseDrawable? = null private var lastPlaybackControlsColor: Int = 0 private var lastDisabledPlaybackControlsColor: Int = 0 private var progressViewUpdateHelper: MusicProgressViewUpdateHelper? = null @@ -45,13 +44,13 @@ class AdaptivePlaybackControlsFragment : AbsPlayerControlsFragment() { setUpMusicControllers() hideVolumeIfAvailable() - playPauseFab!!.setOnClickListener { + playPauseButton.setOnClickListener { if (MusicPlayerRemote.isPlaying) { MusicPlayerRemote.pauseSong() } else { MusicPlayerRemote.resumePlaying() } - showBouceAnimation(playPauseFab!!) + showBouceAnimation(playPauseButton) } } @@ -97,9 +96,9 @@ class AdaptivePlaybackControlsFragment : AbsPlayerControlsFragment() { updatePrevNextColor() updatePlayPauseColor() - TintHelper.setTintAuto(playPauseFab!!, MaterialValueHelper.getPrimaryTextColor(context, ColorUtil.isColorLight(color)), false) - TintHelper.setTintAuto(playPauseFab!!, color, true) - TintHelper.setTintAuto(progressSlider!!, color, false) + TintHelper.setTintAuto(playPauseButton, MaterialValueHelper.getPrimaryTextColor(context, ColorUtil.isColorLight(color)), false) + TintHelper.setTintAuto(playPauseButton, color, true) + TintHelper.setTintAuto(progressSlider, color, false) } private fun updatePlayPauseColor() { @@ -107,18 +106,17 @@ class AdaptivePlaybackControlsFragment : AbsPlayerControlsFragment() { } private fun setUpPlayPauseFab() { - playPauseFab!!.setOnClickListener(PlayPauseButtonOnClickHandler()) + playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler()) } - protected fun updatePlayPauseDrawableState() { + private fun updatePlayPauseDrawableState() { if (MusicPlayerRemote.isPlaying) { - playPauseFab!!.setImageResource(R.drawable.ic_pause_white_24dp) + playPauseButton.setImageResource(R.drawable.ic_pause_white_24dp) } else { - playPauseFab!!.setImageResource(R.drawable.ic_play_arrow_white_24dp) + playPauseButton.setImageResource(R.drawable.ic_play_arrow_white_24dp) } } - private fun setUpMusicControllers() { setUpPlayPauseFab() setUpPrevNext() @@ -129,47 +127,62 @@ class AdaptivePlaybackControlsFragment : AbsPlayerControlsFragment() { private fun setUpPrevNext() { updatePrevNextColor() - nextButton!!.setOnClickListener { MusicPlayerRemote.playNextSong() } - prevButton!!.setOnClickListener { MusicPlayerRemote.back() } + nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() } + previousButton.setOnClickListener { MusicPlayerRemote.back() } } private fun updatePrevNextColor() { - nextButton!!.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) - prevButton!!.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + previousButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) } private fun setUpShuffleButton() { - shuffleButton!!.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() } + shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() } } override fun updateShuffleState() { when (MusicPlayerRemote.shuffleMode) { - MusicService.SHUFFLE_MODE_SHUFFLE -> shuffleButton!!.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) - else -> shuffleButton!!.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + MusicService.SHUFFLE_MODE_SHUFFLE -> shuffleButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + else -> shuffleButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN) } } private fun setUpRepeatButton() { - repeatButton!!.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() } + repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() } } override fun updateRepeatState() { when (MusicPlayerRemote.repeatMode) { MusicService.REPEAT_MODE_NONE -> { - repeatButton!!.setImageResource(R.drawable.ic_repeat_white_24dp) - repeatButton!!.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp) + repeatButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN) } MusicService.REPEAT_MODE_ALL -> { - repeatButton!!.setImageResource(R.drawable.ic_repeat_white_24dp) - repeatButton!!.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp) + repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) } MusicService.REPEAT_MODE_THIS -> { - repeatButton!!.setImageResource(R.drawable.ic_repeat_one_white_24dp) - repeatButton!!.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + repeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp) + repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) } } } + override fun onUpdateProgressViews(progress: Int, total: Int) { + progressSlider.max = total + + val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress) + animator.duration = 1500 + animator.interpolator = LinearInterpolator() + animator.start() + + songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong()) + songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong()) + } + + private fun hideVolumeIfAvailable() { + volumeFragmentContainer.visibility = if (PreferenceUtil.getInstance().volumeToggle) View.VISIBLE else View.GONE + } public override fun show() { //Ignore @@ -180,7 +193,7 @@ class AdaptivePlaybackControlsFragment : AbsPlayerControlsFragment() { } override fun setUpProgressSlider() { - progressSlider!!.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() { + progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() { override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { if (fromUser) { MusicPlayerRemote.seekTo(progress) @@ -191,19 +204,5 @@ class AdaptivePlaybackControlsFragment : AbsPlayerControlsFragment() { }) } - override fun onUpdateProgressViews(progress: Int, total: Int) { - progressSlider!!.max = total - val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress) - animator.duration = 1500 - animator.interpolator = LinearInterpolator() - animator.start() - - songTotalTime!!.text = MusicUtil.getReadableDurationString(total.toLong()) - songCurrentProgress!!.text = MusicUtil.getReadableDurationString(progress.toLong()) - } - - private fun hideVolumeIfAvailable() { - volumeContainer!!.visibility = if (PreferenceUtil.getInstance().volumeToggle) View.VISIBLE else View.GONE - } } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/blur/BlurPlaybackControlsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/blur/BlurPlaybackControlsFragment.kt index f643ad47..90312741 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/blur/BlurPlaybackControlsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/blur/BlurPlaybackControlsFragment.kt @@ -3,19 +3,23 @@ package code.name.monkey.retromusic.ui.fragments.player.blur import android.animation.ObjectAnimator import android.graphics.Color import android.graphics.PorterDuff +import android.graphics.drawable.ClipDrawable +import android.graphics.drawable.LayerDrawable import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.view.animation.AccelerateInterpolator import android.view.animation.DecelerateInterpolator import android.view.animation.LinearInterpolator import android.widget.SeekBar import android.widget.TextView import androidx.appcompat.widget.AppCompatTextView import androidx.core.content.ContextCompat -import butterknife.BindView -import butterknife.ButterKnife -import butterknife.Unbinder +import code.name.monkey.appthemehelper.ThemeStore +import code.name.monkey.appthemehelper.util.ATHUtil +import code.name.monkey.appthemehelper.util.ColorUtil +import code.name.monkey.appthemehelper.util.MaterialValueHelper import code.name.monkey.appthemehelper.util.TintHelper import code.name.monkey.retromusic.R import code.name.monkey.retromusic.helper.MusicPlayerRemote @@ -27,13 +31,14 @@ import code.name.monkey.retromusic.ui.fragments.VolumeFragment import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerControlsFragment import code.name.monkey.retromusic.util.MusicUtil import code.name.monkey.retromusic.util.PreferenceUtil +import kotlinx.android.synthetic.main.fragment_player_playback_controls.* +import kotlinx.android.synthetic.main.media_button.* +import kotlinx.android.synthetic.main.player_time.* +import kotlinx.android.synthetic.main.volume_controls.* class BlurPlaybackControlsFragment : AbsPlayerControlsFragment() { - lateinit var songTitle: AppCompatTextView - lateinit var text: TextView - private var lastPlaybackControlsColor: Int = 0 private var lastDisabledPlaybackControlsColor: Int = 0 private var progressViewUpdateHelper: MusicProgressViewUpdateHelper? = null @@ -43,27 +48,35 @@ class BlurPlaybackControlsFragment : AbsPlayerControlsFragment() { progressViewUpdateHelper = MusicProgressViewUpdateHelper(this) } - override fun onCreateView(inflater: LayoutInflater, - container: ViewGroup?, + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - val view = inflater.inflate(R.layout.fragment_blur_playback_controls, container, false) - songTitle = view.findViewById(R.id.title) - text = view.findViewById(R.id.text) - return view + + return inflater.inflate(R.layout.fragment_player_playback_controls, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) setUpMusicControllers() - volumeContainer!!.visibility = if (PreferenceUtil.getInstance().volumeToggle) View.VISIBLE else View.GONE - val mVolumeFragment = childFragmentManager.findFragmentById(R.id.volume_fragment) as VolumeFragment - mVolumeFragment.tintWhiteColor() + if (PreferenceUtil.getInstance().volumeToggle) { + volumeFragmentContainer.visibility = View.VISIBLE + } else { + volumeFragmentContainer.visibility = View.GONE + } + + playPauseButton.setOnClickListener { + if (MusicPlayerRemote.isPlaying) { + MusicPlayerRemote.pauseSong() + } else { + MusicPlayerRemote.resumePlaying() + } + showBonceAnimation() + } } private fun updateSong() { val song = MusicPlayerRemote.currentSong - songTitle.text = song.title + title.text = song.title text.text = song.artistName } @@ -102,41 +115,50 @@ class BlurPlaybackControlsFragment : AbsPlayerControlsFragment() { } override fun setDark(color: Int) { - lastPlaybackControlsColor = Color.WHITE - lastDisabledPlaybackControlsColor = ContextCompat.getColor(context!!, R.color.md_grey_500) + val colorBg = ATHUtil.resolveColor(activity, android.R.attr.colorBackground) + if (ColorUtil.isColorLight(colorBg)) { + lastPlaybackControlsColor = MaterialValueHelper.getSecondaryTextColor(activity, true) + lastDisabledPlaybackControlsColor = MaterialValueHelper.getSecondaryDisabledTextColor(activity, true) + } else { + lastPlaybackControlsColor = MaterialValueHelper.getPrimaryTextColor(activity, false) + lastDisabledPlaybackControlsColor = MaterialValueHelper.getPrimaryDisabledTextColor(activity, false) + } - songTitle.setTextColor(lastPlaybackControlsColor) - text.setTextColor(lastDisabledPlaybackControlsColor) - - setProgressBarColor() - - songCurrentProgress!!.setTextColor(lastPlaybackControlsColor) - songTotalTime!!.setTextColor(lastPlaybackControlsColor) + if (PreferenceUtil.getInstance().adaptiveColor) { + setFabColor(color) + } else { + setFabColor(ThemeStore.accentColor(context!!)) + } updateRepeatState() updateShuffleState() updatePrevNextColor() } - private fun setProgressBarColor() { - TintHelper.setTintAuto(progressSlider!!, Color.WHITE, false) + private fun setFabColor(i: Int) { + TintHelper.setTintAuto(playPauseButton, MaterialValueHelper.getPrimaryTextColor(context, ColorUtil.isColorLight(i)), false) + TintHelper.setTintAuto(playPauseButton, i, true) + setProgressBarColor(i) + } + + private fun setProgressBarColor(newColor: Int) { + val ld = progressSlider.progressDrawable as LayerDrawable + val clipDrawable = ld.findDrawableByLayerId(android.R.id.progress) as ClipDrawable + clipDrawable.setColorFilter(newColor, PorterDuff.Mode.SRC_IN) } private fun setUpPlayPauseFab() { - TintHelper.setTintAuto(playPauseFab!!, Color.WHITE, true) - TintHelper.setTintAuto(playPauseFab!!, Color.BLACK, false) - playPauseFab!!.setOnClickListener(PlayPauseButtonOnClickHandler()) + playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler()) } - protected fun updatePlayPauseDrawableState() { + private fun updatePlayPauseDrawableState() { if (MusicPlayerRemote.isPlaying) { - playPauseFab!!.setImageResource(R.drawable.ic_pause_white_24dp) + playPauseButton.setImageResource(R.drawable.ic_pause_white_24dp) } else { - playPauseFab!!.setImageResource(R.drawable.ic_play_arrow_white_24dp) + playPauseButton.setImageResource(R.drawable.ic_play_arrow_white_24dp) } } - private fun setUpMusicControllers() { setUpPlayPauseFab() setUpPrevNext() @@ -147,50 +169,49 @@ class BlurPlaybackControlsFragment : AbsPlayerControlsFragment() { private fun setUpPrevNext() { updatePrevNextColor() - nextButton!!.setOnClickListener { MusicPlayerRemote.playNextSong() } - prevButton!!.setOnClickListener { MusicPlayerRemote.back() } + nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() } + previousButton.setOnClickListener { MusicPlayerRemote.back() } } private fun updatePrevNextColor() { - nextButton!!.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) - prevButton!!.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + previousButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) } private fun setUpShuffleButton() { - shuffleButton!!.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() } + shuffleButton.setOnClickListener { v -> MusicPlayerRemote.toggleShuffleMode() } } override fun updateShuffleState() { when (MusicPlayerRemote.shuffleMode) { - MusicService.SHUFFLE_MODE_SHUFFLE -> shuffleButton!!.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) - else -> shuffleButton!!.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + MusicService.SHUFFLE_MODE_SHUFFLE -> shuffleButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + else -> shuffleButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN) } } private fun setUpRepeatButton() { - repeatButton!!.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() } + repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() } } override fun updateRepeatState() { when (MusicPlayerRemote.repeatMode) { MusicService.REPEAT_MODE_NONE -> { - repeatButton!!.setImageResource(R.drawable.ic_repeat_white_24dp) - repeatButton!!.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp) + repeatButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN) } MusicService.REPEAT_MODE_ALL -> { - repeatButton!!.setImageResource(R.drawable.ic_repeat_white_24dp) - repeatButton!!.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp) + repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) } MusicService.REPEAT_MODE_THIS -> { - repeatButton!!.setImageResource(R.drawable.ic_repeat_one_white_24dp) - repeatButton!!.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + repeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp) + repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) } } } - - override fun show() { - playPauseFab!!.animate() + public override fun show() { + playPauseButton!!.animate() .scaleX(1f) .scaleY(1f) .rotation(360f) @@ -198,34 +219,61 @@ class BlurPlaybackControlsFragment : AbsPlayerControlsFragment() { .start() } - override fun hide() { - if (playPauseFab != null) { - playPauseFab!!.scaleX = 0f - playPauseFab!!.scaleY = 0f - playPauseFab!!.rotation = 0f + public override fun hide() { + if (playPauseButton != null) { + playPauseButton!!.apply { + scaleX = 0f + scaleY = 0f + rotation = 0f + } } } override fun setUpProgressSlider() { - progressSlider!!.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() { + progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() { override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { if (fromUser) { MusicPlayerRemote.seekTo(progress) - onUpdateProgressViews(MusicPlayerRemote.songProgressMillis, MusicPlayerRemote.songDurationMillis) + onUpdateProgressViews(MusicPlayerRemote.songProgressMillis, + MusicPlayerRemote.songDurationMillis) } } }) } + private fun showBonceAnimation() { + playPauseButton.apply { + clearAnimation() + scaleX = 0.9f + scaleY = 0.9f + visibility = View.VISIBLE + pivotX = (width / 2).toFloat() + pivotY = (height / 2).toFloat() + + animate().setDuration(200) + .setInterpolator(DecelerateInterpolator()) + .scaleX(1.1f) + .scaleY(1.1f) + .withEndAction { + animate().setDuration(200) + .setInterpolator(AccelerateInterpolator()) + .scaleX(1f) + .scaleY(1f) + .alpha(1f).start() + }.start() + } + } + + override fun onUpdateProgressViews(progress: Int, total: Int) { - progressSlider!!.max = total + progressSlider.max = total val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress) animator.duration = 1500 animator.interpolator = LinearInterpolator() animator.start() - songTotalTime!!.text = MusicUtil.getReadableDurationString(total.toLong()) - songCurrentProgress!!.text = MusicUtil.getReadableDurationString(progress.toLong()) + songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong()) + songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong()) } } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/blur/BlurPlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/blur/BlurPlayerFragment.kt index a29a82c3..52ce65b3 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/blur/BlurPlayerFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/blur/BlurPlayerFragment.kt @@ -6,10 +6,7 @@ import android.preference.PreferenceManager import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.ImageView -import androidx.appcompat.app.AppCompatActivity -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView +import androidx.appcompat.widget.Toolbar import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.retromusic.R import code.name.monkey.retromusic.glide.BlurTransformation @@ -17,20 +14,19 @@ import code.name.monkey.retromusic.glide.RetroMusicColoredTarget import code.name.monkey.retromusic.glide.SongGlideRequest import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.model.Song -import code.name.monkey.retromusic.ui.adapter.song.PlayingQueueAdapter import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerFragment import code.name.monkey.retromusic.ui.fragments.player.PlayerAlbumCoverFragment import com.bumptech.glide.Glide -import com.h6ah4i.android.widget.advrecyclerview.animator.RefactoredDefaultItemAnimator +import kotlinx.android.synthetic.main.fragment_blur.* class BlurPlayerFragment : AbsPlayerFragment() { - lateinit var playbackControlsFragment: BlurPlaybackControlsFragment + override fun toolbarGet(): Toolbar { + return playerToolbar + } + + private lateinit var playbackControlsFragment: BlurPlaybackControlsFragment - private var colorBackground: ImageView? = null private var lastColor: Int = 0 - private var playingQueueAdapter: PlayingQueueAdapter? = null - private var layoutManager: LinearLayoutManager? = null - private var recyclerView: RecyclerView? = null override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, @@ -51,7 +47,7 @@ class BlurPlayerFragment : AbsPlayerFragment() { } private fun setUpPlayerToolbar() { - toolbar!!.apply { + playerToolbar!!.apply { inflateMenu(R.menu.menu_player) setNavigationOnClickListener { activity!!.onBackPressed() } ToolbarContentTintHelper.colorizeToolbar(this, Color.WHITE, activity) @@ -66,7 +62,7 @@ class BlurPlayerFragment : AbsPlayerFragment() { playbackControlsFragment.setDark(color) lastColor = color callbacks!!.onPaletteColorChanged() - ToolbarContentTintHelper.colorizeToolbar(toolbar!!, Color.WHITE, activity) + ToolbarContentTintHelper.colorizeToolbar(playerToolbar!!, Color.WHITE, activity) } override fun toggleFavorite(song: Song) { @@ -95,15 +91,6 @@ class BlurPlayerFragment : AbsPlayerFragment() { override val paletteColor: Int get() = lastColor - override fun onDestroyView() { - recyclerView!!.apply { - itemAnimator = null - adapter = null - } - playingQueueAdapter = null - layoutManager = null - super.onDestroyView() - } private fun updateBlur() { val activity = activity ?: return @@ -130,69 +117,11 @@ class BlurPlayerFragment : AbsPlayerFragment() { override fun onServiceConnected() { updateIsFavorite() updateBlur() - setUpRecyclerView() } override fun onPlayingMetaChanged() { updateIsFavorite() updateBlur() - updateQueuePosition() - } - - private fun setUpRecyclerView() { - if (recyclerView != null) { - val animator = RefactoredDefaultItemAnimator() - - playingQueueAdapter = PlayingQueueAdapter((activity as AppCompatActivity), - MusicPlayerRemote.playingQueue, - MusicPlayerRemote.position, - R.layout.item_song, - Color.WHITE) - layoutManager = LinearLayoutManager(context) - - recyclerView!!.apply { - layoutManager = layoutManager - adapter = playingQueueAdapter - itemAnimator = animator - } - layoutManager!!.scrollToPositionWithOffset(MusicPlayerRemote.position + 1, 0) - } - } - - override fun onQueueChanged() { - updateQueue() - updateCurrentSong() - } - - override fun onMediaStoreChanged() { - updateQueue() - updateCurrentSong() - } - - private fun updateCurrentSong() {} - - private fun updateQueuePosition() { - - playingQueueAdapter!!.apply { - setCurrent(MusicPlayerRemote.position) - resetToCurrentPosition() - } - - } - - private fun updateQueue() { - playingQueueAdapter!!.apply { - swapDataSet(MusicPlayerRemote.playingQueue, MusicPlayerRemote.position) - resetToCurrentPosition() - } - - } - - private fun resetToCurrentPosition() { - recyclerView!!.apply { - stopScroll() - } - layoutManager!!.scrollToPositionWithOffset(MusicPlayerRemote.position + 1, 0) } } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/cardblur/CardBlurFragment.kt b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/cardblur/CardBlurFragment.kt new file mode 100644 index 00000000..b5347a69 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/cardblur/CardBlurFragment.kt @@ -0,0 +1,151 @@ +package code.name.monkey.retromusic.ui.fragments.player.cardblur + +import android.graphics.Color +import android.os.Bundle +import android.preference.PreferenceManager +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.widget.Toolbar +import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.glide.BlurTransformation +import code.name.monkey.retromusic.glide.RetroMusicColoredTarget +import code.name.monkey.retromusic.glide.SongGlideRequest +import code.name.monkey.retromusic.helper.MusicPlayerRemote +import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerFragment +import code.name.monkey.retromusic.ui.fragments.player.PlayerAlbumCoverFragment +import code.name.monkey.retromusic.ui.fragments.player.normal.PlayerFragment +import com.bumptech.glide.Glide +import kotlinx.android.synthetic.main.fragment_card_blur_player.* + +class CardBlurFragment : AbsPlayerFragment(), PlayerAlbumCoverFragment.Callbacks { + override fun toolbarGet(): Toolbar { + return playerToolbar + } + + private var lastColor: Int = 0 + override val paletteColor: Int + get() = lastColor + private lateinit var playbackControlsFragment: CardBlurPlaybackControlsFragment + + + override fun onShow() { + playbackControlsFragment.show() + } + + override fun onHide() { + playbackControlsFragment.hide() + onBackPressed() + } + + override fun onBackPressed(): Boolean { + return false + } + + override fun toolbarIconColor(): Int { + return Color.WHITE + } + + override fun onColorChanged(color: Int) { + playbackControlsFragment.setDark(color) + lastColor = color + callbacks!!.onPaletteColorChanged() + ToolbarContentTintHelper.colorizeToolbar(playerToolbar, Color.WHITE, activity) + + playerToolbar.setTitleTextColor(Color.WHITE) + playerToolbar.setSubtitleTextColor(Color.WHITE) + } + + override fun toggleFavorite(song: Song) { + super.toggleFavorite(song) + if (song.id == MusicPlayerRemote.currentSong.id) { + updateIsFavorite() + } + } + + override fun onFavoriteToggled() { + toggleFavorite(MusicPlayerRemote.currentSong) + } + + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + + return inflater.inflate(R.layout.fragment_card_blur_player, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setUpSubFragments() + setUpPlayerToolbar() + } + + private fun setUpSubFragments() { + playbackControlsFragment = childFragmentManager.findFragmentById(R.id.playbackControlsFragment) as CardBlurPlaybackControlsFragment + val playerAlbumCoverFragment = childFragmentManager.findFragmentById(R.id.playerAlbumCoverFragment) as PlayerAlbumCoverFragment? + if (playerAlbumCoverFragment != null) { + playerAlbumCoverFragment.setCallbacks(this) + playerAlbumCoverFragment.removeEffect() + } + + } + + private fun setUpPlayerToolbar() { + playerToolbar.apply { + inflateMenu(R.menu.menu_player) + setNavigationOnClickListener { activity!!.onBackPressed() } + setTitleTextColor(Color.WHITE) + setSubtitleTextColor(Color.WHITE) + ToolbarContentTintHelper.colorizeToolbar(playerToolbar, Color.WHITE, activity) + }.setOnMenuItemClickListener(this) + } + + override fun onServiceConnected() { + updateIsFavorite() + updateBlur() + updateSong() + } + + override fun onPlayingMetaChanged() { + updateIsFavorite() + updateBlur() + updateSong() + } + + private fun updateSong() { + val song = MusicPlayerRemote.currentSong + playerToolbar.apply { + title = song.title + subtitle = song.artistName + } + } + + private fun updateBlur() { + val activity = activity ?: return + val blurAmount = PreferenceManager.getDefaultSharedPreferences(context) + .getInt("new_blur_amount", 25) + + colorBackground!!.clearColorFilter() + SongGlideRequest.Builder.from(Glide.with(activity), MusicPlayerRemote.currentSong) + .checkIgnoreMediaStore(activity) + .generatePalette(activity) + .build() + .transform(BlurTransformation.Builder(getActivity()!!).blurRadius(blurAmount.toFloat()).build()) + .into(object : RetroMusicColoredTarget(colorBackground!!) { + override fun onColorReady(color: Int) { + if (color == defaultFooterColor) { + colorBackground!!.setColorFilter(color) + } + } + }) + } + + companion object { + + fun newInstance(): PlayerFragment { + return PlayerFragment() + } + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/cardblur/CardBlurPlaybackControlsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/cardblur/CardBlurPlaybackControlsFragment.kt new file mode 100644 index 00000000..f5602749 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/cardblur/CardBlurPlaybackControlsFragment.kt @@ -0,0 +1,222 @@ +package code.name.monkey.retromusic.ui.fragments.player.cardblur + +import android.animation.ObjectAnimator +import android.graphics.Color +import android.graphics.PorterDuff +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.animation.AccelerateInterpolator +import android.view.animation.DecelerateInterpolator +import android.view.animation.LinearInterpolator +import android.widget.SeekBar +import code.name.monkey.appthemehelper.util.ColorUtil +import code.name.monkey.appthemehelper.util.MaterialValueHelper +import code.name.monkey.appthemehelper.util.TintHelper +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.helper.MusicPlayerRemote +import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper +import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler +import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener +import code.name.monkey.retromusic.service.MusicService +import code.name.monkey.retromusic.ui.fragments.VolumeFragment +import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerControlsFragment +import code.name.monkey.retromusic.util.MusicUtil +import code.name.monkey.retromusic.util.PreferenceUtil +import kotlinx.android.synthetic.main.fragment_card_blur_player_playback_controls.* +import kotlinx.android.synthetic.main.media_button.* +import kotlinx.android.synthetic.main.player_time.* +import kotlinx.android.synthetic.main.volume_controls.* + +class CardBlurPlaybackControlsFragment : AbsPlayerControlsFragment() { + + private var lastPlaybackControlsColor: Int = 0 + private var lastDisabledPlaybackControlsColor: Int = 0 + private var progressViewUpdateHelper: MusicProgressViewUpdateHelper? = null + + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + progressViewUpdateHelper = MusicProgressViewUpdateHelper(this) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + + return inflater.inflate(R.layout.fragment_card_blur_player_playback_controls, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setUpMusicControllers() + setupVolumeControls() + hideVolumeIfAvailable() + } + + private fun hideVolumeIfAvailable() { + volumeFragmentContainer.visibility = if (PreferenceUtil.getInstance().volumeToggle) View.VISIBLE else View.GONE + } + + override fun setDark(color: Int) { + lastPlaybackControlsColor = Color.WHITE + lastDisabledPlaybackControlsColor = ColorUtil.withAlpha(Color.WHITE, 0.3f) + + updateRepeatState() + updateShuffleState() + updatePrevNextColor() + updateProgressTextColor() + } + + + private fun setUpPlayPauseFab() { + playPauseButton.apply { + TintHelper.setTintAuto(this, Color.WHITE, true) + TintHelper.setTintAuto(this, Color.BLACK, false) + setOnClickListener(PlayPauseButtonOnClickHandler()) + } + + } + + private fun updatePlayPauseDrawableState() { + when { + MusicPlayerRemote.isPlaying -> playPauseButton.setImageResource(R.drawable.ic_pause_white_24dp) + else -> playPauseButton.setImageResource(R.drawable.ic_play_arrow_white_24dp) + } + } + + private fun setupVolumeControls() { + val volumeFragment = childFragmentManager.findFragmentById(R.id.volumeFragment) as VolumeFragment + volumeFragment.tintWhiteColor() + } + + private fun updateProgressTextColor() { + val color = MaterialValueHelper.getPrimaryTextColor(context, false) + songTotalTime.setTextColor(color) + songCurrentProgress.setTextColor(color) + } + + + override fun onResume() { + super.onResume() + progressViewUpdateHelper!!.start() + } + + override fun onPause() { + super.onPause() + progressViewUpdateHelper!!.stop() + } + + override fun onServiceConnected() { + updatePlayPauseDrawableState() + updateRepeatState() + updateShuffleState() + } + + override fun onPlayStateChanged() { + updatePlayPauseDrawableState() + } + + override fun onRepeatModeChanged() { + updateRepeatState() + } + + override fun onShuffleModeChanged() { + updateShuffleState() + } + + private fun setUpMusicControllers() { + setUpPlayPauseFab() + setUpPrevNext() + setUpRepeatButton() + setUpShuffleButton() + setUpProgressSlider() + } + + private fun setUpPrevNext() { + updatePrevNextColor() + nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() } + previousButton.setOnClickListener { MusicPlayerRemote.back() } + } + + private fun updatePrevNextColor() { + nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + previousButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + } + + private fun setUpShuffleButton() { + shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() } + } + + override fun updateShuffleState() { + when (MusicPlayerRemote.shuffleMode) { + MusicService.SHUFFLE_MODE_SHUFFLE -> shuffleButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + else -> shuffleButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + } + } + + private fun setUpRepeatButton() { + repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() } + } + + override fun updateRepeatState() { + when (MusicPlayerRemote.repeatMode) { + MusicService.REPEAT_MODE_NONE -> { + repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp) + repeatButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + } + MusicService.REPEAT_MODE_ALL -> { + repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp) + repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + } + MusicService.REPEAT_MODE_THIS -> { + repeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp) + repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + } + } + } + + public override fun show() { + playPauseButton!!.animate() + .scaleX(1f) + .scaleY(1f) + .rotation(360f) + .setInterpolator(DecelerateInterpolator()) + .start() + } + + public override fun hide() { + if (playPauseButton != null) { + playPauseButton!!.apply { + scaleX = 0f + scaleY = 0f + rotation = 0f + } + } + } + + override fun setUpProgressSlider() { + progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() { + override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { + if (fromUser) { + MusicPlayerRemote.seekTo(progress) + onUpdateProgressViews(MusicPlayerRemote.songProgressMillis, + MusicPlayerRemote.songDurationMillis) + } + } + }) + } + + + override fun onUpdateProgressViews(progress: Int, total: Int) { + progressSlider.max = total + + val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress) + animator.duration = 1500 + animator.interpolator = LinearInterpolator() + animator.start() + + songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong()) + songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong()) + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/fit/FitFragment.kt b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/fit/FitFragment.kt new file mode 100644 index 00000000..06543780 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/fit/FitFragment.kt @@ -0,0 +1,110 @@ +package code.name.monkey.retromusic.ui.fragments.player.fit + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.widget.Toolbar +import code.name.monkey.appthemehelper.util.ATHUtil +import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.helper.MusicPlayerRemote +import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerFragment +import code.name.monkey.retromusic.ui.fragments.player.PlayerAlbumCoverFragment +import kotlinx.android.synthetic.main.fragment_fit.* + + +class FitFragment : AbsPlayerFragment(), PlayerAlbumCoverFragment.Callbacks { + override fun toolbarGet(): Toolbar { + return playerToolbar + } + + private var lastColor: Int = 0 + override val paletteColor: Int + get() = lastColor + + private lateinit var playbackControlsFragment: FitPlaybackControlsFragment + + override fun onShow() { + playbackControlsFragment.show() + } + + override fun onHide() { + playbackControlsFragment.hide() + onBackPressed() + } + + override fun onBackPressed(): Boolean { + return false + } + + override fun toolbarIconColor(): Int { + return ATHUtil.resolveColor(context, R.attr.iconColor) + } + + override fun onColorChanged(color: Int) { + playbackControlsFragment.setDark(color) + lastColor = color + callbacks!!.onPaletteColorChanged() + + ToolbarContentTintHelper.colorizeToolbar(playerToolbar, ATHUtil.resolveColor(context, R.attr.iconColor), activity) + + } + + override fun toggleFavorite(song: Song) { + super.toggleFavorite(song) + if (song.id == MusicPlayerRemote.currentSong.id) { + updateIsFavorite() + } + } + + override fun onFavoriteToggled() { + toggleFavorite(MusicPlayerRemote.currentSong) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + + return inflater.inflate(R.layout.fragment_fit, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setUpSubFragments() + setUpPlayerToolbar() + } + + private fun setUpSubFragments() { + playbackControlsFragment = childFragmentManager.findFragmentById(R.id.playbackControlsFragment) as FitPlaybackControlsFragment + + val playerAlbumCoverFragment = childFragmentManager.findFragmentById(R.id.playerAlbumCoverFragment) as PlayerAlbumCoverFragment + playerAlbumCoverFragment.setCallbacks(this) + playerAlbumCoverFragment.removeEffect() + } + + private fun setUpPlayerToolbar() { + playerToolbar!!.apply { + inflateMenu(R.menu.menu_player) + setNavigationOnClickListener { activity!!.onBackPressed() } + setOnMenuItemClickListener(this@FitFragment) + ToolbarContentTintHelper.colorizeToolbar(this, ATHUtil.resolveColor(context, R.attr.iconColor), activity) + } + } + + override fun onServiceConnected() { + updateIsFavorite() + + } + + override fun onPlayingMetaChanged() { + updateIsFavorite() + } + + companion object { + + fun newInstance(): FitFragment { + return FitFragment() + } + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/fit/FitPlaybackControlsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/fit/FitPlaybackControlsFragment.kt new file mode 100644 index 00000000..5c508a72 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/fit/FitPlaybackControlsFragment.kt @@ -0,0 +1,271 @@ +package code.name.monkey.retromusic.ui.fragments.player.fit + +import android.animation.ObjectAnimator +import android.graphics.PorterDuff +import android.graphics.drawable.ClipDrawable +import android.graphics.drawable.LayerDrawable +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.animation.AccelerateInterpolator +import android.view.animation.DecelerateInterpolator +import android.view.animation.LinearInterpolator +import android.widget.SeekBar +import code.name.monkey.appthemehelper.ThemeStore +import code.name.monkey.appthemehelper.util.ATHUtil +import code.name.monkey.appthemehelper.util.ColorUtil +import code.name.monkey.appthemehelper.util.MaterialValueHelper +import code.name.monkey.appthemehelper.util.TintHelper +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.helper.MusicPlayerRemote +import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper +import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler +import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener +import code.name.monkey.retromusic.service.MusicService +import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerControlsFragment +import code.name.monkey.retromusic.util.MusicUtil +import code.name.monkey.retromusic.util.PreferenceUtil +import kotlinx.android.synthetic.main.fragment_player_playback_controls.* +import kotlinx.android.synthetic.main.media_button.* +import kotlinx.android.synthetic.main.player_time.* +import kotlinx.android.synthetic.main.volume_controls.* + +class FitPlaybackControlsFragment : AbsPlayerControlsFragment() { + + + private var lastPlaybackControlsColor: Int = 0 + private var lastDisabledPlaybackControlsColor: Int = 0 + private var progressViewUpdateHelper: MusicProgressViewUpdateHelper? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + progressViewUpdateHelper = MusicProgressViewUpdateHelper(this) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + + return inflater.inflate(R.layout.fragment_fit_playback_controls, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setUpMusicControllers() + playPauseButton.setOnClickListener { + if (MusicPlayerRemote.isPlaying) { + MusicPlayerRemote.pauseSong() + } else { + MusicPlayerRemote.resumePlaying() + } + showBonceAnimation() + } + } + + private fun updateSong() { + val song = MusicPlayerRemote.currentSong + title.text = song.title + text.text = song.artistName + } + + override fun onResume() { + super.onResume() + progressViewUpdateHelper!!.start() + } + + override fun onPause() { + super.onPause() + progressViewUpdateHelper!!.stop() + } + + override fun onServiceConnected() { + updatePlayPauseDrawableState() + updateRepeatState() + updateShuffleState() + updateSong() + } + + override fun onPlayingMetaChanged() { + super.onPlayingMetaChanged() + updateSong() + } + + override fun onPlayStateChanged() { + updatePlayPauseDrawableState() + } + + override fun onRepeatModeChanged() { + updateRepeatState() + } + + override fun onShuffleModeChanged() { + updateShuffleState() + } + + override fun setDark(color: Int) { + val colorBg = ATHUtil.resolveColor(activity, android.R.attr.colorBackground) + if (ColorUtil.isColorLight(colorBg)) { + lastPlaybackControlsColor = MaterialValueHelper.getSecondaryTextColor(activity, true) + lastDisabledPlaybackControlsColor = MaterialValueHelper.getSecondaryDisabledTextColor(activity, true) + } else { + lastPlaybackControlsColor = MaterialValueHelper.getPrimaryTextColor(activity, false) + lastDisabledPlaybackControlsColor = MaterialValueHelper.getPrimaryDisabledTextColor(activity, false) + } + + if (PreferenceUtil.getInstance().adaptiveColor) { + setFabColor(color) + } else { + setFabColor(ThemeStore.accentColor(context!!)) + } + + updateRepeatState() + updateShuffleState() + updatePrevNextColor() + } + + private fun setFabColor(i: Int) { + TintHelper.setTintAuto(playPauseButton, MaterialValueHelper.getPrimaryTextColor(context, ColorUtil.isColorLight(i)), false) + TintHelper.setTintAuto(playPauseButton, i, true) + + } + + private fun setUpPlayPauseFab() { + playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler()) + } + + private fun updatePlayPauseDrawableState() { + if (MusicPlayerRemote.isPlaying) { + playPauseButton.setImageResource(R.drawable.ic_pause_white_24dp) + } else { + playPauseButton.setImageResource(R.drawable.ic_play_arrow_white_24dp) + } + } + + private fun setProgressBarColor(newColor: Int) { + val ld = progressSlider!!.progressDrawable as LayerDrawable + val clipDrawable = ld.findDrawableByLayerId(android.R.id.progress) as ClipDrawable + clipDrawable.setColorFilter(newColor, PorterDuff.Mode.SRC_IN) + } + + + private fun setUpMusicControllers() { + setUpPlayPauseFab() + setUpPrevNext() + setUpRepeatButton() + setUpShuffleButton() + setUpProgressSlider() + } + + private fun setUpPrevNext() { + updatePrevNextColor() + nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() } + previousButton.setOnClickListener { MusicPlayerRemote.back() } + } + + private fun updatePrevNextColor() { + nextButton!!.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + previousButton!!.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + } + + private fun setUpShuffleButton() { + shuffleButton.setOnClickListener { v -> MusicPlayerRemote.toggleShuffleMode() } + } + + override fun updateShuffleState() { + when (MusicPlayerRemote.shuffleMode) { + MusicService.SHUFFLE_MODE_SHUFFLE -> shuffleButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + else -> shuffleButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + } + } + + private fun setUpRepeatButton() { + repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() } + } + + override fun updateRepeatState() { + when (MusicPlayerRemote.repeatMode) { + MusicService.REPEAT_MODE_NONE -> { + repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp) + repeatButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + } + MusicService.REPEAT_MODE_ALL -> { + repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp) + repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + } + MusicService.REPEAT_MODE_THIS -> { + repeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp) + repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + } + } + } + + public override fun show() { + playPauseButton!!.animate() + .scaleX(1f) + .scaleY(1f) + .rotation(360f) + .setInterpolator(DecelerateInterpolator()) + .start() + } + + public override fun hide() { + if (playPauseButton != null) { + playPauseButton!!.apply { + scaleX = 0f + scaleY = 0f + rotation = 0f + } + } + } + + override fun setUpProgressSlider() { + progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() { + override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { + if (fromUser) { + MusicPlayerRemote.seekTo(progress) + onUpdateProgressViews(MusicPlayerRemote.songProgressMillis, + MusicPlayerRemote.songDurationMillis) + } + } + }) + } + + private fun showBonceAnimation() { + playPauseButton.apply { + clearAnimation() + scaleX = 0.9f + scaleY = 0.9f + visibility = View.VISIBLE + pivotX = (width / 2).toFloat() + pivotY = (height / 2).toFloat() + + animate().setDuration(200) + .setInterpolator(DecelerateInterpolator()) + .scaleX(1.1f) + .scaleY(1.1f) + .withEndAction { + animate().setDuration(200) + .setInterpolator(AccelerateInterpolator()) + .scaleX(1f) + .scaleY(1f) + .alpha(1f).start() + }.start() + } + } + + override fun onUpdateProgressViews(progress: Int, total: Int) { + progressSlider.max = total + + val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress) + animator.duration = 1500 + animator.interpolator = LinearInterpolator() + animator.start() + + songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong()) + songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong()) + } + + private fun hideVolumeIfAvailable() { + volumeFragmentContainer.visibility = if (PreferenceUtil.getInstance().volumeToggle) View.VISIBLE else View.GONE + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/flat/FlatPlaybackControlsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/flat/FlatPlaybackControlsFragment.kt new file mode 100644 index 00000000..bab7b04f --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/flat/FlatPlaybackControlsFragment.kt @@ -0,0 +1,230 @@ +package code.name.monkey.retromusic.ui.fragments.player.flat + +import android.animation.ObjectAnimator +import android.graphics.PorterDuff +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.animation.DecelerateInterpolator +import android.view.animation.LinearInterpolator +import android.widget.SeekBar +import code.name.monkey.appthemehelper.ThemeStore +import code.name.monkey.appthemehelper.util.ATHUtil +import code.name.monkey.appthemehelper.util.ColorUtil +import code.name.monkey.appthemehelper.util.MaterialValueHelper +import code.name.monkey.appthemehelper.util.TintHelper +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.helper.MusicPlayerRemote +import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper +import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper.Callback +import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler +import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener +import code.name.monkey.retromusic.service.MusicService +import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerControlsFragment +import code.name.monkey.retromusic.util.MusicUtil +import code.name.monkey.retromusic.util.PreferenceUtil +import kotlinx.android.synthetic.main.fragment_flat_player_playback_controls.* +import kotlinx.android.synthetic.main.player_time.* +import kotlinx.android.synthetic.main.volume_controls.* + +class FlatPlaybackControlsFragment : AbsPlayerControlsFragment(), Callback { + + private var lastPlaybackControlsColor: Int = 0 + private var lastDisabledPlaybackControlsColor: Int = 0 + private var progressViewUpdateHelper: MusicProgressViewUpdateHelper? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + progressViewUpdateHelper = MusicProgressViewUpdateHelper(this) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.fragment_flat_player_playback_controls, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setUpMusicControllers() + hideVolumeIfAvailable() + } + + override fun onResume() { + super.onResume() + progressViewUpdateHelper!!.start() + } + + override fun onPause() { + super.onPause() + progressViewUpdateHelper!!.stop() + } + + override fun onUpdateProgressViews(progress: Int, total: Int) { + progressSlider.max = total + + val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress) + animator.duration = 1500 + animator.interpolator = LinearInterpolator() + animator.start() + + songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong()) + songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong()) + } + + private fun hideVolumeIfAvailable() { + volumeFragmentContainer.visibility = if (PreferenceUtil.getInstance().volumeToggle) View.VISIBLE else View.GONE + } + + public override fun show() { + playPauseButton!!.animate() + .scaleX(1f) + .scaleY(1f) + .setInterpolator(DecelerateInterpolator()) + .start() + } + + + public override fun hide() { + playPauseButton!!.apply { + scaleX = 0f + scaleY = 0f + rotation = 0f + } + } + + override fun setDark(color: Int) { + val colorBg = ATHUtil.resolveColor(activity, android.R.attr.colorBackground) + val isDark = ColorUtil.isColorLight(colorBg) + if (isDark) { + lastPlaybackControlsColor = MaterialValueHelper.getSecondaryTextColor(activity, true) + lastDisabledPlaybackControlsColor = MaterialValueHelper.getSecondaryDisabledTextColor(activity, true) + } else { + lastPlaybackControlsColor = MaterialValueHelper.getPrimaryTextColor(activity, false) + lastDisabledPlaybackControlsColor = MaterialValueHelper.getPrimaryDisabledTextColor(activity, false) + } + val accentColor = ThemeStore.accentColor(context!!) + val b = PreferenceUtil.getInstance().adaptiveColor + updateTextColors(if (b) color else accentColor) + setProgressBarColor(if (b) color else accentColor) + + + updateRepeatState() + updateShuffleState() + } + + private fun setProgressBarColor(dark: Int) { + TintHelper.setTintAuto(progressSlider!!, dark, false) + } + + private fun updateTextColors(color: Int) { + val isDark = ColorUtil.isColorLight(color) + val darkColor = ColorUtil.darkenColor(color) + val colorPrimary = MaterialValueHelper.getPrimaryTextColor(context, isDark) + val colorSecondary = MaterialValueHelper.getSecondaryTextColor(context, ColorUtil.isColorLight(darkColor)) + + TintHelper.setTintAuto(playPauseButton!!, colorPrimary, false) + TintHelper.setTintAuto(playPauseButton!!, color, true) + + title.setBackgroundColor(color) + title.setTextColor(colorPrimary) + text.setBackgroundColor(darkColor) + text.setTextColor(colorSecondary) + } + + override fun onServiceConnected() { + updatePlayPauseDrawableState() + updateRepeatState() + updateShuffleState() + updateSong() + } + + override fun onPlayingMetaChanged() { + super.onPlayingMetaChanged() + updateSong() + } + + override fun onPlayStateChanged() { + updatePlayPauseDrawableState() + } + + private fun setUpPlayPauseFab() { + playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler()) + } + + private fun updatePlayPauseDrawableState() { + if (MusicPlayerRemote.isPlaying) { + playPauseButton.setImageResource(R.drawable.ic_pause_white_24dp) + } else { + playPauseButton.setImageResource(R.drawable.ic_play_arrow_white_24dp) + } + } + + private fun setUpMusicControllers() { + setUpPlayPauseFab() + setUpRepeatButton() + setUpShuffleButton() + setUpProgressSlider() + } + + private fun updateSong() { + //TransitionManager.beginDelayedTransition(viewGroup, new ChangeText().setChangeBehavior(ChangeText.CHANGE_BEHAVIOR_OUT_IN)); + val song = MusicPlayerRemote.currentSong + title.text = song.title + text.text = song.artistName + + } + + override fun setUpProgressSlider() { + progressSlider!!.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() { + override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { + if (fromUser) { + MusicPlayerRemote.seekTo(progress) + onUpdateProgressViews(MusicPlayerRemote.songProgressMillis, + MusicPlayerRemote.songDurationMillis) + } + } + }) + } + + override fun onRepeatModeChanged() { + updateRepeatState() + } + + override fun onShuffleModeChanged() { + updateShuffleState() + } + + private fun setUpRepeatButton() { + repeatButton.setOnClickListener { v -> MusicPlayerRemote.cycleRepeatMode() } + } + + override fun updateRepeatState() { + when (MusicPlayerRemote.repeatMode) { + MusicService.REPEAT_MODE_NONE -> { + repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp) + repeatButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + } + MusicService.REPEAT_MODE_ALL -> { + repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp) + repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + } + MusicService.REPEAT_MODE_THIS -> { + repeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp) + repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + } + } + } + + private fun setUpShuffleButton() { + shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() } + } + + override fun updateShuffleState() { + when (MusicPlayerRemote.shuffleMode) { + MusicService.SHUFFLE_MODE_SHUFFLE -> shuffleButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + else -> shuffleButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + } + } + +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/flat/FlatPlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/flat/FlatPlayerFragment.kt new file mode 100644 index 00000000..4ace7ed4 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/flat/FlatPlayerFragment.kt @@ -0,0 +1,128 @@ +package code.name.monkey.retromusic.ui.fragments.player.flat + +import android.animation.ArgbEvaluator +import android.animation.ValueAnimator +import android.graphics.drawable.GradientDrawable +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.widget.Toolbar +import code.name.monkey.appthemehelper.util.ATHUtil +import code.name.monkey.appthemehelper.util.ColorUtil +import code.name.monkey.appthemehelper.util.MaterialValueHelper +import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.helper.MusicPlayerRemote +import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerFragment +import code.name.monkey.retromusic.ui.fragments.player.PlayerAlbumCoverFragment +import code.name.monkey.retromusic.util.PreferenceUtil +import code.name.monkey.retromusic.util.ViewUtil +import code.name.monkey.retromusic.views.DrawableGradient +import kotlinx.android.synthetic.main.fragment_flat_player.* + +class FlatPlayerFragment : AbsPlayerFragment(), PlayerAlbumCoverFragment.Callbacks { + override fun toolbarGet(): Toolbar { + return playerToolbar + } + + private var valueAnimator: ValueAnimator? = null + private lateinit var flatPlaybackControlsFragment: FlatPlaybackControlsFragment + private var lastColor: Int = 0 + override val paletteColor: Int + get() = lastColor + + private fun setUpSubFragments() { + flatPlaybackControlsFragment = childFragmentManager.findFragmentById(R.id.playbackControlsFragment) as FlatPlaybackControlsFragment + val playerAlbumCoverFragment = childFragmentManager.findFragmentById(R.id.playerAlbumCoverFragment) as PlayerAlbumCoverFragment + playerAlbumCoverFragment.setCallbacks(this) + } + + private fun setUpPlayerToolbar() { + playerToolbar.inflateMenu(R.menu.menu_player) + playerToolbar.setNavigationOnClickListener { _ -> activity!!.onBackPressed() } + playerToolbar.setOnMenuItemClickListener(this) + ToolbarContentTintHelper.colorizeToolbar(playerToolbar, ATHUtil.resolveColor(context, + R.attr.iconColor), activity) + } + + private fun colorize(i: Int) { + if (valueAnimator != null) { + valueAnimator!!.cancel() + } + + valueAnimator = ValueAnimator.ofObject(ArgbEvaluator(), android.R.color.transparent, i) + valueAnimator!!.addUpdateListener { animation -> + val drawable = DrawableGradient(GradientDrawable.Orientation.TOP_BOTTOM, + intArrayOf(animation.animatedValue as Int, android.R.color.transparent), 0) + colorGradientBackground.background = drawable + + } + valueAnimator!!.setDuration(ViewUtil.RETRO_MUSIC_ANIM_TIME.toLong()).start() + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.fragment_flat_player, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setUpPlayerToolbar() + setUpSubFragments() + + } + + override fun onShow() { + flatPlaybackControlsFragment.show() + } + + override fun onHide() { + flatPlaybackControlsFragment.hide() + onBackPressed() + } + + override fun onBackPressed(): Boolean { + return false + } + + override fun toolbarIconColor(): Int { + val isLight = ColorUtil.isColorLight(paletteColor) + return if (PreferenceUtil.getInstance().adaptiveColor) + MaterialValueHelper.getPrimaryTextColor(context, isLight) + else + ATHUtil.resolveColor(context, R.attr.iconColor) + } + + override fun onColorChanged(color: Int) { + lastColor = color + flatPlaybackControlsFragment.setDark(color) + callbacks!!.onPaletteColorChanged() + + val isLight = ColorUtil.isColorLight(color) + + //TransitionManager.beginDelayedTransition(mToolbar); + val iconColor = if (PreferenceUtil.getInstance().adaptiveColor) + MaterialValueHelper.getPrimaryTextColor(context, isLight) + else + ATHUtil.resolveColor(context, R.attr.iconColor) + ToolbarContentTintHelper.colorizeToolbar(playerToolbar, iconColor, activity) + if (PreferenceUtil.getInstance().adaptiveColor) { + colorize(color) + } + } + + + override fun onFavoriteToggled() { + toggleFavorite(MusicPlayerRemote.currentSong) + } + + + override fun toggleFavorite(song: Song) { + super.toggleFavorite(song) + if (song.id == MusicPlayerRemote.currentSong.id) { + updateIsFavorite() + } + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/MainSettingsFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/MainSettingsFragment.java deleted file mode 100644 index 20b352a4..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/MainSettingsFragment.java +++ /dev/null @@ -1,162 +0,0 @@ -package code.name.monkey.retromusic.ui.fragments.settings; - -import android.graphics.Bitmap; -import android.graphics.PorterDuff; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import java.io.File; -import java.util.Calendar; -import java.util.List; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.StringRes; -import androidx.appcompat.widget.AppCompatTextView; -import androidx.core.content.ContextCompat; -import androidx.fragment.app.Fragment; -import butterknife.BindView; -import butterknife.BindViews; -import butterknife.ButterKnife; -import butterknife.OnClick; -import butterknife.Unbinder; -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.ui.activities.SettingsActivity; -import code.name.monkey.retromusic.util.Compressor; -import code.name.monkey.retromusic.util.NavigationUtil; -import code.name.monkey.retromusic.util.PreferenceUtil; -import code.name.monkey.retromusic.views.CircularImageView; -import code.name.monkey.retromusic.views.IconImageView; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.CompositeDisposable; -import io.reactivex.schedulers.Schedulers; - -import static code.name.monkey.retromusic.Constants.USER_PROFILE; - -public class MainSettingsFragment extends Fragment { - - @BindViews({R.id.general_settings_icon, R.id.audio_settings_icon, - R.id.now_playing_settings_icon, R.id.personalize_settings_icon, - R.id.image_settings_icon, R.id.notification_settings_icon, R.id.other_settings_icon}) - List icons; - - @BindView(R.id.container) - ViewGroup container; - @BindView(R.id.user_image_bottom) - CircularImageView userImageBottom; - @BindView(R.id.title_welcome) - AppCompatTextView titleWelcome; - @BindView(R.id.text) - AppCompatTextView text; - private Unbinder unbinder; - private ButterKnife.Action apply = (view, index) -> { - //noinspection ConstantConditions - ((IconImageView) view).setColorFilter(ThemeStore.accentColor(getContext()), PorterDuff.Mode.SRC_IN); - }; - private CompositeDisposable disposable = new CompositeDisposable(); - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View layout = inflater.inflate(R.layout.fragment_main_settings, container, false); - unbinder = ButterKnife.bind(this, layout); - ButterKnife.apply(icons, apply); - return layout; - } - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - - text.setTextColor(ThemeStore.textColorSecondary(getContext())); - titleWelcome.setTextColor(ThemeStore.textColorPrimary(getContext())); - titleWelcome.setText(String.format("%s %s!", getTimeOfTheDay(), PreferenceUtil.getInstance().getUserName())); - loadImageFromStorage(); - } - - - @Override - public void onDestroyView() { - super.onDestroyView(); - disposable.clear(); - unbinder.unbind(); - } - - @OnClick({R.id.general_settings, R.id.audio_settings, R.id.now_playing_settings, - R.id.user_info_container, R.id.image_settings, R.id.personalize_settings, R.id.notification_settings, R.id.other_settings}) - public void onViewClicked(View view) { - switch (view.getId()) { - case R.id.general_settings: - inflateFragment(new ThemeSettingsFragment(), R.string.general_settings_title); - break; - case R.id.audio_settings: - inflateFragment(new AudioSettings(), R.string.pref_header_audio); - break; - case R.id.user_info_container: - NavigationUtil.goToUserInfo(getActivity()); - break; - case R.id.now_playing_settings: - inflateFragment(new NowPlayingSettingsFragment(), R.string.now_playing); - break; - case R.id.personalize_settings: - inflateFragment(new PersonaizeSettingsFragment(), R.string.personalize); - break; - case R.id.image_settings: - inflateFragment(new ImageSettingFragment(), R.string.pref_header_images); - break; - case R.id.notification_settings: - inflateFragment(new NotificationSettingsFragment(), R.string.notification); - break; - case R.id.other_settings: - inflateFragment(new OtherSettingsFragment(), R.string.others); - break; - } - } - - private void inflateFragment(Fragment fragment, @StringRes int title) { - if (getActivity() != null) { - ((SettingsActivity) getActivity()).setupFragment(fragment, title); - } - } - - private String getTimeOfTheDay() { - String message = getString(R.string.title_good_day); - Calendar c = Calendar.getInstance(); - int timeOfDay = c.get(Calendar.HOUR_OF_DAY); - - if (timeOfDay >= 0 && timeOfDay < 6) { - message = getString(R.string.title_good_night); - } else if (timeOfDay >= 6 && timeOfDay < 12) { - message = getString(R.string.title_good_morning); - } else if (timeOfDay >= 12 && timeOfDay < 16) { - message = getString(R.string.title_good_afternoon); - } else if (timeOfDay >= 16 && timeOfDay < 20) { - message = getString(R.string.title_good_evening); - } else if (timeOfDay >= 20 && timeOfDay < 24) { - message = getString(R.string.title_good_night); - } - return message; - } - - - private void loadImageFromStorage() { - //noinspection ConstantConditions - disposable.add(new Compressor(getContext()) - .setMaxHeight(300) - .setMaxWidth(300) - .setQuality(75) - .setCompressFormat(Bitmap.CompressFormat.WEBP) - .compressToBitmapAsFlowable( - new File(PreferenceUtil.getInstance().getProfileImage(),USER_PROFILE)) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(userImageBottom::setImageBitmap, - throwable -> userImageBottom.setImageDrawable(ContextCompat - .getDrawable(getContext(), R.drawable.ic_person_flat)))); - } - -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/MainSettingsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/MainSettingsFragment.kt new file mode 100644 index 00000000..8bafb141 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/MainSettingsFragment.kt @@ -0,0 +1,54 @@ +package code.name.monkey.retromusic.ui.fragments.settings + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import androidx.annotation.StringRes +import androidx.fragment.app.Fragment +import code.name.monkey.appthemehelper.ThemeStore +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.ui.activities.SettingsActivity +import kotlinx.android.synthetic.main.fragment_main_settings.* + +class MainSettingsFragment : Fragment(), View.OnClickListener { + override fun onClick(v: View) { + when (v.id) { + R.id.generalSettings -> inflateFragment(ThemeSettingsFragment(), R.string.general_settings_title) + R.id.audioSettings -> inflateFragment(AudioSettings(), R.string.pref_header_audio) + R.id.nowPlayingSettings -> inflateFragment(NowPlayingSettingsFragment(), R.string.now_playing) + R.id.personalizeSettings -> inflateFragment(PersonaizeSettingsFragment(), R.string.personalize) + R.id.imageSettings -> inflateFragment(ImageSettingFragment(), R.string.pref_header_images) + R.id.notificationSettings -> inflateFragment(NotificationSettingsFragment(), R.string.notification) + R.id.otherSettings -> inflateFragment(OtherSettingsFragment(), R.string.others) + } + } + + private val settingsIcons = arrayOf(R.id.general_settings_icon, R.id.audio_settings_icon, R.id.now_playing_settings_icon, R.id.personalize_settings_icon, R.id.image_settings_icon, R.id.notification_settings_icon, R.id.other_settings_icon) + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.fragment_main_settings, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + settingsIcons.forEach { + view.findViewById(it).setColorFilter(ThemeStore.accentColor(context!!)) + } + generalSettings.setOnClickListener(this) + audioSettings.setOnClickListener(this) + nowPlayingSettings.setOnClickListener(this) + personalizeSettings.setOnClickListener(this) + imageSettings.setOnClickListener(this) + notificationSettings.setOnClickListener(this) + otherSettings.setOnClickListener(this) + } + + private fun inflateFragment(fragment: Fragment, @StringRes title: Int) { + if (activity != null) { + (activity as SettingsActivity).setupFragment(fragment, title) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/NowPlayingSettingsFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/NowPlayingSettingsFragment.java index ee67454f..74da5bfe 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/NowPlayingSettingsFragment.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/NowPlayingSettingsFragment.java @@ -7,7 +7,7 @@ import android.view.View; import androidx.annotation.NonNull; import androidx.preference.TwoStatePreference; import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.RetroApplication; +import code.name.monkey.retromusic.App; import code.name.monkey.retromusic.util.PreferenceUtil; /** @@ -25,7 +25,7 @@ public class NowPlayingSettingsFragment extends AbsSettingsFragment implements final TwoStatePreference carouselEffect = (TwoStatePreference) findPreference("carousel_effect"); carouselEffect.setOnPreferenceChangeListener((preference, newValue) -> { - if ((Boolean) newValue && !RetroApplication.Companion.isProVersion()) { + if ((Boolean) newValue && !App.Companion.isProVersion()) { showProToastAndNavigate(getActivity().getString(R.string.pref_title_toggle_carousel_effect)); return false; } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/PersonaizeSettingsFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/PersonaizeSettingsFragment.java index 39dc558d..879c0a1a 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/PersonaizeSettingsFragment.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/PersonaizeSettingsFragment.java @@ -7,7 +7,7 @@ import android.view.View; import androidx.annotation.NonNull; import androidx.preference.TwoStatePreference; import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.RetroApplication; +import code.name.monkey.retromusic.App; import code.name.monkey.retromusic.util.PreferenceUtil; public class PersonaizeSettingsFragment extends AbsSettingsFragment implements SharedPreferences.OnSharedPreferenceChangeListener { @@ -16,7 +16,7 @@ public class PersonaizeSettingsFragment extends AbsSettingsFragment implements S public void invalidateSettings() { final TwoStatePreference cornerWindow = (TwoStatePreference) findPreference("corner_window"); cornerWindow.setOnPreferenceChangeListener((preference, newValue) -> { - if ((Boolean) newValue && !RetroApplication.Companion.isProVersion()) { + if ((Boolean) newValue && !App.Companion.isProVersion()) { showProToastAndNavigate(getActivity().getString(R.string.pref_title_round_corners)); return false; } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/ThemeSettingsFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/ThemeSettingsFragment.java index 2a4e0095..60e4f8af 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/ThemeSettingsFragment.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/ThemeSettingsFragment.java @@ -14,7 +14,7 @@ import code.name.monkey.appthemehelper.common.prefs.supportv7.ATEColorPreference import code.name.monkey.appthemehelper.util.ColorUtil; import code.name.monkey.appthemehelper.util.VersionUtils; import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.RetroApplication; +import code.name.monkey.retromusic.App; import code.name.monkey.retromusic.ui.activities.SettingsActivity; import code.name.monkey.retromusic.util.PreferenceUtil; @@ -48,7 +48,7 @@ public class ThemeSettingsFragment extends AbsSettingsFragment { generalTheme.setOnPreferenceChangeListener((preference, newValue) -> { String theme = (String) newValue; - if (theme.equals("color") && !RetroApplication.Companion.isProVersion()) { + if (theme.equals("color") && !App.Companion.isProVersion()) { primaryColorPref.setVisible(false); showProToastAndNavigate("Color theme"); return false; diff --git a/app/src/main/java/code/name/monkey/retromusic/util/CustomArtistImageUtil.java b/app/src/main/java/code/name/monkey/retromusic/util/CustomArtistImageUtil.java index 34406ea7..462630f5 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/CustomArtistImageUtil.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/CustomArtistImageUtil.java @@ -22,7 +22,7 @@ import java.io.OutputStream; import java.util.Locale; import androidx.annotation.NonNull; -import code.name.monkey.retromusic.RetroApplication; +import code.name.monkey.retromusic.App; import code.name.monkey.retromusic.model.Artist; @@ -55,12 +55,12 @@ public class CustomArtistImageUtil { } public static File getFile(Artist artist) { - File dir = new File(RetroApplication.Companion.getInstance().getFilesDir(), FOLDER_NAME); + File dir = new File(App.Companion.getInstance().getFilesDir(), FOLDER_NAME); return new File(dir, getFileName(artist)); } public void setCustomArtistImage(final Artist artist, Uri uri) { - Glide.with(RetroApplication.Companion.getInstance()) + Glide.with(App.Companion.getInstance()) .load(uri) .asBitmap() .diskCacheStrategy(DiskCacheStrategy.NONE) @@ -70,7 +70,7 @@ public class CustomArtistImageUtil { public void onLoadFailed(Exception e, Drawable errorDrawable) { super.onLoadFailed(e, errorDrawable); e.printStackTrace(); - Toast.makeText(RetroApplication.Companion.getInstance(), e.toString(), Toast.LENGTH_LONG).show(); + Toast.makeText(App.Companion.getInstance(), e.toString(), Toast.LENGTH_LONG).show(); } @SuppressLint("StaticFieldLeak") @@ -80,7 +80,7 @@ public class CustomArtistImageUtil { @SuppressLint("ApplySharedPref") @Override protected Void doInBackground(Void... params) { - File dir = new File(RetroApplication.Companion.getInstance().getFilesDir(), FOLDER_NAME); + File dir = new File(App.Companion.getInstance().getFilesDir(), FOLDER_NAME); if (!dir.exists()) { if (!dir.mkdirs()) { // create the folder return null; @@ -94,13 +94,13 @@ public class CustomArtistImageUtil { succesful = ImageUtil.resizeBitmap(resource, 2048).compress(Bitmap.CompressFormat.JPEG, 100, os); os.close(); } catch (IOException e) { - Toast.makeText(RetroApplication.Companion.getInstance(), e.toString(), Toast.LENGTH_LONG).show(); + Toast.makeText(App.Companion.getInstance(), e.toString(), Toast.LENGTH_LONG).show(); } if (succesful) { mPreferences.edit().putBoolean(getFileName(artist), true).commit(); - ArtistSignatureUtil.getInstance(RetroApplication.Companion.getInstance()).updateArtistSignature(artist.getName()); - RetroApplication.Companion.getInstance().getContentResolver().notifyChange(Uri.parse("content://media"), null); // trigger media store changed to force artist image reload + ArtistSignatureUtil.getInstance(App.Companion.getInstance()).updateArtistSignature(artist.getName()); + App.Companion.getInstance().getContentResolver().notifyChange(Uri.parse("content://media"), null); // trigger media store changed to force artist image reload } return null; } @@ -116,8 +116,8 @@ public class CustomArtistImageUtil { @Override protected Void doInBackground(Void... params) { mPreferences.edit().putBoolean(getFileName(artist), false).commit(); - ArtistSignatureUtil.getInstance(RetroApplication.Companion.getInstance()).updateArtistSignature(artist.getName()); - RetroApplication.Companion.getInstance().getContentResolver().notifyChange(Uri.parse("content://media"), null); // trigger media store changed to force artist image reload + ArtistSignatureUtil.getInstance(App.Companion.getInstance()).updateArtistSignature(artist.getName()); + App.Companion.getInstance().getContentResolver().notifyChange(Uri.parse("content://media"), null); // trigger media store changed to force artist image reload File file = getFile(artist); if (!file.exists()) { diff --git a/app/src/main/java/code/name/monkey/retromusic/util/NavigationUtil.java b/app/src/main/java/code/name/monkey/retromusic/util/NavigationUtil.java index 44ad3fcb..7389773f 100755 --- a/app/src/main/java/code/name/monkey/retromusic/util/NavigationUtil.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/NavigationUtil.java @@ -34,7 +34,6 @@ import code.name.monkey.retromusic.ui.activities.UserInfoActivity; import code.name.monkey.retromusic.ui.activities.WhatsNewActivity; import static code.name.monkey.retromusic.Constants.RATE_ON_GOOGLE_PLAY; -import static code.name.monkey.retromusic.ui.activities.GenreDetailsActivity.EXTRA_GENRE_ID; import static code.name.monkey.retromusic.util.RetroUtil.openUrl; @@ -60,7 +59,7 @@ public class NavigationUtil { public static void goToPlaylistNew(@NonNull Activity activity, Playlist playlist) { Intent intent = new Intent(activity, PlaylistDetailActivity.class); - intent.putExtra(PlaylistDetailActivity.EXTRA_PLAYLIST, playlist); + intent.putExtra(PlaylistDetailActivity.Companion.getEXTRA_PLAYLIST(), playlist); ActivityCompat.startActivity(activity, intent, null); } @@ -102,7 +101,7 @@ public class NavigationUtil { public static void goToGenre(@NonNull Activity activity, @NonNull Genre genre) { Intent intent = new Intent(activity, GenreDetailsActivity.class); - intent.putExtra(EXTRA_GENRE_ID, genre); + intent.putExtra(GenreDetailsActivity.EXTRA_GENRE_ID, genre); ActivityCompat.startActivity(activity, intent, null); } diff --git a/app/src/main/java/code/name/monkey/retromusic/util/PreferenceUtil.java b/app/src/main/java/code/name/monkey/retromusic/util/PreferenceUtil.java index 9b93568b..4b8e93d7 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/PreferenceUtil.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/PreferenceUtil.java @@ -21,8 +21,8 @@ import androidx.annotation.LayoutRes; import androidx.annotation.NonNull; import androidx.annotation.StyleRes; import androidx.viewpager.widget.ViewPager; +import code.name.monkey.retromusic.App; import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.RetroApplication; import code.name.monkey.retromusic.helper.SortOrder; import code.name.monkey.retromusic.model.CategoryInfo; import code.name.monkey.retromusic.transform.CascadingPageTransformer; @@ -122,7 +122,7 @@ public final class PreferenceUtil { public static PreferenceUtil getInstance() { if (sInstance == null) { - sInstance = new PreferenceUtil(RetroApplication.Companion.getContext()); + sInstance = new PreferenceUtil(App.Companion.getContext()); } return sInstance; } @@ -279,7 +279,7 @@ public final class PreferenceUtil { public final int getLastPage() { - return mPreferences.getInt(LAST_PAGE, R.id.action_home); + return mPreferences.getInt(LAST_PAGE, R.id.action_song); } public void setLastPage(final int value) { diff --git a/app/src/main/java/code/name/monkey/retromusic/util/RetroUtil.java b/app/src/main/java/code/name/monkey/retromusic/util/RetroUtil.java index 01bfe30b..a37f0233 100755 --- a/app/src/main/java/code/name/monkey/retromusic/util/RetroUtil.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/RetroUtil.java @@ -46,7 +46,7 @@ import androidx.core.content.ContextCompat; import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat; import code.name.monkey.appthemehelper.ThemeStore; import code.name.monkey.appthemehelper.util.TintHelper; -import code.name.monkey.retromusic.RetroApplication; +import code.name.monkey.retromusic.App; public class RetroUtil { @@ -84,11 +84,11 @@ public class RetroUtil { } public static boolean isTablet() { - return RetroApplication.Companion.getContext().getResources().getConfiguration().smallestScreenWidthDp >= 600; + return App.Companion.getContext().getResources().getConfiguration().smallestScreenWidthDp >= 600; } public static boolean isLandscape() { - return RetroApplication.Companion.getContext().getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; + return App.Companion.getContext().getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; } @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) @@ -199,8 +199,8 @@ public class RetroUtil { public static Drawable getTintedDrawable(@DrawableRes int id) { return TintHelper - .createTintedDrawable(ContextCompat.getDrawable(RetroApplication.Companion.getInstance(), id), - ThemeStore.accentColor(RetroApplication.Companion.getInstance())); + .createTintedDrawable(ContextCompat.getDrawable(App.Companion.getInstance(), id), + ThemeStore.accentColor(App.Companion.getInstance())); } public static Bitmap createBitmap(Drawable drawable, float sizeMultiplier) { @@ -297,9 +297,9 @@ public class RetroUtil { public static int getStatusBarHeight() { int result = 0; - int resourceId = RetroApplication.Companion.getContext().getResources().getIdentifier("status_bar_height", "dimen", "android"); + int resourceId = App.Companion.getContext().getResources().getIdentifier("status_bar_height", "dimen", "android"); if (resourceId > 0) { - result = RetroApplication.Companion.getContext().getResources().getDimensionPixelSize(resourceId); + result = App.Companion.getContext().getResources().getDimensionPixelSize(resourceId); } return result; } @@ -337,9 +337,9 @@ public class RetroUtil { public static int getNavigationBarHeight(Activity activity) { /* int result = 0; - int resourceId = RetroApplication.getContext().getResources().getIdentifier("navigation_bar_height", "dimen", "android"); + int resourceId = App.getContext().getResources().getIdentifier("navigation_bar_height", "dimen", "android"); if (resourceId > 0) { - result = RetroApplication.getContext().getResources().getDimensionPixelSize(resourceId); + result = App.getContext().getResources().getDimensionPixelSize(resourceId); } return result;*/ DisplayMetrics metrics = new DisplayMetrics(); @@ -415,7 +415,7 @@ public class RetroUtil { } public static boolean checkNavigationBarHeight() { - Resources resources = RetroApplication.Companion.getContext().getResources(); + Resources resources = App.Companion.getContext().getResources(); int orientation = resources.getConfiguration().orientation; if (!hasNavBar(resources)) { return false; diff --git a/app/src/main/java/code/name/monkey/retromusic/util/SystemUtils.java b/app/src/main/java/code/name/monkey/retromusic/util/SystemUtils.java index 2d4b7199..5e4b6072 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/SystemUtils.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/SystemUtils.java @@ -7,7 +7,7 @@ import android.content.res.Resources; import android.util.DisplayMetrics; import android.view.ViewGroup; -import code.name.monkey.retromusic.RetroApplication; +import code.name.monkey.retromusic.App; public class SystemUtils { @@ -38,9 +38,9 @@ public class SystemUtils { public static int getNavigationBarHeight() { int result = 0; - int resourceId = RetroApplication.Companion.getContext().getResources().getIdentifier("navigation_bar_height", "dimen", "android"); + int resourceId = App.Companion.getContext().getResources().getIdentifier("navigation_bar_height", "dimen", "android"); if (resourceId > 0) { - result = RetroApplication.Companion.getContext().getResources().getDimensionPixelSize(resourceId); + result = App.Companion.getContext().getResources().getDimensionPixelSize(resourceId); } return result; } diff --git a/app/src/main/java/code/name/monkey/retromusic/views/MaterialButtonTextColor.java b/app/src/main/java/code/name/monkey/retromusic/views/MaterialButtonTextColor.java new file mode 100644 index 00000000..b5ff0287 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/views/MaterialButtonTextColor.java @@ -0,0 +1,25 @@ +package code.name.monkey.retromusic.views; + +import android.content.Context; +import android.util.AttributeSet; + +import com.google.android.material.button.MaterialButton; + +import code.name.monkey.appthemehelper.ThemeStore; +import code.name.monkey.appthemehelper.util.ColorUtil; +import code.name.monkey.appthemehelper.util.MaterialValueHelper; + +public class MaterialButtonTextColor extends MaterialButton { + public MaterialButtonTextColor(Context context) { + this(context, null); + } + + public MaterialButtonTextColor(Context context, AttributeSet attrs) { + this(context, attrs, -1); + } + + public MaterialButtonTextColor(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + setTextColor(MaterialValueHelper.getPrimaryTextColor(getContext(), ColorUtil.isColorLight(ThemeStore.primaryColor(getContext())))); + } +} diff --git a/app/src/main/res/drawable/navigation_item.xml b/app/src/main/res/drawable/navigation_item.xml new file mode 100644 index 00000000..4238c61c --- /dev/null +++ b/app/src/main/res/drawable/navigation_item.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/navigation_view_item.xml b/app/src/main/res/drawable/navigation_view_item.xml new file mode 100644 index 00000000..9e3c2556 --- /dev/null +++ b/app/src/main/res/drawable/navigation_view_item.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/font/circular_std_black.otf b/app/src/main/res/font/circular_std_black.otf deleted file mode 100755 index c62b210c51fc6b0a7e4022ac9dd54d50cf5942a3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 74500 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�$ENekd=fCSx(51l~{o+rz#-JDFw1p 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%0D3`R&67{=)GDS8?p!cah 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 DB}TqP diff --git a/app/src/main/res/font/circular_std_book.otf b/app/src/main/res/font/circular_std_book.otf deleted file mode 100755 index 3a1f1ad82ef9ee891d7977cd5f6736404cb3afa1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68940 zcmc$_2Urx#^C;T0yR(C{xGL+Svdr!Rf{K!rBnA*MD+Z1trUjOuBw-gsQPE>g=a_TC ztO(|u69x>J6$y%{7*FoOM!hw&= z>e?+VOds*O@D4(@#}JC^9y+*J4}_2ep~(;-s~+J42G8xf@FnD}5SrPt$KZ&N>*w!0 zMwFx-LJIwW!5-cN0tSpni1LK?V`7pGY4%N@Jw%9ojF4g) z2*TGI1=Xh|NQQdWr=>`T`qrmqs0kWZpQcd@lvAIUqsC}?eVRcIXj^^S3dzxx`g8-- z2)JSyOM%p=v_5T(oX~Ie>4sD&rK(RWk%wexeOmpmacvPJnFXtfk~ILVCrB37rzzwp zrR&oYq?ER*PfJl}X@~l>4C$pA^=TS;OPAKC<^L*YP>A$$eYq8~mNE6|21qMY)u$E6 zRpwQnwnkyHsQPq6$sF0{`m_>_V%pTF8>8mT?D}*QWW{W(Ppkihv#o%04`Yfk-C#1t z>JwA+-kx4Q`si$ZSZa!?uOV7L*kq1POm)|HOHIp8PmE76L8G`-eVQ>PHZdjMy<1{> zj5*1W4((z?Qd7tEO-wPGQUmq>E_V|u0!7IGF4hahKN-_A5>r$3UhbZrULEz(=ES5} zy{D(A_xF|nSM2{iuTRX-8}z1hL##2`kUmbI8mG66S^qVd(~T(;^&#oz6qr?l$&?o8 z;gOY<<(?#B<{p!p{NEOO3`sDy4K-#Y#zVJ^zJ{zgbCTYiVH5^1B^dQ_K#vT4YPy(- zHJA+g7$`9S&{%y|qA3AL_q~}uE-}fNp*K#5NixSmA~`iSF)kaZ)lQ#oG-PBLlcNC{ zF$bGpOl6o-(;=Y;N~D>gHYO=sZ^$$xCK;lWjQR{?dL|$xbdOEUFeOHtO=4?9N-WeR z3BcU-!715#b4t?xgg8*kl%19upKeG?NQ}{Eq{f-D4CzKdIVnjWZPcfwr)C&q%)oqi zV0pUU5F49l0)`lpL_Vh{8-yH;VrQ!T=?DG#JumXrBn>?vU>W zaT>Hthmv@d0JRoKaZs*@8etrvbqdtFL+eCn83PcK0A6}MJYn<@XrBs4&>-j$|D(RI z*wYA5Q&Awa`d{^T`_W%$^?eTib87+jSoEK^KZz8`fH8zQ>S5mQFneMC9U&JDSR?|T z0(L^FC&2xmAqsQNfO-Q2lSs{2fSD}LZ5))PqB!{edlvsSZGbV-MH)?nb|H{9111*E z2(&Z-1p|=>{IbNKJK!VG(n1k;pkykH_J7{e0}X-JM${IC!dIAieEnz{B8Rg8Ux7Dz z_+~(h?=vuoz4TCPSq;LRQ$_xLuZe{@n?yPYI0-ApP>-oVzbr@#EiG&8d!O&n1h@jF z1&s9YodEMn0t^IN3MI(^QCLH9u+A(isU4IGbTdG`fM+sr%R-rW^t~9;hXVe1OX=jYNEJS1pa@AZ2;K9 zyoG*AKQI&aOEA<3qX>K!M)==R+_JVz&^`^YjR*J!NC{Mo5n&6f%LEXFog`o>P+1sF zSi3?#4a!qR9E2Tb+2;Zd!k!lPo&j(a<{->KpoT&0E6hRQOgd0c;OY0>A3 zVqPWz84}F{X-p1;KtPTu=o{rlg;0Yj12ut~LM@RLNv=uCq|2ncq^G3k^j3O>zNy|$ zuhl#1UG$#%Kz)clR6kIkrq9;T)#p2?92^~791$9Da3J0l4h@flCv>rNs|i zVntjwIUjJo{sWiXN<=_H1zf0ae}Aj`cH!INZ_~eJe2f1!@LRucfnS-g@-O_CkT33E z?7y_AyjNLLdA+iz@=E39$_tgpEB9CKuH03*zH(}1PGx50kcy8Ll@(bDhBOGy)(Hl4}r=yu@ zCAti6nle<5UZ5&efhy5)sslQYuAvXWw72LT)syOlUcqtv9KA#}Xa-tO_s}hP=Xne7PWbz536viiPGzFj zk{8HZG8o=2UdT^!94LGZwT5~4K|s_oTS5p$K2#N=C7#Gl(jDM9qCP;GKGX}S8;F91nEHyGq=8U>9I=w&Xt?Afr0+o7 z9YQMzUm*ViaBB`}Thvst4&s(*xby(T1Y$?1kEoBwLD@j$A_)gr8&J4pF=D0fQ6otr z;hBVBQ0vj}6eEF9a=WLk)%TLIA!%t1qwyzkd5& z(h$;LQMfb`+CsS`2-*t$g!!ey8~PH=DF*ua!aV{T5C#DszXF~D-T#{)lK_4K zF9bgPD^Sh9-4l2L@k@vmlKaT!J0ApI{5uG=6!|0Y;@?5wML59qgK~lH|4opNg>r!x zz=wYa^yOOx@B;Yp?FGbApkeDDd=Pl??*MC6%mXh3e*6f)3z26K3$P>}h!)m^EC=x4 zCGyin;Ny=qBk=9{w-?kjps5f9x{n3k6#$={A+A9VR5sE9|D7bT_oVhfH-QK75XS+3 zn<4Q}G93lW2B1FZDV%zhmH>Qf{Ub~Vc)OvGD+-ftMZ;w};89bgh0s*m4Pb+oirNAE z-wk{|17R72JP4a1tbwozf>7rH{QkZk{uk{e`(e$jguNGqv;w}=XIK*w--vL^FgRls~T?iG>)(ryaps4exHN=k8`ETEVZ+=qx+g%}+)B!z%zkQW5 zz{@*;KmY9?X-^a(h4VxP>rWDc+y#0`3dObHCQ-wDmx=TWldJ~Z8~`t0Scd`7SJ)d` zOAywzu>Pg1QHmJ41I-}-eVn9Spj-$-{dL$If1ws)TFC$Jf;)1O`9qt-fa_A&7b74H z2fDAa1c8D7Eo~EP;QGN;mi!93g9Lq zXqaRdlnLd+eir(bL99nTsl^b70{xCbybNh1QAk6rN6vtohFXof!5NGoSV3qF!3IKm z2xlQ&h2RJw5kdzDS_sY%gmx|v93Z$s&_K8W;W~ug5WMQ+E)e@d5ZWmv?y&AXkv)XY z5d0wcKoI&0;|rk)gtibuAqZo6LJ;8fhI#ryK;qpZoYSx;#q$!*O7VVz^G~dUb4<85 zBq^|tI>I`x-y^U--6TB#@Bc-hdIN6TpnVCftGytX+CS6V?hd-fTn`%vk;_%wIK8C0{P}BNHdo~ zb9xUwLggTFyhXnw9$pi2sv*^s(ol|68_JXNqdHUFss2<1HJVDGQmIUk>t|5&sU_5E zY7@1CDxeNi=cvn6G4+6YMpaR7sXwUC5?azwVk>c!w2^p90wi4|VUm85A(G*eF_L&m zio`6*k^BtW=f#p$l8ut>lD(2cl9Q4P@K(4Zc_b;9R7*Ze{*io@T1lHo?WB&v`BhWdQbX9S|P2LzLWkg#nLY_ zsjPvlkxVVq$Q)#?WNtDaS%9pItcPrfY@{q+mLkiPO_j}(Es!mdt&we(?UWVBj>yi) zF3WDpp2#X?Z)AVSYH1nWh*r}&x*hFLccc5#5%g#}kAdKX~%S6x-z|( zzRW=8Cnk~^!Hi=jGc%cAnbpi@rhqxZoMEmrCCn4%4fB_k%u4N^VopjjrzC>%G*pa& zJuQ)#^X?WP)5%S)io_?0d-x7t0QG0JO>fj|tegZgePd@(-V zuO+f{_p@~O6M%br`dd2qTe|yOfcg7a>MT(FEl~U|Q2Z@W{4G%Y1G<~jQ-#)EUOoeo zjqwIC?JGj{@)e2D?;`16>;ECPt#xTTI2KW~GQN11z8eET97{paU$RMbdhEg;?+mvEUhE z!862yXNU#QZoV*pDbbJw>fyLhW0J`L+Spigj48vMY|*<4gL!+2)A9BS0G)SYi~+R0 zafvC3CQvAwjG)&|GNeJb3C8heLsCkrNsK}LoFOLSLE&sNriv^0%Rc;Ad9gGSxAYH#e@J^NC=R{ScEL3ERcni2wAL5GsGC(Gnc^(;f?)+d2w;;^ zg@M6DlK4F>tYs(?moUWjD_Ur5!6*aPw`c)@5_38je`2zsG8^k|{AQN$3-sn3)(WpaHGHz5)qYcEZ{CBjpb%poJ+R)tmvw7sv@` z6hz;l{cA5m>G$mj$saos(*NF+kcC6Qm?9iH?lFc8qnHzq95I`aot6Mo5o)4g35_$F zezdI5|EH}`VEixD#{X0b)+j@=aDs`z;=$0CWK2%|4>*{sr)O~Jf35vb|M6xcSe{Z- zen0>NpdlkcY;H{ckrdzx#vcG)KcOEb|AE!RJtM=@%OHHc4RD0P!Vr@N!4PFh3P+DQ z8OS!lk~e`}u)Zob5e~GB#0*P?UB85?kPQ40c>}rj!g7STL%J~@>}Jp{R>=Cs z7~lYLHzb+FWo(EMR+5-BnbT9^M#sUw(TTz*NsQrqt{*YQat(aXnq%B8C6+muV}3L= zgO@;zFo-$EGAVP+zh(p(5d(8fbUJJVXar}o1r11xGlE+{oUu6u4(xbCx;Z(?U^YSR z58NSbPKkw$DQrbc=kHRZV2l;5tLT&<09u3UEesT3lhA7P485XsR4lcU+6$st3H5?{ zL;X$h5*rC8@sNy=Op#!zOsbW7OGBhXq-oN7GGAG^Y?5q-Y_)8gtd4F-+tbM)+N`Iy z(YxutMwH>L*@!^ATaMbTvivyRDQ@-QcmBy-@RbQ(pt5~afR?DpRTRpI{50$yM;Z%USO}X zx7qvbE0$Lv1+7ph)C!%#L(xgmR}rO%Rg6=lD$I&(#caiL#cIWNMZRLc;)vpe;)>#~ zqEu0(_@t;+e6yyl8(V9vU97#VJ6H!>q*u#t$(pzX}#8ZtMy*% zBi3iFuUKEVzGMBs`l)rf^$XnSDCfX-vUT7TZd^N7t4LUsy>P1bwS4Z%*;^0W;h9X$ z;4;$E$-j@sG;MVzeR1)=LpL?|cgK#H_;XImY#p&pVioPUR<^OMi4Elx*a08Oe(O42x1Wb4_0eQz{<17f zhr8oob@W(MeEhh)Lx=X{6&B{3qP0p_PW65Or}`_IRgYM8e$+k9o3oei-j6&UoR<1? zR<k(WWbFKdhOaxE4FL)@63#jO*O~Hr*1Bou2ZTnEW2~_=CbY)5!1tZ z^_qU?ytb2V)UpxV;G{{ZkjMY#XKQFN7~Cqn>Oy+xn_4;2IyT>mz$}_Qch~ z^u1%&>J)#=Nx@l`&=7rzjAn^9@0P?V)L|^?M;f;y&EePhE$*jPkkQzebi||ZXzY(W z;?at++s5bTZ`+oie}Ef1e*9Rif`4M$mQ(N!Be->Q_6D%zBG9%iix1pUckEt-WmsBL zR9adxfJlj~`+$x*MV8H8R#oi8syr2Z@HkfWYX@!^X#9V_ zji7gJ+p{KLvvJeR+)cU@<7w5Vtb#q$@->&w>^iqkrwAnt6(Sgqb1+2H81155R4-${6<5Sk^{W>A|U*EBK%c7N@@ zV~CkzPSkaMG9dh6XWEoF*)Ynk-1hP1^Y`vujOg5Ha74Fm1J6IvD%A^S%%3^aE_O^pK8r&QDuoJ$Ao^>DTM$uARMF>$;NuC3jKgM7zGm-t9ZaUbu2#L*d>$ z?a^KIm4jPO-?UpbfBC$X+D+I|U6`^nK0Y}mK7LnnVd2hQg)js;-3v#`=M&Z?n+(^M z$=Bn7zm*qku~K4`!C~_o)qA|LcAPqn3-tDVxs!d&+&r=-KaSOQWwBGgOjfIUmcmWv zZf9Z_cUD1~;lUUJ?Jg%R$Y2bCR^VwQ3^ylXBj8Bl5Aes-k0qj;@~?&{2`mUQ)ERSk0$1-OZN$kL0x zxVZ)^EBa-zfH{<&B*-W6s<;+69Mp9Y4C<>I5@*X6IpqwHvfI^}xOz z+w--GEH(@7hV$yh>o)$fQFG&Vyl?NQA>N((oqs-C*VarsEUs*WU4dm4A2EenINoTd z!~JjOV#^B%)u|swExXd}Ei<8s+F2Dcw z;L)Fd20wCv<343k`odi8EBQP{B&)ov&RUoT*=qS*1xfmut^TCCx=1(|Z+t3xbIqn` z4y$^L4GK6&6zWAwS1eqv*|BzVw#hUtBXRtuRa13wqv(u{2jSG_lHzIdwGDog}3zjHoY%RwPpr41_ zw@=^=TDd=YmoY9m*{D?#>b4)2T`bBwy-lkq#x^&uU8jo5G517@-zaJ+bs~oyY!f*} zp4zabV zm@Ah)QEbn7YRKe$N)^ZOKWbvY0VDt$;1`I+m;~ZLVt`)&fmo{;Id<&mF^T!dwTk6z zohJ@a^Pa?+u3$*fpSb8xNYr@}7h1XTO2_=>)05Ih_Md8nijw~tvK|17_G_O+jG zIj^vK-o=Me+{E*5QbkX&D@;kN$i#Ec-p-=1{bMOkRN%n*)%>?CYQC)2RZjMtWgX>b zh)j(m>VBc?RB|P6tL}`u6T32Ohr=Gj8Dt+o>>1LXEK>*Zy`C~kLw@3sBYXEAK9ab1 z%$Tu>V_<-L#drb?F!!;vNTI&=%gL)xHHuA{yg5s8Jf|GHJi?920jF8T6y6RPg(+|_ z8KlNbnK9cgCOpt!hjJ{%PFUX=O9^bTGBr#;nr_G7V&X<1K$7>w23lFA_Thc1h$nyP zSGJC3l=w={Mov-7s5d1KTNAD!1Bo-?$V@U8t2^MvSfzOW@XpJsu6H^po({-7_H<#E zjbh27JKQ`)CE3j2b@&!tR@cxMuVTnLa*IC7;!<@G9zY_?a3l_YO2YBb05X(O)~eKT ztZqLWsL14PvM9yw&8BhV%%-HIjJ!RH+D&6&5%OmDIx`3s-C&b{R6nkZ>CEskWT^US zT}%+8B!_G8W#*qj<%Cf@>zjF^bP-@e!bq5+tYFB9mAcA8`hHfgQqojII7bC8gdL+$ z6MNo|#@pmdPolIVipTA+EKBRgw8hWV3ZlXD$|!sX&s)!_%gC0atj)o9IRCYxn!i}B zU`XTZ{cr>As)tkL!5Mq*>lDoWEqfk_dD5USNKiUh6lZTzxK@EX|H47W3J|JN6Fr}V z^;JL~t4DBSt<6_p)ia{PZ30Leq6!3p;x=l$t1c#hZzK?g`SaStKXq=a=^?4ZdT15e zjJe=asEJ|D7Zp4{ukgXPbxFk5M^Ti?&j7M*hjStmvoO+^NJhcjiaFY6rS`_kgGX=K zDVUxIN8gyL?K3rIbWc0Q_-$hq#EG0O)$!^jJ9N80ZUr=Yg5(ql(C1?iPi*HHVLd^dilcz5aE^Q!aR z+G>^Zc+!zpl;f7riGRJCF> zSFMoa_H{|Lf~e(!1h=gMx302LBm^(eeppAB&Q@$%!@lA@0K&~oJhB{*fYh0%xW{8` z52;Dd@ysW?aO9QwuQ1cMlh6IoC%?NxE zz`+9S4T2{=__}8xlnx&FNbpqn1-z^gIAMTA0vs{m=?Z35w1)!I1o&Wp69xh}`w{qH zpaT+g9HAo;v<86-1=uanNeZ1s;E(}66=2jrmk>HH0c!?0K7bbln0L@s3fv#ibx-z~}*<;^;92&t)*nqKyb#Lcp^El}o_?0!$=e5do(P@UcKk z5cpPr;RKyS;GTi5P~d(69w6X)1s)$1%z7FH&KTfV0wxx)u}}--;JyY1-@%2n*yg1up@zKhFYeA7dy4n0h~~%>kcTHqTVm09t%%;C*%UwoOa;j zVnF#+05zP#5=PQdGG4MxQV2GZ*OK3*66pl#60m~o1N+Afc!RV6ONYBGR5ndETecKz z8ei!G`jA{L?A z(G8Lt%xtg>9^!W!R5p0q;2S%Tox>Kh580232*pgrGR0NJU)Bw*qph>7bFDX8|Jkrj z!+?g_4d*r7-mp~JMA=N~qzqApE7O!Gl&6)qmA^OgZq&I^uSR1UeQs>sctqn-jpG_; zHlEPqqns_$xZZfb*RFjcSj7=6dxzOa3jm$=2qq4EH>0~q7 zrqJfK%2DO2@>T_^hN(uY;#DcC1*+q!`>JQEm#X(oF%ZnnjUXj*%Yh& z)fd&*)pykuwo+SL+jh3+nkk#vHshMLZPvZnPtD?+Wj9;WoNC^(xodOp=AE0zG@sOb zMGJKcu0_ih-Yo{Th-s1AVtI=VEpE2p?K<1dvCFeNYgcCXheoMUYaBIR8XrxJX1Zp% z=7y%)US==1x3XvL2iOm?kFXzRpI|@1ezyH;`@Qz(>~GjVwa1(T7sid|4sfTqE8Iu! zv$nCeh1OBqR@+|NNgJjepdG3mt&P{FYBRNywKKH~wac~ZbUNJ}U5W0V?oYjqzKedW z{#X53{WphjhfMI(JLYKX7~#0evB2@R<0q#^PVP?qosyjvINf&o&{EaXujSB|*)6v^ zE1erVdpHksHaKTGPjJq2QMrV=^l=&QlI1elWva^wmuoH`T>fdLXyxB3x>a(k=dDp| z@76tAN4K8X`j^%_TVHPdxDC~YYtymKK36MOSJ!r~?yjD$-mV>7JG=I94R;;x8snPm zn&~>#b&l&|*VV3huDe|ix}I=7?|RL(#Px}5rE8UIbzAqgDQ(ZUYuK(wyY21Z7IO1+ zi*n0wD|Y+h_RT%qeS`Z04=<1L9tS*bdVKNp@Qm=B=sC&riq}A|IbMssioB}4yL$KX zj`TKqXL#p$@A1Cseck(+H}(ng3HIshGsq{y=YY>?pQ83-+NZXk(0+RR4ehtK-`0M6 z2iFdJI~?wCvO{Hu>JD#x?R~?2NBCy=&iCEzd)@b$Z?*65zMuV+ep)|wzW~2bzomYg z{C4^k`knTB>U=Q*ll%-=v(sE#Azf;l)QH)-k4=`kbkEh#;VTYVB2A= z63>Erq|+nZ{t@n&MSbMqE^R7LB<*S9)CJodW3eLyw@1WYz8ANraoa~kbAls|5L(J{ zLKii5w6u1VD?hf){LExgk3YVC^7exieG2~>+wbPsl5j-uh5b~OpjF=g@^5w_`!TZGk~@H ztd5*&(>Xqa{050oL`=k0Jq9K|a%%OU%-W_EHYLCHd5^olS0(XRZE*`Z>3{>@VJVH( z4DlwRq&emax6`Nhojwg3jKEuZpLYES6LvNtKS8@{^0c36)uFC~BO^WRJnwp8yQkL< z6<^k>OsYd<_#}1jm@_9&ZQHYVo33y>24&lv+4Pv$*n|j;D!&__s=j!9_o1`eBju#h98h&B_4W?~IM@F|v-+)$6%mA-GqQbEFv8asS=uGLWH+S2D2 zaC5us#yw*UNlEcqv$T4(ll=@4iIRujES1=Dgq+yr53M$M@e$ zaVz{-{c(NCfkWDDTlcIzt~tJE()g{q1JUcdx3+7Y-90v1J3f8fq>-AD-U}IX z1oz|`hyQ*nEx5p8=_?{#z@ELy^4=e%1%ud%BJRwmR#=YnKdS!XH_zcPU50ho!3|{W z4&l*b#_Mp4rIjC-u*H$>JM_8DO#uupR8xHV0k$sLi>E-dGyN2Y`(VGKlU7PUuJRUp z@Cth=oja1JBdXbbT4u8(8pw#3Jmgg89^L2xui|<5uTYjm5!)=--I&^dXFud{`)jI~ z53i}CcV-l17gA#u5Ol~zCHtH&>;N6X?*Po<#A07>=komv-YIk5&;qFb^DephY+7e`eSh(b(gQ=nV<*HXnzW;n>F}70$X<3+=1*HN zRcjW`EL=X~KBn&C71#Pb#IpM~$IAXF<6R!BKJgzgr~dOfJki>~+sFg!$!HP}??E4u zL$crr>xQR1|9$U;%ev_Ebku-u<`4}TL0HgdbrZE(O>rph1P?n%gXGhHqE!{gaw0bwGz59HCUz~a_ozN{JnzUZ-RKpEvQB6^;K^#YIU{=X z(DcqadG*1G`}fZ8N(k4129xsOlz-2EjA6ky2fW~8rzf30ss5^laDF*o0Z;*{RVzdyNBev z{76)fu!zw?Iwf8LPfY6ACn@icqsE{&c#B)?*R|h6yQXvrAq`VlG7WbkEpQ)bYMTFo z-}B-r$J_oU#o;g1{GPS!qW0=lfQ?)^oqftl`SrE~Smgz{2F|?36J9|H4!6Z;@P*gp z0-k`MzpCq2$SLvWoR`55aK9>iu-fL>CqC|t>QwCzTl|T<_QCU*klBNXjCgB66JcG# zVHK8?4{+Y2d%2&+O{0n8IdRvJZg7~Y$qX_B7-WmXu;THRi@SBIoMo+O%QbZS6~37D zl*3;?NNeqFYa`o`i{vZ5*yc;*2YefUCAZ<#nlzTH{v=(*D);BSs%@A>ZTx_ze~@m( z@$fb+^Md`<{MjG4v6i3NrfvZfFnUC{&|!ybw6~ZxW*DR^bp9aax7p&xa&ngE@JW0J zlsKfaj-q)=9A_-^8%DJ9?EKgK7r;fzZ?t_)s`xK$fCl_e(EmMu=e@M{J)lRLG33{} z2V@cdfF>=zM84-w$?=T3Q*^Z))LWKMH*eDFiH~S!+$tq`a!t<{stNiUE_W|#L zT@%iOj%YS^WGc@_bqyIh!dJK6M~$0Ry~Ng7;q!_#(~%Xz?2o`Xc*U4`bR&0?i)D+8 z*X_NiectZY75_JM(Y1qBSY{W>eX1Je5#M`}r9tnU#Hx+g82X*5h5t*N(6z;bAG zk^6}E{U+r{+G4q!bi$|Ue>N6B{oM{X@UJ0uTC(v=A-@*Cx^%aHZ14W3ZxPhEZ#~4KS~%OldRjB+avuUxo0`5A(8?I+CB z{&MmY;pvZZxTjZOd$NL79uIm-@mqpG3$skn!k7-hfy^I_XU&|=>32A}Ms*B~8xz2` zam<@lzMoPNdKcG}+L$}Gzn7*{*cB|nvXYCBbjsHjZD={3n6-feH0<##FcX#xVegA( z!tTSlrLzl;+toI5CpQ=aFof9aRHum33nIe~56>Jto~Kn#=cq$RIB82xN!*6AAg01~ z{*c$JL9^q}1{yg=Sw;+H*ySm{S~@+;=J-3jwMO;q*m!lzmDOFawFdVp!V0X^@dMp^ zf?ob)M&yoQSlZrB#Fp5+;rwe18Mh+Zt+7w>5*c0}~lE$4;|o-iyyi_f}!NvL6z zvsj$>kln^+*0#-LQ#e>!hI0N>7TziVkd_WrkDfGs_~hJ$3ubA9n7NBr&nncEpT71& z$M1DxGI3v_&(3KaUi*;cmzJ``R!(9`BF&F*|B~!2{BNO4g~K-S7hSpz`s4XKFDfsE zwQfBq#J9r`SWQZzhMBhb^m7*ArMiPZ@e|bxW=&Z*MKgRrc5s4DwXZ_J5p3Y9JAa=$ z{B+god2?55m7{Y?@t>vC=~p=TjkG5$F^5@|_)aBm2LhZ7SR7s3P~D-E++ZFzR6E}H zHCZQbe>Spg&z8lDmTF6xxj#>wJ5@70d-G8pUi4?B6Qi8V+B~em88xa2p0E{DIpyL@ zET3Bnrw-B9F@)n8+Ch$o)|u(Ia1Ncm#KPz-X+pjdgulY^g}tE)_I(pxf}7S!gmcc5 zH`A@<4$!0tG*QmF#4fkRGC5X)sk|}iPGef0a)2ms$CLxO3(jp$q;l9toj`8sL=(9j z6x6x2@(@c!u*&55mH0*_#do${z#ZhUN15~(-l4vHHZ533>^E`PZy$&KH*Lq1zwmFt>>%{`)!Nx0%KE54Z<7p<-f%jVc!(0;_0te*W~` znxb=iuI|$fI!-5;6SLwp*;&7?n4lZEl~#3KKW^;OWKI8w#Qt%*OC#xmZTXw`Y1XZu zHEn}Ve2}J+VVSifv#8hbAnl3|<*>(LWpi27={N7d1a#Z>HS=l3rQ6vYk!E#T%RM`M zdN=$2CP>a765rxhAEIkiZ||;VarG{ajNZrMpy%W#x$3Wp2PW-1Y*&1$;M~=tSGlMU zf@mRaAEY0#CT>llPWk*iTUL%&JeQVr=AIwt@PhYJe)(`9Lv`&ob)8cg@5BVu?XFKL zACqxUYd1foD&OIxS5kfgO!{T7Bf$r>Ezi@j8#8wm+Fd%j|N8yH$Y5>tdisyBGSZY# zZ9|D{@SY);60~uXqoVrRc@>X&VATE@K^IQQONz7WKRU5raQxZ(TJLO{NEY7ef}3hE z_3j>)T^M^|NP%wO+7lf;&f)8g!mX~Ydo>Rrb0@JB)?HpxXrYqS=Uzrqf z(@{fYz8yezR8)9j`R^BRUfiJTv5QWco)#IR=`;9b^{Y$Auj!P5YfUcoSRSe7%yz{*SQoF>cYH#WU8cvGrNz*j96FT)KIbZfGy{ zE_nrUrC-Rsuor!HUE%SIc6SF}=%gjOI+sUsJy81eHua`0m0 zgcoD+WKNIYPh|OGN7fI&{F&nyyRb@<4tL^=+U`+YZx-8FyRTBa;f-qB$n9V@Vjt;FymR(z5_*c zs646*9Im5zY7&Rv8@NbLnEus|<2bx;lVC3Dm|2#E`+!->20J|Fna3)3oU)oz&DcW< zL1CaO!H@2Faky}P@#su^DRb)$>{;IyFL;2ZPe8YTyV^oSchEXOzs0${OSmJ8{}F*} zZ*IxL(~IW=@Xb$f@nh8tyjp1N`U^kAMF3u27wW~WCOK8LO+l*t^t$Q>T+#9Pa2L*> zd&G}TfCyjVpGUF&+^|-(<{J6;aT6cQK<_-A=?3DcW z`LF(}+VUEwyrTF$AEcj$+k%~ZGESLtyrBwW>6@MITP}5_`eK;kh%CTO@QE$>FI&r;%Km z(1#?AVjuluv*MrBPvielwO^6Os;+K>4z-c3geTAhu#QYG!{c61H6O(H)EnDXEVMlz7})5Gyy15*SqdA^#D(=Y#4(*O`KaJoPjE6X(j}JACT|i20rT z#wZr@+xXT&>}+oD3hvmakB`__pP)lap##63-|`bTo4Zs1HHTp?5tu#nN#%-&%IE>E^_!eiP2-YyHjiz@z?vhV0CfTP0Cv z25;9UtfN&&Qdea!+h(WqWmoVS@vJY4N7m9&oS7@h#JbjO3U3aA%$0fE>36Z0+3*_G zW<5y$20e)WoAuzV5UT|9SYhqlMD2l3A| zf0u)+pswy?i2A4G{DPx9wj5rp!(N+d6XV!9zNZ$?!I`;S#c{zGM7ftFUnoA!j}TrQ zc+g!bUhF)E%Q~%jR(R;d>F7O!b$JE9t+`OL=~@f%*%9BrJv=*#Fgy-Qt~^@B=}BwS zqRIg`&e4sFq?I8YS;)J_vmsm`{!|n=D+#1@XUP%1T@d7ePKVx4UBRY*P`%7;$$|Q} zt^q%x6}ypr!yk;{V39?C>8@S^9&;={QrkTecK5QngF?Y9_OFLr=^NPXpfoe_hl99T z>_7k5Ecz$;O$r4V7?_*Us-_TXE57DS6wFvKTaC)AK$$HMYrm$gXg zvM+c4|>>)e1M;!uJ=jmp4b1tDBbWrMNy}&(-R5~Pd%Z*Eq@1owz^{~ian+~b92DS$lmnE zc*DvO8hCjwRg1okTer-dx>-wxktKA__G!D0+F@7HRh^#k>&i?W9)_3DYsN22jI|TJ zA+^fBEOFqo$cRE2Fq`9L^D6f`#v-Gq|pU<42|G@J+X`F1y&%+#wMfmaG7S z33LfsReQY^JBy`C-$=KC!a&p#QNMu(WVfIu!ZR4`RYJdDh*ubm54R!DnYISHJ@f7` zO%B6jKi2ZFtAY0jsMu~XZx2J;b_N!8M+QS$wADzGz% zZxzD!I|nC%H-{JD@xt~T$_YopRCe|9T((<}xc+0bMPs_}_tS)P6(#$Mj_CRxyO;Px zWAO|AuKc+;niJJOs^adP%9A|6Uy?b?!wc4&W0sBh`cf75gvw$3&I!WtK1!59G?-oT zVJTe0fmj3A@Fj=3RsT`F+#L=4m<{~+^RpzGvpj7oMR+RDC$-%s2)3%#U%IIma5?8# ztn9@SDSsWz1z0I;R3RfYrV50ng2ZXZAAVd6`TN-Az7+T353ApwynA-w&xM`aB%hJZ;y!JnPS?Y_*alJLjc0AUZVgDsd8%n~&Syn}1Is~zRTz@6%*98Us2 z4@Y@_F{8}kIMi32h*I3J0HSJPQN zZx;Ie&aZ<$q>C+d)Qeya);jfL^`v1YZl8%kX?yT;KNd7jeKK*$Gu#FMw1YS46zr?U zB|A7$!YI4L{om#pUTCT9Dm-r|s=?QXv?+sAPW1m-2rU;f%FBO%J6jgG7#>y=`i&cy zSLAPM1kIoO8zLuicXz^Qm3+^&nOL1g)s|mTPi9H6EqrdkN6BBD&%(1rb+Xc8C={%O zLpW7dB`FfEgtbkuYZjx00sa?`ZeQA%yx|(b z^%&m0X?enQNkjxfRzDSDQG@yKvouCURKPh9eQfCUM;ioW9#c7Po`PSJ2N~ z4LrRfhfU>d+p-=(Q`l7iF({401pshZ9*2(uyr5AWb_Q@kCT@TWha*6jXj@wT1h;rW zVaNBLh?C$ViXR%uG_>^yu*H@t+7kB`U>*Z6cg@*)oYUx55TDNUxy=PsYO zX1(3&1*;dX)PhUdfhROME`PbN?C49)>0`O$3v~w{(|bmRuj#3QH@7C3xQx)bb$TkF zmpgCL6uT)4CM?L+D!pgD;AcFi{-}9YBQ3xy)jrIYUp6n^yu@lO<8i77MnyX|FI%jw zWU@2I<;H1@3sUmV+udcb?9O#8{lo5U z-CRMLv>h4@eTT!}Noz-MsEV+7|Z#sC}UElC7-tb06@c+at$*d=l)7s;{a&+~=n9FW&qmb?Ie}%#h=| z=hkqz$jH{_9$>%p^1&1ouk)$xT17V3cazA;$2z|?9KQrwCBC!3w1wf+0M7@@p~H3` zfJ&|c)G`=+t~>;Q*6xSq3GW0Gs=e&tHqM?Hps^hLfU6qW=3_702w3+9FumoZ&h~ph zHPHxehFj&z&%2?2^gA3Z{A=oOPv3^AXrR5O%KmeL2+C6cmFHtmuabZ1z+bx0;mv%~ zeXcHoPf}L^B6H;3&Z$W=0pMC7h)E!bTa~(yt@`r!9jRIP<^!Y=MyMQOSP)#VGTIZ@lR$ey%ns{#ozj0tAn10o5Nd!p;tn^iNl06)M z2$+~TxhnVpKkq?smCc3syx%)jeysx)ec@6#6+K`FSkipq*N~)u4>hQAu-~hqU1tvIR7pMG!E^a%SObIJ;Zq-^7Y?vK zSOfFa_~6?Nc=0-eQ8WRMBQ97%ymel6m(+Yx1%s{LbtGQI*D1t7r{qhZd+!>48OR|I z4zOf{4^{yL;Nz1jC%&s3C)cf_AIWhlzlyHw+FB?k&0%`YtH>6HB-E{>;h~Qkfk)q2 z4v+u+Hd~uf{v~4%s~ka8nV);Yc|IIeH8W}tsln-2A9_k#@;~w?eD?cq#Sq2 zqCSts2i4?PoIsN$NCJIEivR=5F}c6LEE-{cM$Xh?&)gLMb4421utupfgf42@5KJfz%o znyp=93u(dz;IRz@DseUbALYFVd=ypp zKRlCUcf)L0AY>^CyGth^AYFP1p-Ph?gdPZ?g&q=0Xd+b*xD4sNb@0QwLsE;;q#fwSyR=wl|2fPG3;{&u>K`p;Y`{H9ORcno$Gf6S3OUBs^4#IA=Kvsbx)KjA*Zp5giO_*^H=+ zRG_iqq~g2_`rQ3e$(WT<2uos=+8(8Lpi-lpl;+_{cZynoVw|A0MQ~$7zaMN3B^$v?`*E%&DtC3fmUI@8Ll>6=GZc#!-fVSc(-k+3H8|; z!rTZ9-}(VwH;of#)sE-XfUYg35AW*O+RU*2$nm$ghJ3EBv7SCPwpWs)Om8(nEz~&d z@cnO(eFg0m%!EW243R#!u_uu7z}NXB-oj=q#0%1NACdCd2QM?AN4^0&NFd(547>>3 zB37wu#B#jcy_YeoHeNNVE7c9^I)f!yEWm5HvB#AcwISCTx{Gv9&Oa_eIww7Bq7mX( z-;vWhrLVT$fnKpX?QtKquv0Opl2N>pu>r}%%JkJ>n$Lvgs%Ujh-gndaF-TrUOt6L@ zKQ-ih$ZwY}{`6DWrSfgMMz`qX_^L~Xz zzrz|C{Z}4leKlrOq&=qg9^=m^PW|j~-Y^~AxM8n-^LNq4(2gA|IxLs=*_`pUY!5%R zIoG4SZ`4@RE#Yqvj?{YR6m@4~F~L|z^@qY&!&`@x{o9mt6KwQ5S?NLBz;n)vKz$py zbgD3vRK$h%tRnC$)4s&9QPHEv_I149#(MR$6BjSM@>wH?S`!<;=m-{PBfK^BDU$w5 zdc@;b==O~(&SPIg+4P2U`widpBWSwEuk5hhz-_STZ=CJhBBBm9AFZ0D4Ac-HU0+f}9gOUE7R9CGRbU)8_^!SkmmVK4 z@4G7I-VpPz`=+nh3-ik=HhcXdofEo`?Q2&Dh=B%L<&}*87kINuaK@KKM3q0V_fv9} zU47j|wiAZ2d#4;Z9xT354_L)ylXy|B7CG?M(H&y=FfAY zz{$_f3x7QL;i{;L2SX}^5`0NBTlo_Y3X$cCsEESjMbmO9!dd0KP~KCVUm|L$XxWt0 zv&GK8=xv55ITwnC*9$-HU(zNrlVBtIcy&ix;lEdpAoMg7mNdXENmeYP1j?MQHZ5=a zN)fFfD#uK=V&9TQS3T#fn;EGY3n0-f&g%&`onPIQMfkVQ6ufze73Hj=m52tTV*&zZ zC@g9nIW%S59((d-!{%=7SF{O%77+|$)c*4wYEff)`h2S>VT4jawAxCIHVC^B`bB}z zaH-Y3=dyME?a|E)BR)Mn>3j(6JnO;IGjaij)`*1b&WktYPrh}2fp;mNiL@{9TDKNn z3tRUvn3L+6Ji2YbWyp9z_TLQg$ zvaJZLs7k(pB}6PBzdq9@>J#AA(`sUjK0KnH{ zZSR%0Wpu`CsCnXOiik}SHR9zvm&Gf1HCS7mwjMAIU7bAsP{^;JfAia~-L97FHgL?S z1cw;XEMuWbd?1=wTMX*orR#t_UwyTE_p#$U2ehzThKeI!im_k9#>`jDbl$dpx^D3B zC61I0hEJ#TJW(yAVzVY?%3f~#%iT{-9A4pQy}>Yg=D0zfL%R3bb@9Ufj}AL5^TpBg zga{UtDPN0@*gYuYjJ2NMx%c?-6#a z%AINS%O^V$H!gR?zcq2eO#4OS{KfM&CI(xEh$H8IJaO%;eEw?@4r8aXSWxeJYs>DV zA;&jI_g~;xQqwwk+St+EL%R3hb!@&vEHo~7^WFD@|JC6_nU@-btCDKG-unCNpB^~* zA+{pQiyLtwG361KA%#Ju9nNp8DGd&o_HBN5%?FF;&3oH^&h*xt$qVB`TKDYV*b&j8 z^(i&+YmsQO#EAOmemo=ko_jq$@QbfSo6Ci-r-;4Q7E8`_y%Qn|@B8+SL!1fg2d#?D z2}4)E8d9}gvuZD#Z+Y)<(ppq$pecO#(AGVNZreNC{_{0!*XUhmPVW5Z)1x~Fc5EL# zu$#jYD{jV#?O$*MUJ(^Q{g<4Jtos*i*s(w4*vcXO94WTC(SxQAA7>Z+nr1A#f~~NT zA1Xim7I^p9T9*s|0ByX}R&l?Xdf!y|x2etnOQ5m{gq^kHfBY>_6#TW;6{N9=E!L6K zC&H+4!Gc-#_NMu7eK2Qz$f@MLXBYTisEAI)M!wFnyXa*-bd?R_40z`9 z&!)Zm{!q&p@Yb*_-a=W_dBEDa5Qzz2VBlN`+%TPKPqj%(nGqidZY+&|7z1GR0E&H0 z`!{XhcW_lqq+K0!S@Z+tx9>f$nr|}w+7Rb7n(nD>d?)dLP9&#f1$#;1`iJ= zuimo4p@y4ac6LJR2fO;UX&u$OOP4|D2iVXgQmsmz9Upk{3SPCYOn1Q0dFFzdA;bGk z>p32}8po+7q8@ZLF7MoUWXZep=i$|MWpU)Bn0ZQ0y#S?7-xOzAYt*8h?SBgSZujxS zpA6gCZkglNrH1i~ro6u@c=hU4TMjNCI^iw**;R&Zvqqe%64J6;M5jJmqmEB=d^*jL zIBQwLgy8WL;)nDbyK&oGpwv`+mLdkF{E1bFf`;vuKFivwyK%(i*unORtPJ~Av4t^^nZ@~c}J$ljHO&f#7 z=B8?2Q|~dudiID*-fI_qnySN0O%Haww)5i;wxg52_@N>@m0d~$V>R`ys^Tr^fC;;L z-*ClfNy8)Y!7k+`b#`+x+t2`OO+2`2yd~cJPJNSBT(Y=tXl!hu&VC8cEp?GT8GQGh zanais|6I8EhN=|1YaF}1>7V1aW6Dv#SS@2?)zC;74Ar4U0@E=|Bhe+!ITlrKA{tx8 z2hO_GGHef5501h;? zF#cMf6z!fCo*$+%TiG6*9A z>X`ag;m;|M$MRBq`qh>4`Je87egA>4xK_sct4S1Ce)i_=;M;94s?uuv6xapyGJQU2 zQ^ecRx3?CXe5TJe)x6yQ_a@~`sywwx!xw`qpYHHWoP8Z;e0@!g-;X{y`^<|YioILs zd{q%L#lM0{G!)yceMb&zH{C8en$9F_iXITGzAFw`4{Y0c9CiWiO>N$fP1+r7nR!WA zE{Q_)8~EYZBLCOp>8J2x=UQj7H6u>FVW@6$#))f&jPQ%&mBOmHmSG#OSfJrP^M>yc zXt=+;PE=hlq_wg*0QTx~*&2~Fa?g897QVUAKELarE}f_PcQjpG^2=HALhz-2$q~H< z4~Ex^S?h!WIeK5V_DveHZ(mZa>b#Kfp1d-SpbfV?V8k)j#?EIoqabt)JAi)@!!6owIG$l-5Ufvq5KFJn&6E zX8Y~?@(UCwtiSY&??;`LleV3#ufCo1)t5kD9gJE+*elgp*<;^)fVv9^yjZS`^!mFer!)`(Z}6Npza z{hZaTQ}G@^{nc+OwoV(Y6{Xh*Y#7JTb8Ng7E++bGN$$>5A6WZr9kc7bcisZg=k*xU zy~lL_E~ax!zW+>=3{Dxiy>H)P!(dar&35^9MOR#3vcv|x^+~gUCTSp@`GGW$u0sRq zmv=w=5*kQ8@eMz)UHka(7UjI{SJFQ!^>b_JADx8$(Px{nnGOA;8sDkdp80d)M+rMW z3O=_V`bWD@+4{2nkzZZXKN_@rn8Wg^a)H&4E;P15`)CHHYx8?5r`7&f)8G3>zV<*2 zz31y3iFes|+U^P4X)gygqm}VcGdjK}`Shivo?-U89~ypZbwe#&vU0fU(`9S7-p!CSk+h6D_8-A?lzb8OhB-JoT( zeE-3d!D`iu)_t+0WfVPTc-#zo=%+3sXijD@ASFvTWUI z`?%GcrzC}3RECU5h>dfsjf+`6EQFFeM8GlYA{zm5h$cl`A!G@p7 z$_35D=TEEJhH~ZVsAbh2!Ile(s0{<3 z+2T*D=utm<7M6l$q4Ba!ZI7M@T|l-@d&T)?f--iUO)L==hblnoJR^1fLmNe& z5*3HrmQ7_%s3eyrRGf2B6B`z7z}|UQ9CE$2`j+nhdW-J)t<`7^B)@z?&>$+(+n03- zcSVak;vlIO`8vzNW@0!tNh^xh@4T^O{yUBgYfZ7sSZQkg25=Tas`4;kC?<^8I;-+# z$M;(d!ZujVe+R}5^m^4{iLyZhItDIDR#aCkO%%12bJ$>|iL$_%(aRQP!>dF`6$LL1 zV;bV_6uo{=_)LkvU%3AwWLdNbUQCSrS2};A*MIr?BWX!Q>s7@*0{h~(;BrU=A&I(w(PPO zPW~unOVq%an1R^XXp?YHG`zd?p6GWc@Z7I$&$azkB>dC^!hE{3D0IW`%`jGar9-9C z-Orp&ySM$k{oYhl;XkB4?YDjP`6s(Szxq+1CW{G<$AWr9`9qgtY>sCN&94za)c-CcDfmKqpxqJHw-H`~@){1x{@T4cPyqNDJa z>z!+@Wp35^ac2Qudb^j{U z@})DUBusxT!Lh!i^;6^ED~8oem#<_PT11BgVqX=ayp~vEO+xPP77BHT)7@ zBSxvM3||^8>M`fuxEsIYm2d^?(6y7cyuWzSf(2V_LspK685KVqTWG1yEx+GDri#v+ z)~(}L4xcu2_Wb$wb#HE3w>{(|Q@4%-o4=YIzj51!Ra;>*@zkX^ExT#?t@d@EOOw~8 zZBO%EI|hSThxE7818CYlD-Oi z6&KQ1S#PTQ>!N0q{2fwGtBODXsiz^ZiC&Sbh0(Bmp&Q~l*zdOLzw#b;c_|aqHS8dp^YW0T1)p28xtU&s_rM5(<8peo6e;HWG za0PS>V_w0lYarLbbOx&7*tGXGj!-)rE+P7g z+MC*EeNAqjC73jcCgQsazstqss&XBCp>2RXPM#sZ36H|Z}> zdl-Act=)LzB;y;#CC1gpv&KK**se0HdqONmsX;R^0GWsdTO^1iZK zNmRBfNy=X3INaQ&Dz}v%lwXwJmB(g(b3t=)v&~%ET-jX9+}PaO+}YgQ9Bm$Lo@AbC ze$D&_?DRi`!@KR~kIehbpO}xq<=vUstsR)vNoLQU^IPyVQ|vg zwcC?cjvF&BVf?5`_KwkpPJ_opbPAp-R-aRwo8DUX{`E|~h}47}g9Ih*)(vbj`}1iHsRNZoq)y8+Oiev>t8f{@%eJw?l4!cI^BYJ$7_n$VN+w{6{J8$Nc-aI86sAZpuIiW;+F_%MpIKp%z;dJ>`57Y(hxL@4zY zGs9(zI;yT>o@0YH%{q8>gPvzKvuNKu_2SucFN+CvjA{#Yv_Z6=X+?*@I0ffrqMuq9 zMsxk3+gt~ZdHR{j2xlP1lXozj1i(8S>}m>s4~@5CX1r1Iy(bztoP#T7M442nbB79{ zolXhK?u@FGG1vsRbiUO^S-Y7$sqh}6y`o})Xiu3XBZ zZxW=dWNt#`nCloVDBDUKfsU-?VSY@i zIvy-HB$!$Zi0x^&5MkgHZKqXuI0CCSEQTGls5K(WK>qvGG&n zJ5j)n83@#^%2!d$-(tHje(~@S^R{>2UHSgfSu@|aFMG`}ZRP428$ynKoOIb?Nk3_Y zuQE|wbb&o6Idc<5;kOH?!Q4uL{pW=fHYiK72`JjdrT&ZAPr)i`d$Toa^N3x$wrt+D z3zLed5iwDAGn|=;5t?b}NSKD2S6S;#?mJ^h$he7bEu7-0Ynruk#hl$CmoDykw90YA z`JzFTzxlcCgKLJHEA}o)3R(T(T+E?wn%?6xz8I$nZq^Z;2erJk30sj2r z$1WWe(ls)=nPc)7Ci6VychILcTCkCV6+$D$a-E#+l(JndQc5wmv+b~%KM_`IlQXS; z{_f!Z?W^tPEz90ttZa{P7pOxeTrf=R-ZZ zPaZzfaS$(M#pZkJ1LI5Tm2J~DP2E1V_@<3}KinU3d?;Qt!jE7P zV|j4;k4DRTHcW?Sn5~01joh(g^QIj;Mr|-t;5s%W2|16H1#9Piv47{EC zrP>ZAwd6m|8I@iF`%)Rvc%^FIHM!5nbwbni~& zH{MZ!SUCB4KuwQXkDpK*vvqj$&d1dTY8V7&Sm(~7AwrGR@Z()D%!L`KTa8#d4vyc} z!SUN#IDU&AVHZ`3j97=bwTO#F+&ILU%lEg9CvE3FnzpmqI&kB-B#4i`F@5^Z@b6{% zZ1J_jqG0f6gEtS18aZ;9-O^o|8_$m0k`(k>&^{?~{kEhHV}}eLGiJ!J(HnNyn-pns zrqiWMXFvb?^2?uxg|+L{49y&kFtriQWhxAc)jDkbTk#I=4Y1ED#8#nGC zH+J-lgXw1U0avVu^rLAx888+yu!a# zdc?<&U31ClT%_TFNj)Q~2%HGQ?c4ygLT$W6u9&ggq^3D{8`Ppu1aMw4Ityo9K~3k( zfP>Y$Meb*WnVgNBD-91aHdS+0H)S-)SYfDWGT&+PwJ?6&89b_^&W9Uro4V7M+0x3`ERD z#6%+u9UB_vs}-&PQt_U5xY1JEl+dDmMD=<8Ek$+n*U-oP>07f12|2W9%9M4E!VlG( z>W|jj=RUb|1&-Pvu|};Kzj5Q5H5)gMUps2l_}Ecc#}$E-yK3LSO{xC{;cvc#*VAUv z8gDT3iv%$Row z_Y9kCz9^brF#jU93BORRyve8k*j_E;{({Y* z`Rwr{=g)UJ@-l!{zmgsgn5JgkN56teDXFfTzYrZ$%;J3{#BRb5X2TuG>+x3l*f##~ zSvZ{ZtKh47Jtd#;mhHMqmYB%ck@)JKA0@%}5Pfi=?{4dNGQLpbH*oa0(NeJXJABYs ze2P^2MIwTy!S%s+WqP3^)`(Q}7kPMo9G#74Vnk0zuV|{K5Il=C6^zDV0EnkN*4;%ur1V~P)woM3N0wKywK`GTMF$i zbfM6#z_x|{4lEd0BoNNQ0?P+B4QvbVXrX~oflC9o1wJTTIxqwGh{4ga@btpF3ZE@YTjL_>v=i zudxKaT2=|)ainiGmcmyWo8Sq3kFlE621gBig|QuCyW*&Y?=D8*8;-qj)RFqad)-TL zbQc9qpwH|!!lzb8A}4*wHVpp%CgOXJGjOzoFTFSM&7y^Hy4M~q@s{E1Ngu$0;45$n zxC>mkA4ea2v*{Qho&b;aV;+lS9vgtK7@b22-{M<}gQTBvjFQsuRmRcy0%I9zid+ug zbDA#G*9>2iTgk1Zd2(BP5$X-Oz1&k;AorI0!d=h>je<1CaH^>{LkL4}+8rU9r8@}JQS5B6brG3oR`{hsMqi{~>Bjw9Xl@Dj8 zO8KOq%rwc0vk1OeR}?YDa1F+NNajPSMCL>JNM;JYqgezoMR69xsXa}@(=fvbv#*%UdX2v4LL%>N! znMegr2|;a9OIO(n%1)Dd;eKuA8fkszd(wu?6;dL?8#5P6TX4S}XA<~e2ht@Y|4y8{ zG83g!nJ1*PnJJ7oNsUtdhb32Mqm z@tinY0iKUIR};j9QBIuWa4tj2^6uOU=RS&f(8yITeSkO+xroZa2vgZ%xbKHFk+>g% zs~0rsN8nQqPzj%^I6vT;q~SYZREzw88VpD!k&7Tj;yMISVnAK04dIl4Fz^DT+pgh6K@$8Y*%XT&v=Y#`E!j{sH*!NT!O~J_MDhlt3zlXay)Gah5_UWl$>NO*j+2 zI-cc$uSPq9OcWrT4}i~!hH2n&1yX{jVL^zXY7iAbOp}N=r2Io8iL_1`Sf_A+P$OfiLX&1gtnAV9JtpP)$wOtyAk|*MR24L++ z>GvRyzQ_3^aP|TIjDX}1%H+ef6tHLls6?ef zsEbY&;;8X}_yHgu0mlBo&JIf3r6R~t40plc;t<@IK&u=arl36JiQfB(?_B92X$wHk8qklaeQryIfx8OIs-P_KIMv}k z*C7INF^Ee5tQm+~gYbQX|3I$4P)`-%3{b}h=N4ds7KisyqC^1GrU0Nm1k}?EwKSe` zi{n`{gu~FANxJj`)V{cKf54U6Xedr9c>=C8AZgYhJ_+MTGJ3&I#GOOjJzT%XNf@Pp zF9a}h;!H>QFThsOwi!tG825i8oC!{+w%q`16LH;w7CDOiG9crNs*E@FRSRkthTm#cSd2O(qYlZa!wJ+O8FfgOhlA4b_)_cyoDx<6T}?`exD-l7;riTNM}LIz3B4?fM18`Tvs{>1q&oN3?;f&5O?ARX7g(5foV44jV<_cxwrX08CI z*g@qWP&o)x4g!@!U0m}J<2aOe0GGzn;;zz38f%;mgEpZ0Ho+MNDn_6^ zdI1+tU8$a0{it5r2(8yC4fVKEUSR=1}x}E8_LuPqT-5 zs4LMs0&sd!&qw((`hO~#yK(#N7jg?{TI9dx6u5Gact&C`CoS420>+yno}&boIpoOv6CnRt6Bb z1VD%Ybdp%a7XuNV2wd;s`aRCa;9rtIG|J?Y!ofjnajwI;9_I#}i8wdn+=P=TOS1s< z3!F5+K|6p7)Ry%C6_S?ZqL-u%#xrf^LbI=Aq|;?7)!Bz=%u>`BRK#qI4YZ=t`9I@4V!?#kdU2&weCQZ55{m^l|v0)Z8c0NhLQq#igG zV+>k?Mz!0nmQZ-KhU`I4e8|1e5OOE2MvJ{j-@`Yy0Wssw|D@Sq8Av0aHQMm}} zLmc3l<)#2{s%;s-j=)Jhp2rKsoxn+B*I(dXeGF2#4JF{AN!;5mDkzxK;;ZsF_hxQ| zw7m#Pn?jOSK8Wif^lLw?eM znE>2rWYcBs1l&(X-u-CxbC8YtNc9LXHF>H)o+_?Ui$>lPu2G9d-W0DZMx&U5x{>5`-EnWBo=PhT;`mS6YA}z^SZJiTJ^FNetMNAPqD~d9TAsm8m z37%UnMk$#4as5o7MJS|sDCUZxuDN1QSE)guPEnLik|KoV7_H9;Bf^I8$9xf?+e~$$ za}c;gYXeUoGf6*#X6-Tm?|{^uaP5q<3(jtkmM9VdO8P(PzqY4Jxb^R7_VgBKnZDhVwYbg zwz5ISs3Z5m*%#QzGR+)((Ecj!srD3W1W%;NJ&>jsO6!AgUzFA#&wcPjMGWk{IMf5r zdLpK`77Jccr2u&V?xJuG!WoTo2+q+s$Ks5`8IN-U&WSiDA%_96j}V*Y6`O`R+5p5x zp`X)=AR6ajq#uIoaJ1NHT*u;Mc;Mm*I3ZUt+bAGU2BvR$Md9tXL6k z(w>VpMmZMriFenK=2HgLFbFt4fyR=>Roh3v@ey!*#YXId8F3KrP zx%{r2PpX}kb0TtT*cedWlj%WijF#tmPcE zoeS!x+FXZbDv+9x1=>u3dMb@U{=82_GK6H9wnC&CP!NsgNDb-4D_maBL81g=+|<^s zL6AiS03EX&&Rrby28_-~6NV9*=LV2h7~^CK<lL0fts`5aLf$I{XD;Mz3g?0hjwY(_ByR zkZ6YAM+`Yxi$@y5i{h!Rs0E1@vGM z;NoNSSm2NYJEY^@=}!ODz(2yz*bnutH!$N~rxnhDQh4iGio5%aP(~)X<;jsIwc998bV? zDZo2VN#oAR0jxyxqDI!<@Dv*S6I_=Sv)t}-&@Z<{&m8xk;p`f7o?KdJPHug2hG%~I zZ$Z}RN7C+D{xGs<@4c>Ux%Z!Ef|;9ydzOq(P4j<=H@uO}j+gg+Ui9_OpIi7Dc#kDV z-s!xt@Gjw*c>`}gyz<#S;Vki<=;sLi=Mbo{|EzaE3EgMIPa8Y(hJ)S(*{e%m%wbPJ zZe!Pz<{>#sfRuN)Whlnh(5!HuuRlKPNSU<^{VoS?d5j-=dj0x*I8v^Ess;1vWfY(7 zS&q?E$KLy@)#MrGqMV6Vciyun52WX(h&!)7FK|cvCxL&8&N*@T??KK|*8lU=_#e@k zdWW_ycovF3OB%fypC_%JN4sh{Jv$Vn$3cG-U;L@FFRj0O|8nNMe*Q;v_M*n~$ZGcp zkr(=|_~(K7BvAi*YCH*>=YRGe$lqKF`|nXB%S^&+CysgCg-I@>viJNRS{l16?l$Hp+Rlc14#WMth6jcw4YRid+E#C0cTx{rEy@Mm4Vh!Txq+W#h5X-b zD>tfn2C`z4XELt`peIrA>3fWvhj&?WT_5MHxi6zK+$>2vm}H60{uuQit61HQ@c5nM7En9tQ{k(tbI9{EiG z-t%NzyX$44wpzcScZ~nx-?K`Zrx){|VkrSqh6;xbC^={NHQA|03kr z+{g^s2`YKhncJ9mPIF72Tik!IcVk4#^UoV+O7!o@jVJLQLDc4mxUR^H^KzfVA@38o znj!O;38}im(-X2*HMjXG*YnvRLsKA6$a%ExliD#W&RH?_-2I!>fPM*&>@*k6qaS8( zZN1E=(AlNA0x9)WYG~B*(CjIYZ)^O;~u#{2zrZ zLj%(nHYX?O*;>boK=x@$pWzX5Cu z$ksaoRuBDPs}afe+=E~v6bnl(vf-XACBTMznY0=f+xwwydJYx_-%IzUU!~uqN7C=G zG58bq1!)q#DlDbL;z@gWWx{=dj|}^4SZ7z1Uyv)yRp1M5fILu+k_W*Wd$2r2 z9x4x$hr=d&1T3;g$)n{l@>p19$I9d7I5}RPAWxJh$&+EBy+KZtx56$vSw1WufgOeq zaP!5{6?HbE4kjFS)ZHH%t#H%EG%!oeK?kA)9j!r+K&cC85(GPSD~_I^O%bU(=u?#S z^^1Wj#c^~7t%6~j9)hDJXyyQ=N;2h2fqE~1ex+gkPzFZl5D!4FC>J3hehrfXd&|Dt_j+`is16NLxV!@e{rSa@P zpaT032nUxYN-e;tTcI^gUIfap7lC%*+QZN>KLVSR#`00{Z%1%V4BCinph?1&0x!`~ z5QhysQwZV0IIPT(R>axCd1Z0P;L>ui-7Sx!s8j*`R~fBb3E>wIsti5sD&WPc@Yzur z8sIe$Puy9Vxw9yEpfZHU7Uy`vHMqbZIMXswgpdJ7yS z(X(2EN690EKlr~b!Y|_}$=)H%uxo0Mp3(s|D+yb#SHSU|apXsj>w+-(j>r#w?uPKI zI0~WPbqDr6a0H?!_C#HK;|M|@>4REQuLuDb4M)vJ;K+wwGYV7~jiZt@1&1B}N#=kW zb8+}c^KcZE=EG*Lva}dS5$PQqm8B){VNw)6OqQdEe;}&5#V}>`1T?l`KBS6|M?L+tg4vTb9It)CI;0ThA;wUM7iX(`9R+!l@N>S-^9QoOE zMMdcpjzaKXaT;Zv!4WK-#ZgweA>BkRzI9o&-9l@Bhodws+cev@JBYc9qcpr-`~V(! zfFqyu6ArsvO|B+Yf-lXw_-!CJ$8Rh7W%P=T@G32MEsJur$1e=;qmqGdVCNaaGiA;#ksC7&T9zcw zv-(0!FvCObc!uL%=MeRHRg?wrv)y3k!w@w~0q;EYTB!?@6hLNV}hF$hDxk71OE)w@E0T(L0Si=JYy@oL69$>OD_$ zwFt@pT-xi=Y*WatBTw!_$tk*SHPT_0516o6)z)-+na_(`cFc4*fPNx#s&e&ie|S zw0AnW@&G-}kvG?}KwX`;kb!xC_v%1vczYojxx&f;+!H~(P4Aez`AmDt)Z~>vLB;9r z{kdSPaTS%%)MCsi1e`qiJf%KMh%rm{a8cGgi0Ah^jG0?kJ6FtH>v>P>yKr=`r3q4Q zIkT4NxkK5sl3qca?$K?d5O~_nEiStq>Z@){Juo;#mT36XFyowBjAtJ_o5fVN{JG!K zUNA{(7ySXB#w?Ts?#aHlp{>z#z;ST{*$lh>ToBy#gDlRKL-q98uaH9UHi83thTLWZ znYF1jd8DOBn0o>!oU6=0DxGHBdQ5#yN<62m@ZiTK63v3}o{H=`Gz_%8@-*1TwDY8O zVY!A$h-Tzg6P(TR5-Z zn)CWCoY!xac>Na6>$gh0ehcUITVr0oRp#|uBkbeU16R;0t_pTT8lbMUmaEEZIogG2 z;#$!)<$Z_>ysmqZS9G;`MOTAYbhTZc{%9>%i&t?ic@AaV>cj*NRth z&3F~pg7-GGwcJa*!=bI{XxF0;aOsbuDerR>XU?e3I~GNF6<3c}arJo>SBY0~;k=4# zz^k}$Ud2`7Ron}_ifhEHxT?I03+Gkbi@b`f&8xVYyo#&MtGJfDimT15xH`OwYr(6y zFkZif@%pVMtf-HJ3uup|0I%gL@mj7nujLx^TCNqZ<*M>pt_iQ@8uD(6AMDIib63Y7}73H#WS**usFT}`uAtAgA;^1A7V!R7dig!Uu z@-B#-*LyX1zrn^k4P|)M*M?VpZFpT*iPv@2cwN_$cO8oIu7f|UwY$<9uj0NJmV)=* zfc%22quDjfro}XJ;9iqeWPx}9F=P)$_66_(gkL(LFTu|aU=&}RnaKAVgh#l$i1{(B zrXfwCPl0PP;!YrLF2ad?(CCl(Ee?7+;+}ta%eiL^v;8zMgqJpqKk$hM%&<-(DbJWw z&B%9=0z4Pv2|P1p*Z#<@BpN#akymmE(YQoVU9#RgM{aRQozr&FGg92A{MzWJ{d(Ye z-=lPzC6Fb(XY|NAW_mJa3sJ362Az*I%w1v47XJ|-x@|HUKCN0GqUCr_LD*?WHn3T4 zH9CTx?$O!vI1j4iHdFUx$}{A_)s-aY+$m>F4rt5??$MhmLo)%5_t7Ry0k6><_heyD ze`o_89EV&~(&8wME1kg=^Mnv1SCN8g|p^*|+gz?hMj zfGjQON#hQ=TM}?d8fxwD-2&b%Q(Vi3lwOvzC=F@2O!66#%byD$?m=2OmqbsE%B}xB zHIut0pbBeJ>N%d?@AZ_Zo4Zz&%M<^+A+!^DfFMk0H;FYcL37N;F|ugX(SNlzXNkk3 z4oP`xAH8iUOn2m;Lt8hX2M`x9Cy=*?1Dt~LrINmm=a1G*DBUd=>5f_ue>yi{73N8= zhaf>t0|z|Oa-%l9flJ5(uXNtFVK0`L>=@=S~k0h69yrnT2Hh;)X zV-bakI@o#R1D-S82iXGBae&v07_BM2D-Vs#Sj8eQ>;REsF-{tplQA~aSg##AgcK^n z{paQw#CXC)W$I%zEB3@vrXbXs$|WxbDeyo^5=Cn%7r)`2yk*iH;RN+mZV!@Ip1!F; z%mSAwLM2ikX8zN>UwC+t3eW2grR3Iv1P_1gdxZS75Z>Bq4fIBcsjj8rbhJ0eSm6oH zj&v%M_YfMZ!IOGwJV(9tJMD@XYF8R>=}&{iyOG*+JkxeOb-8jM6ng09LXG=f9H-^b zO7O0m&Ph5%{p!u%dK}8jjVdm$BHrFbo-|h@zV|@XLmJ-MDWE^idZkp{TikMmo_lc1 zD(Ntn z=t65{-oHc66s!vQk9h<0WIWSG1o8%?VdAaf;erlI@D2lIrP6U~eV-%>NgL`<%nKSm zxO2s7_pW?I%bfTzOiDq2JVVTl12pJbBKI#a$tA7(u{)4Gl{YoLse-V!qoGM&mrjgU z7dJ3ZfP4^fyYeqlh2~#OA4tbeFqS((^JkDHTcEY%4~hmsU2zabbp7HxKAg_&G2jEVSpuNqm<@ZlM&f z9lGptl*e0*>M?rVyz8aMd3q;fR)SDtZsP&Gdt=Dd|CKG8Fv*``5sU z@F1!j$=r&5k_;*)0~_i~*tO}RV`73oH?^nJ?HbhV%&WtuLS?&_-CyLxQc0lb|e1tlFv^= zn5CDSo_cEL1X>yK4uMMoWJ^V5=H6;9T!6{*|4|R|%0)6$lO3+I=q^hNuf*Ad+B^^t znEUghN^a0V6|A|l!b6WHUezQxg_z1rDUJ5plWbJ-z;IzfE#;Ebo@f7Fi0Dt_5^$lJ zn}&tE{HzkdU4r)ZmqsS&7}0D;dxCok>v0rI_cTA#!+Ki%N_`e7Faw|%JIdqtoRkN3 zrIFZkkjHcF(8?pZL!$(*1(Dh{`$J7Aj@pY#W_wiJQ67)?It^j`a{KZs7tpj3h4R(_ zMc9KET9)4a(Vw<2hjj+GUoP~|%2nCr0PzT8#lW~U4rxE0Cti^Vy7~?AN_Jjyw^G&| z|HPfn33 zS>vC4_w4n_JtZ)u8B|tYenOGlb7u zEv)TSWLZt4HQZ2LA@5yS9RtkuaL`*OZJx&Q#5Jy#rI{h>21!b@Lz-3Q_ycr3mNDS@ ziRL}Yj=f<`hl_Frxp#_5=aFQ%o{ye-|IvICa@-ZA;hk4n?|gb#+mY6M_hh4rp2U51 z?Owa@IiS^UHn5?kq&pk6n-_Ul4^?ccCthHiZ zvpBuKF--T5yB*yzT7YFN*CM;Lbv^f9#Zi>2#@f@YJzbA``W5ud23M0M#H+Lyz<1eF zJRRyklENbmbm|k>Y%P$T3OP#7{+2LDf@g_vQwg&!*UZJ+bDs_ZUu8frTf68FwHC^2 zqK_xvmuY`m}s7r8Ld%ku68dfbz*+PfNpKtT9}QHHJ$wMar<;K^u58@ZSQ<&LGwaF3LK=C0Hjo zgmr>Tuuiazb%IN?PH7~5 zC%7D1u9ak!Tg$CsVcAA*BL(x<$cxG^!%{PtHFW)0LpP8$bRDSQEpUt%QP>`fjxuM>*C~?*OjKkJcq!^-$JTe~oq3hqJEwYv8j$v_4q_NUV)NpKaGm zum*ZJSQwW=+mar7CAMAnXU?6>+UWC`!y;K5eID~xB=goh=B@s$jXsb0DU!9(=dm_= zB=goh=Bq)}|lA+Vo?%=S<_CGlqN4Xzn><*rz}s_n)!cep^SwdJw^04|=iJgEp+i-eIHb0-v^c1_d!+meNc^kAJk&s2aVbHK@;|U(3E{2 zG-KZfVRC}J9vE(rH^7d8d>_2RRyi?jm6M;X6K1h(P5|2|%wnGiec4WDq?{xtAum~} zk76$gE!k!#hHZ96vdzvYmY9(&F^96m9L+Wjer(equ_uKp7#V2>Fx{t!w(_@F69EeUegk7cU@Nos-AB;P1SV7C(tX=#UqER7yVW4I@e3<+$2wAH++ z6o8B+Zwy5-%7s9>I?%7liwgN)s1z9)6D$3#{SHJ}HtN6Q2FU>fBk4D4i8SKv?^INuG)@{f-|nPJUhg9X(E- zKW;qz#sV#Qk@m~9MKj_mgUa+|`FzWKf*Kv>uT`d-^TOzoq!smw&14WVAB<7Daoul{fj!_qpI3j-#`0 ztnXUiWWP|qFu%Tj34Sa54*T6U_#3JjdKwlO_81--OB%13LQS8WVsL)rZ}boLuj(J> z|Em9bKqH8-ppmvoUF0e9RCyY>kUY{%muGg6W8 zkDD#%A=~7|@;mYp`CWM_`pA3eAxGu+(L<8uW%6EmpS)i_ARm+u$;)8_v_k%nE%jEi zt=`Y_DtR^VSR=2M*P&0!82>!pfQaXH>XV(G<_Cx@%Nk?+ZGjQC6~@>$7+qh+sM?-K zQ}W?KbGWV;H@jnWi@-S9i)XKWF&YlU$TtW+U zBS*>!l0j~dZ_55E&xA}i$sOb#(q#E{NkDcUfelqhIRai;W=T%DA?`ZKJ>i{Ywv;Y6 z!rd!!FX=aVj`Ww@7#L0AEJG z)j41GnT?TFzw_~#fZF_uSqXWF^7aAcP5~HZv~+v4P)D@GE1-85(6yWVD(Wah2GAT; zPc8d|%Hd2U10+Og)FvFfrtw9%dZtL}W&sOyU-Kg;F0T%sn!lGh^5t4K>}G)baAftMNd#fG^)i;;XW z5+=wcbA8$Y-v;i>2h}|%QZ`LvjiIz$`15G??p{cy2;ondn%NYpP?Y z>4hvuT(0SsgS)n5J38c=ax{&-IS%quq;v7lUX-NvkRf%n)-QwR#22V>NOE`dEM0mN z&AGkMXJl}=28*btt!}7Yso%)puvY=g(^4Az(plhh9`x18z?5KwW8SWnuSxYD@EVoi zlItYVUz2CbugkOK+43BDt~^hkFTWu#@RINz8T}qOVwqbSuOu1lC4nmfM@qzT3vTKM zH2M}~Ij_lWvJWRYP4jA!#~!It2hja7=LulBLf-)X3^iyYwUa;RYe)G6=+{O*iQjhU zNwhOTEzt%~iJl~x!a(C-v|4G%@k&@X*1!zAK1!D%nPlockVrD5kqk*B%S~}M!x@IN zInHW;i7%nEbRe%)`2bOeY=jJ^(nwNjag@@8xuMs{$oXmQDDiFprT63?!uh*piTjx2 zvX--z;~*`(@^<35(e5}OaAg6^&U2S>%b2HSv^q(d+}gbt%O1T1AB?zV(F1zO5pqwt bm)u+K19{O;?hmegZf|)^1FfGBrz`&-FR{+x diff --git a/app/src/main/res/font/font.xml b/app/src/main/res/font/font.xml index 485b0418..4fe9ca2d 100644 --- a/app/src/main/res/font/font.xml +++ b/app/src/main/res/font/font.xml @@ -1,11 +1,11 @@ \ No newline at end of file diff --git a/app/src/main/res/font/sans_bold.ttf b/app/src/main/res/font/sans_bold.ttf new file mode 100755 index 0000000000000000000000000000000000000000..80497666ec8ca0abb81ce5bd9ce649896555753e GIT binary patch literal 117916 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 T0?uqfD&gf_PSWS(z4-k%e-4(l literal 0 HcmV?d00001 diff --git a/app/src/main/res/font/sans_regular.ttf b/app/src/main/res/font/sans_regular.ttf new file mode 100755 index 0000000000000000000000000000000000000000..ab605f9e2e3c347708a810435ba3a7debc52e1df GIT binary patch literal 119984 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 @@ -31,16 +31,20 @@ android:layout_height="match_parent" android:orientation="vertical"> - + + + + @@ -78,7 +82,7 @@ android:layout_marginEnd="96dp"> - - @@ -71,7 +66,7 @@ android:layout_height="match_parent"> @@ -42,7 +42,7 @@ android:layout_height="match_parent"> - - - - + tools:ignore="MissingPrefix" /> - + @@ -61,7 +61,7 @@ android:layout_weight="0"> diff --git a/app/src/main/res/layout-xlarge-land/fragment_home.xml b/app/src/main/res/layout-xlarge-land/fragment_home.xml index db53cbb0..6c216571 100644 --- a/app/src/main/res/layout-xlarge-land/fragment_home.xml +++ b/app/src/main/res/layout-xlarge-land/fragment_home.xml @@ -27,24 +27,19 @@ android:elevation="0dp" app:elevation="0dp"> - - - - + tools:ignore="MissingPrefix" /> - + diff --git a/app/src/main/res/layout-xlarge-land/fragment_player.xml b/app/src/main/res/layout-xlarge-land/fragment_player.xml index 9284e24d..68862dbd 100644 --- a/app/src/main/res/layout-xlarge-land/fragment_player.xml +++ b/app/src/main/res/layout-xlarge-land/fragment_player.xml @@ -8,7 +8,7 @@ android:focusable="true"> @@ -54,13 +54,12 @@
diff --git a/app/src/main/res/layout-xlarge/fragment_home.xml b/app/src/main/res/layout-xlarge/fragment_home.xml index 5bc9820c..76f220b8 100644 --- a/app/src/main/res/layout-xlarge/fragment_home.xml +++ b/app/src/main/res/layout-xlarge/fragment_home.xml @@ -28,24 +28,19 @@ android:elevation="0dp" app:elevation="0dp"> - - - - + tools:ignore="MissingPrefix" /> - + @@ -54,13 +54,12 @@ diff --git a/app/src/main/res/layout/fragment_adaptive_player.xml b/app/src/main/res/layout/fragment_adaptive_player.xml index c377147d..1150691a 100644 --- a/app/src/main/res/layout/fragment_adaptive_player.xml +++ b/app/src/main/res/layout/fragment_adaptive_player.xml @@ -34,7 +34,7 @@ @@ -69,7 +69,7 @@ android:layout_weight="0"> @@ -36,13 +36,19 @@ android:orientation="vertical"> + + + + + @@ -77,7 +82,7 @@ android:layout_gravity="bottom"> - + - - + - - - - - - - - - - - - - - - - - - - - - - - - + - + @@ -70,7 +67,7 @@ android:layout_height="match_parent"> - - - + + + + + + android:clickable="true" + android:focusable="true" + android:foreground="?rectSelector" + android:minHeight="72dp" + android:orientation="horizontal" + tools:ignore="UnusedAttribute"> - + android:layout_gravity="center_vertical" + android:layout_weight="0" + app:cardCornerRadius="6dp" + app:cardElevation="0dp" + app:contentPaddingLeft="16dp"> - + - + - - - - - - - - - - - - + android:layout_marginTop="12dp" + android:layout_marginBottom="12dp" + android:layout_weight="1.0" + android:orientation="vertical" + android:paddingStart="16dp" + android:paddingEnd="16dp"> - + + + +
+
diff --git a/app/src/main/res/layout/fragment_fit.xml b/app/src/main/res/layout/fragment_fit.xml index b45af0d6..2fddff91 100644 --- a/app/src/main/res/layout/fragment_fit.xml +++ b/app/src/main/res/layout/fragment_fit.xml @@ -14,13 +14,12 @@ android:orientation="vertical"> - - @@ -93,98 +88,7 @@ android:textColor="?android:attr/textColorSecondary" /> - + - - - - - - - - - - - - - - - - - - +
diff --git a/app/src/main/res/layout/fragment_flat_player.xml b/app/src/main/res/layout/fragment_flat_player.xml index 7f3ef825..f24de9d1 100644 --- a/app/src/main/res/layout/fragment_flat_player.xml +++ b/app/src/main/res/layout/fragment_flat_player.xml @@ -9,7 +9,7 @@ android:orientation="vertical"> @@ -33,26 +33,24 @@ - - - - - - - + - - - - + diff --git a/app/src/main/res/layout/fragment_full.xml b/app/src/main/res/layout/fragment_full.xml index 0782b84b..0bc5d5c5 100644 --- a/app/src/main/res/layout/fragment_full.xml +++ b/app/src/main/res/layout/fragment_full.xml @@ -44,7 +44,7 @@ diff --git a/app/src/main/res/layout/fragment_hmm_player.xml b/app/src/main/res/layout/fragment_hmm_player.xml index b3d534a5..a65dde76 100644 --- a/app/src/main/res/layout/fragment_hmm_player.xml +++ b/app/src/main/res/layout/fragment_hmm_player.xml @@ -103,8 +103,7 @@
diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index c7649a14..4237b4c2 100755 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -27,13 +27,13 @@ - @@ -32,12 +32,11 @@ - - - - - - - - + - + - + - - - - - - - - - - - - - - - -
diff --git a/app/src/main/res/layout/fragment_volume.xml b/app/src/main/res/layout/fragment_volume.xml index ebe69c00..456c394f 100755 --- a/app/src/main/res/layout/fragment_volume.xml +++ b/app/src/main/res/layout/fragment_volume.xml @@ -20,7 +20,7 @@ android:id="@+id/volumeSeekBar" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_weight="1" /> + android:layout_weight="1" /> - - + + + + + + + + + + + + \ 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 b7958152..a79edbc1 100755 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -105,7 +105,6 @@ @layout/activity_album - @layout/activity_album_style_2 @@ -201,7 +200,6 @@ @layout/activity_album - @layout/activity_album_small diff --git a/app/src/main/res/values/ic_launcher_background.xml b/app/src/main/res/values/ic_launcher_background.xml deleted file mode 100644 index a6b3daec..00000000 --- a/app/src/main/res/values/ic_launcher_background.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/app/src/main/res/values/md_colors.xml b/app/src/main/res/values/md_colors.xml deleted file mode 100644 index 0d2c4cc4..00000000 --- a/app/src/main/res/values/md_colors.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ 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 ac68a315..a9fcb4c0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -558,4 +558,5 @@ Click or Slide Click to open with or slide to without transparent navigation of now playing screen Clear queue + Buy Retro Music Pro diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index bb7d822a..3a97910a 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -40,13 +40,10 @@ 16dp - - @@ -100,5 +99,11 @@ start + + diff --git a/app/src/main/res/values/swipe_button_attrs.xml b/app/src/main/res/values/swipe_button_attrs.xml deleted file mode 100644 index ecd20f74..00000000 --- a/app/src/main/res/values/swipe_button_attrs.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/sans/res/font/font.xml b/app/src/sans/res/font/font.xml index e4827093..00bae1c3 100644 --- a/app/src/sans/res/font/font.xml +++ b/app/src/sans/res/font/font.xml @@ -4,4 +4,8 @@ android:font="@font/product_sans_regular" android:fontStyle="normal" android:fontWeight="400" /> + \ No newline at end of file diff --git a/app/src/sans/res/font/product_sans_bold.ttf b/app/src/sans/res/font/product_sans_bold.ttf new file mode 100755 index 0000000000000000000000000000000000000000..d847195c7600a96700df62b68a5009be200f234d GIT binary patch literal 55548 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 R7%yccB}jfijK^r{{{Z