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:
parent
4b6610d236
commit
5ebe17d0ad
9 changed files with 167 additions and 213 deletions
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
|
@ -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" />
|
||||||
|
|
||||||
|
|
|
@ -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" />
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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" />
|
||||||
|
|
||||||
|
|
45
app/src/main/res/layout/view_seek_bar.xml
Normal file
45
app/src/main/res/layout/view_seek_bar.xml
Normal 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>
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in a new issue