Skip to content

Commit 89edd0b

Browse files
authored
Merge pull request #1616 from theovilardo/impl/tap-on-albumart-to-open-screen
Impl/tap on albumart to open screen
2 parents a69b122 + f8b18ca commit 89edd0b

6 files changed

Lines changed: 95 additions & 0 deletions

File tree

app/src/main/java/com/theveloper/pixelplay/presentation/components/AlbumCarouselSelection.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package com.theveloper.pixelplay.presentation.components
33
import androidx.compose.animation.core.FastOutSlowInEasing
44
import androidx.compose.animation.core.tween
55
import androidx.compose.runtime.*
6+
import androidx.compose.foundation.clickable
7+
import androidx.compose.foundation.interaction.MutableInteractionSource
68
import androidx.compose.foundation.layout.*
79
import androidx.compose.ui.Modifier
810
import androidx.compose.ui.unit.*
@@ -39,6 +41,7 @@ fun AlbumCarouselSection(
3941
expansionFraction: Float,
4042
requestedScrollIndex: Int? = null,
4143
onSongSelected: (Song) -> Unit,
44+
onAlbumClick: (Song) -> Unit = {},
4245
modifier: Modifier = Modifier,
4346
carouselStyle: String = CarouselStyle.NO_PEEK,
4447
itemSpacing: Dp = 8.dp,
@@ -141,11 +144,18 @@ fun AlbumCarouselSection(
141144
carouselWidth = availableWidth // Pass the full width for layout calculations
142145
) { index ->
143146
val song = queue[index]
147+
val isFocusedItem = carouselState.pagerState.currentPage == index
144148
key(song.id) {
145149
Box(
146150
Modifier
147151
.fillMaxSize()
148152
.aspectRatio(1f)
153+
.clickable(
154+
enabled = isFocusedItem && song.albumId > 0L,
155+
interactionSource = remember { MutableInteractionSource() },
156+
indication = null,
157+
onClick = { onAlbumClick(song) }
158+
)
149159
) { // Enforce 1:1 aspect ratio for the item itself
150160
OptimizedAlbumArt(
151161
uri = song.albumArtUriString,

app/src/main/java/com/theveloper/pixelplay/presentation/components/UnifiedPlayerSheet.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ import androidx.navigation.compose.currentBackStackEntryAsState
9696
import coil.size.Size
9797
import com.theveloper.pixelplay.R
9898
import com.theveloper.pixelplay.data.model.Song
99+
import com.theveloper.pixelplay.presentation.components.scoped.PlayerAlbumNavigationEffect
99100
import com.theveloper.pixelplay.presentation.components.scoped.PlayerArtistNavigationEffect
100101
import com.theveloper.pixelplay.presentation.components.scoped.PlayerSheetPredictiveBackHandler
101102
import com.theveloper.pixelplay.presentation.components.scoped.QueueSheetRuntimeEffects
@@ -269,6 +270,12 @@ fun UnifiedPlayerSheet(
269270
sheetMotionController = sheetMotionController,
270271
playerViewModel = playerViewModel
271272
)
273+
PlayerAlbumNavigationEffect(
274+
navController = navController,
275+
sheetCollapsedTargetY = sheetCollapsedTargetY,
276+
sheetMotionController = sheetMotionController,
277+
playerViewModel = playerViewModel
278+
)
272279

273280
val fullPlayerVisualState = rememberFullPlayerVisualState(
274281
expansionFraction = playerContentExpansionFraction,

app/src/main/java/com/theveloper/pixelplay/presentation/components/UnifiedPlayerSheetV2.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ import androidx.media3.common.util.UnstableApi
5252
import androidx.navigation.NavHostController
5353
import androidx.navigation.compose.currentBackStackEntryAsState
5454
import com.theveloper.pixelplay.data.model.Song
55+
import com.theveloper.pixelplay.presentation.components.scoped.PlayerAlbumNavigationEffect
5556
import com.theveloper.pixelplay.presentation.components.scoped.PlayerArtistNavigationEffect
5657
import com.theveloper.pixelplay.presentation.components.scoped.PlayerSheetPredictiveBackHandler
5758
import com.theveloper.pixelplay.presentation.components.scoped.QueueSheetRuntimeEffects
@@ -232,6 +233,12 @@ fun UnifiedPlayerSheetV2(
232233
sheetMotionController = sheetMotionController,
233234
playerViewModel = playerViewModel
234235
)
236+
PlayerAlbumNavigationEffect(
237+
navController = navController,
238+
sheetCollapsedTargetY = sheetCollapsedTargetY,
239+
sheetMotionController = sheetMotionController,
240+
playerViewModel = playerViewModel
241+
)
235242

236243
// FullPlayerVisualState now holds lazy getters that read from the Animatable
237244
// inside graphicsLayer (draw-phase), avoiding per-frame recomposition.

app/src/main/java/com/theveloper/pixelplay/presentation/components/player/FullPlayerContent.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,9 @@ fun FullPlayerContent(
495495
albumArtQuality = albumArtQuality,
496496
requestedScrollIndex = pendingCarouselIndex,
497497
onSongSelected = onAlbumSongSelected,
498+
onAlbumClick = { albumSong ->
499+
playerViewModel.triggerAlbumNavigationFromPlayer(albumSong.albumId)
500+
},
498501
modifier = modifier
499502
)
500503
}
@@ -994,6 +997,7 @@ private fun FullPlayerAlbumCoverSection(
994997
albumArtQuality: AlbumArtQuality,
995998
requestedScrollIndex: Int?,
996999
onSongSelected: (Song) -> Unit,
1000+
onAlbumClick: (Song) -> Unit,
9971001
modifier: Modifier = Modifier
9981002
) {
9991003
val shouldDelay = loadingTweaks.delayAll || loadingTweaks.delayAlbumCarousel
@@ -1065,6 +1069,7 @@ private fun FullPlayerAlbumCoverSection(
10651069
onSongSelected(newSong)
10661070
}
10671071
},
1072+
onAlbumClick = onAlbumClick,
10681073
carouselStyle = carouselStyle,
10691074
modifier = Modifier
10701075
.height(carouselHeight)
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.theveloper.pixelplay.presentation.components.scoped
2+
3+
import androidx.compose.runtime.Composable
4+
import androidx.compose.runtime.LaunchedEffect
5+
import androidx.navigation.NavHostController
6+
import com.theveloper.pixelplay.presentation.navigation.Screen
7+
import com.theveloper.pixelplay.presentation.navigation.navigateSafely
8+
import com.theveloper.pixelplay.presentation.viewmodel.PlayerViewModel
9+
import kotlinx.coroutines.flow.collectLatest
10+
11+
@Composable
12+
internal fun PlayerAlbumNavigationEffect(
13+
navController: NavHostController,
14+
sheetCollapsedTargetY: Float,
15+
sheetMotionController: SheetMotionController,
16+
playerViewModel: PlayerViewModel
17+
) {
18+
LaunchedEffect(navController, sheetCollapsedTargetY) {
19+
playerViewModel.albumNavigationRequests.collectLatest { albumId ->
20+
sheetMotionController.snapCollapsed(sheetCollapsedTargetY)
21+
playerViewModel.collapsePlayerSheet()
22+
23+
navController.navigateSafely(Screen.AlbumDetail.createRoute(albumId)) {
24+
launchSingleTop = false
25+
navController.currentBackStackEntry?.destination?.route?.let { currentRoute ->
26+
if (currentRoute == Screen.AlbumDetail.route) {
27+
popUpTo(Screen.AlbumDetail.route) { inclusive = true }
28+
}
29+
}
30+
}
31+
}
32+
}
33+
}

app/src/main/java/com/theveloper/pixelplay/presentation/viewmodel/PlayerViewModel.kt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,8 @@ class PlayerViewModel @Inject constructor(
529529
)
530530
val toastEvents = _toastEvents.asSharedFlow()
531531

532+
private val _albumNavigationRequests = MutableSharedFlow<Long>(extraBufferCapacity = 1)
533+
val albumNavigationRequests = _albumNavigationRequests.asSharedFlow()
532534
private val _artistNavigationRequests = MutableSharedFlow<Long>(extraBufferCapacity = 1)
533535
val artistNavigationRequests = _artistNavigationRequests.asSharedFlow()
534536
private val _searchNavDoubleTapEvents = MutableSharedFlow<Unit>(extraBufferCapacity = 1)
@@ -538,6 +540,7 @@ class PlayerViewModel @Inject constructor(
538540
private val _scrollToIndexEvent = MutableSharedFlow<Int>(extraBufferCapacity = 1)
539541
val scrollToIndexEvent = _scrollToIndexEvent.asSharedFlow()
540542

543+
private var albumNavigationJob: Job? = null
541544
private var artistNavigationJob: Job? = null
542545
private var fullQueuePlaybackJob: Job? = null
543546
private var fullQueuePlaybackToken: Long = 0L
@@ -2040,6 +2043,36 @@ class PlayerViewModel @Inject constructor(
20402043
}
20412044
}
20422045

2046+
fun triggerAlbumNavigationFromPlayer(albumId: Long) {
2047+
if (albumId <= 0) {
2048+
Log.d("AlbumDebug", "triggerAlbumNavigationFromPlayer ignored invalid albumId=$albumId")
2049+
return
2050+
}
2051+
2052+
val existingJob = albumNavigationJob
2053+
if (existingJob != null && existingJob.isActive) {
2054+
Log.d("AlbumDebug", "triggerAlbumNavigationFromPlayer ignored; navigation already in progress for albumId=$albumId")
2055+
return
2056+
}
2057+
2058+
albumNavigationJob?.cancel()
2059+
albumNavigationJob = viewModelScope.launch {
2060+
val currentSong = playbackStateHolder.stablePlayerState.value.currentSong
2061+
Log.d(
2062+
"AlbumDebug",
2063+
"triggerAlbumNavigationFromPlayer: albumId=$albumId, songId=${currentSong?.id}, title=${currentSong?.title}"
2064+
)
2065+
collapsePlayerSheet()
2066+
2067+
withTimeoutOrNull(900) {
2068+
awaitSheetState(PlayerSheetState.COLLAPSED)
2069+
awaitPlayerCollapse()
2070+
}
2071+
2072+
_albumNavigationRequests.emit(albumId)
2073+
}
2074+
}
2075+
20432076
fun triggerArtistNavigationFromPlayer(artistId: Long) {
20442077
if (artistId <= 0) {
20452078
Log.d("ArtistDebug", "triggerArtistNavigationFromPlayer ignored invalid artistId=$artistId")

0 commit comments

Comments
 (0)