diff --git a/app/src/main/kotlin/com/subtracks/ui/library/AlbumsContent.kt b/app/src/main/kotlin/com/subtracks/ui/library/AlbumsContent.kt index 0b5f4c07..84a284f0 100644 --- a/app/src/main/kotlin/com/subtracks/ui/library/AlbumsContent.kt +++ b/app/src/main/kotlin/com/subtracks/ui/library/AlbumsContent.kt @@ -43,6 +43,7 @@ fun AlbumsContent( filtered: Boolean = false, onClearFilters: () -> Unit = {}, resetKey: Any? = null, + topInset: Dp = 0.dp, modifier: Modifier = Modifier, ) { when { @@ -65,7 +66,7 @@ fun AlbumsContent( LazyVerticalGrid( state = gridState, columns = GridCells.Fixed(3), - contentPadding = PaddingValues(start = 8.dp, top = 8.dp, end = 8.dp, bottom = bottomInset + 8.dp), + contentPadding = PaddingValues(start = 8.dp, top = topInset + 8.dp, end = 8.dp, bottom = bottomInset + 8.dp), horizontalArrangement = Arrangement.spacedBy(4.dp), verticalArrangement = Arrangement.spacedBy(4.dp), modifier = modifier.fillMaxSize(), diff --git a/app/src/main/kotlin/com/subtracks/ui/library/ArtistsContent.kt b/app/src/main/kotlin/com/subtracks/ui/library/ArtistsContent.kt index 0b906e49..c2f17400 100644 --- a/app/src/main/kotlin/com/subtracks/ui/library/ArtistsContent.kt +++ b/app/src/main/kotlin/com/subtracks/ui/library/ArtistsContent.kt @@ -41,6 +41,7 @@ fun ArtistsContent( filtered: Boolean = false, onClearFilters: () -> Unit = {}, resetKey: Any? = null, + topInset: Dp = 0.dp, modifier: Modifier = Modifier, ) { when { @@ -63,7 +64,7 @@ fun ArtistsContent( LazyColumn( state = listState, modifier = modifier.fillMaxSize(), - contentPadding = PaddingValues(bottom = bottomInset), + contentPadding = PaddingValues(top = topInset, bottom = bottomInset), ) { items(count = items.itemCount, key = items.itemKey { it.id }) { index -> val artist = items[index] diff --git a/app/src/main/kotlin/com/subtracks/ui/library/LibraryScreen.kt b/app/src/main/kotlin/com/subtracks/ui/library/LibraryScreen.kt index da2643f4..80ef25ac 100644 --- a/app/src/main/kotlin/com/subtracks/ui/library/LibraryScreen.kt +++ b/app/src/main/kotlin/com/subtracks/ui/library/LibraryScreen.kt @@ -91,6 +91,7 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.layout.boundsInParent import androidx.compose.ui.layout.layout import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.unit.Constraints @@ -312,87 +313,96 @@ fun LibraryScreen( val filtersActive = listQuery.starred != StarredFilter.Any || search.isNotEmpty() val listBottomInset = bottomInset + if (searchActive) SEARCH_BAR_CLEARANCE else FAB_CLEARANCE - Box(modifier.fillMaxSize()) { + var headerHeightPx by remember { mutableFloatStateOf(0f) } + val listTopInset = with(density) { headerHeightPx.toDp() } + + Box( + modifier = + modifier + .fillMaxSize() + .background(artwork?.let(::librarySurfaceColor) ?: MaterialTheme.colorScheme.background), + ) { + HorizontalPager( + state = pagerState, + modifier = Modifier.fillMaxSize(), + ) { page -> + val pageTab = LibraryTab.entries[page] + val resetKey = resetKeys[pageTab] + when (pageTab) { + LibraryTab.Albums -> { + AlbumsContent( + albums, + coverArt, + listBottomInset, + onAlbumClick, + filtered = filtersActive, + onClearFilters = onClearFilters, + resetKey = resetKey, + topInset = listTopInset, + ) + } + + LibraryTab.Artists -> { + ArtistsContent( + artists, + coverArt, + listBottomInset, + onArtistClick, + filtered = filtersActive, + onClearFilters = onClearFilters, + resetKey = resetKey, + topInset = listTopInset, + ) + } + + LibraryTab.Songs -> { + SongsContent( + songs, + coverArt, + listBottomInset, + onSongClick, + playingSongId, + filtered = filtersActive, + onClearFilters = onClearFilters, + resetKey = resetKey, + topInset = listTopInset, + ) + } + + LibraryTab.Playlists -> { + PlaylistsContent( + playlists, + coverArt, + listBottomInset, + onPlaylistClick, + filtered = filtersActive, + onClearFilters = onClearFilters, + resetKey = resetKey, + topInset = listTopInset, + ) + } + } + } + Column( modifier = Modifier - .fillMaxSize() - .background(artwork?.let(::librarySurfaceColor) ?: MaterialTheme.colorScheme.background), + .fillMaxWidth() + .align(Alignment.TopStart) + .onSizeChanged { headerHeightPx = it.height.toFloat() } + .background( + (artwork?.let(::playerSurfaceColor) ?: MaterialTheme.colorScheme.background) + .copy(alpha = HEADER_ALPHA), + ).padding(top = statusBarTop + 2.dp, bottom = 2.dp), ) { - Column( - modifier = - Modifier - .fillMaxWidth() - .background(artwork?.let(::playerSurfaceColor) ?: MaterialTheme.colorScheme.background) - .padding(top = statusBarTop + 2.dp, bottom = 2.dp), - ) { - LibraryTabs( - pagerState = pagerState, - onTabSelected = onTabSelected, - syncing = syncing, - onSync = onSync, - onOpenSettings = onOpenSettings, - artwork = artwork, - ) - } - - HorizontalPager( - state = pagerState, - modifier = Modifier.fillMaxWidth().weight(1f), - ) { page -> - val pageTab = LibraryTab.entries[page] - val resetKey = resetKeys[pageTab] - when (pageTab) { - LibraryTab.Albums -> { - AlbumsContent( - albums, - coverArt, - listBottomInset, - onAlbumClick, - filtered = filtersActive, - onClearFilters = onClearFilters, - resetKey = resetKey, - ) - } - - LibraryTab.Artists -> { - ArtistsContent( - artists, - coverArt, - listBottomInset, - onArtistClick, - filtered = filtersActive, - onClearFilters = onClearFilters, - resetKey = resetKey, - ) - } - - LibraryTab.Songs -> { - SongsContent( - songs, - coverArt, - listBottomInset, - onSongClick, - playingSongId, - filtered = filtersActive, - onClearFilters = onClearFilters, - resetKey = resetKey, - ) - } - - LibraryTab.Playlists -> { - PlaylistsContent( - playlists, - coverArt, - listBottomInset, - onPlaylistClick, - filtered = filtersActive, - onClearFilters = onClearFilters, - resetKey = resetKey, - ) - } - } - } + LibraryTabs( + pagerState = pagerState, + onTabSelected = onTabSelected, + syncing = syncing, + onSync = onSync, + onOpenSettings = onOpenSettings, + artwork = artwork, + ) } if (searchActive) { @@ -582,6 +592,7 @@ private fun ListOptionsSheet( } private val FAB_CLEARANCE = 80.dp +private const val HEADER_ALPHA = 0.72f private val SEARCH_BAR_CLEARANCE = 80.dp @Composable diff --git a/app/src/main/kotlin/com/subtracks/ui/library/PlaylistsContent.kt b/app/src/main/kotlin/com/subtracks/ui/library/PlaylistsContent.kt index 6f24169e..45753303 100644 --- a/app/src/main/kotlin/com/subtracks/ui/library/PlaylistsContent.kt +++ b/app/src/main/kotlin/com/subtracks/ui/library/PlaylistsContent.kt @@ -40,6 +40,7 @@ fun PlaylistsContent( filtered: Boolean = false, onClearFilters: () -> Unit = {}, resetKey: Any? = null, + topInset: Dp = 0.dp, modifier: Modifier = Modifier, ) { when { @@ -62,7 +63,7 @@ fun PlaylistsContent( LazyColumn( state = listState, modifier = modifier.fillMaxSize(), - contentPadding = PaddingValues(bottom = bottomInset), + contentPadding = PaddingValues(top = topInset, bottom = bottomInset), ) { items(count = items.itemCount, key = items.itemKey { it.id }) { index -> val playlist = items[index] diff --git a/app/src/main/kotlin/com/subtracks/ui/library/SongsContent.kt b/app/src/main/kotlin/com/subtracks/ui/library/SongsContent.kt index 377b4d60..b0b862da 100644 --- a/app/src/main/kotlin/com/subtracks/ui/library/SongsContent.kt +++ b/app/src/main/kotlin/com/subtracks/ui/library/SongsContent.kt @@ -52,6 +52,7 @@ fun SongsContent( filtered: Boolean = false, onClearFilters: () -> Unit = {}, resetKey: Any? = null, + topInset: Dp = 0.dp, modifier: Modifier = Modifier, ) { when { @@ -74,7 +75,7 @@ fun SongsContent( LazyColumn( state = listState, modifier = modifier.fillMaxSize(), - contentPadding = PaddingValues(bottom = bottomInset), + contentPadding = PaddingValues(top = topInset, bottom = bottomInset), ) { items(count = items.itemCount, key = items.itemKey { it.song.id }) { index -> val item = items[index] diff --git a/app/src/test/kotlin/com/subtracks/ui/AppScreenshotTest.kt b/app/src/test/kotlin/com/subtracks/ui/AppScreenshotTest.kt index b2c14c92..79f2b869 100644 --- a/app/src/test/kotlin/com/subtracks/ui/AppScreenshotTest.kt +++ b/app/src/test/kotlin/com/subtracks/ui/AppScreenshotTest.kt @@ -96,6 +96,35 @@ class AppScreenshotTest { composeRule.onRoot().captureRoboImage("src/test/screenshots/library_albums.png") } + @Test + fun libraryScrolledUnderHeader() { + val albums = (1..60).map { Fixtures.albums.first().copy(id = "scrolled-$it", name = "Album $it") } + composeRule.setContent { + SubtracksTheme { + LibraryScreen( + selectedTab = LibraryTab.Albums, + onTabSelected = {}, + albums = remember { flowOf(PagingData.from(albums)) }.collectAsLazyPagingItems(), + artists = remember { flowOf(PagingData.empty()) }.collectAsLazyPagingItems(), + songs = remember { flowOf(PagingData.empty()) }.collectAsLazyPagingItems(), + playlists = remember { flowOf(PagingData.empty()) }.collectAsLazyPagingItems(), + coverArt = { id, _ -> id?.let { CoverArtRef(it, "test:$it") } }, + onAlbumClick = {}, + onArtistClick = {}, + onPlaylistClick = {}, + onSongClick = {}, + onSync = {}, + onOpenSettings = {}, + artwork = artworkColorsFromSeed(Color.rgb(120, 80, 200)), + ) + } + } + awaitTag(ALBUM_COVER_TAG) + composeRule.onRoot().performTouchInput { swipeUp(startY = centerY + 600f, endY = centerY - 600f, durationMillis = 400) } + composeRule.waitForIdle() + composeRule.onRoot().captureRoboImage("src/test/screenshots/library_scrolled_under_header.png") + } + @Test fun libraryArtists() { setLibraryContent(LibraryTab.Artists)