diff --git a/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt b/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt
index 9048acf13..c39f267a5 100644
--- a/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt
+++ b/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt
@@ -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()
}
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackButton.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackButton.kt
new file mode 100644
index 000000000..c89d8ccba
--- /dev/null
+++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackButton.kt
@@ -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)
+ }
+ }
+}
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt
index a67f375d3..92ae2cd93 100644
--- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt
+++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt
@@ -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 ->
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackLayout.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackLayout.kt
index 6d5a7a2ca..027ccbf00 100644
--- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackLayout.kt
+++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackLayout.kt
@@ -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 {
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackNotification.kt b/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackNotification.kt
index d56a2663d..d8a49490e 100644
--- a/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackNotification.kt
+++ b/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackNotification.kt
@@ -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)
}
diff --git a/app/src/main/java/org/oxycblt/auxio/widgets/Forms.kt b/app/src/main/java/org/oxycblt/auxio/widgets/Forms.kt
index bd0a64a39..50e607e9e 100644
--- a/app/src/main/java/org/oxycblt/auxio/widgets/Forms.kt
+++ b/app/src/main/java/org/oxycblt/auxio/widgets/Forms.kt
@@ -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
}
diff --git a/app/src/main/res/drawable/ic_loop_off.xml b/app/src/main/res/drawable/ic_remote_loop_off.xml
similarity index 89%
rename from app/src/main/res/drawable/ic_loop_off.xml
rename to app/src/main/res/drawable/ic_remote_loop_off.xml
index fb09414e1..433c53d66 100644
--- a/app/src/main/res/drawable/ic_loop_off.xml
+++ b/app/src/main/res/drawable/ic_remote_loop_off.xml
@@ -2,6 +2,7 @@
diff --git a/app/src/main/res/drawable/ic_shuffle_on.xml b/app/src/main/res/drawable/ic_remote_shuffle_on.xml
similarity index 100%
rename from app/src/main/res/drawable/ic_shuffle_on.xml
rename to app/src/main/res/drawable/ic_remote_shuffle_on.xml
diff --git a/app/src/main/res/drawable/ic_shuffle.xml b/app/src/main/res/drawable/ic_shuffle.xml
index 4a14cbffa..a22ea2e59 100644
--- a/app/src/main/res/drawable/ic_shuffle.xml
+++ b/app/src/main/res/drawable/ic_shuffle.xml
@@ -2,7 +2,7 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ui_small_unbounded_ripple.xml b/app/src/main/res/drawable/ui_large_unbounded_ripple.xml
similarity index 72%
rename from app/src/main/res/drawable/ui_small_unbounded_ripple.xml
rename to app/src/main/res/drawable/ui_large_unbounded_ripple.xml
index b4367d84b..3c999556f 100644
--- a/app/src/main/res/drawable/ui_small_unbounded_ripple.xml
+++ b/app/src/main/res/drawable/ui_large_unbounded_ripple.xml
@@ -1,4 +1,4 @@
+ android:radius="@dimen/size_large_unbounded_ripple" />
diff --git a/app/src/main/res/drawable/ui_widget_aspect_ratio.xml b/app/src/main/res/drawable/ui_remote_aspect_ratio.xml
similarity index 100%
rename from app/src/main/res/drawable/ui_widget_aspect_ratio.xml
rename to app/src/main/res/drawable/ui_remote_aspect_ratio.xml
diff --git a/app/src/main/res/drawable/ui_unbounded_ripple.xml b/app/src/main/res/drawable/ui_unbounded_ripple.xml
index 003c9a27b..3118661f4 100644
--- a/app/src/main/res/drawable/ui_unbounded_ripple.xml
+++ b/app/src/main/res/drawable/ui_unbounded_ripple.xml
@@ -1,4 +1,4 @@
+ android:radius="@dimen/size_small_unbounded_ripple" />
diff --git a/app/src/main/res/layout-land/fragment_playback.xml b/app/src/main/res/layout-land/fragment_playback.xml
index 89e89c49f..6a5d9cee3 100644
--- a/app/src/main/res/layout-land/fragment_playback.xml
+++ b/app/src/main/res/layout-land/fragment_playback.xml
@@ -120,7 +120,7 @@
app:layout_constraintTop_toBottomOf="@+id/playback_cover"
app:layout_constraintVertical_chainStyle="packed" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -76,7 +76,7 @@
@@ -77,7 +77,7 @@
@@ -63,7 +63,7 @@
@@ -63,7 +63,7 @@
\ No newline at end of file
diff --git a/app/src/main/res/values-v31/styles_core.xml b/app/src/main/res/values-v31/styles_core.xml
index 977e3313c..8b2afb083 100644
--- a/app/src/main/res/values-v31/styles_core.xml
+++ b/app/src/main/res/values-v31/styles_core.xml
@@ -4,12 +4,13 @@
-
-
+
+
+