Revert to normal navigation structure
Revert to a saner navigation structure for the time being. Nested nav is interesting but I dont want to deal with the bugs at this moment.
This commit is contained in:
parent
5814907798
commit
02e803746b
11 changed files with 384 additions and 223 deletions
|
@ -3,49 +3,15 @@ package org.oxycblt.auxio
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.util.Log
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import androidx.databinding.DataBindingUtil
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import androidx.fragment.app.FragmentActivity
|
|
||||||
import androidx.lifecycle.ViewModelProvider
|
|
||||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
|
||||||
import com.google.android.material.tabs.TabLayout
|
|
||||||
import com.google.android.material.tabs.TabLayoutMediator
|
|
||||||
import org.oxycblt.auxio.databinding.ActivityMainBinding
|
|
||||||
import org.oxycblt.auxio.library.LibraryFragment
|
|
||||||
import org.oxycblt.auxio.music.MusicViewModel
|
|
||||||
import org.oxycblt.auxio.music.processing.MusicLoaderResponse
|
|
||||||
import org.oxycblt.auxio.songs.SongsFragment
|
|
||||||
import org.oxycblt.auxio.theme.accent
|
import org.oxycblt.auxio.theme.accent
|
||||||
import org.oxycblt.auxio.theme.getInactiveAlpha
|
|
||||||
import org.oxycblt.auxio.theme.getTransparentAccent
|
|
||||||
import org.oxycblt.auxio.theme.toColor
|
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
private val shownFragments = listOf(0, 1)
|
|
||||||
|
|
||||||
private val libraryFragment: LibraryFragment by lazy { LibraryFragment() }
|
|
||||||
private val songsFragment: SongsFragment by lazy { SongsFragment() }
|
|
||||||
|
|
||||||
private val tabIcons = listOf(
|
|
||||||
R.drawable.ic_library,
|
|
||||||
R.drawable.ic_song
|
|
||||||
)
|
|
||||||
|
|
||||||
private lateinit var binding: ActivityMainBinding
|
|
||||||
|
|
||||||
private val musicModel: MusicViewModel by lazy {
|
|
||||||
ViewModelProvider(
|
|
||||||
this, MusicViewModel.Factory(application)
|
|
||||||
).get(MusicViewModel::class.java)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? {
|
override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? {
|
||||||
|
// Debug placeholder, ignore
|
||||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
|
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
|
||||||
|
|
||||||
// Apply the theme
|
// Apply the theme
|
||||||
|
@ -56,92 +22,6 @@ class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
setContentView(R.layout.activity_main)
|
||||||
binding = DataBindingUtil.setContentView(
|
|
||||||
this, R.layout.activity_main
|
|
||||||
)
|
|
||||||
|
|
||||||
binding.lifecycleOwner = this
|
|
||||||
|
|
||||||
val adapter = PagerAdapter(this)
|
|
||||||
binding.viewPager.adapter = adapter
|
|
||||||
|
|
||||||
val colorActive = accent.first.toColor(baseContext)
|
|
||||||
val colorInactive = getTransparentAccent(
|
|
||||||
baseContext,
|
|
||||||
accent.first,
|
|
||||||
getInactiveAlpha(accent.first)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Link the ViewPager & Tab View
|
|
||||||
TabLayoutMediator(binding.tabs, binding.viewPager) { tab, position ->
|
|
||||||
tab.icon = ContextCompat.getDrawable(baseContext, tabIcons[position])
|
|
||||||
|
|
||||||
// Set the icon tint to deselected if its not the default tab
|
|
||||||
if (position > 0) {
|
|
||||||
tab.icon?.setTint(colorInactive)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init the fragment
|
|
||||||
fragmentAt(position)
|
|
||||||
}.attach()
|
|
||||||
|
|
||||||
// Set up the selected/deselected colors
|
|
||||||
binding.tabs.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?) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
musicModel.response.observe(
|
|
||||||
this,
|
|
||||||
{
|
|
||||||
if (it == MusicLoaderResponse.DONE) {
|
|
||||||
binding.loadingFragment.visibility = View.GONE
|
|
||||||
binding.viewPager.visibility = View.VISIBLE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
Log.d(this::class.simpleName, "Activity Created.")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun fragmentAt(position: Int): Fragment {
|
|
||||||
return when (position) {
|
|
||||||
0 -> libraryFragment
|
|
||||||
1 -> songsFragment
|
|
||||||
|
|
||||||
else -> libraryFragment
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private inner class PagerAdapter(activity: FragmentActivity) : FragmentStateAdapter(activity) {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not sure how this would happen but it might
|
|
||||||
Log.e(
|
|
||||||
this::class.simpleName,
|
|
||||||
"Attempted to index a fragment that shouldn't be shown. Returning libraryFragment."
|
|
||||||
)
|
|
||||||
|
|
||||||
return libraryFragment
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
122
app/src/main/java/org/oxycblt/auxio/MainFragment.kt
Normal file
122
app/src/main/java/org/oxycblt/auxio/MainFragment.kt
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
package org.oxycblt.auxio
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.databinding.DataBindingUtil
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.fragment.app.FragmentActivity
|
||||||
|
import androidx.fragment.app.activityViewModels
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||||
|
import com.google.android.material.tabs.TabLayout
|
||||||
|
import com.google.android.material.tabs.TabLayoutMediator
|
||||||
|
import org.oxycblt.auxio.databinding.FragmentMainBinding
|
||||||
|
import org.oxycblt.auxio.library.LibraryFragment
|
||||||
|
import org.oxycblt.auxio.music.MusicViewModel
|
||||||
|
import org.oxycblt.auxio.music.processing.MusicLoaderResponse
|
||||||
|
import org.oxycblt.auxio.songs.SongsFragment
|
||||||
|
import org.oxycblt.auxio.theme.accent
|
||||||
|
import org.oxycblt.auxio.theme.getInactiveAlpha
|
||||||
|
import org.oxycblt.auxio.theme.getTransparentAccent
|
||||||
|
import org.oxycblt.auxio.theme.toColor
|
||||||
|
|
||||||
|
class MainFragment : Fragment() {
|
||||||
|
private val shownFragments = listOf(0, 1)
|
||||||
|
|
||||||
|
private val libraryFragment: LibraryFragment by lazy { LibraryFragment() }
|
||||||
|
private val songsFragment: SongsFragment by lazy { SongsFragment() }
|
||||||
|
|
||||||
|
private val tabIcons = listOf(
|
||||||
|
R.drawable.ic_library,
|
||||||
|
R.drawable.ic_song
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View? {
|
||||||
|
val binding = FragmentMainBinding.inflate(inflater)
|
||||||
|
|
||||||
|
binding.lifecycleOwner = viewLifecycleOwner
|
||||||
|
|
||||||
|
binding.viewPager.adapter = PagerAdapter()
|
||||||
|
|
||||||
|
val colorActive = accent.first.toColor(requireContext())
|
||||||
|
val colorInactive = getTransparentAccent(
|
||||||
|
requireContext(),
|
||||||
|
accent.first,
|
||||||
|
getInactiveAlpha(accent.first)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Link the ViewPager & Tab View
|
||||||
|
TabLayoutMediator(binding.tabs, binding.viewPager) { 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init the fragment
|
||||||
|
fragmentAt(position)
|
||||||
|
}.attach()
|
||||||
|
|
||||||
|
// Set up the selected/deselected colors
|
||||||
|
binding.tabs.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?) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Log.d(this::class.simpleName, "Fragment Created.")
|
||||||
|
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fragmentAt(position: Int): Fragment {
|
||||||
|
return when (position) {
|
||||||
|
0 -> libraryFragment
|
||||||
|
1 -> songsFragment
|
||||||
|
|
||||||
|
else -> libraryFragment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not sure how this would happen but it might
|
||||||
|
Log.e(
|
||||||
|
this::class.simpleName,
|
||||||
|
"Attempted to index a fragment that shouldn't be shown. Returning libraryFragment."
|
||||||
|
)
|
||||||
|
|
||||||
|
return libraryFragment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,9 +19,7 @@ class ArtistDetailFragment : Fragment() {
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View? {
|
||||||
val binding = DataBindingUtil.inflate<FragmentArtistDetailBinding>(
|
val binding = FragmentArtistDetailBinding.inflate(inflater)
|
||||||
inflater, R.layout.fragment_artist_detail, container, false
|
|
||||||
)
|
|
||||||
|
|
||||||
// I honestly don't want to turn of the any data classes into a parcelables due to how
|
// I honestly don't want to turn of the any data classes into a parcelables due to how
|
||||||
// many lists they store, so just pick up the artist id and find it from musicModel.
|
// many lists they store, so just pick up the artist id and find it from musicModel.
|
||||||
|
|
|
@ -2,51 +2,52 @@ package org.oxycblt.auxio.library
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.activity.OnBackPressedCallback
|
import android.view.LayoutInflater
|
||||||
import androidx.navigation.fragment.NavHostFragment
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.databinding.DataBindingUtil
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.fragment.app.activityViewModels
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.navigation.fragment.findNavController
|
||||||
|
import org.oxycblt.auxio.MainFragment
|
||||||
|
import org.oxycblt.auxio.MainFragmentDirections
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
|
import org.oxycblt.auxio.databinding.FragmentLibraryBinding
|
||||||
|
import org.oxycblt.auxio.library.adapters.ArtistAdapter
|
||||||
|
import org.oxycblt.auxio.music.MusicViewModel
|
||||||
|
import org.oxycblt.auxio.music.models.Artist
|
||||||
|
import org.oxycblt.auxio.recycler.ClickListener
|
||||||
|
import org.oxycblt.auxio.recycler.applyDivider
|
||||||
|
|
||||||
class LibraryFragment : NavHostFragment() {
|
class LibraryFragment : Fragment() {
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
private val musicModel: MusicViewModel by activityViewModels()
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
|
|
||||||
requireActivity().onBackPressedDispatcher.addCallback(
|
override fun onCreateView(
|
||||||
this, callback
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View? {
|
||||||
|
val binding = FragmentLibraryBinding.inflate(inflater)
|
||||||
|
|
||||||
|
binding.libraryRecycler.adapter = ArtistAdapter(
|
||||||
|
musicModel.artists.value!!,
|
||||||
|
ClickListener { navToArtist(it) }
|
||||||
)
|
)
|
||||||
|
binding.libraryRecycler.applyDivider()
|
||||||
navController.setGraph(R.navigation.nav_library)
|
binding.libraryRecycler.setHasFixedSize(true)
|
||||||
|
|
||||||
Log.d(this::class.simpleName, "Fragment created.")
|
Log.d(this::class.simpleName, "Fragment created.")
|
||||||
|
|
||||||
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
private fun navToArtist(artist: Artist) {
|
||||||
super.onResume()
|
// Don't navigate to a fragment multiple times if multiple items are accepted.
|
||||||
|
|
||||||
callback.isEnabled = true
|
findNavController().navigate(
|
||||||
}
|
MainFragmentDirections.actionShowArtist(artist.id)
|
||||||
|
)
|
||||||
override fun onPause() {
|
|
||||||
super.onPause()
|
|
||||||
|
|
||||||
callback.isEnabled = false
|
|
||||||
}
|
|
||||||
|
|
||||||
val callback = object : OnBackPressedCallback(false) {
|
|
||||||
|
|
||||||
override fun handleOnBackPressed() {
|
|
||||||
|
|
||||||
// If at the root of the navigation, perform onBackPressed from the main activity.
|
|
||||||
if (navController.currentDestination?.id == navController.graph.startDestination) {
|
|
||||||
// Disable the callback as it will get caught in an infinite loop otherwise.
|
|
||||||
isEnabled = false
|
|
||||||
|
|
||||||
requireActivity().onBackPressed()
|
|
||||||
|
|
||||||
isEnabled = true
|
|
||||||
} else {
|
|
||||||
navController.navigateUp()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import androidx.core.content.ContextCompat
|
||||||
import androidx.databinding.DataBindingUtil
|
import androidx.databinding.DataBindingUtil
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
|
import androidx.navigation.fragment.findNavController
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.databinding.FragmentLoadingBinding
|
import org.oxycblt.auxio.databinding.FragmentLoadingBinding
|
||||||
import org.oxycblt.auxio.music.MusicViewModel
|
import org.oxycblt.auxio.music.MusicViewModel
|
||||||
|
@ -20,6 +21,8 @@ import org.oxycblt.auxio.music.processing.MusicLoaderResponse
|
||||||
|
|
||||||
class LoadingFragment : Fragment(R.layout.fragment_loading) {
|
class LoadingFragment : Fragment(R.layout.fragment_loading) {
|
||||||
|
|
||||||
|
// LoadingFragment is [hopefully] going to be the first one to have to create musicModel,
|
||||||
|
// so pass a factory instance so that the model has access to the application resources.
|
||||||
private val musicModel: MusicViewModel by activityViewModels {
|
private val musicModel: MusicViewModel by activityViewModels {
|
||||||
MusicViewModel.Factory(requireActivity().application)
|
MusicViewModel.Factory(requireActivity().application)
|
||||||
}
|
}
|
||||||
|
@ -32,9 +35,7 @@ class LoadingFragment : Fragment(R.layout.fragment_loading) {
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View? {
|
||||||
binding = DataBindingUtil.inflate(
|
binding = FragmentLoadingBinding.inflate(inflater)
|
||||||
inflater, R.layout.fragment_loading, container, false
|
|
||||||
)
|
|
||||||
|
|
||||||
binding.lifecycleOwner = this
|
binding.lifecycleOwner = this
|
||||||
binding.musicModel = musicModel
|
binding.musicModel = musicModel
|
||||||
|
@ -87,6 +88,10 @@ class LoadingFragment : Fragment(R.layout.fragment_loading) {
|
||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
}
|
||||||
|
|
||||||
// Check for two things:
|
// Check for two things:
|
||||||
// - If Auxio needs to show the rationale for getting the READ_EXTERNAL_STORAGE permission.
|
// - If Auxio needs to show the rationale for getting the READ_EXTERNAL_STORAGE permission.
|
||||||
// - If Auxio straight up doesn't have the READ_EXTERNAL_STORAGE permission.
|
// - If Auxio straight up doesn't have the READ_EXTERNAL_STORAGE permission.
|
||||||
|
@ -98,13 +103,16 @@ class LoadingFragment : Fragment(R.layout.fragment_loading) {
|
||||||
) == PackageManager.PERMISSION_DENIED
|
) == PackageManager.PERMISSION_DENIED
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onMusicLoadResponse(repoResponse: MusicLoaderResponse?) {
|
private fun onMusicLoadResponse(response: MusicLoaderResponse?) {
|
||||||
// Don't run this if the value is null, Which is what the value changes to after
|
binding.loadingBar.visibility = View.GONE
|
||||||
// this is run.
|
|
||||||
repoResponse?.let { response ->
|
|
||||||
binding.loadingBar.visibility = View.GONE
|
|
||||||
|
|
||||||
if (response != MusicLoaderResponse.DONE) {
|
if (response == MusicLoaderResponse.DONE) {
|
||||||
|
findNavController().navigate(
|
||||||
|
LoadingFragmentDirections.actionToMain()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
binding.let { binding ->
|
||||||
binding.errorText.text =
|
binding.errorText.text =
|
||||||
if (response == MusicLoaderResponse.NO_MUSIC)
|
if (response == MusicLoaderResponse.NO_MUSIC)
|
||||||
getString(R.string.error_no_music)
|
getString(R.string.error_no_music)
|
||||||
|
@ -126,9 +134,9 @@ class LoadingFragment : Fragment(R.layout.fragment_loading) {
|
||||||
// along with a GRANT button
|
// along with a GRANT button
|
||||||
|
|
||||||
binding.loadingBar.visibility = View.GONE
|
binding.loadingBar.visibility = View.GONE
|
||||||
binding.errorText.visibility = View.VISIBLE
|
|
||||||
binding.statusIcon.visibility = View.VISIBLE
|
binding.statusIcon.visibility = View.VISIBLE
|
||||||
binding.grantButton.visibility = View.VISIBLE
|
binding.grantButton.visibility = View.VISIBLE
|
||||||
|
binding.errorText.visibility = View.VISIBLE
|
||||||
|
|
||||||
binding.errorText.text = getString(R.string.error_no_perms)
|
binding.errorText.text = getString(R.string.error_no_perms)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,53 +1,10 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.fragment.app.FragmentContainerView
|
||||||
|
android:id="@+id/nav_host"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:name="androidx.navigation.fragment.NavHostFragment"
|
||||||
|
app:defaultNavHost="true"
|
||||||
|
app:navGraph="@navigation/nav_main"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:android="http://schemas.android.com/apk/res/android" />
|
||||||
|
|
||||||
<data>
|
|
||||||
|
|
||||||
<variable
|
|
||||||
name="musicModel"
|
|
||||||
type="org.oxycblt.auxio.music.MusicViewModel" />
|
|
||||||
</data>
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:animateLayoutChanges="true">
|
|
||||||
|
|
||||||
<androidx.viewpager2.widget.ViewPager2
|
|
||||||
android:id="@+id/view_pager"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/tabs"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<androidx.fragment.app.FragmentContainerView
|
|
||||||
android:id="@+id/loading_fragment"
|
|
||||||
android:name="org.oxycblt.auxio.loading.LoadingFragment"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/tabs"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<com.google.android.material.tabs.TabLayout
|
|
||||||
android:id="@+id/tabs"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="@dimen/tab_menu_size"
|
|
||||||
android:layout_gravity="bottom"
|
|
||||||
android:background="?android:attr/windowBackground"
|
|
||||||
android:elevation="@dimen/elevation_normal"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:tabGravity="fill"
|
|
||||||
app:tabIconTint="?android:attr/colorPrimary"
|
|
||||||
app:tabIconTintMode="src_in"
|
|
||||||
app:tabIndicator="@drawable/indicator"
|
|
||||||
app:tabIndicatorColor="?android:attr/colorPrimary"
|
|
||||||
app:tabMode="fixed"
|
|
||||||
app:tabRippleColor="@color/selection_color" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
</layout>
|
|
|
@ -1,5 +1,7 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<layout xmlns:android="http://schemas.android.com/apk/res/android">
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
<data>
|
<data>
|
||||||
|
|
||||||
|
@ -14,9 +16,20 @@
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:animateLayoutChanges="true">
|
android:animateLayoutChanges="true">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.Toolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?android:attr/actionBarSize"
|
||||||
|
android:background="?android:attr/windowBackground"
|
||||||
|
android:elevation="@dimen/elevation_normal"
|
||||||
|
app:titleTextAppearance="@style/TextAppearance.Toolbar.Bold"
|
||||||
|
app:title="@string/title_library_fragment"
|
||||||
|
tools:titleTextColor="@color/blue" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@{artist.name}" />
|
android:text="@{artist.name}" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</layout>
|
</layout>
|
30
app/src/main/res/layout/fragment_library.xml
Normal file
30
app/src/main/res/layout/fragment_library.xml
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:animateLayoutChanges="true">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.Toolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?android:attr/actionBarSize"
|
||||||
|
android:background="?android:attr/windowBackground"
|
||||||
|
android:elevation="@dimen/elevation_normal"
|
||||||
|
app:titleTextAppearance="@style/TextAppearance.Toolbar.Bold"
|
||||||
|
app:title="@string/title_library_fragment"
|
||||||
|
tools:titleTextColor="@color/blue" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/library_recycler"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
tools:listitem="@layout/item_album" />
|
||||||
|
</LinearLayout>
|
||||||
|
</layout>
|
35
app/src/main/res/layout/fragment_main.xml
Normal file
35
app/src/main/res/layout/fragment_main.xml
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:animateLayoutChanges="true"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<androidx.viewpager2.widget.ViewPager2
|
||||||
|
android:id="@+id/view_pager"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"/>
|
||||||
|
|
||||||
|
<com.google.android.material.tabs.TabLayout
|
||||||
|
android:id="@+id/tabs"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/tab_menu_size"
|
||||||
|
android:layout_gravity="bottom"
|
||||||
|
android:background="?android:attr/windowBackground"
|
||||||
|
android:elevation="@dimen/elevation_normal"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:tabGravity="fill"
|
||||||
|
app:tabIconTint="?android:attr/colorPrimary"
|
||||||
|
app:tabIconTintMode="src_in"
|
||||||
|
app:tabIndicator="@drawable/indicator"
|
||||||
|
app:tabIndicatorColor="?android:attr/colorPrimary"
|
||||||
|
app:tabMode="fixed"
|
||||||
|
app:tabRippleColor="@color/selection_color" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</layout>
|
81
app/src/main/res/layout/song_item.xml
Normal file
81
app/src/main/res/layout/song_item.xml
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layout xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<data>
|
||||||
|
|
||||||
|
<variable
|
||||||
|
name="song"
|
||||||
|
type="org.oxycblt.auxio.music.models.Song" />
|
||||||
|
</data>
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/ripple"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:padding="@dimen/padding_medium">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/cover"
|
||||||
|
android:layout_width="@dimen/cover_size_compact"
|
||||||
|
android:layout_height="@dimen/cover_size_compact"
|
||||||
|
app:coverArt="@{song}"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:src="@tools:sample/backgrounds/scenic"
|
||||||
|
tools:ignore="ContentDescription" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/song_name"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="@dimen/margin_medium"
|
||||||
|
android:layout_marginEnd="@dimen/margin_medium"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:text="@{song.name}"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||||
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/song_info"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/duration"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/cover"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintVertical_chainStyle="packed"
|
||||||
|
tools:text="Song Name" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/song_info"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="@dimen/margin_medium"
|
||||||
|
android:layout_marginEnd="@dimen/margin_medium"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
android:text="@{@string/format_song_info(song.album.artist.name, song.album.name)}"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/duration"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/cover"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/song_name"
|
||||||
|
tools:text="Artist / Album" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/duration"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@{song.formattedDuration}"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
|
||||||
|
android:textColor="?android:attr/textColorTertiary"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:text="16:16" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</layout>
|
36
app/src/main/res/navigation/nav_main.xml
Normal file
36
app/src/main/res/navigation/nav_main.xml
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/nav_main"
|
||||||
|
app:startDestination="@id/loading_fragment">
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/loading_fragment"
|
||||||
|
android:name="org.oxycblt.auxio.loading.LoadingFragment"
|
||||||
|
android:label="LoadingFragment"
|
||||||
|
tools:layout="@layout/fragment_loading">
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_to_main"
|
||||||
|
app:destination="@id/main_fragment"
|
||||||
|
app:popUpTo="@id/loading_fragment"
|
||||||
|
app:popUpToInclusive="true" />
|
||||||
|
</fragment>
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/main_fragment"
|
||||||
|
android:name="org.oxycblt.auxio.MainFragment"
|
||||||
|
android:label="MainFragment"
|
||||||
|
tools:layout="@layout/fragment_main">
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_show_artist"
|
||||||
|
app:destination="@id/artist_detail_fragment" />
|
||||||
|
</fragment>
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/artist_detail_fragment"
|
||||||
|
android:name="org.oxycblt.auxio.detail.ArtistDetailFragment"
|
||||||
|
android:label="ArtistDetailFragment"
|
||||||
|
tools:layout="@layout/fragment_artist_detail">
|
||||||
|
<argument
|
||||||
|
android:name="artistId"
|
||||||
|
app:argType="long" />
|
||||||
|
</fragment>
|
||||||
|
</navigation>
|
Loading…
Reference in a new issue