Add playing song nav to PlaybackFragment
Add the ability to nav to the playing song/playing artist/playing album from PlaybackFragment.
This commit is contained in:
parent
6ce8c854a9
commit
206d8d1c1f
16 changed files with 173 additions and 37 deletions
|
@ -83,14 +83,14 @@ dependencies {
|
||||||
// Material
|
// Material
|
||||||
implementation 'com.google.android.material:material:1.3.0-alpha03'
|
implementation 'com.google.android.material:material:1.3.0-alpha03'
|
||||||
|
|
||||||
// Lint
|
|
||||||
ktlint "com.pinterest:ktlint:0.37.2"
|
|
||||||
|
|
||||||
// ExoPlayer
|
// ExoPlayer
|
||||||
def exoplayer_version = "2.12.1"
|
def exoplayer_version = "2.12.1"
|
||||||
implementation "com.google.android.exoplayer:exoplayer-core:$exoplayer_version"
|
implementation "com.google.android.exoplayer:exoplayer-core:$exoplayer_version"
|
||||||
implementation "com.google.android.exoplayer:extension-mediasession:$exoplayer_version"
|
implementation "com.google.android.exoplayer:extension-mediasession:$exoplayer_version"
|
||||||
|
|
||||||
|
// Lint
|
||||||
|
ktlint "com.pinterest:ktlint:0.37.2"
|
||||||
|
|
||||||
// Memory Leak checking
|
// Memory Leak checking
|
||||||
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.4'
|
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.4'
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,9 +59,9 @@ class MainFragment : Fragment() {
|
||||||
)
|
)
|
||||||
|
|
||||||
val navController = (
|
val navController = (
|
||||||
childFragmentManager.findFragmentById(R.id.explore_nav_host)
|
childFragmentManager.findFragmentById(R.id.explore_nav_host)
|
||||||
as NavHostFragment?
|
as NavHostFragment?
|
||||||
)?.findNavController()
|
)?.findNavController()
|
||||||
|
|
||||||
// --- UI SETUP ---
|
// --- UI SETUP ---
|
||||||
|
|
||||||
|
@ -93,17 +93,30 @@ class MainFragment : Fragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
playbackModel.navToSong.observe(viewLifecycleOwner) {
|
playbackModel.navToPlayingSong.observe(viewLifecycleOwner) {
|
||||||
if (it) {
|
if (it) {
|
||||||
if (binding.navBar.selectedItemId != R.id.library_fragment ||
|
if (binding.navBar.selectedItemId != R.id.library_fragment ||
|
||||||
(
|
shouldGoToAlbum(navController!!)
|
||||||
navController!!.currentDestination?.id == R.id.album_detail_fragment &&
|
) {
|
||||||
detailModel.currentAlbum.value == null ||
|
binding.navBar.selectedItemId = R.id.library_fragment
|
||||||
detailModel.currentAlbum.value?.id
|
}
|
||||||
!= playbackModel.song.value!!.album.id
|
}
|
||||||
) ||
|
}
|
||||||
navController.currentDestination?.id == R.id.artist_detail_fragment ||
|
|
||||||
navController.currentDestination?.id == R.id.genre_detail_fragment
|
playbackModel.navToPlayingAlbum.observe(viewLifecycleOwner) {
|
||||||
|
if (it) {
|
||||||
|
if (binding.navBar.selectedItemId != R.id.library_fragment ||
|
||||||
|
shouldGoToAlbum(navController!!)
|
||||||
|
) {
|
||||||
|
binding.navBar.selectedItemId = R.id.library_fragment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
playbackModel.navToPlayingArtist.observe(viewLifecycleOwner) {
|
||||||
|
if (it) {
|
||||||
|
if (binding.navBar.selectedItemId != R.id.library_fragment ||
|
||||||
|
shouldGoToArtist(navController!!)
|
||||||
) {
|
) {
|
||||||
binding.navBar.selectedItemId = R.id.library_fragment
|
binding.navBar.selectedItemId = R.id.library_fragment
|
||||||
}
|
}
|
||||||
|
@ -117,6 +130,25 @@ class MainFragment : Fragment() {
|
||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// I have no idea what these things even do
|
||||||
|
private fun shouldGoToAlbum(controller: NavController): Boolean {
|
||||||
|
return (
|
||||||
|
controller.currentDestination!!.id == R.id.album_detail_fragment &&
|
||||||
|
detailModel.currentAlbum.value?.id != playbackModel.song.value!!.album.id
|
||||||
|
) ||
|
||||||
|
controller.currentDestination!!.id == R.id.artist_detail_fragment ||
|
||||||
|
controller.currentDestination!!.id == R.id.genre_detail_fragment
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun shouldGoToArtist(controller: NavController): Boolean {
|
||||||
|
return (
|
||||||
|
controller.currentDestination!!.id == R.id.artist_detail_fragment &&
|
||||||
|
detailModel.currentArtist.value?.id != playbackModel.song.value!!.album.artist.id
|
||||||
|
) ||
|
||||||
|
controller.currentDestination!!.id == R.id.album_detail_fragment ||
|
||||||
|
controller.currentDestination!!.id == R.id.genre_detail_fragment
|
||||||
|
}
|
||||||
|
|
||||||
private fun navigateWithItem(navController: NavController, item: MenuItem): Boolean {
|
private fun navigateWithItem(navController: NavController, item: MenuItem): Boolean {
|
||||||
if (navController.currentDestination!!.id != item.itemId) {
|
if (navController.currentDestination!!.id != item.itemId) {
|
||||||
// Create custom NavOptions myself so that animations work
|
// Create custom NavOptions myself so that animations work
|
||||||
|
|
|
@ -1,5 +1,17 @@
|
||||||
package org.oxycblt.auxio.database
|
package org.oxycblt.auxio.database
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A database entity that stores a compressed variant of the current playback state.
|
||||||
|
* @property id - The database key for this state
|
||||||
|
* @property songId - The song that is currently playing
|
||||||
|
* @property parentId - The parent that is being played from [-1 if none]
|
||||||
|
* @property index - The current index in the queue.
|
||||||
|
* @property mode - The integer form of the current [org.oxycblt.auxio.playback.state.PlaybackMode]
|
||||||
|
* @property isShuffling - A bool for if the queue was shuffled
|
||||||
|
* @property shuffleSeed - A long for the seed used to shuffle the queue [Used for quick-restore]
|
||||||
|
* @property loopMode - The integer form of the current [org.oxycblt.auxio.playback.state.LoopMode]
|
||||||
|
* @property inUserQueue - A bool for if the state was currently playing from the user queue.
|
||||||
|
*/
|
||||||
data class PlaybackState(
|
data class PlaybackState(
|
||||||
val id: Long = 0L,
|
val id: Long = 0L,
|
||||||
val songId: Long = -1L,
|
val songId: Long = -1L,
|
||||||
|
|
|
@ -188,6 +188,7 @@ class PlaybackStateDatabase(context: Context) :
|
||||||
|
|
||||||
var position = 0
|
var position = 0
|
||||||
|
|
||||||
|
// Try to write out the entirety of the queue, any failed inserts will be skipped.
|
||||||
while (position < queue.size) {
|
while (position < queue.size) {
|
||||||
database.beginTransaction()
|
database.beginTransaction()
|
||||||
var i = position
|
var i = position
|
||||||
|
|
|
@ -1,5 +1,12 @@
|
||||||
package org.oxycblt.auxio.database
|
package org.oxycblt.auxio.database
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A database entity that stores a simplified representation of a song in a queue.
|
||||||
|
* @property id The database entity's id
|
||||||
|
* @property songId The song id for this queue item
|
||||||
|
* @property albumId The album id for this queue item, used to make searching quicker
|
||||||
|
* @property isUserQueue A bool for if this queue item is a user queue item or not
|
||||||
|
*/
|
||||||
data class QueueItem(
|
data class QueueItem(
|
||||||
var id: Long = 0L,
|
var id: Long = 0L,
|
||||||
val songId: Long = Long.MIN_VALUE,
|
val songId: Long = Long.MIN_VALUE,
|
||||||
|
|
|
@ -122,18 +122,20 @@ class AlbumDetailFragment : DetailFragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
playbackModel.navToSong.observe(viewLifecycleOwner) {
|
playbackModel.navToPlayingSong.observe(viewLifecycleOwner) {
|
||||||
if (it) {
|
if (it) {
|
||||||
// Calculate where the item for the currently played song is, and navigate to there.
|
// Calculate where the item for the currently played song is, and navigate to there.
|
||||||
val pos = detailModel.currentAlbum.value!!.songs.indexOf(playbackModel.song.value)
|
val pos = detailModel.albumSortMode.value!!.getSortedSongList(
|
||||||
|
detailModel.currentAlbum.value!!.songs
|
||||||
|
).indexOf(playbackModel.song.value)
|
||||||
|
|
||||||
if (pos != -1) {
|
if (pos != -1) {
|
||||||
binding.albumSongRecycler.post {
|
binding.albumSongRecycler.post {
|
||||||
// Only scroll after UI creation
|
// Only scroll after UI creation
|
||||||
val y = binding.albumSongRecycler.y +
|
val y = binding.albumSongRecycler.y +
|
||||||
binding.albumSongRecycler.getChildAt(pos).y
|
binding.albumSongRecycler.getChildAt(pos).y
|
||||||
|
|
||||||
binding.nestedScroll.smoothScrollBy(0, y.toInt())
|
binding.nestedScroll.smoothScrollTo(0, y.toInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
playbackModel.doneWithNavToPlayingSong()
|
playbackModel.doneWithNavToPlayingSong()
|
||||||
|
@ -141,6 +143,12 @@ class AlbumDetailFragment : DetailFragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
playbackModel.navToPlayingAlbum.observe(viewLifecycleOwner) {
|
||||||
|
if (it) {
|
||||||
|
playbackModel.doneWithNavToPlayingAlbum()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Log.d(this::class.simpleName, "Fragment created.")
|
Log.d(this::class.simpleName, "Fragment created.")
|
||||||
|
|
||||||
return binding.root
|
return binding.root
|
||||||
|
|
|
@ -108,6 +108,12 @@ class ArtistDetailFragment : DetailFragment() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
playbackModel.navToPlayingArtist.observe(viewLifecycleOwner) {
|
||||||
|
if (it) {
|
||||||
|
playbackModel.doneWithNavToPlayingArtist()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Log.d(this::class.simpleName, "Fragment created.")
|
Log.d(this::class.simpleName, "Fragment created.")
|
||||||
|
|
||||||
return binding.root
|
return binding.root
|
||||||
|
|
|
@ -151,7 +151,7 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
playbackModel.navToSong.observe(viewLifecycleOwner) {
|
playbackModel.navToPlayingSong.observe(viewLifecycleOwner) {
|
||||||
if (it) {
|
if (it) {
|
||||||
libraryModel.updateNavigationStatus(false)
|
libraryModel.updateNavigationStatus(false)
|
||||||
|
|
||||||
|
@ -159,6 +159,22 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
playbackModel.navToPlayingAlbum.observe(viewLifecycleOwner) {
|
||||||
|
if (it) {
|
||||||
|
libraryModel.updateNavigationStatus(false)
|
||||||
|
|
||||||
|
navToItem(playbackModel.song.value!!.album)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
playbackModel.navToPlayingArtist.observe(viewLifecycleOwner) {
|
||||||
|
if (it) {
|
||||||
|
libraryModel.updateNavigationStatus(false)
|
||||||
|
|
||||||
|
navToItem(playbackModel.song.value!!.album.artist)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Log.d(this::class.simpleName, "Fragment created.")
|
Log.d(this::class.simpleName, "Fragment created.")
|
||||||
|
|
||||||
return binding.root
|
return binding.root
|
||||||
|
|
|
@ -69,6 +69,9 @@ data class Album(
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The data object for an artist. Inherits [BaseModel]
|
* The data object for an artist. Inherits [BaseModel]
|
||||||
|
* @property albums The list of all [Album]s in this artist
|
||||||
|
* @property genres The list of all parent [Genre]s in this artist, sorted by relevance
|
||||||
|
* @property songs The list of all [Song]s in this artist
|
||||||
* @author OxygenCobalt
|
* @author OxygenCobalt
|
||||||
*/
|
*/
|
||||||
data class Artist(
|
data class Artist(
|
||||||
|
@ -89,7 +92,8 @@ data class Artist(
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The data object for a genre. Inherits [BaseModel]
|
* The data object for a genre. Inherits [BaseModel]
|
||||||
* @property artists The list of all [Artist]s in this genre
|
* @property artists The list of all [Artist]s in this genre.
|
||||||
|
* @property albums The list of all [Album]s in this genre.
|
||||||
* @property songs The list of all [Song]s in this genre.
|
* @property songs The list of all [Song]s in this genre.
|
||||||
* @author OxygenCobalt
|
* @author OxygenCobalt
|
||||||
*/
|
*/
|
||||||
|
@ -117,6 +121,8 @@ data class Genre(
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A data object used solely for the "Header" UI element. Inherits [BaseModel].
|
* A data object used solely for the "Header" UI element. Inherits [BaseModel].
|
||||||
|
* @property isAction Value that marks whether this header should have an action attached to it.
|
||||||
|
* @author OxygenCobalt
|
||||||
*/
|
*/
|
||||||
data class Header(
|
data class Header(
|
||||||
override val id: Long = -1,
|
override val id: Long = -1,
|
||||||
|
|
|
@ -9,7 +9,7 @@ import org.oxycblt.auxio.music.processing.MusicSorter
|
||||||
import org.oxycblt.auxio.recycler.ShowMode
|
import org.oxycblt.auxio.recycler.ShowMode
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main storage for music items. Use [MusicStore.from()] to get the instance.
|
* The main storage for music items. Use [MusicStore.getInstance] to get the instance.
|
||||||
*/
|
*/
|
||||||
class MusicStore private constructor() {
|
class MusicStore private constructor() {
|
||||||
private var mGenres = listOf<Genre>()
|
private var mGenres = listOf<Genre>()
|
||||||
|
|
|
@ -11,6 +11,7 @@ import androidx.core.content.ContextCompat
|
||||||
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 androidx.navigation.fragment.findNavController
|
||||||
|
import org.oxycblt.auxio.BuildConfig
|
||||||
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.FragmentCompactPlaybackBinding
|
import org.oxycblt.auxio.databinding.FragmentCompactPlaybackBinding
|
||||||
|
@ -54,10 +55,14 @@ class CompactPlaybackFragment : Fragment() {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.playbackControls.setOnLongClickListener {
|
// Enable the ability to force-save the state in debug builds, in order to check
|
||||||
playbackModel.save(requireContext())
|
// for persistence issues without waiting for PlaybackService to be killed.
|
||||||
getString(R.string.debug_state_saved).createToast(requireContext())
|
if (BuildConfig.DEBUG) {
|
||||||
true
|
binding.playbackControls.setOnLongClickListener {
|
||||||
|
playbackModel.save(requireContext())
|
||||||
|
getString(R.string.debug_state_saved).createToast(requireContext())
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- VIEWMODEL SETUP ---
|
// --- VIEWMODEL SETUP ---
|
||||||
|
|
|
@ -187,6 +187,24 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
playbackModel.navToPlayingSong.observe(viewLifecycleOwner) {
|
||||||
|
if (it) {
|
||||||
|
findNavController().navigateUp()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
playbackModel.navToPlayingAlbum.observe(viewLifecycleOwner) {
|
||||||
|
if (it) {
|
||||||
|
findNavController().navigateUp()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
playbackModel.navToPlayingArtist.observe(viewLifecycleOwner) {
|
||||||
|
if (it) {
|
||||||
|
findNavController().navigateUp()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Log.d(this::class.simpleName, "Fragment Created.")
|
Log.d(this::class.simpleName, "Fragment Created.")
|
||||||
|
|
||||||
return binding.root
|
return binding.root
|
||||||
|
|
|
@ -62,8 +62,14 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
|
||||||
private val mIsSeeking = MutableLiveData(false)
|
private val mIsSeeking = MutableLiveData(false)
|
||||||
val isSeeking: LiveData<Boolean> get() = mIsSeeking
|
val isSeeking: LiveData<Boolean> get() = mIsSeeking
|
||||||
|
|
||||||
private val mNavToSong = MutableLiveData(false)
|
private val mNavToPlayingSong = MutableLiveData(false)
|
||||||
val navToSong: LiveData<Boolean> get() = mNavToSong
|
val navToPlayingSong: LiveData<Boolean> get() = mNavToPlayingSong
|
||||||
|
|
||||||
|
private val mNavToPlayingAlbum = MutableLiveData(false)
|
||||||
|
val navToPlayingAlbum: LiveData<Boolean> get() = mNavToPlayingAlbum
|
||||||
|
|
||||||
|
private val mNavToPlayingArtist = MutableLiveData(false)
|
||||||
|
val navToPlayingArtist: LiveData<Boolean> get() = mNavToPlayingArtist
|
||||||
|
|
||||||
private var mCanAnimate = false
|
private var mCanAnimate = false
|
||||||
val canAnimate: Boolean get() = mCanAnimate
|
val canAnimate: Boolean get() = mCanAnimate
|
||||||
|
@ -283,11 +289,27 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun navToPlayingSong() {
|
fun navToPlayingSong() {
|
||||||
mNavToSong.value = true
|
mNavToPlayingSong.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun doneWithNavToPlayingSong() {
|
fun doneWithNavToPlayingSong() {
|
||||||
mNavToSong.value = false
|
mNavToPlayingSong.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun navToPlayingAlbum() {
|
||||||
|
mNavToPlayingAlbum.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun doneWithNavToPlayingAlbum() {
|
||||||
|
mNavToPlayingAlbum.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun navToPlayingArtist() {
|
||||||
|
mNavToPlayingArtist.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun doneWithNavToPlayingArtist() {
|
||||||
|
mNavToPlayingArtist.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun enableAnimation() {
|
fun enableAnimation() {
|
||||||
|
|
|
@ -22,7 +22,7 @@ import kotlin.random.Random
|
||||||
* - If you want to use the playback state with the ExoPlayer instance or system-side things,
|
* - If you want to use the playback state with the ExoPlayer instance or system-side things,
|
||||||
* use [org.oxycblt.auxio.playback.PlaybackService].
|
* use [org.oxycblt.auxio.playback.PlaybackService].
|
||||||
*
|
*
|
||||||
* All instantiation should be done with [PlaybackStateManager.from()].
|
* All instantiation should be done with [PlaybackStateManager.getInstance].
|
||||||
* @author OxygenCobalt
|
* @author OxygenCobalt
|
||||||
*/
|
*/
|
||||||
class PlaybackStateManager private constructor() {
|
class PlaybackStateManager private constructor() {
|
||||||
|
@ -560,12 +560,12 @@ class PlaybackStateManager private constructor() {
|
||||||
// Traverse albums and then album songs instead of just the songs, as its faster.
|
// Traverse albums and then album songs instead of just the songs, as its faster.
|
||||||
musicStore.albums.find { it.id == item.albumId }
|
musicStore.albums.find { it.id == item.albumId }
|
||||||
?.songs?.find { it.id == item.songId }?.let {
|
?.songs?.find { it.id == item.songId }?.let {
|
||||||
if (item.isUserQueue) {
|
if (item.isUserQueue) {
|
||||||
mUserQueue.add(it)
|
mUserQueue.add(it)
|
||||||
} else {
|
} else {
|
||||||
mQueue.add(it)
|
mQueue.add(it)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a more accurate index [At least if were not in the user queue]
|
// Get a more accurate index [At least if were not in the user queue]
|
||||||
|
|
|
@ -9,7 +9,7 @@ https://stackoverflow.com/a/61157571/14143986
|
||||||
android:top="-2dp">
|
android:top="-2dp">
|
||||||
<shape android:shape="rectangle">
|
<shape android:shape="rectangle">
|
||||||
<stroke
|
<stroke
|
||||||
android:width="1dp"
|
android:width="0.5dp"
|
||||||
android:color="@color/divider_color" />
|
android:color="@color/divider_color" />
|
||||||
</shape>
|
</shape>
|
||||||
</item>
|
</item>
|
||||||
|
|
|
@ -67,6 +67,7 @@
|
||||||
android:layout_marginStart="@dimen/margin_mid_large"
|
android:layout_marginStart="@dimen/margin_mid_large"
|
||||||
android:layout_marginEnd="@dimen/margin_mid_large"
|
android:layout_marginEnd="@dimen/margin_mid_large"
|
||||||
android:text="@{song.name}"
|
android:text="@{song.name}"
|
||||||
|
android:onClick="@{() -> playbackModel.navToPlayingSong()}"
|
||||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/playback_artist"
|
app:layout_constraintBottom_toTopOf="@+id/playback_artist"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
@ -83,6 +84,7 @@
|
||||||
android:layout_marginStart="@dimen/margin_mid_large"
|
android:layout_marginStart="@dimen/margin_mid_large"
|
||||||
android:layout_marginEnd="@dimen/margin_mid_large"
|
android:layout_marginEnd="@dimen/margin_mid_large"
|
||||||
android:text="@{song.album.artist.name}"
|
android:text="@{song.album.artist.name}"
|
||||||
|
android:onClick="@{() -> playbackModel.navToPlayingArtist()}"
|
||||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/playback_album"
|
app:layout_constraintBottom_toTopOf="@+id/playback_album"
|
||||||
|
@ -100,6 +102,7 @@
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:text="@{song.album.name}"
|
android:text="@{song.album.name}"
|
||||||
|
android:onClick="@{() -> playbackModel.navToPlayingAlbum()}"
|
||||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/playback_seek_bar"
|
app:layout_constraintBottom_toTopOf="@+id/playback_seek_bar"
|
||||||
|
|
Loading…
Reference in a new issue