all: tie up loose ends
Tie up some loose ends before 3.0.0's release.
This commit is contained in:
parent
94e1b71d9b
commit
32db8a591a
8 changed files with 43 additions and 39 deletions
|
@ -45,6 +45,7 @@ audio focus was lost
|
||||||
- "Show covers" and "Ignore MediaStore covers" have been unified into "Album covers"
|
- "Show covers" and "Ignore MediaStore covers" have been unified into "Album covers"
|
||||||
|
|
||||||
#### Dev/Meta
|
#### Dev/Meta
|
||||||
|
- Created new wiki with more information about app functionality
|
||||||
- Completed migration to reactive playback system
|
- Completed migration to reactive playback system
|
||||||
- Refactor music backends into a unified chain of extractors
|
- Refactor music backends into a unified chain of extractors
|
||||||
- Add bluetooth connection reciever (No functionality in app yet)
|
- Add bluetooth connection reciever (No functionality in app yet)
|
||||||
|
|
|
@ -345,7 +345,7 @@ class Song constructor(raw: Raw, settings: Settings) : Music() {
|
||||||
val mimeType =
|
val mimeType =
|
||||||
MimeType(
|
MimeType(
|
||||||
fromExtension = requireNotNull(raw.extensionMimeType) { "Invalid raw: No mime type" },
|
fromExtension = requireNotNull(raw.extensionMimeType) { "Invalid raw: No mime type" },
|
||||||
fromFormat = raw.formatMimeType)
|
fromFormat = null)
|
||||||
|
|
||||||
/** The size of the audio file, in bytes. */
|
/** The size of the audio file, in bytes. */
|
||||||
val size = requireNotNull(raw.size) { "Invalid raw: No size" }
|
val size = requireNotNull(raw.size) { "Invalid raw: No size" }
|
||||||
|
@ -547,8 +547,6 @@ class Song constructor(raw: Raw, settings: Settings) : Music() {
|
||||||
var durationMs: Long? = null,
|
var durationMs: Long? = null,
|
||||||
/** @see Song.mimeType */
|
/** @see Song.mimeType */
|
||||||
var extensionMimeType: String? = null,
|
var extensionMimeType: String? = null,
|
||||||
/** @see Song.mimeType */
|
|
||||||
var formatMimeType: String? = null,
|
|
||||||
/** @see Music.UID */
|
/** @see Music.UID */
|
||||||
var musicBrainzId: String? = null,
|
var musicBrainzId: String? = null,
|
||||||
/** @see Music.rawName */
|
/** @see Music.rawName */
|
||||||
|
|
|
@ -132,7 +132,6 @@ class ReadWriteCacheExtractor(private val context: Context) : WriteOnlyCacheExtr
|
||||||
|
|
||||||
rawSong.size = cachedRawSong.size
|
rawSong.size = cachedRawSong.size
|
||||||
rawSong.durationMs = cachedRawSong.durationMs
|
rawSong.durationMs = cachedRawSong.durationMs
|
||||||
rawSong.formatMimeType = cachedRawSong.formatMimeType
|
|
||||||
|
|
||||||
rawSong.track = cachedRawSong.track
|
rawSong.track = cachedRawSong.track
|
||||||
rawSong.disc = cachedRawSong.disc
|
rawSong.disc = cachedRawSong.disc
|
||||||
|
@ -180,7 +179,6 @@ private class CacheDatabase(context: Context) :
|
||||||
append("${Columns.DATE_MODIFIED} LONG NOT NULL,")
|
append("${Columns.DATE_MODIFIED} LONG NOT NULL,")
|
||||||
append("${Columns.SIZE} LONG NOT NULL,")
|
append("${Columns.SIZE} LONG NOT NULL,")
|
||||||
append("${Columns.DURATION} LONG NOT NULL,")
|
append("${Columns.DURATION} LONG NOT NULL,")
|
||||||
append("${Columns.FORMAT_MIME_TYPE} STRING,")
|
|
||||||
append("${Columns.MUSIC_BRAINZ_ID} STRING,")
|
append("${Columns.MUSIC_BRAINZ_ID} STRING,")
|
||||||
append("${Columns.NAME} STRING NOT NULL,")
|
append("${Columns.NAME} STRING NOT NULL,")
|
||||||
append("${Columns.SORT_NAME} STRING,")
|
append("${Columns.SORT_NAME} STRING,")
|
||||||
|
@ -236,7 +234,6 @@ private class CacheDatabase(context: Context) :
|
||||||
|
|
||||||
val sizeIndex = cursor.getColumnIndexOrThrow(Columns.SIZE)
|
val sizeIndex = cursor.getColumnIndexOrThrow(Columns.SIZE)
|
||||||
val durationIndex = cursor.getColumnIndexOrThrow(Columns.DURATION)
|
val durationIndex = cursor.getColumnIndexOrThrow(Columns.DURATION)
|
||||||
val formatMimeTypeIndex = cursor.getColumnIndexOrThrow(Columns.FORMAT_MIME_TYPE)
|
|
||||||
|
|
||||||
val musicBrainzIdIndex = cursor.getColumnIndexOrThrow(Columns.MUSIC_BRAINZ_ID)
|
val musicBrainzIdIndex = cursor.getColumnIndexOrThrow(Columns.MUSIC_BRAINZ_ID)
|
||||||
val nameIndex = cursor.getColumnIndexOrThrow(Columns.NAME)
|
val nameIndex = cursor.getColumnIndexOrThrow(Columns.NAME)
|
||||||
|
@ -275,7 +272,6 @@ private class CacheDatabase(context: Context) :
|
||||||
|
|
||||||
raw.size = cursor.getLong(sizeIndex)
|
raw.size = cursor.getLong(sizeIndex)
|
||||||
raw.durationMs = cursor.getLong(durationIndex)
|
raw.durationMs = cursor.getLong(durationIndex)
|
||||||
raw.formatMimeType = cursor.getStringOrNull(formatMimeTypeIndex)
|
|
||||||
|
|
||||||
raw.musicBrainzId = cursor.getStringOrNull(musicBrainzIdIndex)
|
raw.musicBrainzId = cursor.getStringOrNull(musicBrainzIdIndex)
|
||||||
raw.name = cursor.getString(nameIndex)
|
raw.name = cursor.getString(nameIndex)
|
||||||
|
@ -341,7 +337,6 @@ private class CacheDatabase(context: Context) :
|
||||||
|
|
||||||
put(Columns.SIZE, rawSong.size)
|
put(Columns.SIZE, rawSong.size)
|
||||||
put(Columns.DURATION, rawSong.durationMs)
|
put(Columns.DURATION, rawSong.durationMs)
|
||||||
put(Columns.FORMAT_MIME_TYPE, rawSong.formatMimeType)
|
|
||||||
|
|
||||||
put(Columns.MUSIC_BRAINZ_ID, rawSong.name)
|
put(Columns.MUSIC_BRAINZ_ID, rawSong.name)
|
||||||
put(Columns.NAME, rawSong.name)
|
put(Columns.NAME, rawSong.name)
|
||||||
|
@ -407,8 +402,6 @@ private class CacheDatabase(context: Context) :
|
||||||
const val SIZE = "size"
|
const val SIZE = "size"
|
||||||
/** @see Song.Raw.durationMs */
|
/** @see Song.Raw.durationMs */
|
||||||
const val DURATION = "duration"
|
const val DURATION = "duration"
|
||||||
/** @see Song.Raw.formatMimeType */
|
|
||||||
const val FORMAT_MIME_TYPE = "fmt_mime"
|
|
||||||
/** @see Song.Raw.musicBrainzId */
|
/** @see Song.Raw.musicBrainzId */
|
||||||
const val MUSIC_BRAINZ_ID = "mbid"
|
const val MUSIC_BRAINZ_ID = "mbid"
|
||||||
/** @see Song.Raw.name */
|
/** @see Song.Raw.name */
|
||||||
|
|
|
@ -160,10 +160,6 @@ class Task(context: Context, private val raw: Song.Raw) {
|
||||||
return raw
|
return raw
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populate the format mime type if we have one.
|
|
||||||
// TODO: Check if this is even useful or not.
|
|
||||||
format.sampleMimeType?.let { raw.formatMimeType = it }
|
|
||||||
|
|
||||||
val metadata = format.metadata
|
val metadata = format.metadata
|
||||||
if (metadata != null) {
|
if (metadata != null) {
|
||||||
populateWithMetadata(metadata)
|
populateWithMetadata(metadata)
|
||||||
|
|
|
@ -22,6 +22,7 @@ import android.view.LayoutInflater
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.core.view.children
|
import androidx.core.view.children
|
||||||
import com.google.android.material.checkbox.MaterialCheckBox
|
import com.google.android.material.checkbox.MaterialCheckBox
|
||||||
|
import org.oxycblt.auxio.BuildConfig
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.databinding.DialogSeparatorsBinding
|
import org.oxycblt.auxio.databinding.DialogSeparatorsBinding
|
||||||
import org.oxycblt.auxio.settings.Settings
|
import org.oxycblt.auxio.settings.Settings
|
||||||
|
@ -34,7 +35,6 @@ import org.oxycblt.auxio.util.context
|
||||||
* @author Alexander Capehart (OxygenCobalt)
|
* @author Alexander Capehart (OxygenCobalt)
|
||||||
*/
|
*/
|
||||||
class SeparatorsDialog : ViewBindingDialogFragment<DialogSeparatorsBinding>() {
|
class SeparatorsDialog : ViewBindingDialogFragment<DialogSeparatorsBinding>() {
|
||||||
// TODO: Add saved state for pending configurations.
|
|
||||||
private val settings: Settings by lifecycleObject { binding -> Settings(binding.context) }
|
private val settings: Settings by lifecycleObject { binding -> Settings(binding.context) }
|
||||||
|
|
||||||
override fun onCreateBinding(inflater: LayoutInflater) =
|
override fun onCreateBinding(inflater: LayoutInflater) =
|
||||||
|
@ -45,17 +45,7 @@ class SeparatorsDialog : ViewBindingDialogFragment<DialogSeparatorsBinding>() {
|
||||||
.setTitle(R.string.set_separators)
|
.setTitle(R.string.set_separators)
|
||||||
.setNegativeButton(R.string.lbl_cancel, null)
|
.setNegativeButton(R.string.lbl_cancel, null)
|
||||||
.setPositiveButton(R.string.lbl_save) { _, _ ->
|
.setPositiveButton(R.string.lbl_save) { _, _ ->
|
||||||
// Create the separator list based on the checked configuration of each
|
settings.musicSeparators = getCurrentSeparators()
|
||||||
// view element. It's generally more stable to duplicate this code instead
|
|
||||||
// of use a mapping that could feasibly drift from the actual layout.
|
|
||||||
var separators = ""
|
|
||||||
val binding = requireBinding()
|
|
||||||
if (binding.separatorComma.isChecked) separators += SEPARATOR_COMMA
|
|
||||||
if (binding.separatorSemicolon.isChecked) separators += SEPARATOR_SEMICOLON
|
|
||||||
if (binding.separatorSlash.isChecked) separators += SEPARATOR_SLASH
|
|
||||||
if (binding.separatorPlus.isChecked) separators += SEPARATOR_PLUS
|
|
||||||
if (binding.separatorAnd.isChecked) separators += SEPARATOR_AND
|
|
||||||
settings.musicSeparators = separators
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,7 +61,7 @@ class SeparatorsDialog : ViewBindingDialogFragment<DialogSeparatorsBinding>() {
|
||||||
// More efficient to do one iteration through the separator list and initialize
|
// More efficient to do one iteration through the separator list and initialize
|
||||||
// the corresponding CheckBox for each character instead of doing an iteration
|
// the corresponding CheckBox for each character instead of doing an iteration
|
||||||
// through the separator list for each CheckBox.
|
// through the separator list for each CheckBox.
|
||||||
settings.musicSeparators?.forEach {
|
(savedInstanceState?.getString(KEY_PENDING_SEPARATORS) ?: settings.musicSeparators)?.forEach {
|
||||||
when (it) {
|
when (it) {
|
||||||
SEPARATOR_COMMA -> binding.separatorComma.isChecked = true
|
SEPARATOR_COMMA -> binding.separatorComma.isChecked = true
|
||||||
SEPARATOR_SEMICOLON -> binding.separatorSemicolon.isChecked = true
|
SEPARATOR_SEMICOLON -> binding.separatorSemicolon.isChecked = true
|
||||||
|
@ -83,7 +73,28 @@ class SeparatorsDialog : ViewBindingDialogFragment<DialogSeparatorsBinding>() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
|
super.onSaveInstanceState(outState)
|
||||||
|
outState.putString(KEY_PENDING_SEPARATORS, getCurrentSeparators())
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get the current separator string configuration from the UI. */
|
||||||
|
private fun getCurrentSeparators(): String {
|
||||||
|
// Create the separator list based on the checked configuration of each
|
||||||
|
// view element. It's generally more stable to duplicate this code instead
|
||||||
|
// of use a mapping that could feasibly drift from the actual layout.
|
||||||
|
var separators = ""
|
||||||
|
val binding = requireBinding()
|
||||||
|
if (binding.separatorComma.isChecked) separators += SEPARATOR_COMMA
|
||||||
|
if (binding.separatorSemicolon.isChecked) separators += SEPARATOR_SEMICOLON
|
||||||
|
if (binding.separatorSlash.isChecked) separators += SEPARATOR_SLASH
|
||||||
|
if (binding.separatorPlus.isChecked) separators += SEPARATOR_PLUS
|
||||||
|
if (binding.separatorAnd.isChecked) separators += SEPARATOR_AND
|
||||||
|
return separators
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
private val KEY_PENDING_SEPARATORS = BuildConfig.APPLICATION_ID + ".key.PENDING_SEPARATORS"
|
||||||
// TODO: Move these to a more "Correct" location?
|
// TODO: Move these to a more "Correct" location?
|
||||||
private const val SEPARATOR_COMMA = ','
|
private const val SEPARATOR_COMMA = ','
|
||||||
private const val SEPARATOR_SEMICOLON = ';'
|
private const val SEPARATOR_SEMICOLON = ';'
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
package org.oxycblt.auxio.music.storage
|
package org.oxycblt.auxio.music.storage
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.media.MediaExtractor
|
||||||
|
import android.media.MediaFormat
|
||||||
import android.os.storage.StorageManager
|
import android.os.storage.StorageManager
|
||||||
import android.os.storage.StorageVolume
|
import android.os.storage.StorageVolume
|
||||||
import android.webkit.MimeTypeMap
|
import android.webkit.MimeTypeMap
|
||||||
|
@ -153,17 +155,12 @@ data class MimeType(val fromExtension: String, val fromFormat: String?) {
|
||||||
// We start with the extracted mime types, as they are more consistent. Note that
|
// We start with the extracted mime types, as they are more consistent. Note that
|
||||||
// we do not include container formats at all with these names. It is only the
|
// we do not include container formats at all with these names. It is only the
|
||||||
// inner codec that we bother with.
|
// inner codec that we bother with.
|
||||||
MimeTypes.AUDIO_MPEG,
|
MediaFormat.MIMETYPE_AUDIO_MPEG -> R.string.cdc_mp3
|
||||||
MimeTypes.AUDIO_MPEG_L1,
|
MediaFormat.MIMETYPE_AUDIO_AAC -> R.string.cdc_aac
|
||||||
MimeTypes.AUDIO_MPEG_L2 -> R.string.cdc_mp3
|
MediaFormat.MIMETYPE_AUDIO_VORBIS -> R.string.cdc_vorbis
|
||||||
MimeTypes.AUDIO_AAC -> R.string.cdc_aac
|
MediaFormat.MIMETYPE_AUDIO_OPUS -> R.string.cdc_opus
|
||||||
MimeTypes.AUDIO_VORBIS -> R.string.cdc_vorbis
|
MediaFormat.MIMETYPE_AUDIO_FLAC -> R.string.cdc_flac
|
||||||
MimeTypes.AUDIO_OPUS -> R.string.cdc_opus
|
|
||||||
MimeTypes.AUDIO_FLAC -> R.string.cdc_flac
|
|
||||||
MimeTypes.AUDIO_WAV -> R.string.cdc_wav
|
|
||||||
|
|
||||||
// We don't give a name to more unpopular formats.
|
// We don't give a name to more unpopular formats.
|
||||||
|
|
||||||
else -> -1
|
else -> -1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -276,9 +276,10 @@ class ReplayGainAudioProcessor(context: Context) : BaseAudioProcessor() {
|
||||||
/**
|
/**
|
||||||
* A raw ReplayGain adjustment.
|
* A raw ReplayGain adjustment.
|
||||||
* @param key The tag's key.
|
* @param key The tag's key.
|
||||||
* @param value The tag's adjustment, in dB. TODO: Try to phasse this out.
|
* @param value The tag's adjustment, in dB.
|
||||||
*/
|
*/
|
||||||
private data class GainTag(val key: String, val value: Float)
|
private data class GainTag(val key: String, val value: Float)
|
||||||
|
// TODO: Try to phase this out
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG_RG_TRACK = "replaygain_track_gain"
|
private const val TAG_RG_TRACK = "replaygain_track_gain"
|
||||||
|
|
|
@ -165,8 +165,15 @@ class Settings(private val context: Context, private val callback: Callback? = n
|
||||||
unlikelyToBeNull(callback).onSettingChanged(key)
|
unlikelyToBeNull(callback).onSettingChanged(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** TODO: Rework this */
|
/**
|
||||||
|
* Simplified callback for settings changes.
|
||||||
|
*/
|
||||||
interface Callback {
|
interface Callback {
|
||||||
|
// TODO: Refactor this lifecycle
|
||||||
|
/**
|
||||||
|
* Called when a setting has changed.
|
||||||
|
* @param key The key of the setting that changed.
|
||||||
|
*/
|
||||||
fun onSettingChanged(key: String)
|
fun onSettingChanged(key: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue