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.os.Bundle
|
||||
import android.util.AttributeSet
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
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.getInactiveAlpha
|
||||
import org.oxycblt.auxio.theme.getTransparentAccent
|
||||
import org.oxycblt.auxio.theme.toColor
|
||||
|
||||
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? {
|
||||
// Debug placeholder, ignore
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
|
||||
|
||||
// Apply the theme
|
||||
|
@ -56,92 +22,6 @@ class MainActivity : AppCompatActivity() {
|
|||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
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
|
||||
}
|
||||
setContentView(R.layout.activity_main)
|
||||
}
|
||||
}
|
||||
|
|
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?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
val binding = DataBindingUtil.inflate<FragmentArtistDetailBinding>(
|
||||
inflater, R.layout.fragment_artist_detail, container, false
|
||||
)
|
||||
val binding = FragmentArtistDetailBinding.inflate(inflater)
|
||||
|
||||
// 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.
|
||||
|
|
|
@ -2,51 +2,52 @@ package org.oxycblt.auxio.library
|
|||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.navigation.fragment.NavHostFragment
|
||||
import android.view.LayoutInflater
|
||||
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.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?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
private val musicModel: MusicViewModel by activityViewModels()
|
||||
|
||||
requireActivity().onBackPressedDispatcher.addCallback(
|
||||
this, callback
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
val binding = FragmentLibraryBinding.inflate(inflater)
|
||||
|
||||
binding.libraryRecycler.adapter = ArtistAdapter(
|
||||
musicModel.artists.value!!,
|
||||
ClickListener { navToArtist(it) }
|
||||
)
|
||||
|
||||
navController.setGraph(R.navigation.nav_library)
|
||||
binding.libraryRecycler.applyDivider()
|
||||
binding.libraryRecycler.setHasFixedSize(true)
|
||||
|
||||
Log.d(this::class.simpleName, "Fragment created.")
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
private fun navToArtist(artist: Artist) {
|
||||
// Don't navigate to a fragment multiple times if multiple items are accepted.
|
||||
|
||||
callback.isEnabled = true
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
findNavController().navigate(
|
||||
MainFragmentDirections.actionShowArtist(artist.id)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import androidx.core.content.ContextCompat
|
|||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.FragmentLoadingBinding
|
||||
import org.oxycblt.auxio.music.MusicViewModel
|
||||
|
@ -20,6 +21,8 @@ import org.oxycblt.auxio.music.processing.MusicLoaderResponse
|
|||
|
||||
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 {
|
||||
MusicViewModel.Factory(requireActivity().application)
|
||||
}
|
||||
|
@ -32,9 +35,7 @@ class LoadingFragment : Fragment(R.layout.fragment_loading) {
|
|||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
binding = DataBindingUtil.inflate(
|
||||
inflater, R.layout.fragment_loading, container, false
|
||||
)
|
||||
binding = FragmentLoadingBinding.inflate(inflater)
|
||||
|
||||
binding.lifecycleOwner = this
|
||||
binding.musicModel = musicModel
|
||||
|
@ -87,6 +88,10 @@ class LoadingFragment : Fragment(R.layout.fragment_loading) {
|
|||
return binding.root
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
// Check for two things:
|
||||
// - 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.
|
||||
|
@ -98,13 +103,16 @@ class LoadingFragment : Fragment(R.layout.fragment_loading) {
|
|||
) == PackageManager.PERMISSION_DENIED
|
||||
}
|
||||
|
||||
private fun onMusicLoadResponse(repoResponse: MusicLoaderResponse?) {
|
||||
// Don't run this if the value is null, Which is what the value changes to after
|
||||
// this is run.
|
||||
repoResponse?.let { response ->
|
||||
binding.loadingBar.visibility = View.GONE
|
||||
private fun onMusicLoadResponse(response: MusicLoaderResponse?) {
|
||||
binding.loadingBar.visibility = View.GONE
|
||||
|
||||
if (response != MusicLoaderResponse.DONE) {
|
||||
if (response == MusicLoaderResponse.DONE) {
|
||||
findNavController().navigate(
|
||||
LoadingFragmentDirections.actionToMain()
|
||||
)
|
||||
}
|
||||
else {
|
||||
binding.let { binding ->
|
||||
binding.errorText.text =
|
||||
if (response == MusicLoaderResponse.NO_MUSIC)
|
||||
getString(R.string.error_no_music)
|
||||
|
@ -126,9 +134,9 @@ class LoadingFragment : Fragment(R.layout.fragment_loading) {
|
|||
// along with a GRANT button
|
||||
|
||||
binding.loadingBar.visibility = View.GONE
|
||||
binding.errorText.visibility = View.VISIBLE
|
||||
binding.statusIcon.visibility = View.VISIBLE
|
||||
binding.grantButton.visibility = View.VISIBLE
|
||||
binding.errorText.visibility = View.VISIBLE
|
||||
|
||||
binding.errorText.text = getString(R.string.error_no_perms)
|
||||
}
|
||||
|
|
|
@ -1,53 +1,10 @@
|
|||
<?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:tools="http://schemas.android.com/tools">
|
||||
|
||||
<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>
|
||||
xmlns:android="http://schemas.android.com/apk/res/android" />
|
|
@ -1,5 +1,7 @@
|
|||
<?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>
|
||||
|
||||
|
@ -14,9 +16,20 @@
|
|||
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" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{artist.name}" />
|
||||
|
||||
</LinearLayout>
|
||||
</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