From 2d3ad54ab5e0d5d8a250910e77441fa4913fb138 Mon Sep 17 00:00:00 2001 From: Hemanth S Date: Sat, 9 May 2020 14:48:29 +0530 Subject: [PATCH] Code refactor --- app/build.gradle | 4 +- .../activities/AlbumDetailsActivity.kt | 31 ++- .../activities/PlaylistDetailActivity.kt | 2 +- .../monkey/retromusic/adapter/HomeAdapter.kt | 16 +- .../adapter/album/AlbumFullWidthAdapter.kt | 1 + .../adapter/base/MediaEntryViewHolder.java | 3 + .../player/adaptive/AdaptiveFragment.kt | 4 +- .../fragments/player/card/CardFragment.kt | 7 +- .../player/circle/CirclePlayerFragment.kt | 3 +- .../player/classic/ClassicPlayerFragment.kt | 13 +- .../fragments/player/fit/FitFragment.kt | 4 +- .../player/flat/FlatPlayerFragment.kt | 16 +- .../player/full/FullPlayerFragment.kt | 8 +- .../fragments/player/normal/PlayerFragment.kt | 10 +- .../player/peak/PeakPlayerFragment.kt | 10 +- .../player/plain/PlainPlayerFragment.kt | 4 +- .../player/simple/SimplePlayerFragment.kt | 10 +- .../player/tiny/TinyPlayerFragment.kt | 2 +- .../monkey/retromusic/loaders/AlbumLoader.kt | 32 ++- .../monkey/retromusic/loaders/SongLoader.kt | 6 +- .../monkey/retromusic/util/PlaylistsUtil.java | 238 +++++++++++++++++- .../views/RetroShapeableImageView.kt | 2 +- .../res/drawable-xxxhdpi/np_adaptive.webp | Bin 144074 -> 128200 bytes .../main/res/drawable-xxxhdpi/np_blur.webp | Bin 112184 -> 113682 bytes .../res/drawable-xxxhdpi/np_blur_card.webp | Bin 76560 -> 77592 bytes .../main/res/drawable-xxxhdpi/np_card.webp | Bin 128628 -> 111976 bytes .../main/res/drawable-xxxhdpi/np_circle.webp | Bin 92604 -> 90096 bytes .../main/res/drawable-xxxhdpi/np_classic.webp | Bin 86256 -> 123596 bytes .../main/res/drawable-xxxhdpi/np_color.webp | Bin 104430 -> 106312 bytes app/src/main/res/drawable-xxxhdpi/np_fit.webp | Bin 141158 -> 135072 bytes .../main/res/drawable-xxxhdpi/np_flat.webp | Bin 111200 -> 111600 bytes .../main/res/drawable-xxxhdpi/np_full.webp | Bin 50584 -> 77220 bytes .../res/drawable-xxxhdpi/np_material.webp | Bin 105446 -> 108078 bytes .../main/res/drawable-xxxhdpi/np_normal.webp | Bin 111300 -> 112156 bytes .../main/res/drawable-xxxhdpi/np_peak.webp | Bin 41400 -> 40882 bytes .../main/res/drawable-xxxhdpi/np_plain.webp | Bin 109984 -> 110002 bytes .../main/res/drawable-xxxhdpi/np_simple.webp | Bin 1039244 -> 109858 bytes .../main/res/drawable-xxxhdpi/np_tiny.webp | Bin 113206 -> 14122 bytes .../drawable/ic_check_circle_white_24dp.xml | 5 + .../main/res/layout/activity_pro_version.xml | 18 +- .../layout/fragment_album_full_card_cover.xml | 7 +- .../res/layout/fragment_classic_controls.xml | 6 +- .../layout/fragment_fit_playback_controls.xml | 3 +- .../layout/fragment_full_player_controls.xml | 6 +- .../fragment_player_playback_controls.xml | 14 +- app/src/main/res/layout/item_artist.xml | 22 +- app/src/main/res/layout/item_contributor.xml | 3 +- .../main/res/layout/item_list_no_image.xml | 4 +- .../res/layout/item_list_quick_actions.xml | 9 +- .../layout/layout_notification_expanded.xml | 3 +- .../layout/metal_section_recycler_view.xml | 7 +- app/src/main/res/layout/pager_item.xml | 40 +-- .../main/res/layout/section_recycler_view.xml | 3 +- app/src/main/res/menu/menu_player.xml | 2 +- 54 files changed, 436 insertions(+), 142 deletions(-) create mode 100644 app/src/main/res/drawable/ic_check_circle_white_24dp.xml diff --git a/app/build.gradle b/app/build.gradle index f3534243..4df52f8a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -24,8 +24,8 @@ android { vectorDrawables.useSupportLibrary = true applicationId "code.name.monkey.retromusic" - versionCode 423 - versionName '3.5.120' + versionCode 424 + versionName '3.5.200' multiDexEnabled true diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/AlbumDetailsActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/AlbumDetailsActivity.kt index 7ea2f661..fbb0fe18 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/AlbumDetailsActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/activities/AlbumDetailsActivity.kt @@ -88,7 +88,6 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), AlbumDetailsView, C slide.excludeTarget(R.id.status_bar, true) slide.excludeTarget(android.R.id.statusBarBackground, true) slide.excludeTarget(android.R.id.navigationBarBackground, true) - window.enterTransition = slide } @@ -348,7 +347,8 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), AlbumDetailsView, C 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 + R.id.action_sort_order_artist_song_duration -> + sortOrder = AlbumSongSortOrder.SONG_DURATION } if (sortOrder != null) { item.isChecked = true @@ -364,8 +364,7 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), AlbumDetailsView, C 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 + .isChecked = true AlbumSongSortOrder.SONG_DURATION -> sortOrder.findItem(R.id.action_sort_order_artist_song_duration) .isChecked = true } @@ -373,7 +372,29 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), AlbumDetailsView, C private fun setSaveSortOrder(sortOrder: String?) { PreferenceUtil.getInstance(this).albumDetailSongSortOrder = sortOrder - reload() + when (sortOrder) { + AlbumSongSortOrder.SONG_TRACK_LIST -> album.songs?.sortWith(Comparator { o1, o2 -> + o1.trackNumber.compareTo( + o2.trackNumber + ) + }) + AlbumSongSortOrder.SONG_A_Z -> album.songs?.sortWith(Comparator { o1, o2 -> + o1.title.compareTo( + o2.title + ) + }) + AlbumSongSortOrder.SONG_Z_A -> album.songs?.sortWith(Comparator { o1, o2 -> + o2.title.compareTo( + o1.title + ) + }) + AlbumSongSortOrder.SONG_DURATION -> album.songs?.sortWith(Comparator { o1, o2 -> + o1.duration.compareTo( + o2.duration + ) + }) + } + album.songs?.let { simpleSongAdapter.swapDataSet(it) } } override fun onMediaStoreChanged() { diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/PlaylistDetailActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/PlaylistDetailActivity.kt index 4541ecb5..2026237a 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/PlaylistDetailActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/activities/PlaylistDetailActivity.kt @@ -185,7 +185,7 @@ class PlaylistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder, Playli } private fun setToolbarTitle(title: String) { - supportActionBar!!.title = title + supportActionBar?.title = title } private fun checkForPadding() { diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/HomeAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/HomeAdapter.kt index c186a14b..bd7a6a56 100644 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/HomeAdapter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/HomeAdapter.kt @@ -113,16 +113,16 @@ class HomeAdapter( inner class ArtistViewHolder(view: View) : AbsHomeViewItem(view) { fun bindView(list: ArrayList, titleRes: Int) { if (list.isNotEmpty()) { + val manager = LinearLayoutManager(activity, LinearLayoutManager.HORIZONTAL, false) + val artistAdapter = ArtistAdapter( + activity, + list, + PreferenceUtil.getInstance(activity).getHomeGridStyle(activity), + null + ) recyclerView.apply { show() - layoutManager = - LinearLayoutManager(activity, LinearLayoutManager.HORIZONTAL, false) - val artistAdapter = ArtistAdapter( - activity, - list, - PreferenceUtil.getInstance(activity).getHomeGridStyle(activity), - null - ) + layoutManager = manager adapter = artistAdapter } title.text = activity.getString(titleRes) diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/album/AlbumFullWidthAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/album/AlbumFullWidthAdapter.kt index 0bea5f5b..9986fe28 100644 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/album/AlbumFullWidthAdapter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/album/AlbumFullWidthAdapter.kt @@ -77,6 +77,7 @@ class AlbumFullWidthAdapter( if (holder.image == null) { return } + AlbumGlideRequest.Builder.from(Glide.with(activity), album.safeGetFirstSong()) .checkIgnoreMediaStore(activity) .generatePalette(activity) diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/base/MediaEntryViewHolder.java b/app/src/main/java/code/name/monkey/retromusic/adapter/base/MediaEntryViewHolder.java index 42a76d01..ddf99c21 100644 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/base/MediaEntryViewHolder.java +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/base/MediaEntryViewHolder.java @@ -41,6 +41,8 @@ public class MediaEntryViewHolder extends AbstractDraggableSwipeableItemViewHold @Nullable public ImageView image; + @Nullable + public ImageView artistImage; @Nullable public ImageView playerImage; @@ -87,6 +89,7 @@ public class MediaEntryViewHolder extends AbstractDraggableSwipeableItemViewHold text = itemView.findViewById(R.id.text); image = itemView.findViewById(R.id.image); + artistImage = itemView.findViewById(R.id.artistImage); playerImage = itemView.findViewById(R.id.player_image); time = itemView.findViewById(R.id.time); diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/adaptive/AdaptiveFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/adaptive/AdaptiveFragment.kt index 8a0d96d7..4453d728 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/adaptive/AdaptiveFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/adaptive/AdaptiveFragment.kt @@ -196,8 +196,8 @@ class AdaptiveFragment : AbsPlayerFragment(), MusicProgressViewUpdateHelper.Call } override fun onColorChanged(color: MediaNotificationProcessor) { - playbackControlsFragment.setDark(color.backgroundColor) - lastColor = color.backgroundColor + playbackControlsFragment.setDark(color.primaryTextColor) + lastColor = color.primaryTextColor callbacks?.onPaletteColorChanged() ToolbarContentTintHelper.colorizeToolbar( playerToolbar, diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/card/CardFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/card/CardFragment.kt index f44721b3..011bc12b 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/card/CardFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/card/CardFragment.kt @@ -45,10 +45,9 @@ class CardFragment : AbsPlayerFragment() { } override fun onColorChanged(color: MediaNotificationProcessor) { - playbackControlsFragment.setDark(color.backgroundColor) - lastColor = color.backgroundColor - callbacks!!.onPaletteColorChanged() - + playbackControlsFragment.setDark(color.primaryTextColor) + lastColor = color.primaryTextColor + callbacks?.onPaletteColorChanged() ToolbarContentTintHelper.colorizeToolbar(playerToolbar, Color.WHITE, activity) } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/circle/CirclePlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/circle/CirclePlayerFragment.kt index f433c7d2..e6e2d7d8 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/circle/CirclePlayerFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/circle/CirclePlayerFragment.kt @@ -129,7 +129,7 @@ class CirclePlayerFragment : AbsPlayerFragment(), Callback, OnAudioVolumeChanged if (audioVolumeObserver == null) { audioVolumeObserver = AudioVolumeObserver(requireActivity()) } - audioVolumeObserver!!.register(AudioManager.STREAM_MUSIC, this) + audioVolumeObserver?.register(AudioManager.STREAM_MUSIC, this) val audioManager = audioManager if (audioManager != null) { @@ -163,6 +163,7 @@ class CirclePlayerFragment : AbsPlayerFragment(), Callback, OnAudioVolumeChanged get() = Color.BLACK override fun onColorChanged(color: MediaNotificationProcessor) { + } override fun onFavoriteToggled() { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/classic/ClassicPlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/classic/ClassicPlayerFragment.kt index 30e0c8cb..9a2b1751 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/classic/ClassicPlayerFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/classic/ClassicPlayerFragment.kt @@ -7,7 +7,6 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.LinearLayout import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.Toolbar import androidx.core.view.ViewCompat @@ -66,7 +65,6 @@ class ClassicPlayerFragment : AbsPlayerFragment(), View.OnLayoutChangeListener, playerQueueSheet.contentPaddingRight, playerQueueSheet.contentPaddingBottom ) - val corner = (1 - slideOffset) * DensityUtil.dip2px(requireContext(), 16f) shapeDrawable.interpolation = 1 - slideOffset } @@ -120,6 +118,11 @@ class ClassicPlayerFragment : AbsPlayerFragment(), View.OnLayoutChangeListener, 0 ).build() ) + ToolbarContentTintHelper.colorizeToolbar( + playerToolbar, + Color.WHITE, + requireActivity() + ) } private fun hideVolumeIfAvailable() { @@ -270,6 +273,12 @@ class ClassicPlayerFragment : AbsPlayerFragment(), View.OnLayoutChangeListener, updateRepeatState() updateShuffleState() updatePrevNextColor() + + ToolbarContentTintHelper.colorizeToolbar( + playerToolbar, + Color.WHITE, + requireActivity() + ) } override fun toggleFavorite(song: Song) { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/fit/FitFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/fit/FitFragment.kt index 97b367aa..dbacacbf 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/fit/FitFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/fit/FitFragment.kt @@ -44,8 +44,8 @@ class FitFragment : AbsPlayerFragment() { } override fun onColorChanged(color: MediaNotificationProcessor) { - playbackControlsFragment.setDark(color.backgroundColor) - lastColor = color.backgroundColor + playbackControlsFragment.setDark(color.primaryTextColor) + lastColor = color.primaryTextColor callbacks?.onPaletteColorChanged() ToolbarContentTintHelper.colorizeToolbar( playerToolbar, diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/flat/FlatPlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/flat/FlatPlayerFragment.kt index ae838c8e..763b4b76 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/flat/FlatPlayerFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/flat/FlatPlayerFragment.kt @@ -29,13 +29,13 @@ class FlatPlayerFragment : AbsPlayerFragment() { } private var valueAnimator: ValueAnimator? = null - private lateinit var flatPlaybackControlsFragment: FlatPlaybackControlsFragment + private lateinit var controlsFragment: FlatPlaybackControlsFragment private var lastColor: Int = 0 override val paletteColor: Int get() = lastColor private fun setUpSubFragments() { - flatPlaybackControlsFragment = + controlsFragment = childFragmentManager.findFragmentById(R.id.playbackControlsFragment) as FlatPlaybackControlsFragment val playerAlbumCoverFragment = childFragmentManager.findFragmentById(R.id.playerAlbumCoverFragment) as PlayerAlbumCoverFragment @@ -55,11 +55,11 @@ class FlatPlayerFragment : AbsPlayerFragment() { private fun colorize(i: Int) { if (valueAnimator != null) { - valueAnimator!!.cancel() + valueAnimator?.cancel() } valueAnimator = ValueAnimator.ofObject(ArgbEvaluator(), android.R.color.transparent, i) - valueAnimator!!.addUpdateListener { animation -> + valueAnimator?.addUpdateListener { animation -> val drawable = DrawableGradient( GradientDrawable.Orientation.TOP_BOTTOM, intArrayOf(animation.animatedValue as Int, android.R.color.transparent), 0 @@ -67,7 +67,7 @@ class FlatPlayerFragment : AbsPlayerFragment() { colorGradientBackground?.background = drawable } - valueAnimator!!.setDuration(ViewUtil.RETRO_MUSIC_ANIM_TIME.toLong()).start() + valueAnimator?.setDuration(ViewUtil.RETRO_MUSIC_ANIM_TIME.toLong())?.start() } override fun onCreateView( @@ -84,11 +84,11 @@ class FlatPlayerFragment : AbsPlayerFragment() { } override fun onShow() { - flatPlaybackControlsFragment.show() + controlsFragment.show() } override fun onHide() { - flatPlaybackControlsFragment.hide() + controlsFragment.hide() onBackPressed() } @@ -106,7 +106,7 @@ class FlatPlayerFragment : AbsPlayerFragment() { override fun onColorChanged(color: MediaNotificationProcessor) { lastColor = color.backgroundColor - flatPlaybackControlsFragment.setDark(color.backgroundColor) + controlsFragment.setDark(color.primaryTextColor) callbacks?.onPaletteColorChanged() val isLight = ColorUtil.isColorLight(color.backgroundColor) val iconColor = if (PreferenceUtil.getInstance(requireContext()).adaptiveColor) diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/full/FullPlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/full/FullPlayerFragment.kt index 50ddade6..8fc7bda2 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/full/FullPlayerFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/full/FullPlayerFragment.kt @@ -125,7 +125,7 @@ class FullPlayerFragment : AbsPlayerFragment(), MusicProgressViewUpdateHelper.Ca private var lastColor: Int = 0 override val paletteColor: Int get() = lastColor - private lateinit var fullPlaybackControlsFragment: FullPlaybackControlsFragment + private lateinit var controlsFragment: FullPlaybackControlsFragment private fun setUpPlayerToolbar() { playerToolbar.apply { @@ -174,7 +174,7 @@ class FullPlayerFragment : AbsPlayerFragment(), MusicProgressViewUpdateHelper.Ca } private fun setUpSubFragments() { - fullPlaybackControlsFragment = + controlsFragment = childFragmentManager.findFragmentById(R.id.playbackControlsFragment) as FullPlaybackControlsFragment val playerAlbumCoverFragment = @@ -199,14 +199,14 @@ class FullPlayerFragment : AbsPlayerFragment(), MusicProgressViewUpdateHelper.Ca override fun onColorChanged(color: MediaNotificationProcessor) { lastColor = color.backgroundColor - fullPlaybackControlsFragment.setDark(color.backgroundColor) + controlsFragment.setDark(color.primaryTextColor) callbacks?.onPaletteColorChanged() ToolbarContentTintHelper.colorizeToolbar(playerToolbar, Color.WHITE, activity) } override fun onFavoriteToggled() { toggleFavorite(MusicPlayerRemote.currentSong) - fullPlaybackControlsFragment.onFavoriteToggled() + controlsFragment.onFavoriteToggled() } override fun toggleFavorite(song: Song) { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/normal/PlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/normal/PlayerFragment.kt index 7c618122..1e5f8210 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/normal/PlayerFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/normal/PlayerFragment.kt @@ -28,7 +28,7 @@ class PlayerFragment : AbsPlayerFragment() { override val paletteColor: Int get() = lastColor - private lateinit var playbackControlsFragment: PlayerPlaybackControlsFragment + private lateinit var controlsFragment: PlayerPlaybackControlsFragment private var valueAnimator: ValueAnimator? = null @@ -58,11 +58,11 @@ class PlayerFragment : AbsPlayerFragment() { } override fun onShow() { - playbackControlsFragment.show() + controlsFragment.show() } override fun onHide() { - playbackControlsFragment.hide() + controlsFragment.hide() onBackPressed() } @@ -75,7 +75,7 @@ class PlayerFragment : AbsPlayerFragment() { } override fun onColorChanged(color: MediaNotificationProcessor) { - playbackControlsFragment.setDark(color.backgroundColor) + controlsFragment.setDark(color.primaryTextColor) lastColor = color.backgroundColor callbacks?.onPaletteColorChanged() @@ -118,7 +118,7 @@ class PlayerFragment : AbsPlayerFragment() { private fun setUpSubFragments() { - playbackControlsFragment = + controlsFragment = childFragmentManager.findFragmentById(R.id.playbackControlsFragment) as PlayerPlaybackControlsFragment val playerAlbumCoverFragment = childFragmentManager.findFragmentById(R.id.playerAlbumCoverFragment) as PlayerAlbumCoverFragment diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/peak/PeakPlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/peak/PeakPlayerFragment.kt index 696c552b..ea5748e4 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/peak/PeakPlayerFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/peak/PeakPlayerFragment.kt @@ -40,7 +40,7 @@ import kotlinx.android.synthetic.main.fragment_peak_player.* class PeakPlayerFragment : AbsPlayerFragment() { - private lateinit var playbackControlsFragment: PeakPlayerControlFragment + private lateinit var controlsFragment: PeakPlayerControlFragment private var lastColor: Int = 0 override fun onCreateView( @@ -62,7 +62,7 @@ class PeakPlayerFragment : AbsPlayerFragment() { } private fun setUpSubFragments() { - playbackControlsFragment = + controlsFragment = childFragmentManager.findFragmentById(R.id.playbackControlsFragment) as PeakPlayerControlFragment } @@ -101,8 +101,8 @@ class PeakPlayerFragment : AbsPlayerFragment() { get() = lastColor override fun onColorChanged(color: MediaNotificationProcessor) { - playbackControlsFragment.setDark(color.backgroundColor) - lastColor = color.backgroundColor + controlsFragment.setDark(color.primaryTextColor) + lastColor = color.primaryTextColor callbacks?.onPaletteColorChanged() } @@ -127,7 +127,7 @@ class PeakPlayerFragment : AbsPlayerFragment() { .build() .into(object : RetroMusicColoredTarget(playerImage) { override fun onColorReady(colors: MediaNotificationProcessor) { - playbackControlsFragment.setDark(colors.backgroundColor) + controlsFragment.setDark(colors.primaryTextColor) } }) } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/plain/PlainPlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/plain/PlainPlayerFragment.kt index c268d95c..87527eaf 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/plain/PlainPlayerFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/plain/PlainPlayerFragment.kt @@ -95,8 +95,8 @@ class PlainPlayerFragment : AbsPlayerFragment() { } override fun onColorChanged(color: MediaNotificationProcessor) { - plainPlaybackControlsFragment.setDark(color.backgroundColor) - lastColor = color.backgroundColor + plainPlaybackControlsFragment.setDark(color.primaryTextColor) + lastColor = color.primaryTextColor callbacks!!.onPaletteColorChanged() ToolbarContentTintHelper.colorizeToolbar( playerToolbar, diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/simple/SimplePlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/simple/SimplePlayerFragment.kt index 3bb0a55f..d2e31026 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/simple/SimplePlayerFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/simple/SimplePlayerFragment.kt @@ -29,7 +29,7 @@ class SimplePlayerFragment : AbsPlayerFragment() { override val paletteColor: Int get() = lastColor - private lateinit var simplePlaybackControlsFragment: SimplePlaybackControlsFragment + private lateinit var controlsFragment: SimplePlaybackControlsFragment override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -49,16 +49,16 @@ class SimplePlayerFragment : AbsPlayerFragment() { val playerAlbumCoverFragment = childFragmentManager.findFragmentById(R.id.playerAlbumCoverFragment) as PlayerAlbumCoverFragment playerAlbumCoverFragment.setCallbacks(this) - simplePlaybackControlsFragment = + controlsFragment = childFragmentManager.findFragmentById(R.id.playbackControlsFragment) as SimplePlaybackControlsFragment } override fun onShow() { - simplePlaybackControlsFragment.show() + controlsFragment.show() } override fun onHide() { - simplePlaybackControlsFragment.hide() + controlsFragment.hide() } override fun onBackPressed(): Boolean { @@ -72,7 +72,7 @@ class SimplePlayerFragment : AbsPlayerFragment() { override fun onColorChanged(color: MediaNotificationProcessor) { lastColor = color.backgroundColor callbacks?.onPaletteColorChanged() - simplePlaybackControlsFragment.setDark(color.backgroundColor) + controlsFragment.setDark(color.primaryTextColor) ToolbarContentTintHelper.colorizeToolbar( playerToolbar, ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal), diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/tiny/TinyPlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/tiny/TinyPlayerFragment.kt index 1f25883d..ee96810f 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/tiny/TinyPlayerFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/tiny/TinyPlayerFragment.kt @@ -75,7 +75,7 @@ class TinyPlayerFragment : AbsPlayerFragment(), MusicProgressViewUpdateHelper.Ca override fun onColorChanged(color: MediaNotificationProcessor) { val colorFinal = if (PreferenceUtil.getInstance(requireContext()).adaptiveColor) { - color.backgroundColor + color.primaryTextColor } else { ThemeStore.accentColor(requireContext()) } 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 index 49342d0d..7971994d 100644 --- a/app/src/main/java/code/name/monkey/retromusic/loaders/AlbumLoader.kt +++ b/app/src/main/java/code/name/monkey/retromusic/loaders/AlbumLoader.kt @@ -16,13 +16,13 @@ package code.name.monkey.retromusic.loaders import android.content.Context import android.provider.MediaStore.Audio.AudioColumns +import code.name.monkey.retromusic.App +import code.name.monkey.retromusic.helper.SortOrder import code.name.monkey.retromusic.model.Album import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.util.PreferenceUtil import java.util.* import kotlin.collections.ArrayList -import kotlin.collections.isNotEmpty -import kotlin.collections.sortWith /** @@ -108,12 +108,32 @@ object AlbumLoader { } private fun sortSongsByTrackNumber(album: Album) { - album.songs?.sortWith(Comparator { o1, o2 -> o1.trackNumber.compareTo(o2.trackNumber) }) + when (PreferenceUtil.getInstance(App.getContext()).albumDetailSongSortOrder) { + SortOrder.AlbumSongSortOrder.SONG_TRACK_LIST -> album.songs?.sortWith(Comparator { o1, o2 -> + o1.trackNumber.compareTo( + o2.trackNumber + ) + }) + SortOrder.AlbumSongSortOrder.SONG_A_Z -> album.songs?.sortWith(Comparator { o1, o2 -> + o1.title.compareTo( + o2.title + ) + }) + SortOrder.AlbumSongSortOrder.SONG_Z_A -> album.songs?.sortWith(Comparator { o1, o2 -> + o2.title.compareTo( + o1.title + ) + }) + SortOrder.AlbumSongSortOrder.SONG_DURATION -> album.songs?.sortWith(Comparator { o1, o2 -> + o1.duration.compareTo( + o2.duration + ) + }) + } } private fun getSongLoaderSortOrder(context: Context): String { - return PreferenceUtil.getInstance(context).albumSortOrder + ", " + PreferenceUtil.getInstance( - context - ).albumSongSortOrder + return PreferenceUtil.getInstance(context).albumSortOrder + ", " + + PreferenceUtil.getInstance(context).albumSongSortOrder } } 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 aa9fc369..f95df2cf 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 @@ -31,7 +31,6 @@ import java.util.* object SongLoader { - fun getAllSongs( context: Context ): ArrayList { @@ -128,9 +127,8 @@ object SongLoader { return context.contentResolver.query( MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, baseProjection, - selectionFinal + " AND " + MediaStore.Audio.Media.DURATION + ">= " + (PreferenceUtil.getInstance( - context - ).filterLength * 1000), + selectionFinal + " AND " + MediaStore.Audio.Media.DURATION + ">= " + + (PreferenceUtil.getInstance(context).filterLength * 1000), selectionValuesFinal, sortOrder ) 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 b6df807e..12b6a9d7 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 @@ -42,7 +42,241 @@ import static android.provider.MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI; public class PlaylistsUtil { - public static void addToPlaylist(@NonNull Context context, + public static int createPlaylist(@NonNull final Context context, @Nullable final String name) { + int id = -1; + if (name != null && name.length() > 0) { + try { + Cursor cursor = context.getContentResolver().query(EXTERNAL_CONTENT_URI, + new String[]{MediaStore.Audio.Playlists._ID}, + MediaStore.Audio.PlaylistsColumns.NAME + "=?", new String[]{name}, + null); + if (cursor == null || cursor.getCount() < 1) { + final ContentValues values = new ContentValues(1); + values.put(MediaStore.Audio.PlaylistsColumns.NAME, name); + final Uri uri = context.getContentResolver().insert( + EXTERNAL_CONTENT_URI, + values); + if (uri != null) { + // Necessary because somehow the MediaStoreObserver is not notified when adding a playlist + context.getContentResolver().notifyChange(Uri.parse("content://media"), null); + Toast.makeText(context, context.getResources().getString( + R.string.created_playlist_x, name), Toast.LENGTH_SHORT).show(); + id = Integer.parseInt(uri.getLastPathSegment()); + } + } else { + // Playlist exists + if (cursor.moveToFirst()) { + id = cursor.getInt(cursor.getColumnIndex(MediaStore.Audio.Playlists._ID)); + } + } + if (cursor != null) { + cursor.close(); + } + } catch (SecurityException e) { + e.printStackTrace(); + Toast.makeText(context, "Don't have permission for adding to playlist", Toast.LENGTH_SHORT).show(); + } + } + if (id == -1) { + Toast.makeText(context, context.getResources().getString( + R.string.could_not_create_playlist), Toast.LENGTH_SHORT).show(); + } + return id; + } + + public static void deletePlaylists(@NonNull final Context context, @NonNull final ArrayList playlists) { + final StringBuilder selection = new StringBuilder(); + selection.append(MediaStore.Audio.Playlists._ID + " IN ("); + for (int i = 0; i < playlists.size(); i++) { + selection.append(playlists.get(i).id); + if (i < playlists.size() - 1) { + selection.append(","); + } + } + selection.append(")"); + try { + context.getContentResolver().delete(EXTERNAL_CONTENT_URI, selection.toString(), null); + context.getContentResolver().notifyChange(Uri.parse("content://media"), null); + } catch (SecurityException ignored) { + } + } + + public static void addToPlaylist(@NonNull final Context context, final Song song, final int playlistId, final boolean showToastOnFinish) { + List helperList = new ArrayList<>(); + helperList.add(song); + addToPlaylist(context, helperList, playlistId, showToastOnFinish); + } + + public static void addToPlaylist(@NonNull final Context context, @NonNull final List songs, final int playlistId, final boolean showToastOnFinish) { + final int size = songs.size(); + final ContentResolver resolver = context.getContentResolver(); + final String[] projection = new String[]{ + "max(" + MediaStore.Audio.Playlists.Members.PLAY_ORDER + ")", + }; + final Uri uri = MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId); + Cursor cursor = null; + int base = 0; + + try { + try { + cursor = resolver.query(uri, projection, null, null, null); + + if (cursor != null && cursor.moveToFirst()) { + base = cursor.getInt(0) + 1; + } + } finally { + if (cursor != null) { + cursor.close(); + } + } + + int numInserted = 0; + for (int offSet = 0; offSet < size; offSet += 1000) + numInserted += resolver.bulkInsert(uri, makeInsertItems(songs, offSet, 1000, base)); + + if (showToastOnFinish) { + Toast.makeText(context, context.getResources().getString( + R.string.inserted_x_songs_into_playlist_x, numInserted, getNameForPlaylist(context, playlistId)), Toast.LENGTH_SHORT).show(); + } + } catch (SecurityException ignored) { + ignored.printStackTrace(); + Toast.makeText(context, "Don't have permission for adding to playlist", Toast.LENGTH_SHORT).show(); + } + } + + @NonNull + public static ContentValues[] makeInsertItems(@NonNull final List songs, final int offset, int len, final int base) { + if (offset + len > songs.size()) { + len = songs.size() - offset; + } + + ContentValues[] contentValues = new ContentValues[len]; + + 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).getId()); + } + return contentValues; + } + + public static String getNameForPlaylist(@NonNull final Context context, final long id) { + try { + Cursor cursor = context.getContentResolver().query(EXTERNAL_CONTENT_URI, + new String[]{MediaStore.Audio.PlaylistsColumns.NAME}, + BaseColumns._ID + "=?", + new String[]{String.valueOf(id)}, + null); + if (cursor != null) { + try { + if (cursor.moveToFirst()) { + return cursor.getString(0); + } + } finally { + cursor.close(); + } + } + } catch (SecurityException ignored) { + } + return ""; + } + + public static void removeFromPlaylist(@NonNull final Context context, @NonNull final Song song, int playlistId) { + Uri uri = MediaStore.Audio.Playlists.Members.getContentUri( + "external", playlistId); + String selection = MediaStore.Audio.Playlists.Members.AUDIO_ID + " =?"; + String[] selectionArgs = new String[]{String.valueOf(song.getId())}; + + try { + context.getContentResolver().delete(uri, selection, selectionArgs); + } catch (SecurityException ignored) { + } + } + + public static void removeFromPlaylist(@NonNull final Context context, @NonNull final List songs) { + final int playlistId = songs.get(0).getPlaylistId(); + Uri uri = MediaStore.Audio.Playlists.Members.getContentUri( + "external", playlistId); + String selectionArgs[] = new String[songs.size()]; + for (int i = 0; i < selectionArgs.length; i++) { + selectionArgs[i] = String.valueOf(songs.get(i).getIdInPlayList()); + } + String selection = MediaStore.Audio.Playlists.Members._ID + " in ("; + //noinspection unused + for (String selectionArg : selectionArgs) selection += "?, "; + selection = selection.substring(0, selection.length() - 2) + ")"; + + try { + context.getContentResolver().delete(uri, selection, selectionArgs); + } catch (SecurityException ignored) { + } + } + + public static boolean doPlaylistContains(@NonNull final Context context, final long playlistId, final int songId) { + if (playlistId != -1) { + try { + Cursor c = context.getContentResolver().query( + MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId), + new String[]{MediaStore.Audio.Playlists.Members.AUDIO_ID}, MediaStore.Audio.Playlists.Members.AUDIO_ID + "=?", new String[]{String.valueOf(songId)}, null); + int count = 0; + if (c != null) { + count = c.getCount(); + c.close(); + } + return count > 0; + } catch (SecurityException ignored) { + } + } + return false; + } + + public static boolean moveItem(@NonNull final Context context, int playlistId, int from, int to) { + return MediaStore.Audio.Playlists.Members.moveItem(context.getContentResolver(), + playlistId, from, to); + } + + public static void renamePlaylist(@NonNull final Context context, final long id, final String newName) { + ContentValues contentValues = new ContentValues(); + contentValues.put(MediaStore.Audio.PlaylistsColumns.NAME, newName); + try { + context.getContentResolver().update(EXTERNAL_CONTENT_URI, + contentValues, + MediaStore.Audio.Playlists._ID + "=?", + new String[]{String.valueOf(id)}); + context.getContentResolver().notifyChange(Uri.parse("content://media"), null); + } catch (SecurityException ignored) { + } + } + + public static File savePlaylist(Context context, Playlist playlist) throws IOException { + return M3UWriter.write(context, new File(Environment.getExternalStorageDirectory(), "Playlists"), playlist); + } + + public static boolean doesPlaylistExist(@NonNull final Context context, final int playlistId) { + return playlistId != -1 && doesPlaylistExist(context, + MediaStore.Audio.Playlists._ID + "=?", + new String[]{String.valueOf(playlistId)}); + } + + public static boolean doesPlaylistExist(@NonNull final Context context, final String name) { + return doesPlaylistExist(context, + MediaStore.Audio.PlaylistsColumns.NAME + "=?", + new String[]{name}); + } + + private static boolean doesPlaylistExist(@NonNull Context context, @NonNull final String selection, @NonNull final String[] values) { + Cursor cursor = context.getContentResolver().query(EXTERNAL_CONTENT_URI, + new String[]{}, selection, values, null); + + boolean exists = false; + if (cursor != null) { + exists = cursor.getCount() != 0; + cursor.close(); + } + return exists; + } + + /*public static void addToPlaylist(@NonNull Context context, @NonNull List songs, int playlistId, boolean showToastOnFinish) { @@ -273,5 +507,5 @@ public class PlaylistsUtil { cursor.close(); } return exists; - } + }*/ } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/views/RetroShapeableImageView.kt b/app/src/main/java/code/name/monkey/retromusic/views/RetroShapeableImageView.kt index 5e305122..df479136 100644 --- a/app/src/main/java/code/name/monkey/retromusic/views/RetroShapeableImageView.kt +++ b/app/src/main/java/code/name/monkey/retromusic/views/RetroShapeableImageView.kt @@ -37,7 +37,7 @@ class RetroShapeableImageView @JvmOverloads constructor( typedArray.recycle() } - fun updateCornerSize(cornerSize: Float) { + private fun updateCornerSize(cornerSize: Float) { shapeAppearanceModel = ShapeAppearanceModel.Builder() .setAllCorners(CornerFamily.ROUNDED, cornerSize) .build() diff --git a/app/src/main/res/drawable-xxxhdpi/np_adaptive.webp b/app/src/main/res/drawable-xxxhdpi/np_adaptive.webp index c60809ec9bb4861df0371063f4600b03128075a8..b4c80dc7bd7af4c2cf9cbe22da46ab2a3da596b2 100644 GIT binary patch literal 128200 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