playback: fix state restore regression
Fix a state restore issue that would cause the parent to restore incorrectly. At some point, I accidentally used the index for the PlaybackMode field when restoring the playbackState. This resulted in the playback mode effectively reverting to ALL_SONGS and causing a number of subtle issues.
This commit is contained in:
parent
07127403ff
commit
182b08ab65
11 changed files with 98 additions and 79 deletions
|
|
@ -8,6 +8,7 @@
|
||||||
#### What's Fixed
|
#### What's Fixed
|
||||||
- Fixed crash when seeking to the end of a track as the track changed to a track with a lower duration
|
- Fixed crash when seeking to the end of a track as the track changed to a track with a lower duration
|
||||||
- Fixed regression where GadgetBridge media controls would no longer work
|
- Fixed regression where GadgetBridge media controls would no longer work
|
||||||
|
- Fixed issue where the album/artist/genre would not be correctly restored
|
||||||
|
|
||||||
#### Dev/Meta
|
#### Dev/Meta
|
||||||
- Switched from `LiveData` to `StateFlow`
|
- Switched from `LiveData` to `StateFlow`
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,9 @@ object IntegerTable {
|
||||||
/** DisplayMode.SHOW_SONGS */
|
/** DisplayMode.SHOW_SONGS */
|
||||||
const val DISPLAY_MODE_SHOW_SONGS = 0xA10B
|
const val DISPLAY_MODE_SHOW_SONGS = 0xA10B
|
||||||
|
|
||||||
|
// Note: Sort integer codes are non-contiguous due to significant amounts of time
|
||||||
|
// passing between the additions of new sort modes.
|
||||||
|
|
||||||
/** Sort.ByName */
|
/** Sort.ByName */
|
||||||
const val SORT_BY_NAME = 0xA10C
|
const val SORT_BY_NAME = 0xA10C
|
||||||
/** Sort.ByArtist */
|
/** Sort.ByArtist */
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,8 @@ import org.oxycblt.auxio.util.logW
|
||||||
class ExoPlayerBackend(private val inner: MediaStoreBackend) : Indexer.Backend {
|
class ExoPlayerBackend(private val inner: MediaStoreBackend) : Indexer.Backend {
|
||||||
private val runningTasks: Array<Future<TrackGroupArray>?> = arrayOfNulls(TASK_CAPACITY)
|
private val runningTasks: Array<Future<TrackGroupArray>?> = arrayOfNulls(TASK_CAPACITY)
|
||||||
|
|
||||||
|
// No need to implement our own query logic, as this backend is still reliant on
|
||||||
|
// MediaStore.
|
||||||
override fun query(context: Context) = inner.query(context)
|
override fun query(context: Context) = inner.query(context)
|
||||||
|
|
||||||
override fun loadSongs(context: Context, cursor: Cursor): Collection<Song> {
|
override fun loadSongs(context: Context, cursor: Cursor): Collection<Song> {
|
||||||
|
|
@ -69,7 +71,9 @@ class ExoPlayerBackend(private val inner: MediaStoreBackend) : Indexer.Backend {
|
||||||
val audioUri = requireNotNull(audio.id) { "Malformed audio: No id" }.audioUri
|
val audioUri = requireNotNull(audio.id) { "Malformed audio: No id" }.audioUri
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
// Spin until a task slot opens up, then start trying to parse metadata.
|
// In order to properly parallelize ExoPlayer's parser, we have an array containing
|
||||||
|
// a few slots for ongoing futures. As soon as a finished task opens up their slot,
|
||||||
|
// we begin loading metadata for this audio.
|
||||||
val index = runningTasks.indexOfFirst { it == null }
|
val index = runningTasks.indexOfFirst { it == null }
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
val task =
|
val task =
|
||||||
|
|
@ -82,7 +86,6 @@ class ExoPlayerBackend(private val inner: MediaStoreBackend) : Indexer.Backend {
|
||||||
|
|
||||||
runningTasks[index] = task
|
runningTasks[index] = task
|
||||||
|
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,9 @@ object Indexer {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
val songs = buildSongs(context, mediaStoreBackend)
|
val songs = buildSongs(context, mediaStoreBackend)
|
||||||
if (songs.isEmpty()) return null
|
if (songs.isEmpty()) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
val buildStart = System.currentTimeMillis()
|
val buildStart = System.currentTimeMillis()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ val String.no: Int?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse out the year field from a (presumably) ISO-8601-like date. This differs across tag formats
|
* Parse out the year field from a (presumably) ISO-8601-like date. This differs across tag formats
|
||||||
* and has no real consistent, but it's assumed that most will format granular dates as YYYY-MM-DD
|
* and has no real consistency, but it's assumed that most will format granular dates as YYYY-MM-DD
|
||||||
* (...) and thus we can parse the year out by splitting at the first -.
|
* (...) and thus we can parse the year out by splitting at the first -.
|
||||||
*/
|
*/
|
||||||
val String.iso8601year: Int?
|
val String.iso8601year: Int?
|
||||||
|
|
|
||||||
|
|
@ -63,12 +63,12 @@ import org.oxycblt.auxio.util.contentResolverSafe
|
||||||
* table, so we have to go for the less efficient "make a big query on all the songs lol" method so
|
* table, so we have to go for the less efficient "make a big query on all the songs lol" method so
|
||||||
* that songs don't end up fragmented across artists. Pretty much every OEM has added some extension
|
* that songs don't end up fragmented across artists. Pretty much every OEM has added some extension
|
||||||
* or quirk to MediaStore that I cannot reproduce, with some OEMs (COUGHSAMSUNGCOUGH) crippling the
|
* or quirk to MediaStore that I cannot reproduce, with some OEMs (COUGHSAMSUNGCOUGH) crippling the
|
||||||
* normal tables so that you're railroaded into their music app. The way I do blacklisting relies on
|
* normal tables so that you're railroaded into their music app. I have to use a semi-deprecated
|
||||||
* a semi-deprecated method, and the supposedly "modern" method is SLOWER and causes even more
|
* field to work with file paths, and the supposedly "modern" method is SLOWER and causes even more
|
||||||
* problems since I have to manage databases across version boundaries. Sometimes music will have a
|
* problems since some devices just don't expose those fields for some insane reason. Sometimes
|
||||||
* deformed clone that I can't filter out, sometimes Genres will just break for no reason, and
|
* music will have a deformed clone that I can't filter out, sometimes Genres will just break for
|
||||||
* sometimes tags encoded in UTF-8 will be interpreted as anything from UTF-16 to Latin-1 to *Shift
|
* no reason, and sometimes tags encoded in UTF-8 will be interpreted as anything from UTF-16 to
|
||||||
* JIS* WHY WHY WHY WHY WHY WHY WHY WHY WHY WHY WHY WHY WHY WHY WHY WHY WHY WHY
|
* Latin-1 to *Shift JIS* WHY WHY WHY WHY WHY WHY WHY WHY WHY WHY WHY WHY WHY WHY WHY WHY WHY WHY
|
||||||
*
|
*
|
||||||
* Is there anything we can do about it? No. Google has routinely shut down issues that begged
|
* Is there anything we can do about it? No. Google has routinely shut down issues that begged
|
||||||
* google to fix glaring issues with MediaStore or to just take the API behind the woodshed and
|
* google to fix glaring issues with MediaStore or to just take the API behind the woodshed and
|
||||||
|
|
@ -214,7 +214,10 @@ abstract class MediaStoreBackend : Indexer.Backend {
|
||||||
audio.duration = cursor.getLong(durationIndex)
|
audio.duration = cursor.getLong(durationIndex)
|
||||||
audio.year = cursor.getIntOrNull(yearIndex)
|
audio.year = cursor.getIntOrNull(yearIndex)
|
||||||
|
|
||||||
audio.album = cursor.getStringOrNull(albumIndex)
|
// A non-existent album name should theoretically be the name of the folder it contained
|
||||||
|
// in, but in practice it is more often "0" (as in /storage/emulated/0), even when it the
|
||||||
|
// file is not actually in the root internal storage directory.
|
||||||
|
audio.album = cursor.getString(albumIndex)
|
||||||
audio.albumId = cursor.getLong(albumIdIndex)
|
audio.albumId = cursor.getLong(albumIdIndex)
|
||||||
|
|
||||||
// Android does not make a non-existent artist tag null, it instead fills it in
|
// Android does not make a non-existent artist tag null, it instead fills it in
|
||||||
|
|
@ -230,6 +233,7 @@ abstract class MediaStoreBackend : Indexer.Backend {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The album artist field is nullable and never has placeholder values.
|
||||||
audio.albumArtist = cursor.getStringOrNull(albumArtistIndex)
|
audio.albumArtist = cursor.getStringOrNull(albumArtistIndex)
|
||||||
|
|
||||||
return audio
|
return audio
|
||||||
|
|
|
||||||
|
|
@ -155,7 +155,8 @@ class PlaybackStateDatabase(context: Context) :
|
||||||
isShuffled = cursor.getInt(shuffleIndex) == 1,
|
isShuffled = cursor.getInt(shuffleIndex) == 1,
|
||||||
songId = cursor.getLong(songIdIndex),
|
songId = cursor.getLong(songIdIndex),
|
||||||
parentId = cursor.getLongOrNull(parentIdIndex),
|
parentId = cursor.getLongOrNull(parentIdIndex),
|
||||||
playbackMode = PlaybackMode.fromInt(playbackModeIndex) ?: PlaybackMode.ALL_SONGS)
|
playbackMode = PlaybackMode.fromInt(cursor.getInt(playbackModeIndex))
|
||||||
|
?: PlaybackMode.ALL_SONGS)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,11 +28,14 @@ import androidx.core.view.updatePadding
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
import kotlinx.coroutines.flow.collect
|
|
||||||
import org.oxycblt.auxio.BuildConfig
|
import org.oxycblt.auxio.BuildConfig
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.databinding.FragmentAboutBinding
|
import org.oxycblt.auxio.databinding.FragmentAboutBinding
|
||||||
import org.oxycblt.auxio.home.HomeViewModel
|
import org.oxycblt.auxio.home.HomeViewModel
|
||||||
|
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.ui.ViewBindingFragment
|
import org.oxycblt.auxio.ui.ViewBindingFragment
|
||||||
import org.oxycblt.auxio.util.formatDuration
|
import org.oxycblt.auxio.util.formatDuration
|
||||||
import org.oxycblt.auxio.util.launch
|
import org.oxycblt.auxio.util.launch
|
||||||
|
|
@ -63,38 +66,33 @@ class AboutFragment : ViewBindingFragment<FragmentAboutBinding>() {
|
||||||
binding.aboutFaq.setOnClickListener { openLinkInBrowser(LINK_FAQ) }
|
binding.aboutFaq.setOnClickListener { openLinkInBrowser(LINK_FAQ) }
|
||||||
binding.aboutLicenses.setOnClickListener { openLinkInBrowser(LINK_LICENSES) }
|
binding.aboutLicenses.setOnClickListener { openLinkInBrowser(LINK_LICENSES) }
|
||||||
|
|
||||||
launch {
|
launch { homeModel.songs.collect(::updateSongCount) }
|
||||||
homeModel.songs.collect { songs ->
|
launch { homeModel.albums.collect(::updateAlbumCount) }
|
||||||
binding.aboutSongCount.textSafe = getString(R.string.fmt_songs_loaded, songs.size)
|
launch { homeModel.artists.collect(::updateArtistCount) }
|
||||||
binding.aboutTotalDuration.textSafe =
|
launch { homeModel.genres.collect(::updateGenreCount) }
|
||||||
getString(
|
}
|
||||||
R.string.fmt_total_duration,
|
|
||||||
getString(
|
|
||||||
R.string.fmt_total_duration,
|
|
||||||
songs.sumOf { it.durationSecs }.formatDuration(false)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
launch {
|
private fun updateSongCount(songs: List<Song>) {
|
||||||
homeModel.albums.collect { albums ->
|
val binding = requireBinding()
|
||||||
binding.aboutAlbumCount.textSafe =
|
binding.aboutSongCount.textSafe = getString(R.string.fmt_songs_loaded, songs.size)
|
||||||
getString(R.string.fmt_albums_loaded, albums.size)
|
binding.aboutTotalDuration.textSafe =
|
||||||
}
|
getString(
|
||||||
}
|
R.string.fmt_total_duration, songs.sumOf { it.durationSecs }.formatDuration(false))
|
||||||
|
}
|
||||||
|
|
||||||
launch {
|
private fun updateAlbumCount(albums: List<Album>) {
|
||||||
homeModel.artists.collect { artists ->
|
requireBinding().aboutAlbumCount.textSafe =
|
||||||
binding.aboutArtistCount.textSafe =
|
getString(R.string.fmt_albums_loaded, albums.size)
|
||||||
getString(R.string.fmt_artists_loaded, artists.size)
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
launch {
|
private fun updateArtistCount(artists: List<Artist>) {
|
||||||
homeModel.genres.collect { genres ->
|
requireBinding().aboutArtistCount.textSafe =
|
||||||
binding.aboutGenreCount.textSafe =
|
getString(R.string.fmt_artists_loaded, artists.size)
|
||||||
getString(R.string.fmt_genres_loaded, genres.size)
|
}
|
||||||
}
|
|
||||||
}
|
private fun updateGenreCount(genres: List<Genre>) {
|
||||||
|
requireBinding().aboutGenreCount.textSafe =
|
||||||
|
getString(R.string.fmt_genres_loaded, genres.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Go through the process of opening a [link] in a browser. */
|
/** Go through the process of opening a [link] in a browser. */
|
||||||
|
|
|
||||||
|
|
@ -38,27 +38,27 @@ import org.oxycblt.auxio.util.unlikelyToBeNull
|
||||||
class SettingsManager private constructor(context: Context) :
|
class SettingsManager private constructor(context: Context) :
|
||||||
SharedPreferences.OnSharedPreferenceChangeListener {
|
SharedPreferences.OnSharedPreferenceChangeListener {
|
||||||
|
|
||||||
private val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
private val inner = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
prefs.registerOnSharedPreferenceChangeListener(this)
|
inner.registerOnSharedPreferenceChangeListener(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- VALUES ---
|
// --- VALUES ---
|
||||||
|
|
||||||
/** The current theme */
|
/** The current theme */
|
||||||
val theme: Int
|
val theme: Int
|
||||||
get() = prefs.getInt(KEY_THEME, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
get() = inner.getInt(KEY_THEME, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
||||||
|
|
||||||
/** Whether the dark theme should be black or not */
|
/** Whether the dark theme should be black or not */
|
||||||
val useBlackTheme: Boolean
|
val useBlackTheme: Boolean
|
||||||
get() = prefs.getBoolean(KEY_BLACK_THEME, false)
|
get() = inner.getBoolean(KEY_BLACK_THEME, false)
|
||||||
|
|
||||||
/** The current accent. */
|
/** The current accent. */
|
||||||
var accent: Accent
|
var accent: Accent
|
||||||
get() = handleAccentCompat(prefs)
|
get() = handleAccentCompat(inner)
|
||||||
set(value) {
|
set(value) {
|
||||||
prefs.edit {
|
inner.edit {
|
||||||
putInt(KEY_ACCENT, value.index)
|
putInt(KEY_ACCENT, value.index)
|
||||||
apply()
|
apply()
|
||||||
}
|
}
|
||||||
|
|
@ -69,15 +69,15 @@ class SettingsManager private constructor(context: Context) :
|
||||||
* true if shuffle.
|
* true if shuffle.
|
||||||
*/
|
*/
|
||||||
val useAltNotifAction: Boolean
|
val useAltNotifAction: Boolean
|
||||||
get() = prefs.getBoolean(KEY_USE_ALT_NOTIFICATION_ACTION, false)
|
get() = inner.getBoolean(KEY_USE_ALT_NOTIFICATION_ACTION, false)
|
||||||
|
|
||||||
/** The current library tabs preferred by the user. */
|
/** The current library tabs preferred by the user. */
|
||||||
var libTabs: Array<Tab>
|
var libTabs: Array<Tab>
|
||||||
get() =
|
get() =
|
||||||
Tab.fromSequence(prefs.getInt(KEY_LIB_TABS, Tab.SEQUENCE_DEFAULT))
|
Tab.fromSequence(inner.getInt(KEY_LIB_TABS, Tab.SEQUENCE_DEFAULT))
|
||||||
?: unlikelyToBeNull(Tab.fromSequence(Tab.SEQUENCE_DEFAULT))
|
?: unlikelyToBeNull(Tab.fromSequence(Tab.SEQUENCE_DEFAULT))
|
||||||
set(value) {
|
set(value) {
|
||||||
prefs.edit {
|
inner.edit {
|
||||||
putInt(KEY_LIB_TABS, Tab.toSequence(value))
|
putInt(KEY_LIB_TABS, Tab.toSequence(value))
|
||||||
apply()
|
apply()
|
||||||
}
|
}
|
||||||
|
|
@ -85,33 +85,33 @@ class SettingsManager private constructor(context: Context) :
|
||||||
|
|
||||||
/** Whether to load embedded covers */
|
/** Whether to load embedded covers */
|
||||||
val showCovers: Boolean
|
val showCovers: Boolean
|
||||||
get() = prefs.getBoolean(KEY_SHOW_COVERS, true)
|
get() = inner.getBoolean(KEY_SHOW_COVERS, true)
|
||||||
|
|
||||||
/** Whether to ignore MediaStore covers */
|
/** Whether to ignore MediaStore covers */
|
||||||
val useQualityCovers: Boolean
|
val useQualityCovers: Boolean
|
||||||
get() = prefs.getBoolean(KEY_QUALITY_COVERS, false)
|
get() = inner.getBoolean(KEY_QUALITY_COVERS, false)
|
||||||
|
|
||||||
/** Whether to round album covers */
|
/** Whether to round album covers */
|
||||||
val roundCovers: Boolean
|
val roundCovers: Boolean
|
||||||
get() = prefs.getBoolean(KEY_ROUND_COVERS, false)
|
get() = inner.getBoolean(KEY_ROUND_COVERS, false)
|
||||||
|
|
||||||
/** Whether to resume playback when a headset is connected (may not work well in all cases) */
|
/** Whether to resume playback when a headset is connected (may not work well in all cases) */
|
||||||
val headsetAutoplay: Boolean
|
val headsetAutoplay: Boolean
|
||||||
get() = prefs.getBoolean(KEY_HEADSET_AUTOPLAY, false)
|
get() = inner.getBoolean(KEY_HEADSET_AUTOPLAY, false)
|
||||||
|
|
||||||
/** The current ReplayGain configuration */
|
/** The current ReplayGain configuration */
|
||||||
val replayGainMode: ReplayGainMode
|
val replayGainMode: ReplayGainMode
|
||||||
get() =
|
get() =
|
||||||
ReplayGainMode.fromIntCode(prefs.getInt(KEY_REPLAY_GAIN, Int.MIN_VALUE))
|
ReplayGainMode.fromIntCode(inner.getInt(KEY_REPLAY_GAIN, Int.MIN_VALUE))
|
||||||
?: ReplayGainMode.OFF
|
?: ReplayGainMode.OFF
|
||||||
|
|
||||||
/** The current ReplayGain pre-amp configuration */
|
/** The current ReplayGain pre-amp configuration */
|
||||||
var replayGainPreAmp: ReplayGainPreAmp
|
var replayGainPreAmp: ReplayGainPreAmp
|
||||||
get() =
|
get() =
|
||||||
ReplayGainPreAmp(
|
ReplayGainPreAmp(
|
||||||
prefs.getFloat(KEY_PRE_AMP_WITH, 0f), prefs.getFloat(KEY_PRE_AMP_WITHOUT, 0f))
|
inner.getFloat(KEY_PRE_AMP_WITH, 0f), inner.getFloat(KEY_PRE_AMP_WITHOUT, 0f))
|
||||||
set(value) {
|
set(value) {
|
||||||
prefs.edit {
|
inner.edit {
|
||||||
putFloat(KEY_PRE_AMP_WITH, value.with)
|
putFloat(KEY_PRE_AMP_WITH, value.with)
|
||||||
putFloat(KEY_PRE_AMP_WITHOUT, value.without)
|
putFloat(KEY_PRE_AMP_WITHOUT, value.without)
|
||||||
apply()
|
apply()
|
||||||
|
|
@ -121,29 +121,29 @@ class SettingsManager private constructor(context: Context) :
|
||||||
/** What queue to create when a song is selected (ex. From All Songs or Search) */
|
/** What queue to create when a song is selected (ex. From All Songs or Search) */
|
||||||
val songPlaybackMode: PlaybackMode
|
val songPlaybackMode: PlaybackMode
|
||||||
get() =
|
get() =
|
||||||
PlaybackMode.fromInt(prefs.getInt(KEY_SONG_PLAYBACK_MODE, Int.MIN_VALUE))
|
PlaybackMode.fromInt(inner.getInt(KEY_SONG_PLAYBACK_MODE, Int.MIN_VALUE))
|
||||||
?: PlaybackMode.ALL_SONGS
|
?: PlaybackMode.ALL_SONGS
|
||||||
|
|
||||||
/** Whether shuffle should stay on when a new song is selected. */
|
/** Whether shuffle should stay on when a new song is selected. */
|
||||||
val keepShuffle: Boolean
|
val keepShuffle: Boolean
|
||||||
get() = prefs.getBoolean(KEY_KEEP_SHUFFLE, true)
|
get() = inner.getBoolean(KEY_KEEP_SHUFFLE, true)
|
||||||
|
|
||||||
/** Whether to rewind when the back button is pressed. */
|
/** Whether to rewind when the back button is pressed. */
|
||||||
val rewindWithPrev: Boolean
|
val rewindWithPrev: Boolean
|
||||||
get() = prefs.getBoolean(KEY_PREV_REWIND, true)
|
get() = inner.getBoolean(KEY_PREV_REWIND, true)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether [org.oxycblt.auxio.playback.state.RepeatMode.TRACK] should pause when the track
|
* Whether [org.oxycblt.auxio.playback.state.RepeatMode.TRACK] should pause when the track
|
||||||
* repeats
|
* repeats
|
||||||
*/
|
*/
|
||||||
val pauseOnRepeat: Boolean
|
val pauseOnRepeat: Boolean
|
||||||
get() = prefs.getBoolean(KEY_PAUSE_ON_REPEAT, false)
|
get() = inner.getBoolean(KEY_PAUSE_ON_REPEAT, false)
|
||||||
|
|
||||||
/** The current filter mode of the search tab */
|
/** The current filter mode of the search tab */
|
||||||
var searchFilterMode: DisplayMode?
|
var searchFilterMode: DisplayMode?
|
||||||
get() = DisplayMode.fromInt(prefs.getInt(KEY_SEARCH_FILTER_MODE, Int.MIN_VALUE))
|
get() = DisplayMode.fromInt(inner.getInt(KEY_SEARCH_FILTER_MODE, Int.MIN_VALUE))
|
||||||
set(value) {
|
set(value) {
|
||||||
prefs.edit {
|
inner.edit {
|
||||||
putInt(KEY_SEARCH_FILTER_MODE, value?.intCode ?: Int.MIN_VALUE)
|
putInt(KEY_SEARCH_FILTER_MODE, value?.intCode ?: Int.MIN_VALUE)
|
||||||
apply()
|
apply()
|
||||||
}
|
}
|
||||||
|
|
@ -152,9 +152,9 @@ class SettingsManager private constructor(context: Context) :
|
||||||
/** The song sort mode on HomeFragment */
|
/** The song sort mode on HomeFragment */
|
||||||
var libSongSort: Sort
|
var libSongSort: Sort
|
||||||
get() =
|
get() =
|
||||||
Sort.fromIntCode(prefs.getInt(KEY_LIB_SONGS_SORT, Int.MIN_VALUE)) ?: Sort.ByName(true)
|
Sort.fromIntCode(inner.getInt(KEY_LIB_SONGS_SORT, Int.MIN_VALUE)) ?: Sort.ByName(true)
|
||||||
set(value) {
|
set(value) {
|
||||||
prefs.edit {
|
inner.edit {
|
||||||
putInt(KEY_LIB_SONGS_SORT, value.intCode)
|
putInt(KEY_LIB_SONGS_SORT, value.intCode)
|
||||||
apply()
|
apply()
|
||||||
}
|
}
|
||||||
|
|
@ -163,9 +163,9 @@ class SettingsManager private constructor(context: Context) :
|
||||||
/** The album sort mode on HomeFragment */
|
/** The album sort mode on HomeFragment */
|
||||||
var libAlbumSort: Sort
|
var libAlbumSort: Sort
|
||||||
get() =
|
get() =
|
||||||
Sort.fromIntCode(prefs.getInt(KEY_LIB_ALBUMS_SORT, Int.MIN_VALUE)) ?: Sort.ByName(true)
|
Sort.fromIntCode(inner.getInt(KEY_LIB_ALBUMS_SORT, Int.MIN_VALUE)) ?: Sort.ByName(true)
|
||||||
set(value) {
|
set(value) {
|
||||||
prefs.edit {
|
inner.edit {
|
||||||
putInt(KEY_LIB_ALBUMS_SORT, value.intCode)
|
putInt(KEY_LIB_ALBUMS_SORT, value.intCode)
|
||||||
apply()
|
apply()
|
||||||
}
|
}
|
||||||
|
|
@ -174,9 +174,9 @@ class SettingsManager private constructor(context: Context) :
|
||||||
/** The artist sort mode on HomeFragment */
|
/** The artist sort mode on HomeFragment */
|
||||||
var libArtistSort: Sort
|
var libArtistSort: Sort
|
||||||
get() =
|
get() =
|
||||||
Sort.fromIntCode(prefs.getInt(KEY_LIB_ARTISTS_SORT, Int.MIN_VALUE)) ?: Sort.ByName(true)
|
Sort.fromIntCode(inner.getInt(KEY_LIB_ARTISTS_SORT, Int.MIN_VALUE)) ?: Sort.ByName(true)
|
||||||
set(value) {
|
set(value) {
|
||||||
prefs.edit {
|
inner.edit {
|
||||||
putInt(KEY_LIB_ARTISTS_SORT, value.intCode)
|
putInt(KEY_LIB_ARTISTS_SORT, value.intCode)
|
||||||
apply()
|
apply()
|
||||||
}
|
}
|
||||||
|
|
@ -185,9 +185,9 @@ class SettingsManager private constructor(context: Context) :
|
||||||
/** The genre sort mode on HomeFragment */
|
/** The genre sort mode on HomeFragment */
|
||||||
var libGenreSort: Sort
|
var libGenreSort: Sort
|
||||||
get() =
|
get() =
|
||||||
Sort.fromIntCode(prefs.getInt(KEY_LIB_GENRES_SORT, Int.MIN_VALUE)) ?: Sort.ByName(true)
|
Sort.fromIntCode(inner.getInt(KEY_LIB_GENRES_SORT, Int.MIN_VALUE)) ?: Sort.ByName(true)
|
||||||
set(value) {
|
set(value) {
|
||||||
prefs.edit {
|
inner.edit {
|
||||||
putInt(KEY_LIB_GENRES_SORT, value.intCode)
|
putInt(KEY_LIB_GENRES_SORT, value.intCode)
|
||||||
apply()
|
apply()
|
||||||
}
|
}
|
||||||
|
|
@ -197,7 +197,7 @@ class SettingsManager private constructor(context: Context) :
|
||||||
var detailAlbumSort: Sort
|
var detailAlbumSort: Sort
|
||||||
get() {
|
get() {
|
||||||
var sort =
|
var sort =
|
||||||
Sort.fromIntCode(prefs.getInt(KEY_DETAIL_ALBUM_SORT, Int.MIN_VALUE))
|
Sort.fromIntCode(inner.getInt(KEY_DETAIL_ALBUM_SORT, Int.MIN_VALUE))
|
||||||
?: Sort.ByDisc(true)
|
?: Sort.ByDisc(true)
|
||||||
|
|
||||||
// Correct legacy album sort modes to Disc
|
// Correct legacy album sort modes to Disc
|
||||||
|
|
@ -208,7 +208,7 @@ class SettingsManager private constructor(context: Context) :
|
||||||
return sort
|
return sort
|
||||||
}
|
}
|
||||||
set(value) {
|
set(value) {
|
||||||
prefs.edit {
|
inner.edit {
|
||||||
putInt(KEY_DETAIL_ALBUM_SORT, value.intCode)
|
putInt(KEY_DETAIL_ALBUM_SORT, value.intCode)
|
||||||
apply()
|
apply()
|
||||||
}
|
}
|
||||||
|
|
@ -217,10 +217,10 @@ class SettingsManager private constructor(context: Context) :
|
||||||
/** The detail artist sort mode */
|
/** The detail artist sort mode */
|
||||||
var detailArtistSort: Sort
|
var detailArtistSort: Sort
|
||||||
get() =
|
get() =
|
||||||
Sort.fromIntCode(prefs.getInt(KEY_DETAIL_ARTIST_SORT, Int.MIN_VALUE))
|
Sort.fromIntCode(inner.getInt(KEY_DETAIL_ARTIST_SORT, Int.MIN_VALUE))
|
||||||
?: Sort.ByYear(false)
|
?: Sort.ByYear(false)
|
||||||
set(value) {
|
set(value) {
|
||||||
prefs.edit {
|
inner.edit {
|
||||||
putInt(KEY_DETAIL_ARTIST_SORT, value.intCode)
|
putInt(KEY_DETAIL_ARTIST_SORT, value.intCode)
|
||||||
apply()
|
apply()
|
||||||
}
|
}
|
||||||
|
|
@ -229,10 +229,10 @@ class SettingsManager private constructor(context: Context) :
|
||||||
/** The detail genre sort mode */
|
/** The detail genre sort mode */
|
||||||
var detailGenreSort: Sort
|
var detailGenreSort: Sort
|
||||||
get() =
|
get() =
|
||||||
Sort.fromIntCode(prefs.getInt(KEY_DETAIL_GENRE_SORT, Int.MIN_VALUE))
|
Sort.fromIntCode(inner.getInt(KEY_DETAIL_GENRE_SORT, Int.MIN_VALUE))
|
||||||
?: Sort.ByName(true)
|
?: Sort.ByName(true)
|
||||||
set(value) {
|
set(value) {
|
||||||
prefs.edit {
|
inner.edit {
|
||||||
putInt(KEY_DETAIL_GENRE_SORT, value.intCode)
|
putInt(KEY_DETAIL_GENRE_SORT, value.intCode)
|
||||||
apply()
|
apply()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,13 @@
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
app:startDestination="@id/home_fragment">
|
app:startDestination="@id/home_fragment">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Now, it would be quite cool if we could implement shared element transitions
|
||||||
|
between elements in this navigation web. Sadly though, the shared element transition
|
||||||
|
system is filled with so many bugs and visual errors to make this a terrible idea.
|
||||||
|
Just use the boring, yet sane and functional fade transitions.
|
||||||
|
-->
|
||||||
|
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/artist_detail_fragment"
|
android:id="@+id/artist_detail_fragment"
|
||||||
android:name="org.oxycblt.auxio.detail.ArtistDetailFragment"
|
android:name="org.oxycblt.auxio.detail.ArtistDetailFragment"
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@ if ndk_path is None or not os.path.isfile(os.path.join(ndk_path, "ndk-build")):
|
||||||
else:
|
else:
|
||||||
print(FATAL + "fatal:" + NC + " the android ndk was not installed at a " +
|
print(FATAL + "fatal:" + NC + " the android ndk was not installed at a " +
|
||||||
"recognized location.")
|
"recognized location.")
|
||||||
system.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
ndk_build_path = os.path.join(ndk_path, "ndk-build")
|
ndk_build_path = os.path.join(ndk_path, "ndk-build")
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue