playback: rework playback icon visibility

Add a circular indicator to playback icons.

Sometimes it can be too difficult to tell apart the active and inactive
states of the shuffle/loop icons. However, there is really no good way
to improve the contrast on these icons without some kind of muddled UX
fragmentation, or god-awful design. Try to settle on the okay-est
version, which is to use colorPrimary with a dot indicator on views we
control, and use a more muddled semi-transparent icon on views we don't
control, like notifications and widgets.
This commit is contained in:
OxygenCobalt 2022-02-21 16:05:05 -07:00
parent 2a74ff906c
commit 9304e58190
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
36 changed files with 228 additions and 103 deletions

View file

@ -45,7 +45,7 @@ import java.lang.Exception
* so that songs don't end up fragmented across artists. Pretty much every OEM has added some
* extension or quirk to MediaStore that I cannot reproduce, with some OEMs (COUGHSAMSUNGCOUGH)
* crippling the normal tables so that you're railroaded into their music app. The way I do
* blacklisting relies on a deprecated method, and the supposedly "modern" method is SLOWER and
* blacklisting relies on a semi-deprecated method, and the supposedly "modern" method is SLOWER and
* causes even more problems since I have to manage databases across version boundaries. Sometimes
* music will have a deformed clone that I can't filter out, sometimes Genres will just break for
* no reason, and sometimes tags encoded in UTF-8 will be interpreted as anything from UTF-16 to
@ -119,6 +119,8 @@ class MusicLoader {
// DATA was deprecated on Android 10, but is set to be un-deprecated in Android 12L.
// The only reason we'd want to change this is to add external partitions support, but
// that's less efficient and there's no demand for that right now.
// TODO: Determine if grokking the actual DATA value outside of SQL is more or less
// efficient than the current system
for (path in paths) {
selector += " AND ${MediaStore.Audio.Media.DATA} NOT LIKE ?"
args += "$path%" // Append % so that the selector properly detects children
@ -196,6 +198,7 @@ class MusicLoader {
}
}
// Deduplicate songs to prevent (most) deformed music clones
songs = songs.distinctBy {
it.name to it.internalMediaStoreAlbumName to it.internalMediaStoreArtistName to
it.internalMediaStoreAlbumArtistName to it.track to it.duration
@ -261,6 +264,8 @@ class MusicLoader {
// Album deduplication does not eliminate every case of fragmented artists, do
// we deduplicate in the artist creation step as well.
// Note that we actually don't do this in groupBy. This is generally because we
// only want to default to a lowercase artist name when we have no other choice.
val previousArtistIndex = artists.indexOfFirst { artist ->
artist.name.lowercase() == artistName.lowercase()
}

View file

@ -0,0 +1,92 @@
package org.oxycblt.auxio.playback
import android.content.Context
import android.graphics.Canvas
import android.graphics.Matrix
import android.graphics.RectF
import android.graphics.drawable.Drawable
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatImageButton
import org.oxycblt.auxio.R
import org.oxycblt.auxio.util.getDimenSizeSafe
import org.oxycblt.auxio.util.getDrawableSafe
/**
* An [AppCompatImageButton] designed for the buttons used in the playback display.
*
* Auxio's playback buttons have never followed the typical 24dp icon size that all
* other UI elements do, mostly because those icons just look bad at that size with
* all the gobs of whitespace surrounding them. So, this view resizes the icons to a
* fixed 32dp in a way that doesn't require a whole new icon set.
*
* This view also enables use of an "indicator", which is a dot that can denote when a
* button is active. This is useful for the shuffle/loop buttons, as at times highlighting
* them is not enough to
*/
class PlaybackButton @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = -1
) : AppCompatImageButton(context, attrs, defStyleAttr) {
private val iconSize = context.getDimenSizeSafe(R.dimen.size_playback_icon)
private val centerMatrix = Matrix()
private val matrixSrc = RectF()
private val matrixDst = RectF()
private val indicatorDrawable: Drawable?
init {
val size = context.getDimenSizeSafe(R.dimen.size_btn_small)
minimumWidth = size
minimumHeight = size
scaleType = ScaleType.MATRIX
setBackgroundResource(R.drawable.ui_large_unbounded_ripple)
context.obtainStyledAttributes(attrs, R.styleable.PlaybackButton).use { arr ->
val hasIndicator = arr.getBoolean(R.styleable.PlaybackButton_hasIndicator, false)
indicatorDrawable = if (hasIndicator) {
context.getDrawableSafe(R.drawable.ui_indicator)
} else {
null
}
}
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
imageMatrix = centerMatrix.apply {
reset()
drawable?.let { drawable ->
// Android is too good to allow us to set a fixed image size, so we instead need
// to define a matrix to scale an image directly.
// First scale the icon up to the desired size.
matrixSrc.set(0f, 0f, drawable.intrinsicWidth.toFloat(), drawable.intrinsicHeight.toFloat())
matrixDst.set(0f, 0f, iconSize.toFloat(), iconSize.toFloat())
centerMatrix.setRectToRect(matrixSrc, matrixDst, Matrix.ScaleToFit.CENTER)
// Then actually center it into the icon, which the previous call does not actually do.
centerMatrix.postTranslate(
(measuredWidth - iconSize) / 2f, (measuredHeight - iconSize) / 2f
)
}
}
indicatorDrawable?.let { indicator ->
val x = (measuredWidth - indicator.intrinsicWidth) / 2
val y = ((measuredHeight - iconSize) / 2) + iconSize
indicator.bounds.set(
x, y, x + indicator.intrinsicWidth, y + indicator.intrinsicHeight
)
}
}
override fun onDrawForeground(canvas: Canvas) {
super.onDrawForeground(canvas)
if (indicatorDrawable != null && isActivated) {
indicatorDrawable.draw(canvas)
}
}
}

View file

@ -126,7 +126,10 @@ class PlaybackFragment : Fragment() {
LoopMode.TRACK -> R.drawable.ic_loop_one
}
binding.playbackLoop.setImageResource(resId)
binding.playbackLoop.apply {
isActivated = loopMode != LoopMode.NONE
setImageResource(resId)
}
}
playbackModel.position.observe(viewLifecycleOwner) { pos ->

View file

@ -108,8 +108,6 @@ class PlaybackLayout @JvmOverloads constructor(
}
init {
setWillNotDraw(false)
// Set up our playback views. Doing this allows us to abstract away the implementation
// of these views from the user of this layout [MainFragment].
playbackContainerView = FrameLayout(context).apply {

View file

@ -142,7 +142,7 @@ class PlaybackNotification private constructor(
loopMode: LoopMode
): NotificationCompat.Action {
val drawableRes = when (loopMode) {
LoopMode.NONE -> R.drawable.ic_loop_off
LoopMode.NONE -> R.drawable.ic_remote_loop_off
LoopMode.ALL -> R.drawable.ic_loop
LoopMode.TRACK -> R.drawable.ic_loop_one
}
@ -154,7 +154,7 @@ class PlaybackNotification private constructor(
context: Context,
isShuffled: Boolean
): NotificationCompat.Action {
val drawableRes = if (isShuffled) R.drawable.ic_shuffle else R.drawable.ic_shuffle_off
val drawableRes = if (isShuffled) R.drawable.ic_shuffle else R.drawable.ic_remote_shuffle_off
return buildAction(context, PlaybackService.ACTION_SHUFFLE, drawableRes)
}

View file

@ -179,17 +179,22 @@ private fun RemoteViews.applyFullControls(context: Context, state: WidgetState):
)
)
// While it is technically possible to use the setColorFilter to tint these buttons, its
// actually less efficient than using duplicate drawables.
// And no, we can't control state drawables with RemoteViews. Because of course we can't.
// RemoteView is so restrictive that emulating auxio's playback icons in a sensible way is
// more or less impossible, including:
// 1. Setting foreground drawables
// 2. Applying custom image matrices
// 3. Tinting icons at all
//
// So, we have to do the dumbest possible method of duplicating each drawable and hard-coding
// indicators, tints, and icon sizes. And then google wonders why nobody uses widgets on
// android.
val shuffleRes = when {
state.isShuffled -> R.drawable.ic_shuffle_on
else -> R.drawable.ic_shuffle
state.isShuffled -> R.drawable.ic_remote_shuffle_on
else -> R.drawable.ic_remote_shuffle_off
}
val loopRes = when (state.loopMode) {
LoopMode.NONE -> R.drawable.ic_loop
LoopMode.NONE -> R.drawable.ic_remote_loop_off
LoopMode.ALL -> R.drawable.ic_loop_on
LoopMode.TRACK -> R.drawable.ic_loop_one
}

View file

@ -2,6 +2,7 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path

View file

@ -2,9 +2,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#80ffffff"
android:fillColor="#80FFFFFF"
android:pathData="M10.59 9.17L5.41 4 4 5.41l5.17 5.17 1.42-1.41zM14.5 4l2.04 2.04L4 18.59 5.41 20 17.96 7.46 20 9.5V4h-5.5zm0.33 9.41l-1.41 1.41 3.13 3.13L14.5 20H20v-5.5l-2.04 2.04-3.13-3.13z" />
</vector>

View file

@ -2,7 +2,7 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/colorControlNormal"
android:tint="@color/sel_accented"
android:viewportWidth="24"
android:viewportHeight="24">
<path

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<size android:height="4dp"
android:width="4dp" />
<solid android:color="?attr/colorPrimary" />
</shape>

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?attr/colorControlHighlight"
android:radius="@dimen/size_small_unb_ripple" />
android:radius="@dimen/size_large_unbounded_ripple" />

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?attr/colorControlHighlight"
android:radius="@dimen/size_unb_ripple" />
android:radius="@dimen/size_small_unbounded_ripple" />

View file

@ -120,7 +120,7 @@
app:layout_constraintTop_toBottomOf="@+id/playback_cover"
app:layout_constraintVertical_chainStyle="packed" />
<ImageButton
<org.oxycblt.auxio.playback.PlaybackButton
android:id="@+id/playback_loop"
style="@style/Widget.Auxio.Button.Unbounded"
android:layout_width="wrap_content"
@ -129,11 +129,12 @@
android:contentDescription="@string/desc_change_loop"
android:onClick="@{() -> playbackModel.incrementLoopStatus()}"
android:src="@drawable/ic_loop"
app:hasIndicator="true"
app:layout_constraintBottom_toBottomOf="@+id/playback_skip_prev"
app:layout_constraintStart_toStartOf="@+id/playback_seek_bar"
app:layout_constraintTop_toTopOf="@+id/playback_skip_prev" />
<ImageButton
<org.oxycblt.auxio.playback.PlaybackButton
android:id="@+id/playback_skip_prev"
style="@style/Widget.Auxio.Button.Unbounded"
android:layout_width="wrap_content"
@ -161,7 +162,7 @@
android:layout_marginBottom="@dimen/spacing_mid_large"
tools:src="@drawable/ic_play" />
<ImageButton
<org.oxycblt.auxio.playback.PlaybackButton
android:id="@+id/playback_skip_next"
style="@style/Widget.Auxio.Button.Unbounded"
android:layout_width="wrap_content"
@ -174,7 +175,7 @@
app:layout_constraintStart_toEndOf="@+id/playback_play_pause"
app:layout_constraintTop_toTopOf="@+id/playback_play_pause" />
<ImageButton
<org.oxycblt.auxio.playback.PlaybackButton
android:id="@+id/playback_shuffle"
style="@style/Widget.Auxio.Button.Unbounded"
android:layout_width="wrap_content"
@ -183,6 +184,7 @@
android:layout_marginEnd="@dimen/spacing_medium"
android:onClick="@{() -> playbackModel.invertShuffleStatus()}"
android:src="@drawable/ic_shuffle"
app:hasIndicator="true"
app:layout_constraintBottom_toBottomOf="@+id/playback_skip_next"
app:layout_constraintEnd_toEndOf="@+id/playback_seek_bar"
app:layout_constraintTop_toTopOf="@+id/playback_skip_next"

View file

@ -113,7 +113,7 @@
app:layout_constraintEnd_toEndOf="@+id/playback_song_container"
app:layout_constraintTop_toBottomOf="@+id/playback_album" />
<ImageButton
<org.oxycblt.auxio.playback.PlaybackButton
android:id="@+id/playback_loop"
style="@style/Widget.Auxio.Button.Unbounded"
android:layout_width="wrap_content"
@ -122,12 +122,13 @@
android:contentDescription="@string/desc_change_loop"
android:onClick="@{() -> playbackModel.incrementLoopStatus()}"
android:src="@drawable/ic_loop"
app:hasIndicator="true"
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" />
<ImageButton
<org.oxycblt.auxio.playback.PlaybackButton
android:id="@+id/playback_skip_prev"
style="@style/Widget.Auxio.Button.Unbounded"
android:layout_width="wrap_content"
@ -154,7 +155,7 @@
app:layout_constraintTop_toBottomOf="@+id/playback_seek_bar"
tools:src="@drawable/ic_pause" />
<ImageButton
<org.oxycblt.auxio.playback.PlaybackButton
android:id="@+id/playback_skip_next"
style="@style/Widget.Auxio.Button.Unbounded"
android:layout_width="wrap_content"
@ -167,12 +168,13 @@
app:layout_constraintStart_toEndOf="@+id/playback_play_pause"
app:layout_constraintTop_toTopOf="@+id/playback_play_pause" />
<ImageButton
<org.oxycblt.auxio.playback.PlaybackButton
android:id="@+id/playback_shuffle"
style="@style/Widget.Auxio.Button.Unbounded"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_large"
app:hasIndicator="true"
android:contentDescription="@string/desc_shuffle"
android:onClick="@{() -> playbackModel.invertShuffleStatus()}"
android:src="@drawable/ic_shuffle"

View file

@ -100,7 +100,7 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<ImageButton
<org.oxycblt.auxio.playback.PlaybackButton
android:id="@+id/playback_loop"
style="@style/Widget.Auxio.Button.Unbounded"
android:layout_width="wrap_content"
@ -109,12 +109,13 @@
android:contentDescription="@string/desc_change_loop"
android:onClick="@{() -> playbackModel.incrementLoopStatus()}"
android:src="@drawable/ic_loop"
app:hasIndicator="true"
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" />
<ImageButton
<org.oxycblt.auxio.playback.PlaybackButton
android:id="@+id/playback_skip_prev"
style="@style/Widget.Auxio.Button.Unbounded"
android:layout_width="wrap_content"
@ -141,7 +142,7 @@
app:layout_constraintStart_toStartOf="@+id/playback_seek_bar"
tools:src="@drawable/ic_pause" />
<ImageButton
<org.oxycblt.auxio.playback.PlaybackButton
android:id="@+id/playback_skip_next"
style="@style/Widget.Auxio.Button.Unbounded"
android:layout_width="wrap_content"
@ -154,7 +155,7 @@
app:layout_constraintStart_toEndOf="@+id/playback_play_pause"
app:layout_constraintTop_toTopOf="@+id/playback_play_pause" />
<ImageButton
<org.oxycblt.auxio.playback.PlaybackButton
android:id="@+id/playback_shuffle"
style="@style/Widget.Auxio.Button.Unbounded"
android:layout_width="wrap_content"
@ -163,6 +164,7 @@
android:contentDescription="@string/desc_shuffle"
android:onClick="@{() -> playbackModel.invertShuffleStatus()}"
android:src="@drawable/ic_shuffle"
app:hasIndicator="true"
app:layout_constraintBottom_toBottomOf="@+id/playback_skip_next"
app:layout_constraintStart_toEndOf="@+id/playback_skip_next"
app:layout_constraintTop_toTopOf="@+id/playback_skip_next"

View file

@ -59,7 +59,7 @@
app:layout_constraintTop_toBottomOf="@+id/playback_song"
tools:text="Artist Name / Album Name" />
<ImageButton
<org.oxycblt.auxio.playback.PlaybackButton
android:id="@+id/playback_skip_prev"
style="@style/Widget.Auxio.Button.Unbounded"
android:layout_width="wrap_content"
@ -73,7 +73,7 @@
app:layout_constraintStart_toEndOf="@+id/playback_song"
app:layout_constraintTop_toTopOf="@+id/playback_play_pause" />
<ImageButton
<org.oxycblt.auxio.playback.PlaybackButton
android:id="@+id/playback_play_pause"
style="@style/Widget.Auxio.Button.Unbounded"
android:layout_width="wrap_content"
@ -87,7 +87,7 @@
app:layout_constraintStart_toEndOf="@+id/playback_skip_prev"
app:layout_constraintTop_toTopOf="parent" />
<ImageButton
<org.oxycblt.auxio.playback.PlaybackButton
android:id="@+id/playback_skip_next"
style="@style/Widget.Auxio.Button.Unbounded"
android:layout_width="wrap_content"

View file

@ -104,7 +104,6 @@
app:layout_constraintTop_toBottomOf="@+id/playback_artist"
tools:text="Album Name" />
<org.oxycblt.auxio.playback.PlaybackSeekBar
android:id="@+id/playback_seek_bar"
android:layout_width="0dp"
@ -117,13 +116,13 @@
app:layout_constraintStart_toEndOf="@+id/playback_cover"
app:layout_constraintTop_toBottomOf="@+id/playback_album" />
<ImageButton
<org.oxycblt.auxio.playback.PlaybackButton
android:id="@+id/playback_loop"
style="@style/Widget.Auxio.Button.Unbounded"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_medium"
android:contentDescription="@string/desc_change_loop"
app:hasIndicator="true"
android:onClick="@{() -> playbackModel.incrementLoopStatus()}"
android:src="@drawable/ic_loop"
app:layout_constraintBottom_toBottomOf="@+id/playback_skip_prev"
@ -131,9 +130,8 @@
app:layout_constraintStart_toStartOf="@+id/playback_seek_bar"
app:layout_constraintTop_toTopOf="@+id/playback_skip_prev" />
<ImageButton
<org.oxycblt.auxio.playback.PlaybackButton
android:id="@+id/playback_skip_prev"
style="@style/Widget.Auxio.Button.Unbounded"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/desc_skip_prev"
@ -159,9 +157,8 @@
app:layout_constraintTop_toBottomOf="@+id/playback_seek_bar"
tools:src="@drawable/ic_pause" />
<ImageButton
<org.oxycblt.auxio.playback.PlaybackButton
android:id="@+id/playback_skip_next"
style="@style/Widget.Auxio.Button.Unbounded"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/desc_skip_next"
@ -172,13 +169,13 @@
app:layout_constraintStart_toEndOf="@+id/playback_play_pause"
app:layout_constraintTop_toTopOf="@+id/playback_play_pause" />
<ImageButton
<org.oxycblt.auxio.playback.PlaybackButton
android:id="@+id/playback_shuffle"
style="@style/Widget.Auxio.Button.Unbounded"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/spacing_medium"
android:contentDescription="@string/desc_shuffle"
app:hasIndicator="true"
android:onClick="@{() -> playbackModel.invertShuffleStatus()}"
android:src="@drawable/ic_shuffle"
app:layout_constraintBottom_toBottomOf="@+id/playback_skip_next"

View file

@ -85,7 +85,7 @@
app:layout_constraintStart_toEndOf="@+id/playback_skip_prev"
app:layout_constraintTop_toTopOf="parent" />
<ImageButton
<org.oxycblt.auxio.playback.PlaybackButton
android:id="@+id/playback_skip_next"
style="@style/Widget.Auxio.Button.Unbounded"
android:layout_width="wrap_content"

View file

@ -99,22 +99,21 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<ImageButton
<org.oxycblt.auxio.playback.PlaybackButton
android:id="@+id/playback_loop"
style="@style/Widget.Auxio.Button.Unbounded"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_medium"
android:contentDescription="@string/desc_change_loop"
android:onClick="@{() -> playbackModel.incrementLoopStatus()}"
android:src="@drawable/ic_loop"
app:hasIndicator="true"
app:layout_constraintBottom_toBottomOf="@+id/playback_skip_prev"
app:layout_constraintStart_toStartOf="@+id/playback_seek_bar"
app:layout_constraintTop_toTopOf="@+id/playback_skip_prev" />
<ImageButton
<org.oxycblt.auxio.playback.PlaybackButton
android:id="@+id/playback_skip_prev"
style="@style/Widget.Auxio.Button.Unbounded"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/desc_skip_prev"
@ -139,11 +138,12 @@
app:layout_constraintStart_toStartOf="@+id/playback_seek_bar"
tools:src="@drawable/ic_play" />
<ImageButton
<org.oxycblt.auxio.playback.PlaybackButton
android:id="@+id/playback_skip_next"
style="@style/Widget.Auxio.Button.Unbounded"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minHeight="@dimen/size_btn_small"
android:minWidth="@dimen/size_btn_small"
android:contentDescription="@string/desc_skip_next"
android:onClick="@{() -> playbackModel.skipNext()}"
android:src="@drawable/ic_skip_next"
@ -152,15 +152,17 @@
app:layout_constraintStart_toEndOf="@+id/playback_play_pause"
app:layout_constraintTop_toTopOf="@+id/playback_play_pause" />
<ImageButton
<org.oxycblt.auxio.playback.PlaybackButton
android:id="@+id/playback_shuffle"
style="@style/Widget.Auxio.Button.Unbounded"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minHeight="@dimen/size_btn_small"
android:minWidth="@dimen/size_btn_small"
android:layout_marginEnd="@dimen/spacing_medium"
android:contentDescription="@string/desc_shuffle"
android:onClick="@{() -> playbackModel.invertShuffleStatus()}"
android:src="@drawable/ic_shuffle"
app:hasIndicator="true"
app:layout_constraintBottom_toBottomOf="@+id/playback_skip_next"
app:layout_constraintEnd_toEndOf="@+id/playback_seek_bar"
app:layout_constraintTop_toTopOf="@+id/playback_skip_next"

View file

@ -31,10 +31,10 @@
style="@style/Widget.AppCompat.Button.Borderless"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@drawable/ui_small_unbounded_ripple"
android:contentDescription="@{context.getString(header.desc)}"
android:minWidth="@dimen/size_btn_small"
android:minHeight="@dimen/size_btn_small"
android:background="@drawable/ui_unbounded_ripple"
android:paddingStart="@dimen/spacing_medium"
android:paddingEnd="@dimen/spacing_medium"
android:src="@{context.getDrawable(header.icon)}"

View file

@ -58,7 +58,7 @@
app:layout_constraintTop_toBottomOf="@+id/playback_song"
tools:text="Artist Name / Album Name" />
<ImageButton
<org.oxycblt.auxio.playback.PlaybackButton
android:id="@+id/playback_play_pause"
style="@style/Widget.Auxio.Button.Unbounded"
android:layout_width="wrap_content"

View file

@ -31,7 +31,7 @@
android:layout_marginEnd="@dimen/spacing_medium"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
android:src="@drawable/ui_widget_aspect_ratio"
android:src="@drawable/ui_remote_aspect_ratio"
android:visibility="invisible"
tools:ignore="ContentDescription" />
@ -76,7 +76,7 @@
<android.widget.ImageButton
android:id="@+id/widget_loop"
style="@style/Widget.Auxio.Button.AppWidget"
style="@style/Widget.Auxio.PlaybackButton.AppWidget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
@ -85,7 +85,7 @@
<android.widget.ImageButton
android:id="@+id/widget_skip_prev"
style="@style/Widget.Auxio.Button.AppWidget"
style="@style/Widget.Auxio.PlaybackButton.AppWidget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
@ -94,7 +94,7 @@
<android.widget.ImageButton
android:id="@+id/widget_play_pause"
style="@style/Widget.Auxio.Button.AppWidget"
style="@style/Widget.Auxio.PlaybackButton.AppWidget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
@ -103,7 +103,7 @@
<android.widget.ImageButton
android:id="@+id/widget_skip_next"
style="@style/Widget.Auxio.Button.AppWidget"
style="@style/Widget.Auxio.PlaybackButton.AppWidget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
@ -112,7 +112,7 @@
<android.widget.ImageButton
android:id="@+id/widget_shuffle"
style="@style/Widget.Auxio.Button.AppWidget"
style="@style/Widget.Auxio.PlaybackButton.AppWidget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"

View file

@ -31,7 +31,7 @@
android:layout_marginEnd="@dimen/spacing_medium"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
android:src="@drawable/ui_widget_aspect_ratio"
android:src="@drawable/ui_remote_aspect_ratio"
android:visibility="invisible"
tools:ignore="ContentDescription" />
@ -77,7 +77,7 @@
<android.widget.ImageButton
android:id="@+id/widget_skip_prev"
style="@style/Widget.Auxio.Button.AppWidget"
style="@style/Widget.Auxio.PlaybackButton.AppWidget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
@ -86,7 +86,7 @@
<android.widget.ImageButton
android:id="@+id/widget_play_pause"
style="@style/Widget.Auxio.Button.AppWidget"
style="@style/Widget.Auxio.PlaybackButton.AppWidget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
@ -95,7 +95,7 @@
<android.widget.ImageButton
android:id="@+id/widget_skip_next"
style="@style/Widget.Auxio.Button.AppWidget"
style="@style/Widget.Auxio.PlaybackButton.AppWidget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"

View file

@ -33,7 +33,7 @@
android:layout_marginBottom="@dimen/spacing_small"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
android:src="@drawable/ui_widget_aspect_ratio"
android:src="@drawable/ui_remote_aspect_ratio"
android:visibility="invisible"
tools:ignore="ContentDescription" />
@ -63,7 +63,7 @@
<android.widget.ImageButton
android:id="@+id/widget_skip_prev"
style="@style/Widget.Auxio.Button.AppWidget"
style="@style/Widget.Auxio.PlaybackButton.AppWidget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
@ -72,7 +72,7 @@
<android.widget.ImageButton
android:id="@+id/widget_play_pause"
style="@style/Widget.Auxio.Button.AppWidget"
style="@style/Widget.Auxio.PlaybackButton.AppWidget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
@ -81,7 +81,7 @@
<android.widget.ImageButton
android:id="@+id/widget_skip_next"
style="@style/Widget.Auxio.Button.AppWidget"
style="@style/Widget.Auxio.PlaybackButton.AppWidget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"

View file

@ -52,7 +52,7 @@
<android.widget.ImageButton
android:id="@+id/widget_play_pause"
style="@style/Widget.Auxio.Button.AppWidget"
style="@style/Widget.Auxio.PlaybackButton.AppWidget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/desc_play_pause"

View file

@ -33,7 +33,7 @@
android:layout_marginBottom="@dimen/spacing_small"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
android:src="@drawable/ui_widget_aspect_ratio"
android:src="@drawable/ui_remote_aspect_ratio"
android:visibility="invisible"
tools:ignore="ContentDescription" />
@ -63,7 +63,7 @@
<android.widget.ImageButton
android:id="@+id/widget_loop"
style="@style/Widget.Auxio.Button.AppWidget"
style="@style/Widget.Auxio.PlaybackButton.AppWidget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
@ -72,7 +72,7 @@
<android.widget.ImageButton
android:id="@+id/widget_skip_prev"
style="@style/Widget.Auxio.Button.AppWidget"
style="@style/Widget.Auxio.PlaybackButton.AppWidget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
@ -81,7 +81,7 @@
<android.widget.ImageButton
android:id="@+id/widget_play_pause"
style="@style/Widget.Auxio.Button.AppWidget"
style="@style/Widget.Auxio.PlaybackButton.AppWidget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
@ -90,7 +90,7 @@
<android.widget.ImageButton
android:id="@+id/widget_skip_next"
style="@style/Widget.Auxio.Button.AppWidget"
style="@style/Widget.Auxio.PlaybackButton.AppWidget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
@ -99,7 +99,7 @@
<android.widget.ImageButton
android:id="@+id/widget_shuffle"
style="@style/Widget.Auxio.Button.AppWidget"
style="@style/Widget.Auxio.PlaybackButton.AppWidget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"

View file

@ -4,12 +4,13 @@
<!-- Android 12 configuration -->
<style name="Theme.Auxio.V31" parent="Theme.Auxio.V27">
<!--
Theme.Material3.DayNight doesn't actually apply dynamic colors by default. No.
You have to apply a T H E M E O V E R L A Y for them to work. Except the ThemeOverlay
causes appbars to become a completely different color for basically no reason! Guess
we have to manually copy paste these values into our V31 styles instead!
Why do I keep developing for this platform why do I keep developing for this platform why
Theme.Material3.DayNight doesn't actually apply dynamic colors by default.
You instead have two options for applying them:
1. Using DynamicColors in Activity.onCreate
2. Applying on of the theme overlays
Neither of these work for Auxio, because they override workarounds for android insanity
and generally lead to inexplicable issues. Instead we have to manually copy and paste the
theme values into our own custom theme.
-->
<!-- Color palettes -->

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Widget.Auxio.Button.AppWidget.V31" parent="Widget.AppCompat.Button.Borderless">
<item name="android:background">@drawable/ui_unbounded_ripple</item>
<item name="android:background">@drawable/ui_large_unbounded_ripple</item>
</style>
</resources>

View file

@ -4,12 +4,13 @@
<!-- Android 12 configuration -->
<style name="Theme.Auxio.V31" parent="Theme.Auxio.V27">
<!--
Theme.Material3.DayNight doesn't actually apply dynamic colors by default. No.
You have to apply a T H E M E O V E R L A Y for them to work. Except the ThemeOverlay
causes appbars to become a completely different color for basically no reason! Guess
we have to manually ctrl-c ctrl-v these values into our V31 styles instead!
Why do I keep developing for this platform why do I keep developing for this platform why
Theme.Material3.DayNight doesn't actually apply dynamic colors by default.
You instead have two options for applying them:
1. Using DynamicColors in Activity.onCreate
2. Applying on of the theme overlays
Neither of these work for Auxio, because they override workarounds for android insanity
and generally lead to inexplicable issues. Instead we have to manually copy and paste the
theme values into our own custom theme.
-->
<!-- Color palettes -->

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="PlaybackButton">
<attr name="hasIndicator" format="boolean" />
</declare-styleable>
<declare-styleable name="IntListPreference">
<attr name="entries" format="reference" />
<attr name="entryValues" format="reference" />
</declare-styleable>
</resources>

View file

@ -16,11 +16,11 @@
<dimen name="size_cover_mid_huge">192dp</dimen>
<dimen name="size_cover_huge">256dp</dimen>
<dimen name="size_small_unb_ripple">20dp</dimen>
<dimen name="size_unb_ripple">24dp</dimen>
<dimen name="size_small_unbounded_ripple">20dp</dimen>
<dimen name="size_large_unbounded_ripple">24dp</dimen>
<dimen name="size_track_number">32dp</dimen>
<dimen name="size_play_fab_icon">32dp</dimen>
<dimen name="size_playback_icon">32dp</dimen>
<dimen name="text_size_ext_label_larger">16sp</dimen>
<dimen name="text_size_ext_title_mid_large">18sp</dimen>

View file

@ -1,11 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Preference values -->
<declare-styleable name="IntListPreference">
<attr name="entries" format="reference" />
<attr name="entryValues" format="reference" />
</declare-styleable>
<string-array name="entries_theme">
<item>@string/set_theme_auto</item>
<item>@string/set_theme_day</item>

View file

@ -60,13 +60,18 @@
<item name="android:background">?android:attr/selectableItemBackgroundBorderless</item>
</style>
<!-- Widget button that follows the weird widget restrictions -->
<style name="Widget.Auxio.Button.AppWidget" parent="Widget.Auxio.Button.AppWidget.V31">
<!-- A variant of PlaybackButton that plays along with AppWidget restrictions. -->
<style name="Widget.Auxio.PlaybackButton.AppWidget" parent="Widget.Auxio.Button.AppWidget.V31">
<item name="android:minHeight">@dimen/size_btn_small</item>
<item name="android:scaleType">fitCenter</item>
<item name="android:padding">@dimen/spacing_small</item>
</style>
<!-- A variant of PlaybackButton that is intended for buttons that show indicators -->
<style name="Widget.Auxio.PlaybackButton.Indicator.AppWidget" parent="Widget.Auxio.Button.AppWidget.V31">
<item name="android:minHeight">@dimen/size_btn_small</item>
</style>
<!-- Widget panel -->
<style name="Widget.Auxio.AppWidget.Panel" parent="">
<item name="android:layout_height">wrap_content</item>

View file

@ -152,11 +152,7 @@
<!-- BUTTON STYLES -->
<style name="Widget.Auxio.Button.Unbounded" parent="Widget.AppCompat.Button.Borderless">
<item name="android:minHeight">@dimen/size_btn_small</item>
<item name="android:minWidth">@dimen/size_btn_small</item>
<item name="android:background">@drawable/ui_unbounded_ripple</item>
<item name="android:scaleType">fitCenter</item>
<item name="android:padding">@dimen/spacing_small</item>
<item name="android:padding">0dp</item>
</style>
<style name="Widget.Auxio.Button.Primary" parent="Widget.Material3.Button">
@ -168,7 +164,7 @@
</style>
<style name="Widget.Auxio.FloatingActionButton.PlayPause" parent="Widget.Material3.FloatingActionButton.Secondary">
<item name="maxImageSize">@dimen/size_play_fab_icon</item>
<item name="maxImageSize">@dimen/size_playback_icon</item>
<item name="fabCustomSize">@dimen/size_btn_large</item>
<!--
@ -177,7 +173,6 @@
-->
<item name="android:elevation">0dp</item>
<item name="elevation">0dp</item>
<item name="shapeAppearanceOverlay">@style/ShapeAppearance.Auxio.FloatingActionButton.PlayPause</item>
</style>
<style name="ShapeAppearance.Auxio.FloatingActionButton.PlayPause" parent="">