diff --git a/app/build.gradle b/app/build.gradle index 60b5954dc..b4941e512 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -73,10 +73,9 @@ dependencies { implementation 'androidx.media:media:1.2.0' // Database - def room_version = '2.2.5' + def room_version = '2.3.0-alpha03' implementation "androidx.room:room-runtime:$room_version" kapt "androidx.room:room-compiler:$room_version" - implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation "androidx.room:room-ktx:$room_version" // --- THIRD PARTY --- diff --git a/app/src/main/java/org/oxycblt/auxio/MainFragment.kt b/app/src/main/java/org/oxycblt/auxio/MainFragment.kt index 5aaecde3a..c57bcefcd 100644 --- a/app/src/main/java/org/oxycblt/auxio/MainFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/MainFragment.kt @@ -1,22 +1,20 @@ package org.oxycblt.auxio +import android.content.res.ColorStateList import android.os.Bundle import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels +import androidx.navigation.findNavController +import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.findNavController -import androidx.viewpager2.adapter.FragmentStateAdapter -import com.google.android.material.tabs.TabLayout -import com.google.android.material.tabs.TabLayoutMediator +import androidx.navigation.ui.setupWithNavController import org.oxycblt.auxio.databinding.FragmentMainBinding -import org.oxycblt.auxio.library.LibraryFragment import org.oxycblt.auxio.music.MusicStore import org.oxycblt.auxio.playback.PlaybackViewModel -import org.oxycblt.auxio.songs.SongsFragment import org.oxycblt.auxio.ui.accent import org.oxycblt.auxio.ui.getInactiveAlpha import org.oxycblt.auxio.ui.getTransparentAccent @@ -52,40 +50,24 @@ class MainFragment : Fragment() { accent.first, getInactiveAlpha(accent.first) ) + val navIconTints = ColorStateList( + arrayOf( + intArrayOf(-android.R.attr.state_checked), + intArrayOf(android.R.attr.state_checked) + ), + intArrayOf(colorInactive, colorActive) + ) // --- UI SETUP --- - // TODO: Add nested viewpager navigation [If practical] - binding.lifecycleOwner = this - binding.mainViewPager.adapter = PagerAdapter() - // Link the ViewPager & Tab View - TabLayoutMediator(binding.mainTabs, binding.mainViewPager) { tab, position -> - tab.icon = ContextCompat.getDrawable(requireContext(), tabIcons[position]) - - // Set the icon tint to deselected if its not the default tab - if (position > 0) { - tab.icon?.setTint(colorInactive) - } - }.attach() - - // Set up the selected/deselected colors - binding.mainTabs.addOnTabSelectedListener( - object : TabLayout.OnTabSelectedListener { - - override fun onTabSelected(tab: TabLayout.Tab) { - tab.icon?.setTint(colorActive) - } - - override fun onTabUnselected(tab: TabLayout.Tab) { - tab.icon?.setTint(colorInactive) - } - - override fun onTabReselected(tab: TabLayout.Tab?) { - } - } - ) + binding.navBar.itemIconTintList = navIconTints + binding.navBar.itemTextColor = navIconTints + ((childFragmentManager.findFragmentById(R.id.explore_nav_host) as NavHostFragment?))?.let { + // TODO: Add animation with BottomNavigationView navs + binding.navBar.setupWithNavController(it.findNavController()) + } // --- VIEWMODEL SETUP --- @@ -107,35 +89,4 @@ class MainFragment : Fragment() { return binding.root } - - private fun fragmentAt(position: Int): Fragment { - return when (position) { - 0 -> LibraryFragment() - 1 -> SongsFragment() - - else -> SongsFragment() - } - } - - private inner class PagerAdapter : - FragmentStateAdapter(childFragmentManager, viewLifecycleOwner.lifecycle) { - override fun getItemCount(): Int = shownFragments.size - - override fun createFragment(position: Int): Fragment { - Log.d(this::class.simpleName, "Switching to fragment $position.") - - if (shownFragments.contains(position)) { - return fragmentAt(position) - } - - // If a fragment that shouldn't be shown is somehow shown anyway, just return - // its intended fragment. - Log.e( - this::class.simpleName, - "Attempted to index a fragment that shouldn't be shown." - ) - - return fragmentAt(position) - } - } } 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 4e3fbba5a..42790078f 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt @@ -6,7 +6,6 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.appcompat.widget.PopupMenu -import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs @@ -20,7 +19,7 @@ import org.oxycblt.auxio.ui.applyDivider import org.oxycblt.auxio.ui.disable import org.oxycblt.auxio.ui.setupAlbumSongActions -class AlbumDetailFragment : Fragment() { +class AlbumDetailFragment : DetailFragment() { private val args: AlbumDetailFragmentArgs by navArgs() private val detailModel: DetailViewModel by activityViewModels() 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 4739d5846..1addcf673 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt @@ -5,7 +5,6 @@ import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs @@ -17,7 +16,7 @@ import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.ui.applyDivider import org.oxycblt.auxio.ui.disable -class ArtistDetailFragment : Fragment() { +class ArtistDetailFragment : DetailFragment() { private val args: ArtistDetailFragmentArgs by navArgs() private val detailModel: DetailViewModel by activityViewModels() private val playbackModel: PlaybackViewModel by activityViewModels() @@ -107,10 +106,4 @@ class ArtistDetailFragment : Fragment() { return binding.root } - - override fun onResume() { - super.onResume() - - detailModel.updateNavigationStatus(false) - } } diff --git a/app/src/main/java/org/oxycblt/auxio/detail/DetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/DetailFragment.kt new file mode 100644 index 000000000..87ba8147f --- /dev/null +++ b/app/src/main/java/org/oxycblt/auxio/detail/DetailFragment.kt @@ -0,0 +1,44 @@ +package org.oxycblt.auxio.detail + +import android.os.Bundle +import android.view.View +import androidx.activity.OnBackPressedCallback +import androidx.fragment.app.Fragment +import androidx.navigation.fragment.findNavController + +/** + * A Base [Fragment] implementing a [OnBackPressedCallback] so that Auxio will navigate upwards + * instead of out of the app if a Detail Fragment is currently open. + * @author OxygenCobalt + */ +abstract class DetailFragment : Fragment() { + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, callback) + } + + override fun onResume() { + super.onResume() + + callback.isEnabled = true + } + + override fun onPause() { + super.onPause() + callback.isEnabled = false + } + + private val callback = object : OnBackPressedCallback(false) { + + override fun handleOnBackPressed() { + val navController = findNavController() + // Check if it's the root of nested fragments in this navhost + if (navController.currentDestination?.id == navController.graph.startDestination) { + isEnabled = false + requireActivity().onBackPressed() + isEnabled = true + } else { + navController.navigateUp() + } + } + } +} 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 501bde764..a3d3c5760 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt @@ -5,7 +5,6 @@ import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs @@ -17,7 +16,7 @@ import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.ui.applyDivider import org.oxycblt.auxio.ui.disable -class GenreDetailFragment : Fragment() { +class GenreDetailFragment : DetailFragment() { private val args: GenreDetailFragmentArgs by navArgs() private val detailModel: DetailViewModel by activityViewModels() 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 bdf60282d..a1b00305b 100644 --- a/app/src/main/java/org/oxycblt/auxio/library/LibraryFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/library/LibraryFragment.kt @@ -203,9 +203,9 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener { findNavController().navigate( when (baseModel) { - is Genre -> MainFragmentDirections.actionShowGenre(baseModel.id) - is Artist -> MainFragmentDirections.actionShowArtist(baseModel.id) - is Album -> MainFragmentDirections.actionShowAlbum(baseModel.id, true) + is Genre -> LibraryFragmentDirections.actionShowGenre(baseModel.id) + is Artist -> LibraryFragmentDirections.actionShowArtist(baseModel.id) + is Album -> LibraryFragmentDirections.actionShowAlbum(baseModel.id, true) // If given model wasn't valid, then reset the navigation status // and abort the navigation. diff --git a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueDragCallback.kt b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueDragCallback.kt index 4cbb0984b..cb1838ae5 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueDragCallback.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueDragCallback.kt @@ -53,7 +53,11 @@ class QueueDragCallback(private val playbackModel: PlaybackViewModel) : ItemTouc viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder ): Boolean { - return playbackModel.moveQueueItems(viewHolder.adapterPosition, target.adapterPosition, queueAdapter) + return playbackModel.moveQueueItems( + viewHolder.adapterPosition, + target.adapterPosition, + queueAdapter + ) } override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { 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 728ed9202..d9569f72f 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 @@ -539,7 +539,10 @@ class PlaybackStateManager private constructor() { // If the parent was somehow dropped during saving, attempt to restore it. mSong?.let { if (mParent == null && mMode != PlaybackMode.ALL_SONGS) { - Log.d(this::class.simpleName, "Parent was corrupted while in mode $mMode. Attempting to restore.") + Log.d( + this::class.simpleName, + "Parent was corrupted while in mode $mMode. Attempting to restore." + ) mParent = when (mMode) { PlaybackMode.IN_ARTIST -> it.album.artist PlaybackMode.IN_ALBUM -> it.album diff --git a/app/src/main/res/layout/fragment_compact_playback.xml b/app/src/main/res/layout/fragment_compact_playback.xml index 0f7cb94ea..031ca55be 100644 --- a/app/src/main/res/layout/fragment_compact_playback.xml +++ b/app/src/main/res/layout/fragment_compact_playback.xml @@ -15,6 +15,8 @@ type="org.oxycblt.auxio.playback.PlaybackViewModel" /> + + - - + android:elevation="@dimen/elevation_normal" + app:layout_constraintBottom_toTopOf="@+id/compact_playback" + app:layout_constraintTop_toTopOf="parent" + app:navGraph="@navigation/nav_explore" + tools:layout="@layout/fragment_library" /> + app:layout_constraintBottom_toTopOf="@+id/nav_bar" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + tools:layout="@layout/fragment_compact_playback" /> - + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:menu="@menu/menu_nav" /> - + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_playback.xml b/app/src/main/res/layout/fragment_playback.xml index 5433e37a9..2442c9741 100644 --- a/app/src/main/res/layout/fragment_playback.xml +++ b/app/src/main/res/layout/fragment_playback.xml @@ -46,7 +46,7 @@ android:layout_margin="@dimen/margin_mid_large" android:contentDescription="@{@string/description_album_cover(song.name)}" android:outlineProvider="bounds" - android:elevation="4dp" + android:elevation="2dp" app:coverArt="@{song}" app:layout_constraintBottom_toTopOf="@+id/playback_song" app:layout_constraintDimensionRatio="1:1" diff --git a/app/src/main/res/menu/menu_nav.xml b/app/src/main/res/menu/menu_nav.xml new file mode 100644 index 000000000..97a007ec1 --- /dev/null +++ b/app/src/main/res/menu/menu_nav.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/nav_explore.xml b/app/src/main/res/navigation/nav_explore.xml new file mode 100644 index 000000000..a7acf0aa9 --- /dev/null +++ b/app/src/main/res/navigation/nav_explore.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file