playback: make dedicated seekbar view

Make a dedicated seekbar view so that does the layout magic necessary
to have an adequate touch target while not taking up too much space.
Isolating this makes handling the playback layout's view much easier.
This commit is contained in:
OxygenCobalt 2021-10-05 19:30:45 -06:00
parent 4b6610d236
commit 5ebe17d0ad
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
9 changed files with 167 additions and 213 deletions

View file

@ -23,7 +23,7 @@ import android.view.LayoutInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.SeekBar import androidx.core.view.iterator
import androidx.core.view.updatePadding import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
@ -43,7 +43,7 @@ import org.oxycblt.auxio.util.logD
* also make material sliders usable maybe. * also make material sliders usable maybe.
* @author OxygenCobalt * @author OxygenCobalt
*/ */
class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener { class PlaybackFragment : Fragment() {
private val playbackModel: PlaybackViewModel by activityViewModels() private val playbackModel: PlaybackViewModel by activityViewModels()
private val detailModel: DetailViewModel by activityViewModels() private val detailModel: DetailViewModel by activityViewModels()
private val binding by memberBinding(FragmentPlaybackBinding::inflate) { private val binding by memberBinding(FragmentPlaybackBinding::inflate) {
@ -90,9 +90,9 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
// Make marquee of song title work // Make marquee of song title work
binding.playbackSong.isSelected = true binding.playbackSong.isSelected = true
binding.playbackSeekBar.apply {
setOnSeekBarChangeListener(this@PlaybackFragment) binding.playbackSeekBar.onConfirmListener = { pos ->
isEnabled = true playbackModel.setPosition(pos)
} }
// --- VIEWMODEL SETUP -- // --- VIEWMODEL SETUP --
@ -102,7 +102,7 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
logD("Updating song display to ${song.name}.") logD("Updating song display to ${song.name}.")
binding.song = song binding.song = song
binding.playbackSeekBar.max = song.seconds.toInt() binding.playbackSeekBar.setDuration(song.seconds)
} else { } else {
logD("No song is being played, leaving.") logD("No song is being played, leaving.")
@ -124,14 +124,8 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
binding.playbackLoop.setImageResource(resId) binding.playbackLoop.setImageResource(resId)
} }
playbackModel.isSeeking.observe(viewLifecycleOwner) { isSeeking -> playbackModel.position.observe(viewLifecycleOwner) { pos ->
binding.playbackDurationCurrent.isActivated = isSeeking binding.playbackSeekBar.setProgress(pos)
}
playbackModel.positionAsProgress.observe(viewLifecycleOwner) { pos ->
if (!playbackModel.isSeeking.value!!) {
binding.playbackSeekBar.progress = pos
}
} }
playbackModel.nextItemsInQueue.observe(viewLifecycleOwner) { playbackModel.nextItemsInQueue.observe(viewLifecycleOwner) {
@ -165,25 +159,4 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
// inactive. We just need to set the flag. // inactive. We just need to set the flag.
queueItem.isEnabled = !(userQueue.isEmpty() && nextQueue.isEmpty()) 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)
}
} }

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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()
}
}
}

View file

@ -32,7 +32,6 @@ import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.MusicStore import org.oxycblt.auxio.music.MusicStore
import org.oxycblt.auxio.music.Parent import org.oxycblt.auxio.music.Parent
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.toDuration
import org.oxycblt.auxio.playback.queue.QueueAdapter import org.oxycblt.auxio.playback.queue.QueueAdapter
import org.oxycblt.auxio.playback.state.LoopMode import org.oxycblt.auxio.playback.state.LoopMode
import org.oxycblt.auxio.playback.state.PlaybackMode import org.oxycblt.auxio.playback.state.PlaybackMode
@ -68,7 +67,6 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
private val mIsInUserQueue = MutableLiveData(false) private val mIsInUserQueue = MutableLiveData(false)
// Other // Other
private val mIsSeeking = MutableLiveData(false)
private var mIntentUri: Uri? = null private var mIntentUri: Uri? = null
/** The current song. */ /** The current song. */
@ -92,13 +90,6 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
/** The current repeat mode, see [LoopMode] for more information */ /** The current repeat mode, see [LoopMode] for more information */
val loopMode: LiveData<LoopMode> get() = mLoopMode val loopMode: LiveData<LoopMode> get() = mLoopMode
val isSeeking: LiveData<Boolean> get() = mIsSeeking
/** The position as a duration string. */
val formattedPosition = Transformations.map(mPosition) {
it.toDuration()
}
/** The position as SeekBar progress. */ /** The position as SeekBar progress. */
val positionAsProgress = Transformations.map(mPosition) { val positionAsProgress = Transformations.map(mPosition) {
if (mSong.value != null) it.toInt() else 0 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] * Update the position and push it to [PlaybackStateManager]
*/ */
fun setPosition(progress: Int) { fun setPosition(progress: Long) {
playbackManager.seekTo((progress * 1000).toLong()) playbackManager.seekTo((progress * 1000))
}
/**
* 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()
} }
// --- QUEUE FUNCTIONS --- // --- QUEUE FUNCTIONS ---
@ -428,15 +410,6 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
mLoopMode.value = playbackManager.loopMode mLoopMode.value = playbackManager.loopMode
} }
// --- OTHER FUNCTIONS ---
/**
* Set whether the seeking indicator should be highlighted
*/
fun setSeekingStatus(isSeeking: Boolean) {
mIsSeeking.value = isSeeking
}
// --- OVERRIDES --- // --- OVERRIDES ---
override fun onCleared() { override fun onCleared() {
@ -452,10 +425,8 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
} }
override fun onPositionUpdate(position: Long) { override fun onPositionUpdate(position: Long) {
if (!mIsSeeking.value!!) {
mPosition.value = position / 1000 mPosition.value = position / 1000
} }
}
override fun onQueueUpdate(queue: List<Song>) { override fun onQueueUpdate(queue: List<Song>) {
mQueue.value = queue mQueue.value = queue

View file

@ -104,55 +104,31 @@
app:layout_constraintTop_toBottomOf="@+id/playback_artist" app:layout_constraintTop_toBottomOf="@+id/playback_artist"
tools:text="Album Name" /> tools:text="Album Name" />
<SeekBar
<org.oxycblt.auxio.playback.PlaybackSeekBar
android:id="@+id/playback_seek_bar" android:id="@+id/playback_seek_bar"
style="@style/Widget.Auxio.SeekBar"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingStart="@dimen/spacing_mid_large" android:layout_marginStart="@dimen/spacing_medium"
android:paddingEnd="@dimen/spacing_mid_large" android:layout_marginEnd="@dimen/spacing_medium"
app:layout_constraintBottom_toTopOf="@+id/playback_play_pause" app:layout_constraintBottom_toTopOf="@+id/playback_play_pause"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5" app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/playback_cover" app:layout_constraintStart_toEndOf="@+id/playback_cover"
app:layout_constraintTop_toBottomOf="@+id/playback_album" app:layout_constraintTop_toBottomOf="@+id/playback_album" />
tools:progress="70" />
<TextView
android:id="@+id/playback_duration_current"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_mid_large"
android:text="@{playbackModel.formattedPosition}"
android:textColor="@color/sel_accented_secondary"
app:layout_constraintBottom_toBottomOf="@+id/playback_seek_bar"
app:layout_constraintStart_toEndOf="@+id/playback_cover"
app:layout_constraintTop_toBottomOf="@+id/playback_seek_bar"
tools:text="11:38" />
<TextView
android:id="@+id/playback_song_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/spacing_mid_large"
android:text="@{song.formattedDuration}"
app:layout_constraintBottom_toBottomOf="@+id/playback_seek_bar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/playback_seek_bar"
tools:text="16:16" />
<ImageButton <ImageButton
android:id="@+id/playback_loop" android:id="@+id/playback_loop"
style="@style/Widget.Auxio.Button.Unbounded" style="@style/Widget.Auxio.Button.Unbounded"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_small" android:layout_marginStart="@dimen/spacing_medium"
android:contentDescription="@string/desc_change_loop" android:contentDescription="@string/desc_change_loop"
android:onClick="@{() -> playbackModel.incrementLoopStatus()}" android:onClick="@{() -> playbackModel.incrementLoopStatus()}"
android:src="@drawable/ic_loop" android:src="@drawable/ic_loop"
app:layout_constraintBottom_toBottomOf="@+id/playback_skip_prev" app:layout_constraintBottom_toBottomOf="@+id/playback_skip_prev"
app:layout_constraintHorizontal_chainStyle="packed" app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="@+id/playback_duration_current" app:layout_constraintStart_toStartOf="@+id/playback_seek_bar"
app:layout_constraintTop_toTopOf="@+id/playback_skip_prev" /> app:layout_constraintTop_toTopOf="@+id/playback_skip_prev" />
<ImageButton <ImageButton
@ -173,16 +149,13 @@
style="@style/Widget.Auxio.Button.Circular" style="@style/Widget.Auxio.Button.Circular"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_medium"
android:layout_marginTop="@dimen/spacing_medium"
android:layout_marginEnd="@dimen/spacing_medium"
android:contentDescription="@string/desc_play_pause" android:contentDescription="@string/desc_play_pause"
android:onClick="@{() -> playbackModel.invertPlayingStatus()}" android:onClick="@{() -> playbackModel.invertPlayingStatus()}"
android:src="@drawable/sel_playing_state" android:src="@drawable/sel_playing_state"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@+id/playback_song_duration" app:layout_constraintEnd_toEndOf="@+id/playback_seek_bar"
app:layout_constraintHorizontal_bias="0.5" app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="@+id/playback_duration_current" app:layout_constraintStart_toEndOf="@+id/playback_cover"
app:layout_constraintTop_toBottomOf="@+id/playback_seek_bar" app:layout_constraintTop_toBottomOf="@+id/playback_seek_bar"
tools:src="@drawable/ic_pause" /> tools:src="@drawable/ic_pause" />
@ -204,12 +177,12 @@
style="@style/Widget.Auxio.Button.Unbounded" style="@style/Widget.Auxio.Button.Unbounded"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="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:contentDescription="@string/desc_shuffle"
android:onClick="@{() -> playbackModel.invertShuffleStatus()}" android:onClick="@{() -> playbackModel.invertShuffleStatus()}"
android:src="@drawable/ic_shuffle" android:src="@drawable/ic_shuffle"
app:layout_constraintBottom_toBottomOf="@+id/playback_skip_next" 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:layout_constraintTop_toTopOf="@+id/playback_skip_next"
app:tint="@color/sel_accented" /> app:tint="@color/sel_accented" />

View file

@ -106,42 +106,17 @@
app:layout_constraintTop_toBottomOf="@+id/playback_artist" app:layout_constraintTop_toBottomOf="@+id/playback_artist"
tools:text="Album Name" /> tools:text="Album Name" />
<SeekBar <org.oxycblt.auxio.playback.PlaybackSeekBar
android:id="@+id/playback_seek_bar" android:id="@+id/playback_seek_bar"
style="@style/Widget.Auxio.SeekBar"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingStart="@dimen/spacing_mid_large" android:layout_marginStart="@dimen/spacing_medium"
android:paddingEnd="@dimen/spacing_mid_large" android:layout_marginEnd="@dimen/spacing_medium"
app:layout_constraintBottom_toTopOf="@+id/playback_play_pause" app:layout_constraintBottom_toTopOf="@+id/playback_play_pause"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5" app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/playback_cover" app:layout_constraintStart_toEndOf="@+id/playback_cover"
app:layout_constraintTop_toBottomOf="@+id/playback_album" app:layout_constraintTop_toBottomOf="@+id/playback_album" />
tools:progress="70" />
<TextView
android:id="@+id/playback_duration_current"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_mid_large"
android:text="@{playbackModel.formattedPosition}"
android:textColor="@color/sel_accented_secondary"
app:layout_constraintBottom_toBottomOf="@+id/playback_seek_bar"
app:layout_constraintStart_toEndOf="@+id/playback_cover"
app:layout_constraintTop_toBottomOf="@+id/playback_seek_bar"
tools:text="11:38" />
<TextView
android:id="@+id/playback_song_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/spacing_mid_large"
android:text="@{song.formattedDuration}"
app:layout_constraintBottom_toBottomOf="@+id/playback_seek_bar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/playback_seek_bar"
tools:text="16:16" />
<ImageButton <ImageButton
android:id="@+id/playback_loop" android:id="@+id/playback_loop"
@ -175,16 +150,13 @@
style="@style/Widget.Auxio.Button.Circular" style="@style/Widget.Auxio.Button.Circular"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_medium"
android:layout_marginTop="@dimen/spacing_medium"
android:layout_marginEnd="@dimen/spacing_medium"
android:contentDescription="@string/desc_play_pause" android:contentDescription="@string/desc_play_pause"
android:onClick="@{() -> playbackModel.invertPlayingStatus()}" android:onClick="@{() -> playbackModel.invertPlayingStatus()}"
android:src="@drawable/sel_playing_state" android:src="@drawable/sel_playing_state"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@+id/playback_song_duration" app:layout_constraintEnd_toEndOf="@+id/playback_seek_bar"
app:layout_constraintHorizontal_bias="0.5" app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="@+id/playback_duration_current" app:layout_constraintStart_toEndOf="@+id/playback_cover"
app:layout_constraintTop_toBottomOf="@+id/playback_seek_bar" app:layout_constraintTop_toBottomOf="@+id/playback_seek_bar"
tools:src="@drawable/ic_pause" /> tools:src="@drawable/ic_pause" />

View file

@ -83,7 +83,6 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_mid_huge" android:layout_marginStart="@dimen/spacing_mid_huge"
android:layout_marginEnd="@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:onClick="@{() -> detailModel.navToItem(playbackModel.song.album)}"
android:text="@{song.album.name}" android:text="@{song.album.name}"
app:layout_constraintBottom_toTopOf="@+id/playback_seek_bar" app:layout_constraintBottom_toTopOf="@+id/playback_seek_bar"
@ -91,40 +90,15 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
tools:text="Album Name" /> tools:text="Album Name" />
<SeekBar <org.oxycblt.auxio.playback.PlaybackSeekBar
android:id="@+id/playback_seek_bar" android:id="@+id/playback_seek_bar"
style="@style/Widget.Auxio.SeekBar" android:layout_width="0dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:paddingStart="@dimen/spacing_mid_huge"
android:paddingEnd="@dimen/spacing_mid_huge"
app:layout_constraintBottom_toTopOf="@+id/playback_play_pause"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:progress="70" />
<TextView
android:id="@+id/playback_duration_current"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_mid_huge" android:layout_marginStart="@dimen/spacing_mid_huge"
android:layout_marginBottom="@dimen/spacing_small"
android:text="@{playbackModel.formattedPosition}"
android:textColor="@color/sel_accented_secondary"
app:layout_constraintBottom_toTopOf="@+id/playback_play_pause"
app:layout_constraintStart_toStartOf="parent"
tools:text="11:38" />
<TextView
android:id="@+id/playback_song_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/spacing_mid_huge" android:layout_marginEnd="@dimen/spacing_mid_huge"
android:text="@{song.formattedDuration}"
app:layout_constraintBottom_toTopOf="@+id/playback_play_pause" app:layout_constraintBottom_toTopOf="@+id/playback_play_pause"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
tools:text="16:16" /> app:layout_constraintStart_toStartOf="parent" />
<ImageButton <ImageButton
android:id="@+id/playback_loop" android:id="@+id/playback_loop"
@ -158,16 +132,13 @@
style="@style/Widget.Auxio.Button.Circular" style="@style/Widget.Auxio.Button.Circular"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_medium"
android:layout_marginTop="@dimen/spacing_medium"
android:layout_marginEnd="@dimen/spacing_medium"
android:layout_marginBottom="@dimen/spacing_large" android:layout_marginBottom="@dimen/spacing_large"
android:contentDescription="@string/desc_play_pause" android:contentDescription="@string/desc_play_pause"
android:onClick="@{() -> playbackModel.invertPlayingStatus()}" android:onClick="@{() -> playbackModel.invertPlayingStatus()}"
android:src="@drawable/sel_playing_state" android:src="@drawable/sel_playing_state"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@+id/playback_song_duration" app:layout_constraintEnd_toEndOf="@+id/playback_seek_bar"
app:layout_constraintStart_toStartOf="@+id/playback_duration_current" app:layout_constraintStart_toStartOf="@+id/playback_seek_bar"
tools:src="@drawable/ic_pause" /> tools:src="@drawable/ic_pause" />
<ImageButton <ImageButton

View file

@ -89,53 +89,27 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
tools:text="Album Name" /> tools:text="Album Name" />
<SeekBar <org.oxycblt.auxio.playback.PlaybackSeekBar
android:id="@+id/playback_seek_bar" android:id="@+id/playback_seek_bar"
style="@style/Widget.Auxio.SeekBar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingStart="@dimen/spacing_mid_large" android:layout_marginStart="@dimen/spacing_medium"
android:paddingEnd="@dimen/spacing_mid_large" android:layout_marginEnd="@dimen/spacing_medium"
android:layout_marginBottom="@dimen/spacing_medium"
app:layout_constraintBottom_toTopOf="@+id/playback_play_pause" app:layout_constraintBottom_toTopOf="@+id/playback_play_pause"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent" />
tools:progress="70" />
<TextView
android:id="@+id/playback_duration_current"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginStart="@dimen/spacing_mid_large"
android:text="@{playbackModel.formattedPosition}"
android:textColor="@color/sel_accented_secondary"
app:layout_constraintBottom_toTopOf="@+id/playback_play_pause"
app:layout_constraintStart_toStartOf="parent"
tools:text="11:38" />
<TextView
android:id="@+id/playback_song_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/spacing_mid_large"
android:layout_marginBottom="8dp"
android:text="@{song.formattedDuration}"
app:layout_constraintBottom_toTopOf="@+id/playback_play_pause"
app:layout_constraintEnd_toEndOf="parent"
tools:text="16:16" />
<ImageButton <ImageButton
android:id="@+id/playback_loop" android:id="@+id/playback_loop"
style="@style/Widget.Auxio.Button.Unbounded" style="@style/Widget.Auxio.Button.Unbounded"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_small" android:layout_marginStart="@dimen/spacing_medium"
android:contentDescription="@string/desc_change_loop" android:contentDescription="@string/desc_change_loop"
android:onClick="@{() -> playbackModel.incrementLoopStatus()}" android:onClick="@{() -> playbackModel.incrementLoopStatus()}"
android:src="@drawable/ic_loop" android:src="@drawable/ic_loop"
app:layout_constraintBottom_toBottomOf="@+id/playback_skip_prev" app:layout_constraintBottom_toBottomOf="@+id/playback_skip_prev"
app:layout_constraintStart_toStartOf="@+id/playback_duration_current" app:layout_constraintStart_toStartOf="@+id/playback_seek_bar"
app:layout_constraintTop_toTopOf="@+id/playback_skip_prev" /> app:layout_constraintTop_toTopOf="@+id/playback_skip_prev" />
<ImageButton <ImageButton
@ -156,15 +130,13 @@
style="@style/Widget.Auxio.Button.Circular" style="@style/Widget.Auxio.Button.Circular"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_medium"
android:layout_marginEnd="@dimen/spacing_medium"
android:layout_marginBottom="@dimen/spacing_medium" android:layout_marginBottom="@dimen/spacing_medium"
android:contentDescription="@string/desc_play_pause" android:contentDescription="@string/desc_play_pause"
android:onClick="@{() -> playbackModel.invertPlayingStatus()}" android:onClick="@{() -> playbackModel.invertPlayingStatus()}"
android:src="@drawable/sel_playing_state" android:src="@drawable/sel_playing_state"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@+id/playback_song_duration" app:layout_constraintEnd_toEndOf="@+id/playback_seek_bar"
app:layout_constraintStart_toStartOf="@+id/playback_duration_current" app:layout_constraintStart_toStartOf="@+id/playback_seek_bar"
tools:src="@drawable/ic_play" /> tools:src="@drawable/ic_play" />
<ImageButton <ImageButton
@ -185,12 +157,12 @@
style="@style/Widget.Auxio.Button.Unbounded" style="@style/Widget.Auxio.Button.Unbounded"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="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:contentDescription="@string/desc_shuffle"
android:onClick="@{() -> playbackModel.invertShuffleStatus()}" android:onClick="@{() -> playbackModel.invertShuffleStatus()}"
android:src="@drawable/ic_shuffle" android:src="@drawable/ic_shuffle"
app:layout_constraintBottom_toBottomOf="@+id/playback_skip_next" 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:layout_constraintTop_toTopOf="@+id/playback_skip_next"
app:tint="@color/sel_accented" /> app:tint="@color/sel_accented" />

View file

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<merge
tools:layout_height="wrap_content"
tools:layout_width="match_parent"
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
<SeekBar
android:id="@+id/playback_seek_bar"
style="@style/Widget.Auxio.SeekBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="@dimen/spacing_small"
android:paddingEnd="@dimen/spacing_small"
android:layout_marginBottom="@dimen/spacing_medium"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/playback_duration_current"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/spacing_small"
android:layout_marginStart="@dimen/spacing_small"
android:textColor="@color/sel_accented_secondary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:text="11:38" />
<TextView
android:id="@+id/playback_song_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/spacing_small"
android:layout_marginEnd="@dimen/spacing_small"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
tools:text="16:16" />
</merge>
</layout>

View file

@ -47,7 +47,6 @@
</style> </style>
<style name="ThemeOverlay.SeekbarHalo" parent=""> <style name="ThemeOverlay.SeekbarHalo" parent="">
<!-- TODO: Remove this dumb hack and use a slider -->
<item name="colorControlHighlight">@color/overlay_seekbar_halo</item> <item name="colorControlHighlight">@color/overlay_seekbar_halo</item>
</style> </style>
@ -202,7 +201,6 @@
<item name="android:minHeight">@dimen/size_btn_large</item> <item name="android:minHeight">@dimen/size_btn_large</item>
<item name="android:minWidth">@dimen/size_btn_large</item> <item name="android:minWidth">@dimen/size_btn_large</item>
<item name="android:background">@drawable/ui_circle_ripple</item> <item name="android:background">@drawable/ui_circle_ripple</item>
<item name="android:elevation">@dimen/elevation_normal</item>
<item name="android:contentDescription">@string/desc_play_pause</item> <item name="android:contentDescription">@string/desc_play_pause</item>
<item name="android:tint">?attr/colorSurface</item> <item name="android:tint">?attr/colorSurface</item>
<item name="android:scaleType">fitCenter</item> <item name="android:scaleType">fitCenter</item>