all: general cleanup
Do some general code cleanup.
This commit is contained in:
parent
4d22b99577
commit
0209e526e1
13 changed files with 110 additions and 122 deletions
|
@ -42,7 +42,7 @@ I primarily built Auxio for myself, but you can use it too, I guess.
|
|||
- Customizable UI & Behavior
|
||||
- Genres/Artists/Albums/Songs indexing
|
||||
- Reliable playback state persistence
|
||||
- ReplayGain support (On MP3, FLAC, OGG, and OPUS)
|
||||
- ReplayGain support (On MP3, MP4, FLAC, OGG, and OPUS)
|
||||
- Material You (Android 12+ only)
|
||||
- Edge-to-edge
|
||||
- Embedded covers support
|
||||
|
|
|
@ -20,7 +20,7 @@ package org.oxycblt.auxio.detail
|
|||
|
||||
import androidx.annotation.MenuRes
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.core.view.forEach
|
||||
import androidx.core.view.children
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
|
@ -125,7 +125,7 @@ abstract class DetailFragment : Fragment() {
|
|||
}
|
||||
|
||||
if (showItem != null) {
|
||||
menu.forEach { item ->
|
||||
for (item in menu.children) {
|
||||
item.isVisible = showItem(item.itemId)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ sealed class Tab(open val mode: DisplayMode) {
|
|||
var sequence = 0b0100
|
||||
var shift = SEQUENCE_LEN * 4
|
||||
|
||||
distinct.forEach { tab ->
|
||||
for (tab in distinct) {
|
||||
val bin = when (tab) {
|
||||
is Visible -> 1.shl(3) or tab.mode.ordinal
|
||||
is Invisible -> tab.mode.ordinal
|
||||
|
@ -107,10 +107,9 @@ sealed class Tab(open val mode: DisplayMode) {
|
|||
// Make sure there are no duplicate tabs
|
||||
val distinct = tabs.distinctBy { it.mode }
|
||||
|
||||
// For safety, use the default configuration if something went wrong
|
||||
// and we have an empty or larger-than-expected tab array.
|
||||
// For safety, return null if we have an empty or larger-than-expected tab array.
|
||||
if (distinct.isEmpty() || distinct.size < SEQUENCE_LEN) {
|
||||
logE("Sequence size was ${distinct.size}, which is invalid. Using defaults instead")
|
||||
logE("Sequence size was ${distinct.size}, which is invalid.")
|
||||
return null
|
||||
}
|
||||
|
||||
|
|
|
@ -137,7 +137,7 @@ data class Album(
|
|||
val _mediaStoreArtistName: String,
|
||||
) : MusicParent() {
|
||||
init {
|
||||
songs.forEach { song ->
|
||||
for (song in songs) {
|
||||
song.mediaStoreLinkAlbum(this)
|
||||
}
|
||||
}
|
||||
|
@ -181,7 +181,7 @@ data class Artist(
|
|||
val albums: List<Album>
|
||||
) : MusicParent() {
|
||||
init {
|
||||
albums.forEach { album ->
|
||||
for (album in albums) {
|
||||
album.mediaStoreLinkArtist(this)
|
||||
}
|
||||
}
|
||||
|
@ -198,23 +198,19 @@ data class Artist(
|
|||
data class Genre(
|
||||
override val name: String,
|
||||
override val resolvedName: String,
|
||||
/** Internal field. Do not use. */
|
||||
val _mediaStoreId: Long
|
||||
val songs: List<Song>
|
||||
) : MusicParent() {
|
||||
init {
|
||||
for (song in songs) {
|
||||
song.mediaStoreLinkGenre(this)
|
||||
}
|
||||
}
|
||||
|
||||
override val id = name.hashCode().toLong()
|
||||
|
||||
/** The formatted total duration of this genre */
|
||||
val totalDuration: String get() =
|
||||
songs.sumOf { it.seconds }.toDuration(false)
|
||||
|
||||
private val mSongs = mutableListOf<Song>()
|
||||
val songs: List<Song> get() = mSongs
|
||||
|
||||
/** Internal method. Do not use. */
|
||||
fun linkSong(song: Song) {
|
||||
mSongs.add(song)
|
||||
song.mediaStoreLinkGenre(this)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -22,13 +22,14 @@ import java.lang.Exception
|
|||
*
|
||||
* You think that if you wanted to query a song's genre from a media database, you could just
|
||||
* put "genre" in the query and it would return it, right? But not with MediaStore! No, that's
|
||||
* too straightforward for this class that was dropped on it's head as a baby. So instead, you
|
||||
* too straightforward for this contract that was dropped on it's head as a baby. So instead, you
|
||||
* have to query for each genre, query all the songs in each genre, and then iterate through those
|
||||
* songs to link every song with their genre. This is not documented anywhere, and the
|
||||
* O(mom im scared) algorithm you have to run to get it working single-handedly DOUBLES Auxio's
|
||||
* loading times. At no point have the devs considered that this column is absolutely insane, and
|
||||
* instead focused on adding infuriat- I mean nice proprietary extensions to MediaStore for their
|
||||
* own Google Play Music, and we all know how great that worked out!
|
||||
* own Google Play Music, and of course every Google Play Music user knew how great that turned
|
||||
* out!
|
||||
*
|
||||
* It's not even ergonomics that makes this API bad. It's base implementation is completely borked
|
||||
* as well. Did you know that MediaStore doesn't accept dates that aren't from ID3v2.3 MP3 files?
|
||||
|
@ -37,7 +38,7 @@ import java.lang.Exception
|
|||
* DATE tag. Once again, this is because internally android uses an ancient in-house metadata
|
||||
* parser to get everything indexed, and so far they have not bothered to modernize this parser
|
||||
* or even switch it to something more powerful like Taglib, not even in Android 12. ID3v2.4 has
|
||||
* been around for 21 years. It can drink now. All of my what.
|
||||
* been around for *21 years.* *It can drink now.* All of my what.
|
||||
*
|
||||
* Not to mention all the other infuriating quirks. Album artists can't be accessed from the albums
|
||||
* table, so we have to go for the less efficient "make a big query on all the songs lol" method
|
||||
|
@ -46,21 +47,21 @@ import java.lang.Exception
|
|||
* 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
|
||||
* 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 Latin-1
|
||||
* to Shift JIS WHY WHY WHY WHY WHY WHY WHY WHY WHY WHY WHY WHY WHY WHY WHY WHY WHY WHY WHY WHY WHY
|
||||
* 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
|
||||
* Latin-1 to *Shift JIS* WHY WHY WHY WHY WHY WHY WHY WHY WHY WHY WHY WHY WHY WHY WHY WHY WHY WHY
|
||||
*
|
||||
* Is there anything we can do about it? No. Google has routinely shut down issues that begged google
|
||||
* to fix glaring issues with MediaStore or to just take the API behind the woodshed and shoot it.
|
||||
* Largely because they have zero incentive to improve it given how "obscure" music listening is.
|
||||
* As a result, some players like Vanilla and VLC just hack their own pidgin version of MediaStore
|
||||
* from their own parsers, but this is both infeasible for Auxio due to how incredibly slow it is
|
||||
* to get a file handle from the android sandbox AND how much harder it is to manage a database of
|
||||
* your own media that mirrors the filesystem perfectly. And even if I set aside those crippling
|
||||
* issues and changed my indexer to that, it would face the even larger problem of how google keeps
|
||||
* trying to kill the filesystem and force you into their ContentResolver API. In the future
|
||||
* MediaStore could be the only system we have, which is also the day that greenland melts and
|
||||
* birthdays stop happening forever.
|
||||
* Largely because they have zero incentive to improve it given how "obscure" local music listening
|
||||
* is. As a result, some players like Vanilla and VLC just hack their own pseudo-MediaStore
|
||||
* implementation from their own (better) parsers, but this is both infeasible for Auxio due to how
|
||||
* incredibly slow it is to get a file handle from the android sandbox AND how much harder it is to
|
||||
* manage a database of your own media that mirrors the filesystem perfectly. And even if I set
|
||||
* aside those crippling issues and changed my indexer to that, it would face the even larger
|
||||
* problem of how google keeps trying to kill the filesystem and force you into their
|
||||
* ContentResolver API. In the future MediaStore could be the only system we have, which is also the
|
||||
* day that greenland melts and birthdays stop happening forever.
|
||||
*
|
||||
* I'm pretty sure nothing is going to happen and MediaStore will continue to be neglected and
|
||||
* probably deprecated eventually for a "new" API that just coincidentally excludes music indexing.
|
||||
|
@ -122,32 +123,32 @@ class MusicLoader {
|
|||
args += "$path%" // Append % so that the selector properly detects children
|
||||
}
|
||||
|
||||
context.contentResolver.query(
|
||||
context.applicationContext.contentResolver.query(
|
||||
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
|
||||
arrayOf(
|
||||
MediaStore.Audio.Media._ID,
|
||||
MediaStore.Audio.Media.TITLE,
|
||||
MediaStore.Audio.Media.DISPLAY_NAME,
|
||||
MediaStore.Audio.Media.ALBUM,
|
||||
MediaStore.Audio.Media.ALBUM_ID,
|
||||
MediaStore.Audio.Media.ARTIST,
|
||||
MediaStore.Audio.Media.ALBUM_ARTIST,
|
||||
MediaStore.Audio.Media.YEAR,
|
||||
MediaStore.Audio.Media.TRACK,
|
||||
MediaStore.Audio.Media.DURATION,
|
||||
MediaStore.Audio.AudioColumns._ID,
|
||||
MediaStore.Audio.AudioColumns.TITLE,
|
||||
MediaStore.Audio.AudioColumns.DISPLAY_NAME,
|
||||
MediaStore.Audio.AudioColumns.ALBUM,
|
||||
MediaStore.Audio.AudioColumns.ALBUM_ID,
|
||||
MediaStore.Audio.AudioColumns.ARTIST,
|
||||
MediaStore.Audio.AudioColumns.ALBUM_ARTIST,
|
||||
MediaStore.Audio.AudioColumns.YEAR,
|
||||
MediaStore.Audio.AudioColumns.TRACK,
|
||||
MediaStore.Audio.AudioColumns.DURATION,
|
||||
),
|
||||
selector, args.toTypedArray(), null
|
||||
)?.use { cursor ->
|
||||
val idIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID)
|
||||
val titleIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE)
|
||||
val fileIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DISPLAY_NAME)
|
||||
val albumIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM)
|
||||
val albumIdIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM_ID)
|
||||
val artistIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST)
|
||||
val albumArtistIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM_ARTIST)
|
||||
val yearIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.YEAR)
|
||||
val trackIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TRACK)
|
||||
val durationIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION)
|
||||
val idIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns._ID)
|
||||
val titleIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.TITLE)
|
||||
val fileIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.DISPLAY_NAME)
|
||||
val albumIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.ALBUM)
|
||||
val albumIdIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.ALBUM_ID)
|
||||
val artistIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.ARTIST)
|
||||
val albumArtistIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.ALBUM_ARTIST)
|
||||
val yearIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.YEAR)
|
||||
val trackIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.TRACK)
|
||||
val durationIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.DURATION)
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
val id = cursor.getLong(idIndex)
|
||||
|
@ -284,7 +285,6 @@ class MusicLoader {
|
|||
private fun readGenres(context: Context, songs: List<Song>): List<Genre> {
|
||||
val genres = mutableListOf<Genre>()
|
||||
|
||||
// First, get a cursor for every genre in the android system
|
||||
val genreCursor = context.contentResolver.query(
|
||||
MediaStore.Audio.Genres.EXTERNAL_CONTENT_URI,
|
||||
arrayOf(
|
||||
|
@ -300,45 +300,46 @@ class MusicLoader {
|
|||
val nameIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.Genres.NAME)
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
// Genre names can be a normal name, an ID3v2 constant, or null. Normal names are
|
||||
// resolved as usual, but null values don't make sense and are often junk anyway,
|
||||
// so we skip genres that have them.
|
||||
val id = cursor.getLong(idIndex)
|
||||
// No non-broken genre would be missing a name.
|
||||
val name = cursor.getStringOrNull(nameIndex) ?: continue
|
||||
val resolvedName = when (name) {
|
||||
MediaStore.UNKNOWN_STRING -> context.getString(R.string.def_genre)
|
||||
else -> name.getGenreNameCompat() ?: name
|
||||
}
|
||||
val resolvedName = name.getGenreNameCompat() ?: name
|
||||
val genreSongs = queryGenreSongs(context, id, songs) ?: continue
|
||||
|
||||
val genre = Genre(name, resolvedName, id)
|
||||
|
||||
linkGenre(context, genre, songs)
|
||||
genres.add(genre)
|
||||
genres.add(
|
||||
Genre(
|
||||
name,
|
||||
resolvedName,
|
||||
genreSongs
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Songs that don't have a genre will be thrown into an unknown genre.
|
||||
val unknownGenre = Genre(
|
||||
name = MediaStore.UNKNOWN_STRING,
|
||||
resolvedName = context.getString(R.string.def_genre),
|
||||
Long.MIN_VALUE
|
||||
)
|
||||
val songsWithoutGenres = songs.filter { it.genre == null }
|
||||
|
||||
songs.forEach { song ->
|
||||
if (song.genre == null) {
|
||||
unknownGenre.linkSong(song)
|
||||
}
|
||||
}
|
||||
if (songsWithoutGenres.isNotEmpty()) {
|
||||
// Songs that don't have a genre will be thrown into an unknown genre.
|
||||
val unknownGenre = Genre(
|
||||
name = MediaStore.UNKNOWN_STRING,
|
||||
resolvedName = context.getString(R.string.def_genre),
|
||||
songsWithoutGenres
|
||||
)
|
||||
|
||||
if (unknownGenre.songs.isNotEmpty()) {
|
||||
genres.add(unknownGenre)
|
||||
}
|
||||
|
||||
return genres
|
||||
}
|
||||
|
||||
private fun linkGenre(context: Context, genre: Genre, songs: List<Song>) {
|
||||
private fun queryGenreSongs(context: Context, genreId: Long, songs: List<Song>): List<Song>? {
|
||||
val genreSongs = mutableListOf<Song>()
|
||||
|
||||
// Don't even bother blacklisting here as useless iterations are less expensive than IO
|
||||
val songCursor = context.contentResolver.query(
|
||||
MediaStore.Audio.Genres.Members.getContentUri("external", genre._mediaStoreId),
|
||||
MediaStore.Audio.Genres.Members.getContentUri("external", genreId),
|
||||
arrayOf(MediaStore.Audio.Genres.Members._ID),
|
||||
null, null, null
|
||||
)
|
||||
|
@ -350,9 +351,13 @@ class MusicLoader {
|
|||
val id = cursor.getLong(idIndex)
|
||||
|
||||
songs.find { it._mediaStoreId == id }?.let { song ->
|
||||
genre.linkSong(song)
|
||||
genreSongs.add(song)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Some genres might be empty due to MediaStore empty.
|
||||
// If that is the case, we drop them.
|
||||
return genreSongs.ifEmpty { null }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -98,6 +98,8 @@ class AudioReactor(
|
|||
return
|
||||
}
|
||||
|
||||
logD("$metadata")
|
||||
|
||||
// ReplayGain is configurable, so determine what to do based off of the mode.
|
||||
val useAlbumGain: (Gain) -> Boolean = when (settingsManager.replayGainMode) {
|
||||
ReplayGainMode.OFF -> {
|
||||
|
@ -144,8 +146,8 @@ class AudioReactor(
|
|||
|
||||
// Final adjustment along the volume curve.
|
||||
// Ensure this is clamped to 0 or 1 so that it can be used as a volume.
|
||||
// TODO: Support positive ReplayGain values. They're more obscure but still exist.
|
||||
// It will likely require moving functionality from this class to an AudioProcessor
|
||||
// While positive ReplayGain values *could* be theoretically added, it's such
|
||||
// a niche use-case that to be worth the effort required. Maybe if someone requests it.
|
||||
volume = MathUtils.clamp((10f.pow((adjust / 20f))), 0f, 1f)
|
||||
}
|
||||
|
||||
|
|
|
@ -55,11 +55,8 @@ class SettingsListFragment : PreferenceFragmentCompat() {
|
|||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
preferenceScreen.children.forEach { pref ->
|
||||
recursivelyHandleChildren(pref)
|
||||
}
|
||||
|
||||
preferenceManager.onDisplayPreferenceDialogListener = this
|
||||
preferenceScreen.children.forEach(::recursivelyHandlePreference)
|
||||
|
||||
view.findViewById<RecyclerView>(androidx.preference.R.id.recycler_view).apply {
|
||||
clipToPadding = false
|
||||
|
@ -87,28 +84,18 @@ class SettingsListFragment : PreferenceFragmentCompat() {
|
|||
}
|
||||
|
||||
/**
|
||||
* Recursively call [handlePreference] on a preference.
|
||||
* Recursively handle a preference, doing any specific actions on it.
|
||||
*/
|
||||
private fun recursivelyHandleChildren(preference: Preference) {
|
||||
if (!preference.isVisible) {
|
||||
return
|
||||
}
|
||||
private fun recursivelyHandlePreference(preference: Preference) {
|
||||
if (!preference.isVisible) return
|
||||
|
||||
if (preference is PreferenceCategory) {
|
||||
// If this preference is a category of its own, handle its own children
|
||||
preference.children.forEach { pref ->
|
||||
recursivelyHandleChildren(pref)
|
||||
for (child in preference.children) {
|
||||
recursivelyHandlePreference(child)
|
||||
}
|
||||
} else {
|
||||
handlePreference(preference)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a preference, doing any specific actions on it.
|
||||
*/
|
||||
private fun handlePreference(pref: Preference) {
|
||||
pref.apply {
|
||||
preference.apply {
|
||||
when (key) {
|
||||
SettingsManager.KEY_THEME -> {
|
||||
setIcon(AppCompatDelegate.getDefaultNightMode().toThemeIcon())
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
android:text="@{String.valueOf(song.track)}"
|
||||
android:textAlignment="center"
|
||||
android:textAppearance="@style/TextAppearance.Auxio.TitleMedium"
|
||||
android:textSize="20sp"
|
||||
android:textSize="@dimen/text_size_ext_title_mid_larger"
|
||||
android:textColor="@color/sel_accented_secondary"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
|
|
@ -22,6 +22,10 @@
|
|||
<dimen name="size_track_number">32dp</dimen>
|
||||
<dimen name="size_play_fab_icon">32dp</dimen>
|
||||
|
||||
<dimen name="text_size_ext_label_larger">16sp</dimen>
|
||||
<dimen name="text_size_ext_title_mid_large">18sp</dimen>
|
||||
<dimen name="text_size_ext_title_mid_larger">20sp</dimen>
|
||||
|
||||
<!-- Misc -->
|
||||
<dimen name="elevation_small">2dp</dimen>
|
||||
<dimen name="elevation_normal">4dp</dimen>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<style name="TextAppearance.Auxio.DisplayLarge" parent="TextAppearance.Material3.DisplayLarge">
|
||||
<item name="fontFamily">@font/inter_semibold</item>
|
||||
<item name="android:fontFamily">@font/inter_semibold</item>
|
||||
<item name="android:textStyle">normal</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.Auxio.DisplayMedium" parent="TextAppearance.Material3.DisplayMedium">
|
||||
|
@ -22,7 +22,7 @@
|
|||
<style name="TextAppearance.Auxio.HeadlineLarge" parent="TextAppearance.Material3.HeadlineLarge">
|
||||
<item name="fontFamily">@font/inter_semibold</item>
|
||||
<item name="android:fontFamily">@font/inter_semibold</item>
|
||||
<item name="android:textStyle">normal</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.Auxio.HeadlineMedium" parent="TextAppearance.Material3.HeadlineMedium">
|
||||
|
@ -40,7 +40,7 @@
|
|||
<style name="TextAppearance.Auxio.TitleLarge" parent="TextAppearance.Material3.TitleLarge">
|
||||
<item name="fontFamily">@font/inter_semibold</item>
|
||||
<item name="android:fontFamily">@font/inter_semibold</item>
|
||||
<item name="android:textStyle">normal</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.Auxio.TitleMedium" parent="TextAppearance.Material3.TitleMedium">
|
||||
|
@ -58,21 +58,21 @@
|
|||
<style name="TextAppearance.Auxio.LabelLarge" parent="TextAppearance.Material3.LabelLarge">
|
||||
<item name="fontFamily">@font/inter_semibold</item>
|
||||
<item name="android:fontFamily">@font/inter_semibold</item>
|
||||
<item name="android:textStyle">normal</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
<item name="android:letterSpacing">0.01</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.Auxio.LabelMedium" parent="TextAppearance.Material3.LabelMedium">
|
||||
<item name="fontFamily">@font/inter_semibold</item>
|
||||
<item name="android:fontFamily">@font/inter_semibold</item>
|
||||
<item name="android:textStyle">normal</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
<item name="android:letterSpacing">0.01</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.Auxio.LabelSmall" parent="TextAppearance.Material3.LabelSmall">
|
||||
<item name="fontFamily">@font/inter_semibold</item>
|
||||
<item name="android:fontFamily">@font/inter_semibold</item>
|
||||
<item name="android:textStyle">normal</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
<item name="android:letterSpacing">0.01</item>
|
||||
</style>
|
||||
|
||||
|
@ -97,19 +97,14 @@
|
|||
<item name="android:letterSpacing">0.03</item>
|
||||
</style>
|
||||
|
||||
<!--
|
||||
Text extensions
|
||||
Material3 TextAppearances are really inflexible, so these add some extra categories that
|
||||
allow for better UX.
|
||||
-->
|
||||
<style name="TextAppearance.Auxio.TitleMidLarge" parent="TextAppearance.Material3.TitleMedium">
|
||||
<item name="fontFamily">@font/inter_semibold</item>
|
||||
<item name="android:fontFamily">@font/inter_semibold</item>
|
||||
<item name="android:textStyle">normal</item>
|
||||
<item name="android:textSize">18sp</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
<item name="android:textSize">@dimen/text_size_ext_title_mid_large</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.Auxio.LabelLarger" parent="TextAppearance.Auxio.LabelLarge">
|
||||
<item name="android:textSize">16sp</item>
|
||||
<item name="android:textSize">@dimen/text_size_ext_label_larger</item>
|
||||
</style>
|
||||
</resources>
|
|
@ -9,7 +9,7 @@ buildscript {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.1.0'
|
||||
classpath 'com.android.tools.build:gradle:7.1.1'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$navigation_version"
|
||||
|
||||
|
|
|
@ -29,4 +29,5 @@ Feel free to fork Auxio to add your own feature set however.
|
|||
- Tag editing [#33] (Out of scope)
|
||||
- Gapless Playback [#35] (Technical issues)
|
||||
- Reduce leading instrument [#45] (Technical issues, Out of scope)
|
||||
- Disabling track numbers [#73] (Out of scope)
|
||||
- Opening music through a provider [#30] (Out of scope)
|
|
@ -38,8 +38,7 @@ I hope to make the app rescan music on the fly eventually.
|
|||
#### ReplayGain isn't working on my music!
|
||||
|
||||
This is for a couple reason:
|
||||
- Auxio doesn't extract ReplayGain tags for your format. This is the case with MP4 files since there's no
|
||||
defined ReplayGain standard for those.
|
||||
- Auxio doesn't extract ReplayGain tags for your format.
|
||||
- Auxio doesn't recognize your ReplayGain tags. This is usually because of a non-standard tag like ID3v2's `RVAD` or
|
||||
an unrecognized name.
|
||||
- Your tags use a ReplayGain value higher than 0. Due to technical limitations, Auxio does not support this right now.
|
||||
|
|
Loading…
Reference in a new issue