settings: move impls out
Move settings implementations out of the class so that they are easier to manage.
This commit is contained in:
parent
866d20f6eb
commit
fb004a9e8b
8 changed files with 360 additions and 357 deletions
|
|
@ -41,38 +41,37 @@ interface HomeSettings : Settings<HomeSettings.Listener> {
|
||||||
fun onHideCollaboratorsChanged()
|
fun onHideCollaboratorsChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Real(context: Context) : Settings.Real<Listener>(context), HomeSettings {
|
|
||||||
override var homeTabs: Array<Tab>
|
|
||||||
get() =
|
|
||||||
Tab.fromIntCode(
|
|
||||||
sharedPreferences.getInt(
|
|
||||||
getString(R.string.set_key_home_tabs), Tab.SEQUENCE_DEFAULT))
|
|
||||||
?: unlikelyToBeNull(Tab.fromIntCode(Tab.SEQUENCE_DEFAULT))
|
|
||||||
set(value) {
|
|
||||||
sharedPreferences.edit {
|
|
||||||
putInt(getString(R.string.set_key_home_tabs), Tab.toIntCode(value))
|
|
||||||
apply()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override val shouldHideCollaborators: Boolean
|
|
||||||
get() =
|
|
||||||
sharedPreferences.getBoolean(getString(R.string.set_key_hide_collaborators), false)
|
|
||||||
|
|
||||||
override fun onSettingChanged(key: String, listener: Listener) {
|
|
||||||
when (key) {
|
|
||||||
getString(R.string.set_key_home_tabs) -> listener.onTabsChanged()
|
|
||||||
getString(R.string.set_key_hide_collaborators) ->
|
|
||||||
listener.onHideCollaboratorsChanged()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
* Get a framework-backed implementation.
|
* Get a framework-backed implementation.
|
||||||
* @param context [Context] required.
|
* @param context [Context] required.
|
||||||
*/
|
*/
|
||||||
fun from(context: Context): HomeSettings = Real(context)
|
fun from(context: Context): HomeSettings = RealHomeSettings(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RealHomeSettings(context: Context) :
|
||||||
|
Settings.Real<HomeSettings.Listener>(context), HomeSettings {
|
||||||
|
override var homeTabs: Array<Tab>
|
||||||
|
get() =
|
||||||
|
Tab.fromIntCode(
|
||||||
|
sharedPreferences.getInt(
|
||||||
|
getString(R.string.set_key_home_tabs), Tab.SEQUENCE_DEFAULT))
|
||||||
|
?: unlikelyToBeNull(Tab.fromIntCode(Tab.SEQUENCE_DEFAULT))
|
||||||
|
set(value) {
|
||||||
|
sharedPreferences.edit {
|
||||||
|
putInt(getString(R.string.set_key_home_tabs), Tab.toIntCode(value))
|
||||||
|
apply()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override val shouldHideCollaborators: Boolean
|
||||||
|
get() = sharedPreferences.getBoolean(getString(R.string.set_key_hide_collaborators), false)
|
||||||
|
|
||||||
|
override fun onSettingChanged(key: String, listener: HomeSettings.Listener) {
|
||||||
|
when (key) {
|
||||||
|
getString(R.string.set_key_home_tabs) -> listener.onTabsChanged()
|
||||||
|
getString(R.string.set_key_hide_collaborators) -> listener.onHideCollaboratorsChanged()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,52 +36,53 @@ interface ImageSettings : Settings<ImageSettings.Listener> {
|
||||||
fun onCoverModeChanged() {}
|
fun onCoverModeChanged() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Real(context: Context) : Settings.Real<Listener>(context), ImageSettings {
|
|
||||||
override val coverMode: CoverMode
|
|
||||||
get() =
|
|
||||||
CoverMode.fromIntCode(
|
|
||||||
sharedPreferences.getInt(getString(R.string.set_key_cover_mode), Int.MIN_VALUE))
|
|
||||||
?: CoverMode.MEDIA_STORE
|
|
||||||
|
|
||||||
override fun migrate() {
|
|
||||||
// Show album covers and Ignore MediaStore covers were unified in 3.0.0
|
|
||||||
if (sharedPreferences.contains(OLD_KEY_SHOW_COVERS) ||
|
|
||||||
sharedPreferences.contains(OLD_KEY_QUALITY_COVERS)) {
|
|
||||||
logD("Migrating cover settings")
|
|
||||||
|
|
||||||
val mode =
|
|
||||||
when {
|
|
||||||
!sharedPreferences.getBoolean(OLD_KEY_SHOW_COVERS, true) -> CoverMode.OFF
|
|
||||||
!sharedPreferences.getBoolean(OLD_KEY_QUALITY_COVERS, true) ->
|
|
||||||
CoverMode.MEDIA_STORE
|
|
||||||
else -> CoverMode.QUALITY
|
|
||||||
}
|
|
||||||
|
|
||||||
sharedPreferences.edit {
|
|
||||||
putInt(getString(R.string.set_key_cover_mode), mode.intCode)
|
|
||||||
remove(OLD_KEY_SHOW_COVERS)
|
|
||||||
remove(OLD_KEY_QUALITY_COVERS)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSettingChanged(key: String, listener: Listener) {
|
|
||||||
if (key == getString(R.string.set_key_cover_mode)) {
|
|
||||||
listOf(key, listener)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private companion object {
|
|
||||||
const val OLD_KEY_SHOW_COVERS = "KEY_SHOW_COVERS"
|
|
||||||
const val OLD_KEY_QUALITY_COVERS = "KEY_QUALITY_COVERS"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
* Get a framework-backed implementation.
|
* Get a framework-backed implementation.
|
||||||
* @param context [Context] required.
|
* @param context [Context] required.
|
||||||
*/
|
*/
|
||||||
fun from(context: Context): ImageSettings = Real(context)
|
fun from(context: Context): ImageSettings = RealImageSettings(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RealImageSettings(context: Context) :
|
||||||
|
Settings.Real<ImageSettings.Listener>(context), ImageSettings {
|
||||||
|
override val coverMode: CoverMode
|
||||||
|
get() =
|
||||||
|
CoverMode.fromIntCode(
|
||||||
|
sharedPreferences.getInt(getString(R.string.set_key_cover_mode), Int.MIN_VALUE))
|
||||||
|
?: CoverMode.MEDIA_STORE
|
||||||
|
|
||||||
|
override fun migrate() {
|
||||||
|
// Show album covers and Ignore MediaStore covers were unified in 3.0.0
|
||||||
|
if (sharedPreferences.contains(OLD_KEY_SHOW_COVERS) ||
|
||||||
|
sharedPreferences.contains(OLD_KEY_QUALITY_COVERS)) {
|
||||||
|
logD("Migrating cover settings")
|
||||||
|
|
||||||
|
val mode =
|
||||||
|
when {
|
||||||
|
!sharedPreferences.getBoolean(OLD_KEY_SHOW_COVERS, true) -> CoverMode.OFF
|
||||||
|
!sharedPreferences.getBoolean(OLD_KEY_QUALITY_COVERS, true) ->
|
||||||
|
CoverMode.MEDIA_STORE
|
||||||
|
else -> CoverMode.QUALITY
|
||||||
|
}
|
||||||
|
|
||||||
|
sharedPreferences.edit {
|
||||||
|
putInt(getString(R.string.set_key_cover_mode), mode.intCode)
|
||||||
|
remove(OLD_KEY_SHOW_COVERS)
|
||||||
|
remove(OLD_KEY_QUALITY_COVERS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSettingChanged(key: String, listener: ImageSettings.Listener) {
|
||||||
|
if (key == getString(R.string.set_key_cover_mode)) {
|
||||||
|
listener.onCoverModeChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
const val OLD_KEY_SHOW_COVERS = "KEY_SHOW_COVERS"
|
||||||
|
const val OLD_KEY_QUALITY_COVERS = "KEY_QUALITY_COVERS"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -62,163 +62,159 @@ interface MusicSettings : Settings<MusicSettings.Listener> {
|
||||||
fun onObservingChanged() {}
|
fun onObservingChanged() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Real(context: Context) : Settings.Real<Listener>(context), MusicSettings {
|
|
||||||
private val storageManager = context.getSystemServiceCompat(StorageManager::class)
|
|
||||||
|
|
||||||
override var musicDirs: MusicDirectories
|
|
||||||
get() {
|
|
||||||
val dirs =
|
|
||||||
(sharedPreferences.getStringSet(getString(R.string.set_key_music_dirs), null)
|
|
||||||
?: emptySet())
|
|
||||||
.mapNotNull { Directory.fromDocumentTreeUri(storageManager, it) }
|
|
||||||
return MusicDirectories(
|
|
||||||
dirs,
|
|
||||||
sharedPreferences.getBoolean(
|
|
||||||
getString(R.string.set_key_music_dirs_include), false))
|
|
||||||
}
|
|
||||||
set(value) {
|
|
||||||
sharedPreferences.edit {
|
|
||||||
putStringSet(
|
|
||||||
getString(R.string.set_key_music_dirs),
|
|
||||||
value.dirs.map(Directory::toDocumentTreeUri).toSet())
|
|
||||||
putBoolean(getString(R.string.set_key_music_dirs_include), value.shouldInclude)
|
|
||||||
apply()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override val excludeNonMusic: Boolean
|
|
||||||
get() =
|
|
||||||
sharedPreferences.getBoolean(getString(R.string.set_key_exclude_non_music), true)
|
|
||||||
|
|
||||||
override val shouldBeObserving: Boolean
|
|
||||||
get() = sharedPreferences.getBoolean(getString(R.string.set_key_observing), false)
|
|
||||||
|
|
||||||
override var multiValueSeparators: String
|
|
||||||
// Differ from convention and store a string of separator characters instead of an int
|
|
||||||
// code. This makes it easier to use and more extendable.
|
|
||||||
get() = sharedPreferences.getString(getString(R.string.set_key_separators), "") ?: ""
|
|
||||||
set(value) {
|
|
||||||
sharedPreferences.edit {
|
|
||||||
putString(getString(R.string.set_key_separators), value)
|
|
||||||
apply()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override var songSort: Sort
|
|
||||||
get() =
|
|
||||||
Sort.fromIntCode(
|
|
||||||
sharedPreferences.getInt(getString(R.string.set_key_songs_sort), Int.MIN_VALUE))
|
|
||||||
?: Sort(Sort.Mode.ByName, true)
|
|
||||||
set(value) {
|
|
||||||
sharedPreferences.edit {
|
|
||||||
putInt(getString(R.string.set_key_songs_sort), value.intCode)
|
|
||||||
apply()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override var albumSort: Sort
|
|
||||||
get() =
|
|
||||||
Sort.fromIntCode(
|
|
||||||
sharedPreferences.getInt(
|
|
||||||
getString(R.string.set_key_albums_sort), Int.MIN_VALUE))
|
|
||||||
?: Sort(Sort.Mode.ByName, true)
|
|
||||||
set(value) {
|
|
||||||
sharedPreferences.edit {
|
|
||||||
putInt(getString(R.string.set_key_albums_sort), value.intCode)
|
|
||||||
apply()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override var artistSort: Sort
|
|
||||||
get() =
|
|
||||||
Sort.fromIntCode(
|
|
||||||
sharedPreferences.getInt(
|
|
||||||
getString(R.string.set_key_artists_sort), Int.MIN_VALUE))
|
|
||||||
?: Sort(Sort.Mode.ByName, true)
|
|
||||||
set(value) {
|
|
||||||
sharedPreferences.edit {
|
|
||||||
putInt(getString(R.string.set_key_artists_sort), value.intCode)
|
|
||||||
apply()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override var genreSort: Sort
|
|
||||||
get() =
|
|
||||||
Sort.fromIntCode(
|
|
||||||
sharedPreferences.getInt(
|
|
||||||
getString(R.string.set_key_genres_sort), Int.MIN_VALUE))
|
|
||||||
?: Sort(Sort.Mode.ByName, true)
|
|
||||||
set(value) {
|
|
||||||
sharedPreferences.edit {
|
|
||||||
putInt(getString(R.string.set_key_genres_sort), value.intCode)
|
|
||||||
apply()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override var albumSongSort: Sort
|
|
||||||
get() {
|
|
||||||
var sort =
|
|
||||||
Sort.fromIntCode(
|
|
||||||
sharedPreferences.getInt(
|
|
||||||
getString(R.string.set_key_album_songs_sort), Int.MIN_VALUE))
|
|
||||||
?: Sort(Sort.Mode.ByDisc, true)
|
|
||||||
|
|
||||||
// Correct legacy album sort modes to Disc
|
|
||||||
if (sort.mode is Sort.Mode.ByName) {
|
|
||||||
sort = sort.withMode(Sort.Mode.ByDisc)
|
|
||||||
}
|
|
||||||
|
|
||||||
return sort
|
|
||||||
}
|
|
||||||
set(value) {
|
|
||||||
sharedPreferences.edit {
|
|
||||||
putInt(getString(R.string.set_key_album_songs_sort), value.intCode)
|
|
||||||
apply()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override var artistSongSort: Sort
|
|
||||||
get() =
|
|
||||||
Sort.fromIntCode(
|
|
||||||
sharedPreferences.getInt(
|
|
||||||
getString(R.string.set_key_artist_songs_sort), Int.MIN_VALUE))
|
|
||||||
?: Sort(Sort.Mode.ByDate, false)
|
|
||||||
set(value) {
|
|
||||||
sharedPreferences.edit {
|
|
||||||
putInt(getString(R.string.set_key_artist_songs_sort), value.intCode)
|
|
||||||
apply()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override var genreSongSort: Sort
|
|
||||||
get() =
|
|
||||||
Sort.fromIntCode(
|
|
||||||
sharedPreferences.getInt(
|
|
||||||
getString(R.string.set_key_genre_songs_sort), Int.MIN_VALUE))
|
|
||||||
?: Sort(Sort.Mode.ByName, true)
|
|
||||||
set(value) {
|
|
||||||
sharedPreferences.edit {
|
|
||||||
putInt(getString(R.string.set_key_genre_songs_sort), value.intCode)
|
|
||||||
apply()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSettingChanged(key: String, listener: Listener) {
|
|
||||||
when (key) {
|
|
||||||
getString(R.string.set_key_exclude_non_music),
|
|
||||||
getString(R.string.set_key_music_dirs),
|
|
||||||
getString(R.string.set_key_music_dirs_include),
|
|
||||||
getString(R.string.set_key_separators) -> listener.onIndexingSettingChanged()
|
|
||||||
getString(R.string.set_key_observing) -> listener.onObservingChanged()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
* Get a framework-backed implementation.
|
* Get a framework-backed implementation.
|
||||||
* @param context [Context] required.
|
* @param context [Context] required.
|
||||||
*/
|
*/
|
||||||
fun from(context: Context): MusicSettings = Real(context)
|
fun from(context: Context): MusicSettings = RealMusicSettings(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RealMusicSettings(context: Context) :
|
||||||
|
Settings.Real<MusicSettings.Listener>(context), MusicSettings {
|
||||||
|
private val storageManager = context.getSystemServiceCompat(StorageManager::class)
|
||||||
|
|
||||||
|
override var musicDirs: MusicDirectories
|
||||||
|
get() {
|
||||||
|
val dirs =
|
||||||
|
(sharedPreferences.getStringSet(getString(R.string.set_key_music_dirs), null)
|
||||||
|
?: emptySet())
|
||||||
|
.mapNotNull { Directory.fromDocumentTreeUri(storageManager, it) }
|
||||||
|
return MusicDirectories(
|
||||||
|
dirs,
|
||||||
|
sharedPreferences.getBoolean(getString(R.string.set_key_music_dirs_include), false))
|
||||||
|
}
|
||||||
|
set(value) {
|
||||||
|
sharedPreferences.edit {
|
||||||
|
putStringSet(
|
||||||
|
getString(R.string.set_key_music_dirs),
|
||||||
|
value.dirs.map(Directory::toDocumentTreeUri).toSet())
|
||||||
|
putBoolean(getString(R.string.set_key_music_dirs_include), value.shouldInclude)
|
||||||
|
apply()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override val excludeNonMusic: Boolean
|
||||||
|
get() = sharedPreferences.getBoolean(getString(R.string.set_key_exclude_non_music), true)
|
||||||
|
|
||||||
|
override val shouldBeObserving: Boolean
|
||||||
|
get() = sharedPreferences.getBoolean(getString(R.string.set_key_observing), false)
|
||||||
|
|
||||||
|
override var multiValueSeparators: String
|
||||||
|
// Differ from convention and store a string of separator characters instead of an int
|
||||||
|
// code. This makes it easier to use and more extendable.
|
||||||
|
get() = sharedPreferences.getString(getString(R.string.set_key_separators), "") ?: ""
|
||||||
|
set(value) {
|
||||||
|
sharedPreferences.edit {
|
||||||
|
putString(getString(R.string.set_key_separators), value)
|
||||||
|
apply()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override var songSort: Sort
|
||||||
|
get() =
|
||||||
|
Sort.fromIntCode(
|
||||||
|
sharedPreferences.getInt(getString(R.string.set_key_songs_sort), Int.MIN_VALUE))
|
||||||
|
?: Sort(Sort.Mode.ByName, true)
|
||||||
|
set(value) {
|
||||||
|
sharedPreferences.edit {
|
||||||
|
putInt(getString(R.string.set_key_songs_sort), value.intCode)
|
||||||
|
apply()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override var albumSort: Sort
|
||||||
|
get() =
|
||||||
|
Sort.fromIntCode(
|
||||||
|
sharedPreferences.getInt(getString(R.string.set_key_albums_sort), Int.MIN_VALUE))
|
||||||
|
?: Sort(Sort.Mode.ByName, true)
|
||||||
|
set(value) {
|
||||||
|
sharedPreferences.edit {
|
||||||
|
putInt(getString(R.string.set_key_albums_sort), value.intCode)
|
||||||
|
apply()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override var artistSort: Sort
|
||||||
|
get() =
|
||||||
|
Sort.fromIntCode(
|
||||||
|
sharedPreferences.getInt(getString(R.string.set_key_artists_sort), Int.MIN_VALUE))
|
||||||
|
?: Sort(Sort.Mode.ByName, true)
|
||||||
|
set(value) {
|
||||||
|
sharedPreferences.edit {
|
||||||
|
putInt(getString(R.string.set_key_artists_sort), value.intCode)
|
||||||
|
apply()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override var genreSort: Sort
|
||||||
|
get() =
|
||||||
|
Sort.fromIntCode(
|
||||||
|
sharedPreferences.getInt(getString(R.string.set_key_genres_sort), Int.MIN_VALUE))
|
||||||
|
?: Sort(Sort.Mode.ByName, true)
|
||||||
|
set(value) {
|
||||||
|
sharedPreferences.edit {
|
||||||
|
putInt(getString(R.string.set_key_genres_sort), value.intCode)
|
||||||
|
apply()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override var albumSongSort: Sort
|
||||||
|
get() {
|
||||||
|
var sort =
|
||||||
|
Sort.fromIntCode(
|
||||||
|
sharedPreferences.getInt(
|
||||||
|
getString(R.string.set_key_album_songs_sort), Int.MIN_VALUE))
|
||||||
|
?: Sort(Sort.Mode.ByDisc, true)
|
||||||
|
|
||||||
|
// Correct legacy album sort modes to Disc
|
||||||
|
if (sort.mode is Sort.Mode.ByName) {
|
||||||
|
sort = sort.withMode(Sort.Mode.ByDisc)
|
||||||
|
}
|
||||||
|
|
||||||
|
return sort
|
||||||
|
}
|
||||||
|
set(value) {
|
||||||
|
sharedPreferences.edit {
|
||||||
|
putInt(getString(R.string.set_key_album_songs_sort), value.intCode)
|
||||||
|
apply()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override var artistSongSort: Sort
|
||||||
|
get() =
|
||||||
|
Sort.fromIntCode(
|
||||||
|
sharedPreferences.getInt(
|
||||||
|
getString(R.string.set_key_artist_songs_sort), Int.MIN_VALUE))
|
||||||
|
?: Sort(Sort.Mode.ByDate, false)
|
||||||
|
set(value) {
|
||||||
|
sharedPreferences.edit {
|
||||||
|
putInt(getString(R.string.set_key_artist_songs_sort), value.intCode)
|
||||||
|
apply()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override var genreSongSort: Sort
|
||||||
|
get() =
|
||||||
|
Sort.fromIntCode(
|
||||||
|
sharedPreferences.getInt(
|
||||||
|
getString(R.string.set_key_genre_songs_sort), Int.MIN_VALUE))
|
||||||
|
?: Sort(Sort.Mode.ByName, true)
|
||||||
|
set(value) {
|
||||||
|
sharedPreferences.edit {
|
||||||
|
putInt(getString(R.string.set_key_genre_songs_sort), value.intCode)
|
||||||
|
apply()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSettingChanged(key: String, listener: MusicSettings.Listener) {
|
||||||
|
when (key) {
|
||||||
|
getString(R.string.set_key_exclude_non_music),
|
||||||
|
getString(R.string.set_key_music_dirs),
|
||||||
|
getString(R.string.set_key_music_dirs_include),
|
||||||
|
getString(R.string.set_key_separators) -> listener.onIndexingSettingChanged()
|
||||||
|
getString(R.string.set_key_observing) -> listener.onObservingChanged()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ class Directory private constructor(val volume: StorageVolume, val relativePath:
|
||||||
/**
|
/**
|
||||||
* Converts this [Directory] instance into an opaque document tree path. This is a huge
|
* Converts this [Directory] instance into an opaque document tree path. This is a huge
|
||||||
* violation of the document tree URI contract, but it's also the only one can sensibly work
|
* violation of the document tree URI contract, but it's also the only one can sensibly work
|
||||||
* with these uris in the UI, and it doesn't exactly matter since we never write or read
|
* with these uris in the UI, and it doesn't exactly matter since we never write or read to
|
||||||
* directory.
|
* directory.
|
||||||
* @return A URI [String] abiding by the document tree specification, or null if the [Directory]
|
* @return A URI [String] abiding by the document tree specification, or null if the [Directory]
|
||||||
* is not valid.
|
* is not valid.
|
||||||
|
|
|
||||||
|
|
@ -52,72 +52,71 @@ interface SearchEngine {
|
||||||
val genres: List<Genre>?
|
val genres: List<Genre>?
|
||||||
)
|
)
|
||||||
|
|
||||||
private class Real(private val context: Context) : SearchEngine {
|
|
||||||
override suspend fun search(items: Items, query: String) =
|
|
||||||
Items(
|
|
||||||
songs =
|
|
||||||
items.songs?.searchListImpl(query) { q, song -> song.path.name.contains(q) },
|
|
||||||
albums = items.albums?.searchListImpl(query),
|
|
||||||
artists = items.artists?.searchListImpl(query),
|
|
||||||
genres = items.genres?.searchListImpl(query))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Search a given [Music] list.
|
|
||||||
* @param query The query to search for. The routine will compare this query to the names of
|
|
||||||
* each object in the list and
|
|
||||||
* @param fallback Additional comparison code to run if the item does not match the query
|
|
||||||
* initially. This can be used to compare against additional attributes to improve search
|
|
||||||
* result quality.
|
|
||||||
*/
|
|
||||||
private inline fun <T : Music> List<T>.searchListImpl(
|
|
||||||
query: String,
|
|
||||||
fallback: (String, T) -> Boolean = { _, _ -> false }
|
|
||||||
) =
|
|
||||||
filter {
|
|
||||||
// See if the plain resolved name matches the query. This works for most
|
|
||||||
// situations.
|
|
||||||
val name = it.resolveName(context)
|
|
||||||
if (name.contains(query, ignoreCase = true)) {
|
|
||||||
return@filter true
|
|
||||||
}
|
|
||||||
|
|
||||||
// See if the sort name matches. This can sometimes be helpful as certain
|
|
||||||
// libraries
|
|
||||||
// will tag sort names to have a alphabetized version of the title.
|
|
||||||
val sortName = it.rawSortName
|
|
||||||
if (sortName != null && sortName.contains(query, ignoreCase = true)) {
|
|
||||||
return@filter true
|
|
||||||
}
|
|
||||||
|
|
||||||
// As a last-ditch effort, see if the normalized name matches. This will replace
|
|
||||||
// any non-alphabetical characters with their alphabetical representations,
|
|
||||||
// which
|
|
||||||
// could make it match the query.
|
|
||||||
val normalizedName =
|
|
||||||
NORMALIZATION_SANITIZE_REGEX.replace(
|
|
||||||
Normalizer.normalize(name, Normalizer.Form.NFKD), "")
|
|
||||||
if (normalizedName.contains(query, ignoreCase = true)) {
|
|
||||||
return@filter true
|
|
||||||
}
|
|
||||||
|
|
||||||
fallback(query, it)
|
|
||||||
}
|
|
||||||
.ifEmpty { null }
|
|
||||||
|
|
||||||
private companion object {
|
|
||||||
/**
|
|
||||||
* Converts the output of [Normalizer] to remove any junk characters added by it's
|
|
||||||
* replacements.
|
|
||||||
*/
|
|
||||||
val NORMALIZATION_SANITIZE_REGEX = Regex("\\p{InCombiningDiacriticalMarks}+")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
* Get a framework-backed implementation.
|
* Get a framework-backed implementation.
|
||||||
* @param context [Context] required.
|
* @param context [Context] required.
|
||||||
*/
|
*/
|
||||||
fun from(context: Context): SearchEngine = Real(context)
|
fun from(context: Context): SearchEngine = RealSearchEngine(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RealSearchEngine(private val context: Context) : SearchEngine {
|
||||||
|
override suspend fun search(items: SearchEngine.Items, query: String) =
|
||||||
|
SearchEngine.Items(
|
||||||
|
songs = items.songs?.searchListImpl(query) { q, song -> song.path.name.contains(q) },
|
||||||
|
albums = items.albums?.searchListImpl(query),
|
||||||
|
artists = items.artists?.searchListImpl(query),
|
||||||
|
genres = items.genres?.searchListImpl(query))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search a given [Music] list.
|
||||||
|
* @param query The query to search for. The routine will compare this query to the names of
|
||||||
|
* each object in the list and
|
||||||
|
* @param fallback Additional comparison code to run if the item does not match the query
|
||||||
|
* initially. This can be used to compare against additional attributes to improve search result
|
||||||
|
* quality.
|
||||||
|
*/
|
||||||
|
private inline fun <T : Music> List<T>.searchListImpl(
|
||||||
|
query: String,
|
||||||
|
fallback: (String, T) -> Boolean = { _, _ -> false }
|
||||||
|
) =
|
||||||
|
filter {
|
||||||
|
// See if the plain resolved name matches the query. This works for most
|
||||||
|
// situations.
|
||||||
|
val name = it.resolveName(context)
|
||||||
|
if (name.contains(query, ignoreCase = true)) {
|
||||||
|
return@filter true
|
||||||
|
}
|
||||||
|
|
||||||
|
// See if the sort name matches. This can sometimes be helpful as certain
|
||||||
|
// libraries
|
||||||
|
// will tag sort names to have a alphabetized version of the title.
|
||||||
|
val sortName = it.rawSortName
|
||||||
|
if (sortName != null && sortName.contains(query, ignoreCase = true)) {
|
||||||
|
return@filter true
|
||||||
|
}
|
||||||
|
|
||||||
|
// As a last-ditch effort, see if the normalized name matches. This will replace
|
||||||
|
// any non-alphabetical characters with their alphabetical representations,
|
||||||
|
// which
|
||||||
|
// could make it match the query.
|
||||||
|
val normalizedName =
|
||||||
|
NORMALIZATION_SANITIZE_REGEX.replace(
|
||||||
|
Normalizer.normalize(name, Normalizer.Form.NFKD), "")
|
||||||
|
if (normalizedName.contains(query, ignoreCase = true)) {
|
||||||
|
return@filter true
|
||||||
|
}
|
||||||
|
|
||||||
|
fallback(query, it)
|
||||||
|
}
|
||||||
|
.ifEmpty { null }
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
/**
|
||||||
|
* Converts the output of [Normalizer] to remove any junk characters added by it's
|
||||||
|
* replacements.
|
||||||
|
*/
|
||||||
|
val NORMALIZATION_SANITIZE_REGEX = Regex("\\p{InCombiningDiacriticalMarks}+")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,26 +31,25 @@ interface SearchSettings : Settings<Nothing> {
|
||||||
/** The type of Music the search view is currently filtering to. */
|
/** The type of Music the search view is currently filtering to. */
|
||||||
var searchFilterMode: MusicMode?
|
var searchFilterMode: MusicMode?
|
||||||
|
|
||||||
private class Real(context: Context) : Settings.Real<Nothing>(context), SearchSettings {
|
|
||||||
override var searchFilterMode: MusicMode?
|
|
||||||
get() =
|
|
||||||
MusicMode.fromIntCode(
|
|
||||||
sharedPreferences.getInt(
|
|
||||||
getString(R.string.set_key_search_filter), Int.MIN_VALUE))
|
|
||||||
set(value) {
|
|
||||||
sharedPreferences.edit {
|
|
||||||
putInt(
|
|
||||||
getString(R.string.set_key_search_filter), value?.intCode ?: Int.MIN_VALUE)
|
|
||||||
apply()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
* Get a framework-backed implementation.
|
* Get a framework-backed implementation.
|
||||||
* @param context [Context] required.
|
* @param context [Context] required.
|
||||||
*/
|
*/
|
||||||
fun from(context: Context): SearchSettings = Real(context)
|
fun from(context: Context): SearchSettings = RealSearchSettings(context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class RealSearchSettings(context: Context) :
|
||||||
|
Settings.Real<Nothing>(context), SearchSettings {
|
||||||
|
override var searchFilterMode: MusicMode?
|
||||||
|
get() =
|
||||||
|
MusicMode.fromIntCode(
|
||||||
|
sharedPreferences.getInt(getString(R.string.set_key_search_filter), Int.MIN_VALUE))
|
||||||
|
set(value) {
|
||||||
|
sharedPreferences.edit {
|
||||||
|
putInt(getString(R.string.set_key_search_filter), value?.intCode ?: Int.MIN_VALUE)
|
||||||
|
apply()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,65 +45,66 @@ interface UISettings : Settings<UISettings.Listener> {
|
||||||
fun onRoundModeChanged()
|
fun onRoundModeChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Real(context: Context) : Settings.Real<Listener>(context), UISettings {
|
|
||||||
override val theme: Int
|
|
||||||
get() =
|
|
||||||
sharedPreferences.getInt(
|
|
||||||
getString(R.string.set_key_theme), AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
|
||||||
|
|
||||||
override val useBlackTheme: Boolean
|
|
||||||
get() = sharedPreferences.getBoolean(getString(R.string.set_key_black_theme), false)
|
|
||||||
|
|
||||||
override var accent: Accent
|
|
||||||
get() =
|
|
||||||
Accent.from(
|
|
||||||
sharedPreferences.getInt(getString(R.string.set_key_accent), Accent.DEFAULT))
|
|
||||||
set(value) {
|
|
||||||
sharedPreferences.edit {
|
|
||||||
putInt(getString(R.string.set_key_accent), value.index)
|
|
||||||
apply()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override val roundMode: Boolean
|
|
||||||
get() = sharedPreferences.getBoolean(getString(R.string.set_key_round_mode), false)
|
|
||||||
|
|
||||||
override fun migrate() {
|
|
||||||
if (sharedPreferences.contains(OLD_KEY_ACCENT3)) {
|
|
||||||
logD("Migrating $OLD_KEY_ACCENT3")
|
|
||||||
|
|
||||||
var accent = sharedPreferences.getInt(OLD_KEY_ACCENT3, 5)
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
|
||||||
// Accents were previously frozen as soon as the OS was updated to android
|
|
||||||
// twelve, as dynamic colors were enabled by default. This is no longer the
|
|
||||||
// case, so we need to re-update the setting to dynamic colors here.
|
|
||||||
accent = 16
|
|
||||||
}
|
|
||||||
|
|
||||||
sharedPreferences.edit {
|
|
||||||
putInt(getString(R.string.set_key_accent), accent)
|
|
||||||
remove(OLD_KEY_ACCENT3)
|
|
||||||
apply()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSettingChanged(key: String, listener: Listener) {
|
|
||||||
if (key == getString(R.string.set_key_round_mode)) {
|
|
||||||
listener.onRoundModeChanged()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private companion object {
|
|
||||||
const val OLD_KEY_ACCENT3 = "auxio_accent"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
* Get a framework-backed implementation.
|
* Get a framework-backed implementation.
|
||||||
* @param context [Context] required.
|
* @param context [Context] required.
|
||||||
*/
|
*/
|
||||||
fun from(context: Context): UISettings = Real(context)
|
fun from(context: Context): UISettings = RealUISettings(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RealUISettings(context: Context) :
|
||||||
|
Settings.Real<UISettings.Listener>(context), UISettings {
|
||||||
|
override val theme: Int
|
||||||
|
get() =
|
||||||
|
sharedPreferences.getInt(
|
||||||
|
getString(R.string.set_key_theme), AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
||||||
|
|
||||||
|
override val useBlackTheme: Boolean
|
||||||
|
get() = sharedPreferences.getBoolean(getString(R.string.set_key_black_theme), false)
|
||||||
|
|
||||||
|
override var accent: Accent
|
||||||
|
get() =
|
||||||
|
Accent.from(
|
||||||
|
sharedPreferences.getInt(getString(R.string.set_key_accent), Accent.DEFAULT))
|
||||||
|
set(value) {
|
||||||
|
sharedPreferences.edit {
|
||||||
|
putInt(getString(R.string.set_key_accent), value.index)
|
||||||
|
apply()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override val roundMode: Boolean
|
||||||
|
get() = sharedPreferences.getBoolean(getString(R.string.set_key_round_mode), false)
|
||||||
|
|
||||||
|
override fun migrate() {
|
||||||
|
if (sharedPreferences.contains(OLD_KEY_ACCENT3)) {
|
||||||
|
logD("Migrating $OLD_KEY_ACCENT3")
|
||||||
|
|
||||||
|
var accent = sharedPreferences.getInt(OLD_KEY_ACCENT3, 5)
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
// Accents were previously frozen as soon as the OS was updated to android
|
||||||
|
// twelve, as dynamic colors were enabled by default. This is no longer the
|
||||||
|
// case, so we need to re-update the setting to dynamic colors here.
|
||||||
|
accent = 16
|
||||||
|
}
|
||||||
|
|
||||||
|
sharedPreferences.edit {
|
||||||
|
putInt(getString(R.string.set_key_accent), accent)
|
||||||
|
remove(OLD_KEY_ACCENT3)
|
||||||
|
apply()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSettingChanged(key: String, listener: UISettings.Listener) {
|
||||||
|
if (key == getString(R.string.set_key_round_mode)) {
|
||||||
|
listener.onRoundModeChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
const val OLD_KEY_ACCENT3 = "auxio_accent"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,18 @@
|
||||||
|
|
||||||
package org.oxycblt.auxio.music.tags
|
package org.oxycblt.auxio.music.tags
|
||||||
|
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Assert.assertTrue
|
import org.junit.Assert.assertTrue
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
class DiscTest {
|
class DiscTest {
|
||||||
|
@Test
|
||||||
|
fun disc_compare() {
|
||||||
|
val a = Disc(1, "Part I")
|
||||||
|
val b = Disc(2, "Part II")
|
||||||
|
assertEquals(-1, a.compareTo(b))
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun disc_equals_correct() {
|
fun disc_equals_correct() {
|
||||||
val a = Disc(1, "Part I")
|
val a = Disc(1, "Part I")
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue