diff --git a/app/src/main/java/org/oxycblt/auxio/MainFragment.kt b/app/src/main/java/org/oxycblt/auxio/MainFragment.kt index e1b80594d..87e238161 100644 --- a/app/src/main/java/org/oxycblt/auxio/MainFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/MainFragment.kt @@ -83,8 +83,8 @@ class MainFragment : Fragment() { } navController?.let { controller -> - binding.navBar.setOnNavigationItemSelectedListener { - navigateWithItem(controller, it) + binding.navBar.setOnNavigationItemSelectedListener { item -> + navigateWithItem(controller, item) } } } @@ -94,12 +94,12 @@ class MainFragment : Fragment() { // Change CompactPlaybackFragment's visibility here so that an animation occurs. handleCompactPlaybackVisibility(binding, playbackModel.song.value) - playbackModel.song.observe(viewLifecycleOwner) { - handleCompactPlaybackVisibility(binding, it) + playbackModel.song.observe(viewLifecycleOwner) { song -> + handleCompactPlaybackVisibility(binding, song) } - detailModel.navToItem.observe(viewLifecycleOwner) { - if (it != null && navController != null) { + detailModel.navToItem.observe(viewLifecycleOwner) { item -> + if (item != null && navController != null) { val curDest = navController.currentDestination?.id // SongsFragment and SettingsFragment have no navigation pathways, so correct diff --git a/app/src/main/java/org/oxycblt/auxio/coil/AlbumArtFetcher.kt b/app/src/main/java/org/oxycblt/auxio/coil/AlbumArtFetcher.kt index 7e4019822..29fe947ba 100644 --- a/app/src/main/java/org/oxycblt/auxio/coil/AlbumArtFetcher.kt +++ b/app/src/main/java/org/oxycblt/auxio/coil/AlbumArtFetcher.kt @@ -15,7 +15,6 @@ import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.music.toURI import org.oxycblt.auxio.settings.SettingsManager import java.io.ByteArrayInputStream -import java.io.InputStream /** * Fetcher that returns the album art for a given [Album]. Handles settings on whether to use @@ -39,11 +38,13 @@ class AlbumArtFetcher(private val context: Context) : Fetcher { } private fun loadMediaStoreCovers(data: Album): SourceResult { - val stream: InputStream? = context.contentResolver.openInputStream(data.coverUri) + val stream = context.contentResolver.openInputStream(data.coverUri) - stream?.let { stm -> + if (stream != null) { + // Don't close the stream here as it will cause an error later from an attempted read. + // This stream still seems to close itself at some point, so its fine. return SourceResult( - source = stm.source().buffer(), + source = stream.source().buffer(), mimeType = context.contentResolver.getType(data.coverUri), dataSource = DataSource.DISK ) diff --git a/app/src/main/java/org/oxycblt/auxio/coil/MosaicFetcher.kt b/app/src/main/java/org/oxycblt/auxio/coil/MosaicFetcher.kt index 4b324bd0a..b5c336403 100644 --- a/app/src/main/java/org/oxycblt/auxio/coil/MosaicFetcher.kt +++ b/app/src/main/java/org/oxycblt/auxio/coil/MosaicFetcher.kt @@ -34,6 +34,7 @@ class MosaicFetcher(private val context: Context) : Fetcher { size: Size, options: Options ): FetchResult { + options.allowRgb565 // Get the URIs for either a genre or artist val uris = mutableListOf() diff --git a/app/src/main/java/org/oxycblt/auxio/database/PlaybackStateDatabase.kt b/app/src/main/java/org/oxycblt/auxio/database/PlaybackStateDatabase.kt index a5bd1853b..439f187e7 100644 --- a/app/src/main/java/org/oxycblt/auxio/database/PlaybackStateDatabase.kt +++ b/app/src/main/java/org/oxycblt/auxio/database/PlaybackStateDatabase.kt @@ -132,7 +132,6 @@ class PlaybackStateDatabase(context: Context) : assertBackgroundThread() val database = writableDatabase - var state: PlaybackState? = null try { diff --git a/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt index 2edd752e9..12e79591d 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt @@ -55,16 +55,13 @@ class AlbumDetailFragment : DetailFragment() { binding.lifecycleOwner = this - setupToolbar(R.menu.menu_album_detail) { - when (it) { - R.id.action_queue_add -> { - playbackModel.addToUserQueue(detailModel.currentAlbum.value!!) - getString(R.string.label_queue_added).createToast(requireContext()) - - true - } - - else -> false + setupToolbar(R.menu.menu_album_detail) { itemId -> + if (itemId == R.id.action_queue_add) { + playbackModel.addToUserQueue(detailModel.currentAlbum.value!!) + getString(R.string.label_queue_added).createToast(requireContext()) + true + } else { + false } } @@ -83,18 +80,18 @@ class AlbumDetailFragment : DetailFragment() { detailAdapter.submitList(data) } - detailModel.navToItem.observe(viewLifecycleOwner) { - when (it) { + detailModel.navToItem.observe(viewLifecycleOwner) { item -> + when (item) { // Songs should be scrolled to if the album matches, or a new detail // fragment should be launched otherwise. is Song -> { - if (detailModel.currentAlbum.value!!.id == it.album.id) { - scrollToItem(it.id) + if (detailModel.currentAlbum.value!!.id == item.album.id) { + scrollToItem(item.id) detailModel.doneWithNavToItem() } else { findNavController().navigate( - AlbumDetailFragmentDirections.actionShowAlbum(it.album.id) + AlbumDetailFragmentDirections.actionShowAlbum(item.album.id) ) } } @@ -102,12 +99,12 @@ class AlbumDetailFragment : DetailFragment() { // If the album matches, no need to do anything. Otherwise launch a new // detail fragment. is Album -> { - if (detailModel.currentAlbum.value!!.id == it.id) { + if (detailModel.currentAlbum.value!!.id == item.id) { binding.detailRecycler.scrollToPosition(0) detailModel.doneWithNavToItem() } else { findNavController().navigate( - AlbumDetailFragmentDirections.actionShowAlbum(it.id) + AlbumDetailFragmentDirections.actionShowAlbum(item.id) ) } } @@ -115,7 +112,7 @@ class AlbumDetailFragment : DetailFragment() { // Always launch a new ArtistDetailFragment. is Artist -> { findNavController().navigate( - AlbumDetailFragmentDirections.actionShowArtist(it.id) + AlbumDetailFragmentDirections.actionShowArtist(item.id) ) } @@ -125,19 +122,19 @@ class AlbumDetailFragment : DetailFragment() { // --- PLAYBACKVIEWMODEL SETUP --- - playbackModel.song.observe(viewLifecycleOwner) { + playbackModel.song.observe(viewLifecycleOwner) { song -> if (playbackModel.mode.value == PlaybackMode.IN_ALBUM && playbackModel.parent.value?.id == detailModel.currentAlbum.value!!.id ) { - detailAdapter.highlightSong(playbackModel.song.value, binding.detailRecycler) + detailAdapter.highlightSong(song, binding.detailRecycler) } else { // Clear the viewholders if the mode isn't ALL_SONGS detailAdapter.highlightSong(null, binding.detailRecycler) } } - playbackModel.isInUserQueue.observe(viewLifecycleOwner) { - if (it) { + playbackModel.isInUserQueue.observe(viewLifecycleOwner) { inUserQueue -> + if (inUserQueue) { detailAdapter.highlightSong(null, binding.detailRecycler) } } diff --git a/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt index 4003746e2..a5e1e49ce 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt @@ -11,7 +11,6 @@ import org.oxycblt.auxio.logD import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.music.Artist import org.oxycblt.auxio.music.BaseModel -import org.oxycblt.auxio.music.Genre import org.oxycblt.auxio.music.MusicStore import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.ui.ActionMenu @@ -44,16 +43,18 @@ class ArtistDetailFragment : DetailFragment() { val detailAdapter = ArtistDetailAdapter( detailModel, playbackModel, viewLifecycleOwner, - doOnClick = { + doOnClick = { album -> if (!detailModel.isNavigating) { - detailModel.updateNavigationStatus(true) + detailModel.setNavigating(true) findNavController().navigate( - ArtistDetailFragmentDirections.actionShowAlbum(it.id) + ArtistDetailFragmentDirections.actionShowAlbum(album.id) ) } }, - doOnLongClick = { view, data -> newMenu(view, data, ActionMenu.FLAG_IN_ARTIST) } + doOnLongClick = { view, data -> + newMenu(view, data, ActionMenu.FLAG_IN_ARTIST) + } ) // --- UI SETUP --- @@ -76,26 +77,28 @@ class ArtistDetailFragment : DetailFragment() { detailAdapter.submitList(data) } - detailModel.navToItem.observe(viewLifecycleOwner) { - if (it != null) { - // If the artist matches, no need to do anything, otherwise launch a new detail - if (it is Artist) { - if (it.id == detailModel.currentArtist.value!!.id) { + detailModel.navToItem.observe(viewLifecycleOwner) { item -> + when (item) { + is Artist -> { + if (item.id == detailModel.currentArtist.value!!.id) { binding.detailRecycler.scrollToPosition(0) detailModel.doneWithNavToItem() } else { findNavController().navigate( - ArtistDetailFragmentDirections.actionShowArtist(it.id) + ArtistDetailFragmentDirections.actionShowArtist(item.id) ) } - } else if (it !is Genre) { - // Determine the album id of the song or album, and then launch it otherwise - val albumId = if (it is Song) it.album.id else it.id - - findNavController().navigate( - ArtistDetailFragmentDirections.actionShowAlbum(albumId) - ) } + + is Album -> findNavController().navigate( + ArtistDetailFragmentDirections.actionShowAlbum(item.id) + ) + + is Song -> findNavController().navigate( + ArtistDetailFragmentDirections.actionShowAlbum(item.album.id) + ) + + else -> {} } } diff --git a/app/src/main/java/org/oxycblt/auxio/detail/DetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/DetailFragment.kt index 829f49cc4..18f4394e4 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/DetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/DetailFragment.kt @@ -35,7 +35,7 @@ abstract class DetailFragment : Fragment() { super.onResume() callback.isEnabled = true - detailModel.updateNavigationStatus(false) + detailModel.setNavigating(false) } override fun onPause() { @@ -56,7 +56,7 @@ abstract class DetailFragment : Fragment() { */ protected fun setupToolbar( @MenuRes menu: Int = -1, - onMenuClick: ((id: Int) -> Boolean)? = null + onMenuClick: ((itemId: Int) -> Boolean)? = null ) { binding.detailToolbar.apply { if (menu != -1) { @@ -67,9 +67,9 @@ abstract class DetailFragment : Fragment() { findNavController().navigateUp() } - onMenuClick?.let { - setOnMenuItemClickListener { - it(it.itemId) + onMenuClick?.let { onClick -> + setOnMenuItemClickListener { item -> + onClick(item.itemId) } } } diff --git a/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt b/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt index bb09f6311..83fc1ffa7 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt @@ -16,26 +16,27 @@ import org.oxycblt.auxio.recycler.SortMode */ class DetailViewModel : ViewModel() { private val mCurrentGenre = MutableLiveData() - private val mCurrentArtist = MutableLiveData() - private val mCurrentAlbum = MutableLiveData() - - private val mGenreSortMode = MutableLiveData(SortMode.ALPHA_DOWN) - private val mArtistSortMode = MutableLiveData(SortMode.NUMERIC_DOWN) - private val mAlbumSortMode = MutableLiveData(SortMode.NUMERIC_DOWN) - - private val mNavToItem = MutableLiveData() - private var mIsNavigating = false - val currentGenre: LiveData get() = mCurrentGenre + + private val mCurrentArtist = MutableLiveData() val currentArtist: LiveData get() = mCurrentArtist + + private val mCurrentAlbum = MutableLiveData() val currentAlbum: LiveData get() = mCurrentAlbum + private val mGenreSortMode = MutableLiveData(SortMode.ALPHA_DOWN) val genreSortMode: LiveData get() = mGenreSortMode + + private val mArtistSortMode = MutableLiveData(SortMode.NUMERIC_DOWN) val albumSortMode: LiveData get() = mAlbumSortMode + + private val mAlbumSortMode = MutableLiveData(SortMode.NUMERIC_DOWN) val artistSortMode: LiveData get() = mArtistSortMode + private var mIsNavigating = false val isNavigating: Boolean get() = mIsNavigating + private val mNavToItem = MutableLiveData() /** Flag for unified navigation. Observe this to coordinate navigation to an item's UI. */ val navToItem: LiveData get() = mNavToItem @@ -106,7 +107,7 @@ class DetailViewModel : ViewModel() { /** * Update the current navigation status to [isNavigating] */ - fun updateNavigationStatus(isNavigating: Boolean) { + fun setNavigating(isNavigating: Boolean) { mIsNavigating = isNavigating } } diff --git a/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt index 41c3fb40e..ef85b5d2a 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt @@ -43,10 +43,12 @@ class GenreDetailFragment : DetailFragment() { val detailAdapter = GenreDetailAdapter( detailModel, playbackModel, viewLifecycleOwner, - doOnClick = { - playbackModel.playSong(it, PlaybackMode.IN_GENRE) + doOnClick = { song -> + playbackModel.playSong(song, PlaybackMode.IN_GENRE) }, - doOnLongClick = { view, data -> newMenu(view, data, ActionMenu.FLAG_IN_GENRE) } + doOnLongClick = { view, data -> + newMenu(view, data, ActionMenu.FLAG_IN_GENRE) + } ) // --- UI SETUP --- @@ -69,19 +71,19 @@ class GenreDetailFragment : DetailFragment() { detailAdapter.submitList(data) } - detailModel.navToItem.observe(viewLifecycleOwner) { - when (it) { + detailModel.navToItem.observe(viewLifecycleOwner) { item -> + when (item) { // All items will launch new detail fragments. is Artist -> findNavController().navigate( - GenreDetailFragmentDirections.actionShowArtist(it.id) + GenreDetailFragmentDirections.actionShowArtist(item.id) ) is Album -> findNavController().navigate( - GenreDetailFragmentDirections.actionShowAlbum(it.id) + GenreDetailFragmentDirections.actionShowAlbum(item.id) ) is Song -> findNavController().navigate( - GenreDetailFragmentDirections.actionShowAlbum(it.album.id) + GenreDetailFragmentDirections.actionShowAlbum(item.album.id) ) else -> {} @@ -90,19 +92,19 @@ class GenreDetailFragment : DetailFragment() { // --- PLAYBACKVIEWMODEL SETUP --- - playbackModel.song.observe(viewLifecycleOwner) { + playbackModel.song.observe(viewLifecycleOwner) { song -> if (playbackModel.mode.value == PlaybackMode.IN_GENRE && playbackModel.parent.value?.id == detailModel.currentGenre.value!!.id ) { - detailAdapter.highlightSong(playbackModel.song.value, binding.detailRecycler) + detailAdapter.highlightSong(song, binding.detailRecycler) } else { // Clear the viewholders if the mode isn't ALL_SONGS detailAdapter.highlightSong(null, binding.detailRecycler) } } - playbackModel.isInUserQueue.observe(viewLifecycleOwner) { - if (it) { + playbackModel.isInUserQueue.observe(viewLifecycleOwner) { inUserQueue -> + if (inUserQueue) { detailAdapter.highlightSong(null, binding.detailRecycler) } } diff --git a/app/src/main/java/org/oxycblt/auxio/detail/adapters/AlbumDetailAdapter.kt b/app/src/main/java/org/oxycblt/auxio/detail/adapters/AlbumDetailAdapter.kt index b04900b5e..4072f6d57 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/adapters/AlbumDetailAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/adapters/AlbumDetailAdapter.kt @@ -89,8 +89,8 @@ class AlbumDetailAdapter( if (song != null) { // Use existing data instead of having to re-sort it. - val pos = currentList.indexOfFirst { - it.name == song.name && it is Song + val pos = currentList.indexOfFirst { item -> + item.name == song.name && item is Song } // Check if the ViewHolder for this song is visible, if it is then highlight it. diff --git a/app/src/main/java/org/oxycblt/auxio/detail/adapters/ArtistDetailAdapter.kt b/app/src/main/java/org/oxycblt/auxio/detail/adapters/ArtistDetailAdapter.kt index 00fa41fbc..449c57422 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/adapters/ArtistDetailAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/adapters/ArtistDetailAdapter.kt @@ -90,8 +90,8 @@ class ArtistDetailAdapter( if (album != null) { // Use existing data instead of having to re-sort it. - val pos = currentList.indexOfFirst { - it.name == album.name && it is Album + val pos = currentList.indexOfFirst { item -> + item.name == album.name && item is Album } // Check if the ViewHolder if this album is visible, and highlight it if so. diff --git a/app/src/main/java/org/oxycblt/auxio/detail/adapters/GenreDetailAdapter.kt b/app/src/main/java/org/oxycblt/auxio/detail/adapters/GenreDetailAdapter.kt index e54f8ab0e..9e667a6af 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/adapters/GenreDetailAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/adapters/GenreDetailAdapter.kt @@ -90,8 +90,8 @@ class GenreDetailAdapter( if (song != null) { // Use existing data instead of having to re-sort it. - val pos = currentList.indexOfFirst { - it.name == song.name && it is Song + val pos = currentList.indexOfFirst { item -> + item.name == song.name && item is Song } // Check if the ViewHolder for this song is visible, if it is then highlight it. diff --git a/app/src/main/java/org/oxycblt/auxio/library/LibraryFragment.kt b/app/src/main/java/org/oxycblt/auxio/library/LibraryFragment.kt index 2b6a5ef13..c083e2012 100644 --- a/app/src/main/java/org/oxycblt/auxio/library/LibraryFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/library/LibraryFragment.kt @@ -37,24 +37,23 @@ class LibraryFragment : Fragment() { ): View { val binding = FragmentLibraryBinding.inflate(inflater) - val libraryAdapter = LibraryAdapter(::navToDetail) { view, data -> newMenu(view, data) } + val libraryAdapter = LibraryAdapter(::navToDetail) { view, data -> + newMenu(view, data) + } // --- UI SETUP --- binding.libraryToolbar.apply { menu.findItem(libraryModel.sortMode.toMenuId()).isChecked = true - setOnMenuItemClickListener { - when (it.itemId) { - R.id.submenu_sorting -> {} - - else -> { - it.isChecked = true - libraryModel.updateSortMode(it.itemId) - } + setOnMenuItemClickListener { item -> + if (item.itemId != R.id.submenu_sorting) { + libraryModel.updateSortMode(item.itemId) + item.isChecked = true + true + } else { + false } - - true } } @@ -70,18 +69,18 @@ class LibraryFragment : Fragment() { // --- VIEWMODEL SETUP --- - libraryModel.libraryData.observe(viewLifecycleOwner) { - libraryAdapter.updateData(it) + libraryModel.libraryData.observe(viewLifecycleOwner) { data -> + libraryAdapter.updateData(data) } - detailModel.navToItem.observe(viewLifecycleOwner) { - if (it != null) { - libraryModel.updateNavigationStatus(false) + detailModel.navToItem.observe(viewLifecycleOwner) { item -> + if (item != null) { + libraryModel.setNavigating(false) - if (it is Parent) { - navToDetail(it) - } else if (it is Song) { - navToDetail(it.album) + if (item is Parent) { + navToDetail(item) + } else if (item is Song) { + navToDetail(item.album) } } } @@ -94,7 +93,7 @@ class LibraryFragment : Fragment() { override fun onResume() { super.onResume() - libraryModel.updateNavigationStatus(false) + libraryModel.setNavigating(false) } override fun onDestroyView() { @@ -110,7 +109,7 @@ class LibraryFragment : Fragment() { requireView().rootView.clearFocus() if (!libraryModel.isNavigating) { - libraryModel.updateNavigationStatus(true) + libraryModel.setNavigating(true) logD("Navigating to the detail fragment for ${parent.name}") diff --git a/app/src/main/java/org/oxycblt/auxio/library/LibraryViewModel.kt b/app/src/main/java/org/oxycblt/auxio/library/LibraryViewModel.kt index c5abde99f..9ae6139a3 100644 --- a/app/src/main/java/org/oxycblt/auxio/library/LibraryViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/library/LibraryViewModel.kt @@ -64,7 +64,7 @@ class LibraryViewModel : ViewModel(), SettingsManager.Callback { /** * Update the current navigation status */ - fun updateNavigationStatus(isNavigating: Boolean) { + fun setNavigating(isNavigating: Boolean) { mIsNavigating = isNavigating } diff --git a/app/src/main/java/org/oxycblt/auxio/loading/LoadingFragment.kt b/app/src/main/java/org/oxycblt/auxio/loading/LoadingFragment.kt index 3bd8125dd..c54870a80 100644 --- a/app/src/main/java/org/oxycblt/auxio/loading/LoadingFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/loading/LoadingFragment.kt @@ -42,8 +42,8 @@ class LoadingFragment : Fragment() { // --- VIEWMODEL SETUP --- - loadingModel.doGrant.observe(viewLifecycleOwner) { - if (it) { + loadingModel.doGrant.observe(viewLifecycleOwner) { doGrant -> + if (doGrant) { permLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE) loadingModel.doneWithGrant() } @@ -80,7 +80,9 @@ class LoadingFragment : Fragment() { override fun onResume() { super.onResume() - if (MusicStore.getInstance().loaded) { + // Navigate away if the music has already been loaded. + // This causes a memory leak, but there's nothing I can do about it. + if (loadingModel.loaded) { findNavController().navigate( LoadingFragmentDirections.actionToMain() ) @@ -106,8 +108,8 @@ class LoadingFragment : Fragment() { private fun onPermResult(granted: Boolean) { if (granted) { - // If granted, its now safe to load [Which will clear the NO_PERMS response we applied - // earlier] + // If granted, its now safe to load, which will clear the NO_PERMS response + // we applied earlier. loadingModel.load() } } diff --git a/app/src/main/java/org/oxycblt/auxio/loading/LoadingViewModel.kt b/app/src/main/java/org/oxycblt/auxio/loading/LoadingViewModel.kt index 70e489d6c..241388a66 100644 --- a/app/src/main/java/org/oxycblt/auxio/loading/LoadingViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/loading/LoadingViewModel.kt @@ -17,11 +17,12 @@ class LoadingViewModel(private val app: Application) : ViewModel() { private val mResponse = MutableLiveData(null) private val mDoGrant = MutableLiveData(false) + private var isBusy = false + /** The last response from [MusicStore]. Null if the loading process is occurring. */ val response: LiveData = mResponse val doGrant: LiveData = mDoGrant - - private var isBusy = false + val loaded: Boolean get() = musicStore.loaded private val musicStore = MusicStore.getInstance() diff --git a/app/src/main/java/org/oxycblt/auxio/music/MusicStore.kt b/app/src/main/java/org/oxycblt/auxio/music/MusicStore.kt index 0686f008a..e6d8e0720 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/MusicStore.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/MusicStore.kt @@ -46,6 +46,7 @@ class MusicStore private constructor() { */ suspend fun load(app: Application): Response { return withContext(Dispatchers.IO) { + // TODO: Move this to an internal function this@MusicStore.logD("Starting initial music load...") val start = System.currentTimeMillis() diff --git a/app/src/main/java/org/oxycblt/auxio/music/MusicUtils.kt b/app/src/main/java/org/oxycblt/auxio/music/MusicUtils.kt index 18501a4f0..721f5f2d1 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/MusicUtils.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/MusicUtils.kt @@ -5,12 +5,10 @@ import android.content.Context import android.net.Uri import android.provider.MediaStore import android.text.format.DateUtils -import android.widget.ImageButton import android.widget.TextView import androidx.core.text.isDigitsOnly import androidx.databinding.BindingAdapter import org.oxycblt.auxio.R -import org.oxycblt.auxio.recycler.SortMode import org.oxycblt.auxio.ui.getPlural /** @@ -169,11 +167,3 @@ fun TextView.bindAlbumInfo(album: Album) { fun TextView.bindAlbumYear(album: Album) { text = album.year.toYear(context) } - -/** - * Bind the [SortMode] icon for an ImageButton. - */ -@BindingAdapter("sortIcon") -fun ImageButton.bindSortIcon(mode: SortMode) { - setImageResource(mode.iconRes) -} diff --git a/app/src/main/java/org/oxycblt/auxio/playback/CompactPlaybackFragment.kt b/app/src/main/java/org/oxycblt/auxio/playback/CompactPlaybackFragment.kt index ceb08ad58..44ecf58d5 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/CompactPlaybackFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/CompactPlaybackFragment.kt @@ -64,8 +64,8 @@ class CompactPlaybackFragment : Fragment() { } } - playbackModel.isPlaying.observe(viewLifecycleOwner) { - binding.playbackPlayPause.setPlaying(it, playbackModel.canAnimate) + playbackModel.isPlaying.observe(viewLifecycleOwner) { isPlaying -> + binding.playbackPlayPause.setPlaying(isPlaying, playbackModel.canAnimate) playbackModel.enableAnimation() } diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt index 34ad21202..e93c37e61 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt @@ -61,12 +61,14 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener { findNavController().navigateUp() } - setOnMenuItemClickListener { - if (it.itemId == R.id.action_queue) { + setOnMenuItemClickListener { item -> + if (item.itemId == R.id.action_queue) { findNavController().navigate(PlaybackFragmentDirections.actionShowQueue()) true - } else false + } else { + false + } } queueItem = menu.findItem(R.id.action_queue) @@ -90,12 +92,16 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener { } } - playbackModel.isShuffling.observe(viewLifecycleOwner) { - binding.playbackShuffle.imageTintList = if (it) accentColor else controlColor + playbackModel.isShuffling.observe(viewLifecycleOwner) { isShuffling -> + binding.playbackShuffle.imageTintList = if (isShuffling) { + accentColor + } else { + controlColor + } } - playbackModel.loopMode.observe(viewLifecycleOwner) { - when (it) { + playbackModel.loopMode.observe(viewLifecycleOwner) { loopMode -> + when (loopMode) { LoopMode.NONE -> { binding.playbackLoop.imageTintList = controlColor binding.playbackLoop.setImageResource(R.drawable.ic_loop) @@ -115,17 +121,17 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener { } } - playbackModel.isSeeking.observe(viewLifecycleOwner) { - if (it) { + playbackModel.isSeeking.observe(viewLifecycleOwner) { isSeeking -> + if (isSeeking) { binding.playbackDurationCurrent.setTextColor(accentColor) } else { binding.playbackDurationCurrent.setTextColor(normalTextColor) } } - playbackModel.positionAsProgress.observe(viewLifecycleOwner) { + playbackModel.positionAsProgress.observe(viewLifecycleOwner) { pos -> if (!playbackModel.isSeeking.value!!) { - binding.playbackSeekBar.progress = it + binding.playbackSeekBar.progress = pos } } @@ -153,9 +159,9 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener { } } - playbackModel.isPlaying.observe(viewLifecycleOwner) { + playbackModel.isPlaying.observe(viewLifecycleOwner) { isPlaying -> binding.playbackPlayPause.apply { - if (it) { + if (isPlaying) { backgroundTintList = accentColor setPlaying(true, playbackModel.canAnimate) } else { @@ -167,8 +173,8 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener { playbackModel.enableAnimation() } - detailModel.navToItem.observe(viewLifecycleOwner) { - if (it != null) { + detailModel.navToItem.observe(viewLifecycleOwner) { item -> + if (item != null) { findNavController().navigateUp() } } diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt index 224dfd3bb..dc9f00d9c 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt @@ -380,7 +380,7 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback { mIntentUri = null // Were not going to be restoring playbackManager after this, so mark it as such. - playbackManager.setRestored() + playbackManager.markRestored() } else if (!playbackManager.isRestored) { // Otherwise just restore viewModelScope.launch { diff --git a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueAdapter.kt b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueAdapter.kt index f911f045e..a4e7a16e5 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueAdapter.kt @@ -61,23 +61,21 @@ class QueueAdapter( ItemQueueSongBinding.inflate(parent.context.inflater) ) - else -> error("Someone messed with the ViewHolder item types.") + else -> error("Invalid viewholder item type $viewType.") } } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { when (val item = data[position]) { is Song -> (holder as QueueSongViewHolder).bind(item) - is Header -> - if (item.isAction) { - (holder as UserQueueHeaderViewHolder).bind(item) - } else { - (holder as HeaderViewHolder).bind(item) - } - else -> { - logE("Bad data fed to QueueAdapter.") + is Header -> if (item.isAction) { + (holder as UserQueueHeaderViewHolder).bind(item) + } else { + (holder as HeaderViewHolder).bind(item) } + + else -> logE("Bad data given to QueueAdapter.") } } @@ -95,7 +93,7 @@ class QueueAdapter( /** * Move Items. - * Used since [submitList] will cause QueueAdapter to freak-out here. + * Used since [submitList] will cause QueueAdapter to freak out. */ fun moveItems(adapterFrom: Int, adapterTo: Int) { val item = data.removeAt(adapterFrom) @@ -106,7 +104,7 @@ class QueueAdapter( /** * Remove an item. - * Used since [submitList] will cause QueueAdapter to freak-out here. + * Used since [submitList] will cause QueueAdapter to freak out. */ fun removeItem(adapterIndex: Int) { data.removeAt(adapterIndex) @@ -154,10 +152,8 @@ class QueueAdapter( if (motionEvent.actionMasked == MotionEvent.ACTION_DOWN) { touchHelper.startDrag(this) - return@setOnTouchListener true - } - - false + true + } else false } } } diff --git a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueFragment.kt b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueFragment.kt index 8f1a4a592..39957f50c 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueFragment.kt @@ -49,10 +49,11 @@ class QueueFragment : Fragment() { } if (!requireActivity().isIrregularLandscape() && isEdgeOn()) { - setOnApplyWindowInsetsListener @Suppress("DEPRECATION") { _, insets -> + setOnApplyWindowInsetsListener { _, insets -> val top = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { insets.getInsets(WindowInsets.Type.systemBars()).top } else { + @Suppress("DEPRECATION") insets.systemWindowInsetTop } @@ -74,8 +75,8 @@ class QueueFragment : Fragment() { // --- VIEWMODEL SETUP ---- - playbackModel.userQueue.observe(viewLifecycleOwner) { - if (it.isEmpty() && playbackModel.nextItemsInQueue.value!!.isEmpty()) { + playbackModel.userQueue.observe(viewLifecycleOwner) { userQueue -> + if (userQueue.isEmpty() && playbackModel.nextItemsInQueue.value!!.isEmpty()) { findNavController().navigateUp() return@observe @@ -84,17 +85,17 @@ class QueueFragment : Fragment() { queueAdapter.submitList(createQueueData()) } - playbackModel.nextItemsInQueue.observe(viewLifecycleOwner) { - if (it.isEmpty() && playbackModel.userQueue.value!!.isEmpty()) { + playbackModel.nextItemsInQueue.observe(viewLifecycleOwner) { nextQueue -> + if (nextQueue.isEmpty() && playbackModel.userQueue.value!!.isEmpty()) { findNavController().navigateUp() } queueAdapter.submitList(createQueueData()) } - playbackModel.isShuffling.observe(viewLifecycleOwner) { - if (it != lastShuffle) { - lastShuffle = it + playbackModel.isShuffling.observe(viewLifecycleOwner) { isShuffling -> + if (isShuffling != lastShuffle) { + lastShuffle = isShuffling binding.queueRecycler.scrollToPosition(0) } @@ -109,21 +110,27 @@ class QueueFragment : Fragment() { */ private fun createQueueData(): MutableList { val queue = mutableListOf() + val userQueue = playbackModel.userQueue.value!! + val nextQueue = playbackModel.nextItemsInQueue.value!! - if (playbackModel.userQueue.value!!.isNotEmpty()) { - queue.add(Header(id = -2, name = getString(R.string.label_next_user_queue), isAction = true)) - queue.addAll(playbackModel.userQueue.value!!) + if (userQueue.isNotEmpty()) { + queue += Header( + id = -2, + name = getString(R.string.label_next_user_queue), + isAction = true + ) + + queue += userQueue } - if (playbackModel.nextItemsInQueue.value!!.isNotEmpty()) { - queue.add( - Header( - id = -3, - name = getString(R.string.format_next_from, getParentName()), - isAction = false - ) + if (nextQueue.isNotEmpty()) { + queue += Header( + id = -3, + name = getString(R.string.format_next_from, getParentName()), + isAction = false ) - queue.addAll(playbackModel.nextItemsInQueue.value!!) + + queue += nextQueue } return queue diff --git a/app/src/main/java/org/oxycblt/auxio/playback/state/LoopMode.kt b/app/src/main/java/org/oxycblt/auxio/playback/state/LoopMode.kt index 2217d9bad..335ee36b8 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/state/LoopMode.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/state/LoopMode.kt @@ -24,16 +24,16 @@ enum class LoopMode { */ fun toInt(): Int { return when (this) { - NONE -> CONSTANT_NONE - ONCE -> CONSTANT_ONCE - INFINITE -> CONSTANT_INFINITE + NONE -> CONST_NONE + ONCE -> CONST_ONCE + INFINITE -> CONST_INFINITE } } companion object { - const val CONSTANT_NONE = 0xA050 - const val CONSTANT_ONCE = 0xA051 - const val CONSTANT_INFINITE = 0xA052 + private const val CONST_NONE = 0xA050 + private const val CONST_ONCE = 0xA051 + private const val CONST_INFINITE = 0xA052 /** * Convert an int [constant] into a LoopMode @@ -41,9 +41,9 @@ enum class LoopMode { */ fun fromInt(constant: Int): LoopMode? { return when (constant) { - CONSTANT_NONE -> NONE - CONSTANT_ONCE -> ONCE - CONSTANT_INFINITE -> INFINITE + CONST_NONE -> NONE + CONST_ONCE -> ONCE + CONST_INFINITE -> INFINITE else -> null } diff --git a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackMode.kt b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackMode.kt index 7e3d1adf2..0aeae3ef5 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackMode.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackMode.kt @@ -20,18 +20,18 @@ enum class PlaybackMode { */ fun toInt(): Int { return when (this) { - IN_ARTIST -> CONSTANT_IN_ARTIST - IN_GENRE -> CONSTANT_IN_GENRE - IN_ALBUM -> CONSTANT_IN_ALBUM - ALL_SONGS -> CONSTANT_ALL_SONGS + IN_ARTIST -> CONST_IN_ARTIST + IN_GENRE -> CONST_IN_GENRE + IN_ALBUM -> CONST_IN_ALBUM + ALL_SONGS -> CONST_ALL_SONGS } } companion object { - const val CONSTANT_IN_ARTIST = 0xA040 - const val CONSTANT_IN_GENRE = 0xA041 - const val CONSTANT_IN_ALBUM = 0xA042 - const val CONSTANT_ALL_SONGS = 0xA043 + private const val CONST_IN_ARTIST = 0xA040 + private const val CONST_IN_GENRE = 0xA041 + private const val CONST_IN_ALBUM = 0xA042 + private const val CONST_ALL_SONGS = 0xA043 /** * Get a [PlaybackMode] for an int [constant] @@ -39,10 +39,10 @@ enum class PlaybackMode { */ fun fromInt(constant: Int): PlaybackMode? { return when (constant) { - CONSTANT_IN_ARTIST -> IN_ARTIST - CONSTANT_IN_ALBUM -> IN_ALBUM - CONSTANT_IN_GENRE -> IN_GENRE - CONSTANT_ALL_SONGS -> ALL_SONGS + CONST_IN_ARTIST -> IN_ARTIST + CONST_IN_ALBUM -> IN_ALBUM + CONST_IN_GENRE -> IN_GENRE + CONST_ALL_SONGS -> ALL_SONGS else -> null } diff --git a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt index 774d8292e..e4babfac1 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt @@ -185,6 +185,8 @@ class PlaybackStateManager private constructor() { clearLoopMode() updatePlayback(song) + + // Keep shuffle on, if enabled setShuffling(settingsManager.keepShuffle && mIsShuffling, keepSong = true) } @@ -203,10 +205,12 @@ class PlaybackStateManager private constructor() { mQueue = parent.songs.toMutableList() mMode = PlaybackMode.IN_ALBUM } + is Artist -> { mQueue = parent.songs.toMutableList() mMode = PlaybackMode.IN_ARTIST } + is Genre -> { mQueue = parent.songs.toMutableList() mMode = PlaybackMode.IN_GENRE @@ -228,6 +232,8 @@ class PlaybackStateManager private constructor() { setShuffling(true, keepSong = false) updatePlayback(mQueue[0]) + + // FIXME: Add clearLoopMode here? } /** @@ -293,9 +299,7 @@ class PlaybackStateManager private constructor() { } clearLoopMode() - updatePlayback(mQueue[mIndex]) - forceQueueUpdate() } } @@ -307,18 +311,18 @@ class PlaybackStateManager private constructor() { when (settingsManager.doAtEnd) { SettingsManager.EntryValues.AT_END_LOOP_PAUSE -> { mIndex = 0 - forceQueueUpdate() - - mSong = mQueue[0] mPosition = 0 - setPlaying(false) mIsInUserQueue = false + mSong = mQueue[0] + + setPlaying(false) + forceQueueUpdate() } SettingsManager.EntryValues.AT_END_LOOP -> { mIndex = 0 - forceQueueUpdate() + forceQueueUpdate() updatePlayback(mQueue[0]) } @@ -529,9 +533,9 @@ class PlaybackStateManager private constructor() { * @see seekTo */ fun setPosition(position: Long) { - mSong?.let { + mSong?.let { song -> // Don't accept any bugged positions that are over the duration of the song. - if (position <= it.duration) { + if (position <= song.duration) { mPosition = position } } @@ -584,7 +588,7 @@ class PlaybackStateManager private constructor() { /** * Mark this instance as restored. */ - fun setRestored() { + fun markRestored() { mIsRestored = true } @@ -620,32 +624,34 @@ class PlaybackStateManager private constructor() { logD("Getting state from DB.") val now: Long - val state: PlaybackState? + val playbackState: PlaybackState? // The coroutine call is locked at queueItems so that this function does not // go ahead until EVERYTHING is read. + // TODO: Improve this val queueItems = withContext(Dispatchers.IO) { now = System.currentTimeMillis() val database = PlaybackStateDatabase.getInstance(context) - state = database.readState() + + playbackState = database.readState() database.readQueue() } // Get off the IO coroutine since it will cause LiveData updates to throw an exception - state?.let { - logD("Valid playback state $it") - logD("Valid queue size ${queueItems.size}") + if (playbackState != null) { + logD("Found playback state $playbackState") + logD("Found queue size ${queueItems.size}") - unpackFromPlaybackState(it) + unpackFromPlaybackState(playbackState) unpackQueue(queueItems) doParentSanityCheck() } logD("Restore finished in ${System.currentTimeMillis() - now}ms") - setRestored() + markRestored() } /** @@ -653,19 +659,14 @@ class PlaybackStateManager private constructor() { * @return A [PlaybackState] reflecting the current state. */ private fun packToPlaybackState(): PlaybackState { - val songName = mSong?.name ?: "" - val parentName = mParent?.name ?: "" - val intMode = mMode.toInt() - val intLoopMode = mLoopMode.toInt() - return PlaybackState( - songName = songName, + songName = mSong?.name ?: "", position = mPosition, - parentName = parentName, + parentName = mParent?.name ?: "", index = mIndex, - mode = intMode, + mode = mMode.toInt(), isShuffling = mIsShuffling, - loopMode = intLoopMode, + loopMode = mLoopMode.toInt(), inUserQueue = mIsInUserQueue ) } @@ -695,13 +696,13 @@ class PlaybackStateManager private constructor() { var queueItemId = 0L - mUserQueue.forEach { - unified.add(QueueItem(queueItemId, it.name, it.album.name, true)) + mUserQueue.forEach { song -> + unified.add(QueueItem(queueItemId, song.name, song.album.name, true)) queueItemId++ } - mQueue.forEach { - unified.add(QueueItem(queueItemId, it.name, it.album.name, false)) + mQueue.forEach { song -> + unified.add(QueueItem(queueItemId, song.name, song.album.name, false)) queueItemId++ } @@ -714,15 +715,15 @@ class PlaybackStateManager private constructor() { */ private fun unpackQueue(queueItems: List) { // When unpacking, first traverse albums and then traverse album songs to reduce - // the amount of useless comparisons in large queues. + // the amount of comparisons in large queues. queueItems.forEach { item -> - musicStore.albums.find { it.name == item.albumName }?.songs?.find { - it.name == item.songName - }?.let { + musicStore.albums.find { + it.name == item.albumName + }?.songs?.find { it.name == item.songName }?.let { song -> if (item.isUserQueue) { - mUserQueue.add(it) + mUserQueue.add(song) } else { - mQueue.add(it) + mQueue.add(song) } } } @@ -731,8 +732,8 @@ class PlaybackStateManager private constructor() { // to the db but are now deleted when the restore occurred. // Not done if in user queue because that could result in a bad index being created. if (!mIsInUserQueue) { - mSong?.let { - val index = mQueue.indexOf(it) + mSong?.let { song -> + val index = mQueue.indexOf(song) mIndex = if (index != -1) index else mIndex } } diff --git a/app/src/main/java/org/oxycblt/auxio/playback/system/AudioReactor.kt b/app/src/main/java/org/oxycblt/auxio/playback/system/AudioReactor.kt index 03d09aad2..6b77cbe32 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/system/AudioReactor.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/system/AudioReactor.kt @@ -93,7 +93,7 @@ class AudioReactor( onEnd = { player.volume = VOLUME_FULL } ) addUpdateListener { - player.volume = it.animatedValue as Float + player.volume = animatedValue as Float } start() } diff --git a/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackNotification.kt b/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackNotification.kt index 73f62edf6..f8f245bce 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackNotification.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackNotification.kt @@ -77,14 +77,12 @@ class PlaybackNotification private constructor( if (colorize) { // loadBitmap() is concurrent, so only call back to the object calling this function when // the loading is over. - loadBitmap(context, song) { - setLargeIcon(it) - + loadBitmap(context, song) { bitmap -> + setLargeIcon(bitmap) onDone() } } else { setLargeIcon(null) - onDone() } } @@ -114,9 +112,7 @@ class PlaybackNotification private constructor( * Apply the current [parent] to the header of the notification. */ fun setParent(context: Context, parent: Parent?) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { - return - } + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) return // A blank parent always means that the mode is ALL_SONGS setSubText(parent?.displayName ?: context.getString(R.string.label_all_songs)) diff --git a/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackService.kt b/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackService.kt index a6b2fdfcf..152282dc7 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackService.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackService.kt @@ -282,7 +282,7 @@ class PlaybackService : Service(), Player.EventListener, PlaybackStateManager.Ca override fun onColorizeNotifUpdate(doColorize: Boolean) { playbackManager.song?.let { song -> notification.setMetadata( - this, song, settingsManager.colorizeNotif, {} + this, song, settingsManager.colorizeNotif, ::startForegroundOrNotify ) } } @@ -365,8 +365,8 @@ class PlaybackService : Service(), Player.EventListener, PlaybackStateManager.Ca .putString(MediaMetadataCompat.METADATA_KEY_ALBUM, song.album.name) .putLong(MediaMetadataCompat.METADATA_KEY_DURATION, song.duration) - loadBitmap(this, song) { - builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, it) + loadBitmap(this, song) { bitmap -> + builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, bitmap) mediaSession.setMetadata(builder.build()) } } @@ -383,8 +383,8 @@ class PlaybackService : Service(), Player.EventListener, PlaybackStateManager.Ca }.conflate() serviceScope.launch { - pollFlow.takeWhile { player.isPlaying }.collect { - playbackManager.setPosition(it) + pollFlow.takeWhile { player.isPlaying }.collect { pos -> + playbackManager.setPosition(pos) } } } diff --git a/app/src/main/java/org/oxycblt/auxio/recycler/SortMode.kt b/app/src/main/java/org/oxycblt/auxio/recycler/SortMode.kt index a54298e7c..0824423a9 100644 --- a/app/src/main/java/org/oxycblt/auxio/recycler/SortMode.kt +++ b/app/src/main/java/org/oxycblt/auxio/recycler/SortMode.kt @@ -1,7 +1,9 @@ package org.oxycblt.auxio.recycler +import android.widget.ImageButton import androidx.annotation.DrawableRes import androidx.annotation.IdRes +import androidx.databinding.BindingAdapter import org.oxycblt.auxio.R import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.music.Artist @@ -163,20 +165,22 @@ enum class SortMode(@DrawableRes val iconRes: Int) { */ fun toInt(): Int { return when (this) { - NONE -> CONSTANT_NONE - ALPHA_UP -> CONSTANT_ALPHA_UP - ALPHA_DOWN -> CONSTANT_ALPHA_DOWN - NUMERIC_UP -> CONSTANT_NUMERIC_UP - NUMERIC_DOWN -> CONSTANT_NUMERIC_DOWN + NONE -> CONST_NONE + ALPHA_UP -> CONST_ALPHA_UP + ALPHA_DOWN -> CONST_ALPHA_DOWN + NUMERIC_UP -> CONST_NUMERIC_UP + NUMERIC_DOWN -> CONST_NUMERIC_DOWN } } companion object { - const val CONSTANT_NONE = 0xA060 - const val CONSTANT_ALPHA_UP = 0xA061 - const val CONSTANT_ALPHA_DOWN = 0xA062 - const val CONSTANT_NUMERIC_UP = 0xA063 - const val CONSTANT_NUMERIC_DOWN = 0xA065 + private const val CONST_NONE = 0xA060 + private const val CONST_ALPHA_UP = 0xA061 + private const val CONST_ALPHA_DOWN = 0xA062 + private const val CONST_NUMERIC_UP = 0xA063 + private const val CONST_NUMERIC_DOWN = 0xA065 + + const val CONST_SORT_DEFAULT = CONST_ALPHA_DOWN /** * Get an enum for an int constant @@ -184,14 +188,22 @@ enum class SortMode(@DrawableRes val iconRes: Int) { */ fun fromInt(value: Int): SortMode? { return when (value) { - CONSTANT_NONE -> NONE - CONSTANT_ALPHA_UP -> ALPHA_UP - CONSTANT_ALPHA_DOWN -> ALPHA_DOWN - CONSTANT_NUMERIC_UP -> NUMERIC_UP - CONSTANT_NUMERIC_DOWN -> NUMERIC_DOWN + CONST_NONE -> NONE + CONST_ALPHA_UP -> ALPHA_UP + CONST_ALPHA_DOWN -> ALPHA_DOWN + CONST_NUMERIC_UP -> NUMERIC_UP + CONST_NUMERIC_DOWN -> NUMERIC_DOWN else -> null } } } } + +/** + * Bind the [SortMode] icon for an ImageButton. + */ +@BindingAdapter("sortIcon") +fun ImageButton.bindSortIcon(mode: SortMode) { + setImageResource(mode.iconRes) +} diff --git a/app/src/main/java/org/oxycblt/auxio/search/SearchFragment.kt b/app/src/main/java/org/oxycblt/auxio/search/SearchFragment.kt index 3c0b43ceb..cac358325 100644 --- a/app/src/main/java/org/oxycblt/auxio/search/SearchFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/search/SearchFragment.kt @@ -47,10 +47,13 @@ class SearchFragment : Fragment() { ): View { val binding = FragmentSearchBinding.inflate(inflater) + val searchAdapter = SearchAdapter(::onItemSelection) { view, data -> + newMenu(view, data) + } + // Apply the accents manually. Not going through the mess of converting my app's // styling to Material given all the second-and-third-order effects it has. val accent = Accent.get().color.toColor(requireContext()) - val searchAdapter = SearchAdapter(::onItemSelection) { view, data -> newMenu(view, data) } val toolbarParams = binding.searchToolbar.layoutParams as AppBarLayout.LayoutParams val defaultParams = toolbarParams.scrollFlags @@ -59,13 +62,14 @@ class SearchFragment : Fragment() { binding.searchToolbar.apply { menu.findItem(searchModel.filterMode.toId()).isChecked = true - setOnMenuItemClickListener { - if (it.itemId != R.id.submenu_filtering) { - it.isChecked = true - searchModel.updateFilterModeWithId(it.itemId, requireContext()) - + setOnMenuItemClickListener { item -> + if (item.itemId != R.id.submenu_filtering) { + searchModel.updateFilterModeWithId(item.itemId, requireContext()) + item.isChecked = true true - } else false + } else { + false + } } } @@ -75,9 +79,9 @@ class SearchFragment : Fragment() { setEndIconTintList(R.color.control_color.toStateList(context)) } - binding.searchEditText.addTextChangedListener { + binding.searchEditText.addTextChangedListener { text -> // Run the search with the updated text as the query - searchModel.doSearch(it?.toString() ?: "", requireContext()) + searchModel.doSearch(text?.toString() ?: "", requireContext()) } binding.searchRecycler.apply { @@ -96,12 +100,12 @@ class SearchFragment : Fragment() { // --- VIEWMODEL SETUP --- - searchModel.searchResults.observe(viewLifecycleOwner) { - searchAdapter.submitList(it) { + searchModel.searchResults.observe(viewLifecycleOwner) { results -> + searchAdapter.submitList(results) { binding.searchRecycler.scrollToPosition(0) } - if (it.isEmpty()) { + if (results.isEmpty()) { // If the data is empty, then the ability for the toolbar to collapse // on scroll should be disabled. binding.searchAppbar.setExpanded(true) @@ -113,18 +117,16 @@ class SearchFragment : Fragment() { } } - detailModel.navToItem.observe(viewLifecycleOwner) { - if (it != null) { - findNavController().navigate( - when (it) { - is Song -> SearchFragmentDirections.actionShowAlbum(it.album.id) - is Album -> SearchFragmentDirections.actionShowAlbum(it.id) - is Artist -> SearchFragmentDirections.actionShowArtist(it.id) + detailModel.navToItem.observe(viewLifecycleOwner) { item -> + findNavController().navigate( + when (item) { + is Song -> SearchFragmentDirections.actionShowAlbum(item.album.id) + is Album -> SearchFragmentDirections.actionShowAlbum(item.id) + is Artist -> SearchFragmentDirections.actionShowArtist(item.id) - else -> return@observe - } - ) - } + else -> return@observe + } + ) } logD("Fragment created.") @@ -135,7 +137,7 @@ class SearchFragment : Fragment() { override fun onResume() { super.onResume() - searchModel.updateNavigationStatus(false) + searchModel.setNavigating(false) } override fun onDestroyView() { @@ -159,7 +161,7 @@ class SearchFragment : Fragment() { requireView().rootView.clearFocus() if (!searchModel.isNavigating) { - searchModel.updateNavigationStatus(true) + searchModel.setNavigating(true) logD("Navigating to the detail fragment for ${item.name}") @@ -172,7 +174,7 @@ class SearchFragment : Fragment() { // If given model wasn't valid, then reset the navigation status // and abort the navigation. else -> { - searchModel.updateNavigationStatus(false) + searchModel.setNavigating(false) return } } diff --git a/app/src/main/java/org/oxycblt/auxio/search/SearchViewModel.kt b/app/src/main/java/org/oxycblt/auxio/search/SearchViewModel.kt index dce995318..d0025b924 100644 --- a/app/src/main/java/org/oxycblt/auxio/search/SearchViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/search/SearchViewModel.kt @@ -53,30 +53,30 @@ class SearchViewModel : ViewModel() { val results = mutableListOf() if (mFilterMode.isAllOr(DisplayMode.SHOW_ARTISTS)) { - musicStore.artists.filterByOrNull(query)?.let { + musicStore.artists.filterByOrNull(query)?.let { artists -> results.add(Header(id = -2, name = context.getString(R.string.label_artists))) - results.addAll(it) + results.addAll(artists) } } if (mFilterMode.isAllOr(DisplayMode.SHOW_ALBUMS)) { - musicStore.albums.filterByOrNull(query)?.let { + musicStore.albums.filterByOrNull(query)?.let { albums -> results.add(Header(id = -3, name = context.getString(R.string.label_albums))) - results.addAll(it) + results.addAll(albums) } } if (mFilterMode.isAllOr(DisplayMode.SHOW_GENRES)) { - musicStore.genres.filterByOrNull(query)?.let { + musicStore.genres.filterByOrNull(query)?.let { genres -> results.add(Header(id = -4, name = context.getString(R.string.label_genres))) - results.addAll(it) + results.addAll(genres) } } if (mFilterMode.isAllOr(DisplayMode.SHOW_SONGS)) { - musicStore.songs.filterByOrNull(query)?.let { + musicStore.songs.filterByOrNull(query)?.let { songs -> results.add(Header(id = -5, name = context.getString(R.string.label_songs))) - results.addAll(it) + results.addAll(songs) } } @@ -101,7 +101,9 @@ class SearchViewModel : ViewModel() { * a value if the resulting list is empty. */ private fun List.filterByOrNull(value: String): List? { - val filtered = filter { it.name.contains(value, ignoreCase = true) } + val filtered = filter { + it.name.contains(value, ignoreCase = true) + } return if (filtered.isNotEmpty()) filtered else null } @@ -109,7 +111,7 @@ class SearchViewModel : ViewModel() { /** * Update the current navigation status to [isNavigating] */ - fun updateNavigationStatus(isNavigating: Boolean) { + fun setNavigating(isNavigating: Boolean) { mIsNavigating = isNavigating } } diff --git a/app/src/main/java/org/oxycblt/auxio/settings/SettingsFragment.kt b/app/src/main/java/org/oxycblt/auxio/settings/SettingsFragment.kt index b4ef4512b..7d2873df2 100644 --- a/app/src/main/java/org/oxycblt/auxio/settings/SettingsFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/settings/SettingsFragment.kt @@ -23,11 +23,17 @@ class SettingsFragment : Fragment() { binding.settingsToolbar.setOnMenuItemClickListener { if (it.itemId == R.id.action_open_about) { - AboutDialog().show(childFragmentManager, "DIALOG") + AboutDialog().show(childFragmentManager, ABOUT_DIALOG_TAG) true - } else false + } else { + false + } } return binding.root } + + companion object { + private const val ABOUT_DIALOG_TAG = "TAG_ABOUT_DIALOG" + } } diff --git a/app/src/main/java/org/oxycblt/auxio/settings/SettingsListFragment.kt b/app/src/main/java/org/oxycblt/auxio/settings/SettingsListFragment.kt index 1828f7838..6e4746596 100644 --- a/app/src/main/java/org/oxycblt/auxio/settings/SettingsListFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/settings/SettingsListFragment.kt @@ -51,7 +51,9 @@ class SettingsListFragment : PreferenceFragmentCompat() { private fun recursivelyHandleChildren(pref: Preference) { if (pref is PreferenceCategory) { // If this preference is a category of its own, handle its own children - pref.children.forEach { recursivelyHandleChildren(it) } + pref.children.forEach { pref -> + recursivelyHandleChildren(pref) + } } else { handlePreference(pref) } @@ -151,9 +153,10 @@ class SettingsListFragment : PreferenceFragmentCompat() { // has a bug where ugly dividers will show with the RecyclerView even if you disable them. // This is why I hate using third party libraries. val recycler = RecyclerView(requireContext()).apply { - adapter = AccentAdapter { - if (it != Accent.get()) { - settingsManager.accent = it + adapter = AccentAdapter { accent -> + if (accent != Accent.get()) { + // TODO: Move this to Accent.set? + settingsManager.accent = accent requireActivity().recreate() } @@ -179,11 +182,8 @@ class SettingsListFragment : PreferenceFragmentCompat() { } customView(view = recycler) - invalidateDividers(showTop = false, showBottom = false) - negativeButton(android.R.string.cancel) - show() } } diff --git a/app/src/main/java/org/oxycblt/auxio/settings/SettingsManager.kt b/app/src/main/java/org/oxycblt/auxio/settings/SettingsManager.kt index 1974e9248..8ab52b629 100644 --- a/app/src/main/java/org/oxycblt/auxio/settings/SettingsManager.kt +++ b/app/src/main/java/org/oxycblt/auxio/settings/SettingsManager.kt @@ -40,11 +40,10 @@ class SettingsManager private constructor(context: Context) : ) // When converted, write them to the new accent pref and delete the old one. - sharedPrefs.edit { - putInt(Keys.KEY_ACCENT, newAccent) - remove(Keys.KEY_ACCENT_OLD) - apply() - } + sharedPrefs.edit() + .putInt(Keys.KEY_ACCENT, newAccent) + .remove(Keys.KEY_ACCENT_OLD) + .apply() } return ACCENTS[sharedPrefs.getInt(Keys.KEY_ACCENT, 5)] @@ -124,8 +123,7 @@ class SettingsManager private constructor(context: Context) : var librarySortMode: SortMode get() = SortMode.fromInt( sharedPrefs.getInt( - Keys.KEY_LIBRARY_SORT_MODE, - SortMode.CONSTANT_ALPHA_DOWN + Keys.KEY_LIBRARY_SORT_MODE, SortMode.CONST_SORT_DEFAULT ) ) ?: SortMode.ALPHA_DOWN diff --git a/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt b/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt index 24ec23cce..2109eb2d2 100644 --- a/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt @@ -68,14 +68,13 @@ class SongsFragment : Fragment() { setHasFixedSize(true) val spans = getSpans() - if (spans != 1) { layoutManager = GridLayoutManager(requireContext(), spans) } post { - // Disable fast scrolling if there is nothing to scroll if (!canScroll()) { + // Disable fast scrolling if there is nothing to scroll binding.songFastScroll.visibility = View.GONE binding.songFastScrollThumb.visibility = View.GONE } diff --git a/app/src/main/java/org/oxycblt/auxio/ui/ActionMenu.kt b/app/src/main/java/org/oxycblt/auxio/ui/ActionMenu.kt index 24e823cda..1caa1c89b 100644 --- a/app/src/main/java/org/oxycblt/auxio/ui/ActionMenu.kt +++ b/app/src/main/java/org/oxycblt/auxio/ui/ActionMenu.kt @@ -62,8 +62,8 @@ class ActionMenu( } inflate(menuRes) - setOnMenuItemClickListener { - onMenuClick(it.itemId) + setOnMenuItemClickListener { item -> + onMenuClick(item.itemId) true } } diff --git a/app/src/main/java/org/oxycblt/auxio/ui/InterfaceUtils.kt b/app/src/main/java/org/oxycblt/auxio/ui/InterfaceUtils.kt index 081ce4300..78dbb784a 100644 --- a/app/src/main/java/org/oxycblt/auxio/ui/InterfaceUtils.kt +++ b/app/src/main/java/org/oxycblt/auxio/ui/InterfaceUtils.kt @@ -196,8 +196,7 @@ fun RecyclerView.canScroll() = computeVerticalScrollRange() > height * @return True if we are in the irregular landscape mode, false if not. */ fun Activity.isIrregularLandscape(): Boolean { - return isLandscape(resources) && - !isSystemBarOnBottom(this) + return isLandscape(resources) && !isSystemBarOnBottom(this) } /** diff --git a/app/src/main/java/org/oxycblt/auxio/ui/SlideLinearLayout.kt b/app/src/main/java/org/oxycblt/auxio/ui/SlideLinearLayout.kt index 506d31192..ee8ed3eb6 100644 --- a/app/src/main/java/org/oxycblt/auxio/ui/SlideLinearLayout.kt +++ b/app/src/main/java/org/oxycblt/auxio/ui/SlideLinearLayout.kt @@ -64,8 +64,8 @@ class SlideLinearLayout @JvmOverloads constructor( // I dont even know what this does. var consumed = false - children.forEach { - consumed = consumed or super.drawChild(canvas, it, drawingTime) + children.forEach { view -> + consumed = consumed or super.drawChild(canvas, view, drawingTime) } return consumed diff --git a/app/src/main/res/drawable/ic_pause_to_play.xml b/app/src/main/res/drawable/ic_pause_to_play.xml index c6d7ebcc2..0fe1f999f 100644 --- a/app/src/main/res/drawable/ic_pause_to_play.xml +++ b/app/src/main/res/drawable/ic_pause_to_play.xml @@ -1,5 +1,4 @@ - - + diff --git a/app/src/main/res/layout-xlarge/fragment_playback.xml b/app/src/main/res/layout-xlarge/fragment_playback.xml index fd57b2f65..6b7f4d317 100644 --- a/app/src/main/res/layout-xlarge/fragment_playback.xml +++ b/app/src/main/res/layout-xlarge/fragment_playback.xml @@ -132,9 +132,9 @@ android:id="@+id/playback_duration_current" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="@{playbackModel.formattedPosition}" android:layout_marginStart="@dimen/margin_mid_huge" android:layout_marginBottom="@dimen/margin_medium" + android:text="@{playbackModel.formattedPosition}" app:layout_constraintBottom_toTopOf="@+id/playback_play_pause" app:layout_constraintStart_toStartOf="parent" tools:text="11:38" />