Add playback fragment

Add a fragment to show songs that are currently being played.
This commit is contained in:
OxygenCobalt 2020-10-05 15:54:53 -06:00
parent 21ff93d5f0
commit a72cb48c71
18 changed files with 266 additions and 103 deletions

View file

@ -17,8 +17,8 @@ TODOs surrounded with !s are things I tried to do, but failed for reasons includ
/songs/ /songs/
- Search when LibraryFragment isnt enabled
- ? Sorting ? - ? Sorting ?
- ? Search ?
- ? Fast Scrolling ? - ? Fast Scrolling ?
/library/ /library/
@ -28,9 +28,14 @@ TODOs surrounded with !s are things I tried to do, but failed for reasons includ
- ? Add Nested Nav to Library ViewPager fragment [Hold fire on this until everything else is added, there could be sneaky bugs later on if you add it now] ? - ? Add Nested Nav to Library ViewPager fragment [Hold fire on this until everything else is added, there could be sneaky bugs later on if you add it now] ?
- ! Move Adapter functionality to ListAdapter [RecyclerView scrolls to middle/bottom when data is re-sorted] ! - ! Move Adapter functionality to ListAdapter [RecyclerView scrolls to middle/bottom when data is re-sorted] !
/playback/
-
/other/
- Highlight recycler items when they are being played
/bugs/ /bugs/
- Fix issue where fast navigations will cause the app to not display anything - Fix issue where fast navigations will cause the app to not display anything
To be added: To be added:
/prefs/ /prefs/
/playback/

View file

@ -47,9 +47,9 @@ dependencies {
// --- SUPPORT --- // --- SUPPORT ---
// General // General
implementation 'androidx.core:core-ktx:1.3.1' implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.activity:activity:1.2.0-alpha08' implementation 'androidx.activity:activity:1.2.0-beta01'
implementation 'androidx.fragment:fragment:1.3.0-alpha08' implementation 'androidx.fragment:fragment:1.3.0-beta01'
// Layout // Layout
implementation 'androidx.constraintlayout:constraintlayout:2.0.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
@ -71,7 +71,7 @@ dependencies {
implementation 'io.coil-kt:coil:0.13.0' implementation 'io.coil-kt:coil:0.13.0'
// Material // Material
implementation 'com.google.android.material:material:1.3.0-alpha02' implementation 'com.google.android.material:material:1.3.0-alpha03'
// Lint // Lint
ktlint "com.pinterest:ktlint:0.37.2" ktlint "com.pinterest:ktlint:0.37.2"

View file

@ -41,7 +41,7 @@ class MainFragment : Fragment() {
val binding = FragmentMainBinding.inflate(inflater) val binding = FragmentMainBinding.inflate(inflater)
// If musicModel was cleared while the app was closed [Likely due to Auxio being suspended // If musicModel was cleared while the app was closed [Likely due to Auxio being suspended
// in the background], then navigate back to loading to reload the music. // in the background], then navigate back to LoadingFragment to reload the music.
if (musicModel.response.value == null) { if (musicModel.response.value == null) {
findNavController().navigate(MainFragmentDirections.actionReturnToLoading()) findNavController().navigate(MainFragmentDirections.actionReturnToLoading())
@ -57,7 +57,7 @@ class MainFragment : Fragment() {
// --- UI SETUP --- // --- UI SETUP ---
binding.lifecycleOwner = viewLifecycleOwner binding.lifecycleOwner = this
binding.mainViewPager.adapter = PagerAdapter() binding.mainViewPager.adapter = PagerAdapter()
// Link the ViewPager & Tab View // Link the ViewPager & Tab View

View file

@ -13,6 +13,7 @@ import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.FragmentAlbumDetailBinding import org.oxycblt.auxio.databinding.FragmentAlbumDetailBinding
import org.oxycblt.auxio.detail.adapters.DetailSongAdapter import org.oxycblt.auxio.detail.adapters.DetailSongAdapter
import org.oxycblt.auxio.music.MusicViewModel import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.recycler.ClickListener import org.oxycblt.auxio.recycler.ClickListener
import org.oxycblt.auxio.theme.applyDivider import org.oxycblt.auxio.theme.applyDivider
import org.oxycblt.auxio.theme.disable import org.oxycblt.auxio.theme.disable
@ -21,6 +22,7 @@ class AlbumDetailFragment : Fragment() {
private val args: AlbumDetailFragmentArgs by navArgs() private val args: AlbumDetailFragmentArgs by navArgs()
private val detailModel: DetailViewModel by activityViewModels() private val detailModel: DetailViewModel by activityViewModels()
private val playbackModel: PlaybackViewModel by activityViewModels()
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
@ -45,7 +47,7 @@ class AlbumDetailFragment : Fragment() {
val songAdapter = DetailSongAdapter( val songAdapter = DetailSongAdapter(
ClickListener { ClickListener {
Log.d(this::class.simpleName, it.name) playbackModel.updateSong(it)
} }
) )

View file

@ -17,13 +17,15 @@ import androidx.transition.TransitionManager
import org.oxycblt.auxio.MainFragmentDirections 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.databinding.FragmentLibraryBinding
import org.oxycblt.auxio.library.recycler.LibraryAdapter import org.oxycblt.auxio.library.adapters.LibraryAdapter
import org.oxycblt.auxio.library.recycler.SearchAdapter import org.oxycblt.auxio.library.adapters.SearchAdapter
import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.music.Artist import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.BaseModel import org.oxycblt.auxio.music.BaseModel
import org.oxycblt.auxio.music.Genre import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.MusicViewModel import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.recycler.ShowMode import org.oxycblt.auxio.recycler.ShowMode
import org.oxycblt.auxio.theme.applyColor import org.oxycblt.auxio.theme.applyColor
import org.oxycblt.auxio.theme.applyDivider import org.oxycblt.auxio.theme.applyDivider
@ -36,6 +38,7 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
} }
private val libraryModel: LibraryViewModel by activityViewModels() private val libraryModel: LibraryViewModel by activityViewModels()
private val playbackModel: PlaybackViewModel by activityViewModels()
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
@ -173,6 +176,13 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
} }
private fun navToItem(baseModel: BaseModel) { private fun navToItem(baseModel: BaseModel) {
// If the item is a song [That was selected through search], then update the playback
// to that song instead of doing any naviagation
if (baseModel is Song) {
playbackModel.updateSong(baseModel)
return
}
if (!libraryModel.isNavigating) { if (!libraryModel.isNavigating) {
libraryModel.updateNavigationStatus(true) libraryModel.updateNavigationStatus(true)

View file

@ -1,4 +1,4 @@
package org.oxycblt.auxio.library.recycler package org.oxycblt.auxio.library.adapters
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
@ -13,7 +13,8 @@ import org.oxycblt.auxio.recycler.viewholders.ArtistViewHolder
import org.oxycblt.auxio.recycler.viewholders.GenreViewHolder import org.oxycblt.auxio.recycler.viewholders.GenreViewHolder
// A ListAdapter that can contain three different types of ViewHolders depending // A ListAdapter that can contain three different types of ViewHolders depending
// the showmode given. It cannot display multiple types of viewholders *at once*. // the ShowMode given.
// It cannot display multiple ViewHolders *at once* however. That's what SearchAdapter is for.
class LibraryAdapter( class LibraryAdapter(
private val showMode: ShowMode, private val showMode: ShowMode,
private val doOnClick: (BaseModel) -> Unit private val doOnClick: (BaseModel) -> Unit

View file

@ -1,4 +1,4 @@
package org.oxycblt.auxio.library.recycler package org.oxycblt.auxio.library.adapters
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.ListAdapter

View file

@ -20,62 +20,44 @@ 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)
} }
private lateinit var binding: FragmentLoadingBinding
private lateinit var permLauncher: ActivityResultLauncher<String>
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? { ): View? {
binding = FragmentLoadingBinding.inflate(inflater) val binding = FragmentLoadingBinding.inflate(inflater)
binding.lifecycleOwner = this
binding.musicModel = musicModel
musicModel.response.observe(
viewLifecycleOwner,
{ response ->
onMusicLoadResponse(response)
}
)
musicModel.doReload.observe(
viewLifecycleOwner,
{ retry ->
onRetry(retry)
}
)
musicModel.doGrant.observe(
viewLifecycleOwner,
{ grant ->
onGrant(grant)
}
)
// Set up the permission launcher, as its disallowed outside of onCreate. // Set up the permission launcher, as its disallowed outside of onCreate.
permLauncher = val permLauncher =
registerForActivityResult( registerForActivityResult(
ActivityResultContracts.RequestPermission() ActivityResultContracts.RequestPermission()
) { granted: Boolean -> ) { granted: Boolean ->
// If its actually granted, restart the loading process again. // If its actually granted, restart the loading process again.
if (granted) { if (granted) {
wipeViews() wipeViews(binding)
musicModel.reload() musicModel.reload()
} }
} }
// --- UI SETUP ---
binding.lifecycleOwner = this
binding.musicModel = musicModel
// --- VIEWMODEL SETUP ---
musicModel.response.observe(viewLifecycleOwner) { onMusicLoadResponse(it, binding) }
musicModel.doReload.observe(viewLifecycleOwner) { onRetry(it, binding) }
musicModel.doGrant.observe(viewLifecycleOwner) { onGrant(it, permLauncher) }
// Force an error screen if the permissions are denied or the prompt needs to be shown. // Force an error screen if the permissions are denied or the prompt needs to be shown.
if (checkPerms()) { if (checkPerms()) {
onNoPerms() onNoPerms(binding)
} else { } else {
musicModel.go() musicModel.go()
} }
@ -96,13 +78,15 @@ class LoadingFragment : Fragment(R.layout.fragment_loading) {
) == PackageManager.PERMISSION_DENIED ) == PackageManager.PERMISSION_DENIED
} }
private fun onMusicLoadResponse(response: MusicLoaderResponse?) { private fun onMusicLoadResponse(
response: MusicLoaderResponse?,
binding: FragmentLoadingBinding
) {
if (response == MusicLoaderResponse.DONE) { if (response == MusicLoaderResponse.DONE) {
findNavController().navigate( findNavController().navigate(
LoadingFragmentDirections.actionToMain() LoadingFragmentDirections.actionToMain()
) )
} else { } else {
binding.let { binding ->
binding.loadingErrorText.text = binding.loadingErrorText.text =
if (response == MusicLoaderResponse.NO_MUSIC) if (response == MusicLoaderResponse.NO_MUSIC)
getString(R.string.error_no_music) getString(R.string.error_no_music)
@ -112,15 +96,13 @@ class LoadingFragment : Fragment(R.layout.fragment_loading) {
// If the response wasn't a success, then show the specific error message // If the response wasn't a success, then show the specific error message
// depending on which error response was given, along with a retry button // depending on which error response was given, along with a retry button
binding.loadingBar.visibility = View.GONE binding.loadingBar.visibility = View.GONE
binding.loadingErrorText.visibility = View.VISIBLE binding.loadingErrorText.visibility = View.VISIBLE
binding.loadingErrorIcon.visibility = View.VISIBLE binding.loadingErrorIcon.visibility = View.VISIBLE
binding.loadingRetryButton.visibility = View.VISIBLE binding.loadingRetryButton.visibility = View.VISIBLE
} }
} }
}
private fun onNoPerms() { private fun onNoPerms(binding: FragmentLoadingBinding) {
// If there are no perms, switch out the view elements as if an error screen was being // If there are no perms, switch out the view elements as if an error screen was being
// shown, but show the label that Auxio needs to read external storage to function, // shown, but show the label that Auxio needs to read external storage to function,
// along with a GRANT button // along with a GRANT button
@ -133,15 +115,15 @@ class LoadingFragment : Fragment(R.layout.fragment_loading) {
binding.loadingErrorText.text = getString(R.string.error_no_perms) binding.loadingErrorText.text = getString(R.string.error_no_perms)
} }
private fun onRetry(retry: Boolean) { private fun onRetry(retry: Boolean, binding: FragmentLoadingBinding) {
if (retry) { if (retry) {
wipeViews() wipeViews(binding)
musicModel.doneWithReload() musicModel.doneWithReload()
} }
} }
private fun onGrant(grant: Boolean) { private fun onGrant(grant: Boolean, permLauncher: ActivityResultLauncher<String>) {
if (grant) { if (grant) {
permLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE) permLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE)
@ -150,7 +132,7 @@ class LoadingFragment : Fragment(R.layout.fragment_loading) {
} }
// Wipe views and switch back to the plain ProgressBar // Wipe views and switch back to the plain ProgressBar
private fun wipeViews() { private fun wipeViews(binding: FragmentLoadingBinding) {
binding.loadingBar.visibility = View.VISIBLE binding.loadingBar.visibility = View.VISIBLE
binding.loadingErrorText.visibility = View.GONE binding.loadingErrorText.visibility = View.GONE
binding.loadingErrorIcon.visibility = View.GONE binding.loadingErrorIcon.visibility = View.GONE

View file

@ -13,9 +13,9 @@ sealed class BaseModel {
data class Song( data class Song(
override val id: Long = -1, override val id: Long = -1,
override var name: String, override var name: String,
val albumId: Long, val albumId: Long = -1,
val track: Int, val track: Int = -1,
val duration: Long, val duration: Long = 0,
) : BaseModel() { ) : BaseModel() {
lateinit var album: Album lateinit var album: Album

View file

@ -0,0 +1,52 @@
package org.oxycblt.auxio.playback
import android.os.Bundle
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 org.oxycblt.auxio.databinding.FragmentCompactPlaybackBinding
import org.oxycblt.auxio.music.MusicViewModel
class CompactPlaybackFragment : Fragment() {
private val musicModel: MusicViewModel by activityViewModels {
MusicViewModel.Factory(requireActivity().application)
}
private val playbackModel: PlaybackViewModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding = FragmentCompactPlaybackBinding.inflate(inflater)
binding.lifecycleOwner = this
// Put a placeholder song in the binding & hide the playback fragment initially,
// as for some reason the attach event doesn't register anymore w/LiveData
binding.song = musicModel.songs.value!![0]
binding.root.visibility = View.GONE
playbackModel.currentSong.observe(viewLifecycleOwner) {
if (it == null) {
Log.d(this::class.simpleName, "Hiding playback bar due to no song being played.")
binding.root.visibility = View.GONE
} else {
Log.d(this::class.simpleName, "Updating song display to ${it.name}")
binding.song = it
binding.root.visibility = View.VISIBLE
}
}
Log.d(this::class.simpleName, "Fragment Created")
return binding.root
}
}

View file

@ -0,0 +1,15 @@
package org.oxycblt.auxio.playback
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import org.oxycblt.auxio.music.Song
class PlaybackViewModel : ViewModel() {
private val mCurrentSong = MutableLiveData<Song>()
val currentSong: LiveData<Song> get() = mCurrentSong
fun updateSong(song: Song) {
mCurrentSong.value = song
}
}

View file

@ -9,15 +9,17 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import org.oxycblt.auxio.databinding.FragmentSongsBinding import org.oxycblt.auxio.databinding.FragmentSongsBinding
import org.oxycblt.auxio.music.MusicViewModel import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.recycler.ClickListener import org.oxycblt.auxio.recycler.ClickListener
import org.oxycblt.auxio.theme.applyDivider import org.oxycblt.auxio.theme.applyDivider
class SongsFragment : Fragment() { class SongsFragment : Fragment() {
private val musicModel: MusicViewModel by activityViewModels { private val musicModel: MusicViewModel by activityViewModels {
MusicViewModel.Factory(requireActivity().application) MusicViewModel.Factory(requireActivity().application)
} }
private val playbackModel: PlaybackViewModel by activityViewModels()
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
@ -31,7 +33,7 @@ class SongsFragment : Fragment() {
adapter = SongAdapter( adapter = SongAdapter(
musicModel.songs.value!!, musicModel.songs.value!!,
ClickListener { song -> ClickListener { song ->
Log.d(this::class.simpleName, song.name) playbackModel.updateSong(song)
} }
) )
applyDivider() applyDivider()

View file

@ -87,7 +87,7 @@ fun resolveAttr(context: Context, @AttrRes attr: Int): Int {
} }
// Apply a color to a Menu Item // Apply a color to a Menu Item
fun MenuItem.applyColor(@ColorRes color: Int) { fun MenuItem.applyColor(@ColorInt color: Int) {
SpannableString(title).apply { SpannableString(title).apply {
setSpan(ForegroundColorSpan(color), 0, length, SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE) setSpan(ForegroundColorSpan(color), 0, length, SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE)
title = this title = this

View file

@ -0,0 +1,78 @@
<?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"
tools:context=".playback.CompactPlaybackFragment">
<data>
<variable
name="song"
type="org.oxycblt.auxio.music.Song" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:animateLayoutChanges="true"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ProgressBar
android:id="@+id/song_progress"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="@dimen/playback_progress_size"
android:clickable="false"
android:progressBackgroundTint="?android:attr/colorControlNormal"
android:progressTint="?android:attr/colorPrimary"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:progress="70" />
<ImageView
android:id="@+id/album_cover"
android:layout_width="@dimen/cover_size_compact"
android:layout_height="@dimen/cover_size_compact"
android:contentDescription="@{@string/description_album_cover(song.name)}"
android:layout_margin="@dimen/margin_smallish"
app:coverArt="@{song}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/song_progress"
tools:src="@drawable/ic_song" />
<TextView
android:id="@+id/song_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:fontFamily="@font/inter_semibold"
android:text="@{song.name}"
android:layout_marginStart="@dimen/margin_smallish"
android:textAppearance="@style/TextAppearance.SmallHeader"
android:singleLine="true"
android:ellipsize="marquee"
android:marqueeRepeatLimit="marquee_forever"
app:layout_constraintBottom_toTopOf="@+id/song_info"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/album_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_smallish"
android:ellipsize="marquee"
android:singleLine="true"
android:marqueeRepeatLimit="marquee_forever"
android:textAppearance="@style/TextAppearance.MaterialComponents.Caption"
android:text="@{@string/format_info(song.album.name, song.album.artist.name)}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/album_cover"
app:layout_constraintTop_toBottomOf="@+id/song_name"
tools:text="Artist / Album" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View file

@ -15,6 +15,13 @@
android:layout_height="0dp" android:layout_height="0dp"
android:layout_weight="1" /> android:layout_weight="1" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/playback_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:name="org.oxycblt.auxio.playback.CompactPlaybackFragment"
android:elevation="@dimen/elevation_normal" />
<com.google.android.material.tabs.TabLayout <com.google.android.material.tabs.TabLayout
android:id="@+id/main_tabs" android:id="@+id/main_tabs"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -29,7 +36,8 @@
app:tabIndicator="@drawable/indicator" app:tabIndicator="@drawable/indicator"
app:tabIndicatorColor="?android:attr/colorPrimary" app:tabIndicatorColor="?android:attr/colorPrimary"
app:tabMode="fixed" app:tabMode="fixed"
app:tabRippleColor="@color/selection_color" /> app:tabRippleColor="@color/selection_color"
tools:background="@color/control_color" />
</LinearLayout> </LinearLayout>
</layout> </layout>

View file

@ -12,10 +12,10 @@
<action <action
android:id="@+id/action_to_main" android:id="@+id/action_to_main"
app:destination="@id/main_fragment" app:destination="@id/main_fragment"
app:enterAnim="@anim/fragment_fade_enter" app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/fragment_fade_exit" app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/fragment_fade_enter" app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/fragment_fade_exit" app:popExitAnim="@anim/nav_default_pop_exit_anim"
app:popUpTo="@id/loading_fragment" app:popUpTo="@id/loading_fragment"
app:popUpToInclusive="true" app:popUpToInclusive="true"
app:launchSingleTop="true" /> app:launchSingleTop="true" />
@ -27,24 +27,24 @@
tools:layout="@layout/fragment_main"> tools:layout="@layout/fragment_main">
<action <action
android:id="@+id/action_show_artist" android:id="@+id/action_show_artist"
app:enterAnim="@anim/fragment_fade_enter" app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/fragment_fade_exit" app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/fragment_fade_enter" app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/fragment_fade_exit" app:popExitAnim="@anim/nav_default_pop_exit_anim"
app:destination="@id/artist_detail_fragment" /> app:destination="@id/artist_detail_fragment" />
<action <action
android:id="@+id/action_show_album" android:id="@+id/action_show_album"
app:enterAnim="@anim/fragment_fade_enter" app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/fragment_fade_exit" app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/fragment_fade_enter" app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/fragment_fade_exit" app:popExitAnim="@anim/nav_default_pop_exit_anim"
app:destination="@id/album_detail_fragment" /> app:destination="@id/album_detail_fragment" />
<action <action
android:id="@+id/action_show_genre" android:id="@+id/action_show_genre"
app:enterAnim="@anim/fragment_fade_enter" app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/fragment_fade_exit" app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/fragment_fade_enter" app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/fragment_fade_exit" app:popExitAnim="@anim/nav_default_pop_exit_anim"
app:destination="@id/genreDetailFragment" /> app:destination="@id/genreDetailFragment" />
<action <action
android:id="@+id/action_return_to_loading" android:id="@+id/action_return_to_loading"
@ -62,10 +62,10 @@
app:argType="long" /> app:argType="long" />
<action <action
android:id="@+id/action_show_album" android:id="@+id/action_show_album"
app:enterAnim="@anim/fragment_fade_enter" app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/fragment_fade_exit" app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/fragment_fade_enter" app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/fragment_fade_exit" app:popExitAnim="@anim/nav_default_pop_exit_anim"
app:destination="@id/album_detail_fragment" /> app:destination="@id/album_detail_fragment" />
</fragment> </fragment>
<fragment <fragment
@ -81,10 +81,10 @@
app:argType="boolean" /> app:argType="boolean" />
<action <action
android:id="@+id/action_show_parent_artist" android:id="@+id/action_show_parent_artist"
app:enterAnim="@anim/fragment_fade_enter" app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/fragment_fade_exit" app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/fragment_fade_enter" app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/fragment_fade_exit" app:popExitAnim="@anim/nav_default_pop_exit_anim"
app:destination="@id/artist_detail_fragment" /> app:destination="@id/artist_detail_fragment" />
</fragment> </fragment>
<fragment <fragment
@ -94,10 +94,10 @@
tools:layout="@layout/fragment_genre_detail"> tools:layout="@layout/fragment_genre_detail">
<action <action
android:id="@+id/action_show_artist" android:id="@+id/action_show_artist"
app:enterAnim="@anim/fragment_fade_enter" app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/fragment_fade_exit" app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/fragment_fade_enter" app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/fragment_fade_exit" app:popExitAnim="@anim/nav_default_pop_exit_anim"
app:destination="@id/artist_detail_fragment" /> app:destination="@id/artist_detail_fragment" />
<argument <argument
android:name="genreId" android:name="genreId"

View file

@ -5,12 +5,14 @@
<dimen name="padding_medium">16dp</dimen> <dimen name="padding_medium">16dp</dimen>
<dimen name="margin_small">8dp</dimen> <dimen name="margin_small">8dp</dimen>
<dimen name="margin_smallish">10dp</dimen>
<dimen name="margin_medium">16dp</dimen> <dimen name="margin_medium">16dp</dimen>
<dimen name="status_icon_size">48dp</dimen> <dimen name="status_icon_size">48dp</dimen>
<dimen name="tab_menu_size">40dp</dimen> <dimen name="tab_menu_size">40dp</dimen>
<dimen name="cover_size_small">36dp</dimen>
<dimen name="cover_size_compact">44dp</dimen> <dimen name="cover_size_compact">44dp</dimen>
<dimen name="cover_size_normal">56dp</dimen> <dimen name="cover_size_normal">56dp</dimen>
<dimen name="cover_size_large">68dp</dimen> <dimen name="cover_size_large">68dp</dimen>
@ -26,5 +28,7 @@
<dimen name="divider_ripple_size">18dp</dimen> <dimen name="divider_ripple_size">18dp</dimen>
<dimen name="playback_progress_size">2dp</dimen>
<dimen name="elevation_normal">4dp</dimen> <dimen name="elevation_normal">4dp</dimen>
</resources> </resources>

View file

@ -27,6 +27,10 @@
<item name="android:textSize">@dimen/detail_header_size_max</item> <item name="android:textSize">@dimen/detail_header_size_max</item>
</style> </style>
<style name="TextAppearance.SmallHeader" parent="TextAppearance.MaterialComponents.Body2">
<item name="android:fontFamily">@font/inter_semibold</item>
</style>
<style name="AppThemeOverlay.Popup" parent="ThemeOverlay.AppCompat.DayNight"> <style name="AppThemeOverlay.Popup" parent="ThemeOverlay.AppCompat.DayNight">
<item name="android:colorBackground">@color/background</item> <item name="android:colorBackground">@color/background</item>
<item name="colorControlHighlight">@color/selection_color</item> <item name="colorControlHighlight">@color/selection_color</item>