playback: force LTR on timeline controls

Force LTR on timeline controls, as per the Material Design guidelines.

The guidelines state that while "directional" UIs should be LTR/RTL
depending on locale, "timeline" UIs should always by LTR, as the
direction of time is universal. Auxio did not do this, and so the
timeline controls would be RTL on other elements. Fix this by forcing
LTR on the UI elements that correspond to timelines.

Now, this is not the best system. To ensure that the rest of the layout
remains sane, much of the directional views have to be wrapped in a
redundant layout, which is somewhat in-efficient. However, the impact
seems to be at least negligable.
This commit is contained in:
OxygenCobalt 2022-06-22 12:31:52 -06:00
parent 55f9d4c819
commit 630950ea5d
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
26 changed files with 710 additions and 390 deletions

View file

@ -6,6 +6,9 @@
- Added a shuffle shortcut
- You can now customize what occurs when a song is played from an album/artist/genre [#164]
#### What's Improved
- Made "timeline" elements (like playback controls) always left-to-right
#### What's Fixed
- Fixed broken tablet layouts
- Fixed seam that would appear on some album covers

View file

@ -43,8 +43,6 @@ import org.oxycblt.auxio.util.logD
*
* TODO: Custom language support
*
* TODO: Rework padding ethos
*
* TODO: Add multi-select
*
* @author OxygenCobalt

View file

@ -144,15 +144,15 @@ class AlbumDetailFragment :
override fun onShowSortMenu(anchor: View) {
menu(anchor, R.menu.menu_album_sort) {
val sort = detailModel.albumSort
requireNotNull(menu.findItem(sort.mode.itemId)).isChecked = true
requireNotNull(menu.findItem(R.id.option_sort_asc)).isChecked = sort.isAscending
unlikelyToBeNull(menu.findItem(sort.mode.itemId)).isChecked = true
unlikelyToBeNull(menu.findItem(R.id.option_sort_asc)).isChecked = sort.isAscending
setOnMenuItemClickListener { item ->
item.isChecked = !item.isChecked
detailModel.albumSort =
if (item.itemId == R.id.option_sort_asc) {
sort.withAscending(item.isChecked)
} else {
sort.withMode(requireNotNull(Sort.Mode.fromItemId(item.itemId)))
sort.withMode(unlikelyToBeNull(Sort.Mode.fromItemId(item.itemId)))
}
true
}

View file

@ -139,8 +139,8 @@ class ArtistDetailFragment :
override fun onShowSortMenu(anchor: View) {
menu(anchor, R.menu.menu_artist_sort) {
val sort = detailModel.artistSort
requireNotNull(menu.findItem(sort.mode.itemId)).isChecked = true
requireNotNull(menu.findItem(R.id.option_sort_asc)).isChecked = sort.isAscending
unlikelyToBeNull(menu.findItem(sort.mode.itemId)).isChecked = true
unlikelyToBeNull(menu.findItem(R.id.option_sort_asc)).isChecked = sort.isAscending
setOnMenuItemClickListener { item ->
item.isChecked = !item.isChecked
@ -148,7 +148,7 @@ class ArtistDetailFragment :
if (item.itemId == R.id.option_sort_asc) {
sort.withAscending(item.isChecked)
} else {
sort.withMode(requireNotNull(Sort.Mode.fromItemId(item.itemId)))
sort.withMode(unlikelyToBeNull(Sort.Mode.fromItemId(item.itemId)))
}
true

View file

@ -140,15 +140,15 @@ class GenreDetailFragment :
override fun onShowSortMenu(anchor: View) {
menu(anchor, R.menu.menu_genre_sort) {
val sort = detailModel.genreSort
requireNotNull(menu.findItem(sort.mode.itemId)).isChecked = true
requireNotNull(menu.findItem(R.id.option_sort_asc)).isChecked = sort.isAscending
unlikelyToBeNull(menu.findItem(sort.mode.itemId)).isChecked = true
unlikelyToBeNull(menu.findItem(R.id.option_sort_asc)).isChecked = sort.isAscending
setOnMenuItemClickListener { item ->
item.isChecked = !item.isChecked
detailModel.genreSort =
if (item.itemId == R.id.option_sort_asc) {
sort.withAscending(item.isChecked)
} else {
sort.withMode(requireNotNull(Sort.Mode.fromItemId(item.itemId)))
sort.withMode(unlikelyToBeNull(Sort.Mode.fromItemId(item.itemId)))
}
true
}

View file

@ -27,8 +27,8 @@ import org.oxycblt.auxio.ui.DisplayMode
import org.oxycblt.auxio.ui.Item
import org.oxycblt.auxio.ui.MenuItemListener
import org.oxycblt.auxio.ui.MonoAdapter
import org.oxycblt.auxio.ui.Sort
import org.oxycblt.auxio.ui.SongViewHolder
import org.oxycblt.auxio.ui.Sort
import org.oxycblt.auxio.ui.SyncBackingData
import org.oxycblt.auxio.util.collectImmediately
import org.oxycblt.auxio.util.context

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2022 Auxio Project
*
* 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.view.View
import android.widget.FrameLayout
/**
* A class that programmatically overrides the child layout to a left-to-right (LTR) layout
* direction.
*
* The Material Design guidelines state that any components that represent a "Timeline" should
* always be LTR. In Auxio, this applies to most of the playback components. This layout in
* particular overrides the layout direction in a way that will not disrupt how other views are laid
* out.
*/
open class NoRtlFrameLayout
@JvmOverloads
constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
) : FrameLayout(context, attrs, defStyleAttr) {
override fun onFinishInflate() {
super.onFinishInflate()
check(childCount == 1) { "This view should only contain one child" }
getChildAt(0).layoutDirection = View.LAYOUT_DIRECTION_LTR
}
}

View file

@ -73,10 +73,9 @@ class PlaybackBarFragment : ViewBindingFragment<FragmentPlaybackBarBinding>() {
// Load the track color in manually as it's unclear whether the track actually supports
// using a ColorStateList in the resources
binding.playbackProgressBar.trackColor =
requireContext().getColorStateListSafe(R.color.sel_track).defaultColor
binding.playbackProgressBar.progress = 0
binding.playbackProgressBar.apply {
trackColor = requireContext().getColorStateListSafe(R.color.sel_track).defaultColor
}
binding.playbackPlayPause.setOnClickListener { playbackModel.invertPlaying() }

View file

@ -46,8 +46,6 @@ import org.oxycblt.auxio.util.textSafe
* Instantiation is done by the navigation component, **do not instantiate this fragment manually.**
* @author OxygenCobalt
*
* TODO: Handle RTL correctly in the playback buttons
*
* TODO: Make seek thumb grow when selected
*/
class PlaybackPanelFragment :

View file

@ -19,7 +19,6 @@ package org.oxycblt.auxio.playback
import android.content.Context
import android.util.AttributeSet
import android.widget.FrameLayout
import com.google.android.material.slider.Slider
import kotlin.math.max
import org.oxycblt.auxio.databinding.ViewSeekBarBinding
@ -52,12 +51,14 @@ constructor(
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
) :
FrameLayout(context, attrs, defStyleAttr),
NoRtlFrameLayout(context, attrs, defStyleAttr),
Slider.OnSliderTouchListener,
Slider.OnChangeListener {
private val binding = ViewSeekBarBinding.inflate(context.inflater, this)
private val binding = ViewSeekBarBinding.inflate(context.inflater, this, true)
init {
// As per the Material Design guidelines, timeline elements like SeekBars and Controls
// should always be LTR.
binding.seekBarSlider.addOnSliderTouchListener(this)
binding.seekBarSlider.addOnChangeListener(this)
}

View file

@ -18,6 +18,7 @@
package org.oxycblt.auxio.widgets
import android.content.Context
import android.view.View
import android.widget.RemoteViews
import androidx.annotation.LayoutRes
import org.oxycblt.auxio.R
@ -114,6 +115,9 @@ private fun RemoteViews.applyPlayPauseControls(
R.id.widget_play_pause,
context.newBroadcastPendingIntent(PlaybackService.ACTION_PLAY_PAUSE))
// Controls are timeline elements, override the layout direction to RTL
setInt(R.id.widget_controls, "setLayoutDirection", View.LAYOUT_DIRECTION_LTR)
setImageViewResource(
R.id.widget_play_pause,
if (state.isPlaying) {

View file

@ -81,17 +81,31 @@
android:id="@+id/playback_seek_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@+id/playback_play_pause"
app:layout_constraintBottom_toTopOf="@+id/playback_controls_container"
app:layout_constraintEnd_toEndOf="@+id/playback_song_container"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent" />
<org.oxycblt.auxio.playback.NoRtlFrameLayout
android:id="@+id/playback_controls_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/spacing_medium"
android:layout_marginStart="@dimen/spacing_medium"
android:layout_marginEnd="@dimen/spacing_medium"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<org.oxycblt.auxio.playback.IndicatorMaterialButton
android:id="@+id/playback_repeat"
style="@style/Widget.Auxio.Button.Icon.Large"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_medium"
android:contentDescription="@string/desc_change_repeat"
app:icon="@drawable/ic_repeat"
app:iconTint="@color/sel_accented"
@ -116,12 +130,12 @@
style="@style/Widget.Auxio.FloatingActionButton.PlayPause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/spacing_medium"
android:contentDescription="@string/desc_play_pause"
android:src="@drawable/sel_playing_state"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@+id/playback_seek_bar"
app:layout_constraintStart_toStartOf="@+id/playback_seek_bar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/ic_play" />
<Button
@ -141,7 +155,6 @@
style="@style/Widget.Auxio.Button.Icon.Large"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/spacing_medium"
android:contentDescription="@string/desc_shuffle"
app:icon="@drawable/ic_shuffle"
app:iconTint="@color/sel_accented"
@ -151,3 +164,8 @@
app:tint="@color/sel_accented" />
</androidx.constraintlayout.widget.ConstraintLayout>
</org.oxycblt.auxio.playback.NoRtlFrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -78,12 +78,25 @@
android:layout_height="wrap_content"
android:layout_marginStart="-16dp"
android:layout_marginEnd="-16dp"
app:layout_constraintBottom_toTopOf="@+id/playback_play_pause"
app:layout_constraintBottom_toTopOf="@+id/playback_controls_container"
app:layout_constraintEnd_toEndOf="@+id/playback_album"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="@+id/playback_album"
app:layout_constraintTop_toBottomOf="@+id/playback_album" />
<org.oxycblt.auxio.playback.NoRtlFrameLayout
android:id="@+id/playback_controls_container"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@+id/playback_seek_bar"
app:layout_constraintStart_toStartOf="@+id/playback_seek_bar"
app:layout_constraintTop_toBottomOf="@+id/playback_seek_bar">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<org.oxycblt.auxio.playback.IndicatorMaterialButton
android:id="@+id/playback_repeat"
style="@style/Widget.Auxio.Button.Icon.Large"
@ -118,9 +131,9 @@
android:contentDescription="@string/desc_play_pause"
android:src="@drawable/sel_playing_state"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@+id/playback_seek_bar"
app:layout_constraintStart_toStartOf="@+id/playback_seek_bar"
app:layout_constraintTop_toBottomOf="@+id/playback_seek_bar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/ic_pause" />
<Button
@ -149,4 +162,8 @@
app:layout_constraintTop_toTopOf="@+id/playback_skip_next"
app:tint="@color/sel_accented" />
</androidx.constraintlayout.widget.ConstraintLayout>
</org.oxycblt.auxio.playback.NoRtlFrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -10,7 +10,7 @@
android:id="@+id/playback_cover"
style="@style/Widget.Auxio.Image.Medium"
android:layout_margin="@dimen/spacing_small"
app:layout_constraintBottom_toTopOf="@+id/playback_progress_bar"
app:layout_constraintBottom_toTopOf="@+id/playback_progress_container"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:staticIcon="@drawable/ic_album" />
@ -22,9 +22,8 @@
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_small"
android:layout_marginEnd="@dimen/spacing_small"
android:textAppearance="@style/TextAppearance.Auxio.LabelLarge"
app:layout_constraintBottom_toTopOf="@+id/playback_info"
app:layout_constraintEnd_toStartOf="@+id/playback_play_pause"
app:layout_constraintEnd_toStartOf="@+id/playback_controls_wrapper"
app:layout_constraintStart_toEndOf="@+id/playback_cover"
app:layout_constraintTop_toTopOf="@+id/playback_cover"
app:layout_constraintVertical_chainStyle="packed"
@ -38,35 +37,52 @@
android:layout_marginStart="@dimen/spacing_small"
android:layout_marginEnd="@dimen/spacing_small"
android:ellipsize="end"
android:textAppearance="@style/TextAppearance.Auxio.LabelMedium"
app:layout_constraintBottom_toBottomOf="@+id/playback_cover"
app:layout_constraintEnd_toStartOf="@+id/playback_play_pause"
app:layout_constraintEnd_toStartOf="@+id/playback_controls_wrapper"
app:layout_constraintStart_toEndOf="@+id/playback_cover"
app:layout_constraintTop_toBottomOf="@+id/playback_song"
tools:text="Artist Name / Album Name" />
<org.oxycblt.auxio.playback.NoRtlFrameLayout
android:id="@+id/playback_controls_wrapper"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/spacing_tiny"
app:layout_constraintBottom_toTopOf="@+id/playback_progress_container"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:id="@+id/playback_controls_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/playback_play_pause"
style="@style/Widget.Auxio.Button.Icon.Small"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/desc_play_pause"
app:icon="@drawable/sel_playing_state"
app:layout_constraintBottom_toTopOf="@+id/playback_progress_bar"
app:layout_constraintEnd_toStartOf="@+id/playback_skip_next"
app:layout_constraintTop_toTopOf="parent" />
app:icon="@drawable/sel_playing_state" />
<Button
android:id="@+id/playback_skip_next"
style="@style/Widget.Auxio.Button.Icon.Small"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/spacing_tiny"
android:contentDescription="@string/desc_play_pause"
app:icon="@drawable/ic_skip_next"
app:layout_constraintBottom_toTopOf="@+id/playback_progress_bar"
app:icon="@drawable/ic_skip_next" />
</LinearLayout>
</org.oxycblt.auxio.playback.NoRtlFrameLayout>
<org.oxycblt.auxio.playback.NoRtlFrameLayout
android:id="@+id/playback_progress_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintStart_toStartOf="parent">
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/playback_progress_bar"
@ -74,9 +90,8 @@
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_small"
android:layout_marginEnd="@dimen/spacing_small"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:progress="70" />
</org.oxycblt.auxio.playback.NoRtlFrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -68,17 +68,30 @@
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_medium"
android:layout_marginEnd="@dimen/spacing_medium"
app:layout_constraintBottom_toTopOf="@+id/playback_play_pause"
app:layout_constraintBottom_toTopOf="@+id/playback_controls_container"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent" />
<org.oxycblt.auxio.playback.NoRtlFrameLayout
android:id="@+id/playback_controls_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_medium"
android:layout_marginEnd="@dimen/spacing_medium"
android:layout_marginBottom="@dimen/spacing_mid_large"
app:layout_constraintBottom_toBottomOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<org.oxycblt.auxio.playback.IndicatorMaterialButton
android:id="@+id/playback_repeat"
style="@style/Widget.Auxio.Button.Icon.Large"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/spacing_mid_large"
android:layout_marginEnd="@dimen/spacing_large"
android:contentDescription="@string/desc_change_repeat"
app:icon="@drawable/ic_repeat"
app:iconTint="@color/sel_accented"
@ -92,7 +105,7 @@
style="@style/Widget.Auxio.Button.Icon.Large"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/spacing_mid_large"
android:layout_marginEnd="@dimen/spacing_large"
android:contentDescription="@string/desc_skip_prev"
app:icon="@drawable/ic_skip_prev"
app:layout_constraintBottom_toBottomOf="@+id/playback_play_pause"
@ -104,12 +117,12 @@
style="@style/Widget.Auxio.FloatingActionButton.PlayPause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/spacing_mid_large"
android:contentDescription="@string/desc_play_pause"
android:src="@drawable/sel_playing_state"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@+id/playback_seek_bar"
app:layout_constraintStart_toStartOf="@+id/playback_seek_bar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/ic_pause" />
<Button
@ -117,7 +130,7 @@
style="@style/Widget.Auxio.Button.Icon.Large"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_mid_large"
android:layout_marginStart="@dimen/spacing_large"
android:contentDescription="@string/desc_skip_next"
app:icon="@drawable/ic_skip_next"
app:layout_constraintBottom_toBottomOf="@+id/playback_play_pause"
@ -129,7 +142,7 @@
style="@style/Widget.Auxio.Button.Icon.Large"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_mid_large"
android:layout_marginStart="@dimen/spacing_large"
android:contentDescription="@string/desc_shuffle"
app:icon="@drawable/ic_shuffle"
app:iconTint="@color/sel_accented"
@ -139,3 +152,6 @@
app:tint="@color/sel_accented" />
</androidx.constraintlayout.widget.ConstraintLayout>
</org.oxycblt.auxio.playback.NoRtlFrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="utf-8"?>
<merge 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"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:parentTag="android.widget.FrameLayout">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<org.oxycblt.auxio.playback.IndicatorMaterialButton
android:id="@+id/playback_repeat"
style="@style/Widget.Auxio.Button.Icon.Large"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/spacing_large"
android:contentDescription="@string/desc_change_repeat"
app:icon="@drawable/ic_repeat"
app:iconTint="@color/sel_accented"
app:layout_constraintBottom_toBottomOf="@+id/playback_skip_prev"
app:layout_constraintEnd_toStartOf="@+id/playback_skip_prev"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintTop_toTopOf="@+id/playback_skip_prev" />
<Button
android:id="@+id/playback_skip_prev"
style="@style/Widget.Auxio.Button.Icon.Large"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/spacing_large"
android:contentDescription="@string/desc_skip_prev"
app:icon="@drawable/ic_skip_prev"
app:layout_constraintBottom_toBottomOf="@+id/playback_play_pause"
app:layout_constraintEnd_toStartOf="@+id/playback_play_pause"
app:layout_constraintTop_toTopOf="@+id/playback_play_pause" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/playback_play_pause"
style="@style/Widget.Auxio.FloatingActionButton.PlayPause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/desc_play_pause"
android:src="@drawable/sel_playing_state"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/ic_pause" />
<Button
android:id="@+id/playback_skip_next"
style="@style/Widget.Auxio.Button.Icon.Large"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_large"
android:contentDescription="@string/desc_skip_next"
app:icon="@drawable/ic_skip_next"
app:layout_constraintBottom_toBottomOf="@+id/playback_play_pause"
app:layout_constraintStart_toEndOf="@+id/playback_play_pause"
app:layout_constraintTop_toTopOf="@+id/playback_play_pause" />
<org.oxycblt.auxio.playback.IndicatorMaterialButton
android:id="@+id/playback_shuffle"
style="@style/Widget.Auxio.Button.Icon.Large"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_large"
android:contentDescription="@string/desc_shuffle"
app:icon="@drawable/ic_shuffle"
app:iconTint="@color/sel_accented"
app:layout_constraintBottom_toBottomOf="@+id/playback_skip_next"
app:layout_constraintStart_toEndOf="@+id/playback_skip_next"
app:layout_constraintTop_toTopOf="@+id/playback_skip_next"
app:tint="@color/sel_accented" />
</androidx.constraintlayout.widget.ConstraintLayout>
</merge>

View file

@ -81,24 +81,37 @@
android:id="@+id/playback_seek_bar"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@+id/playback_play_pause"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/playback_cover"
app:layout_constraintTop_toBottomOf="@+id/playback_album" />
app:layout_constraintTop_toBottomOf="@+id/playback_album"
app:layout_constraintBottom_toTopOf="@+id/playback_controls_container"/>
<org.oxycblt.auxio.playback.NoRtlFrameLayout
android:id="@+id/playback_controls_container"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginStart="@dimen/spacing_medium"
android:layout_marginEnd="@dimen/spacing_medium"
app:layout_constraintEnd_toEndOf="@+id/playback_seek_bar"
app:layout_constraintStart_toStartOf="@+id/playback_seek_bar"
app:layout_constraintTop_toBottomOf="@+id/playback_seek_bar">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<org.oxycblt.auxio.playback.IndicatorMaterialButton
android:id="@+id/playback_repeat"
style="@style/Widget.Auxio.Button.Icon.Large"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_medium"
android:contentDescription="@string/desc_change_repeat"
app:icon="@drawable/ic_repeat"
app:iconTint="@color/sel_accented"
app:layout_constraintBottom_toBottomOf="@+id/playback_skip_prev"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="@+id/playback_seek_bar"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/playback_skip_prev" />
<Button
@ -121,11 +134,10 @@
android:contentDescription="@string/desc_play_pause"
android:src="@drawable/sel_playing_state"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@+id/playback_seek_bar"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/playback_cover"
app:layout_constraintTop_toBottomOf="@+id/playback_seek_bar"
tools:src="@drawable/ic_pause" />
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/ic_play" />
<Button
android:id="@+id/playback_skip_next"
@ -144,13 +156,16 @@
style="@style/Widget.Auxio.Button.Icon.Large"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/spacing_medium"
android:contentDescription="@string/desc_shuffle"
app:icon="@drawable/ic_shuffle"
app:iconTint="@color/sel_accented"
app:layout_constraintBottom_toBottomOf="@+id/playback_skip_next"
app:layout_constraintEnd_toEndOf="@+id/playback_seek_bar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/playback_skip_next"
app:tint="@color/sel_accented" />
</androidx.constraintlayout.widget.ConstraintLayout>
</org.oxycblt.auxio.playback.NoRtlFrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -10,7 +10,7 @@
android:id="@+id/playback_cover"
style="@style/Widget.Auxio.Image.Small"
android:layout_margin="@dimen/spacing_small"
app:layout_constraintBottom_toTopOf="@+id/playback_progress_bar"
app:layout_constraintBottom_toTopOf="@+id/playback_progress_container"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:staticIcon="@drawable/ic_album" />
@ -23,7 +23,7 @@
android:layout_marginStart="@dimen/spacing_small"
android:layout_marginEnd="@dimen/spacing_small"
app:layout_constraintBottom_toTopOf="@+id/playback_info"
app:layout_constraintEnd_toStartOf="@+id/playback_play_pause"
app:layout_constraintEnd_toStartOf="@+id/playback_controls_wrapper"
app:layout_constraintStart_toEndOf="@+id/playback_cover"
app:layout_constraintTop_toTopOf="@+id/playback_cover"
app:layout_constraintVertical_chainStyle="packed"
@ -38,33 +38,51 @@
android:layout_marginEnd="@dimen/spacing_small"
android:ellipsize="end"
app:layout_constraintBottom_toBottomOf="@+id/playback_cover"
app:layout_constraintEnd_toStartOf="@+id/playback_play_pause"
app:layout_constraintEnd_toStartOf="@+id/playback_controls_wrapper"
app:layout_constraintStart_toEndOf="@+id/playback_cover"
app:layout_constraintTop_toBottomOf="@+id/playback_song"
tools:text="Artist Name / Album Name" />
<org.oxycblt.auxio.playback.NoRtlFrameLayout
android:id="@+id/playback_controls_wrapper"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/spacing_tiny"
app:layout_constraintBottom_toTopOf="@+id/playback_progress_container"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:id="@+id/playback_controls_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/playback_play_pause"
style="@style/Widget.Auxio.Button.Icon.Small"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/desc_play_pause"
app:icon="@drawable/sel_playing_state"
app:layout_constraintBottom_toTopOf="@+id/playback_progress_bar"
app:layout_constraintEnd_toStartOf="@+id/playback_skip_next"
app:layout_constraintTop_toTopOf="parent" />
app:icon="@drawable/sel_playing_state" />
<Button
android:id="@+id/playback_skip_next"
style="@style/Widget.Auxio.Button.Icon.Small"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/spacing_tiny"
android:contentDescription="@string/desc_play_pause"
app:icon="@drawable/ic_skip_next"
app:layout_constraintBottom_toTopOf="@+id/playback_progress_bar"
app:icon="@drawable/ic_skip_next" />
</LinearLayout>
</org.oxycblt.auxio.playback.NoRtlFrameLayout>
<org.oxycblt.auxio.playback.NoRtlFrameLayout
android:id="@+id/playback_progress_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintStart_toStartOf="parent">
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/playback_progress_bar"
@ -72,9 +90,8 @@
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_small"
android:layout_marginEnd="@dimen/spacing_small"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:progress="70" />
</org.oxycblt.auxio.playback.NoRtlFrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -65,17 +65,30 @@
android:id="@+id/playback_seek_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@+id/playback_play_pause"
app:layout_constraintBottom_toTopOf="@+id/playback_controls_container"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent" />
<org.oxycblt.auxio.playback.NoRtlFrameLayout
android:id="@+id/playback_controls_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/spacing_medium"
android:layout_marginStart="@dimen/spacing_medium"
android:layout_marginEnd="@dimen/spacing_medium"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<org.oxycblt.auxio.playback.IndicatorMaterialButton
android:id="@+id/playback_repeat"
style="@style/Widget.Auxio.Button.Icon.Large"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_medium"
android:contentDescription="@string/desc_change_repeat"
app:icon="@drawable/ic_repeat"
app:iconTint="@color/sel_accented"
@ -100,12 +113,12 @@
style="@style/Widget.Auxio.FloatingActionButton.PlayPause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/spacing_medium"
android:contentDescription="@string/desc_play_pause"
android:src="@drawable/sel_playing_state"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/ic_play" />
<Button
@ -125,7 +138,6 @@
style="@style/Widget.Auxio.Button.Icon.Large"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/spacing_medium"
android:contentDescription="@string/desc_shuffle"
app:icon="@drawable/ic_shuffle"
app:iconTint="@color/sel_accented"
@ -135,3 +147,7 @@
app:tint="@color/sel_accented" />
</androidx.constraintlayout.widget.ConstraintLayout>
</org.oxycblt.auxio.playback.NoRtlFrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,79 @@
<?xml version="1.0" encoding="utf-8"?>
<merge 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"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:parentTag="android.widget.FrameLayout">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<org.oxycblt.auxio.playback.IndicatorMaterialButton
android:id="@+id/playback_repeat"
style="@style/Widget.Auxio.Button.Icon.Large"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_medium"
android:contentDescription="@string/desc_change_repeat"
app:icon="@drawable/ic_repeat"
app:iconTint="@color/sel_accented"
app:layout_constraintBottom_toBottomOf="@+id/playback_skip_prev"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/playback_skip_prev" />
<Button
android:id="@+id/playback_skip_prev"
style="@style/Widget.Auxio.Button.Icon.Large"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/desc_skip_prev"
app:icon="@drawable/ic_skip_prev"
app:layout_constraintBottom_toBottomOf="@+id/playback_play_pause"
app:layout_constraintEnd_toStartOf="@+id/playback_play_pause"
app:layout_constraintStart_toEndOf="@+id/playback_repeat"
app:layout_constraintTop_toTopOf="@+id/playback_play_pause" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/playback_play_pause"
style="@style/Widget.Auxio.FloatingActionButton.PlayPause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/desc_play_pause"
android:src="@drawable/sel_playing_state"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/ic_play" />
<Button
android:id="@+id/playback_skip_next"
style="@style/Widget.Auxio.Button.Icon.Large"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/desc_skip_next"
app:icon="@drawable/ic_skip_next"
app:layout_constraintBottom_toBottomOf="@+id/playback_play_pause"
app:layout_constraintEnd_toStartOf="@+id/playback_shuffle"
app:layout_constraintStart_toEndOf="@+id/playback_play_pause"
app:layout_constraintTop_toTopOf="@+id/playback_play_pause" />
<org.oxycblt.auxio.playback.IndicatorMaterialButton
android:id="@+id/playback_shuffle"
style="@style/Widget.Auxio.Button.Icon.Large"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/spacing_medium"
android:contentDescription="@string/desc_shuffle"
app:icon="@drawable/ic_shuffle"
app:iconTint="@color/sel_accented"
app:layout_constraintBottom_toBottomOf="@+id/playback_skip_next"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/playback_skip_next"
app:tint="@color/sel_accented" />
</androidx.constraintlayout.widget.ConstraintLayout>
</merge>

View file

@ -1,10 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
<FrameLayout 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"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:parentTag="android.widget.FrameLayout">
android:layout_height="wrap_content">
<com.google.android.material.slider.Slider
android:id="@+id/seek_bar_slider"
@ -42,4 +41,4 @@
app:layout_constraintTop_toBottomOf="@+id/playback_seek_bar"
tools:text="16:16" />
</merge>
</FrameLayout>

View file

@ -63,6 +63,7 @@
android:text="@string/def_widget_artist" />
<android.widget.LinearLayout
android:id="@+id/widget_controls"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_small">

View file

@ -63,6 +63,7 @@
android:text="@string/def_widget_artist" />
<android.widget.LinearLayout
android:id="@+id/widget_controls"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_small"

View file

@ -22,7 +22,6 @@
android:id="@+id/widget_aspect_ratio"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/widget_panel"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
@ -49,7 +48,7 @@
tools:ignore="ContentDescription" />
<android.widget.LinearLayout
android:id="@+id/widget_panel"
android:id="@+id/widget_controls"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"

View file

@ -99,7 +99,6 @@
android:minHeight="@dimen/size_btn"
android:src="@drawable/ic_play" />
</android.widget.LinearLayout>
</LinearLayout>

View file

@ -15,7 +15,6 @@
android:id="@+id/widget_aspect_ratio"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/widget_panel"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
@ -42,7 +41,7 @@
tools:ignore="ContentDescription" />
<android.widget.LinearLayout
android:id="@+id/widget_panel"
android:id="@+id/widget_controls"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"