list: add editable list listener
Add a listener for the editable lists in the queue and tab config views. This simply reduces the amount of duplicated code within both of those views.
This commit is contained in:
parent
f4aa20b2f1
commit
dc46c49f07
29 changed files with 147 additions and 128 deletions
4
.github/workflows/android.yml
vendored
4
.github/workflows/android.yml
vendored
|
@ -29,9 +29,9 @@ jobs:
|
||||||
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
|
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
|
||||||
- name: Grant execute permission for gradlew
|
- name: Grant execute permission for gradlew
|
||||||
run: chmod +x gradlew
|
run: chmod +x gradlew
|
||||||
- name: Build Debug APK with Gradle
|
- name: Build debug APK with Gradle
|
||||||
run: ./gradlew app:packageDebug
|
run: ./gradlew app:packageDebug
|
||||||
- name: Upload a Build Artifact
|
- name: Upload debug APK artifact
|
||||||
uses: actions/upload-artifact@v3.1.1
|
uses: actions/upload-artifact@v3.1.1
|
||||||
with:
|
with:
|
||||||
name: Auxio_Canary
|
name: Auxio_Canary
|
||||||
|
|
|
@ -10,6 +10,9 @@
|
||||||
- Value lists are now properly localized
|
- Value lists are now properly localized
|
||||||
- Queue no longer primarily shows previous songs when opened
|
- Queue no longer primarily shows previous songs when opened
|
||||||
|
|
||||||
|
#### What's Fixed
|
||||||
|
- Fixed mangled multi-value ID3v2 tags when UTF-16 is used
|
||||||
|
|
||||||
## 3.0.0
|
## 3.0.0
|
||||||
|
|
||||||
#### What's New
|
#### What's New
|
||||||
|
|
|
@ -227,7 +227,7 @@ private class AlbumSongViewHolder private constructor(private val binding: ItemA
|
||||||
* @param listener A [SelectableListListener] to bind interactions to.
|
* @param listener A [SelectableListListener] to bind interactions to.
|
||||||
*/
|
*/
|
||||||
fun bind(song: Song, listener: SelectableListListener) {
|
fun bind(song: Song, listener: SelectableListListener) {
|
||||||
listener.bind(this, song, binding.songMenu)
|
listener.bind(song, this, menuButton = binding.songMenu)
|
||||||
|
|
||||||
binding.songTrack.apply {
|
binding.songTrack.apply {
|
||||||
if (song.track != null) {
|
if (song.track != null) {
|
||||||
|
|
|
@ -184,7 +184,7 @@ private class ArtistAlbumViewHolder private constructor(private val binding: Ite
|
||||||
* @param listener An [SelectableListListener] to bind interactions to.
|
* @param listener An [SelectableListListener] to bind interactions to.
|
||||||
*/
|
*/
|
||||||
fun bind(album: Album, listener: SelectableListListener) {
|
fun bind(album: Album, listener: SelectableListListener) {
|
||||||
listener.bind(this, album, binding.parentMenu)
|
listener.bind(album, this, menuButton = binding.parentMenu)
|
||||||
binding.parentImage.bind(album)
|
binding.parentImage.bind(album)
|
||||||
binding.parentName.text = album.resolveName(binding.context)
|
binding.parentName.text = album.resolveName(binding.context)
|
||||||
binding.parentInfo.text =
|
binding.parentInfo.text =
|
||||||
|
@ -236,7 +236,7 @@ private class ArtistSongViewHolder private constructor(private val binding: Item
|
||||||
* @param listener An [SelectableListListener] to bind interactions to.
|
* @param listener An [SelectableListListener] to bind interactions to.
|
||||||
*/
|
*/
|
||||||
fun bind(song: Song, listener: SelectableListListener) {
|
fun bind(song: Song, listener: SelectableListListener) {
|
||||||
listener.bind(this, song, binding.songMenu)
|
listener.bind(song, this, menuButton = binding.songMenu)
|
||||||
binding.songAlbumCover.bind(song)
|
binding.songAlbumCover.bind(song)
|
||||||
binding.songName.text = song.resolveName(binding.context)
|
binding.songName.text = song.resolveName(binding.context)
|
||||||
binding.songInfo.text = song.album.resolveName(binding.context)
|
binding.songInfo.text = song.album.resolveName(binding.context)
|
||||||
|
|
|
@ -354,6 +354,7 @@ class HomeFragment :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is Indexer.Response.NoMusic -> {
|
is Indexer.Response.NoMusic -> {
|
||||||
|
// TODO: Move this state to the list fragments (makes life easier)
|
||||||
logD("Updating UI to Response.NoMusic state")
|
logD("Updating UI to Response.NoMusic state")
|
||||||
binding.homeIndexingStatus.text = context.getString(R.string.err_no_music)
|
binding.homeIndexingStatus.text = context.getString(R.string.err_no_music)
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
package org.oxycblt.auxio.home.tabs
|
package org.oxycblt.auxio.home.tabs
|
||||||
|
|
||||||
|
import org.oxycblt.auxio.list.Item
|
||||||
import org.oxycblt.auxio.music.MusicMode
|
import org.oxycblt.auxio.music.MusicMode
|
||||||
import org.oxycblt.auxio.util.logE
|
import org.oxycblt.auxio.util.logE
|
||||||
|
|
||||||
|
@ -25,7 +26,7 @@ import org.oxycblt.auxio.util.logE
|
||||||
* @param mode The type of list in the home view this instance corresponds to.
|
* @param mode The type of list in the home view this instance corresponds to.
|
||||||
* @author Alexander Capehart (OxygenCobalt)
|
* @author Alexander Capehart (OxygenCobalt)
|
||||||
*/
|
*/
|
||||||
sealed class Tab(open val mode: MusicMode) {
|
sealed class Tab(open val mode: MusicMode) : Item {
|
||||||
/**
|
/**
|
||||||
* A visible tab. This will be visible in the home and tab configuration views.
|
* A visible tab. This will be visible in the home and tab configuration views.
|
||||||
* @param mode The type of list in the home view this instance corresponds to.
|
* @param mode The type of list in the home view this instance corresponds to.
|
||||||
|
|
|
@ -18,21 +18,22 @@
|
||||||
package org.oxycblt.auxio.home.tabs
|
package org.oxycblt.auxio.home.tabs
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.view.MotionEvent
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.databinding.ItemTabBinding
|
import org.oxycblt.auxio.databinding.ItemTabBinding
|
||||||
|
import org.oxycblt.auxio.list.EditableListListener
|
||||||
import org.oxycblt.auxio.list.recycler.DialogRecyclerView
|
import org.oxycblt.auxio.list.recycler.DialogRecyclerView
|
||||||
import org.oxycblt.auxio.music.MusicMode
|
import org.oxycblt.auxio.music.MusicMode
|
||||||
import org.oxycblt.auxio.util.inflater
|
import org.oxycblt.auxio.util.inflater
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A [RecyclerView.Adapter] that displays an array of [Tab]s open for configuration.
|
* A [RecyclerView.Adapter] that displays an array of [Tab]s open for configuration.
|
||||||
* @param listener A [Listener] for tab interactions.
|
* @param listener A [EditableListListener] for tab interactions.
|
||||||
*/
|
*/
|
||||||
class TabAdapter(private val listener: Listener) : RecyclerView.Adapter<TabViewHolder>() {
|
class TabAdapter(private val listener: EditableListListener) :
|
||||||
|
RecyclerView.Adapter<TabViewHolder>() {
|
||||||
/** The current array of [Tab]s. */
|
/** The current array of [Tab]s. */
|
||||||
var tabs = arrayOf<Tab>()
|
var tabs = arrayOf<Tab>()
|
||||||
private set
|
private set
|
||||||
|
@ -75,23 +76,6 @@ class TabAdapter(private val listener: Listener) : RecyclerView.Adapter<TabViewH
|
||||||
notifyItemMoved(a, b)
|
notifyItemMoved(a, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A listener for interactions specific to tab configuration. */
|
|
||||||
interface Listener {
|
|
||||||
/**
|
|
||||||
* Called when a tab is clicked, requesting that the visibility should be inverted (i.e
|
|
||||||
* Visible -> Invisible and vice versa).
|
|
||||||
* @param tabMode The [MusicMode] of the tab clicked.
|
|
||||||
*/
|
|
||||||
fun onToggleVisibility(tabMode: MusicMode)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the drag handle on a [RecyclerView.ViewHolder] is clicked, requesting that a
|
|
||||||
* drag should be started.
|
|
||||||
* @param viewHolder The [RecyclerView.ViewHolder] to start dragging.
|
|
||||||
*/
|
|
||||||
fun onPickUp(viewHolder: RecyclerView.ViewHolder)
|
|
||||||
}
|
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
val PAYLOAD_TAB_CHANGED = Any()
|
val PAYLOAD_TAB_CHANGED = Any()
|
||||||
}
|
}
|
||||||
|
@ -106,12 +90,11 @@ class TabViewHolder private constructor(private val binding: ItemTabBinding) :
|
||||||
/**
|
/**
|
||||||
* Bind new data to this instance.
|
* Bind new data to this instance.
|
||||||
* @param tab The new [Tab] to bind.
|
* @param tab The new [Tab] to bind.
|
||||||
* @param listener A [TabAdapter.Listener] to bind interactions to.
|
* @param listener A [EditableListListener] to bind interactions to.
|
||||||
*/
|
*/
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
fun bind(tab: Tab, listener: TabAdapter.Listener) {
|
fun bind(tab: Tab, listener: EditableListListener) {
|
||||||
binding.root.setOnClickListener { listener.onToggleVisibility(tab.mode) }
|
listener.bind(tab, this, dragHandle = binding.tabDragHandle)
|
||||||
|
|
||||||
binding.tabCheckBox.apply {
|
binding.tabCheckBox.apply {
|
||||||
// Update the CheckBox name to align with the mode
|
// Update the CheckBox name to align with the mode
|
||||||
setText(
|
setText(
|
||||||
|
@ -126,15 +109,6 @@ class TabViewHolder private constructor(private val binding: ItemTabBinding) :
|
||||||
// the tab data since they are in the same data structure (Tab)
|
// the tab data since they are in the same data structure (Tab)
|
||||||
isChecked = tab is Tab.Visible
|
isChecked = tab is Tab.Visible
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up the drag handle to start a drag whenever it is touched.
|
|
||||||
binding.tabDragHandle.setOnTouchListener { _, motionEvent ->
|
|
||||||
binding.tabDragHandle.performClick()
|
|
||||||
if (motionEvent.actionMasked == MotionEvent.ACTION_DOWN) {
|
|
||||||
listener.onPickUp(this)
|
|
||||||
true
|
|
||||||
} else false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -25,7 +25,8 @@ import androidx.recyclerview.widget.RecyclerView
|
||||||
import org.oxycblt.auxio.BuildConfig
|
import org.oxycblt.auxio.BuildConfig
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.databinding.DialogTabsBinding
|
import org.oxycblt.auxio.databinding.DialogTabsBinding
|
||||||
import org.oxycblt.auxio.music.MusicMode
|
import org.oxycblt.auxio.list.EditableListListener
|
||||||
|
import org.oxycblt.auxio.list.Item
|
||||||
import org.oxycblt.auxio.settings.Settings
|
import org.oxycblt.auxio.settings.Settings
|
||||||
import org.oxycblt.auxio.ui.ViewBindingDialogFragment
|
import org.oxycblt.auxio.ui.ViewBindingDialogFragment
|
||||||
import org.oxycblt.auxio.util.logD
|
import org.oxycblt.auxio.util.logD
|
||||||
|
@ -34,7 +35,7 @@ import org.oxycblt.auxio.util.logD
|
||||||
* A [ViewBindingDialogFragment] that allows the user to modify the home [Tab] configuration.
|
* A [ViewBindingDialogFragment] that allows the user to modify the home [Tab] configuration.
|
||||||
* @author Alexander Capehart (OxygenCobalt)
|
* @author Alexander Capehart (OxygenCobalt)
|
||||||
*/
|
*/
|
||||||
class TabCustomizeDialog : ViewBindingDialogFragment<DialogTabsBinding>(), TabAdapter.Listener {
|
class TabCustomizeDialog : ViewBindingDialogFragment<DialogTabsBinding>(), EditableListListener {
|
||||||
private val tabAdapter = TabAdapter(this)
|
private val tabAdapter = TabAdapter(this)
|
||||||
private var touchHelper: ItemTouchHelper? = null
|
private var touchHelper: ItemTouchHelper? = null
|
||||||
|
|
||||||
|
@ -80,12 +81,11 @@ class TabCustomizeDialog : ViewBindingDialogFragment<DialogTabsBinding>(), TabAd
|
||||||
binding.tabRecycler.adapter = null
|
binding.tabRecycler.adapter = null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onToggleVisibility(tabMode: MusicMode) {
|
override fun onClick(item: Item, viewHolder: RecyclerView.ViewHolder) {
|
||||||
logD("Toggling tab $tabMode")
|
check(item is Tab) { "Unexpected datatype: ${item::class.java}" }
|
||||||
|
|
||||||
// We will need the exact index of the tab to update on in order to
|
// We will need the exact index of the tab to update on in order to
|
||||||
// notify the adapter of the change.
|
// notify the adapter of the change.
|
||||||
val index = tabAdapter.tabs.indexOfFirst { it.mode == tabMode }
|
val index = tabAdapter.tabs.indexOfFirst { it.mode == item.mode }
|
||||||
val tab = tabAdapter.tabs[index]
|
val tab = tabAdapter.tabs[index]
|
||||||
tabAdapter.setTab(
|
tabAdapter.setTab(
|
||||||
index,
|
index,
|
||||||
|
|
|
@ -107,7 +107,7 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
|
||||||
|
|
||||||
// Playback indicator should sit above the inner StyledImageView and custom view/
|
// Playback indicator should sit above the inner StyledImageView and custom view/
|
||||||
addView(playbackIndicatorView)
|
addView(playbackIndicatorView)
|
||||||
// Selction indicator should never be obscured, so place it at the top.
|
// Selection indicator should never be obscured, so place it at the top.
|
||||||
addView(
|
addView(
|
||||||
selectionIndicatorView,
|
selectionIndicatorView,
|
||||||
LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT).apply {
|
LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT).apply {
|
||||||
|
|
|
@ -22,6 +22,7 @@ import android.view.View
|
||||||
import androidx.annotation.MenuRes
|
import androidx.annotation.MenuRes
|
||||||
import androidx.appcompat.widget.PopupMenu
|
import androidx.appcompat.widget.PopupMenu
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.viewbinding.ViewBinding
|
import androidx.viewbinding.ViewBinding
|
||||||
import org.oxycblt.auxio.MainFragmentDirections
|
import org.oxycblt.auxio.MainFragmentDirections
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
|
@ -53,7 +54,7 @@ abstract class ListFragment<VB : ViewBinding> : SelectionFragment<VB>(), Selecta
|
||||||
*/
|
*/
|
||||||
abstract fun onRealClick(music: Music)
|
abstract fun onRealClick(music: Music)
|
||||||
|
|
||||||
override fun onClick(item: Item) {
|
override fun onClick(item: Item, viewHolder: RecyclerView.ViewHolder) {
|
||||||
check(item is Music) { "Unexpected datatype: ${item::class.simpleName}" }
|
check(item is Music) { "Unexpected datatype: ${item::class.simpleName}" }
|
||||||
if (selectionModel.selected.value.isNotEmpty()) {
|
if (selectionModel.selected.value.isNotEmpty()) {
|
||||||
// Map clicking an item to selecting an item when items are already selected.
|
// Map clicking an item to selecting an item when items are already selected.
|
||||||
|
|
|
@ -17,8 +17,8 @@
|
||||||
|
|
||||||
package org.oxycblt.auxio.list
|
package org.oxycblt.auxio.list
|
||||||
|
|
||||||
|
import android.view.MotionEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.Button
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -26,13 +26,63 @@ import androidx.recyclerview.widget.RecyclerView
|
||||||
* @author Alexander Capehart (OxygenCobalt)
|
* @author Alexander Capehart (OxygenCobalt)
|
||||||
*/
|
*/
|
||||||
interface ClickableListListener {
|
interface ClickableListListener {
|
||||||
// TODO: Supply a ViewHolder on clicks
|
|
||||||
// (allows editable lists to be standardized into a listener.)
|
|
||||||
/**
|
/**
|
||||||
* Called when an [Item] in the list is clicked.
|
* Called when an [Item] in the list is clicked.
|
||||||
* @param item The [Item] that was clicked.
|
* @param item The [Item] that was clicked.
|
||||||
|
* @param viewHolder The [RecyclerView.ViewHolder] of the item that was clicked.
|
||||||
*/
|
*/
|
||||||
fun onClick(item: Item)
|
fun onClick(item: Item, viewHolder: RecyclerView.ViewHolder)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds this instance to a list item.
|
||||||
|
* @param item The [Item] that this list entry is bound to.
|
||||||
|
* @param viewHolder The [RecyclerView.ViewHolder] of the item that was clicked.
|
||||||
|
* @param bodyView The [View] containing the main body of the list item. Any click events on
|
||||||
|
* this [View] are routed to the listener. Defaults to the root view.
|
||||||
|
*/
|
||||||
|
fun bind(
|
||||||
|
item: Item,
|
||||||
|
viewHolder: RecyclerView.ViewHolder,
|
||||||
|
bodyView: View = viewHolder.itemView
|
||||||
|
) {
|
||||||
|
bodyView.setOnClickListener { onClick(item, viewHolder) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An extension of [ClickableListListener] that enables list editing functionality.
|
||||||
|
* @author Alexander Capehart (OxygenCobalt)
|
||||||
|
*/
|
||||||
|
interface EditableListListener : ClickableListListener {
|
||||||
|
/**
|
||||||
|
* Called when a [RecyclerView.ViewHolder] requests that it should be dragged.
|
||||||
|
* @param viewHolder The [RecyclerView.ViewHolder] that should start being dragged.
|
||||||
|
*/
|
||||||
|
fun onPickUp(viewHolder: RecyclerView.ViewHolder)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds this instance to a list item.
|
||||||
|
* @param item The [Item] that this list entry is bound to.
|
||||||
|
* @param viewHolder The [RecyclerView.ViewHolder] to bind.
|
||||||
|
* @param bodyView The [View] containing the main body of the list item. Any click events on
|
||||||
|
* this [View] are routed to the listener. Defaults to the root view.
|
||||||
|
* @param dragHandle A touchable [View]. Any drag on this view will start a drag event.
|
||||||
|
*/
|
||||||
|
fun bind(
|
||||||
|
item: Item,
|
||||||
|
viewHolder: RecyclerView.ViewHolder,
|
||||||
|
bodyView: View = viewHolder.itemView,
|
||||||
|
dragHandle: View
|
||||||
|
) {
|
||||||
|
bind(item, viewHolder, bodyView)
|
||||||
|
dragHandle.setOnTouchListener { _, motionEvent ->
|
||||||
|
dragHandle.performClick()
|
||||||
|
if (motionEvent.actionMasked == MotionEvent.ACTION_DOWN) {
|
||||||
|
onPickUp(viewHolder)
|
||||||
|
true
|
||||||
|
} else false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -55,19 +105,23 @@ interface SelectableListListener : ClickableListListener {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Binds this instance to a list item.
|
* Binds this instance to a list item.
|
||||||
* @param viewHolder The [RecyclerView.ViewHolder] to bind.
|
|
||||||
* @param item The [Item] that this list entry is bound to.
|
* @param item The [Item] that this list entry is bound to.
|
||||||
* @param menuButton A [Button] that opens a menu.
|
* @param viewHolder The [RecyclerView.ViewHolder] to bind.
|
||||||
|
* @param bodyView The [View] containing the main body of the list item. Any click events on
|
||||||
|
* this [View] are routed to the listener. Defaults to the root view.
|
||||||
|
* @param menuButton A clickable [View]. Any click events on this [View] will open a menu.
|
||||||
*/
|
*/
|
||||||
fun bind(viewHolder: RecyclerView.ViewHolder, item: Item, menuButton: Button) {
|
fun bind(
|
||||||
viewHolder.itemView.apply {
|
item: Item,
|
||||||
// Map clicks to the click listener.
|
viewHolder: RecyclerView.ViewHolder,
|
||||||
setOnClickListener { onClick(item) }
|
bodyView: View = viewHolder.itemView,
|
||||||
// Map long clicks to the selection listener.
|
menuButton: View
|
||||||
setOnLongClickListener {
|
) {
|
||||||
onSelect(item)
|
bind(item, viewHolder, bodyView)
|
||||||
true
|
// Map long clicks to the selection listener.
|
||||||
}
|
bodyView.setOnLongClickListener {
|
||||||
|
onSelect(item)
|
||||||
|
true
|
||||||
}
|
}
|
||||||
// Map the menu button to the menu opening listener.
|
// Map the menu button to the menu opening listener.
|
||||||
menuButton.setOnClickListener { onOpenMenu(item, it) }
|
menuButton.setOnClickListener { onOpenMenu(item, it) }
|
||||||
|
|
|
@ -46,7 +46,7 @@ class SongViewHolder private constructor(private val binding: ItemSongBinding) :
|
||||||
* @param listener An [SelectableListListener] to bind interactions to.
|
* @param listener An [SelectableListListener] to bind interactions to.
|
||||||
*/
|
*/
|
||||||
fun bind(song: Song, listener: SelectableListListener) {
|
fun bind(song: Song, listener: SelectableListListener) {
|
||||||
listener.bind(this, song, binding.songMenu)
|
listener.bind(song, this, menuButton = binding.songMenu)
|
||||||
binding.songAlbumCover.bind(song)
|
binding.songAlbumCover.bind(song)
|
||||||
binding.songName.text = song.resolveName(binding.context)
|
binding.songName.text = song.resolveName(binding.context)
|
||||||
binding.songInfo.text = song.resolveArtistContents(binding.context)
|
binding.songInfo.text = song.resolveArtistContents(binding.context)
|
||||||
|
@ -93,7 +93,7 @@ class AlbumViewHolder private constructor(private val binding: ItemParentBinding
|
||||||
* @param listener An [SelectableListListener] to bind interactions to.
|
* @param listener An [SelectableListListener] to bind interactions to.
|
||||||
*/
|
*/
|
||||||
fun bind(album: Album, listener: SelectableListListener) {
|
fun bind(album: Album, listener: SelectableListListener) {
|
||||||
listener.bind(this, album, binding.parentMenu)
|
listener.bind(album, this, menuButton = binding.parentMenu)
|
||||||
binding.parentImage.bind(album)
|
binding.parentImage.bind(album)
|
||||||
binding.parentName.text = album.resolveName(binding.context)
|
binding.parentName.text = album.resolveName(binding.context)
|
||||||
binding.parentInfo.text = album.resolveArtistContents(binding.context)
|
binding.parentInfo.text = album.resolveArtistContents(binding.context)
|
||||||
|
@ -142,7 +142,7 @@ class ArtistViewHolder private constructor(private val binding: ItemParentBindin
|
||||||
* @param listener An [SelectableListListener] to bind interactions to.
|
* @param listener An [SelectableListListener] to bind interactions to.
|
||||||
*/
|
*/
|
||||||
fun bind(artist: Artist, listener: SelectableListListener) {
|
fun bind(artist: Artist, listener: SelectableListListener) {
|
||||||
listener.bind(this, artist, binding.parentMenu)
|
listener.bind(artist, this, menuButton = binding.parentMenu)
|
||||||
binding.parentImage.bind(artist)
|
binding.parentImage.bind(artist)
|
||||||
binding.parentName.text = artist.resolveName(binding.context)
|
binding.parentName.text = artist.resolveName(binding.context)
|
||||||
binding.parentInfo.text =
|
binding.parentInfo.text =
|
||||||
|
@ -201,7 +201,7 @@ class GenreViewHolder private constructor(private val binding: ItemParentBinding
|
||||||
* @param listener An [SelectableListListener] to bind interactions to.
|
* @param listener An [SelectableListListener] to bind interactions to.
|
||||||
*/
|
*/
|
||||||
fun bind(genre: Genre, listener: SelectableListListener) {
|
fun bind(genre: Genre, listener: SelectableListListener) {
|
||||||
listener.bind(this, genre, binding.parentMenu)
|
listener.bind(genre, this, menuButton = binding.parentMenu)
|
||||||
binding.parentImage.bind(genre)
|
binding.parentImage.bind(genre)
|
||||||
binding.parentName.text = genre.resolveName(binding.context)
|
binding.parentName.text = genre.resolveName(binding.context)
|
||||||
binding.parentInfo.text =
|
binding.parentInfo.text =
|
||||||
|
|
|
@ -282,7 +282,7 @@ sealed class Music : Item {
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
/** Cached collator instance re-used with [makeCollationKeyImpl]. */
|
/** Cached collator instance re-used with [makeCollationKeyImpl]. */
|
||||||
val COLLATOR = Collator.getInstance().apply { strength = Collator.PRIMARY }
|
val COLLATOR: Collator = Collator.getInstance().apply { strength = Collator.PRIMARY }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,7 @@ class ArtistChoiceViewHolder(private val binding: ItemPickerChoiceBinding) :
|
||||||
* @param listener A [ClickableListListener] to bind interactions to.
|
* @param listener A [ClickableListListener] to bind interactions to.
|
||||||
*/
|
*/
|
||||||
fun bind(artist: Artist, listener: ClickableListListener) {
|
fun bind(artist: Artist, listener: ClickableListListener) {
|
||||||
binding.root.setOnClickListener { listener.onClick(artist) }
|
listener.bind(artist, this)
|
||||||
binding.pickerImage.bind(artist)
|
binding.pickerImage.bind(artist)
|
||||||
binding.pickerName.text = artist.resolveName(binding.context)
|
binding.pickerName.text = artist.resolveName(binding.context)
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ package org.oxycblt.auxio.music.picker
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.navigation.fragment.navArgs
|
import androidx.navigation.fragment.navArgs
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import org.oxycblt.auxio.databinding.DialogMusicPickerBinding
|
import org.oxycblt.auxio.databinding.DialogMusicPickerBinding
|
||||||
import org.oxycblt.auxio.list.Item
|
import org.oxycblt.auxio.list.Item
|
||||||
import org.oxycblt.auxio.music.Artist
|
import org.oxycblt.auxio.music.Artist
|
||||||
|
@ -40,8 +41,8 @@ class ArtistNavigationPickerDialog : ArtistPickerDialog() {
|
||||||
super.onBindingCreated(binding, savedInstanceState)
|
super.onBindingCreated(binding, savedInstanceState)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onClick(item: Item) {
|
override fun onClick(item: Item, viewHolder: RecyclerView.ViewHolder) {
|
||||||
super.onClick(item)
|
super.onClick(item, viewHolder)
|
||||||
check(item is Artist) { "Unexpected datatype: ${item::class.simpleName}" }
|
check(item is Artist) { "Unexpected datatype: ${item::class.simpleName}" }
|
||||||
// User made a choice, navigate to it.
|
// User made a choice, navigate to it.
|
||||||
navModel.exploreNavigateTo(item)
|
navModel.exploreNavigateTo(item)
|
||||||
|
|
|
@ -22,6 +22,7 @@ import android.view.LayoutInflater
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.databinding.DialogMusicPickerBinding
|
import org.oxycblt.auxio.databinding.DialogMusicPickerBinding
|
||||||
import org.oxycblt.auxio.list.ClickableListListener
|
import org.oxycblt.auxio.list.ClickableListListener
|
||||||
|
@ -67,7 +68,7 @@ abstract class ArtistPickerDialog :
|
||||||
binding.pickerRecycler.adapter = null
|
binding.pickerRecycler.adapter = null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onClick(item: Item) {
|
override fun onClick(item: Item, viewHolder: RecyclerView.ViewHolder) {
|
||||||
findNavController().navigateUp()
|
findNavController().navigateUp()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ package org.oxycblt.auxio.music.picker
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.navigation.fragment.navArgs
|
import androidx.navigation.fragment.navArgs
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import org.oxycblt.auxio.databinding.DialogMusicPickerBinding
|
import org.oxycblt.auxio.databinding.DialogMusicPickerBinding
|
||||||
import org.oxycblt.auxio.list.Item
|
import org.oxycblt.auxio.list.Item
|
||||||
import org.oxycblt.auxio.music.Artist
|
import org.oxycblt.auxio.music.Artist
|
||||||
|
@ -41,8 +42,8 @@ class ArtistPlaybackPickerDialog : ArtistPickerDialog() {
|
||||||
super.onBindingCreated(binding, savedInstanceState)
|
super.onBindingCreated(binding, savedInstanceState)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onClick(item: Item) {
|
override fun onClick(item: Item, viewHolder: RecyclerView.ViewHolder) {
|
||||||
super.onClick(item)
|
super.onClick(item, viewHolder)
|
||||||
// User made a choice, play the given song from that artist.
|
// User made a choice, play the given song from that artist.
|
||||||
check(item is Artist) { "Unexpected datatype: ${item::class.simpleName}" }
|
check(item is Artist) { "Unexpected datatype: ${item::class.simpleName}" }
|
||||||
val song = pickerModel.currentItem.value
|
val song = pickerModel.currentItem.value
|
||||||
|
|
|
@ -68,7 +68,7 @@ class GenreChoiceViewHolder(private val binding: ItemPickerChoiceBinding) :
|
||||||
* @param listener A [ClickableListListener] to bind interactions to.
|
* @param listener A [ClickableListListener] to bind interactions to.
|
||||||
*/
|
*/
|
||||||
fun bind(genre: Genre, listener: ClickableListListener) {
|
fun bind(genre: Genre, listener: ClickableListListener) {
|
||||||
binding.root.setOnClickListener { listener.onClick(genre) }
|
listener.bind(genre, this)
|
||||||
binding.pickerImage.bind(genre)
|
binding.pickerImage.bind(genre)
|
||||||
binding.pickerName.text = genre.resolveName(binding.context)
|
binding.pickerName.text = genre.resolveName(binding.context)
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.navigation.fragment.navArgs
|
import androidx.navigation.fragment.navArgs
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.databinding.DialogMusicPickerBinding
|
import org.oxycblt.auxio.databinding.DialogMusicPickerBinding
|
||||||
import org.oxycblt.auxio.list.ClickableListListener
|
import org.oxycblt.auxio.list.ClickableListListener
|
||||||
|
@ -74,7 +75,7 @@ class GenrePlaybackPickerDialog :
|
||||||
binding.pickerRecycler.adapter = null
|
binding.pickerRecycler.adapter = null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onClick(item: Item) {
|
override fun onClick(item: Item, viewHolder: RecyclerView.ViewHolder) {
|
||||||
// User made a choice, play the given song from that genre.
|
// User made a choice, play the given song from that genre.
|
||||||
check(item is Genre) { "Unexpected datatype: ${item::class.simpleName}" }
|
check(item is Genre) { "Unexpected datatype: ${item::class.simpleName}" }
|
||||||
val song = pickerModel.currentItem.value
|
val song = pickerModel.currentItem.value
|
||||||
|
|
|
@ -72,7 +72,6 @@ class IndexingNotification(private val context: Context) :
|
||||||
// Determinate state, show an active progress meter. Since these updates arrive
|
// Determinate state, show an active progress meter. Since these updates arrive
|
||||||
// highly rapidly, only update every 1.5 seconds to prevent notification rate
|
// highly rapidly, only update every 1.5 seconds to prevent notification rate
|
||||||
// limiting.
|
// limiting.
|
||||||
// TODO: Can I port this to the playback notification somehow?
|
|
||||||
val now = SystemClock.elapsedRealtime()
|
val now = SystemClock.elapsedRealtime()
|
||||||
if (lastUpdateTime > -1 && (now - lastUpdateTime) < 1500) {
|
if (lastUpdateTime > -1 && (now - lastUpdateTime) < 1500) {
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -198,7 +198,7 @@ class IndexerService :
|
||||||
// 2. If a non-foreground service is killed, the app will probably still be alive,
|
// 2. If a non-foreground service is killed, the app will probably still be alive,
|
||||||
// and thus the music library will not be updated at all.
|
// and thus the music library will not be updated at all.
|
||||||
// TODO: Assuming I unify this with PlaybackService, it's possible that I won't need
|
// TODO: Assuming I unify this with PlaybackService, it's possible that I won't need
|
||||||
// this anymore.
|
// this anymore, or at least I only have to use it when the app task is not removed.
|
||||||
if (!foregroundManager.tryStartForeground(observingNotification)) {
|
if (!foregroundManager.tryStartForeground(observingNotification)) {
|
||||||
observingNotification.post()
|
observingNotification.post()
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,6 +102,7 @@ class PlaybackPanelFragment :
|
||||||
binding.playbackSeekBar.listener = this
|
binding.playbackSeekBar.listener = this
|
||||||
|
|
||||||
// Set up actions
|
// Set up actions
|
||||||
|
// TODO: Add better playback button accessibility
|
||||||
binding.playbackRepeat.setOnClickListener { playbackModel.toggleRepeatMode() }
|
binding.playbackRepeat.setOnClickListener { playbackModel.toggleRepeatMode() }
|
||||||
binding.playbackSkipPrev.setOnClickListener { playbackModel.prev() }
|
binding.playbackSkipPrev.setOnClickListener { playbackModel.prev() }
|
||||||
binding.playbackPlayPause.setOnClickListener { playbackModel.toggleIsPlaying() }
|
binding.playbackPlayPause.setOnClickListener { playbackModel.toggleIsPlaying() }
|
||||||
|
|
|
@ -19,7 +19,6 @@ package org.oxycblt.auxio.playback.queue
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.graphics.drawable.LayerDrawable
|
import android.graphics.drawable.LayerDrawable
|
||||||
import android.view.MotionEvent
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.core.view.isInvisible
|
import androidx.core.view.isInvisible
|
||||||
|
@ -27,6 +26,7 @@ import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.google.android.material.shape.MaterialShapeDrawable
|
import com.google.android.material.shape.MaterialShapeDrawable
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.databinding.ItemQueueSongBinding
|
import org.oxycblt.auxio.databinding.ItemQueueSongBinding
|
||||||
|
import org.oxycblt.auxio.list.EditableListListener
|
||||||
import org.oxycblt.auxio.list.recycler.PlayingIndicatorAdapter
|
import org.oxycblt.auxio.list.recycler.PlayingIndicatorAdapter
|
||||||
import org.oxycblt.auxio.list.recycler.SongViewHolder
|
import org.oxycblt.auxio.list.recycler.SongViewHolder
|
||||||
import org.oxycblt.auxio.list.recycler.SyncListDiffer
|
import org.oxycblt.auxio.list.recycler.SyncListDiffer
|
||||||
|
@ -38,10 +38,11 @@ import org.oxycblt.auxio.util.inflater
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A [RecyclerView.Adapter] that shows an editable list of queue items.
|
* A [RecyclerView.Adapter] that shows an editable list of queue items.
|
||||||
* @param listener A [Listener] to bind interactions to.
|
* @param listener A [EditableListListener] to bind interactions to.
|
||||||
* @author Alexander Capehart (OxygenCobalt)
|
* @author Alexander Capehart (OxygenCobalt)
|
||||||
*/
|
*/
|
||||||
class QueueAdapter(private val listener: Listener) : RecyclerView.Adapter<QueueSongViewHolder>() {
|
class QueueAdapter(private val listener: EditableListListener) :
|
||||||
|
RecyclerView.Adapter<QueueSongViewHolder>() {
|
||||||
private var differ = SyncListDiffer(this, QueueSongViewHolder.DIFF_CALLBACK)
|
private var differ = SyncListDiffer(this, QueueSongViewHolder.DIFF_CALLBACK)
|
||||||
// Since PlayingIndicator adapter relies on an item value, we cannot use it for this
|
// Since PlayingIndicator adapter relies on an item value, we cannot use it for this
|
||||||
// adapter, as one item can appear at several points in the UI. Use a similar implementation
|
// adapter, as one item can appear at several points in the UI. Use a similar implementation
|
||||||
|
@ -121,22 +122,6 @@ class QueueAdapter(private val listener: Listener) : RecyclerView.Adapter<QueueS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A listener for queue list events. */
|
|
||||||
interface Listener {
|
|
||||||
/**
|
|
||||||
* Called when a [RecyclerView.ViewHolder] in the list as clicked.
|
|
||||||
* @param viewHolder The [RecyclerView.ViewHolder] that was clicked.
|
|
||||||
*/
|
|
||||||
fun onClick(viewHolder: RecyclerView.ViewHolder)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the drag handle on a [RecyclerView.ViewHolder] is clicked, requesting that a
|
|
||||||
* drag should be started.
|
|
||||||
* @param viewHolder The [RecyclerView.ViewHolder] to start dragging.
|
|
||||||
*/
|
|
||||||
fun onPickUp(viewHolder: RecyclerView.ViewHolder)
|
|
||||||
}
|
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
val PAYLOAD_UPDATE_POSITION = Any()
|
val PAYLOAD_UPDATE_POSITION = Any()
|
||||||
}
|
}
|
||||||
|
@ -190,27 +175,17 @@ class QueueSongViewHolder private constructor(private val binding: ItemQueueSong
|
||||||
/**
|
/**
|
||||||
* Bind new data to this instance.
|
* Bind new data to this instance.
|
||||||
* @param song The new [Song] to bind.
|
* @param song The new [Song] to bind.
|
||||||
* @param listener A [QueueAdapter.Listener] to bind interactions to.
|
* @param listener A [EditableListListener] to bind interactions to.
|
||||||
*/
|
*/
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
fun bind(song: Song, listener: QueueAdapter.Listener) {
|
fun bind(song: Song, listener: EditableListListener) {
|
||||||
binding.body.setOnClickListener { listener.onClick(this) }
|
listener.bind(song, this, bodyView, binding.songDragHandle)
|
||||||
|
|
||||||
binding.songAlbumCover.bind(song)
|
binding.songAlbumCover.bind(song)
|
||||||
binding.songName.text = song.resolveName(binding.context)
|
binding.songName.text = song.resolveName(binding.context)
|
||||||
binding.songInfo.text = song.resolveArtistContents(binding.context)
|
binding.songInfo.text = song.resolveArtistContents(binding.context)
|
||||||
// Not swiping this ViewHolder if it's being re-bound, ensure that the background is
|
// Not swiping this ViewHolder if it's being re-bound, ensure that the background is
|
||||||
// not visible. See QueueDragCallback for why this is done.
|
// not visible. See QueueDragCallback for why this is done.
|
||||||
binding.background.isInvisible = true
|
binding.background.isInvisible = true
|
||||||
|
|
||||||
// Set up the drag handle to start a drag whenever it is touched.
|
|
||||||
binding.songDragHandle.setOnTouchListener { _, motionEvent ->
|
|
||||||
binding.songDragHandle.performClick()
|
|
||||||
if (motionEvent.actionMasked == MotionEvent.ACTION_DOWN) {
|
|
||||||
listener.onPickUp(this)
|
|
||||||
true
|
|
||||||
} else false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updatePlayingIndicator(isActive: Boolean, isPlaying: Boolean) {
|
override fun updatePlayingIndicator(isActive: Boolean, isPlaying: Boolean) {
|
||||||
|
|
|
@ -26,6 +26,8 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
import org.oxycblt.auxio.databinding.FragmentQueueBinding
|
import org.oxycblt.auxio.databinding.FragmentQueueBinding
|
||||||
|
import org.oxycblt.auxio.list.EditableListListener
|
||||||
|
import org.oxycblt.auxio.list.Item
|
||||||
import org.oxycblt.auxio.music.Song
|
import org.oxycblt.auxio.music.Song
|
||||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||||
import org.oxycblt.auxio.ui.ViewBindingFragment
|
import org.oxycblt.auxio.ui.ViewBindingFragment
|
||||||
|
@ -37,7 +39,7 @@ import org.oxycblt.auxio.util.logD
|
||||||
* A [ViewBindingFragment] that displays an editable queue.
|
* A [ViewBindingFragment] that displays an editable queue.
|
||||||
* @author Alexander Capehart (OxygenCobalt)
|
* @author Alexander Capehart (OxygenCobalt)
|
||||||
*/
|
*/
|
||||||
class QueueFragment : ViewBindingFragment<FragmentQueueBinding>(), QueueAdapter.Listener {
|
class QueueFragment : ViewBindingFragment<FragmentQueueBinding>(), EditableListListener {
|
||||||
private val queueModel: QueueViewModel by activityViewModels()
|
private val queueModel: QueueViewModel by activityViewModels()
|
||||||
private val playbackModel: PlaybackViewModel by androidActivityViewModels()
|
private val playbackModel: PlaybackViewModel by androidActivityViewModels()
|
||||||
private val queueAdapter = QueueAdapter(this)
|
private val queueAdapter = QueueAdapter(this)
|
||||||
|
@ -79,8 +81,7 @@ class QueueFragment : ViewBindingFragment<FragmentQueueBinding>(), QueueAdapter.
|
||||||
binding.queueRecycler.adapter = null
|
binding.queueRecycler.adapter = null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onClick(viewHolder: RecyclerView.ViewHolder) {
|
override fun onClick(item: Item, viewHolder: RecyclerView.ViewHolder) {
|
||||||
// Clicking on a queue item should start playing it.
|
|
||||||
queueModel.goto(viewHolder.bindingAdapterPosition)
|
queueModel.goto(viewHolder.bindingAdapterPosition)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -94,13 +94,13 @@ class AccentViewHolder private constructor(private val binding: ItemAccentBindin
|
||||||
* @param listener A [ClickableListListener] to bind interactions to.
|
* @param listener A [ClickableListListener] to bind interactions to.
|
||||||
*/
|
*/
|
||||||
fun bind(accent: Accent, listener: ClickableListListener) {
|
fun bind(accent: Accent, listener: ClickableListListener) {
|
||||||
|
listener.bind(accent, this, binding.accent)
|
||||||
binding.accent.apply {
|
binding.accent.apply {
|
||||||
setOnClickListener { listener.onClick(accent) }
|
|
||||||
backgroundTintList = context.getColorCompat(accent.primary)
|
|
||||||
// Add a Tooltip based on the content description so that the purpose of this
|
// Add a Tooltip based on the content description so that the purpose of this
|
||||||
// button can be clear.
|
// button can be clear.
|
||||||
contentDescription = context.getString(accent.name)
|
contentDescription = context.getString(accent.name)
|
||||||
TooltipCompat.setTooltipText(this, contentDescription)
|
TooltipCompat.setTooltipText(this, contentDescription)
|
||||||
|
backgroundTintList = context.getColorCompat(accent.primary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ package org.oxycblt.auxio.ui.accent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import org.oxycblt.auxio.BuildConfig
|
import org.oxycblt.auxio.BuildConfig
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.databinding.DialogAccentBinding
|
import org.oxycblt.auxio.databinding.DialogAccentBinding
|
||||||
|
@ -79,7 +80,7 @@ class AccentCustomizeDialog :
|
||||||
binding.accentRecycler.adapter = null
|
binding.accentRecycler.adapter = null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onClick(item: Item) {
|
override fun onClick(item: Item, viewHolder: RecyclerView.ViewHolder) {
|
||||||
check(item is Accent) { "Unexpected datatype: ${item::class.java}" }
|
check(item is Accent) { "Unexpected datatype: ${item::class.java}" }
|
||||||
accentAdapter.setSelectedAccent(item)
|
accentAdapter.setSelectedAccent(item)
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,9 +60,9 @@
|
||||||
<string name="lbl_mixtapes">Mixtapes</string>
|
<string name="lbl_mixtapes">Mixtapes</string>
|
||||||
<!-- As in the collection of music -->
|
<!-- As in the collection of music -->
|
||||||
<string name="lbl_mixtape">Mixtape</string>
|
<string name="lbl_mixtape">Mixtape</string>
|
||||||
<!-- As in a compilation of several performances that blend into a single continuous flow of music -->
|
<!-- As in a compilation of several performances that blend into a single continuous flow of music (Also known as DJ Mixes) -->
|
||||||
<string name="lbl_mixes">Mixes</string>
|
<string name="lbl_mixes">Mixes</string>
|
||||||
<!-- As in a compilation of several performances that blend into a single continuous flow of music -->
|
<!-- As in a compilation of several performances that blend into a single continuous flow of music (Also known as DJ Mixes) -->
|
||||||
<string name="lbl_mix">Mix</string>
|
<string name="lbl_mix">Mix</string>
|
||||||
|
|
||||||
<!-- As in music that was performed live -->
|
<!-- As in music that was performed live -->
|
||||||
|
@ -341,7 +341,10 @@
|
||||||
<!-- Format Namespace | Value formatting/plurals -->
|
<!-- Format Namespace | Value formatting/plurals -->
|
||||||
<eat-comment />
|
<eat-comment />
|
||||||
|
|
||||||
<!-- Comma (,) separator should be localized (For example, "、" in japanese) -->
|
<!--
|
||||||
|
Comma (,) separator should be localized (For example, "、" in japanese).
|
||||||
|
Do not use "and" or equivalents.
|
||||||
|
-->
|
||||||
<string name="fmt_list">%1$s, %2$s</string>
|
<string name="fmt_list">%1$s, %2$s</string>
|
||||||
|
|
||||||
<!-- As in an amount of items that are selected -->
|
<!-- As in an amount of items that are selected -->
|
||||||
|
@ -365,7 +368,7 @@
|
||||||
<string name="fmt_lib_album_count">Albums loaded: %d</string>
|
<string name="fmt_lib_album_count">Albums loaded: %d</string>
|
||||||
<string name="fmt_lib_artist_count">Artists loaded: %d</string>
|
<string name="fmt_lib_artist_count">Artists loaded: %d</string>
|
||||||
<string name="fmt_lib_genre_count">Genres loaded: %d</string>
|
<string name="fmt_lib_genre_count">Genres loaded: %d</string>
|
||||||
<!-- AS in the total duration of all songs in the music library -->
|
<!-- As in the total duration of all songs in the music library -->
|
||||||
<string name="fmt_lib_total_duration">Total duration: %s</string>
|
<string name="fmt_lib_total_duration">Total duration: %s</string>
|
||||||
|
|
||||||
<plurals name="fmt_song_count">
|
<plurals name="fmt_song_count">
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
Auxio is a local music player with a fast, reliable UI/UX without the many useless features present in other music players. Built off of <a href="https://exoplayer.dev/">Exoplayer</a>, Auxio has superior library support and listening quality compared to other apps that use outdated android functionality. In short, It plays music.
|
Auxio is a local music player with a fast, reliable UI/UX without the many useless features present in other music players. Built off of <a href="https://exoplayer.dev/">Exoplayer</a>, Auxio has superior library support and listening quality compared to other apps that use outdated android functionality. In short, <b>It plays music</b>.
|
||||||
|
|
||||||
<b>Features</b>
|
<b>Features</b>
|
||||||
|
|
||||||
|
|
17
prebuild.py
17
prebuild.py
|
@ -19,13 +19,14 @@ import re
|
||||||
|
|
||||||
# WARNING: THE EXOPLAYER VERSION MUST BE KEPT IN LOCK-STEP WITH THE FLAC EXTENSION AND
|
# WARNING: THE EXOPLAYER VERSION MUST BE KEPT IN LOCK-STEP WITH THE FLAC EXTENSION AND
|
||||||
# THE GRADLE DEPENDENCY. IF NOT, VERY UNFRIENDLY BUILD FAILURES AND CRASHES MAY ENSUE.
|
# THE GRADLE DEPENDENCY. IF NOT, VERY UNFRIENDLY BUILD FAILURES AND CRASHES MAY ENSUE.
|
||||||
# EXO_VERSION = "2.18.1"
|
# EXO_VERSION = "2.18.2"
|
||||||
FLAC_VERSION = "1.3.2"
|
FLAC_VERSION = "1.3.2"
|
||||||
|
|
||||||
FATAL="\033[1;31m"
|
OK="\033[1;32m" # Bold green
|
||||||
WARN="\033[1;91m"
|
FATAL="\033[1;31m" # Bold red
|
||||||
INFO="\033[1;94m"
|
WARN="\033[1;33m" # Bold yellow
|
||||||
OK="\033[1;92m"
|
RUN="\033[1;34m" # Bold blue
|
||||||
|
INFO="\033[1m" # Bold white
|
||||||
NC="\033[0m"
|
NC="\033[0m"
|
||||||
|
|
||||||
# We do some shell scripting later on, so we can't support windows.
|
# We do some shell scripting later on, so we can't support windows.
|
||||||
|
@ -36,7 +37,7 @@ if system not in ["Linux", "Darwin"]:
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
def sh(cmd):
|
def sh(cmd):
|
||||||
print(INFO + "execute: " + NC + cmd)
|
print(RUN + "execute: " + NC + cmd)
|
||||||
code = subprocess.call(["sh", "-c", "set -e; " + cmd])
|
code = subprocess.call(["sh", "-c", "set -e; " + cmd])
|
||||||
if code != 0:
|
if code != 0:
|
||||||
print(FATAL + "fatal:" + NC + " command failed with exit code " + str(code))
|
print(FATAL + "fatal:" + NC + " command failed with exit code " + str(code))
|
||||||
|
@ -79,11 +80,11 @@ if ndk_path is None or not os.path.isfile(os.path.join(ndk_path, "ndk-build")):
|
||||||
"candidates were found however:")
|
"candidates were found however:")
|
||||||
for i, candidate in enumerate(candidates):
|
for i, candidate in enumerate(candidates):
|
||||||
print("[" + str(i) + "] " + candidate)
|
print("[" + str(i) + "] " + candidate)
|
||||||
print(WARN + "info:" + NC + " NDK r21e is recommended for this script. Other " +
|
print(INFO + "info:" + NC + " NDK r21e is recommended for this script. Other " +
|
||||||
"NDKs may result in unexpected behavior.")
|
"NDKs may result in unexpected behavior.")
|
||||||
try:
|
try:
|
||||||
ndk_path = candidates[int(input("enter the ndk to use [default 0]: "))]
|
ndk_path = candidates[int(input("enter the ndk to use [default 0]: "))]
|
||||||
except:
|
except ValueError:
|
||||||
ndk_path = candidates[0]
|
ndk_path = candidates[0]
|
||||||
else:
|
else:
|
||||||
print(FATAL + "fatal:" + NC + " the android ndk was not installed at a " +
|
print(FATAL + "fatal:" + NC + " the android ndk was not installed at a " +
|
||||||
|
|
Loading…
Reference in a new issue