Merge pull request #669 from OxygenCobalt/dev

Version 3.3.1
This commit is contained in:
Alexander Capehart 2024-01-13 21:36:07 -07:00 committed by GitHub
commit 7b01e59519
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
49 changed files with 389 additions and 83 deletions

View file

@ -1,6 +1,19 @@
# Changelog
## dev
## 3.3.1
#### What's Improved
- The OPUS base volume adjustment field is now parsed and used as a ReplayGain adjustment
- Added ReplayGain adjustment values to Song Properties dialog
#### What's Changed
- Added donation links to the about page
#### What's Fixed
- Fixed a crash occuring if you navigated to the settings page from the playlist view
and then back
- Fixed music loading failing with an SQL error with certain music folder configurations
- Fixed issue where song title on playback screen would not scroll
## 3.3.0

View file

@ -2,8 +2,8 @@
<h1 align="center"><b>Auxio</b></h1>
<h4 align="center">A simple, rational music player for android.</h4>
<p align="center">
<a href="https://github.com/oxygencobalt/Auxio/releases/tag/v3.3.0">
<img alt="Latest Version" src="https://img.shields.io/static/v1?label=tag&message=v3.3.0&color=64B5F6&style=flat">
<a href="https://github.com/oxygencobalt/Auxio/releases/tag/v3.3.1">
<img alt="Latest Version" src="https://img.shields.io/static/v1?label=tag&message=v3.3.1&color=64B5F6&style=flat">
</a>
<a href="https://github.com/oxygencobalt/Auxio/releases/">
<img alt="Releases" src="https://img.shields.io/github/downloads/OxygenCobalt/Auxio/total.svg?color=4B95DE&style=flat">
@ -13,10 +13,10 @@
</a>
<img alt="Minimum SDK Version" src="https://img.shields.io/badge/API-24%2B-1450A8?style=flat">
</p>
<h4 align="center"><a href="/CHANGELOG.md">Changelog</a> | <a href="https://github.com/OxygenCobalt/Auxio/wiki">Wiki</a></h4>
<h4 align="center"><a href="/CHANGELOG.md">Changelog</a> | <a href="https://github.com/OxygenCobalt/Auxio/wiki">Wiki</a> | <a href="https://github.com/OxygenCobalt/Auxio#Donate">Donate</a></h4>
<p align="center">
<a href="https://f-droid.org/app/org.oxycblt.auxio"><img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" width="170"></a>
<a href="https://hosted.weblate.org/engage/auxio/"><img src="https://hosted.weblate.org/widgets/auxio/-/strings/287x66-grey.png" alt="Translation status" /></a>
<a href="https://hosted.weblate.org/engage/auxio/"><img height=64 src="https://hosted.weblate.org/widgets/auxio/-/strings/287x66-grey.png" alt="Translation status" /></a>
</p>
## About
@ -38,6 +38,7 @@ Auxio is a local music player with a fast, reliable UI/UX without the many usele
<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot7.png" width=200>
</p>
## Features
- Playback based on [Media3 ExoPlayer](https://developer.android.com/guide/topics/media/exoplayer)
@ -65,6 +66,20 @@ precise/original dates, sort tags, and more
- Storage (`READ_MEDIA_AUDIO`, `READ_EXTERNAL_STORAGE`) to read and play your music files
- Services (`FOREGROUND_SERVICE`, `WAKE_LOCK`) to keep the music playing in the background
## Donate
You can support Auxio's development through [my Github Sponsors page](https://github.com/sponsors/OxygenCobalt). Get the ability to prioritize features and have your profile added to the README, Release Changelogs, and even App Itself!
**$16/month supporters:**
*Be the first to have their profile picture and username added here!*
**$8/month supporters:**
<p align="start">
<a href="https://github.com/alanorth"><img src="https://avatars.githubusercontent.com/u/191754?v=4" width=50 /></a>
</p>
## Building
Auxio relies on a custom version of Media3 that enables some extra features. This adds some caveats to the build process:
@ -82,6 +97,8 @@ However, feature additions and major UI changes are less likely to be accepted.
[Why Are These Features Missing?](https://github.com/OxygenCobalt/Auxio/wiki/Why-Are-These-Features-Missing%3F)
for more information.
## License
[![GNU GPLv3 Image](https://www.gnu.org/graphics/gplv3-127x51.png)](http://www.gnu.org/licenses/gpl-3.0.en.html)

View file

@ -21,8 +21,8 @@ android {
defaultConfig {
applicationId namespace
versionName "3.3.0"
versionCode 37
versionName "3.3.1"
versionCode 38
minSdk 24
targetSdk 34

View file

@ -25,6 +25,7 @@ import android.view.WindowInsets
import androidx.activity.OnBackPressedCallback
import androidx.core.view.ViewCompat
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.core.view.updatePadding
import androidx.fragment.app.activityViewModels
import androidx.navigation.findNavController
@ -33,7 +34,9 @@ import com.google.android.material.R as MR
import com.google.android.material.bottomsheet.BackportBottomSheetBehavior
import com.google.android.material.shape.MaterialShapeDrawable
import com.google.android.material.transition.MaterialFadeThrough
import com.leinardi.android.speeddial.SpeedDialOverlayLayout
import dagger.hilt.android.AndroidEntryPoint
import java.lang.reflect.Field
import kotlin.math.max
import kotlin.math.min
import org.oxycblt.auxio.databinding.FragmentMainBinding
@ -56,6 +59,7 @@ import org.oxycblt.auxio.util.context
import org.oxycblt.auxio.util.coordinatorLayoutBehavior
import org.oxycblt.auxio.util.getAttrColorCompat
import org.oxycblt.auxio.util.getDimen
import org.oxycblt.auxio.util.lazyReflectedField
import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.navigateSafe
import org.oxycblt.auxio.util.systemBarInsetsCompat
@ -153,6 +157,9 @@ class MainFragment :
}
}
binding.mainScrim.setOnClickListener { homeModel.setSpeedDialOpen(false) }
binding.sheetScrim.setOnClickListener { homeModel.setSpeedDialOpen(false) }
// --- VIEWMODEL SETUP ---
// This has to be done here instead of the playback panel to make sure that it's prioritized
// by StateFlow over any detail fragment.
@ -161,7 +168,7 @@ class MainFragment :
collect(detailModel.toShow.flow, ::handleShow)
collectImmediately(detailModel.editedPlaylist, detailBackCallback::invalidateEnabled)
collectImmediately(homeModel.showOuter.flow, ::handleShowOuter)
collectImmediately(homeModel.speedDialOpen, speedDialBackCallback::invalidateEnabled)
collectImmediately(homeModel.speedDialOpen, ::handleSpeedDialState)
collectImmediately(listModel.selected, selectionBackCallback::invalidateEnabled)
collectImmediately(playbackModel.song, ::updateSong)
collectImmediately(playbackModel.openPanel.flow, ::handlePanel)
@ -336,6 +343,13 @@ class MainFragment :
homeModel.showOuter.consume()
}
private fun handleSpeedDialState(open: Boolean) {
requireNotNull(speedDialBackCallback) { "SpeedDialBackPressedCallback was not available" }
.invalidateEnabled(open)
requireBinding().mainScrim.isVisible = open
requireBinding().sheetScrim.isVisible = open
}
private fun updateSong(song: Song?) {
if (song != null) {
tryShowSheets()
@ -519,4 +533,9 @@ class MainFragment :
isEnabled = open
}
}
private companion object {
val SPEED_DIAL_OVERLAY_ANIMATION_DURATION_FIELD: Field by
lazyReflectedField(SpeedDialOverlayLayout::class, "mAnimationDuration")
}
}

View file

@ -38,6 +38,7 @@ import org.oxycblt.auxio.music.info.Name
import org.oxycblt.auxio.music.metadata.AudioProperties
import org.oxycblt.auxio.music.resolveNames
import org.oxycblt.auxio.playback.formatDurationMs
import org.oxycblt.auxio.playback.replaygain.formatDb
import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment
import org.oxycblt.auxio.util.collectImmediately
import org.oxycblt.auxio.util.concatLocalized
@ -118,6 +119,12 @@ class SongDetailDialog : ViewBindingMaterialDialogFragment<DialogSongDetailBindi
SongProperty(
R.string.lbl_sample_rate, getString(R.string.fmt_sample_rate, it)))
}
song.replayGainAdjustment.track?.let {
add(SongProperty(R.string.lbl_replaygain_track, it.formatDb(context)))
}
song.replayGainAdjustment.album?.let {
add(SongProperty(R.string.lbl_replaygain_album, it.formatDb(context)))
}
},
UpdateInstructions.Replace(0))
}

View file

@ -18,6 +18,7 @@
package org.oxycblt.auxio.home
import android.annotation.SuppressLint
import android.os.Bundle
import android.view.LayoutInflater
import android.view.MenuItem
@ -124,6 +125,7 @@ class HomeFragment :
override fun getSelectionToolbar(binding: FragmentHomeBinding) = binding.homeSelectionToolbar
@SuppressLint("ClickableViewAccessibility")
override fun onBindingCreated(binding: FragmentHomeBinding, savedInstanceState: Bundle?) {
super.onBindingCreated(binding, savedInstanceState)
@ -146,23 +148,16 @@ class HomeFragment :
// --- UI SETUP ---
// Stock bottom sheet overlay won't work with our nested UI setup, have to replicate
// it ourselves.
binding.root.rootView.apply {
// Stock bottom sheet overlay won't work with our nested UI setup, have to replicate
// it ourselves.
findViewById<View>(R.id.main_scrim).setOnClickListener {
homeModel.setSpeedDialOpen(false)
}
findViewById<View>(R.id.main_scrim).setOnTouchListener { _, event ->
handleSpeedDialBoundaryTouch(event)
}
findViewById<View>(R.id.sheet_scrim).setOnClickListener {
homeModel.setSpeedDialOpen(false)
}
findViewById<View>(R.id.sheet_scrim).setOnTouchListener { _, event ->
handleSpeedDialBoundaryTouch(event)
post {
findViewById<View>(R.id.main_scrim).setOnTouchListener { _, event ->
handleSpeedDialBoundaryTouch(event)
}
findViewById<View>(R.id.sheet_scrim).setOnTouchListener { _, event ->
handleSpeedDialBoundaryTouch(event)
}
}
}
@ -616,13 +611,6 @@ class HomeFragment :
private fun updateSpeedDial(open: Boolean) {
val binding = requireBinding()
binding.root.rootView.apply {
// Stock bottom sheet overlay won't work with our nested UI setup, have to replicate
// it ourselves.
findViewById<View>(R.id.main_scrim).isClickable = open
findViewById<View>(R.id.sheet_scrim).isClickable = open
}
if (open) {
binding.homeNewPlaylistFab.open(true)
} else {

View file

@ -143,7 +143,13 @@ class ExportPlaylistDialog : ViewBindingMaterialDialogFragment<DialogPlaylistExp
} else {
R.id.export_relative_paths
})
logD(config.windowsPaths)
if (config.absolute) {
binding.exportRelativePaths.icon = null
binding.exportAbsolutePaths.setIconResource(R.drawable.ic_check_24)
} else {
binding.exportAbsolutePaths.icon = null
binding.exportRelativePaths.setIconResource(R.drawable.ic_check_24)
}
binding.exportWindowsPaths.isChecked = config.windowsPaths
}

View file

@ -21,6 +21,7 @@ package org.oxycblt.auxio.music.fs
import android.database.Cursor
import android.os.Build
import android.provider.MediaStore
import org.oxycblt.auxio.util.logE
/**
* Wrapper around a [Cursor] that interprets path information on a per-API/manufacturer basis.
@ -111,6 +112,8 @@ private constructor(private val cursor: Cursor, volumeManager: VolumeManager) :
}
}
logE("Could not find volume for $data [tried: ${volumes.map { it.components }}]")
return null
}
@ -134,7 +137,7 @@ private constructor(private val cursor: Cursor, volumeManager: VolumeManager) :
val path = paths[i]
val volume = path.volume.components ?: continue
template +=
if (i == 0) {
if (args.isEmpty()) {
"${MediaStore.Audio.AudioColumns.DATA} LIKE ?"
} else {
" OR ${MediaStore.Audio.AudioColumns.DATA} LIKE ?"
@ -172,11 +175,16 @@ private constructor(private val cursor: Cursor, volumeManager: VolumeManager) :
override fun extract(): Path? {
// Find the StorageVolume whose MediaStore name corresponds to it.
val volumeName = cursor.getString(volumeIndex)
val volume = volumes.find { it.mediaStoreName == volumeName } ?: return null
// Relative path does not include file name, must use DISPLAY_NAME and add it
// in manually.
val relativePath = cursor.getString(relativePathIndex)
val displayName = cursor.getString(displayNameIndex)
val volume = volumes.find { it.mediaStoreName == volumeName }
if (volume == null) {
logE(
"Could not find volume for $volumeName:$relativePath/$displayName [tried: ${volumes.map { it.mediaStoreName }}]")
return null
}
val components = Components.parseUnix(relativePath).child(displayName)
return Path(volume, components)
}
@ -209,7 +217,7 @@ private constructor(private val cursor: Cursor, volumeManager: VolumeManager) :
for (i in paths.indices) {
val path = paths[i]
template =
if (i == 0) {
if (args.isEmpty()) {
"(${MediaStore.Audio.AudioColumns.VOLUME_NAME} LIKE ? " +
"AND ${MediaStore.Audio.AudioColumns.RELATIVE_PATH} LIKE ?)"
} else {

View file

@ -20,6 +20,7 @@ package org.oxycblt.auxio.music.metadata
import androidx.core.text.isDigitsOnly
import androidx.media3.common.MediaItem
import androidx.media3.common.MimeTypes
import androidx.media3.exoplayer.MetadataRetriever
import androidx.media3.exoplayer.source.MediaSource
import androidx.media3.exoplayer.source.TrackGroupArray
@ -97,6 +98,20 @@ private class TagWorkerImpl(
val textTags = TextTags(metadata)
populateWithId3v2(textTags.id3v2)
populateWithVorbis(textTags.vorbis)
// If this metadata is of a vorbis file, we actually need to extract it's base gain
// and divide it by 256 to get the gain in decibels.
if (format.sampleMimeType == MimeTypes.AUDIO_OPUS &&
format.initializationData.isNotEmpty() &&
format.initializationData[0].size >= 18) {
val header = format.initializationData[0]
val gain = header[1].toInt() or ((header[0].toInt() shl 8) and 0xFF)
logD("Obtained opus base gain: ${gain / 256f} dB")
rawSong.replayGainTrackAdjustment =
rawSong.replayGainTrackAdjustment?.plus(gain / 256f)
rawSong.replayGainAlbumAdjustment =
rawSong.replayGainAlbumAdjustment?.plus(gain / 256f)
}
} else {
logD("No metadata could be extracted for ${rawSong.name}")
}

View file

@ -104,7 +104,10 @@ class PlaybackPanelFragment :
}
binding.playbackCover.onSwipeListener = this
binding.playbackSong.setOnClickListener { navigateToCurrentSong() }
binding.playbackSong.apply {
isSelected = true
setOnClickListener { navigateToCurrentSong() }
}
binding.playbackArtist.setOnClickListener { navigateToCurrentArtist() }
binding.playbackAlbum.setOnClickListener { navigateToCurrentAlbum() }
@ -130,6 +133,7 @@ class PlaybackPanelFragment :
override fun onDestroyBinding(binding: FragmentPlaybackPanelBinding) {
equalizerLauncher = null
coverAdapter = null
binding.playbackSong.isSelected = false
binding.playbackToolbar.setOnMenuItemClickListener(null)
}

View file

@ -24,7 +24,6 @@ import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import kotlin.math.abs
import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.DialogPreAmpBinding
import org.oxycblt.auxio.playback.PlaybackSettings
@ -85,11 +84,6 @@ class PreAmpCustomizeDialog : ViewBindingMaterialDialogFragment<DialogPreAmpBind
// It is more clear to prepend a +/- before the pre-amp value to make it easier to
// gauge how much it may be increasing the volume, however android does not add +
// to positive float values when formatting them in a string. Instead, add it ourselves.
ticker.text =
if (valueDb >= 0) {
getString(R.string.fmt_db_pos, valueDb)
} else {
getString(R.string.fmt_db_neg, abs(valueDb))
}
ticker.text = valueDb.formatDb(requireContext())
}
}

View file

@ -18,7 +18,10 @@
package org.oxycblt.auxio.playback.replaygain
import android.content.Context
import kotlin.math.abs
import org.oxycblt.auxio.IntegerTable
import org.oxycblt.auxio.R
/**
* The current ReplayGain configuration.
@ -67,3 +70,16 @@ data class ReplayGainAdjustment(val track: Float?, val album: Float?)
* @author Alexander Capehart (OxygenCobalt)
*/
data class ReplayGainPreAmp(val with: Float, val without: Float)
/**
* Format a decibel value in a human-readable format.
*
* @param context The context to resolve resources from.
* @return A formatted decibel value. Will be prefixed by a + or - sign.
*/
fun Float.formatDb(context: Context) =
if (this >= 0) {
context.getString(R.string.fmt_db_pos, this)
} else {
context.getString(R.string.fmt_db_neg, abs(this))
}

View file

@ -0,0 +1,125 @@
/*
* Copyright (c) 2017 Auxio Project
* BetterShuffleOrder.kt is part of Auxio.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.oxycblt.auxio.playback.system
import androidx.media3.common.C
import androidx.media3.exoplayer.source.ShuffleOrder
import java.util.*
/**
* A ShuffleOrder that fixes the poorly defined default implementation of cloneAndInsert. Whereas
* the default implementation will randomly spread out added media items, this implementation will
* insert them in the order they are added contiguously.
*
* @author media3 team, Alexander Capehart (OxygenCobalt)
*/
class BetterShuffleOrder
private constructor(private val shuffled: IntArray, private val random: Random) : ShuffleOrder {
private val indexInShuffled: IntArray = IntArray(shuffled.size)
/**
* Creates an instance with a specified length.
*
* @param length The length of the shuffle order.
*/
constructor(length: Int) : this(length, Random())
constructor(length: Int, random: Random) : this(createShuffledList(length, random), random)
init {
for (i in shuffled.indices) {
indexInShuffled[shuffled[i]] = i
}
}
override fun getLength(): Int {
return shuffled.size
}
override fun getNextIndex(index: Int): Int {
var shuffledIndex = indexInShuffled[index]
return if (++shuffledIndex < shuffled.size) shuffled[shuffledIndex] else C.INDEX_UNSET
}
override fun getPreviousIndex(index: Int): Int {
var shuffledIndex = indexInShuffled[index]
return if (--shuffledIndex >= 0) shuffled[shuffledIndex] else C.INDEX_UNSET
}
override fun getLastIndex(): Int {
return if (shuffled.isNotEmpty()) shuffled[shuffled.size - 1] else C.INDEX_UNSET
}
override fun getFirstIndex(): Int {
return if (shuffled.isNotEmpty()) shuffled[0] else C.INDEX_UNSET
}
override fun cloneAndInsert(insertionIndex: Int, insertionCount: Int): ShuffleOrder {
val newShuffled = IntArray(shuffled.size + insertionCount)
val pivot = indexInShuffled[insertionIndex]
for (i in shuffled.indices) {
var currentIndex = shuffled[i]
if (currentIndex > insertionIndex) {
currentIndex += insertionCount
}
if (i <= pivot) {
newShuffled[i] = currentIndex
} else if (i > pivot) {
newShuffled[i + insertionCount] = currentIndex
}
}
for (i in 0 until insertionCount) {
newShuffled[pivot + i + 1] = insertionIndex + i + 1
}
return BetterShuffleOrder(newShuffled, Random(random.nextLong()))
}
override fun cloneAndRemove(indexFrom: Int, indexToExclusive: Int): ShuffleOrder {
val numberOfElementsToRemove = indexToExclusive - indexFrom
val newShuffled = IntArray(shuffled.size - numberOfElementsToRemove)
var foundElementsCount = 0
for (i in shuffled.indices) {
if (shuffled[i] in indexFrom until indexToExclusive) {
foundElementsCount++
} else {
newShuffled[i - foundElementsCount] =
if (shuffled[i] >= indexFrom) shuffled[i] - numberOfElementsToRemove
else shuffled[i]
}
}
return BetterShuffleOrder(newShuffled, Random(random.nextLong()))
}
override fun cloneAndClear(): ShuffleOrder {
return BetterShuffleOrder(0, Random(random.nextLong()))
}
companion object {
private fun createShuffledList(length: Int, random: Random): IntArray {
val shuffled = IntArray(length)
for (i in 0 until length) {
val swapIndex = random.nextInt(i + 1)
shuffled[i] = shuffled[swapIndex]
shuffled[swapIndex] = i
}
return shuffled
}
}
}

View file

@ -66,7 +66,11 @@ class AboutFragment : ViewBindingFragment<FragmentAboutBinding>() {
binding.aboutCode.setOnClickListener { requireContext().openInBrowser(LINK_SOURCE) }
binding.aboutWiki.setOnClickListener { requireContext().openInBrowser(LINK_WIKI) }
binding.aboutLicenses.setOnClickListener { requireContext().openInBrowser(LINK_LICENSES) }
binding.aboutAuthor.setOnClickListener { requireContext().openInBrowser(LINK_AUTHOR) }
binding.aboutProfile.setOnClickListener { requireContext().openInBrowser(LINK_PROFILE) }
binding.aboutDonate.setOnClickListener { requireContext().openInBrowser(LINK_DONATE) }
binding.aboutSupportersPromo.setOnClickListener {
requireContext().openInBrowser(LINK_DONATE)
}
// VIEWMODEL SETUP
collectImmediately(musicModel.statistics, ::updateStatistics)
@ -88,13 +92,10 @@ class AboutFragment : ViewBindingFragment<FragmentAboutBinding>() {
}
private companion object {
/** The URL to the source code. */
const val LINK_SOURCE = "https://github.com/OxygenCobalt/Auxio"
/** The URL to the app wiki. */
const val LINK_WIKI = "$LINK_SOURCE/wiki"
/** The URL to the licenses wiki page. */
const val LINK_LICENSES = "$LINK_WIKI/Licenses"
/** The URL to the app author. */
const val LINK_AUTHOR = "https://github.com/OxygenCobalt"
const val LINK_PROFILE = "https://github.com/OxygenCobalt"
const val LINK_DONATE = "https://github.com/sponsors/OxygenCobalt"
}
}

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M480,840L422,788Q321,697 255,631Q189,565 150,512.5Q111,460 95.5,416Q80,372 80,326Q80,232 143,169Q206,106 300,106Q352,106 399,128Q446,150 480,190Q514,150 561,128Q608,106 660,106Q754,106 817,169Q880,232 880,326Q880,372 864.5,416Q849,460 810,512.5Q771,565 705,631Q639,697 538,788L480,840ZM480,732Q576,646 638,584.5Q700,523 736,477.5Q772,432 786,396.5Q800,361 800,326Q800,266 760,226Q720,186 660,186Q613,186 573,212.5Q533,239 518,280L518,280L442,280L442,280Q427,239 387,212.5Q347,186 300,186Q240,186 200,226Q160,266 160,326Q160,361 174,396.5Q188,432 224,477.5Q260,523 322,584.5Q384,646 480,732ZM480,459Q480,459 480,459Q480,459 480,459Q480,459 480,459Q480,459 480,459Q480,459 480,459Q480,459 480,459Q480,459 480,459Q480,459 480,459L480,459L480,459L480,459Q480,459 480,459Q480,459 480,459Q480,459 480,459Q480,459 480,459Q480,459 480,459Q480,459 480,459Q480,459 480,459Q480,459 480,459Z"/>
</vector>

View file

@ -152,19 +152,92 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/about_wiki" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_medium">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/spacing_tiny">
<TextView
android:id="@+id/about_author"
style="@style/Widget.Auxio.TextView.Header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/lbl_author"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/about_profile"
style="@style/Widget.Auxio.TextView.Icon.Clickable"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/lng_author"
android:text="@string/lbl_author_name"
app:drawableStartCompat="@drawable/ic_author_24"
app:drawableTint="?attr/colorControlNormal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/about_licenses" />
</androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:id="@+id/about_donate"
style="@style/Widget.Auxio.TextView.Icon.Clickable"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/lbl_donate"
app:drawableStartCompat="@drawable/ic_donate_24"
app:drawableTint="?attr/colorControlNormal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/about_licenses" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_medium">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/spacing_tiny">
<TextView
android:id="@+id/about_supporters"
style="@style/Widget.Auxio.TextView.Header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/lbl_supporters"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/about_supporters_promo"
style="@style/Widget.Auxio.TextView.Icon.Clickable"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/lng_supporters_promo"
android:textStyle="italic"
android:textAppearance="@style/TextAppearance.Auxio.BodyMedium"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/about_licenses" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView

View file

@ -40,7 +40,7 @@
<string name="lbl_version">الإصدار</string>
<string name="lbl_code">عرض على الكود في Github</string>
<string name="lbl_licenses">التراخيص</string>
<string name="lng_author">تمت برمجة التطبيق من قبل الكساندر كابيهارت</string>
<string name="lbl_author_name">تمت برمجة التطبيق من قبل الكساندر كابيهارت</string>
<!-- Settings namespace | Settings-related labels -->
<string name="set_root_title">الإعدادات</string>
<string name="set_ui">المظهر</string>

View file

@ -10,7 +10,7 @@
<string name="set_personalize">Персаналізаваць</string>
<string name="lng_observing">Адсочванне змяненняў у вашай музычнай бібліятэцы…</string>
<string name="lng_queue_added">Дададзены ў чаргу</string>
<string name="lng_author">Распрацавана Аляксандрам Кейпхартам</string>
<string name="lbl_author_name">Распрацавана Аляксандрам Кейпхартам</string>
<string name="set_bar_action">Карыстальніцкае дзеянне панэлі прайгравання</string>
<string name="set_play_song_from_album">Прайграць з альбома</string>
<string name="set_separators_comma">Коска (,)</string>

View file

@ -49,7 +49,7 @@
<string name="lbl_version">Verze</string>
<string name="lbl_code">Zdrojový kód</string>
<string name="lbl_licenses">Licence</string>
<string name="lng_author">Vytvořil Alexander Capehart</string>
<string name="lbl_author_name">Vytvořil Alexander Capehart</string>
<string name="lbl_library_counts">Statistiky knihovny</string>
<!-- Settings namespace | Settings-related labels -->
<string name="set_root_title">Nastavení</string>

View file

@ -32,7 +32,7 @@
<string name="lbl_version">Version</string>
<string name="lbl_code">Quellcode</string>
<string name="lbl_licenses">Lizenzen</string>
<string name="lng_author">Entwickelt von Alexander Capehart</string>
<string name="lbl_author_name">Entwickelt von Alexander Capehart</string>
<!-- Settings namespace | Settings-related labels -->
<string name="set_root_title">Einstellungen</string>
<string name="set_ui">Erscheinungsbild</string>

View file

@ -40,7 +40,7 @@
<string name="lbl_version">Versión</string>
<string name="lbl_code">Código fuente</string>
<string name="lbl_licenses">Licencias</string>
<string name="lng_author">Desarrollado por Alexander Capehart</string>
<string name="lbl_author_name">Desarrollado por Alexander Capehart</string>
<!-- Settings namespace | Settings-related labels -->
<string name="set_root_title">Ajustes</string>
<string name="set_ui">Aspecto y Comportamiento</string>

View file

@ -155,7 +155,7 @@
</plurals>
<string name="lbl_observing">Tarkkaillaan musiikkikirjastoa</string>
<string name="lbl_code">Lähdekoodi</string>
<string name="lng_author">Kehittänyt Alexander Capehart</string>
<string name="lbl_author_name">Kehittänyt Alexander Capehart</string>
<string name="fmt_lib_total_duration">Kesto yhteensä: %s</string>
<string name="lbl_album_live">Live-albumi</string>
<string name="lbl_album_remix">Remix-albumi</string>

View file

@ -59,7 +59,7 @@
<string name="lbl_version">Ulat</string>
<string name="lbl_code">Tignan sa GitHub</string>
<string name="lbl_licenses">Mga Lisensya</string>
<string name="lng_author">Binuo ni OxygenCobalt</string>
<string name="lbl_author_name">Binuo ni OxygenCobalt</string>
<string name="lbl_library_counts">Istatistika ng library</string>
<string name="set_root_title">Mga Setting</string>
<string name="set_ui">Hitsura</string>

View file

@ -26,7 +26,7 @@
<string name="lbl_version">Version</string>
<string name="lbl_code">Code source</string>
<string name="lbl_licenses">Licences</string>
<string name="lng_author">Développé par Alexandre Capehart</string>
<string name="lbl_author_name">Développé par Alexandre Capehart</string>
<!-- Settings namespace | Settings-related labels -->
<string name="set_root_title">Paramètres</string>
<string name="set_ui">Apparence</string>

View file

@ -126,7 +126,7 @@
<string name="lbl_sample_rate">Frecuencia de mostraxe</string>
<string name="lbl_about">Acerca de</string>
<string name="lng_observing">Monitorizando cambios na túa biblioteca…</string>
<string name="lng_author">Desenvolvido por Alexander Capehart</string>
<string name="lbl_author_name">Desenvolvido por Alexander Capehart</string>
<string name="set_ui">Aspecto e sensación</string>
<string name="set_round_mode">Modo redondo</string>
<string name="set_round_mode_desc">Habilita as esquinas redondeadas en elementos adicionais da interface de usuario (require que as portadas dos álbums estean redondeadas)</string>

View file

@ -177,7 +177,7 @@
<string name="lbl_shuffle_shortcut_short">शफल करें</string>
<string name="lbl_state_restored">स्थिति बहाल</string>
<string name="lng_playlist_renamed">प्लेलिस्ट का नाम बदला गया</string>
<string name="lng_author">अलेक्जेंडर कैपहार्ट द्वारा विकसित</string>
<string name="lbl_author_name">अलेक्जेंडर कैपहार्ट द्वारा विकसित</string>
<string name="set_separators_desc">एकाधिक टैग मानों को निरूपित करने वाले वर्ण कॉन्फ़िगर करें</string>
<string name="set_separators_comma">अल्पविराम (,)</string>
<string name="set_separators_slash">स्लैश (/)</string>

View file

@ -56,7 +56,7 @@
<string name="lbl_version">Inačica</string>
<string name="lbl_code">Izvorni kod</string>
<string name="lbl_licenses">Licencije</string>
<string name="lng_author">Programer: Alexander Capehart</string>
<string name="lbl_author_name">Programer: Alexander Capehart</string>
<string name="lbl_library_counts">Statistika zbirke</string>
<string name="set_ui">Izgled</string>
<string name="set_theme_day">Svjetla</string>

View file

@ -170,7 +170,7 @@
<string name="lbl_save">Ment</string>
<string name="lbl_reset">Alaphelyzet</string>
<string name="lbl_state_wiped">Állapot törölve</string>
<string name="lng_author">Fejlesztő Alexander Capehart</string>
<string name="lbl_author_name">Fejlesztő Alexander Capehart</string>
<string name="set_play_song_from_all">Lejátszás az összes dalból</string>
<string name="set_play_song_from_genre">Lejátszás műfajból</string>
<string name="set_content">Tartalom</string>

View file

@ -79,7 +79,7 @@
<string name="lbl_disc">Disk</string>
<string name="lbl_song_detail">Lihat properti</string>
<string name="lbl_format">Format</string>
<string name="lng_author">Dikembangkan oleh Alexander Capehart</string>
<string name="lbl_author_name">Dikembangkan oleh Alexander Capehart</string>
<string name="lbl_library_counts">Statistik pustaka</string>
<string name="set_notif_action">Tindakan notifikasi khusus</string>
<string name="lbl_shuffle_shortcut_short">Acak</string>

View file

@ -40,7 +40,7 @@
<string name="lbl_version">Versione</string>
<string name="lbl_code">Codice sorgente</string>
<string name="lbl_licenses">Licenze</string>
<string name="lng_author">Sviluppato da Alexander Capehart</string>
<string name="lbl_author_name">Sviluppato da Alexander Capehart</string>
<string name="lbl_library_counts">Statistiche libreria</string>
<!-- Settings namespace | Settings-related labels -->
<string name="set_root_title">Opzioni</string>

View file

@ -66,7 +66,7 @@
<string name="lng_indexing">ספריית המוזיקה שלך נטענת…</string>
<string name="lng_observing">ספריית המוזיקה שלך נסרקת לאיתור שינויים…</string>
<string name="lng_queue_added">התווסף לתור</string>
<string name="lng_author">מפותח על ידי אלכסנדר קייפהארט</string>
<string name="lbl_author_name">מפותח על ידי אלכסנדר קייפהארט</string>
<string name="lng_search_library">חיפוש בספרייה שלך…</string>
<string name="set_ui">מראה ותחושה</string>
<string name="set_ui_desc">שינוי ערכת הנושא והצבעים של היישום</string>

View file

@ -118,7 +118,7 @@
<string name="lbl_album_details">アルバムに移動</string>
<string name="lbl_props">曲のプロパティ</string>
<string name="lng_queue_added">再生待ちに追加</string>
<string name="lng_author">開発者 アレクサンダー・ケイプハート (Alexander Capehart)</string>
<string name="lbl_author_name">開発者 アレクサンダー・ケイプハート (Alexander Capehart)</string>
<string name="desc_artist_image">%s のアーティスト画像</string>
<string name="lbl_compilations">オムニバス</string>
<string name="lbl_compilation">オムニバス</string>

View file

@ -47,7 +47,7 @@
<string name="lbl_version">버전</string>
<string name="lbl_code">소스 코드</string>
<string name="lbl_licenses">라이선스</string>
<string name="lng_author">Alexander Capehart가 개발</string>
<string name="lbl_author_name">Alexander Capehart가 개발</string>
<string name="lbl_library_counts">라이브러리 통계</string>
<!-- Settings namespace | Settings-related labels -->
<string name="set_root_title">설정</string>

View file

@ -122,7 +122,7 @@
<string name="lbl_live_group">Gyvai</string>
<string name="set_headset_autoplay_desc">Visada pradėti groti, kai ausinės yra prijungtos (gali neveikti visuose įrenginiuose)</string>
<string name="cdc_ogg">Ogg garsas</string>
<string name="lng_author">Sukūrė Alexanderis Capehartas (angl. Alexander Capehart)</string>
<string name="lbl_author_name">Sukūrė Alexanderis Capehartas (angl. Alexander Capehart)</string>
<string name="set_replay_gain_mode_track">Pageidauti takelį</string>
<string name="err_no_dirs">Jokių aplankų</string>
<string name="err_bad_dir">Šis aplankas nepalaikomas</string>

View file

@ -202,7 +202,7 @@
<string name="lbl_track">Spor</string>
<string name="lbl_date_added">Dato tillagt</string>
<string name="lbl_sort">Sorter</string>
<string name="lng_author">Utviklet av Alexander Capehart</string>
<string name="lbl_author_name">Utviklet av Alexander Capehart</string>
<string name="lng_search_library">Søk i biblioteket ditt …</string>
<string name="set_root_title">Innstillinger</string>
<string name="lng_playlist_created">Spilleliste opprettet</string>

View file

@ -35,7 +35,7 @@
<string name="lbl_version">Versie</string>
<string name="lbl_code">Broncode</string>
<string name="lbl_licenses">Licenties</string>
<string name="lng_author">Ontwikkeld door Alexander Capehart</string>
<string name="lbl_author_name">Ontwikkeld door Alexander Capehart</string>
<!-- Settings namespace | Settings-related labels -->
<string name="set_root_title">Instellingen</string>
<string name="set_ui">Uiterlijk en gevoel</string>

View file

@ -84,7 +84,7 @@
<string name="lng_widget">ਸੰਗੀਤ ਪਲੇਬੈਕ ਵੇਖੋ ਅਤੇ ਕੰਟਰੋਲ ਕਰੋ</string>
<string name="lng_indexing">ਤੁਹਾਡੀ ਸੰਗੀਤ ਲਾਇਬ੍ਰੇਰੀ ਨੂੰ ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…</string>
<string name="lng_queue_added">ਕਤਾਰ ਵਿੱਚ ਸ਼ਾਮਿਲ ਕੀਤਾ</string>
<string name="lng_author">ਅਲੈਗਜ਼ੈਂਡਰ ਕੇਪਹਾਰਟ ਦੁਆਰਾ ਵਿਕਸਿਤ</string>
<string name="lbl_author_name">ਅਲੈਗਜ਼ੈਂਡਰ ਕੇਪਹਾਰਟ ਦੁਆਰਾ ਵਿਕਸਿਤ</string>
<string name="lng_search_library">ਆਪਣੀ ਲਾਇਬ੍ਰੇਰੀ ਖੋਜੋ…</string>
<string name="set_root_title">ਸੈਟਿੰਗਾਂ</string>
<string name="set_ui">ਦਿੱਖ ਅਤੇ ਛੋਹ</string>

View file

@ -147,7 +147,7 @@
<string name="lbl_mix">DJ Mix</string>
<string name="desc_skip_prev">Przejdź do ostatniego utworu</string>
<string name="desc_skip_next">Przejdź do następnego utworu</string>
<string name="lng_author">Autorstwa Alexandra Capeharta</string>
<string name="lbl_author_name">Autorstwa Alexandra Capeharta</string>
<string name="set_round_mode">Zaokrąglone krawędzie</string>
<string name="set_round_mode_desc">Włącz zaokrąglone rogi na dodatkowych elementach interfejsu (wymaga zaokrąglenia okładek albumów)</string>
<string name="set_bar_action">Akcja na pasku odtwarzania</string>

View file

@ -25,7 +25,7 @@
<string name="lbl_version">Versão</string>
<string name="lbl_code">Código-fonte</string>
<string name="lbl_licenses">Licenças</string>
<string name="lng_author">Desenvolvido por Alexander Capehart</string>
<string name="lbl_author_name">Desenvolvido por Alexander Capehart</string>
<!-- Settings namespace | Settings-related labels -->
<string name="set_root_title">Configurações</string>
<string name="set_ui">Visualização e Aparência</string>

View file

@ -26,7 +26,7 @@
<string name="lbl_version">Versão</string>
<string name="lbl_code">Código fonte</string>
<string name="lbl_licenses">Licenças</string>
<string name="lng_author">Desenvolvido por Alexander Capehart</string>
<string name="lbl_author_name">Desenvolvido por Alexander Capehart</string>
<!-- Settings namespace | Settings-related labels -->
<string name="set_root_title">Definições</string>
<string name="set_ui">Aparência</string>

View file

@ -26,7 +26,7 @@
<string name="lbl_version">Versiune</string>
<string name="lbl_code">Cod sursă</string>
<string name="lbl_licenses">Licențe</string>
<string name="lng_author">Dezvoltat de Alexander Capehart</string>
<string name="lbl_author_name">Dezvoltat de Alexander Capehart</string>
<!-- Settings namespace | Settings-related labels -->
<string name="set_root_title">Setări</string>
<string name="set_ui">Aspect și caracteristici</string>

View file

@ -40,7 +40,7 @@
<string name="lbl_version">Версия</string>
<string name="lbl_code">Исходный код</string>
<string name="lbl_licenses">Лицензии</string>
<string name="lng_author">Разработано Александром Кейпхартом</string>
<string name="lbl_author_name">Разработано Александром Кейпхартом</string>
<!-- Settings namespace | Settings-related labels -->
<string name="set_root_title">Настройки</string>
<string name="set_ui">Внешний вид</string>

View file

@ -128,7 +128,7 @@
<string name="set_music">Glasba</string>
<string name="err_bad_dir">Ta mapa ni podprta</string>
<string name="set_restore_desc">Obnovi prej shranjeno stanje predvajanja (če obstaja)</string>
<string name="lng_author">Razvil Alexander Capehart</string>
<string name="lbl_author_name">Razvil Alexander Capehart</string>
<string name="desc_music_dir_delete">Odstrani mapo</string>
<string name="lbl_copied">Kopirano</string>
<string name="err_index_failed">Nalaganje glasbe ni uspelo</string>

View file

@ -107,7 +107,7 @@
<string name="lbl_library_counts">Statistik över beroende</string>
<string name="lng_playlist_renamed">Byt namn av spellista</string>
<string name="lng_playlist_deleted">Spellista tog bort</string>
<string name="lng_author">Utvecklad av Alexander Capeheart</string>
<string name="lbl_author_name">Utvecklad av Alexander Capeheart</string>
<string name="set_theme">Tema</string>
<string name="set_theme_night">Mörkt</string>
<string name="set_accent">Färgschema</string>

View file

@ -78,7 +78,7 @@
<string name="lbl_date">Yıl</string>
<string name="lbl_duration">Süre</string>
<string name="lbl_state_saved">Durum kaydedildi</string>
<string name="lng_author">Alexander Capehart tarafından geliştirildi</string>
<string name="lbl_author_name">Alexander Capehart tarafından geliştirildi</string>
<string name="set_black_mode">Siyah tema</string>
<string name="lbl_library_counts">Kitaplık istatistikleri</string>
<string name="set_black_mode_desc">Kapkara koyu tema kullan</string>

View file

@ -130,7 +130,7 @@
<string name="lbl_ep_live">Концертний мініальбом</string>
<string name="lbl_library_counts">Статистика бібліотеки</string>
<string name="lng_indexing">Завантаження музичної бібліотеки…</string>
<string name="lng_author">Розроблено Олександром Кейпхартом</string>
<string name="lbl_author_name">Розроблено Олександром Кейпхартом</string>
<string name="fmt_lib_artist_count">Завантажено виконавців: %d</string>
<string name="set_theme_auto">Автоматично</string>
<string name="set_accent">Кольоровий акцент</string>

View file

@ -40,7 +40,7 @@
<string name="lbl_version">版本</string>
<string name="lbl_code">源代码</string>
<string name="lbl_licenses">许可证</string>
<string name="lng_author">由 Alexander Capehart 开发</string>
<string name="lbl_author_name">由 Alexander Capehart 开发</string>
<!-- Settings namespace | Settings-related labels -->
<string name="set_root_title">设置</string>
<string name="set_ui">外观和感觉</string>

View file

@ -146,6 +146,8 @@
<string name="lbl_size">Size</string>
<string name="lbl_bitrate">Bit rate</string>
<string name="lbl_sample_rate">Sample rate</string>
<string name="lbl_replaygain_track">ReplayGain Track Adjustment</string>
<string name="lbl_replaygain_album">ReplayGain Album Adjustment</string>
<!-- Limit to 10 characters -->
<string name="lbl_shuffle_shortcut_short">Shuffle</string>
@ -189,6 +191,11 @@
<!-- As in to report an error -->
<string name="lbl_report">Report</string>
<string name="lbl_author">Author</string>
<string name="lbl_author_name">Alexander Capehart</string>
<string name="lbl_donate">Donate</string>
<string name="lbl_supporters">Supporters</string>
<!-- Long Namespace | Longer Descriptions -->
<eat-comment />
@ -202,7 +209,7 @@
<string name="lng_playlist_exported">Playlist exported</string>
<string name="lng_playlist_deleted">Playlist deleted</string>
<string name="lng_playlist_added">Added to playlist</string>
<string name="lng_author">Developed by Alexander Capehart</string>
<string name="lng_supporters_promo">Donate to the project to get your name added here!</string>
<!-- As in music library -->
<string name="lng_search_library">Search your library…</string>

View file

@ -0,0 +1,3 @@
Auxio 3.3.1 adds the ability to import and export playlists, skip gestures, and fixes/improvements to the music loader.
This release fixes a critical bug with the music loader, among other issues.
For more information, see https://github.com/OxygenCobalt/Auxio/releases/tag/v3.3.1