diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackPanelFragment.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackPanelFragment.kt
index 942e67bd8..67a343723 100644
--- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackPanelFragment.kt
+++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackPanelFragment.kt
@@ -15,7 +15,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-
+
package org.oxycblt.auxio.playback
import android.content.ActivityNotFoundException
@@ -33,7 +33,6 @@ import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
import dagger.hilt.android.AndroidEntryPoint
-import kotlin.math.abs
import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.FragmentPlaybackPanelBinding
import org.oxycblt.auxio.detail.DetailViewModel
@@ -41,8 +40,8 @@ import org.oxycblt.auxio.detail.Show
import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.music.Song
-import org.oxycblt.auxio.music.resolveNames
-import org.oxycblt.auxio.playback.carousel.CoverCarouselAdapter
+import org.oxycblt.auxio.playback.pager.PlaybackPageListener
+import org.oxycblt.auxio.playback.pager.PlaybackPagerAdapter
import org.oxycblt.auxio.playback.queue.QueueViewModel
import org.oxycblt.auxio.playback.state.RepeatMode
import org.oxycblt.auxio.playback.ui.StyledSeekBar
@@ -55,6 +54,7 @@ import org.oxycblt.auxio.util.share
import org.oxycblt.auxio.util.showToast
import org.oxycblt.auxio.util.systemBarInsetsCompat
import java.lang.reflect.Field
+import kotlin.math.abs
/**
* A [ViewBindingFragment] more information about the currently playing song, alongside all
@@ -68,13 +68,14 @@ import java.lang.reflect.Field
class PlaybackPanelFragment :
ViewBindingFragment(),
Toolbar.OnMenuItemClickListener,
- StyledSeekBar.Listener {
+ StyledSeekBar.Listener,
+ PlaybackPageListener {
private val playbackModel: PlaybackViewModel by activityViewModels()
private val musicModel: MusicViewModel by activityViewModels()
private val detailModel: DetailViewModel by activityViewModels()
private val queueModel: QueueViewModel by activityViewModels()
private var equalizerLauncher: ActivityResultLauncher? = null
- private var coverAdapter: CoverCarouselAdapter? = null
+ private var coverAdapter: PlaybackPagerAdapter? = null
override fun onCreateBinding(inflater: LayoutInflater) =
FragmentPlaybackPanelBinding.inflate(inflater)
@@ -105,7 +106,7 @@ class PlaybackPanelFragment :
}
// cover carousel adapter
- coverAdapter = CoverCarouselAdapter()
+ coverAdapter = PlaybackPagerAdapter(this, viewLifecycleOwner)
binding.playbackCoverPager.apply {
adapter = coverAdapter
registerOnPageChangeCallback(OnCoverChangedCallback(queueModel))
@@ -113,26 +114,6 @@ class PlaybackPanelFragment :
recycler.isNestedScrollingEnabled = false
}
- // Set up marquee on song information, alongside click handlers that navigate to each
- // respective item.
- binding.playbackSong.apply {
- isSelected = true
- setOnClickListener {
- playbackModel.song.value?.let {
- detailModel.showAlbum(it)
- playbackModel.openMain()
- }
- }
- }
- binding.playbackArtist.apply {
- isSelected = true
- setOnClickListener { navigateToCurrentArtist() }
- }
- binding.playbackAlbum.apply {
- isSelected = true
- setOnClickListener { navigateToCurrentAlbum() }
- }
-
binding.playbackSeekBar.listener = this
// Set up actions
@@ -159,10 +140,6 @@ class PlaybackPanelFragment :
equalizerLauncher = null
coverAdapter = null
binding.playbackToolbar.setOnMenuItemClickListener(null)
- // Marquee elements leak if they are not disabled when the views are destroyed.
- binding.playbackSong.isSelected = false
- binding.playbackArtist.isSelected = false
- binding.playbackAlbum.isSelected = false
}
override fun onMenuItemClick(item: MenuItem) =
@@ -235,12 +212,7 @@ class PlaybackPanelFragment :
}
val binding = requireBinding()
- val context = requireContext()
logD("Updating song display: $song")
- // binding.playbackCover.bind(song)
- binding.playbackSong.text = song.name.resolve(context)
- binding.playbackArtist.text = song.artists.resolveNames(context)
- binding.playbackAlbum.text = song.album.name.resolve(context)
binding.playbackSeekBar.durationDs = song.durationMs.msToDs()
}
@@ -280,15 +252,23 @@ class PlaybackPanelFragment :
is Show.AlbumArtistDecision,
is Show.GenreDetails,
is Show.PlaylistDetails,
- null -> {}
+ null -> {
+ }
}
}
- private fun navigateToCurrentArtist() {
+ override fun navigateToCurrentSong() {
+ playbackModel.song.value?.let {
+ detailModel.showAlbum(it)
+ playbackModel.openMain()
+ }
+ }
+
+ override fun navigateToCurrentArtist() {
playbackModel.song.value?.let(detailModel::showArtist)
}
- private fun navigateToCurrentAlbum() {
+ override fun navigateToCurrentAlbum() {
playbackModel.song.value?.let { detailModel.showAlbum(it.album) }
}
@@ -306,12 +286,13 @@ class PlaybackPanelFragment :
super.onPageScrollStateChanged(state)
if (state == ViewPager2.SCROLL_STATE_IDLE &&
targetPosition != RecyclerView.NO_POSITION &&
- targetPosition != viewModel.index.value) {
+ targetPosition != viewModel.index.value
+ ) {
viewModel.goto(targetPosition)
}
}
}
-
+
private companion object {
val VP_RECYCLER_FIELD: Field by lazyReflectedField(ViewPager2::class, "mRecyclerView")
}
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/carousel/CoverCarouselAdapter.kt b/app/src/main/java/org/oxycblt/auxio/playback/carousel/CoverCarouselAdapter.kt
deleted file mode 100644
index 0102d5b50..000000000
--- a/app/src/main/java/org/oxycblt/auxio/playback/carousel/CoverCarouselAdapter.kt
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (c) 2023 Auxio Project
- * CoverCarouselAdapter.kt is part of Auxio.
- *
- * This program 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 program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package org.oxycblt.auxio.playback.carousel
-
-import android.view.ViewGroup
-import androidx.recyclerview.widget.DiffUtil
-import androidx.recyclerview.widget.RecyclerView
-import org.oxycblt.auxio.databinding.ItemCoverBinding
-import org.oxycblt.auxio.list.adapter.FlexibleListAdapter
-import org.oxycblt.auxio.music.Song
-import org.oxycblt.auxio.util.inflater
-
-class CoverCarouselAdapter :
- FlexibleListAdapter(CoverViewHolder.DIFF_CALLBACK) {
-
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CoverViewHolder {
- return CoverViewHolder.from(parent)
- }
-
- override fun onBindViewHolder(holder: CoverViewHolder, position: Int) {
- holder.bind(getItem(position))
- }
-}
-
-class CoverViewHolder private constructor(private val binding: ItemCoverBinding) :
- RecyclerView.ViewHolder(binding.root) {
- /**
- * Bind new data to this instance.
- *
- * @param item The new [Song] to bind.
- */
- fun bind(item: Song) {
- binding.playbackCover.bind(item)
- }
-
- companion object {
- /**
- * Create a new instance.
- *
- * @param parent The parent to inflate this instance from.
- * @return A new instance.
- */
- fun from(parent: ViewGroup) =
- CoverViewHolder(ItemCoverBinding.inflate(parent.context.inflater, parent, false))
-
- /** A comparator that can be used with DiffUtil. */
- val DIFF_CALLBACK =
- object : DiffUtil.ItemCallback() {
- override fun areItemsTheSame(oldItem: Song, newItem: Song) =
- oldItem.uid == newItem.uid
-
- override fun areContentsTheSame(oldItem: Song, newItem: Song) =
- oldItem.album.coverUri == newItem.album.coverUri
- }
- }
-}
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/pager/PlaybackPageListener.kt b/app/src/main/java/org/oxycblt/auxio/playback/pager/PlaybackPageListener.kt
new file mode 100644
index 000000000..6cf93aa22
--- /dev/null
+++ b/app/src/main/java/org/oxycblt/auxio/playback/pager/PlaybackPageListener.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2023 Auxio Project
+ * PlaybackPageListener.kt is part of Auxio.
+ *
+ * This program 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package org.oxycblt.auxio.playback.pager
+
+interface PlaybackPageListener {
+
+ fun navigateToCurrentArtist()
+
+ fun navigateToCurrentAlbum()
+
+ fun navigateToCurrentSong()
+}
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/pager/PlaybackPagerAdapter.kt b/app/src/main/java/org/oxycblt/auxio/playback/pager/PlaybackPagerAdapter.kt
new file mode 100644
index 000000000..1445c3a74
--- /dev/null
+++ b/app/src/main/java/org/oxycblt/auxio/playback/pager/PlaybackPagerAdapter.kt
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2023 Auxio Project
+ * PlaybackPagerAdapter.kt is part of Auxio.
+ *
+ * This program 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package org.oxycblt.auxio.playback.pager
+
+import android.view.View
+import android.view.ViewGroup
+import androidx.lifecycle.DefaultLifecycleObserver
+import androidx.lifecycle.LifecycleOwner
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.RecyclerView
+import org.oxycblt.auxio.R
+import org.oxycblt.auxio.databinding.ItemPlaybackSongBinding
+import org.oxycblt.auxio.list.adapter.FlexibleListAdapter
+import org.oxycblt.auxio.music.Song
+import org.oxycblt.auxio.music.resolveNames
+import org.oxycblt.auxio.util.inflater
+import kotlin.jvm.internal.Intrinsics
+
+class PlaybackPagerAdapter(
+ private val listener: PlaybackPageListener,
+ private val lifecycleOwner: LifecycleOwner
+) : FlexibleListAdapter(CoverViewHolder.DIFF_CALLBACK) {
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CoverViewHolder {
+ return CoverViewHolder.from(parent, listener).also {
+ lifecycleOwner.lifecycle.addObserver(it)
+ }
+ }
+
+ override fun onBindViewHolder(holder: CoverViewHolder, position: Int) {
+ holder.bind(getItem(position))
+ }
+
+ override fun onViewRecycled(holder: CoverViewHolder) {
+ holder.recycle()
+ super.onViewRecycled(holder)
+ }
+}
+
+class CoverViewHolder
+private constructor(
+ private val binding: ItemPlaybackSongBinding,
+ private val listener: PlaybackPageListener
+) : RecyclerView.ViewHolder(binding.root), DefaultLifecycleObserver, View.OnClickListener {
+
+ init {
+ binding.playbackSong.setOnClickListener(this)
+ binding.playbackArtist.setOnClickListener(this)
+ binding.playbackAlbum.setOnClickListener(this)
+ }
+
+ override fun onClick(v: View) {
+ when (v.id) {
+ R.id.playback_album -> listener.navigateToCurrentAlbum()
+ R.id.playback_artist -> listener.navigateToCurrentArtist()
+ R.id.playback_song -> listener.navigateToCurrentSong()
+ }
+ }
+
+ override fun onResume(owner: LifecycleOwner) {
+ super.onResume(owner)
+ setSelected(true)
+ }
+
+ override fun onPause(owner: LifecycleOwner) {
+ super.onPause(owner)
+ setSelected(false)
+ }
+
+ override fun onDestroy(owner: LifecycleOwner) {
+ super.onDestroy(owner)
+ owner.lifecycle.removeObserver(this)
+ }
+
+ /**
+ * Bind new data to this instance.
+ *
+ * @param item The new [Song] to bind.
+ */
+ fun bind(item: Song) {
+ binding.playbackCover.bind(item)
+ val context = binding.root.context
+ binding.playbackSong.text = item.name.resolve(context)
+ binding.playbackArtist.text = item.artists.resolveNames(context)
+ binding.playbackAlbum.text = item.album.name.resolve(context)
+ setSelected(true)
+ }
+
+ fun recycle() {
+ // Marquee elements leak if they are not disabled when the views are destroyed.
+ setSelected(false)
+ }
+
+ private fun setSelected(value: Boolean) {
+ binding.playbackSong.isSelected = value
+ binding.playbackArtist.isSelected = value
+ binding.playbackAlbum.isSelected = value
+ }
+
+ companion object {
+ /**
+ * Create a new instance.
+ *
+ * @param parent The parent to inflate this instance from.
+ * @return A new instance.
+ */
+ fun from(parent: ViewGroup, listener: PlaybackPageListener) =
+ CoverViewHolder(
+ ItemPlaybackSongBinding.inflate(parent.context.inflater, parent, false),
+ listener
+ )
+
+ /** A comparator that can be used with DiffUtil. */
+ val DIFF_CALLBACK =
+ object : DiffUtil.ItemCallback() {
+ override fun areItemsTheSame(oldItem: Song, newItem: Song) =
+ oldItem.uid == newItem.uid
+
+ override fun areContentsTheSame(oldItem: Song, newItem: Song): Boolean {
+ return Intrinsics.areEqual(oldItem, newItem)
+ }
+ }
+ }
+}
diff --git a/app/src/main/res/layout-h480dp/fragment_playback_panel.xml b/app/src/main/res/layout-h480dp/fragment_playback_panel.xml
index 234c0581e..28f69b2b2 100644
--- a/app/src/main/res/layout-h480dp/fragment_playback_panel.xml
+++ b/app/src/main/res/layout-h480dp/fragment_playback_panel.xml
@@ -18,49 +18,12 @@
-
-
-
-
-
-
-
+ app:layout_constraintTop_toBottomOf="@+id/playback_toolbar" />
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout-sw600dp/fragment_playback_panel.xml b/app/src/main/res/layout-sw600dp/fragment_playback_panel.xml
index c0b2af850..adeac9d27 100644
--- a/app/src/main/res/layout-sw600dp/fragment_playback_panel.xml
+++ b/app/src/main/res/layout-sw600dp/fragment_playback_panel.xml
@@ -18,49 +18,12 @@
-
-
-
-
-
-
-
+ app:layout_constraintTop_toBottomOf="@+id/playback_toolbar" />
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_playback_panel.xml b/app/src/main/res/layout/fragment_playback_panel.xml
index 23ce8412e..e873722e1 100644
--- a/app/src/main/res/layout/fragment_playback_panel.xml
+++ b/app/src/main/res/layout/fragment_playback_panel.xml
@@ -18,56 +18,20 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/item_cover.xml b/app/src/main/res/layout/item_cover.xml
deleted file mode 100644
index 223738538..000000000
--- a/app/src/main/res/layout/item_cover.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
diff --git a/app/src/main/res/layout/item_playback_song.xml b/app/src/main/res/layout/item_playback_song.xml
new file mode 100644
index 000000000..3e8c0c6a1
--- /dev/null
+++ b/app/src/main/res/layout/item_playback_song.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file