music: replace loading with indexing
Replace all in-code instances of music loading with music indexing, primarily for clarity.
This commit is contained in:
parent
fb3c32b14c
commit
0a18883a6a
26 changed files with 152 additions and 147 deletions
|
|
@ -53,6 +53,7 @@ import org.oxycblt.auxio.ui.DisplayMode
|
||||||
import org.oxycblt.auxio.ui.MainNavigationAction
|
import org.oxycblt.auxio.ui.MainNavigationAction
|
||||||
import org.oxycblt.auxio.ui.NavigationViewModel
|
import org.oxycblt.auxio.ui.NavigationViewModel
|
||||||
import org.oxycblt.auxio.ui.ViewBindingFragment
|
import org.oxycblt.auxio.ui.ViewBindingFragment
|
||||||
|
import org.oxycblt.auxio.util.getColorStateListSafe
|
||||||
import org.oxycblt.auxio.util.launch
|
import org.oxycblt.auxio.util.launch
|
||||||
import org.oxycblt.auxio.util.logD
|
import org.oxycblt.auxio.util.logD
|
||||||
import org.oxycblt.auxio.util.logE
|
import org.oxycblt.auxio.util.logE
|
||||||
|
|
@ -92,11 +93,16 @@ class HomeFragment : ViewBindingFragment<FragmentHomeBinding>(), Toolbar.OnMenuI
|
||||||
|
|
||||||
updateTabConfiguration()
|
updateTabConfiguration()
|
||||||
|
|
||||||
binding.homeLoadingContainer.setOnApplyWindowInsetsListener { view, insets ->
|
binding.homeIndexingContainer.setOnApplyWindowInsetsListener { view, insets ->
|
||||||
view.updatePadding(bottom = insets.systemBarInsetsCompat.bottom)
|
view.updatePadding(bottom = insets.systemBarInsetsCompat.bottom)
|
||||||
insets
|
insets
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load the track color in manually as it's unclear whether the track actually supports
|
||||||
|
// using a ColorStateList in the resources
|
||||||
|
binding.homeIndexingProgress.trackColor =
|
||||||
|
requireContext().getColorStateListSafe(R.color.sel_track).defaultColor
|
||||||
|
|
||||||
binding.homePager.apply {
|
binding.homePager.apply {
|
||||||
adapter = HomePagerAdapter()
|
adapter = HomePagerAdapter()
|
||||||
|
|
||||||
|
|
@ -262,41 +268,41 @@ class HomeFragment : ViewBindingFragment<FragmentHomeBinding>(), Toolbar.OnMenuI
|
||||||
val binding = requireBinding()
|
val binding = requireBinding()
|
||||||
|
|
||||||
when (state) {
|
when (state) {
|
||||||
is Indexer.State.Complete -> handleLoaderResponse(binding, state.response)
|
is Indexer.State.Complete -> handleIndexerResponse(binding, state.response)
|
||||||
is Indexer.State.Loading -> handleLoadingState(binding, state.loading)
|
is Indexer.State.Indexing -> handleIndexingState(binding, state.indexing)
|
||||||
null -> {
|
null -> {
|
||||||
logW("Loading is in indeterminate state, doing nothing")
|
logW("Indexer is in indeterminate state, doing nothing")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleLoaderResponse(binding: FragmentHomeBinding, response: Indexer.Response) {
|
private fun handleIndexerResponse(binding: FragmentHomeBinding, response: Indexer.Response) {
|
||||||
if (response is Indexer.Response.Ok) {
|
if (response is Indexer.Response.Ok) {
|
||||||
binding.homeFab.show()
|
binding.homeFab.show()
|
||||||
binding.homeLoadingContainer.visibility = View.INVISIBLE
|
binding.homeIndexingContainer.visibility = View.INVISIBLE
|
||||||
binding.homePager.visibility = View.VISIBLE
|
binding.homePager.visibility = View.VISIBLE
|
||||||
} else {
|
} else {
|
||||||
binding.homeFab.hide()
|
binding.homeFab.hide()
|
||||||
binding.homePager.visibility = View.INVISIBLE
|
binding.homePager.visibility = View.INVISIBLE
|
||||||
binding.homeLoadingContainer.visibility = View.VISIBLE
|
binding.homeIndexingContainer.visibility = View.VISIBLE
|
||||||
|
|
||||||
logD("Received non-ok response $response")
|
logD("Received non-ok response $response")
|
||||||
|
|
||||||
when (response) {
|
when (response) {
|
||||||
is Indexer.Response.Ok -> error("Unreachable")
|
is Indexer.Response.Ok -> error("Unreachable")
|
||||||
is Indexer.Response.Err -> {
|
is Indexer.Response.Err -> {
|
||||||
binding.homeLoadingProgress.visibility = View.INVISIBLE
|
binding.homeIndexingProgress.visibility = View.INVISIBLE
|
||||||
binding.homeLoadingStatus.textSafe = getString(R.string.err_load_failed)
|
binding.homeIndexingStatus.textSafe = getString(R.string.err_index_failed)
|
||||||
binding.homeLoadingAction.apply {
|
binding.homeIndexingAction.apply {
|
||||||
visibility = View.VISIBLE
|
visibility = View.VISIBLE
|
||||||
text = getString(R.string.lbl_retry)
|
text = getString(R.string.lbl_retry)
|
||||||
setOnClickListener { indexerModel.reindex() }
|
setOnClickListener { indexerModel.reindex() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is Indexer.Response.NoMusic -> {
|
is Indexer.Response.NoMusic -> {
|
||||||
binding.homeLoadingProgress.visibility = View.INVISIBLE
|
binding.homeIndexingProgress.visibility = View.INVISIBLE
|
||||||
binding.homeLoadingStatus.textSafe = getString(R.string.err_no_music)
|
binding.homeIndexingStatus.textSafe = getString(R.string.err_no_music)
|
||||||
binding.homeLoadingAction.apply {
|
binding.homeIndexingAction.apply {
|
||||||
visibility = View.VISIBLE
|
visibility = View.VISIBLE
|
||||||
text = getString(R.string.lbl_retry)
|
text = getString(R.string.lbl_retry)
|
||||||
setOnClickListener { indexerModel.reindex() }
|
setOnClickListener { indexerModel.reindex() }
|
||||||
|
|
@ -308,9 +314,9 @@ class HomeFragment : ViewBindingFragment<FragmentHomeBinding>(), Toolbar.OnMenuI
|
||||||
"Cannot access permission launcher while detached"
|
"Cannot access permission launcher while detached"
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.homeLoadingProgress.visibility = View.INVISIBLE
|
binding.homeIndexingProgress.visibility = View.INVISIBLE
|
||||||
binding.homeLoadingStatus.textSafe = getString(R.string.err_no_perms)
|
binding.homeIndexingStatus.textSafe = getString(R.string.err_no_perms)
|
||||||
binding.homeLoadingAction.apply {
|
binding.homeIndexingAction.apply {
|
||||||
visibility = View.VISIBLE
|
visibility = View.VISIBLE
|
||||||
text = getString(R.string.lbl_grant)
|
text = getString(R.string.lbl_grant)
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
|
|
@ -322,25 +328,25 @@ class HomeFragment : ViewBindingFragment<FragmentHomeBinding>(), Toolbar.OnMenuI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleLoadingState(binding: FragmentHomeBinding, loading: Indexer.Loading) {
|
private fun handleIndexingState(binding: FragmentHomeBinding, indexing: Indexer.Indexing) {
|
||||||
binding.homeFab.hide()
|
binding.homeFab.hide()
|
||||||
binding.homePager.visibility = View.INVISIBLE
|
binding.homePager.visibility = View.INVISIBLE
|
||||||
binding.homeLoadingContainer.visibility = View.VISIBLE
|
binding.homeIndexingContainer.visibility = View.VISIBLE
|
||||||
binding.homeLoadingProgress.visibility = View.VISIBLE
|
binding.homeIndexingProgress.visibility = View.VISIBLE
|
||||||
binding.homeLoadingAction.visibility = View.INVISIBLE
|
binding.homeIndexingAction.visibility = View.INVISIBLE
|
||||||
|
|
||||||
when (loading) {
|
when (indexing) {
|
||||||
is Indexer.Loading.Indeterminate -> {
|
is Indexer.Indexing.Indeterminate -> {
|
||||||
binding.homeLoadingStatus.textSafe = getString(R.string.lbl_loading)
|
binding.homeIndexingStatus.textSafe = getString(R.string.lbl_indexing)
|
||||||
binding.homeLoadingProgress.isIndeterminate = true
|
binding.homeIndexingProgress.isIndeterminate = true
|
||||||
}
|
}
|
||||||
is Indexer.Loading.Songs -> {
|
is Indexer.Indexing.Songs -> {
|
||||||
binding.homeLoadingStatus.textSafe =
|
binding.homeIndexingStatus.textSafe =
|
||||||
getString(R.string.fmt_indexing, loading.current, loading.total)
|
getString(R.string.fmt_indexing, indexing.current, indexing.total)
|
||||||
binding.homeLoadingProgress.apply {
|
binding.homeIndexingProgress.apply {
|
||||||
isIndeterminate = false
|
isIndeterminate = false
|
||||||
max = loading.total
|
max = indexing.total
|
||||||
progress = loading.current
|
progress = indexing.current
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ import org.oxycblt.auxio.util.logE
|
||||||
*/
|
*/
|
||||||
class Indexer {
|
class Indexer {
|
||||||
private var lastResponse: Response? = null
|
private var lastResponse: Response? = null
|
||||||
private var loadingState: Loading? = null
|
private var indexingState: Indexing? = null
|
||||||
|
|
||||||
private var currentGeneration = 0L
|
private var currentGeneration = 0L
|
||||||
private val callbacks = mutableListOf<Callback>()
|
private val callbacks = mutableListOf<Callback>()
|
||||||
|
|
@ -66,11 +66,11 @@ class Indexer {
|
||||||
* loaded, yet no loading is going on.
|
* loaded, yet no loading is going on.
|
||||||
*/
|
*/
|
||||||
val isIndeterminate: Boolean
|
val isIndeterminate: Boolean
|
||||||
get() = lastResponse == null && loadingState == null
|
get() = lastResponse == null && indexingState == null
|
||||||
|
|
||||||
fun addCallback(callback: Callback) {
|
fun addCallback(callback: Callback) {
|
||||||
val currentState =
|
val currentState =
|
||||||
loadingState?.let { State.Loading(it) } ?: lastResponse?.let { State.Complete(it) }
|
indexingState?.let { State.Indexing(it) } ?: lastResponse?.let { State.Complete(it) }
|
||||||
|
|
||||||
callback.onIndexerStateChanged(currentState)
|
callback.onIndexerStateChanged(currentState)
|
||||||
callbacks.add(callback)
|
callbacks.add(callback)
|
||||||
|
|
@ -98,7 +98,7 @@ class Indexer {
|
||||||
val library = withContext(Dispatchers.IO) { indexImpl(context, generation) }
|
val library = withContext(Dispatchers.IO) { indexImpl(context, generation) }
|
||||||
if (library != null) {
|
if (library != null) {
|
||||||
logD(
|
logD(
|
||||||
"Music load completed successfully in " +
|
"Music indexing completed successfully in " +
|
||||||
"${System.currentTimeMillis() - start}ms")
|
"${System.currentTimeMillis() - start}ms")
|
||||||
Response.Ok(library)
|
Response.Ok(library)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -106,7 +106,7 @@ class Indexer {
|
||||||
Response.NoMusic
|
Response.NoMusic
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logE("Music loading failed.")
|
logE("Music indexing failed.")
|
||||||
logE(e.stackTraceToString())
|
logE(e.stackTraceToString())
|
||||||
Response.Err(e)
|
Response.Err(e)
|
||||||
}
|
}
|
||||||
|
|
@ -133,22 +133,22 @@ class Indexer {
|
||||||
fun cancelLast() {
|
fun cancelLast() {
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
currentGeneration++
|
currentGeneration++
|
||||||
emitLoading(null, currentGeneration)
|
emitIndexing(null, currentGeneration)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun emitLoading(loading: Loading?, generation: Long) {
|
private fun emitIndexing(indexing: Indexing?, generation: Long) {
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
if (currentGeneration != generation) {
|
if (currentGeneration != generation) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
loadingState = loading
|
indexingState = indexing
|
||||||
|
|
||||||
// If we have canceled the loading process, we want to revert to a previous completion
|
// If we have canceled the loading process, we want to revert to a previous completion
|
||||||
// whenever possible to prevent state inconsistency.
|
// whenever possible to prevent state inconsistency.
|
||||||
val state =
|
val state =
|
||||||
loadingState?.let { State.Loading(it) } ?: lastResponse?.let { State.Complete(it) }
|
indexingState?.let { State.Indexing(it) } ?: lastResponse?.let { State.Complete(it) }
|
||||||
|
|
||||||
for (callback in callbacks) {
|
for (callback in callbacks) {
|
||||||
callback.onIndexerStateChanged(state)
|
callback.onIndexerStateChanged(state)
|
||||||
|
|
@ -163,7 +163,7 @@ class Indexer {
|
||||||
}
|
}
|
||||||
|
|
||||||
lastResponse = response
|
lastResponse = response
|
||||||
loadingState = null
|
indexingState = null
|
||||||
|
|
||||||
val state = State.Complete(response)
|
val state = State.Complete(response)
|
||||||
for (callback in callbacks) {
|
for (callback in callbacks) {
|
||||||
|
|
@ -177,11 +177,10 @@ class Indexer {
|
||||||
* calling this function.
|
* calling this function.
|
||||||
*/
|
*/
|
||||||
private fun indexImpl(context: Context, generation: Long): MusicStore.Library? {
|
private fun indexImpl(context: Context, generation: Long): MusicStore.Library? {
|
||||||
emitLoading(Loading.Indeterminate, generation)
|
emitIndexing(Indexing.Indeterminate, generation)
|
||||||
|
|
||||||
// Establish the backend to use when initially loading songs.
|
// Establish the backend to use when initially loading songs.
|
||||||
val mediaStoreBackend =
|
val mediaStoreBackend = when {
|
||||||
when {
|
|
||||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> Api30MediaStoreBackend()
|
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> Api30MediaStoreBackend()
|
||||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> Api29MediaStoreBackend()
|
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> Api29MediaStoreBackend()
|
||||||
else -> Api21MediaStoreBackend()
|
else -> Api21MediaStoreBackend()
|
||||||
|
|
@ -230,7 +229,7 @@ class Indexer {
|
||||||
"Successfully queried media database " +
|
"Successfully queried media database " +
|
||||||
"in ${System.currentTimeMillis() - start}ms")
|
"in ${System.currentTimeMillis() - start}ms")
|
||||||
|
|
||||||
backend.loadSongs(context, cursor) { loading -> emitLoading(loading, generation) }
|
backend.buildSongs(context, cursor) { indexing -> emitIndexing(indexing, generation) }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deduplicate songs to prevent (most) deformed music clones
|
// Deduplicate songs to prevent (most) deformed music clones
|
||||||
|
|
@ -251,7 +250,7 @@ class Indexer {
|
||||||
// Ensure that sorting order is consistent so that grouping is also consistent.
|
// Ensure that sorting order is consistent so that grouping is also consistent.
|
||||||
Sort.ByName(true).songsInPlace(songs)
|
Sort.ByName(true).songsInPlace(songs)
|
||||||
|
|
||||||
logD("Successfully loaded ${songs.size} songs in ${System.currentTimeMillis() - start}ms")
|
logD("Successfully built ${songs.size} songs in ${System.currentTimeMillis() - start}ms")
|
||||||
|
|
||||||
return songs
|
return songs
|
||||||
}
|
}
|
||||||
|
|
@ -344,13 +343,13 @@ class Indexer {
|
||||||
|
|
||||||
/** Represents the current indexer state. */
|
/** Represents the current indexer state. */
|
||||||
sealed class State {
|
sealed class State {
|
||||||
data class Loading(val loading: Indexer.Loading) : State()
|
data class Indexing(val indexing: Indexer.Indexing) : State()
|
||||||
data class Complete(val response: Response) : State()
|
data class Complete(val response: Response) : State()
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class Loading {
|
sealed class Indexing {
|
||||||
object Indeterminate : Loading()
|
object Indeterminate : Indexing()
|
||||||
class Songs(val current: Int, val total: Int) : Loading()
|
class Songs(val current: Int, val total: Int) : Indexing()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Represents the possible outcomes of a loading process. */
|
/** Represents the possible outcomes of a loading process. */
|
||||||
|
|
@ -385,10 +384,10 @@ class Indexer {
|
||||||
fun query(context: Context): Cursor
|
fun query(context: Context): Cursor
|
||||||
|
|
||||||
/** Create a list of songs from the [Cursor] queried in [query]. */
|
/** Create a list of songs from the [Cursor] queried in [query]. */
|
||||||
fun loadSongs(
|
fun buildSongs(
|
||||||
context: Context,
|
context: Context,
|
||||||
cursor: Cursor,
|
cursor: Cursor,
|
||||||
emitLoading: (Loading) -> Unit
|
emitIndexing: (Indexing) -> Unit
|
||||||
): Collection<Song>
|
): Collection<Song>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,6 @@ import androidx.core.app.NotificationCompat
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.cancel
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.oxycblt.auxio.BuildConfig
|
import org.oxycblt.auxio.BuildConfig
|
||||||
import org.oxycblt.auxio.IntegerTable
|
import org.oxycblt.auxio.IntegerTable
|
||||||
|
|
@ -64,7 +63,7 @@ class IndexerService : Service(), Indexer.Callback {
|
||||||
|
|
||||||
indexer.addCallback(this)
|
indexer.addCallback(this)
|
||||||
if (musicStore.library == null && indexer.isIndeterminate) {
|
if (musicStore.library == null && indexer.isIndeterminate) {
|
||||||
logD("No library present and no previous response, loading music now")
|
logD("No library present and no previous response, indexing music now")
|
||||||
onRequestReindex()
|
onRequestReindex()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,12 +104,12 @@ class IndexerService : Service(), Indexer.Callback {
|
||||||
// database.
|
// database.
|
||||||
stopForegroundSession()
|
stopForegroundSession()
|
||||||
}
|
}
|
||||||
is Indexer.State.Loading -> {
|
is Indexer.State.Indexing -> {
|
||||||
// When loading, we want to enter the foreground state so that android does
|
// When loading, we want to enter the foreground state so that android does
|
||||||
// not shut off the loading process. Note that while we will always post the
|
// not shut off the loading process. Note that while we will always post the
|
||||||
// notification when initially starting, we will not update the notification
|
// notification when initially starting, we will not update the notification
|
||||||
// unless it indicates that we have changed it.
|
// unless it indicates that we have changed it.
|
||||||
val changed = notification.updateLoadingState(state.loading)
|
val changed = notification.updateIndexingState(state.indexing)
|
||||||
if (!isForeground) {
|
if (!isForeground) {
|
||||||
logD("Starting foreground session")
|
logD("Starting foreground session")
|
||||||
startForeground(IntegerTable.INDEXER_NOTIFICATION_CODE, notification.build())
|
startForeground(IntegerTable.INDEXER_NOTIFICATION_CODE, notification.build())
|
||||||
|
|
@ -163,7 +162,7 @@ private class IndexerNotification(private val context: Context) :
|
||||||
setContentIntent(context.newMainPendingIntent())
|
setContentIntent(context.newMainPendingIntent())
|
||||||
setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||||
setContentTitle(context.getString(R.string.info_indexer_channel_name))
|
setContentTitle(context.getString(R.string.info_indexer_channel_name))
|
||||||
setContentText(context.getString(R.string.lbl_loading))
|
setContentText(context.getString(R.string.lbl_indexing))
|
||||||
setProgress(0, 0, true)
|
setProgress(0, 0, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -171,19 +170,19 @@ private class IndexerNotification(private val context: Context) :
|
||||||
notificationManager.notify(IntegerTable.INDEXER_NOTIFICATION_CODE, build())
|
notificationManager.notify(IntegerTable.INDEXER_NOTIFICATION_CODE, build())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateLoadingState(loading: Indexer.Loading): Boolean {
|
fun updateIndexingState(indexing: Indexer.Indexing): Boolean {
|
||||||
when (loading) {
|
when (indexing) {
|
||||||
is Indexer.Loading.Indeterminate -> {
|
is Indexer.Indexing.Indeterminate -> {
|
||||||
setContentText(context.getString(R.string.lbl_loading))
|
setContentText(context.getString(R.string.lbl_indexing))
|
||||||
setProgress(0, 0, true)
|
setProgress(0, 0, true)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
is Indexer.Loading.Songs -> {
|
is Indexer.Indexing.Songs -> {
|
||||||
// Only update the notification every 50 songs to prevent excessive updates.
|
// Only update the notification every 50 songs to prevent excessive updates.
|
||||||
if (loading.current % 50 == 0) {
|
if (indexing.current % 50 == 0) {
|
||||||
setContentText(
|
setContentText(
|
||||||
context.getString(R.string.fmt_indexing, loading.current, loading.total))
|
context.getString(R.string.fmt_indexing, indexing.current, indexing.total))
|
||||||
setProgress(loading.total, loading.current, false)
|
setProgress(indexing.total, indexing.current, false)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,10 +59,10 @@ class ExoPlayerBackend(private val inner: MediaStoreBackend) : Indexer.Backend {
|
||||||
// MediaStore.
|
// MediaStore.
|
||||||
override fun query(context: Context) = inner.query(context)
|
override fun query(context: Context) = inner.query(context)
|
||||||
|
|
||||||
override fun loadSongs(
|
override fun buildSongs(
|
||||||
context: Context,
|
context: Context,
|
||||||
cursor: Cursor,
|
cursor: Cursor,
|
||||||
emitLoading: (Indexer.Loading) -> Unit
|
emitIndexing: (Indexer.Indexing) -> Unit
|
||||||
): Collection<Song> {
|
): Collection<Song> {
|
||||||
// Metadata retrieval with ExoPlayer is asynchronous, so a callback may at any point
|
// Metadata retrieval with ExoPlayer is asynchronous, so a callback may at any point
|
||||||
// add a completed song to the list. To prevent a crash in that case, we use the
|
// add a completed song to the list. To prevent a crash in that case, we use the
|
||||||
|
|
@ -91,7 +91,7 @@ class ExoPlayerBackend(private val inner: MediaStoreBackend) : Indexer.Backend {
|
||||||
AudioCallback(audio) {
|
AudioCallback(audio) {
|
||||||
runningTasks[index] = null
|
runningTasks[index] = null
|
||||||
songs.add(it)
|
songs.add(it)
|
||||||
emitLoading(Indexer.Loading.Songs(songs.size, total))
|
emitIndexing(Indexer.Indexing.Songs(songs.size, total))
|
||||||
},
|
},
|
||||||
// Normal JVM dispatcher will suffice here, as there is no IO work
|
// Normal JVM dispatcher will suffice here, as there is no IO work
|
||||||
// going on (and there is no cost from switching contexts with executors)
|
// going on (and there is no cost from switching contexts with executors)
|
||||||
|
|
|
||||||
|
|
@ -125,10 +125,10 @@ abstract class MediaStoreBackend : Indexer.Backend {
|
||||||
selector.args.toTypedArray())) { "Content resolver failure: No Cursor returned" }
|
selector.args.toTypedArray())) { "Content resolver failure: No Cursor returned" }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun loadSongs(
|
override fun buildSongs(
|
||||||
context: Context,
|
context: Context,
|
||||||
cursor: Cursor,
|
cursor: Cursor,
|
||||||
emitLoading: (Indexer.Loading) -> Unit
|
emitIndexing: (Indexer.Indexing) -> Unit
|
||||||
): Collection<Song> {
|
): Collection<Song> {
|
||||||
// Note: We do not actually update the callback with a current/total value, this is because
|
// Note: We do not actually update the callback with a current/total value, this is because
|
||||||
// loading music from MediaStore tends to be quite fast, with the only bottlenecks being
|
// loading music from MediaStore tends to be quite fast, with the only bottlenecks being
|
||||||
|
|
@ -210,8 +210,9 @@ abstract class MediaStoreBackend : Indexer.Backend {
|
||||||
|
|
||||||
// Try to use the DISPLAY_NAME field to obtain a (probably sane) file name
|
// Try to use the DISPLAY_NAME field to obtain a (probably sane) file name
|
||||||
// from the android system. Once again though, OEM issues get in our way and
|
// from the android system. Once again though, OEM issues get in our way and
|
||||||
// this field isn't available on some platforms. In that case, see if we can
|
// this field isn't available on some platforms. In that case, version-specific
|
||||||
// grok a file name from the DATA field.
|
// implementation will fall back to the equivalent of the path field if it
|
||||||
|
// cannot be obtained here.
|
||||||
audio.displayName = cursor.getStringOrNull(displayNameIndex)
|
audio.displayName = cursor.getStringOrNull(displayNameIndex)
|
||||||
|
|
||||||
audio.duration = cursor.getLong(durationIndex)
|
audio.duration = cursor.getLong(durationIndex)
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ class AboutFragment : ViewBindingFragment<FragmentAboutBinding>() {
|
||||||
|
|
||||||
private fun updateSongCount(songs: List<Song>) {
|
private fun updateSongCount(songs: List<Song>) {
|
||||||
val binding = requireBinding()
|
val binding = requireBinding()
|
||||||
binding.aboutSongCount.textSafe = getString(R.string.fmt_songs_loaded, songs.size)
|
binding.aboutSongCount.textSafe = getString(R.string.fmt_song_count, songs.size)
|
||||||
binding.aboutTotalDuration.textSafe =
|
binding.aboutTotalDuration.textSafe =
|
||||||
getString(
|
getString(
|
||||||
R.string.fmt_total_duration, songs.sumOf { it.durationSecs }.formatDuration(false))
|
R.string.fmt_total_duration, songs.sumOf { it.durationSecs }.formatDuration(false))
|
||||||
|
|
@ -82,17 +82,17 @@ class AboutFragment : ViewBindingFragment<FragmentAboutBinding>() {
|
||||||
|
|
||||||
private fun updateAlbumCount(albums: List<Album>) {
|
private fun updateAlbumCount(albums: List<Album>) {
|
||||||
requireBinding().aboutAlbumCount.textSafe =
|
requireBinding().aboutAlbumCount.textSafe =
|
||||||
getString(R.string.fmt_albums_loaded, albums.size)
|
getString(R.string.fmt_album_count, albums.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateArtistCount(artists: List<Artist>) {
|
private fun updateArtistCount(artists: List<Artist>) {
|
||||||
requireBinding().aboutArtistCount.textSafe =
|
requireBinding().aboutArtistCount.textSafe =
|
||||||
getString(R.string.fmt_artists_loaded, artists.size)
|
getString(R.string.fmt_artist_count, artists.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateGenreCount(genres: List<Genre>) {
|
private fun updateGenreCount(genres: List<Genre>) {
|
||||||
requireBinding().aboutGenreCount.textSafe =
|
requireBinding().aboutGenreCount.textSafe =
|
||||||
getString(R.string.fmt_genres_loaded, genres.size)
|
getString(R.string.fmt_genre_count, genres.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Go through the process of opening a [link] in a browser. */
|
/** Go through the process of opening a [link] in a browser. */
|
||||||
|
|
|
||||||
|
|
@ -183,7 +183,7 @@ class SettingsListFragment : PreferenceFragmentCompat() {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SettingsManager.KEY_RELOAD -> {
|
SettingsManager.KEY_REINDEX -> {
|
||||||
onPreferenceClickListener =
|
onPreferenceClickListener =
|
||||||
Preference.OnPreferenceClickListener {
|
Preference.OnPreferenceClickListener {
|
||||||
playbackModel.savePlaybackState(requireContext()) {
|
playbackModel.savePlaybackState(requireContext()) {
|
||||||
|
|
|
||||||
|
|
@ -323,7 +323,7 @@ class SettingsManager private constructor(context: Context) :
|
||||||
const val KEY_PAUSE_ON_REPEAT = "KEY_LOOP_PAUSE"
|
const val KEY_PAUSE_ON_REPEAT = "KEY_LOOP_PAUSE"
|
||||||
|
|
||||||
const val KEY_SAVE_STATE = "auxio_save_state"
|
const val KEY_SAVE_STATE = "auxio_save_state"
|
||||||
const val KEY_RELOAD = "auxio_reload"
|
const val KEY_REINDEX = "auxio_reindex"
|
||||||
const val KEY_EXCLUDED = "auxio_excluded_dirs"
|
const val KEY_EXCLUDED = "auxio_excluded_dirs"
|
||||||
|
|
||||||
const val KEY_SEARCH_FILTER_MODE = "KEY_SEARCH_FILTER"
|
const val KEY_SEARCH_FILTER_MODE = "KEY_SEARCH_FILTER"
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@
|
||||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
|
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:id="@+id/home_loading_container"
|
android:id="@+id/home_indexing_container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:animateLayoutChanges="true"
|
android:animateLayoutChanges="true"
|
||||||
|
|
@ -42,35 +42,35 @@
|
||||||
android:paddingEnd="@dimen/spacing_medium">
|
android:paddingEnd="@dimen/spacing_medium">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/home_loading_status"
|
android:id="@+id/home_indexing_status"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:layout_marginBottom="@dimen/spacing_medium"
|
android:layout_marginBottom="@dimen/spacing_medium"
|
||||||
android:textAppearance="@style/TextAppearance.Auxio.BodyLarge"
|
android:textAppearance="@style/TextAppearance.Auxio.BodyLarge"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/home_loading_action"
|
app:layout_constraintBottom_toTopOf="@+id/home_indexing_action"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintVertical_chainStyle="packed"
|
app:layout_constraintVertical_chainStyle="packed"
|
||||||
tools:text="Status" />
|
tools:text="Status" />
|
||||||
|
|
||||||
<com.google.android.material.progressindicator.LinearProgressIndicator
|
<com.google.android.material.progressindicator.LinearProgressIndicator
|
||||||
android:id="@+id/home_loading_progress"
|
android:id="@+id/home_indexing_progress"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:indeterminate="true"
|
android:indeterminate="true"
|
||||||
app:indeterminateAnimationType="disjoint"
|
app:indeterminateAnimationType="disjoint"
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/home_loading_action"
|
app:layout_constraintBottom_toBottomOf="@+id/home_indexing_action"
|
||||||
app:layout_constraintTop_toTopOf="@+id/home_loading_action"
|
app:layout_constraintTop_toTopOf="@+id/home_indexing_action"
|
||||||
app:trackColor="@color/sel_track" />
|
app:trackColor="@color/sel_track" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/home_loading_action"
|
android:id="@+id/home_indexing_action"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/lbl_retry"
|
android:text="@string/lbl_retry"
|
||||||
android:visibility="invisible"
|
android:visibility="invisible"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/home_loading_status" />
|
app:layout_constraintTop_toBottomOf="@+id/home_indexing_status" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,7 @@
|
||||||
|
|
||||||
<!-- Error Namespace | Error Labels -->
|
<!-- Error Namespace | Error Labels -->
|
||||||
<string name="err_no_music">لم يتم ايجاد موسيقى</string>
|
<string name="err_no_music">لم يتم ايجاد موسيقى</string>
|
||||||
<string name="err_load_failed">فشل تحميل الموسيقى</string>
|
<string name="err_index_failed">فشل تحميل الموسيقى</string>
|
||||||
<string name="err_no_perms">اوكسيو يحتاج إلى صلاحيات لقراءة للاطلاع على مكتبتك للموسيقى</string>
|
<string name="err_no_perms">اوكسيو يحتاج إلى صلاحيات لقراءة للاطلاع على مكتبتك للموسيقى</string>
|
||||||
<string name="err_no_app">لا يوجد تطبيق لفتح هذا الرابط</string>
|
<string name="err_no_app">لا يوجد تطبيق لفتح هذا الرابط</string>
|
||||||
<string name="err_bad_dir">هذا المجلد غير مدعوم</string>
|
<string name="err_bad_dir">هذا المجلد غير مدعوم</string>
|
||||||
|
|
@ -161,7 +161,7 @@
|
||||||
<string name="clr_grey">رمادي</string>
|
<string name="clr_grey">رمادي</string>
|
||||||
|
|
||||||
<!-- Format Namespace | Value formatting/plurals -->
|
<!-- Format Namespace | Value formatting/plurals -->
|
||||||
<string name="fmt_songs_loaded">الاغنية المُحملة: %d</string>
|
<string name="fmt_song_count">الاغنية المُحملة: %d</string>
|
||||||
|
|
||||||
<plurals name="fmt_song_count">
|
<plurals name="fmt_song_count">
|
||||||
<item quantity="zero">%d اغاني</item>
|
<item quantity="zero">%d اغاني</item>
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
<string name="info_widget_desc">Zobrazení a ovládání přehrávání hudby</string>
|
<string name="info_widget_desc">Zobrazení a ovládání přehrávání hudby</string>
|
||||||
|
|
||||||
<!-- Label Namespace | Static Labels -->
|
<!-- Label Namespace | Static Labels -->
|
||||||
<string name="lbl_loading">Načítání vaší hudební knihovny…</string>
|
<string name="lbl_indexing">Načítání vaší hudební knihovny…</string>
|
||||||
<string name="lbl_retry">Zkusit znovu</string>
|
<string name="lbl_retry">Zkusit znovu</string>
|
||||||
<string name="lbl_grant">Udělit</string>
|
<string name="lbl_grant">Udělit</string>
|
||||||
|
|
||||||
|
|
@ -115,14 +115,14 @@
|
||||||
<string name="set_content">Obsah</string>
|
<string name="set_content">Obsah</string>
|
||||||
<string name="set_save">Uložit stav přehrávání</string>
|
<string name="set_save">Uložit stav přehrávání</string>
|
||||||
<string name="set_save_desc">Uložit aktuální stav přehrávání</string>
|
<string name="set_save_desc">Uložit aktuální stav přehrávání</string>
|
||||||
<string name="set_reload">Znovu načíst hudbu</string>
|
<string name="set_reindex">Znovu načíst hudbu</string>
|
||||||
<string name="set_reload_desc">Aplikace bude restartována</string>
|
<string name="set_reindex_desc">Aplikace bude restartována</string>
|
||||||
<string name="set_excluded">Vyloučené složky</string>
|
<string name="set_excluded">Vyloučené složky</string>
|
||||||
<string name="set_excluded_desc">Obsah vyloučených složek je skrytý z vaší knihovny</string>
|
<string name="set_excluded_desc">Obsah vyloučených složek je skrytý z vaší knihovny</string>
|
||||||
|
|
||||||
<!-- Error Namespace | Error Labels -->
|
<!-- Error Namespace | Error Labels -->
|
||||||
<string name="err_no_music">Nenalezena žádná hudba</string>
|
<string name="err_no_music">Nenalezena žádná hudba</string>
|
||||||
<string name="err_load_failed">Načítání hudby selhalo</string>
|
<string name="err_index_failed">Načítání hudby selhalo</string>
|
||||||
<string name="err_no_perms">Auxio potřebuje oprávnění ke čtení vaší hudební knihovny</string>
|
<string name="err_no_perms">Auxio potřebuje oprávnění ke čtení vaší hudební knihovny</string>
|
||||||
<string name="err_no_app">Žádná aplikace nedokáže otevřít tento odkaz</string>
|
<string name="err_no_app">Žádná aplikace nedokáže otevřít tento odkaz</string>
|
||||||
<string name="err_no_dirs">Žádné složky</string>
|
<string name="err_no_dirs">Žádné složky</string>
|
||||||
|
|
@ -190,10 +190,10 @@
|
||||||
|
|
||||||
<string name="fmt_indexing">Načítání hudební knihovny… (%1$d/%2$d)</string>
|
<string name="fmt_indexing">Načítání hudební knihovny… (%1$d/%2$d)</string>
|
||||||
|
|
||||||
<string name="fmt_songs_loaded">Načtených skladeb: %d</string>
|
<string name="fmt_song_count">Načtených skladeb: %d</string>
|
||||||
<string name="fmt_albums_loaded">Načtených alb: %d</string>
|
<string name="fmt_album_count">Načtených alb: %d</string>
|
||||||
<string name="fmt_artists_loaded">Načtených umělců: %d</string>
|
<string name="fmt_artist_count">Načtených umělců: %d</string>
|
||||||
<string name="fmt_genres_loaded">Načtených žánrů: %d</string>
|
<string name="fmt_genre_count">Načtených žánrů: %d</string>
|
||||||
<string name="fmt_total_duration">Celková doba trvání: %s</string>
|
<string name="fmt_total_duration">Celková doba trvání: %s</string>
|
||||||
|
|
||||||
<plurals name="fmt_song_count">
|
<plurals name="fmt_song_count">
|
||||||
|
|
|
||||||
|
|
@ -93,14 +93,14 @@
|
||||||
<string name="set_content">Inhalt</string>
|
<string name="set_content">Inhalt</string>
|
||||||
<string name="set_save">Wiedergabezustand speichern</string>
|
<string name="set_save">Wiedergabezustand speichern</string>
|
||||||
<string name="set_save_desc">Den aktuellen Wiedergabezustand speichern</string>
|
<string name="set_save_desc">Den aktuellen Wiedergabezustand speichern</string>
|
||||||
<string name="set_reload">Musik neu laden</string>
|
<string name="set_reindex">Musik neu laden</string>
|
||||||
<string name="set_reload_desc">Startet die App neu</string>
|
<string name="set_reindex_desc">Startet die App neu</string>
|
||||||
<string name="set_excluded">Ausgeschlossene Ordner</string>
|
<string name="set_excluded">Ausgeschlossene Ordner</string>
|
||||||
<string name="set_excluded_desc">Die Inhalte der ausgeschlossenen Ordner werden nicht deiner Musikbibliothek angezeigt</string>
|
<string name="set_excluded_desc">Die Inhalte der ausgeschlossenen Ordner werden nicht deiner Musikbibliothek angezeigt</string>
|
||||||
|
|
||||||
<!-- Error Namespace | Error Labels -->
|
<!-- Error Namespace | Error Labels -->
|
||||||
<string name="err_no_music">Keine Musik gefunden</string>
|
<string name="err_no_music">Keine Musik gefunden</string>
|
||||||
<string name="err_load_failed">Laden der Musik fehlgeschlagen</string>
|
<string name="err_index_failed">Laden der Musik fehlgeschlagen</string>
|
||||||
<string name="err_no_perms">Auxio benötigt die Berechtigung, um deine Musikbibliothek zu lesen</string>
|
<string name="err_no_perms">Auxio benötigt die Berechtigung, um deine Musikbibliothek zu lesen</string>
|
||||||
<string name="err_no_app">Link konnte nicht geöffnet werden</string>
|
<string name="err_no_app">Link konnte nicht geöffnet werden</string>
|
||||||
<string name="err_bad_dir">Das Verzeichnis wird nicht unterstützt</string>
|
<string name="err_bad_dir">Das Verzeichnis wird nicht unterstützt</string>
|
||||||
|
|
@ -146,7 +146,7 @@
|
||||||
<string name="clr_grey">Grau</string>
|
<string name="clr_grey">Grau</string>
|
||||||
|
|
||||||
<!-- Format Namespace | Value formatting/plurals -->
|
<!-- Format Namespace | Value formatting/plurals -->
|
||||||
<string name="fmt_songs_loaded">Geladene Lieder: %d</string>
|
<string name="fmt_song_count">Geladene Lieder: %d</string>
|
||||||
|
|
||||||
<plurals name="fmt_song_count">
|
<plurals name="fmt_song_count">
|
||||||
<item quantity="one">%d Lied</item>
|
<item quantity="one">%d Lied</item>
|
||||||
|
|
@ -191,8 +191,8 @@
|
||||||
<string name="fmt_disc_no">Schallplatte %d</string>
|
<string name="fmt_disc_no">Schallplatte %d</string>
|
||||||
<string name="fmt_db_pos">+%.1f dB</string>
|
<string name="fmt_db_pos">+%.1f dB</string>
|
||||||
<string name="fmt_db_neg">-%.1f dB</string>
|
<string name="fmt_db_neg">-%.1f dB</string>
|
||||||
<string name="fmt_albums_loaded">Geladene Alben: %d</string>
|
<string name="fmt_album_count">Geladene Alben: %d</string>
|
||||||
<string name="fmt_artists_loaded">Geladene Künstler: %d</string>
|
<string name="fmt_artist_count">Geladene Künstler: %d</string>
|
||||||
<string name="fmt_genres_loaded">Geladene Genres: %d</string>
|
<string name="fmt_genre_count">Geladene Genres: %d</string>
|
||||||
<string name="fmt_total_duration">Gesamtdauer: %s</string>
|
<string name="fmt_total_duration">Gesamtdauer: %s</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@
|
||||||
<string name="clr_grey">Γκρί</string>
|
<string name="clr_grey">Γκρί</string>
|
||||||
|
|
||||||
<!-- Format Namespace | Value formatting/plurals -->
|
<!-- Format Namespace | Value formatting/plurals -->
|
||||||
<string name="fmt_songs_loaded">Τραγούδια φορτώθηκε: %d</string>
|
<string name="fmt_song_count">Τραγούδια φορτώθηκε: %d</string>
|
||||||
|
|
||||||
<plurals name="fmt_song_count">
|
<plurals name="fmt_song_count">
|
||||||
<item quantity="one">%d Τραγούδι</item>
|
<item quantity="one">%d Τραγούδι</item>
|
||||||
|
|
|
||||||
|
|
@ -99,14 +99,14 @@
|
||||||
<string name="set_content">Contenido</string>
|
<string name="set_content">Contenido</string>
|
||||||
<string name="set_save">Guardar estado de reproducción</string>
|
<string name="set_save">Guardar estado de reproducción</string>
|
||||||
<string name="set_save_desc">Guardar el estado de reproduccion ahora</string>
|
<string name="set_save_desc">Guardar el estado de reproduccion ahora</string>
|
||||||
<string name="set_reload">Recargar música</string>
|
<string name="set_reindex">Recargar música</string>
|
||||||
<string name="set_reload_desc">Se reiniciará la aplicación</string>
|
<string name="set_reindex_desc">Se reiniciará la aplicación</string>
|
||||||
<string name="set_excluded">Directorios excluidos</string>
|
<string name="set_excluded">Directorios excluidos</string>
|
||||||
<string name="set_excluded_desc">El contenido de los directorios excluidos no se mostrará</string>
|
<string name="set_excluded_desc">El contenido de los directorios excluidos no se mostrará</string>
|
||||||
|
|
||||||
<!-- Error Namespace | Error Labels -->
|
<!-- Error Namespace | Error Labels -->
|
||||||
<string name="err_no_music">Sin música</string>
|
<string name="err_no_music">Sin música</string>
|
||||||
<string name="err_load_failed">Falló la carga de música</string>
|
<string name="err_index_failed">Falló la carga de música</string>
|
||||||
<string name="err_no_perms">Auxio necesita permiso para leer su biblioteca de música</string>
|
<string name="err_no_perms">Auxio necesita permiso para leer su biblioteca de música</string>
|
||||||
<string name="err_no_app">Sin aplicación para abrir este enlace</string>
|
<string name="err_no_app">Sin aplicación para abrir este enlace</string>
|
||||||
<string name="err_bad_dir">Directorio no soportado</string>
|
<string name="err_bad_dir">Directorio no soportado</string>
|
||||||
|
|
@ -165,7 +165,7 @@
|
||||||
<string name="clr_grey">Gris</string>
|
<string name="clr_grey">Gris</string>
|
||||||
|
|
||||||
<!-- Format Namespace | Value formatting/plurals -->
|
<!-- Format Namespace | Value formatting/plurals -->
|
||||||
<string name="fmt_songs_loaded">Canciones cargadas: %d</string>
|
<string name="fmt_song_count">Canciones cargadas: %d</string>
|
||||||
|
|
||||||
<plurals name="fmt_song_count">
|
<plurals name="fmt_song_count">
|
||||||
<item quantity="one">%d Canción</item>
|
<item quantity="one">%d Canción</item>
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@
|
||||||
<string name="clr_grey">Gris</string>
|
<string name="clr_grey">Gris</string>
|
||||||
|
|
||||||
<!-- Format Namespace | Value formatting/plurals -->
|
<!-- Format Namespace | Value formatting/plurals -->
|
||||||
<string name="fmt_songs_loaded">Titres chargés: %d</string>
|
<string name="fmt_song_count">Titres chargés: %d</string>
|
||||||
|
|
||||||
<plurals name="fmt_song_count">
|
<plurals name="fmt_song_count">
|
||||||
<item quantity="one">%s Titre</item>
|
<item quantity="one">%s Titre</item>
|
||||||
|
|
|
||||||
|
|
@ -100,8 +100,8 @@
|
||||||
<string name="set_content">Contenuti</string>
|
<string name="set_content">Contenuti</string>
|
||||||
<string name="set_save">Salva stato riproduzione</string>
|
<string name="set_save">Salva stato riproduzione</string>
|
||||||
<string name="set_save_desc">Salva lo stato di riproduzione corrente</string>
|
<string name="set_save_desc">Salva lo stato di riproduzione corrente</string>
|
||||||
<string name="set_reload">Ricarica musica</string>
|
<string name="set_reindex">Ricarica musica</string>
|
||||||
<string name="set_reload_desc">L\'applicazione sarà riavviata</string>
|
<string name="set_reindex_desc">L\'applicazione sarà riavviata</string>
|
||||||
<string name="set_excluded">Cartelle escluse</string>
|
<string name="set_excluded">Cartelle escluse</string>
|
||||||
<string name="set_excluded_desc">Il contenuto delle cartelle escluse sarà nascosto dalla tua libreria</string>
|
<string name="set_excluded_desc">Il contenuto delle cartelle escluse sarà nascosto dalla tua libreria</string>
|
||||||
|
|
||||||
|
|
@ -109,7 +109,7 @@
|
||||||
|
|
||||||
<!-- Error Namespace | Error Labels -->
|
<!-- Error Namespace | Error Labels -->
|
||||||
<string name="err_no_music">Musica non trovata</string>
|
<string name="err_no_music">Musica non trovata</string>
|
||||||
<string name="err_load_failed">Caricamento musica fallito</string>
|
<string name="err_index_failed">Caricamento musica fallito</string>
|
||||||
<string name="err_no_perms">Auxio ha bisogno del permesso per leggere la tua libreria musicale</string>
|
<string name="err_no_perms">Auxio ha bisogno del permesso per leggere la tua libreria musicale</string>
|
||||||
<string name="err_no_app">Nessuna app può aprire il link</string>
|
<string name="err_no_app">Nessuna app può aprire il link</string>
|
||||||
<string name="err_bad_dir">Questa cartella non è supportata</string>
|
<string name="err_bad_dir">Questa cartella non è supportata</string>
|
||||||
|
|
@ -168,10 +168,10 @@
|
||||||
<string name="clr_grey">Grigio</string>
|
<string name="clr_grey">Grigio</string>
|
||||||
|
|
||||||
<!-- Format Namespace | Value formatting/plurals -->
|
<!-- Format Namespace | Value formatting/plurals -->
|
||||||
<string name="fmt_songs_loaded">Canzoni trovate: %d</string>
|
<string name="fmt_song_count">Canzoni trovate: %d</string>
|
||||||
<string name="fmt_albums_loaded">Dischi trovati: %d</string>
|
<string name="fmt_album_count">Dischi trovati: %d</string>
|
||||||
<string name="fmt_artists_loaded">Artisti trovati: %d</string>
|
<string name="fmt_artist_count">Artisti trovati: %d</string>
|
||||||
<string name="fmt_genres_loaded">Generi trovati: %d</string>
|
<string name="fmt_genre_count">Generi trovati: %d</string>
|
||||||
<string name="fmt_total_duration">Durata totale: %s</string>
|
<string name="fmt_total_duration">Durata totale: %s</string>
|
||||||
|
|
||||||
<plurals name="fmt_song_count">
|
<plurals name="fmt_song_count">
|
||||||
|
|
|
||||||
|
|
@ -113,14 +113,14 @@
|
||||||
<string name="set_content">내용</string>
|
<string name="set_content">내용</string>
|
||||||
<string name="set_save">재생 상태 저장</string>
|
<string name="set_save">재생 상태 저장</string>
|
||||||
<string name="set_save_desc">현재 재생 상태를 지금 저장</string>
|
<string name="set_save_desc">현재 재생 상태를 지금 저장</string>
|
||||||
<string name="set_reload">음악 다시 불러오기</string>
|
<string name="set_reindex">음악 다시 불러오기</string>
|
||||||
<string name="set_reload_desc">앱이 다시 시작됩니다.</string>
|
<string name="set_reindex_desc">앱이 다시 시작됩니다.</string>
|
||||||
<string name="set_excluded">폴더 제외</string>
|
<string name="set_excluded">폴더 제외</string>
|
||||||
<string name="set_excluded_desc">제외한 폴더는 라이브러리에서 숨겨집니다.</string>
|
<string name="set_excluded_desc">제외한 폴더는 라이브러리에서 숨겨집니다.</string>
|
||||||
|
|
||||||
<!-- Error Namespace | Error Labels -->
|
<!-- Error Namespace | Error Labels -->
|
||||||
<string name="err_no_music">음악 없음</string>
|
<string name="err_no_music">음악 없음</string>
|
||||||
<string name="err_load_failed">음악 불러오기 실패</string>
|
<string name="err_index_failed">음악 불러오기 실패</string>
|
||||||
<string name="err_no_perms">앱에서 음악 라이브러리를 읽기 위해 권한이 필요합니다.</string>
|
<string name="err_no_perms">앱에서 음악 라이브러리를 읽기 위해 권한이 필요합니다.</string>
|
||||||
<string name="err_no_app">이 링크를 열 수 있는 앱 없음</string>
|
<string name="err_no_app">이 링크를 열 수 있는 앱 없음</string>
|
||||||
<string name="err_no_dirs">폴더 없음</string>
|
<string name="err_no_dirs">폴더 없음</string>
|
||||||
|
|
@ -186,10 +186,10 @@
|
||||||
<string name="fmt_db_pos">+%.1f dB</string>
|
<string name="fmt_db_pos">+%.1f dB</string>
|
||||||
<string name="fmt_db_neg">-%.1f dB</string>
|
<string name="fmt_db_neg">-%.1f dB</string>
|
||||||
|
|
||||||
<string name="fmt_songs_loaded">불러온 음악: %d</string>
|
<string name="fmt_song_count">불러온 음악: %d</string>
|
||||||
<string name="fmt_albums_loaded">불러온 앨범: %d</string>
|
<string name="fmt_album_count">불러온 앨범: %d</string>
|
||||||
<string name="fmt_artists_loaded">불러온 아티스트: %d</string>
|
<string name="fmt_artist_count">불러온 아티스트: %d</string>
|
||||||
<string name="fmt_genres_loaded">불러온 장르: %d</string>
|
<string name="fmt_genre_count">불러온 장르: %d</string>
|
||||||
<string name="fmt_total_duration">총 길이: %s</string>
|
<string name="fmt_total_duration">총 길이: %s</string>
|
||||||
|
|
||||||
<plurals name="fmt_song_count">
|
<plurals name="fmt_song_count">
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@
|
||||||
|
|
||||||
<!-- Error Namespace | Error Labels -->
|
<!-- Error Namespace | Error Labels -->
|
||||||
<string name="err_no_music">Geen muziek aangetroffen</string>
|
<string name="err_no_music">Geen muziek aangetroffen</string>
|
||||||
<string name="err_load_failed">Laden van muziek mislukt</string>
|
<string name="err_index_failed">Laden van muziek mislukt</string>
|
||||||
<string name="err_no_perms">Auxio heeft toestemming nodig om uw muziekbibliotheek te lezen</string>
|
<string name="err_no_perms">Auxio heeft toestemming nodig om uw muziekbibliotheek te lezen</string>
|
||||||
<string name="err_no_app">Geen app kan deze link openen</string>
|
<string name="err_no_app">Geen app kan deze link openen</string>
|
||||||
<string name="err_bad_dir">Deze map wordt niet ondersteund</string>
|
<string name="err_bad_dir">Deze map wordt niet ondersteund</string>
|
||||||
|
|
@ -133,7 +133,7 @@
|
||||||
<string name="clr_grey">Grijis</string>
|
<string name="clr_grey">Grijis</string>
|
||||||
|
|
||||||
<!-- Format Namespace | Value formatting/plurals -->
|
<!-- Format Namespace | Value formatting/plurals -->
|
||||||
<string name="fmt_songs_loaded">Nummers geladen: %d</string>
|
<string name="fmt_song_count">Nummers geladen: %d</string>
|
||||||
|
|
||||||
<plurals name="fmt_song_count">
|
<plurals name="fmt_song_count">
|
||||||
<item quantity="one">%d Nummer</item>
|
<item quantity="one">%d Nummer</item>
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@
|
||||||
<string name="clr_grey">Szary</string>
|
<string name="clr_grey">Szary</string>
|
||||||
|
|
||||||
<!-- Format Namespace | Value formatting/plurals -->
|
<!-- Format Namespace | Value formatting/plurals -->
|
||||||
<string name="fmt_songs_loaded">Utwory uruchamia się: %d</string>
|
<string name="fmt_song_count">Utwory uruchamia się: %d</string>
|
||||||
|
|
||||||
<plurals name="fmt_song_count">
|
<plurals name="fmt_song_count">
|
||||||
<item quantity="one">%d Utwór</item>
|
<item quantity="one">%d Utwór</item>
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@
|
||||||
<string name="clr_grey">Grisalho</string>
|
<string name="clr_grey">Grisalho</string>
|
||||||
|
|
||||||
<!-- Format Namespace | Value formatting/plurals -->
|
<!-- Format Namespace | Value formatting/plurals -->
|
||||||
<string name="fmt_songs_loaded">Músicas carregado: %d</string>
|
<string name="fmt_song_count">Músicas carregado: %d</string>
|
||||||
|
|
||||||
<plurals name="fmt_song_count">
|
<plurals name="fmt_song_count">
|
||||||
<item quantity="one">%d Música</item>
|
<item quantity="one">%d Música</item>
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@
|
||||||
<string name="clr_grey">Grisalho</string>
|
<string name="clr_grey">Grisalho</string>
|
||||||
|
|
||||||
<!-- Format Namespace | Value formatting/plurals -->
|
<!-- Format Namespace | Value formatting/plurals -->
|
||||||
<string name="fmt_songs_loaded">Músicas carregado: %d</string>
|
<string name="fmt_song_count">Músicas carregado: %d</string>
|
||||||
|
|
||||||
<plurals name="fmt_song_count">
|
<plurals name="fmt_song_count">
|
||||||
<item quantity="one">%d Música</item>
|
<item quantity="one">%d Música</item>
|
||||||
|
|
|
||||||
|
|
@ -101,14 +101,14 @@
|
||||||
<string name="set_content">Библиотека</string>
|
<string name="set_content">Библиотека</string>
|
||||||
<string name="set_save">Запоминать позицию</string>
|
<string name="set_save">Запоминать позицию</string>
|
||||||
<string name="set_save_desc">Запоминать позицию в треке</string>
|
<string name="set_save_desc">Запоминать позицию в треке</string>
|
||||||
<string name="set_reload">Перезагрузить музыку</string>
|
<string name="set_reindex">Перезагрузить музыку</string>
|
||||||
<string name="set_reload_desc">Это перезапустит приложение</string>
|
<string name="set_reindex_desc">Это перезапустит приложение</string>
|
||||||
<string name="set_excluded">Исключённые папки</string>
|
<string name="set_excluded">Исключённые папки</string>
|
||||||
<string name="set_excluded_desc">Содержимое исключённых папок будет скрыто из библиотеки</string>
|
<string name="set_excluded_desc">Содержимое исключённых папок будет скрыто из библиотеки</string>
|
||||||
|
|
||||||
<!-- Error Namespace | Error Labels -->
|
<!-- Error Namespace | Error Labels -->
|
||||||
<string name="err_no_music">Треков нет</string>
|
<string name="err_no_music">Треков нет</string>
|
||||||
<string name="err_load_failed">Ошибка чтения библиотеки</string>
|
<string name="err_index_failed">Ошибка чтения библиотеки</string>
|
||||||
<string name="err_no_perms">Auxio требуется разрешение на чтение музыкальной библиотеки</string>
|
<string name="err_no_perms">Auxio требуется разрешение на чтение музыкальной библиотеки</string>
|
||||||
<string name="err_no_app">Нет приложений для открытия данной ссылки</string>
|
<string name="err_no_app">Нет приложений для открытия данной ссылки</string>
|
||||||
<string name="err_bad_dir">Эта папка не поддерживается</string>
|
<string name="err_bad_dir">Эта папка не поддерживается</string>
|
||||||
|
|
@ -166,7 +166,7 @@
|
||||||
<string name="clr_grey">Серый</string>
|
<string name="clr_grey">Серый</string>
|
||||||
|
|
||||||
<!-- Format Namespace | Value formatting/plurals -->
|
<!-- Format Namespace | Value formatting/plurals -->
|
||||||
<string name="fmt_songs_loaded">Всего треков: %d</string>
|
<string name="fmt_song_count">Всего треков: %d</string>
|
||||||
|
|
||||||
<plurals name="fmt_song_count">
|
<plurals name="fmt_song_count">
|
||||||
<item quantity="one">%d трек</item>
|
<item quantity="one">%d трек</item>
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@
|
||||||
<string name="desc_play_pause">Відтворити/Зупинити</string>
|
<string name="desc_play_pause">Відтворити/Зупинити</string>
|
||||||
|
|
||||||
<!-- Format Namespace | Value formatting/plurals -->
|
<!-- Format Namespace | Value formatting/plurals -->
|
||||||
<string name="fmt_songs_loaded">Пісні завантажено: %d</string>
|
<string name="fmt_song_count">Пісні завантажено: %d</string>
|
||||||
|
|
||||||
<plurals name="fmt_song_count">
|
<plurals name="fmt_song_count">
|
||||||
<item quantity="one">%d Пісня</item>
|
<item quantity="one">%d Пісня</item>
|
||||||
|
|
|
||||||
|
|
@ -99,8 +99,8 @@
|
||||||
<string name="set_content">内容</string>
|
<string name="set_content">内容</string>
|
||||||
<string name="set_save">保存播放状态</string>
|
<string name="set_save">保存播放状态</string>
|
||||||
<string name="set_save_desc">立即保存当前播放状态</string>
|
<string name="set_save_desc">立即保存当前播放状态</string>
|
||||||
<string name="set_reload">重新加载音乐</string>
|
<string name="set_reindex">重新加载音乐</string>
|
||||||
<string name="set_reload_desc">将会重启应用</string>
|
<string name="set_reindex_desc">将会重启应用</string>
|
||||||
<string name="set_excluded">排除文件夹</string>
|
<string name="set_excluded">排除文件夹</string>
|
||||||
<string name="set_excluded_desc">被排除文件夹的内容将从媒体库中隐藏</string>
|
<string name="set_excluded_desc">被排除文件夹的内容将从媒体库中隐藏</string>
|
||||||
|
|
||||||
|
|
@ -108,7 +108,7 @@
|
||||||
|
|
||||||
<!-- Error Namespace | Error Labels -->
|
<!-- Error Namespace | Error Labels -->
|
||||||
<string name="err_no_music">没有找到音乐</string>
|
<string name="err_no_music">没有找到音乐</string>
|
||||||
<string name="err_load_failed">加载音乐失败</string>
|
<string name="err_index_failed">加载音乐失败</string>
|
||||||
<string name="err_no_perms">Auxio 需要权限来读取音乐库</string>
|
<string name="err_no_perms">Auxio 需要权限来读取音乐库</string>
|
||||||
<string name="err_no_app">没有可以打开该链接的应用</string>
|
<string name="err_no_app">没有可以打开该链接的应用</string>
|
||||||
<string name="err_bad_dir">该目录不受支持</string>
|
<string name="err_bad_dir">该目录不受支持</string>
|
||||||
|
|
@ -167,7 +167,7 @@
|
||||||
<string name="clr_grey">灰色</string>
|
<string name="clr_grey">灰色</string>
|
||||||
|
|
||||||
<!-- Format Namespace | Value formatting/plurals -->
|
<!-- Format Namespace | Value formatting/plurals -->
|
||||||
<string name="fmt_songs_loaded">已加载 %d 首曲目</string>
|
<string name="fmt_song_count">已加载 %d 首曲目</string>
|
||||||
|
|
||||||
<plurals name="fmt_song_count">
|
<plurals name="fmt_song_count">
|
||||||
<item quantity="other">"%d 首歌曲"</item>
|
<item quantity="other">"%d 首歌曲"</item>
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
<string name="info_widget_desc">View and control music playback</string>
|
<string name="info_widget_desc">View and control music playback</string>
|
||||||
|
|
||||||
<!-- Label Namespace | Static Labels -->
|
<!-- Label Namespace | Static Labels -->
|
||||||
<string name="lbl_loading">Loading your music library…</string>
|
<string name="lbl_indexing">Loading your music library…</string>
|
||||||
<string name="lbl_retry">Retry</string>
|
<string name="lbl_retry">Retry</string>
|
||||||
<string name="lbl_grant">Grant</string>
|
<string name="lbl_grant">Grant</string>
|
||||||
|
|
||||||
|
|
@ -115,14 +115,14 @@
|
||||||
<string name="set_content">Content</string>
|
<string name="set_content">Content</string>
|
||||||
<string name="set_save">Save playback state</string>
|
<string name="set_save">Save playback state</string>
|
||||||
<string name="set_save_desc">Save the current playback state now</string>
|
<string name="set_save_desc">Save the current playback state now</string>
|
||||||
<string name="set_reload">Reload music</string>
|
<string name="set_reindex">Reload music</string>
|
||||||
<string name="set_reload_desc">Will restart app</string>
|
<string name="set_reindex_desc">Will restart app</string>
|
||||||
<string name="set_excluded">Excluded folders</string>
|
<string name="set_excluded">Excluded folders</string>
|
||||||
<string name="set_excluded_desc">The content of excluded folders is hidden from your library</string>
|
<string name="set_excluded_desc">The content of excluded folders is hidden from your library</string>
|
||||||
|
|
||||||
<!-- Error Namespace | Error Labels -->
|
<!-- Error Namespace | Error Labels -->
|
||||||
<string name="err_no_music">No music found</string>
|
<string name="err_no_music">No music found</string>
|
||||||
<string name="err_load_failed">Music loading failed</string>
|
<string name="err_index_failed">Music loading failed</string>
|
||||||
<string name="err_no_perms">Auxio needs permission to read your music library</string>
|
<string name="err_no_perms">Auxio needs permission to read your music library</string>
|
||||||
<string name="err_no_app">No app can open this link</string>
|
<string name="err_no_app">No app can open this link</string>
|
||||||
<string name="err_no_dirs">No Folders</string>
|
<string name="err_no_dirs">No Folders</string>
|
||||||
|
|
@ -190,10 +190,10 @@
|
||||||
|
|
||||||
<string name="fmt_indexing">Loading your music library… (%1$d/%2$d)</string>
|
<string name="fmt_indexing">Loading your music library… (%1$d/%2$d)</string>
|
||||||
|
|
||||||
<string name="fmt_songs_loaded">Songs loaded: %d</string>
|
<string name="fmt_song_count">Songs loaded: %d</string>
|
||||||
<string name="fmt_albums_loaded">Albums loaded: %d</string>
|
<string name="fmt_album_count">Albums loaded: %d</string>
|
||||||
<string name="fmt_artists_loaded">Artists loaded: %d</string>
|
<string name="fmt_artist_count">Artists loaded: %d</string>
|
||||||
<string name="fmt_genres_loaded">Genres loaded: %d</string>
|
<string name="fmt_genre_count">Genres loaded: %d</string>
|
||||||
<string name="fmt_total_duration">Total duration: %s</string>
|
<string name="fmt_total_duration">Total duration: %s</string>
|
||||||
|
|
||||||
<plurals name="fmt_song_count">
|
<plurals name="fmt_song_count">
|
||||||
|
|
|
||||||
|
|
@ -153,9 +153,9 @@
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
app:iconSpaceReserved="false"
|
app:iconSpaceReserved="false"
|
||||||
app:key="auxio_reload"
|
app:key="auxio_reindex"
|
||||||
app:summary="@string/set_reload_desc"
|
app:summary="@string/set_reindex_desc"
|
||||||
app:title="@string/set_reload" />
|
app:title="@string/set_reindex" />
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
app:iconSpaceReserved="false"
|
app:iconSpaceReserved="false"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue