Improve documentation slightly

Add some more documentation to the code.
This commit is contained in:
OxygenCobalt 2020-11-25 11:18:36 -07:00
parent fd4b7b247f
commit 38afa8f4d2
28 changed files with 188 additions and 53 deletions

View file

@ -120,6 +120,9 @@ class MainFragment : Fragment() {
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 {
return (
controller.currentDestination!!.id == R.id.album_detail_fragment &&
@ -157,6 +160,8 @@ class MainFragment : Fragment() {
}
}
//
return false
}
}

View file

@ -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 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.
* @author OxygenCobalt
*/
data class PlaybackState(
val id: Long = 0L,

View file

@ -6,6 +6,7 @@ package org.oxycblt.auxio.database
* @property songId The song id for this queue item
* @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
* @author OxygenCobalt
*/
data class QueueItem(
var id: Long = 0L,

View file

@ -11,13 +11,17 @@ import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import org.oxycblt.auxio.R
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.playback.PlaybackViewModel
import org.oxycblt.auxio.playback.state.PlaybackMode
import org.oxycblt.auxio.ui.disable
import org.oxycblt.auxio.ui.setupAlbumSongActions
/**
* The [DetailFragment] for an album.
* @author OxygenCobalt
*/
class AlbumDetailFragment : DetailFragment() {
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) },
doOnLongClick = { data, view ->
PopupMenu(requireContext(), view).setupAlbumSongActions(

View file

@ -11,13 +11,17 @@ import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import org.oxycblt.auxio.R
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.MusicStore
import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.ui.disable
import org.oxycblt.auxio.ui.setupAlbumActions
/**
* The [DetailFragment] for an artist.
* @author OxygenCobalt
*/
class ArtistDetailFragment : DetailFragment() {
private val args: ArtistDetailFragmentArgs by navArgs()
private val playbackModel: PlaybackViewModel by activityViewModels()
@ -41,7 +45,7 @@ class ArtistDetailFragment : DetailFragment() {
)
}
val albumAdapter = DetailAlbumAdapter(
val albumAdapter = ArtistAlbumAdapter(
doOnClick = {
if (!detailModel.isNavigating) {
detailModel.updateNavigationStatus(true)

View file

@ -8,7 +8,10 @@ import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.Genre
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() {
private var mIsNavigating = false
val isNavigating: Boolean get() = mIsNavigating

View file

@ -11,12 +11,16 @@ import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import org.oxycblt.auxio.R
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.playback.PlaybackViewModel
import org.oxycblt.auxio.ui.disable
import org.oxycblt.auxio.ui.setupArtistActions
/**
* The [DetailFragment] for a genre.
* @author OxygenCobalt
*/
class GenreDetailFragment : DetailFragment() {
private val args: GenreDetailFragmentArgs by navArgs()
@ -41,7 +45,7 @@ class GenreDetailFragment : DetailFragment() {
)
}
val artistAdapter = DetailArtistAdapter(
val artistAdapter = GenreArtistAdapter(
doOnClick = {
if (!detailModel.isNavigating) {
detailModel.updateNavigationStatus(true)

View file

@ -9,10 +9,13 @@ import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.recycler.DiffCallback
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 doOnLongClick: (data: Song, view: View) -> Unit
) : ListAdapter<Song, DetailSongAdapter.ViewHolder>(DiffCallback()) {
) : ListAdapter<Song, AlbumSongAdapter.ViewHolder>(DiffCallback()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(
ItemAlbumSongBinding.inflate(LayoutInflater.from(parent.context))

View file

@ -10,7 +10,10 @@ import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.recycler.DiffCallback
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 doOnLongClick: (data: Album, view: View) -> Unit,
) : ListAdapter<Album, RecyclerView.ViewHolder>(DiffCallback()) {

View file

@ -9,10 +9,13 @@ import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.recycler.DiffCallback
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 doOnLongClick: (data: Artist, view: View) -> Unit
) : ListAdapter<Artist, DetailArtistAdapter.ViewHolder>(DiffCallback()) {
) : ListAdapter<Artist, GenreArtistAdapter.ViewHolder>(DiffCallback()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(

View file

@ -69,7 +69,7 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
setOnMenuItemClickListener {
if (it.itemId != R.id.action_search) {
libraryModel.updateSortMode(it)
libraryModel.updateSortMode(it.itemId)
} else {
// Do whatever this is in order to make the SearchView focusable.
(it.actionView as SearchView).isIconified = false
@ -92,7 +92,7 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
searchView.setOnQueryTextListener(this@LibraryFragment)
searchView.setOnQueryTextFocusChangeListener { _, hasFocus ->
libraryModel.updateSearchFocusStatus(hasFocus)
libraryModel.updateSearchQuery(searchView.query.toString(), requireContext())
libraryModel.doSearch(searchView.query.toString(), requireContext())
item.isVisible = !hasFocus
}
@ -177,7 +177,7 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean = false
override fun onQueryTextChange(query: String): Boolean {
libraryModel.updateSearchQuery(query, requireContext())
libraryModel.doSearch(query, requireContext())
return true
}

View file

@ -1,7 +1,7 @@
package org.oxycblt.auxio.library
import android.content.Context
import android.view.MenuItem
import androidx.annotation.IdRes
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
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.SortMode
/**
* A [ViewModel] that manages what [LibraryFragment] is currently showing, and also the search
* functionality.
* @author OxygenCobalt
*/
class LibraryViewModel : ViewModel() {
private var mIsNavigating = false
val isNavigating: Boolean get() = mIsNavigating
@ -31,21 +36,13 @@ class LibraryViewModel : ViewModel() {
private val mSearchResults = MutableLiveData(listOf<BaseModel>())
val searchResults: LiveData<List<BaseModel>> get() = mSearchResults
fun updateSortMode(item: MenuItem) {
val mode = when (item.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
}
}
fun updateSearchQuery(query: String, context: Context) {
/**
* Perform a search of the music library, given a query.
* Results are pushed to [searchResults].
* @param query The query for this search
* @param context The context needed to create the header text
*/
fun doSearch(query: String, context: Context) {
// Don't bother if the query is blank.
if (query == "") {
resetQuery()
@ -110,4 +107,18 @@ class LibraryViewModel : ViewModel() {
fun updateSearchFocusStatus(value: Boolean) {
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
}
}
}

View file

@ -12,7 +12,10 @@ import org.oxycblt.auxio.recycler.viewholders.AlbumViewHolder
import org.oxycblt.auxio.recycler.viewholders.ArtistViewHolder
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(
private val showMode: ShowMode,
private val doOnClick: (data: BaseModel) -> Unit,

View file

@ -17,6 +17,10 @@ import org.oxycblt.auxio.recycler.viewholders.GenreViewHolder
import org.oxycblt.auxio.recycler.viewholders.HeaderViewHolder
import org.oxycblt.auxio.recycler.viewholders.SongViewHolder
/**
* A Multi-ViewHolder adapter that displays the results of a search query.
* @author OxygenCobalt
*/
class SearchAdapter(
private val doOnClick: (data: BaseModel) -> Unit,
private val doOnLongClick: (data: BaseModel, view: View) -> Unit

View file

@ -17,6 +17,11 @@ import org.oxycblt.auxio.databinding.FragmentLoadingBinding
import org.oxycblt.auxio.music.MusicStore
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) {
private val loadingModel: LoadingViewModel by activityViewModels {

View file

@ -12,6 +12,11 @@ import kotlinx.coroutines.withContext
import org.oxycblt.auxio.music.MusicStore
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() {
// UI control
private val mResponse = MutableLiveData<MusicLoaderResponse>()

View file

@ -9,7 +9,8 @@ import org.oxycblt.auxio.music.processing.MusicSorter
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() {
private var mGenres = listOf<Genre>()

View file

@ -80,6 +80,7 @@ class PlaybackService : Service(), Player.EventListener, PlaybackStateManager.Ca
return START_NOT_STICKY
}
// No binding, service is headless. Deliver updates through PlaybackStateManager instead.
override fun onBind(intent: Intent): IBinder? = null
override fun onCreate() {

View file

@ -20,7 +20,7 @@ import org.oxycblt.auxio.playback.state.PlaybackMode
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
*/
class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {

View file

@ -95,10 +95,13 @@ class QueueAdapter(
fun removeItem(adapterIndex: Int) {
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]
// 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 that item and the removed item in a range. Otherwise just remove the item.
/*
* Check for two things:
* If the data from the next queue is now entirely empty [Signified by a header at the end]
* 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) {
val lastIndex = data.lastIndex
@ -114,7 +117,7 @@ class QueueAdapter(
}
}
fun clearUserQueue() {
private fun clearUserQueue() {
val nextQueueHeaderIndex = data.indexOfLast { it is Header }
val slice = data.slice(0 until nextQueueHeaderIndex)

View file

@ -8,7 +8,11 @@ import kotlin.math.max
import kotlin.math.min
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() {
private lateinit var queueAdapter: QueueAdapter

View file

@ -20,6 +20,7 @@ import org.oxycblt.auxio.playback.state.PlaybackMode
* edit them as well.
*
* Instantiation is done by the navigation component, **do not instantiate this fragment manually.**
* @author OxygenCobalt
*/
class QueueFragment : Fragment() {
private val playbackModel: PlaybackViewModel by activityViewModels()

View file

@ -3,7 +3,11 @@ package org.oxycblt.auxio.recycler
import androidx.recyclerview.widget.DiffUtil
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>() {
override fun areContentsTheSame(oldItem: T, newItem: T): Boolean {
return oldItem.hashCode() == newItem.hashCode()

View file

@ -1,10 +1,18 @@
package org.oxycblt.auxio.recycler
/**
* An enum for determining what items to show in a given list.
* @author OxygenCobalt
*/
enum class ShowMode {
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> {
val vals = values()

View file

@ -6,7 +6,12 @@ import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.BaseModel
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) {
// Icons for each mode are assigned to the enums themselves
NONE(R.drawable.ic_sort_none),

View file

@ -15,6 +15,11 @@ import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.playback.state.PlaybackMode
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() {
private val playbackModel: PlaybackViewModel by activityViewModels()

View file

@ -21,7 +21,10 @@ import org.oxycblt.auxio.playback.state.PlaybackMode
// 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) {
SpannableString(title).apply {
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) {
if (isEnabled) {
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) {
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) {
setOnMenuItemClickListener {
when (it.itemId) {
@ -68,11 +81,13 @@ fun PopupMenu.setupSongActions(song: Song, context: Context, playbackModel: Play
}
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(
song: Song,
context: Context,
detailViewModel: DetailViewModel,
detailModel: DetailViewModel,
playbackModel: PlaybackViewModel
) {
setOnMenuItemClickListener {
@ -85,7 +100,7 @@ fun PopupMenu.setupAlbumSongActions(
}
R.id.action_go_artist -> {
detailViewModel.doNavToParent()
detailModel.doNavToParent()
true
}
@ -100,6 +115,9 @@ fun PopupMenu.setupAlbumSongActions(
inflateAndShow(R.menu.menu_album_song_actions)
}
/**
* Show actions for an [Album]
*/
fun PopupMenu.setupAlbumActions(
album: Album,
context: Context,
@ -130,6 +148,9 @@ fun PopupMenu.setupAlbumActions(
inflateAndShow(R.menu.menu_album_actions)
}
/**
* Show actions for an [Artist]
*/
fun PopupMenu.setupArtistActions(
artist: Artist,
context: Context,
@ -160,6 +181,9 @@ fun PopupMenu.setupArtistActions(
inflateAndShow(R.menu.menu_detail)
}
/**
* Show actions for a [Genre]
*/
fun PopupMenu.setupGenreActions(
genre: Genre,
context: Context,
@ -190,6 +214,9 @@ fun PopupMenu.setupGenreActions(
inflateAndShow(R.menu.menu_detail)
}
/**
* Shortcut method that inflates a menu and shows the action menu.
*/
private fun PopupMenu.inflateAndShow(@MenuRes menuRes: Int) {
inflate(menuRes)
show()

View file

@ -36,7 +36,13 @@ private val ACCENTS = listOf(
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
fun getTransparentAccent(context: Context, @ColorRes color: Int, alpha: Int): Int {
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
fun getInactiveAlpha(@ColorRes color: Int): Int {
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
fun Int.toColor(context: Context): Int {
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
fun resolveAttr(context: Context, @AttrRes attr: Int): Int {
// Convert the attribute into its color