diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt
index e27920402..99ea70a5c 100644
--- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt
+++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt
@@ -23,7 +23,7 @@ import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
-import android.widget.SeekBar
+import androidx.core.view.iterator
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
@@ -43,7 +43,7 @@ import org.oxycblt.auxio.util.logD
* also make material sliders usable maybe.
* @author OxygenCobalt
*/
-class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
+class PlaybackFragment : Fragment() {
private val playbackModel: PlaybackViewModel by activityViewModels()
private val detailModel: DetailViewModel by activityViewModels()
private val binding by memberBinding(FragmentPlaybackBinding::inflate) {
@@ -90,9 +90,9 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
// Make marquee of song title work
binding.playbackSong.isSelected = true
- binding.playbackSeekBar.apply {
- setOnSeekBarChangeListener(this@PlaybackFragment)
- isEnabled = true
+
+ binding.playbackSeekBar.onConfirmListener = { pos ->
+ playbackModel.setPosition(pos)
}
// --- VIEWMODEL SETUP --
@@ -102,7 +102,7 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
logD("Updating song display to ${song.name}.")
binding.song = song
- binding.playbackSeekBar.max = song.seconds.toInt()
+ binding.playbackSeekBar.setDuration(song.seconds)
} else {
logD("No song is being played, leaving.")
@@ -124,14 +124,8 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
binding.playbackLoop.setImageResource(resId)
}
- playbackModel.isSeeking.observe(viewLifecycleOwner) { isSeeking ->
- binding.playbackDurationCurrent.isActivated = isSeeking
- }
-
- playbackModel.positionAsProgress.observe(viewLifecycleOwner) { pos ->
- if (!playbackModel.isSeeking.value!!) {
- binding.playbackSeekBar.progress = pos
- }
+ playbackModel.position.observe(viewLifecycleOwner) { pos ->
+ binding.playbackSeekBar.setProgress(pos)
}
playbackModel.nextItemsInQueue.observe(viewLifecycleOwner) {
@@ -165,25 +159,4 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
// inactive. We just need to set the flag.
queueItem.isEnabled = !(userQueue.isEmpty() && nextQueue.isEmpty())
}
-
- // --- SEEK CALLBACKS ---
-
- override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
- if (fromUser) {
- // Only update the display when seeking, as to have PlaybackService seek
- // [causing possible buffering] on every movement is really odd.
- playbackModel.updatePositionDisplay(progress)
- }
- }
-
- override fun onStartTrackingTouch(seekBar: SeekBar) {
- playbackModel.setSeekingStatus(true)
- }
-
- override fun onStopTrackingTouch(seekBar: SeekBar) {
- playbackModel.setSeekingStatus(false)
-
- // Confirm the position when seeking stops.
- playbackModel.setPosition(seekBar.progress)
- }
}
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackSeekBar.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackSeekBar.kt
new file mode 100644
index 000000000..6519f9f80
--- /dev/null
+++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackSeekBar.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2021 Auxio Project
+ * PlaybackSeeker.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
+
+import android.content.Context
+import android.util.AttributeSet
+import android.widget.SeekBar
+import androidx.constraintlayout.widget.ConstraintLayout
+import org.oxycblt.auxio.databinding.ViewSeekBarBinding
+import org.oxycblt.auxio.music.toDuration
+import org.oxycblt.auxio.util.inflater
+
+/**
+ * A custom view that bundles together a seekbar with a current duration and a total duration.
+ * The sub-views are specifically laid out so that the seekbar has an adequate touch height while
+ * still not having gobs of whitespace everywhere.
+ * TODO: Fix the padding on this thing
+ * @author OxygenCobalt
+ */
+class PlaybackSeekBar @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleRes: Int = -1
+) : ConstraintLayout(context, attrs, defStyleRes), SeekBar.OnSeekBarChangeListener {
+ private val binding = ViewSeekBarBinding.inflate(context.inflater, this, true)
+ private val isSeeking: Boolean get() = binding.playbackDurationCurrent.isActivated
+
+ var onConfirmListener: ((Long) -> Unit)? = null
+
+ init {
+ binding.playbackSeekBar.setOnSeekBarChangeListener(this)
+ }
+
+ fun setProgress(seconds: Long) {
+ // Don't update the progress while we are seeking, that will make the SeekBar jump around.
+ if (!isSeeking) {
+ binding.playbackSeekBar.progress = seconds.toInt()
+ binding.playbackDurationCurrent.text = seconds.toDuration()
+ }
+ }
+
+ fun setDuration(seconds: Long) {
+ binding.playbackSeekBar.max = seconds.toInt()
+ binding.playbackSongDuration.text = seconds.toDuration()
+ }
+
+ override fun onStartTrackingTouch(seekbar: SeekBar) {
+ binding.playbackDurationCurrent.isActivated = true
+ }
+
+ override fun onStopTrackingTouch(seekbar: SeekBar) {
+ binding.playbackDurationCurrent.isActivated = false
+ onConfirmListener?.invoke(seekbar.progress.toLong())
+ }
+
+ override fun onProgressChanged(seekbar: SeekBar, value: Int, fromUser: Boolean) {
+ if (fromUser) {
+ // Don't actually seek yet when the user moves the progress bar, as to make our
+ // player seek during every movement is both inefficient and weird.
+ binding.playbackDurationCurrent.text = value.toLong().toDuration()
+ }
+ }
+}
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt
index 95ddccb8f..dacb1ed83 100644
--- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt
+++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt
@@ -32,7 +32,6 @@ import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.MusicStore
import org.oxycblt.auxio.music.Parent
import org.oxycblt.auxio.music.Song
-import org.oxycblt.auxio.music.toDuration
import org.oxycblt.auxio.playback.queue.QueueAdapter
import org.oxycblt.auxio.playback.state.LoopMode
import org.oxycblt.auxio.playback.state.PlaybackMode
@@ -68,7 +67,6 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
private val mIsInUserQueue = MutableLiveData(false)
// Other
- private val mIsSeeking = MutableLiveData(false)
private var mIntentUri: Uri? = null
/** The current song. */
@@ -92,13 +90,6 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
/** The current repeat mode, see [LoopMode] for more information */
val loopMode: LiveData get() = mLoopMode
- val isSeeking: LiveData get() = mIsSeeking
-
- /** The position as a duration string. */
- val formattedPosition = Transformations.map(mPosition) {
- it.toDuration()
- }
-
/** The position as SeekBar progress. */
val positionAsProgress = Transformations.map(mPosition) {
if (mSong.value != null) it.toInt() else 0
@@ -223,17 +214,8 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
/**
* Update the position and push it to [PlaybackStateManager]
*/
- fun setPosition(progress: Int) {
- playbackManager.seekTo((progress * 1000).toLong())
- }
-
- /**
- * Update the position without pushing the change to [PlaybackStateManager].
- * This is used during seek events to give the user an idea of where they're seeking to.
- * @param progress The SeekBar progress to seek to.
- */
- fun updatePositionDisplay(progress: Int) {
- mPosition.value = progress.toLong()
+ fun setPosition(progress: Long) {
+ playbackManager.seekTo((progress * 1000))
}
// --- QUEUE FUNCTIONS ---
@@ -428,15 +410,6 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
mLoopMode.value = playbackManager.loopMode
}
- // --- OTHER FUNCTIONS ---
-
- /**
- * Set whether the seeking indicator should be highlighted
- */
- fun setSeekingStatus(isSeeking: Boolean) {
- mIsSeeking.value = isSeeking
- }
-
// --- OVERRIDES ---
override fun onCleared() {
@@ -452,9 +425,7 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
}
override fun onPositionUpdate(position: Long) {
- if (!mIsSeeking.value!!) {
- mPosition.value = position / 1000
- }
+ mPosition.value = position / 1000
}
override fun onQueueUpdate(queue: List) {
diff --git a/app/src/main/res/layout-land/fragment_playback.xml b/app/src/main/res/layout-land/fragment_playback.xml
index 80a165152..417613444 100644
--- a/app/src/main/res/layout-land/fragment_playback.xml
+++ b/app/src/main/res/layout-land/fragment_playback.xml
@@ -104,55 +104,31 @@
app:layout_constraintTop_toBottomOf="@+id/playback_artist"
tools:text="Album Name" />
-
-
-
-
-
+ app:layout_constraintTop_toBottomOf="@+id/playback_album" />
@@ -204,12 +177,12 @@
style="@style/Widget.Auxio.Button.Unbounded"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginEnd="@dimen/spacing_small"
+ android:layout_marginEnd="@dimen/spacing_medium"
android:contentDescription="@string/desc_shuffle"
android:onClick="@{() -> playbackModel.invertShuffleStatus()}"
android:src="@drawable/ic_shuffle"
app:layout_constraintBottom_toBottomOf="@+id/playback_skip_next"
- app:layout_constraintEnd_toEndOf="@+id/playback_song_duration"
+ app:layout_constraintEnd_toEndOf="@+id/playback_seek_bar"
app:layout_constraintTop_toTopOf="@+id/playback_skip_next"
app:tint="@color/sel_accented" />
diff --git a/app/src/main/res/layout-xlarge-land/fragment_playback.xml b/app/src/main/res/layout-xlarge-land/fragment_playback.xml
index 656c96d6d..dd88ab917 100644
--- a/app/src/main/res/layout-xlarge-land/fragment_playback.xml
+++ b/app/src/main/res/layout-xlarge-land/fragment_playback.xml
@@ -106,42 +106,17 @@
app:layout_constraintTop_toBottomOf="@+id/playback_artist"
tools:text="Album Name" />
-
-
-
-
-
+ app:layout_constraintTop_toBottomOf="@+id/playback_album" />
diff --git a/app/src/main/res/layout-xlarge/fragment_playback.xml b/app/src/main/res/layout-xlarge/fragment_playback.xml
index 299e0f90e..5a4ed2a8e 100644
--- a/app/src/main/res/layout-xlarge/fragment_playback.xml
+++ b/app/src/main/res/layout-xlarge/fragment_playback.xml
@@ -83,7 +83,6 @@
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_mid_huge"
android:layout_marginEnd="@dimen/spacing_mid_huge"
- android:layout_marginBottom="@dimen/spacing_medium"
android:onClick="@{() -> detailModel.navToItem(playbackModel.song.album)}"
android:text="@{song.album.name}"
app:layout_constraintBottom_toTopOf="@+id/playback_seek_bar"
@@ -91,40 +90,15 @@
app:layout_constraintStart_toStartOf="parent"
tools:text="Album Name" />
-
-
-
-
-
+ app:layout_constraintStart_toStartOf="parent" />
-
-
-
-
-
+ app:layout_constraintStart_toStartOf="parent" />
diff --git a/app/src/main/res/layout/view_seek_bar.xml b/app/src/main/res/layout/view_seek_bar.xml
new file mode 100644
index 000000000..930ae5673
--- /dev/null
+++ b/app/src/main/res/layout/view_seek_bar.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/styles_ui.xml b/app/src/main/res/values/styles_ui.xml
index 61c611c3e..9fca85998 100644
--- a/app/src/main/res/values/styles_ui.xml
+++ b/app/src/main/res/values/styles_ui.xml
@@ -47,7 +47,6 @@
@@ -202,7 +201,6 @@
- @dimen/size_btn_large
- @dimen/size_btn_large
- @drawable/ui_circle_ripple
- - @dimen/elevation_normal
- @string/desc_play_pause
- ?attr/colorSurface
- fitCenter