all: tweak package structure
Try to tweak the package structure to be more coherent.
This commit is contained in:
parent
3e73cd8080
commit
f3c14b81cb
42 changed files with 190 additions and 201 deletions
|
@ -96,12 +96,6 @@ dependencies {
|
|||
// Preferences
|
||||
implementation "androidx.preference:preference-ktx:1.2.0"
|
||||
|
||||
// Room
|
||||
def room_version = "2.4.3"
|
||||
implementation "androidx.room:room-runtime:$room_version"
|
||||
kapt "androidx.room:room-compiler:$room_version"
|
||||
implementation "androidx.room:room-ktx:$room_version"
|
||||
|
||||
// --- THIRD PARTY ---
|
||||
|
||||
// Exoplayer
|
||||
|
|
2
app/proguard-rules.pro
vendored
2
app/proguard-rules.pro
vendored
|
@ -21,7 +21,7 @@
|
|||
#-renamesourcefileattribute SourceFile
|
||||
|
||||
-keep class org.oxycblt.auxio.AuxioApp
|
||||
-keep class org.oxycblt.auxio.settings.SettingsListFragment
|
||||
-keep class org.oxycblt.auxio.settings.prefs.SettingsListFragment
|
||||
|
||||
# Free software does not obsfucate. Also it's easier to debug stack traces.
|
||||
-dontobfuscate
|
|
@ -36,7 +36,7 @@ import org.oxycblt.auxio.music.Music
|
|||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.playback.queue.QueueSheetBehavior
|
||||
import org.oxycblt.auxio.playback.ui.PlaybackSheetBehavior
|
||||
import org.oxycblt.auxio.playback.PlaybackSheetBehavior
|
||||
import org.oxycblt.auxio.ui.MainNavigationAction
|
||||
import org.oxycblt.auxio.ui.NavigationViewModel
|
||||
import org.oxycblt.auxio.ui.fragment.ViewBindingFragment
|
||||
|
|
|
@ -33,12 +33,12 @@ import org.oxycblt.auxio.R
|
|||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.MimeType
|
||||
import org.oxycblt.auxio.music.Music
|
||||
import org.oxycblt.auxio.music.MusicStore
|
||||
import org.oxycblt.auxio.music.ReleaseType
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.Sort
|
||||
import org.oxycblt.auxio.music.storage.MimeType
|
||||
import org.oxycblt.auxio.settings.Settings
|
||||
import org.oxycblt.auxio.ui.recycler.Header
|
||||
import org.oxycblt.auxio.ui.recycler.Item
|
||||
|
|
|
@ -26,7 +26,7 @@ import androidx.navigation.fragment.findNavController
|
|||
import androidx.navigation.fragment.navArgs
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.DialogSongDetailBinding
|
||||
import org.oxycblt.auxio.music.formatDurationMs
|
||||
import org.oxycblt.auxio.playback.formatDurationMs
|
||||
import org.oxycblt.auxio.ui.fragment.ViewBindingDialogFragment
|
||||
import org.oxycblt.auxio.util.androidActivityViewModels
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
|
|
|
@ -29,7 +29,7 @@ import org.oxycblt.auxio.databinding.ItemDiscHeaderBinding
|
|||
import org.oxycblt.auxio.detail.DiscHeader
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.formatDurationMs
|
||||
import org.oxycblt.auxio.playback.formatDurationMs
|
||||
import org.oxycblt.auxio.ui.recycler.IndicatorAdapter
|
||||
import org.oxycblt.auxio.ui.recycler.Item
|
||||
import org.oxycblt.auxio.ui.recycler.MenuItemListener
|
||||
|
@ -120,7 +120,7 @@ private class AlbumDetailViewHolder private constructor(private val binding: Ite
|
|||
|
||||
binding.detailInfo.apply {
|
||||
val date =
|
||||
item.date?.let { context.getString(R.string.fmt_number, it.year) }
|
||||
item.date?.resolveYear(context)
|
||||
?: context.getString(R.string.def_date)
|
||||
|
||||
val songCount = context.getPlural(R.plurals.fmt_song_count, item.songs.size)
|
||||
|
|
|
@ -29,7 +29,6 @@ import org.oxycblt.auxio.databinding.ItemSongBinding
|
|||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.resolveYear
|
||||
import org.oxycblt.auxio.ui.recycler.IndicatorAdapter
|
||||
import org.oxycblt.auxio.ui.recycler.Item
|
||||
import org.oxycblt.auxio.ui.recycler.MenuItemListener
|
||||
|
@ -158,7 +157,10 @@ private class ArtistAlbumViewHolder private constructor(private val binding: Ite
|
|||
fun bind(item: Album, listener: MenuItemListener) {
|
||||
binding.parentImage.bind(item)
|
||||
binding.parentName.text = item.resolveName(binding.context)
|
||||
binding.parentInfo.text = item.date.resolveYear(binding.context)
|
||||
binding.parentInfo.text =
|
||||
item.date?.resolveYear(binding.context)
|
||||
?: binding.context.getString(R.string.def_date)
|
||||
|
||||
// binding.parentMenu.setOnClickListener { listener.onOpenMenu(item, it) }
|
||||
binding.root.setOnLongClickListener {
|
||||
listener.onOpenMenu(item, it)
|
||||
|
|
|
@ -26,7 +26,7 @@ import org.oxycblt.auxio.R
|
|||
import org.oxycblt.auxio.databinding.ItemDetailBinding
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.formatDurationMs
|
||||
import org.oxycblt.auxio.playback.formatDurationMs
|
||||
import org.oxycblt.auxio.ui.recycler.Item
|
||||
import org.oxycblt.auxio.ui.recycler.SimpleItemCallback
|
||||
import org.oxycblt.auxio.ui.recycler.SongViewHolder
|
||||
|
|
|
@ -27,8 +27,8 @@ import org.oxycblt.auxio.music.Album
|
|||
import org.oxycblt.auxio.music.MusicMode
|
||||
import org.oxycblt.auxio.music.MusicParent
|
||||
import org.oxycblt.auxio.music.Sort
|
||||
import org.oxycblt.auxio.music.formatDurationMs
|
||||
import org.oxycblt.auxio.music.secsToMs
|
||||
import org.oxycblt.auxio.playback.formatDurationMs
|
||||
import org.oxycblt.auxio.playback.secsToMs
|
||||
import org.oxycblt.auxio.ui.recycler.AlbumViewHolder
|
||||
import org.oxycblt.auxio.ui.recycler.IndicatorAdapter
|
||||
import org.oxycblt.auxio.ui.recycler.Item
|
||||
|
|
|
@ -26,7 +26,7 @@ import org.oxycblt.auxio.music.Artist
|
|||
import org.oxycblt.auxio.music.MusicMode
|
||||
import org.oxycblt.auxio.music.MusicParent
|
||||
import org.oxycblt.auxio.music.Sort
|
||||
import org.oxycblt.auxio.music.formatDurationMs
|
||||
import org.oxycblt.auxio.playback.formatDurationMs
|
||||
import org.oxycblt.auxio.ui.recycler.ArtistViewHolder
|
||||
import org.oxycblt.auxio.ui.recycler.IndicatorAdapter
|
||||
import org.oxycblt.auxio.ui.recycler.Item
|
||||
|
|
|
@ -26,7 +26,7 @@ import org.oxycblt.auxio.music.Genre
|
|||
import org.oxycblt.auxio.music.MusicMode
|
||||
import org.oxycblt.auxio.music.MusicParent
|
||||
import org.oxycblt.auxio.music.Sort
|
||||
import org.oxycblt.auxio.music.formatDurationMs
|
||||
import org.oxycblt.auxio.playback.formatDurationMs
|
||||
import org.oxycblt.auxio.ui.recycler.GenreViewHolder
|
||||
import org.oxycblt.auxio.ui.recycler.IndicatorAdapter
|
||||
import org.oxycblt.auxio.ui.recycler.Item
|
||||
|
|
|
@ -28,9 +28,9 @@ import org.oxycblt.auxio.music.MusicMode
|
|||
import org.oxycblt.auxio.music.MusicParent
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.Sort
|
||||
import org.oxycblt.auxio.music.formatDurationMs
|
||||
import org.oxycblt.auxio.music.picker.PickerMode
|
||||
import org.oxycblt.auxio.music.secsToMs
|
||||
import org.oxycblt.auxio.playback.formatDurationMs
|
||||
import org.oxycblt.auxio.playback.secsToMs
|
||||
import org.oxycblt.auxio.settings.Settings
|
||||
import org.oxycblt.auxio.ui.MainNavigationAction
|
||||
import org.oxycblt.auxio.ui.recycler.IndicatorAdapter
|
||||
|
|
|
@ -26,7 +26,12 @@ import kotlinx.parcelize.Parcelize
|
|||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.music.extractor.parseId3GenreNames
|
||||
import org.oxycblt.auxio.music.extractor.parseMultiValue
|
||||
import org.oxycblt.auxio.music.extractor.parseReleaseType
|
||||
import org.oxycblt.auxio.music.extractor.toUuidOrNull
|
||||
import org.oxycblt.auxio.music.storage.Directory
|
||||
import org.oxycblt.auxio.music.storage.MimeType
|
||||
import org.oxycblt.auxio.music.storage.Path
|
||||
import org.oxycblt.auxio.music.storage.albumCoverUri
|
||||
import org.oxycblt.auxio.music.storage.audioUri
|
||||
import org.oxycblt.auxio.settings.Settings
|
||||
import org.oxycblt.auxio.ui.recycler.Item
|
||||
import org.oxycblt.auxio.util.nonZeroOrNull
|
||||
|
@ -358,7 +363,7 @@ class Song constructor(raw: Raw, settings: Settings) : Music() {
|
|||
musicBrainzId = raw.albumMusicBrainzId?.toUuidOrNull(),
|
||||
name = requireNotNull(raw.albumName) { "Invalid raw: No album name" },
|
||||
sortName = raw.albumSortName,
|
||||
releaseType = raw.albumReleaseTypes.parseReleaseType(settings),
|
||||
releaseType = ReleaseType.parse(raw.albumReleaseTypes.parseMultiValue(settings)),
|
||||
rawArtists = rawAlbumArtists.ifEmpty { rawArtists }.ifEmpty { listOf(Artist.Raw(null, null)) }
|
||||
)
|
||||
|
||||
|
@ -557,7 +562,9 @@ class Album constructor(raw: Raw, override val songs: List<Song>) : MusicParent(
|
|||
*/
|
||||
class Artist
|
||||
constructor(raw: Raw, songAlbums: List<Music>) : MusicParent() {
|
||||
override val uid = raw.musicBrainzId?.let { UID.musicBrainz(MusicMode.ARTISTS, it) } ?: UID.auxio(MusicMode.ARTISTS) { update(raw.name) }
|
||||
override val uid = raw.musicBrainzId?.let { UID.musicBrainz(MusicMode.ARTISTS, it) } ?: UID.auxio(
|
||||
MusicMode.ARTISTS
|
||||
) { update(raw.name) }
|
||||
|
||||
override val rawName = raw.name
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ import android.net.Uri
|
|||
import android.provider.OpenableColumns
|
||||
import org.oxycblt.auxio.music.MusicStore.Callback
|
||||
import org.oxycblt.auxio.music.MusicStore.Library
|
||||
import org.oxycblt.auxio.music.storage.useQuery
|
||||
import org.oxycblt.auxio.util.contentResolverSafe
|
||||
|
||||
/**
|
||||
|
|
|
@ -26,13 +26,13 @@ import android.provider.MediaStore
|
|||
import androidx.annotation.RequiresApi
|
||||
import androidx.core.database.getIntOrNull
|
||||
import androidx.core.database.getStringOrNull
|
||||
import org.oxycblt.auxio.music.Directory
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.directoryCompat
|
||||
import org.oxycblt.auxio.music.mediaStoreVolumeNameCompat
|
||||
import org.oxycblt.auxio.music.queryCursor
|
||||
import org.oxycblt.auxio.music.storageVolumesCompat
|
||||
import org.oxycblt.auxio.music.useQuery
|
||||
import org.oxycblt.auxio.music.storage.Directory
|
||||
import org.oxycblt.auxio.music.storage.directoryCompat
|
||||
import org.oxycblt.auxio.music.storage.mediaStoreVolumeNameCompat
|
||||
import org.oxycblt.auxio.music.storage.queryCursor
|
||||
import org.oxycblt.auxio.music.storage.storageVolumesCompat
|
||||
import org.oxycblt.auxio.music.storage.useQuery
|
||||
import org.oxycblt.auxio.settings.Settings
|
||||
import org.oxycblt.auxio.util.contentResolverSafe
|
||||
import org.oxycblt.auxio.util.getSystemServiceCompat
|
||||
|
|
|
@ -26,7 +26,7 @@ import com.google.android.exoplayer2.metadata.id3.TextInformationFrame
|
|||
import com.google.android.exoplayer2.metadata.vorbis.VorbisComment
|
||||
import org.oxycblt.auxio.music.Date
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.audioUri
|
||||
import org.oxycblt.auxio.music.storage.audioUri
|
||||
import org.oxycblt.auxio.util.logD
|
||||
import org.oxycblt.auxio.util.logW
|
||||
|
||||
|
|
|
@ -19,9 +19,9 @@ package org.oxycblt.auxio.music.extractor
|
|||
|
||||
import androidx.core.text.isDigitsOnly
|
||||
import org.oxycblt.auxio.music.Date
|
||||
import org.oxycblt.auxio.music.ReleaseType
|
||||
import org.oxycblt.auxio.settings.Settings
|
||||
import org.oxycblt.auxio.util.nonZeroOrNull
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* Parse out the track number field as if the given Int is formatted as DTTT, where D Is the disc
|
||||
|
@ -110,8 +110,11 @@ fun String.maybeParseSeparators(settings: Settings): List<String> {
|
|||
return splitEscaped { separators.contains(it) }
|
||||
}
|
||||
|
||||
/** Parse a multi-value tag into a [ReleaseType], handling separators in the process. */
|
||||
fun List<String>.parseReleaseType(settings: Settings) = ReleaseType.parse(parseMultiValue(settings))
|
||||
fun String.toUuidOrNull(): UUID? = try {
|
||||
UUID.fromString(this)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
null
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a multi-value genre name using ID3v2 rules. If there is one value, the ID3v2.3 rules will
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.music.settings
|
||||
package org.oxycblt.auxio.music.extractor
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
|
@ -15,13 +15,12 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.music.settings
|
||||
package org.oxycblt.auxio.music.storage
|
||||
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.oxycblt.auxio.databinding.ItemMusicDirBinding
|
||||
import org.oxycblt.auxio.music.Directory
|
||||
import org.oxycblt.auxio.ui.recycler.DialogViewHolder
|
||||
import org.oxycblt.auxio.util.context
|
||||
import org.oxycblt.auxio.util.inflater
|
|
@ -15,9 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.music.settings
|
||||
|
||||
import org.oxycblt.auxio.music.Directory
|
||||
package org.oxycblt.auxio.music.storage
|
||||
|
||||
/** Represents a the configuration for the "Folder Management" setting */
|
||||
data class MusicDirs(val dirs: List<Directory>, val shouldInclude: Boolean)
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.music.settings
|
||||
package org.oxycblt.auxio.music.storage
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
|
@ -28,7 +28,6 @@ import androidx.core.view.isVisible
|
|||
import org.oxycblt.auxio.BuildConfig
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.DialogMusicDirsBinding
|
||||
import org.oxycblt.auxio.music.Directory
|
||||
import org.oxycblt.auxio.settings.Settings
|
||||
import org.oxycblt.auxio.ui.fragment.ViewBindingDialogFragment
|
||||
import org.oxycblt.auxio.util.context
|
|
@ -15,10 +15,14 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.music
|
||||
package org.oxycblt.auxio.music.storage
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.ContentResolver
|
||||
import android.content.ContentUris
|
||||
import android.content.Context
|
||||
import android.database.Cursor
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import android.os.storage.StorageManager
|
||||
|
@ -91,6 +95,107 @@ class Directory private constructor(val volume: StorageVolume, val relativePath:
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a mime type as it is loaded by Auxio. [fromExtension] is based on the file extension
|
||||
* should always exist, while [fromFormat] is based on the file itself and may not be available.
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
data class MimeType(val fromExtension: String, val fromFormat: String?) {
|
||||
fun resolveName(context: Context): String {
|
||||
// We try our best to produce a more readable name for the common audio formats.
|
||||
val formatName =
|
||||
when (fromFormat) {
|
||||
// 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
|
||||
// inner codec that we show.
|
||||
MimeTypes.AUDIO_MPEG,
|
||||
MimeTypes.AUDIO_MPEG_L1,
|
||||
MimeTypes.AUDIO_MPEG_L2 -> R.string.cdc_mp3
|
||||
MimeTypes.AUDIO_AAC -> R.string.cdc_aac
|
||||
MimeTypes.AUDIO_VORBIS -> R.string.cdc_vorbis
|
||||
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.
|
||||
|
||||
else -> -1
|
||||
}
|
||||
|
||||
if (formatName > -1) {
|
||||
return context.getString(formatName)
|
||||
}
|
||||
|
||||
// Fall back to the file extension in the case that we have no mime type or
|
||||
// a useless "audio/raw" mime type. Here:
|
||||
// - We return names for container formats instead of the inner format, as we
|
||||
// cannot parse the file.
|
||||
// - We are at the mercy of the Android OS, hence we check for every possible mime
|
||||
// type for a particular format.
|
||||
val extensionName =
|
||||
when (fromExtension) {
|
||||
"audio/mpeg",
|
||||
"audio/mp3" -> R.string.cdc_mp3
|
||||
"audio/mp4",
|
||||
"audio/mp4a-latm",
|
||||
"audio/mpeg4-generic" -> R.string.cdc_mp4
|
||||
"audio/aac",
|
||||
"audio/aacp",
|
||||
"audio/3gpp",
|
||||
"audio/3gpp2" -> R.string.cdc_aac
|
||||
"audio/ogg",
|
||||
"application/ogg",
|
||||
"application/x-ogg" -> R.string.cdc_ogg
|
||||
"audio/flac" -> R.string.cdc_flac
|
||||
"audio/wav",
|
||||
"audio/x-wav",
|
||||
"audio/wave",
|
||||
"audio/vnd.wave" -> R.string.cdc_wav
|
||||
"audio/x-matroska" -> R.string.cdc_mka
|
||||
else -> -1
|
||||
}
|
||||
|
||||
return if (extensionName > -1) {
|
||||
context.getString(extensionName)
|
||||
} else {
|
||||
// Fall back to the extension if we can't find a special name for this format.
|
||||
MimeTypeMap.getSingleton().getExtensionFromMimeType(fromExtension)?.uppercase()
|
||||
?: context.getString(R.string.def_codec)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Shortcut for making a [ContentResolver] query with less superfluous arguments. */
|
||||
fun ContentResolver.queryCursor(
|
||||
uri: Uri,
|
||||
projection: Array<out String>,
|
||||
selector: String? = null,
|
||||
args: Array<String>? = null
|
||||
) = query(uri, projection, selector, args, null)
|
||||
|
||||
/** Shortcut for making a [ContentResolver] query and using the particular cursor with [use]. */
|
||||
inline fun <reified R> ContentResolver.useQuery(
|
||||
uri: Uri,
|
||||
projection: Array<out String>,
|
||||
selector: String? = null,
|
||||
args: Array<String>? = null,
|
||||
block: (Cursor) -> R
|
||||
) = queryCursor(uri, projection, selector, args)?.use(block)
|
||||
|
||||
/**
|
||||
* For some reason the album cover URI namespace does not have a member in [MediaStore], but it
|
||||
* still works since at least API 21.
|
||||
*/
|
||||
private val EXTERNAL_ALBUM_ART_URI = Uri.parse("content://media/external/audio/albumart")
|
||||
|
||||
/** Converts a [Long] Audio ID into a URI to that particular audio file. */
|
||||
val Long.audioUri: Uri
|
||||
get() = ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, this)
|
||||
|
||||
/** Converts a [Long] Album ID into a URI pointing to MediaStore-cached album art. */
|
||||
val Long.albumCoverUri: Uri
|
||||
get() = ContentUris.withAppendedId(EXTERNAL_ALBUM_ART_URI, this)
|
||||
|
||||
@Suppress("NewApi")
|
||||
private val SM_API21_GET_VOLUME_LIST_METHOD: Method by
|
||||
lazyReflectedMethod(StorageManager::class, "getVolumeList")
|
||||
|
@ -181,73 +286,3 @@ val StorageVolume.mediaStoreVolumeNameCompat: String?
|
|||
uuidCompat?.lowercase()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a mime type as it is loaded by Auxio. [fromExtension] is based on the file extension
|
||||
* should always exist, while [fromFormat] is based on the file itself and may not be available.
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
data class MimeType(val fromExtension: String, val fromFormat: String?) {
|
||||
fun resolveName(context: Context): String {
|
||||
// We try our best to produce a more readable name for the common audio formats.
|
||||
val formatName =
|
||||
when (fromFormat) {
|
||||
// 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
|
||||
// inner codec that we show.
|
||||
MimeTypes.AUDIO_MPEG,
|
||||
MimeTypes.AUDIO_MPEG_L1,
|
||||
MimeTypes.AUDIO_MPEG_L2 -> R.string.cdc_mp3
|
||||
MimeTypes.AUDIO_AAC -> R.string.cdc_aac
|
||||
MimeTypes.AUDIO_VORBIS -> R.string.cdc_vorbis
|
||||
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.
|
||||
|
||||
else -> -1
|
||||
}
|
||||
|
||||
if (formatName > -1) {
|
||||
return context.getString(formatName)
|
||||
}
|
||||
|
||||
// Fall back to the file extension in the case that we have no mime type or
|
||||
// a useless "audio/raw" mime type. Here:
|
||||
// - We return names for container formats instead of the inner format, as we
|
||||
// cannot parse the file.
|
||||
// - We are at the mercy of the Android OS, hence we check for every possible mime
|
||||
// type for a particular format.
|
||||
val extensionName =
|
||||
when (fromExtension) {
|
||||
"audio/mpeg",
|
||||
"audio/mp3" -> R.string.cdc_mp3
|
||||
"audio/mp4",
|
||||
"audio/mp4a-latm",
|
||||
"audio/mpeg4-generic" -> R.string.cdc_mp4
|
||||
"audio/aac",
|
||||
"audio/aacp",
|
||||
"audio/3gpp",
|
||||
"audio/3gpp2" -> R.string.cdc_aac
|
||||
"audio/ogg",
|
||||
"application/ogg",
|
||||
"application/x-ogg" -> R.string.cdc_ogg
|
||||
"audio/flac" -> R.string.cdc_flac
|
||||
"audio/wav",
|
||||
"audio/x-wav",
|
||||
"audio/wave",
|
||||
"audio/vnd.wave" -> R.string.cdc_wav
|
||||
"audio/x-matroska" -> R.string.cdc_mka
|
||||
else -> -1
|
||||
}
|
||||
|
||||
return if (extensionName > -1) {
|
||||
context.getString(extensionName)
|
||||
} else {
|
||||
// Fall back to the extension if we can't find a special name for this format.
|
||||
MimeTypeMap.getSingleton().getExtensionFromMimeType(fromExtension)?.uppercase()
|
||||
?: context.getString(R.string.def_codec)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,7 +24,6 @@ import org.oxycblt.auxio.IntegerTable
|
|||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.FragmentPlaybackBarBinding
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.msToDs
|
||||
import org.oxycblt.auxio.playback.state.RepeatMode
|
||||
import org.oxycblt.auxio.settings.Settings
|
||||
import org.oxycblt.auxio.ui.MainNavigationAction
|
||||
|
@ -63,6 +62,7 @@ class PlaybackBarFragment : ViewBindingFragment<FragmentPlaybackBarBinding>() {
|
|||
}
|
||||
|
||||
binding.playbackSong.isSelected = true
|
||||
binding.playbackInfo.isSelected = true
|
||||
|
||||
// Load the track color in manually as it's unclear whether the track actually supports
|
||||
// using a ColorStateList in the resources
|
||||
|
@ -111,6 +111,7 @@ class PlaybackBarFragment : ViewBindingFragment<FragmentPlaybackBarBinding>() {
|
|||
override fun onDestroyBinding(binding: FragmentPlaybackBarBinding) {
|
||||
super.onDestroyBinding(binding)
|
||||
binding.playbackSong.isSelected = false
|
||||
binding.playbackInfo.isSelected = false
|
||||
}
|
||||
|
||||
private fun updateSong(song: Song?) {
|
||||
|
|
|
@ -32,7 +32,6 @@ import org.oxycblt.auxio.R
|
|||
import org.oxycblt.auxio.databinding.FragmentPlaybackPanelBinding
|
||||
import org.oxycblt.auxio.music.MusicParent
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.msToDs
|
||||
import org.oxycblt.auxio.music.picker.PickerMode
|
||||
import org.oxycblt.auxio.playback.state.RepeatMode
|
||||
import org.oxycblt.auxio.playback.ui.StyledSeekBar
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.playback.ui
|
||||
package org.oxycblt.auxio.playback
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.LayerDrawable
|
|
@ -15,59 +15,10 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.music
|
||||
package org.oxycblt.auxio.playback
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.content.ContentUris
|
||||
import android.content.Context
|
||||
import android.database.Cursor
|
||||
import android.net.Uri
|
||||
import android.provider.MediaStore
|
||||
import android.text.format.DateUtils
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.util.logD
|
||||
import java.util.UUID
|
||||
|
||||
/** Shortcut for making a [ContentResolver] query with less superfluous arguments. */
|
||||
fun ContentResolver.queryCursor(
|
||||
uri: Uri,
|
||||
projection: Array<out String>,
|
||||
selector: String? = null,
|
||||
args: Array<String>? = null
|
||||
) = query(uri, projection, selector, args, null)
|
||||
|
||||
/** Shortcut for making a [ContentResolver] query and using the particular cursor with [use]. */
|
||||
inline fun <reified R> ContentResolver.useQuery(
|
||||
uri: Uri,
|
||||
projection: Array<out String>,
|
||||
selector: String? = null,
|
||||
args: Array<String>? = null,
|
||||
block: (Cursor) -> R
|
||||
) = queryCursor(uri, projection, selector, args)?.use(block)
|
||||
|
||||
/**
|
||||
* For some reason the album cover URI namespace does not have a member in [MediaStore], but it
|
||||
* still works since at least API 21.
|
||||
*/
|
||||
private val EXTERNAL_ALBUM_ART_URI = Uri.parse("content://media/external/audio/albumart")
|
||||
|
||||
/** Converts a [Long] Audio ID into a URI to that particular audio file. */
|
||||
val Long.audioUri: Uri
|
||||
get() = ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, this)
|
||||
|
||||
/** Converts a [Long] Album ID into a URI pointing to MediaStore-cached album art. */
|
||||
val Long.albumCoverUri: Uri
|
||||
get() = ContentUris.withAppendedId(EXTERNAL_ALBUM_ART_URI, this)
|
||||
|
||||
fun String.toUuidOrNull(): UUID? = try {
|
||||
UUID.fromString(this)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
null
|
||||
}
|
||||
|
||||
/** Shortcut to resolve a year from a nullable date. Will return "No Date" if it is null. */
|
||||
fun Date?.resolveYear(context: Context) =
|
||||
this?.resolveYear(context) ?: context.getString(R.string.def_date)
|
||||
|
||||
/** Converts a long in milliseconds to a long in deci-seconds */
|
||||
fun Long.msToDs() = floorDiv(100)
|
|
@ -30,8 +30,6 @@ import org.oxycblt.auxio.music.Artist
|
|||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.MusicParent
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.dsToMs
|
||||
import org.oxycblt.auxio.music.msToDs
|
||||
import org.oxycblt.auxio.playback.state.InternalPlayer
|
||||
import org.oxycblt.auxio.playback.state.PlaybackStateDatabase
|
||||
import org.oxycblt.auxio.playback.state.PlaybackStateManager
|
||||
|
|
|
@ -21,7 +21,7 @@ import android.content.Context
|
|||
import android.util.AttributeSet
|
||||
import com.google.android.material.slider.Slider
|
||||
import org.oxycblt.auxio.databinding.ViewSeekBarBinding
|
||||
import org.oxycblt.auxio.music.formatDurationDs
|
||||
import org.oxycblt.auxio.playback.formatDurationDs
|
||||
import org.oxycblt.auxio.util.inflater
|
||||
import org.oxycblt.auxio.util.logD
|
||||
import kotlin.math.max
|
||||
|
|
|
@ -36,7 +36,7 @@ import org.oxycblt.auxio.music.Album
|
|||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.formatDurationMs
|
||||
import org.oxycblt.auxio.playback.formatDurationMs
|
||||
import org.oxycblt.auxio.ui.fragment.ViewBindingFragment
|
||||
import org.oxycblt.auxio.util.androidActivityViewModels
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
|
|
|
@ -27,10 +27,10 @@ import androidx.preference.PreferenceManager
|
|||
import org.oxycblt.auxio.IntegerTable
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.home.tabs.Tab
|
||||
import org.oxycblt.auxio.music.Directory
|
||||
import org.oxycblt.auxio.music.MusicMode
|
||||
import org.oxycblt.auxio.music.Sort
|
||||
import org.oxycblt.auxio.music.settings.MusicDirs
|
||||
import org.oxycblt.auxio.music.storage.Directory
|
||||
import org.oxycblt.auxio.music.storage.MusicDirs
|
||||
import org.oxycblt.auxio.playback.BarAction
|
||||
import org.oxycblt.auxio.playback.replaygain.ReplayGainMode
|
||||
import org.oxycblt.auxio.playback.replaygain.ReplayGainPreAmp
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.settings
|
||||
package org.oxycblt.auxio.settings.prefs
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.TypedArray
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.settings
|
||||
package org.oxycblt.auxio.settings.prefs
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.preference.PreferenceDialogFragmentCompat
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.settings
|
||||
package org.oxycblt.auxio.settings.prefs
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
|
@ -33,6 +33,7 @@ import coil.Coil
|
|||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.music.MusicViewModel
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.settings.Settings
|
||||
import org.oxycblt.auxio.ui.NavigationViewModel
|
||||
import org.oxycblt.auxio.util.androidActivityViewModels
|
||||
import org.oxycblt.auxio.util.isNight
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.settings
|
||||
package org.oxycblt.auxio.settings.prefs
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
|
@ -50,14 +50,14 @@ private val Any.autoTag: String
|
|||
get() = "Auxio.${this::class.simpleName ?: "Anonymous Object"}"
|
||||
|
||||
/**
|
||||
* I know that this will not stop you, but consider what you are doing with your life, plagiarizers.
|
||||
* Note: If you are politely forking this project while keeping the source open, you can ignore
|
||||
* the following passage. If not, give me a moment of your time.
|
||||
*
|
||||
* Do you want to live a fulfilling existence on this planet? Or do you want to spend your life
|
||||
* taking work others did and making it objectively worse so you could arbitrage a fraction of a
|
||||
* penny on every AdMob impression you get?
|
||||
*
|
||||
* You could do so many great things if you simply had the courage to come up with an idea of your
|
||||
* own.
|
||||
* Consider what you are doing with your life, plagiarizers. Do you want to live a fulfilling
|
||||
* existence on this planet? Or do you want to spend your life taking work others did and making
|
||||
* it objectively worse so you could arbitrage a fraction of a penny on every AdMob impression you
|
||||
* get? You could do so many great things if you simply had the courage to come up with an idea of
|
||||
* your own.
|
||||
*
|
||||
* If you still want to go on, I guess the only thing I can say is this:
|
||||
*
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
android:id="@+id/playback_sheet"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="org.oxycblt.auxio.playback.ui.PlaybackSheetBehavior">
|
||||
app:layout_behavior="org.oxycblt.auxio.playback.PlaybackSheetBehavior">
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/playback_bar_fragment"
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
style="@style/Widget.Auxio.DisableDropShadows"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="org.oxycblt.auxio.playback.ui.PlaybackSheetBehavior">
|
||||
app:layout_behavior="org.oxycblt.auxio.playback.PlaybackSheetBehavior">
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/playback_bar_fragment"
|
||||
|
|
|
@ -36,12 +36,13 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/spacing_small"
|
||||
android:layout_marginEnd="@dimen/spacing_tiny"
|
||||
android:ellipsize="end"
|
||||
android:ellipsize="marquee"
|
||||
android:marqueeRepeatLimit="marquee_forever"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/playback_cover"
|
||||
app:layout_constraintEnd_toStartOf="@+id/playback_controls_wrapper"
|
||||
app:layout_constraintStart_toEndOf="@+id/playback_cover"
|
||||
app:layout_constraintTop_toBottomOf="@+id/playback_song"
|
||||
tools:text="Artist Name / Album Name" />
|
||||
tools:text="Artist Name" />
|
||||
|
||||
<org.oxycblt.auxio.playback.ui.ForcedLTRFrameLayout
|
||||
android:id="@+id/playback_controls_wrapper"
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/settings_list_fragment"
|
||||
android:name="org.oxycblt.auxio.settings.SettingsListFragment"
|
||||
android:name="org.oxycblt.auxio.settings.prefs.SettingsListFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
|
||||
|
|
|
@ -84,12 +84,12 @@
|
|||
tools:layout="@layout/dialog_pre_amp" />
|
||||
<dialog
|
||||
android:id="@+id/music_dirs_dialog"
|
||||
android:name="org.oxycblt.auxio.music.settings.MusicDirsDialog"
|
||||
android:name="org.oxycblt.auxio.music.storage.MusicDirsDialog"
|
||||
android:label="music_dirs_dialog"
|
||||
tools:layout="@layout/dialog_music_dirs" />
|
||||
<dialog
|
||||
android:id="@+id/separators_dialog"
|
||||
android:name="org.oxycblt.auxio.music.settings.SeparatorsDialog"
|
||||
android:name="org.oxycblt.auxio.music.extractor.SeparatorsDialog"
|
||||
android:label="music_dirs_dialog"
|
||||
tools:layout="@layout/dialog_separators" />
|
||||
|
||||
|
|
|
@ -237,7 +237,7 @@
|
|||
<string name="set_exclude_non_music_desc">Ignore audio files that are not music, such as podcasts</string>
|
||||
<string name="set_separators">Multi-value separators</string>
|
||||
<string name="set_separators_desc">Configure characters that denote multiple tag values</string>
|
||||
<string name="set_separators_warning">Warning: Using this setting may result in some tags being incorrectly interpreted as having multiple values.</string>
|
||||
<string name="set_separators_warning">Warning: Using this setting may result in some tags being incorrectly interpreted as having multiple values. You can resolve this by escaping unwanted separator characters with a backslash (\\).</string>
|
||||
<string name="set_separators_comma">Comma (,)</string>
|
||||
<string name="set_separators_semicolon">Semicolon (;)</string>
|
||||
<string name="set_separators_slash">Slash (/)</string>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<PreferenceCategory app:title="@string/set_ui">
|
||||
|
||||
<org.oxycblt.auxio.settings.IntListPreference
|
||||
<org.oxycblt.auxio.settings.prefs.IntListPreference
|
||||
app:defaultValue="@integer/theme_auto"
|
||||
app:entries="@array/entries_theme"
|
||||
app:entryIcons="@array/icons_theme"
|
||||
|
@ -11,7 +11,7 @@
|
|||
app:key="@string/set_key_theme"
|
||||
app:title="@string/set_theme" />
|
||||
|
||||
<org.oxycblt.auxio.settings.WrappedDialogPreference
|
||||
<org.oxycblt.auxio.settings.prefs.WrappedDialogPreference
|
||||
app:icon="@drawable/ic_accent_24"
|
||||
app:key="@string/set_key_accent"
|
||||
app:title="@string/set_accent" />
|
||||
|
@ -26,7 +26,7 @@
|
|||
|
||||
<PreferenceCategory app:title="@string/set_display">
|
||||
|
||||
<org.oxycblt.auxio.settings.WrappedDialogPreference
|
||||
<org.oxycblt.auxio.settings.prefs.WrappedDialogPreference
|
||||
app:key="@string/set_key_lib_tabs"
|
||||
app:summary="@string/set_lib_tabs_desc"
|
||||
app:title="@string/set_lib_tabs" />
|
||||
|
@ -50,7 +50,7 @@
|
|||
app:summary="@string/set_round_mode_desc"
|
||||
app:title="@string/set_round_mode" />
|
||||
|
||||
<org.oxycblt.auxio.settings.IntListPreference
|
||||
<org.oxycblt.auxio.settings.prefs.IntListPreference
|
||||
app:defaultValue="@integer/bar_action_next"
|
||||
app:entries="@array/entries_bar_action"
|
||||
app:entryValues="@array/values_bar_action"
|
||||
|
@ -73,14 +73,14 @@
|
|||
app:summary="@string/set_headset_autoplay_desc"
|
||||
app:title="@string/set_headset_autoplay" />
|
||||
|
||||
<org.oxycblt.auxio.settings.IntListPreference
|
||||
<org.oxycblt.auxio.settings.prefs.IntListPreference
|
||||
app:defaultValue="@integer/replay_gain_dynamic"
|
||||
app:entries="@array/entries_replay_gain"
|
||||
app:entryValues="@array/values_replay_gain"
|
||||
app:key="@string/set_key_replay_gain"
|
||||
app:title="@string/set_replay_gain" />
|
||||
|
||||
<org.oxycblt.auxio.settings.WrappedDialogPreference
|
||||
<org.oxycblt.auxio.settings.prefs.WrappedDialogPreference
|
||||
app:key="@string/set_key_pre_amp"
|
||||
app:summary="@string/set_pre_amp_desc"
|
||||
app:title="@string/set_pre_amp" />
|
||||
|
@ -89,7 +89,7 @@
|
|||
|
||||
<PreferenceCategory app:title="@string/set_behavior">
|
||||
|
||||
<org.oxycblt.auxio.settings.IntListPreference
|
||||
<org.oxycblt.auxio.settings.prefs.IntListPreference
|
||||
app:defaultValue="@integer/music_mode_songs"
|
||||
app:entries="@array/entries_library_song_playback_mode"
|
||||
app:entryValues="@array/values_library_song_playback_mode"
|
||||
|
@ -97,7 +97,7 @@
|
|||
app:title="@string/set_library_song_playback_mode"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<org.oxycblt.auxio.settings.IntListPreference
|
||||
<org.oxycblt.auxio.settings.prefs.IntListPreference
|
||||
app:defaultValue="@integer/music_mode_none"
|
||||
app:entries="@array/entries_detail_song_playback_mode"
|
||||
app:entryValues="@array/values_detail_song_playback_mode"
|
||||
|
@ -159,12 +159,12 @@
|
|||
app:summary="@string/set_exclude_non_music_desc"
|
||||
app:title="@string/set_exclude_non_music" />
|
||||
|
||||
<org.oxycblt.auxio.settings.WrappedDialogPreference
|
||||
<org.oxycblt.auxio.settings.prefs.WrappedDialogPreference
|
||||
app:key="@string/set_key_music_dirs"
|
||||
app:summary="@string/set_dirs_desc"
|
||||
app:title="@string/set_dirs" />
|
||||
|
||||
<org.oxycblt.auxio.settings.WrappedDialogPreference
|
||||
<org.oxycblt.auxio.settings.prefs.WrappedDialogPreference
|
||||
app:key="@string/set_key_separators"
|
||||
app:summary="@string/set_separators_desc"
|
||||
app:title="@string/set_separators" />
|
||||
|
|
Loading…
Reference in a new issue