Improve documentation slightly
Add some more documentation to the code.
This commit is contained in:
parent
fd4b7b247f
commit
38afa8f4d2
28 changed files with 188 additions and 53 deletions
|
@ -120,6 +120,9 @@ class MainFragment : Fragment() {
|
||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Functions that check if MainFragment should nav over to LibraryFragment, or whether
|
||||||
|
// it should stay put. Mostly by checking if the navController is currently in a detail
|
||||||
|
// fragment, and if the playing item is already being shown.
|
||||||
private fun shouldGoToAlbum(controller: NavController): Boolean {
|
private fun shouldGoToAlbum(controller: NavController): Boolean {
|
||||||
return (
|
return (
|
||||||
controller.currentDestination!!.id == R.id.album_detail_fragment &&
|
controller.currentDestination!!.id == R.id.album_detail_fragment &&
|
||||||
|
@ -157,6 +160,8 @@ class MainFragment : Fragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ package org.oxycblt.auxio.database
|
||||||
* @property shuffleSeed - A long for the seed used to shuffle the queue [Used for quick-restore]
|
* @property shuffleSeed - A long for the seed used to shuffle the queue [Used for quick-restore]
|
||||||
* @property loopMode - The integer form of the current [org.oxycblt.auxio.playback.state.LoopMode]
|
* @property loopMode - The integer form of the current [org.oxycblt.auxio.playback.state.LoopMode]
|
||||||
* @property inUserQueue - A bool for if the state was currently playing from the user queue.
|
* @property inUserQueue - A bool for if the state was currently playing from the user queue.
|
||||||
|
* @author OxygenCobalt
|
||||||
*/
|
*/
|
||||||
data class PlaybackState(
|
data class PlaybackState(
|
||||||
val id: Long = 0L,
|
val id: Long = 0L,
|
||||||
|
|
|
@ -6,6 +6,7 @@ package org.oxycblt.auxio.database
|
||||||
* @property songId The song id for this queue item
|
* @property songId The song id for this queue item
|
||||||
* @property albumId The album id for this queue item, used to make searching quicker
|
* @property albumId The album id for this queue item, used to make searching quicker
|
||||||
* @property isUserQueue A bool for if this queue item is a user queue item or not
|
* @property isUserQueue A bool for if this queue item is a user queue item or not
|
||||||
|
* @author OxygenCobalt
|
||||||
*/
|
*/
|
||||||
data class QueueItem(
|
data class QueueItem(
|
||||||
var id: Long = 0L,
|
var id: Long = 0L,
|
||||||
|
|
|
@ -11,13 +11,17 @@ import androidx.navigation.fragment.findNavController
|
||||||
import androidx.navigation.fragment.navArgs
|
import androidx.navigation.fragment.navArgs
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.databinding.FragmentAlbumDetailBinding
|
import org.oxycblt.auxio.databinding.FragmentAlbumDetailBinding
|
||||||
import org.oxycblt.auxio.detail.adapters.DetailSongAdapter
|
import org.oxycblt.auxio.detail.adapters.AlbumSongAdapter
|
||||||
import org.oxycblt.auxio.music.MusicStore
|
import org.oxycblt.auxio.music.MusicStore
|
||||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||||
import org.oxycblt.auxio.playback.state.PlaybackMode
|
import org.oxycblt.auxio.playback.state.PlaybackMode
|
||||||
import org.oxycblt.auxio.ui.disable
|
import org.oxycblt.auxio.ui.disable
|
||||||
import org.oxycblt.auxio.ui.setupAlbumSongActions
|
import org.oxycblt.auxio.ui.setupAlbumSongActions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [DetailFragment] for an album.
|
||||||
|
* @author OxygenCobalt
|
||||||
|
*/
|
||||||
class AlbumDetailFragment : DetailFragment() {
|
class AlbumDetailFragment : DetailFragment() {
|
||||||
|
|
||||||
private val args: AlbumDetailFragmentArgs by navArgs()
|
private val args: AlbumDetailFragmentArgs by navArgs()
|
||||||
|
@ -42,7 +46,7 @@ class AlbumDetailFragment : DetailFragment() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val songAdapter = DetailSongAdapter(
|
val songAdapter = AlbumSongAdapter(
|
||||||
doOnClick = { playbackModel.playSong(it, PlaybackMode.IN_ALBUM) },
|
doOnClick = { playbackModel.playSong(it, PlaybackMode.IN_ALBUM) },
|
||||||
doOnLongClick = { data, view ->
|
doOnLongClick = { data, view ->
|
||||||
PopupMenu(requireContext(), view).setupAlbumSongActions(
|
PopupMenu(requireContext(), view).setupAlbumSongActions(
|
||||||
|
|
|
@ -11,13 +11,17 @@ import androidx.navigation.fragment.findNavController
|
||||||
import androidx.navigation.fragment.navArgs
|
import androidx.navigation.fragment.navArgs
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.databinding.FragmentArtistDetailBinding
|
import org.oxycblt.auxio.databinding.FragmentArtistDetailBinding
|
||||||
import org.oxycblt.auxio.detail.adapters.DetailAlbumAdapter
|
import org.oxycblt.auxio.detail.adapters.ArtistAlbumAdapter
|
||||||
import org.oxycblt.auxio.music.Artist
|
import org.oxycblt.auxio.music.Artist
|
||||||
import org.oxycblt.auxio.music.MusicStore
|
import org.oxycblt.auxio.music.MusicStore
|
||||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||||
import org.oxycblt.auxio.ui.disable
|
import org.oxycblt.auxio.ui.disable
|
||||||
import org.oxycblt.auxio.ui.setupAlbumActions
|
import org.oxycblt.auxio.ui.setupAlbumActions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [DetailFragment] for an artist.
|
||||||
|
* @author OxygenCobalt
|
||||||
|
*/
|
||||||
class ArtistDetailFragment : DetailFragment() {
|
class ArtistDetailFragment : DetailFragment() {
|
||||||
private val args: ArtistDetailFragmentArgs by navArgs()
|
private val args: ArtistDetailFragmentArgs by navArgs()
|
||||||
private val playbackModel: PlaybackViewModel by activityViewModels()
|
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||||
|
@ -41,7 +45,7 @@ class ArtistDetailFragment : DetailFragment() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val albumAdapter = DetailAlbumAdapter(
|
val albumAdapter = ArtistAlbumAdapter(
|
||||||
doOnClick = {
|
doOnClick = {
|
||||||
if (!detailModel.isNavigating) {
|
if (!detailModel.isNavigating) {
|
||||||
detailModel.updateNavigationStatus(true)
|
detailModel.updateNavigationStatus(true)
|
||||||
|
|
|
@ -8,7 +8,10 @@ import org.oxycblt.auxio.music.Artist
|
||||||
import org.oxycblt.auxio.music.Genre
|
import org.oxycblt.auxio.music.Genre
|
||||||
import org.oxycblt.auxio.recycler.SortMode
|
import org.oxycblt.auxio.recycler.SortMode
|
||||||
|
|
||||||
// ViewModel for the Detail Fragments.
|
/**
|
||||||
|
* ViewModel that stores data for the [DetailFragment]s, such as what they're showing & what
|
||||||
|
* [SortMode] they are currently on.
|
||||||
|
*/
|
||||||
class DetailViewModel : ViewModel() {
|
class DetailViewModel : ViewModel() {
|
||||||
private var mIsNavigating = false
|
private var mIsNavigating = false
|
||||||
val isNavigating: Boolean get() = mIsNavigating
|
val isNavigating: Boolean get() = mIsNavigating
|
||||||
|
|
|
@ -11,12 +11,16 @@ import androidx.navigation.fragment.findNavController
|
||||||
import androidx.navigation.fragment.navArgs
|
import androidx.navigation.fragment.navArgs
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.databinding.FragmentGenreDetailBinding
|
import org.oxycblt.auxio.databinding.FragmentGenreDetailBinding
|
||||||
import org.oxycblt.auxio.detail.adapters.DetailArtistAdapter
|
import org.oxycblt.auxio.detail.adapters.GenreArtistAdapter
|
||||||
import org.oxycblt.auxio.music.MusicStore
|
import org.oxycblt.auxio.music.MusicStore
|
||||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||||
import org.oxycblt.auxio.ui.disable
|
import org.oxycblt.auxio.ui.disable
|
||||||
import org.oxycblt.auxio.ui.setupArtistActions
|
import org.oxycblt.auxio.ui.setupArtistActions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [DetailFragment] for a genre.
|
||||||
|
* @author OxygenCobalt
|
||||||
|
*/
|
||||||
class GenreDetailFragment : DetailFragment() {
|
class GenreDetailFragment : DetailFragment() {
|
||||||
|
|
||||||
private val args: GenreDetailFragmentArgs by navArgs()
|
private val args: GenreDetailFragmentArgs by navArgs()
|
||||||
|
@ -41,7 +45,7 @@ class GenreDetailFragment : DetailFragment() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val artistAdapter = DetailArtistAdapter(
|
val artistAdapter = GenreArtistAdapter(
|
||||||
doOnClick = {
|
doOnClick = {
|
||||||
if (!detailModel.isNavigating) {
|
if (!detailModel.isNavigating) {
|
||||||
detailModel.updateNavigationStatus(true)
|
detailModel.updateNavigationStatus(true)
|
||||||
|
|
|
@ -9,10 +9,13 @@ import org.oxycblt.auxio.music.Song
|
||||||
import org.oxycblt.auxio.recycler.DiffCallback
|
import org.oxycblt.auxio.recycler.DiffCallback
|
||||||
import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder
|
import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder
|
||||||
|
|
||||||
class DetailSongAdapter(
|
/**
|
||||||
|
* An adapter for displaying the [Song]s of an album.
|
||||||
|
*/
|
||||||
|
class AlbumSongAdapter(
|
||||||
private val doOnClick: (data: Song) -> Unit,
|
private val doOnClick: (data: Song) -> Unit,
|
||||||
private val doOnLongClick: (data: Song, view: View) -> Unit
|
private val doOnLongClick: (data: Song, view: View) -> Unit
|
||||||
) : ListAdapter<Song, DetailSongAdapter.ViewHolder>(DiffCallback()) {
|
) : ListAdapter<Song, AlbumSongAdapter.ViewHolder>(DiffCallback()) {
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
return ViewHolder(
|
return ViewHolder(
|
||||||
ItemAlbumSongBinding.inflate(LayoutInflater.from(parent.context))
|
ItemAlbumSongBinding.inflate(LayoutInflater.from(parent.context))
|
|
@ -10,7 +10,10 @@ import org.oxycblt.auxio.music.Album
|
||||||
import org.oxycblt.auxio.recycler.DiffCallback
|
import org.oxycblt.auxio.recycler.DiffCallback
|
||||||
import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder
|
import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder
|
||||||
|
|
||||||
class DetailAlbumAdapter(
|
/**
|
||||||
|
* An adapter for displaying the [Album]s of an artist.
|
||||||
|
*/
|
||||||
|
class ArtistAlbumAdapter(
|
||||||
private val doOnClick: (data: Album) -> Unit,
|
private val doOnClick: (data: Album) -> Unit,
|
||||||
private val doOnLongClick: (data: Album, view: View) -> Unit,
|
private val doOnLongClick: (data: Album, view: View) -> Unit,
|
||||||
) : ListAdapter<Album, RecyclerView.ViewHolder>(DiffCallback()) {
|
) : ListAdapter<Album, RecyclerView.ViewHolder>(DiffCallback()) {
|
|
@ -9,10 +9,13 @@ import org.oxycblt.auxio.music.Artist
|
||||||
import org.oxycblt.auxio.recycler.DiffCallback
|
import org.oxycblt.auxio.recycler.DiffCallback
|
||||||
import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder
|
import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder
|
||||||
|
|
||||||
class DetailArtistAdapter(
|
/**
|
||||||
|
* An adapter for displaying the [Artist]s of an genre.
|
||||||
|
*/
|
||||||
|
class GenreArtistAdapter(
|
||||||
private val doOnClick: (data: Artist) -> Unit,
|
private val doOnClick: (data: Artist) -> Unit,
|
||||||
private val doOnLongClick: (data: Artist, view: View) -> Unit
|
private val doOnLongClick: (data: Artist, view: View) -> Unit
|
||||||
) : ListAdapter<Artist, DetailArtistAdapter.ViewHolder>(DiffCallback()) {
|
) : ListAdapter<Artist, GenreArtistAdapter.ViewHolder>(DiffCallback()) {
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
return ViewHolder(
|
return ViewHolder(
|
|
@ -69,7 +69,7 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
|
||||||
|
|
||||||
setOnMenuItemClickListener {
|
setOnMenuItemClickListener {
|
||||||
if (it.itemId != R.id.action_search) {
|
if (it.itemId != R.id.action_search) {
|
||||||
libraryModel.updateSortMode(it)
|
libraryModel.updateSortMode(it.itemId)
|
||||||
} else {
|
} else {
|
||||||
// Do whatever this is in order to make the SearchView focusable.
|
// Do whatever this is in order to make the SearchView focusable.
|
||||||
(it.actionView as SearchView).isIconified = false
|
(it.actionView as SearchView).isIconified = false
|
||||||
|
@ -92,7 +92,7 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
|
||||||
searchView.setOnQueryTextListener(this@LibraryFragment)
|
searchView.setOnQueryTextListener(this@LibraryFragment)
|
||||||
searchView.setOnQueryTextFocusChangeListener { _, hasFocus ->
|
searchView.setOnQueryTextFocusChangeListener { _, hasFocus ->
|
||||||
libraryModel.updateSearchFocusStatus(hasFocus)
|
libraryModel.updateSearchFocusStatus(hasFocus)
|
||||||
libraryModel.updateSearchQuery(searchView.query.toString(), requireContext())
|
libraryModel.doSearch(searchView.query.toString(), requireContext())
|
||||||
item.isVisible = !hasFocus
|
item.isVisible = !hasFocus
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,7 +177,7 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
|
||||||
override fun onQueryTextSubmit(query: String): Boolean = false
|
override fun onQueryTextSubmit(query: String): Boolean = false
|
||||||
|
|
||||||
override fun onQueryTextChange(query: String): Boolean {
|
override fun onQueryTextChange(query: String): Boolean {
|
||||||
libraryModel.updateSearchQuery(query, requireContext())
|
libraryModel.doSearch(query, requireContext())
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package org.oxycblt.auxio.library
|
package org.oxycblt.auxio.library
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.view.MenuItem
|
import androidx.annotation.IdRes
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
|
@ -14,6 +14,11 @@ import org.oxycblt.auxio.music.MusicStore
|
||||||
import org.oxycblt.auxio.recycler.ShowMode
|
import org.oxycblt.auxio.recycler.ShowMode
|
||||||
import org.oxycblt.auxio.recycler.SortMode
|
import org.oxycblt.auxio.recycler.SortMode
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [ViewModel] that manages what [LibraryFragment] is currently showing, and also the search
|
||||||
|
* functionality.
|
||||||
|
* @author OxygenCobalt
|
||||||
|
*/
|
||||||
class LibraryViewModel : ViewModel() {
|
class LibraryViewModel : ViewModel() {
|
||||||
private var mIsNavigating = false
|
private var mIsNavigating = false
|
||||||
val isNavigating: Boolean get() = mIsNavigating
|
val isNavigating: Boolean get() = mIsNavigating
|
||||||
|
@ -31,21 +36,13 @@ class LibraryViewModel : ViewModel() {
|
||||||
private val mSearchResults = MutableLiveData(listOf<BaseModel>())
|
private val mSearchResults = MutableLiveData(listOf<BaseModel>())
|
||||||
val searchResults: LiveData<List<BaseModel>> get() = mSearchResults
|
val searchResults: LiveData<List<BaseModel>> get() = mSearchResults
|
||||||
|
|
||||||
fun updateSortMode(item: MenuItem) {
|
/**
|
||||||
val mode = when (item.itemId) {
|
* Perform a search of the music library, given a query.
|
||||||
R.id.option_sort_none -> SortMode.NONE
|
* Results are pushed to [searchResults].
|
||||||
R.id.option_sort_alpha_down -> SortMode.ALPHA_DOWN
|
* @param query The query for this search
|
||||||
R.id.option_sort_alpha_up -> SortMode.ALPHA_UP
|
* @param context The context needed to create the header text
|
||||||
|
*/
|
||||||
else -> SortMode.NONE
|
fun doSearch(query: String, context: Context) {
|
||||||
}
|
|
||||||
|
|
||||||
if (mode != mSortMode.value) {
|
|
||||||
mSortMode.value = mode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun updateSearchQuery(query: String, context: Context) {
|
|
||||||
// Don't bother if the query is blank.
|
// Don't bother if the query is blank.
|
||||||
if (query == "") {
|
if (query == "") {
|
||||||
resetQuery()
|
resetQuery()
|
||||||
|
@ -110,4 +107,18 @@ class LibraryViewModel : ViewModel() {
|
||||||
fun updateSearchFocusStatus(value: Boolean) {
|
fun updateSearchFocusStatus(value: Boolean) {
|
||||||
mSearchHasFocus = value
|
mSearchHasFocus = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun updateSortMode(@IdRes itemId: Int) {
|
||||||
|
val mode = when (itemId) {
|
||||||
|
R.id.option_sort_none -> SortMode.NONE
|
||||||
|
R.id.option_sort_alpha_down -> SortMode.ALPHA_DOWN
|
||||||
|
R.id.option_sort_alpha_up -> SortMode.ALPHA_UP
|
||||||
|
|
||||||
|
else -> SortMode.NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode != mSortMode.value) {
|
||||||
|
mSortMode.value = mode
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,10 @@ import org.oxycblt.auxio.recycler.viewholders.AlbumViewHolder
|
||||||
import org.oxycblt.auxio.recycler.viewholders.ArtistViewHolder
|
import org.oxycblt.auxio.recycler.viewholders.ArtistViewHolder
|
||||||
import org.oxycblt.auxio.recycler.viewholders.GenreViewHolder
|
import org.oxycblt.auxio.recycler.viewholders.GenreViewHolder
|
||||||
|
|
||||||
// The primary RecyclerView adapter for the library. Displays genres, artists, and albums.
|
/**
|
||||||
|
* The primary recyclerview for the library. Can display either Genres, Artists, and Albums.
|
||||||
|
* @author OxygenCobalt
|
||||||
|
*/
|
||||||
class LibraryAdapter(
|
class LibraryAdapter(
|
||||||
private val showMode: ShowMode,
|
private val showMode: ShowMode,
|
||||||
private val doOnClick: (data: BaseModel) -> Unit,
|
private val doOnClick: (data: BaseModel) -> Unit,
|
||||||
|
|
|
@ -17,6 +17,10 @@ import org.oxycblt.auxio.recycler.viewholders.GenreViewHolder
|
||||||
import org.oxycblt.auxio.recycler.viewholders.HeaderViewHolder
|
import org.oxycblt.auxio.recycler.viewholders.HeaderViewHolder
|
||||||
import org.oxycblt.auxio.recycler.viewholders.SongViewHolder
|
import org.oxycblt.auxio.recycler.viewholders.SongViewHolder
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Multi-ViewHolder adapter that displays the results of a search query.
|
||||||
|
* @author OxygenCobalt
|
||||||
|
*/
|
||||||
class SearchAdapter(
|
class SearchAdapter(
|
||||||
private val doOnClick: (data: BaseModel) -> Unit,
|
private val doOnClick: (data: BaseModel) -> Unit,
|
||||||
private val doOnLongClick: (data: BaseModel, view: View) -> Unit
|
private val doOnLongClick: (data: BaseModel, view: View) -> Unit
|
||||||
|
|
|
@ -17,6 +17,11 @@ import org.oxycblt.auxio.databinding.FragmentLoadingBinding
|
||||||
import org.oxycblt.auxio.music.MusicStore
|
import org.oxycblt.auxio.music.MusicStore
|
||||||
import org.oxycblt.auxio.music.processing.MusicLoaderResponse
|
import org.oxycblt.auxio.music.processing.MusicLoaderResponse
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The intermediary [Fragment] that asks for the READ_EXTERNAL_STORAGE permission and runs
|
||||||
|
* the music loading process in the background.
|
||||||
|
* @author OxygenCobalt
|
||||||
|
*/
|
||||||
class LoadingFragment : Fragment(R.layout.fragment_loading) {
|
class LoadingFragment : Fragment(R.layout.fragment_loading) {
|
||||||
|
|
||||||
private val loadingModel: LoadingViewModel by activityViewModels {
|
private val loadingModel: LoadingViewModel by activityViewModels {
|
||||||
|
|
|
@ -12,6 +12,11 @@ import kotlinx.coroutines.withContext
|
||||||
import org.oxycblt.auxio.music.MusicStore
|
import org.oxycblt.auxio.music.MusicStore
|
||||||
import org.oxycblt.auxio.music.processing.MusicLoaderResponse
|
import org.oxycblt.auxio.music.processing.MusicLoaderResponse
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [ViewModel] responsible for getting the music loading process going and managing the response
|
||||||
|
* returned.
|
||||||
|
* @author OxygenCobalt
|
||||||
|
*/
|
||||||
class LoadingViewModel(private val app: Application) : ViewModel() {
|
class LoadingViewModel(private val app: Application) : ViewModel() {
|
||||||
// UI control
|
// UI control
|
||||||
private val mResponse = MutableLiveData<MusicLoaderResponse>()
|
private val mResponse = MutableLiveData<MusicLoaderResponse>()
|
||||||
|
|
|
@ -9,7 +9,8 @@ import org.oxycblt.auxio.music.processing.MusicSorter
|
||||||
import org.oxycblt.auxio.recycler.ShowMode
|
import org.oxycblt.auxio.recycler.ShowMode
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main storage for music items. Use [MusicStore.getInstance] to get the instance.
|
* The main storage for music items. Use [MusicStore.getInstance] to get the single instance of it.
|
||||||
|
* TODO: Add a viewmodel so that UI elements aren't messing with the shared object.
|
||||||
*/
|
*/
|
||||||
class MusicStore private constructor() {
|
class MusicStore private constructor() {
|
||||||
private var mGenres = listOf<Genre>()
|
private var mGenres = listOf<Genre>()
|
||||||
|
|
|
@ -80,6 +80,7 @@ class PlaybackService : Service(), Player.EventListener, PlaybackStateManager.Ca
|
||||||
return START_NOT_STICKY
|
return START_NOT_STICKY
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// No binding, service is headless. Deliver updates through PlaybackStateManager instead.
|
||||||
override fun onBind(intent: Intent): IBinder? = null
|
override fun onBind(intent: Intent): IBinder? = null
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
|
|
|
@ -20,7 +20,7 @@ import org.oxycblt.auxio.playback.state.PlaybackMode
|
||||||
import org.oxycblt.auxio.playback.state.PlaybackStateManager
|
import org.oxycblt.auxio.playback.state.PlaybackStateManager
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The ViewModel that provides a UI-Focused frontend for [PlaybackStateManager].
|
* The ViewModel that provides a UI frontend for [PlaybackStateManager].
|
||||||
* @author OxygenCobalt
|
* @author OxygenCobalt
|
||||||
*/
|
*/
|
||||||
class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
|
class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
|
||||||
|
|
|
@ -95,10 +95,13 @@ class QueueAdapter(
|
||||||
fun removeItem(adapterIndex: Int) {
|
fun removeItem(adapterIndex: Int) {
|
||||||
data.removeAt(adapterIndex)
|
data.removeAt(adapterIndex)
|
||||||
|
|
||||||
// Check for two things:
|
/*
|
||||||
// If the data from the next queue is now entirely empty [Signified by a header at the end]
|
* Check for two things:
|
||||||
// Or if the data from the last queue is now entirely empty [Signified by there being 2 headers with no items in between]
|
* If the data from the next queue is now entirely empty [Signified by a header at the end]
|
||||||
// If so, remove that item and the removed item in a range. Otherwise just remove the item.
|
* Or if the data from the last queue is now entirely empty [Signified by there being
|
||||||
|
* 2 headers with no items in between]
|
||||||
|
* If so, remove the header and the removed item in a range. Otherwise just remove the item.
|
||||||
|
*/
|
||||||
if (data[data.lastIndex] is Header) {
|
if (data[data.lastIndex] is Header) {
|
||||||
val lastIndex = data.lastIndex
|
val lastIndex = data.lastIndex
|
||||||
|
|
||||||
|
@ -114,7 +117,7 @@ class QueueAdapter(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clearUserQueue() {
|
private fun clearUserQueue() {
|
||||||
val nextQueueHeaderIndex = data.indexOfLast { it is Header }
|
val nextQueueHeaderIndex = data.indexOfLast { it is Header }
|
||||||
val slice = data.slice(0 until nextQueueHeaderIndex)
|
val slice = data.slice(0 until nextQueueHeaderIndex)
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,11 @@ import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
import kotlin.math.sign
|
import kotlin.math.sign
|
||||||
|
|
||||||
// The drag callback used for the Queue RecyclerView.
|
/**
|
||||||
|
* The Drag callback used by the queue recyclerview. Delivers updates to [PlaybackViewModel]
|
||||||
|
* and [QueueAdapter] simultaneously.
|
||||||
|
* @author OxygenCobalt
|
||||||
|
*/
|
||||||
class QueueDragCallback(private val playbackModel: PlaybackViewModel) : ItemTouchHelper.Callback() {
|
class QueueDragCallback(private val playbackModel: PlaybackViewModel) : ItemTouchHelper.Callback() {
|
||||||
private lateinit var queueAdapter: QueueAdapter
|
private lateinit var queueAdapter: QueueAdapter
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ import org.oxycblt.auxio.playback.state.PlaybackMode
|
||||||
* edit them as well.
|
* edit them as well.
|
||||||
*
|
*
|
||||||
* Instantiation is done by the navigation component, **do not instantiate this fragment manually.**
|
* Instantiation is done by the navigation component, **do not instantiate this fragment manually.**
|
||||||
|
* @author OxygenCobalt
|
||||||
*/
|
*/
|
||||||
class QueueFragment : Fragment() {
|
class QueueFragment : Fragment() {
|
||||||
private val playbackModel: PlaybackViewModel by activityViewModels()
|
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||||
|
|
|
@ -3,7 +3,11 @@ package org.oxycblt.auxio.recycler
|
||||||
import androidx.recyclerview.widget.DiffUtil
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
import org.oxycblt.auxio.music.BaseModel
|
import org.oxycblt.auxio.music.BaseModel
|
||||||
|
|
||||||
// Base Diff callback
|
/**
|
||||||
|
* A re-usable diff callback for all [BaseModel] implementations.
|
||||||
|
* **Use this instead of creating a DiffCallback for each adapter.**
|
||||||
|
* @author OxygenCobalt
|
||||||
|
*/
|
||||||
class DiffCallback<T : BaseModel> : DiffUtil.ItemCallback<T>() {
|
class DiffCallback<T : BaseModel> : DiffUtil.ItemCallback<T>() {
|
||||||
override fun areContentsTheSame(oldItem: T, newItem: T): Boolean {
|
override fun areContentsTheSame(oldItem: T, newItem: T): Boolean {
|
||||||
return oldItem.hashCode() == newItem.hashCode()
|
return oldItem.hashCode() == newItem.hashCode()
|
||||||
|
|
|
@ -1,10 +1,18 @@
|
||||||
package org.oxycblt.auxio.recycler
|
package org.oxycblt.auxio.recycler
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An enum for determining what items to show in a given list.
|
||||||
|
* @author OxygenCobalt
|
||||||
|
*/
|
||||||
enum class ShowMode {
|
enum class ShowMode {
|
||||||
SHOW_GENRES, SHOW_ARTISTS, SHOW_ALBUMS, SHOW_SONGS;
|
SHOW_GENRES, SHOW_ARTISTS, SHOW_ALBUMS, SHOW_SONGS;
|
||||||
|
|
||||||
// Make a slice of all the values that this ShowMode covers.
|
/**
|
||||||
// ex. SHOW_ARTISTS would return SHOW_ARTISTS, SHOW_ALBUMS, and SHOW_SONGS
|
* Make a slice of all the values that this ShowMode covers.
|
||||||
|
*
|
||||||
|
* ex. SHOW_ARTISTS would return SHOW_ARTISTS, SHOW_ALBUMS, and SHOW_SONGS
|
||||||
|
* @return The values that this ShowMode covers.
|
||||||
|
*/
|
||||||
fun getChildren(): List<ShowMode> {
|
fun getChildren(): List<ShowMode> {
|
||||||
val vals = values()
|
val vals = values()
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,12 @@ import org.oxycblt.auxio.music.Artist
|
||||||
import org.oxycblt.auxio.music.BaseModel
|
import org.oxycblt.auxio.music.BaseModel
|
||||||
import org.oxycblt.auxio.music.Song
|
import org.oxycblt.auxio.music.Song
|
||||||
|
|
||||||
// Sorting modes
|
/**
|
||||||
|
* An enum for the current sorting mode. Contains helper functions to sort lists based
|
||||||
|
* off the given sorting mode.
|
||||||
|
* @property iconRes The icon for this [SortMode]
|
||||||
|
* @author OxygenCobalt
|
||||||
|
*/
|
||||||
enum class SortMode(val iconRes: Int) {
|
enum class SortMode(val iconRes: Int) {
|
||||||
// Icons for each mode are assigned to the enums themselves
|
// Icons for each mode are assigned to the enums themselves
|
||||||
NONE(R.drawable.ic_sort_none),
|
NONE(R.drawable.ic_sort_none),
|
||||||
|
|
|
@ -15,6 +15,11 @@ import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||||
import org.oxycblt.auxio.playback.state.PlaybackMode
|
import org.oxycblt.auxio.playback.state.PlaybackMode
|
||||||
import org.oxycblt.auxio.ui.setupSongActions
|
import org.oxycblt.auxio.ui.setupSongActions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [Fragment] that shows a list of all songs on the device. Contains options to search/shuffle
|
||||||
|
* them.
|
||||||
|
* @author OxygenCobalt
|
||||||
|
*/
|
||||||
class SongsFragment : Fragment() {
|
class SongsFragment : Fragment() {
|
||||||
private val playbackModel: PlaybackViewModel by activityViewModels()
|
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,10 @@ import org.oxycblt.auxio.playback.state.PlaybackMode
|
||||||
|
|
||||||
// Functions for managing UI elements [Not Colors]
|
// Functions for managing UI elements [Not Colors]
|
||||||
|
|
||||||
// Apply a color to a Menu Item
|
/**
|
||||||
|
* Apply a text color to a [MenuItem]
|
||||||
|
* @param color The text color that should be applied.
|
||||||
|
*/
|
||||||
fun MenuItem.applyColor(@ColorInt color: Int) {
|
fun MenuItem.applyColor(@ColorInt color: Int) {
|
||||||
SpannableString(title).apply {
|
SpannableString(title).apply {
|
||||||
setSpan(ForegroundColorSpan(color), 0, length, SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE)
|
setSpan(ForegroundColorSpan(color), 0, length, SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||||
|
@ -29,7 +32,10 @@ fun MenuItem.applyColor(@ColorInt color: Int) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable an ImageButton
|
/**
|
||||||
|
* Disable an image button.
|
||||||
|
* @param context [Context] required to change the [ImageButton]s color.
|
||||||
|
*/
|
||||||
fun ImageButton.disable(context: Context) {
|
fun ImageButton.disable(context: Context) {
|
||||||
if (isEnabled) {
|
if (isEnabled) {
|
||||||
imageTintList = ColorStateList.valueOf(
|
imageTintList = ColorStateList.valueOf(
|
||||||
|
@ -40,10 +46,17 @@ fun ImageButton.disable(context: Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a [Toast] from a [String]
|
||||||
|
* @param context [Context] required to create the toast
|
||||||
|
*/
|
||||||
fun String.createToast(context: Context) {
|
fun String.createToast(context: Context) {
|
||||||
Toast.makeText(context.applicationContext, this, Toast.LENGTH_SHORT).show()
|
Toast.makeText(context.applicationContext, this, Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show actions for a song item, such as the ones found in [org.oxycblt.auxio.songs.SongsFragment]
|
||||||
|
*/
|
||||||
fun PopupMenu.setupSongActions(song: Song, context: Context, playbackModel: PlaybackViewModel) {
|
fun PopupMenu.setupSongActions(song: Song, context: Context, playbackModel: PlaybackViewModel) {
|
||||||
setOnMenuItemClickListener {
|
setOnMenuItemClickListener {
|
||||||
when (it.itemId) {
|
when (it.itemId) {
|
||||||
|
@ -68,11 +81,13 @@ fun PopupMenu.setupSongActions(song: Song, context: Context, playbackModel: Play
|
||||||
}
|
}
|
||||||
inflateAndShow(R.menu.menu_song_actions)
|
inflateAndShow(R.menu.menu_song_actions)
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Show actions for a song item, such as the ones found in [org.oxycblt.auxio.songs.SongsFragment]
|
||||||
|
*/
|
||||||
fun PopupMenu.setupAlbumSongActions(
|
fun PopupMenu.setupAlbumSongActions(
|
||||||
song: Song,
|
song: Song,
|
||||||
context: Context,
|
context: Context,
|
||||||
detailViewModel: DetailViewModel,
|
detailModel: DetailViewModel,
|
||||||
playbackModel: PlaybackViewModel
|
playbackModel: PlaybackViewModel
|
||||||
) {
|
) {
|
||||||
setOnMenuItemClickListener {
|
setOnMenuItemClickListener {
|
||||||
|
@ -85,7 +100,7 @@ fun PopupMenu.setupAlbumSongActions(
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.action_go_artist -> {
|
R.id.action_go_artist -> {
|
||||||
detailViewModel.doNavToParent()
|
detailModel.doNavToParent()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,6 +115,9 @@ fun PopupMenu.setupAlbumSongActions(
|
||||||
inflateAndShow(R.menu.menu_album_song_actions)
|
inflateAndShow(R.menu.menu_album_song_actions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show actions for an [Album]
|
||||||
|
*/
|
||||||
fun PopupMenu.setupAlbumActions(
|
fun PopupMenu.setupAlbumActions(
|
||||||
album: Album,
|
album: Album,
|
||||||
context: Context,
|
context: Context,
|
||||||
|
@ -130,6 +148,9 @@ fun PopupMenu.setupAlbumActions(
|
||||||
inflateAndShow(R.menu.menu_album_actions)
|
inflateAndShow(R.menu.menu_album_actions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show actions for an [Artist]
|
||||||
|
*/
|
||||||
fun PopupMenu.setupArtistActions(
|
fun PopupMenu.setupArtistActions(
|
||||||
artist: Artist,
|
artist: Artist,
|
||||||
context: Context,
|
context: Context,
|
||||||
|
@ -160,6 +181,9 @@ fun PopupMenu.setupArtistActions(
|
||||||
inflateAndShow(R.menu.menu_detail)
|
inflateAndShow(R.menu.menu_detail)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show actions for a [Genre]
|
||||||
|
*/
|
||||||
fun PopupMenu.setupGenreActions(
|
fun PopupMenu.setupGenreActions(
|
||||||
genre: Genre,
|
genre: Genre,
|
||||||
context: Context,
|
context: Context,
|
||||||
|
@ -190,6 +214,9 @@ fun PopupMenu.setupGenreActions(
|
||||||
inflateAndShow(R.menu.menu_detail)
|
inflateAndShow(R.menu.menu_detail)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcut method that inflates a menu and shows the action menu.
|
||||||
|
*/
|
||||||
private fun PopupMenu.inflateAndShow(@MenuRes menuRes: Int) {
|
private fun PopupMenu.inflateAndShow(@MenuRes menuRes: Int) {
|
||||||
inflate(menuRes)
|
inflate(menuRes)
|
||||||
show()
|
show()
|
||||||
|
|
|
@ -36,7 +36,13 @@ private val ACCENTS = listOf(
|
||||||
|
|
||||||
val accent = ACCENTS[5]
|
val accent = ACCENTS[5]
|
||||||
|
|
||||||
// Get the transparent variant of a color int
|
/**
|
||||||
|
* Gets the transparent form of a color.
|
||||||
|
* @param context [Context] required to create the color
|
||||||
|
* @param color The RESOURCE ID for the color
|
||||||
|
* @param alpha The new alpha that wants to be applied
|
||||||
|
* @return The new, resolved transparent color
|
||||||
|
*/
|
||||||
@ColorInt
|
@ColorInt
|
||||||
fun getTransparentAccent(context: Context, @ColorRes color: Int, alpha: Int): Int {
|
fun getTransparentAccent(context: Context, @ColorRes color: Int, alpha: Int): Int {
|
||||||
return ColorUtils.setAlphaComponent(
|
return ColorUtils.setAlphaComponent(
|
||||||
|
@ -45,13 +51,19 @@ fun getTransparentAccent(context: Context, @ColorRes color: Int, alpha: Int): In
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the inactive transparency of an accent
|
/**
|
||||||
|
* Get the inactive alpha of an accent.
|
||||||
|
*/
|
||||||
@ColorInt
|
@ColorInt
|
||||||
fun getInactiveAlpha(@ColorRes color: Int): Int {
|
fun getInactiveAlpha(@ColorRes color: Int): Int {
|
||||||
return if (color == R.color.yellow) 100 else 150
|
return if (color == R.color.yellow) 100 else 150
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert an integer to a color
|
/**
|
||||||
|
* Resolve a color.
|
||||||
|
* @param context [Context] required
|
||||||
|
* @return The resolved color, black if the resolving process failed.
|
||||||
|
*/
|
||||||
@ColorInt
|
@ColorInt
|
||||||
fun Int.toColor(context: Context): Int {
|
fun Int.toColor(context: Context): Int {
|
||||||
return try {
|
return try {
|
||||||
|
@ -62,7 +74,12 @@ fun Int.toColor(context: Context): Int {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve an attribute into a color
|
/**
|
||||||
|
* Resolve an attribute into a color.
|
||||||
|
* @param context [Context] required
|
||||||
|
* @param attr The Resource ID for the attribute
|
||||||
|
* @return The resolved color for that attribute. Black if the process failed.
|
||||||
|
*/
|
||||||
@ColorInt
|
@ColorInt
|
||||||
fun resolveAttr(context: Context, @AttrRes attr: Int): Int {
|
fun resolveAttr(context: Context, @AttrRes attr: Int): Int {
|
||||||
// Convert the attribute into its color
|
// Convert the attribute into its color
|
||||||
|
|
Loading…
Reference in a new issue