diff --git a/app/src/main/assets/retro-changelog.html b/app/src/main/assets/retro-changelog.html
index d110ac751..fc5a504cb 100644
--- a/app/src/main/assets/retro-changelog.html
+++ b/app/src/main/assets/retro-changelog.html
@@ -67,6 +67,7 @@
v5.8.3
What's New
+ - Added a new MD3 now playing theme
- Swipe down to dismiss Mini player
- Add support for Just Black with Material You
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsSlidingMusicPanelActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsSlidingMusicPanelActivity.kt
index 62101f2d4..d26e25015 100644
--- a/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsSlidingMusicPanelActivity.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsSlidingMusicPanelActivity.kt
@@ -49,6 +49,7 @@ import code.name.monkey.retromusic.fragments.player.flat.FlatPlayerFragment
import code.name.monkey.retromusic.fragments.player.full.FullPlayerFragment
import code.name.monkey.retromusic.fragments.player.gradient.GradientPlayerFragment
import code.name.monkey.retromusic.fragments.player.material.MaterialFragment
+import code.name.monkey.retromusic.fragments.player.md3.MD3PlayerFragment
import code.name.monkey.retromusic.fragments.player.normal.PlayerFragment
import code.name.monkey.retromusic.fragments.player.peek.PeekPlayerFragment
import code.name.monkey.retromusic.fragments.player.plain.PlainPlayerFragment
@@ -457,6 +458,7 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
Peek -> PeekPlayerFragment()
Circle -> CirclePlayerFragment()
Classic -> ClassicPlayerFragment()
+ MD3 -> MD3PlayerFragment()
else -> PlayerFragment()
} // must implement AbsPlayerFragment
supportFragmentManager.commit {
diff --git a/app/src/main/java/code/name/monkey/retromusic/extensions/ColorExtensions.kt b/app/src/main/java/code/name/monkey/retromusic/extensions/ColorExtensions.kt
index 20d6d14d8..cfd09a642 100644
--- a/app/src/main/java/code/name/monkey/retromusic/extensions/ColorExtensions.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/extensions/ColorExtensions.kt
@@ -292,6 +292,9 @@ fun Context.accentColorVariant(): Int {
inline val @receiver:ColorInt Int.isColorLight
get() = ColorUtil.isColorLight(this)
+inline val @receiver:ColorInt Int.lightColor
+ get() = ColorUtil.withAlpha(this, 0.5F)
+
inline val @receiver:ColorInt Int.lighterColor
get() = ColorUtil.lightenColor(this)
diff --git a/app/src/main/java/code/name/monkey/retromusic/extensions/ViewExtensions.kt b/app/src/main/java/code/name/monkey/retromusic/extensions/ViewExtensions.kt
index 2ca9bbd37..0147255a0 100644
--- a/app/src/main/java/code/name/monkey/retromusic/extensions/ViewExtensions.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/extensions/ViewExtensions.kt
@@ -37,6 +37,7 @@ import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.RetroUtil
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.bottomsheet.BottomSheetBehavior
+import com.google.android.material.card.MaterialCardView
import dev.chrisbanes.insetter.applyInsetter
@Suppress("UNCHECKED_CAST")
@@ -163,6 +164,23 @@ fun BottomSheetBehavior<*>.peekHeightAnimate(value: Int): Animator {
}
}
+fun MaterialCardView.animateRadius(cornerRadius: Float, pause: Boolean = true) {
+ ValueAnimator.ofFloat(radius, cornerRadius).apply {
+ addUpdateListener { radius = animatedValue as Float }
+ start()
+ }
+ ValueAnimator.ofInt(measuredWidth, if (pause) (height * 1.5).toInt() else height).apply {
+ addUpdateListener {
+ updateLayoutParams { width = animatedValue as Int }
+ }
+ start()
+ }
+}
+
+fun MaterialCardView.animateToCircle() {
+ animateRadius(measuredHeight / 2F, pause = false)
+}
+
fun View.focusAndShowKeyboard() {
/**
* This is to be called when the window already has focus.
diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/NowPlayingScreen.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/NowPlayingScreen.kt
index 448df7112..0b2e6b1ff 100644
--- a/app/src/main/java/code/name/monkey/retromusic/fragments/NowPlayingScreen.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/fragments/NowPlayingScreen.kt
@@ -39,6 +39,7 @@ enum class NowPlayingScreen constructor(
Full(R.string.full, R.drawable.np_full, 2, AlbumCoverStyle.Full),
Gradient(R.string.gradient, R.drawable.np_gradient, 17, AlbumCoverStyle.Full),
Material(R.string.material, R.drawable.np_material, 11, AlbumCoverStyle.Normal),
+ MD3(R.string.md3, R.drawable.np_normal, 18, AlbumCoverStyle.Normal),
Normal(R.string.normal, R.drawable.np_normal, 0, AlbumCoverStyle.Normal),
Peek(R.string.peek, R.drawable.np_peek, 14, AlbumCoverStyle.Normal),
Plain(R.string.plain, R.drawable.np_plain, 3, AlbumCoverStyle.Normal),
diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/md3/MD3PlaybackControlsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/md3/MD3PlaybackControlsFragment.kt
new file mode 100644
index 000000000..17809656b
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/md3/MD3PlaybackControlsFragment.kt
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 2020 Hemanth Savarla.
+ *
+ * Licensed under the GNU General Public License v3
+ *
+ * This is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ */
+package code.name.monkey.retromusic.fragments.player.md3
+
+import android.animation.ObjectAnimator
+import android.os.Bundle
+import android.view.View
+import android.view.animation.DecelerateInterpolator
+import android.view.animation.LinearInterpolator
+import android.widget.ImageButton
+import android.widget.SeekBar
+import android.widget.TextView
+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.databinding.FragmentMd3PlayerPlaybackControlsBinding
+import code.name.monkey.retromusic.extensions.*
+import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
+import code.name.monkey.retromusic.fragments.base.goToAlbum
+import code.name.monkey.retromusic.fragments.base.goToArtist
+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.util.MusicUtil
+import code.name.monkey.retromusic.util.PreferenceUtil
+import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
+
+class MD3PlaybackControlsFragment :
+ AbsPlayerControlsFragment(R.layout.fragment_md3_player_playback_controls) {
+
+ private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
+ private var _binding: FragmentMd3PlayerPlaybackControlsBinding? = null
+ private val binding get() = _binding!!
+
+ override val progressSlider: SeekBar
+ get() = binding.progressSlider
+
+ override val shuffleButton: ImageButton
+ get() = binding.shuffleButton
+
+ override val repeatButton: ImageButton
+ get() = binding.repeatButton
+
+ override val nextButton: ImageButton
+ get() = binding.nextButton
+
+ override val previousButton: ImageButton
+ get() = binding.previousButton
+
+ override val songTotalTime: TextView
+ get() = binding.songTotalTime
+
+ override val songCurrentProgress: TextView
+ get() = binding.songCurrentProgress
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ _binding = FragmentMd3PlayerPlaybackControlsBinding.bind(view)
+ setUpMusicControllers()
+ binding.playPauseButton.setOnClickListener {
+ if (MusicPlayerRemote.isPlaying) {
+ MusicPlayerRemote.pauseSong()
+ } else {
+ MusicPlayerRemote.resumePlaying()
+ }
+ }
+ binding.title.isSelected = true
+ binding.text.isSelected = true
+ binding.title.setOnClickListener {
+ goToAlbum(requireActivity())
+ }
+ binding.text.setOnClickListener {
+ goToArtist(requireActivity())
+ }
+ }
+
+ override fun setColor(color: MediaNotificationProcessor) {
+ val colorBg = ATHUtil.resolveColor(requireContext(), android.R.attr.colorBackground)
+ if (ColorUtil.isColorLight(colorBg)) {
+ lastPlaybackControlsColor =
+ MaterialValueHelper.getSecondaryTextColor(requireContext(), true)
+ lastDisabledPlaybackControlsColor =
+ MaterialValueHelper.getSecondaryDisabledTextColor(requireContext(), true)
+ } else {
+ lastPlaybackControlsColor =
+ MaterialValueHelper.getPrimaryTextColor(requireContext(), false)
+ lastDisabledPlaybackControlsColor =
+ MaterialValueHelper.getPrimaryDisabledTextColor(requireContext(), false)
+ }
+
+ val colorFinal = if (PreferenceUtil.isAdaptiveColor) {
+ color.primaryTextColor
+ } else {
+ ThemeStore.accentColor(requireContext())
+ }.ripAlpha()
+
+ TintHelper.setTintAuto(
+ binding.playPauseButton,
+ MaterialValueHelper.getPrimaryTextColor(
+ requireContext(),
+ ColorUtil.isColorLight(colorFinal)
+ ),
+ false
+ )
+ binding.playPauseCard.setCardBackgroundColor(colorFinal)
+
+ binding.progressSlider.applyColor(colorFinal)
+ volumeFragment?.setTintable(colorFinal)
+ updateRepeatState()
+ updateShuffleState()
+ updatePrevNextColor()
+ updatePlayPauseDrawableState()
+ }
+
+ private fun updateSong() {
+ val song = MusicPlayerRemote.currentSong
+ binding.title.text = song.title
+ binding.text.text = song.artistName
+
+ if (PreferenceUtil.isSongInfo) {
+ binding.songInfo.text = getSongInfo(song)
+ binding.songInfo.show()
+ } else {
+ binding.songInfo.hide()
+ }
+ }
+
+ 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()
+ }
+
+ private fun setUpPlayPauseFab() {
+ binding.playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
+ updatePlayPauseDrawableState()
+ }
+
+ private fun updatePlayPauseDrawableState() {
+ if (MusicPlayerRemote.isPlaying) {
+ binding.playPauseButton.setImageResource(R.drawable.ic_pause_outline_small)
+ binding.playPauseCard.animateRadius(40F)
+ } else {
+ binding.playPauseButton.setImageResource(R.drawable.ic_play_arrow_outline_small)
+ binding.playPauseCard.animateToCircle()
+ }
+ }
+
+ private fun setUpMusicControllers() {
+ setUpPlayPauseFab()
+ }
+
+ public override fun show() {
+ binding.playPauseButton.animate()
+ .scaleX(1f)
+ .scaleY(1f)
+ .rotation(360f)
+ .setInterpolator(DecelerateInterpolator())
+ .start()
+ }
+
+ public override fun hide() {
+ binding.playPauseButton.apply {
+ scaleX = 0f
+ scaleY = 0f
+ rotation = 0f
+ }
+ }
+
+ override fun onUpdateProgressViews(progress: Int, total: Int) {
+ binding.progressSlider.max = total
+
+ val animator = ObjectAnimator.ofInt(binding.progressSlider, "progress", progress)
+ animator.duration = SLIDER_ANIMATION_TIME
+ animator.interpolator = LinearInterpolator()
+ animator.start()
+
+ binding.songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
+ binding.songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ _binding = null
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/md3/MD3PlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/md3/MD3PlayerFragment.kt
new file mode 100644
index 000000000..e0640349b
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/md3/MD3PlayerFragment.kt
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2020 Hemanth Savarla.
+ *
+ * Licensed under the GNU General Public License v3
+ *
+ * This is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ */
+package code.name.monkey.retromusic.fragments.player.md3
+
+import android.os.Bundle
+import android.view.View
+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.databinding.FragmentMd3PlayerBinding
+import code.name.monkey.retromusic.extensions.drawAboveSystemBars
+import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
+import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
+import code.name.monkey.retromusic.helper.MusicPlayerRemote
+import code.name.monkey.retromusic.model.Song
+import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
+
+class MD3PlayerFragment : AbsPlayerFragment(R.layout.fragment_md3_player) {
+
+ private var lastColor: Int = 0
+ override val paletteColor: Int
+ get() = lastColor
+
+ private lateinit var controlsFragment: MD3PlaybackControlsFragment
+
+ private var _binding: FragmentMd3PlayerBinding? = null
+ private val binding get() = _binding!!
+
+ override fun onShow() {
+ controlsFragment.show()
+ }
+
+ override fun onHide() {
+ controlsFragment.hide()
+ onBackPressed()
+ }
+
+ override fun onBackPressed(): Boolean {
+ return false
+ }
+
+ override fun toolbarIconColor(): Int {
+ return ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal)
+ }
+
+ override fun onColorChanged(color: MediaNotificationProcessor) {
+ controlsFragment.setColor(color)
+ lastColor = color.backgroundColor
+ libraryViewModel.updateColor(color.backgroundColor)
+
+ ToolbarContentTintHelper.colorizeToolbar(
+ binding.playerToolbar,
+ ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal),
+ requireActivity()
+ )
+ }
+
+ override fun toggleFavorite(song: Song) {
+ super.toggleFavorite(song)
+ if (song.id == MusicPlayerRemote.currentSong.id) {
+ updateIsFavorite()
+ }
+ }
+
+ override fun onFavoriteToggled() {
+ toggleFavorite(MusicPlayerRemote.currentSong)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ _binding = FragmentMd3PlayerBinding.bind(view)
+ setUpSubFragments()
+ setUpPlayerToolbar()
+ playerToolbar().drawAboveSystemBars()
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ _binding = null
+ }
+
+ private fun setUpSubFragments() {
+ controlsFragment =
+ childFragmentManager.findFragmentById(R.id.playbackControlsFragment) as MD3PlaybackControlsFragment
+ val playerAlbumCoverFragment =
+ childFragmentManager.findFragmentById(R.id.playerAlbumCoverFragment) as PlayerAlbumCoverFragment
+ playerAlbumCoverFragment.setCallbacks(this)
+ }
+
+ private fun setUpPlayerToolbar() {
+ binding.playerToolbar.inflateMenu(R.menu.menu_player)
+ //binding.playerToolbar.menu.setUpWithIcons()
+ binding.playerToolbar.setNavigationOnClickListener { requireActivity().onBackPressed() }
+ binding.playerToolbar.setOnMenuItemClickListener(this)
+
+ ToolbarContentTintHelper.colorizeToolbar(
+ binding.playerToolbar,
+ ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal),
+ requireActivity()
+ )
+ }
+
+ override fun onServiceConnected() {
+ updateIsFavorite()
+ }
+
+ override fun onPlayingMetaChanged() {
+ updateIsFavorite()
+ }
+
+ override fun playerToolbar(): Toolbar {
+ return binding.playerToolbar
+ }
+
+ companion object {
+
+ fun newInstance(): MD3PlayerFragment {
+ return MD3PlayerFragment()
+ }
+ }
+}
diff --git a/app/src/main/res/drawable/ic_pause_outline_small.xml b/app/src/main/res/drawable/ic_pause_outline_small.xml
new file mode 100644
index 000000000..aadd8e81d
--- /dev/null
+++ b/app/src/main/res/drawable/ic_pause_outline_small.xml
@@ -0,0 +1,16 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_play_arrow_outline_small.xml b/app/src/main/res/drawable/ic_play_arrow_outline_small.xml
new file mode 100644
index 000000000..1437cf963
--- /dev/null
+++ b/app/src/main/res/drawable/ic_play_arrow_outline_small.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/layout-land/fragment_md3_player.xml b/app/src/main/res/layout-land/fragment_md3_player.xml
new file mode 100644
index 000000000..f67be7aaa
--- /dev/null
+++ b/app/src/main/res/layout-land/fragment_md3_player.xml
@@ -0,0 +1,83 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_md3_player.xml b/app/src/main/res/layout/fragment_md3_player.xml
new file mode 100644
index 000000000..d42cd56b1
--- /dev/null
+++ b/app/src/main/res/layout/fragment_md3_player.xml
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_md3_player_playback_controls.xml b/app/src/main/res/layout/fragment_md3_player_playback_controls.xml
new file mode 100644
index 000000000..70513475e
--- /dev/null
+++ b/app/src/main/res/layout/fragment_md3_player_playback_controls.xml
@@ -0,0 +1,210 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ 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 c22f24b3a..136218a79 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -239,6 +239,7 @@
Material
Error
Permission error
+ MD3
Name
Most played
Never