From a1b3ac398b62dad9e4df5e99ea8d116d44245ee2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Font=C3=A1n?= Date: Mon, 2 Dec 2024 00:02:47 +0100 Subject: [PATCH] feat: Replaced song cards with compact song cards feat: Added Settings This commit introduces compact cards to replace the previous vertical cards for displaying songs. The size of the cards can be configured in the Settings. Additionally, this change adds conditional marquee text, to give the user the option to disable the marque text animation. --- .../cards/songs/HorizontalSongCard.kt | 5 +- .../cards/songs/VerticalSongCard.kt | 5 +- .../cards/songs/compact/CompactCardSize.kt | 27 ++ .../cards/songs/compact/CompactSongCard.kt | 118 +++++++++ .../spotify/SpotifyHorizontalSongCard.kt | 5 +- .../components/text/ConditionedMarqueeText.kt | 93 +++++++ .../presentation/pages/MediaStorePage.kt | 29 ++- .../presentation/pages/home/HomePage.kt | 239 +++++++++++------- .../presentation/pages/home/LayoutType.kt | 15 ++ .../mediaplayer/player/PlayerControls.kt | 3 +- .../player/views/MiniplayerContent.kt | 5 +- .../settings/modules/GeneralSettingsPage.kt | 2 +- .../utilities/tageditor/MetadataEditorPage.kt | 2 +- .../spotify/stages/SpMetadataBsDetails.kt | 3 +- app/src/main/res/values/strings.xml | 1 + .../utilities/preferences/Preferences.kt | 4 +- .../utilities/preferences/PreferencesKeys.kt | 4 +- 17 files changed, 443 insertions(+), 117 deletions(-) create mode 100644 app/src/main/java/com/bobbyesp/metadator/presentation/components/cards/songs/compact/CompactCardSize.kt create mode 100644 app/src/main/java/com/bobbyesp/metadator/presentation/components/cards/songs/compact/CompactSongCard.kt create mode 100644 app/src/main/java/com/bobbyesp/metadator/presentation/components/text/ConditionedMarqueeText.kt create mode 100644 app/src/main/java/com/bobbyesp/metadator/presentation/pages/home/LayoutType.kt diff --git a/app/src/main/java/com/bobbyesp/metadator/presentation/components/cards/songs/HorizontalSongCard.kt b/app/src/main/java/com/bobbyesp/metadator/presentation/components/cards/songs/HorizontalSongCard.kt index 9e09050..7ac2bf9 100644 --- a/app/src/main/java/com/bobbyesp/metadator/presentation/components/cards/songs/HorizontalSongCard.kt +++ b/app/src/main/java/com/bobbyesp/metadator/presentation/components/cards/songs/HorizontalSongCard.kt @@ -19,6 +19,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.bobbyesp.metadator.presentation.components.image.AsyncImage +import com.bobbyesp.metadator.presentation.components.text.ConditionedMarqueeText import com.bobbyesp.metadator.presentation.theme.MetadatorTheme import com.bobbyesp.ui.components.text.MarqueeText import com.bobbyesp.utilities.Time @@ -50,13 +51,13 @@ fun HorizontalSongCard( .padding(vertical = 8.dp, horizontal = 6.dp) .weight(1f) ) { - MarqueeText( + ConditionedMarqueeText( text = song.title, style = MaterialTheme.typography.bodyLarge, fontWeight = FontWeight.Bold, fontSize = 15.sp ) - MarqueeText( + ConditionedMarqueeText( text = song.artist, style = MaterialTheme.typography.bodyMedium.copy( color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f) diff --git a/app/src/main/java/com/bobbyesp/metadator/presentation/components/cards/songs/VerticalSongCard.kt b/app/src/main/java/com/bobbyesp/metadator/presentation/components/cards/songs/VerticalSongCard.kt index 90d1276..3fef5f5 100644 --- a/app/src/main/java/com/bobbyesp/metadator/presentation/components/cards/songs/VerticalSongCard.kt +++ b/app/src/main/java/com/bobbyesp/metadator/presentation/components/cards/songs/VerticalSongCard.kt @@ -16,6 +16,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.bobbyesp.metadator.presentation.components.image.AsyncImage +import com.bobbyesp.metadator.presentation.components.text.ConditionedMarqueeText import com.bobbyesp.metadator.presentation.theme.MetadatorTheme import com.bobbyesp.ui.components.text.MarqueeText import com.bobbyesp.utilities.model.Song @@ -41,13 +42,13 @@ fun VerticalSongCard( Column( horizontalAlignment = Alignment.Start, modifier = Modifier.padding(8.dp) ) { - MarqueeText( + ConditionedMarqueeText( text = song.title, style = MaterialTheme.typography.bodyLarge, fontWeight = FontWeight.Bold, fontSize = 15.sp ) - MarqueeText( + ConditionedMarqueeText( text = song.artist, style = MaterialTheme.typography.bodyMedium.copy( color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f) diff --git a/app/src/main/java/com/bobbyesp/metadator/presentation/components/cards/songs/compact/CompactCardSize.kt b/app/src/main/java/com/bobbyesp/metadator/presentation/components/cards/songs/compact/CompactCardSize.kt new file mode 100644 index 0000000..4afbd23 --- /dev/null +++ b/app/src/main/java/com/bobbyesp/metadator/presentation/components/cards/songs/compact/CompactCardSize.kt @@ -0,0 +1,27 @@ +package com.bobbyesp.metadator.presentation.components.cards.songs.compact + +import androidx.compose.foundation.shape.CornerBasedShape +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp + +enum class CompactCardSize(val value: Dp) { + SMALL(96.dp), + MEDIUM(120.dp), + LARGE(144.dp), + EXTRA_LARGE(168.dp); + + companion object { + + fun Int.toCompactCardSize(): CompactCardSize = CompactCardSize.entries.first { it.ordinal == this } + + @Composable + fun CompactCardSize.toShape(): CornerBasedShape = when(this) { + SMALL -> MaterialTheme.shapes.small + MEDIUM -> MaterialTheme.shapes.medium + LARGE -> MaterialTheme.shapes.large + EXTRA_LARGE -> MaterialTheme.shapes.extraLarge + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/bobbyesp/metadator/presentation/components/cards/songs/compact/CompactSongCard.kt b/app/src/main/java/com/bobbyesp/metadator/presentation/components/cards/songs/compact/CompactSongCard.kt new file mode 100644 index 0000000..47eb27b --- /dev/null +++ b/app/src/main/java/com/bobbyesp/metadator/presentation/components/cards/songs/compact/CompactSongCard.kt @@ -0,0 +1,118 @@ +package com.bobbyesp.metadator.presentation.components.cards.songs.compact + +import android.net.Uri +import android.util.Log +import androidx.compose.animation.animateContentSize +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.shadow +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import com.bobbyesp.metadator.presentation.components.cards.songs.compact.CompactCardSize.Companion.toShape +import com.bobbyesp.metadator.presentation.components.image.AsyncImage +import com.bobbyesp.metadator.presentation.components.text.ConditionedMarqueeText + +@Composable +fun CompactSongCard( + modifier: Modifier = Modifier, + name: String, + artists: String, + artworkUri: Uri? = null, + listIndex: Int? = null, + shadow: Dp? = 4.dp, + size: CompactCardSize = CompactCardSize.LARGE, + shape: Shape? = MaterialTheme.shapes.large, + onClick: () -> Unit +) { + val cardSize by remember(size) { + mutableStateOf(size.value) + } + + val formalizedShape = shape ?: size.toShape() + + Box( + modifier = modifier + .shadow( + elevation = shadow ?: 0.dp, + shape = formalizedShape + ) + .clip(formalizedShape) + .size(cardSize) + .clickable(onClick = onClick) + + ) { + AsyncImage( + modifier = Modifier.fillMaxSize(), + imageModel = artworkUri, + ) + + listIndex?.let { + Text( + text = "$it.", + style = MaterialTheme.typography.bodySmall, + color = Color.White.copy(alpha = 0.8f), + fontWeight = FontWeight.Bold, + modifier = Modifier + .padding(8.dp) + .align(Alignment.TopEnd) + ) + } + Column( + modifier = Modifier + .background( + brush = Brush.verticalGradient( + colors = listOf( + Color.Transparent, + MaterialTheme.colorScheme.scrim + ), + startY = 0f, + endY = 500f + ), + alpha = 0.6f + ) + .fillMaxSize() + .padding(horizontal = 8.dp, vertical = 6.dp), + verticalArrangement = Arrangement.Bottom, + horizontalAlignment = Alignment.Start + ) { + ConditionedMarqueeText( + text = name, + style = MaterialTheme.typography.titleSmall, + color = Color.White, + overflow = TextOverflow.Ellipsis, + maxLines = 1 + ) + + if(artists.isNotEmpty()) { + ConditionedMarqueeText( + text = artists, + style = MaterialTheme.typography.bodySmall, + color = Color.White.copy(alpha = 0.6f), + overflow = TextOverflow.Ellipsis, + maxLines = 1 + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/bobbyesp/metadator/presentation/components/cards/songs/spotify/SpotifyHorizontalSongCard.kt b/app/src/main/java/com/bobbyesp/metadator/presentation/components/cards/songs/spotify/SpotifyHorizontalSongCard.kt index 71c7c59..ef2da50 100644 --- a/app/src/main/java/com/bobbyesp/metadator/presentation/components/cards/songs/spotify/SpotifyHorizontalSongCard.kt +++ b/app/src/main/java/com/bobbyesp/metadator/presentation/components/cards/songs/spotify/SpotifyHorizontalSongCard.kt @@ -22,6 +22,7 @@ import androidx.compose.ui.unit.dp import com.adamratzman.spotify.models.Track import com.bobbyesp.metadator.ext.formatArtists import com.bobbyesp.metadator.presentation.components.image.AsyncImage +import com.bobbyesp.metadator.presentation.components.text.ConditionedMarqueeText import com.bobbyesp.ui.components.text.MarqueeText @OptIn(ExperimentalFoundationApi::class) @@ -80,12 +81,12 @@ fun SpotifyHorizontalSongCard( .padding(8.dp) .weight(1f) ) { - MarqueeText( + ConditionedMarqueeText( text = track.name, style = MaterialTheme.typography.bodyLarge, fontWeight = FontWeight.Bold, ) - MarqueeText( + ConditionedMarqueeText( text = track.artists.formatArtists(), style = MaterialTheme.typography.bodyMedium.copy( color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f) diff --git a/app/src/main/java/com/bobbyesp/metadator/presentation/components/text/ConditionedMarqueeText.kt b/app/src/main/java/com/bobbyesp/metadator/presentation/components/text/ConditionedMarqueeText.kt new file mode 100644 index 0000000..31e2014 --- /dev/null +++ b/app/src/main/java/com/bobbyesp/metadator/presentation/components/text/ConditionedMarqueeText.kt @@ -0,0 +1,93 @@ +package com.bobbyesp.metadator.presentation.components.text + +import androidx.compose.animation.core.Easing +import androidx.compose.material3.LocalTextStyle +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextLayoutResult +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextDecoration +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.TextUnit +import com.bobbyesp.ui.components.text.MarqueeText +import com.bobbyesp.ui.components.text.MarqueeTextGradientOptions +import com.bobbyesp.utilities.preferences.PreferencesKeys.MARQUEE_TEXT +import com.bobbyesp.utilities.preferences.booleanState + +@Composable +fun ConditionedMarqueeText( + text: String, + modifier: Modifier = Modifier, + textModifier: Modifier = Modifier, + color: Color = Color.Unspecified, + fontSize: TextUnit = TextUnit.Unspecified, + fontStyle: FontStyle? = null, + fontWeight: FontWeight? = null, + fontFamily: FontFamily? = null, + letterSpacing: TextUnit = TextUnit.Unspecified, + textDecoration: TextDecoration? = null, + textAlign: TextAlign? = null, + lineHeight: TextUnit = TextUnit.Unspecified, + maxLines: Int = 1, + overflow: TextOverflow = TextOverflow.Clip, + softWrap: Boolean = true, + onTextLayout: (TextLayoutResult) -> Unit = {}, + style: TextStyle = LocalTextStyle.current.plus(TextStyle()), + sideGradient: MarqueeTextGradientOptions = MarqueeTextGradientOptions(), + customEasing: Easing? = null, + animationDuration: Float = 4000f, + delayBetweenAnimations: Long = 500L +) { + val useMarqueeText = MARQUEE_TEXT.booleanState + + if(useMarqueeText.value) { + MarqueeText( + text, + modifier, + textModifier, + color, + fontSize, + fontStyle, + fontWeight, + fontFamily, + letterSpacing, + textDecoration, + textAlign, + lineHeight, + maxLines, + overflow, + softWrap, + onTextLayout, + style, + sideGradient, + customEasing, + animationDuration, + delayBetweenAnimations + ) + } else { + Text( + text = text, + modifier = textModifier, + color = color, + fontSize = fontSize, + fontStyle = fontStyle, + fontWeight = fontWeight, + fontFamily = fontFamily, + letterSpacing = letterSpacing, + textDecoration = textDecoration, + textAlign = textAlign, + lineHeight = lineHeight, + maxLines = maxLines, + overflow = overflow, + softWrap = softWrap, + onTextLayout = onTextLayout, + style = style + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/bobbyesp/metadator/presentation/pages/MediaStorePage.kt b/app/src/main/java/com/bobbyesp/metadator/presentation/pages/MediaStorePage.kt index e2fef46..7cae32a 100644 --- a/app/src/main/java/com/bobbyesp/metadator/presentation/pages/MediaStorePage.kt +++ b/app/src/main/java/com/bobbyesp/metadator/presentation/pages/MediaStorePage.kt @@ -1,6 +1,7 @@ package com.bobbyesp.metadator.presentation.pages import androidx.compose.animation.Crossfade +import androidx.compose.animation.animateContentSize import androidx.compose.animation.core.tween import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement @@ -15,17 +16,24 @@ import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.State +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.bobbyesp.metadator.R import com.bobbyesp.metadator.presentation.components.cards.songs.HorizontalSongCard -import com.bobbyesp.metadator.presentation.components.cards.songs.VerticalSongCard +import com.bobbyesp.metadator.presentation.components.cards.songs.compact.CompactCardSize +import com.bobbyesp.metadator.presentation.components.cards.songs.compact.CompactCardSize.Companion.toCompactCardSize +import com.bobbyesp.metadator.presentation.components.cards.songs.compact.CompactSongCard import com.bobbyesp.metadator.presentation.components.others.status.EmptyMediaStore import com.bobbyesp.metadator.presentation.pages.home.LayoutType import com.bobbyesp.ui.common.pages.ErrorPage import com.bobbyesp.ui.common.pages.LoadingPage import com.bobbyesp.utilities.model.Song +import com.bobbyesp.utilities.preferences.PreferencesKeys.SONG_CARD_SIZE +import com.bobbyesp.utilities.preferences.intState import com.bobbyesp.utilities.states.ResourceState import my.nanihadesuka.compose.LazyColumnScrollbar import my.nanihadesuka.compose.LazyVerticalGridScrollbar @@ -39,10 +47,12 @@ fun MediaStorePage( lazyGridState: LazyGridState, lazyListState: LazyListState, desiredLayout: LayoutType, + compactCardSize: CompactCardSize, onReloadMediaStore: () -> Unit, onItemClicked: (Song) -> Unit ) { val songsList = songs.value + Box( modifier = modifier.fillMaxSize() ) { @@ -58,7 +68,7 @@ fun MediaStorePage( ) { onReloadMediaStore() } is ResourceState.Success -> { - val dataSongsList = songsList.data!! + val dataSongsList = songsList.data ?: throw IllegalStateException("Data is null") if (dataSongsList.isEmpty()) { EmptyMediaStore( modifier = Modifier.fillMaxSize() @@ -88,11 +98,16 @@ fun MediaStorePage( key = { index -> dataSongsList[index].id }, contentType = { _ -> "songItem" }) { index -> val song = dataSongsList[index] - VerticalSongCard( - song = song, - modifier = Modifier.animateItem( - fadeInSpec = null, fadeOutSpec = null - ), + + CompactSongCard( + modifier = Modifier + .animateItem( + fadeInSpec = null, fadeOutSpec = null + ), + size = compactCardSize, + name = song.title, + artists = song.artist, + artworkUri = song.artworkPath, onClick = { onItemClicked(song) }) diff --git a/app/src/main/java/com/bobbyesp/metadator/presentation/pages/home/HomePage.kt b/app/src/main/java/com/bobbyesp/metadator/presentation/pages/home/HomePage.kt index 9fa8fa6..e8bcc74 100644 --- a/app/src/main/java/com/bobbyesp/metadator/presentation/pages/home/HomePage.kt +++ b/app/src/main/java/com/bobbyesp/metadator/presentation/pages/home/HomePage.kt @@ -17,12 +17,11 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.rounded.List -import androidx.compose.material.icons.rounded.GridView +import androidx.compose.material.icons.rounded.CropSquare import androidx.compose.material.icons.rounded.KeyboardDoubleArrowUp import androidx.compose.material.icons.rounded.Menu import androidx.compose.material.icons.rounded.MoreVert -import androidx.compose.material3.CenterAlignedTopAppBar +import androidx.compose.material.icons.rounded.Settings import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon @@ -33,19 +32,18 @@ import androidx.compose.material3.SegmentedButton import androidx.compose.material3.SegmentedButtonDefaults import androidx.compose.material3.SingleChoiceSegmentedButtonRow import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.State import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontFamily @@ -57,15 +55,16 @@ import com.bobbyesp.metadator.ext.toParcelableSong import com.bobbyesp.metadator.presentation.common.LocalDrawerState import com.bobbyesp.metadator.presentation.common.LocalNavController import com.bobbyesp.metadator.presentation.common.Route +import com.bobbyesp.metadator.presentation.components.cards.songs.compact.CompactCardSize import com.bobbyesp.metadator.presentation.pages.MediaStorePage import com.bobbyesp.metadator.presentation.pages.MediaStorePageViewModel -import com.bobbyesp.metadator.presentation.pages.home.LayoutType.entries import com.bobbyesp.ui.components.dropdown.AnimatedDropdownMenu import com.bobbyesp.ui.components.dropdown.DropdownItemContainer import com.bobbyesp.ui.components.text.AutoResizableText import com.bobbyesp.utilities.model.Song import com.bobbyesp.utilities.preferences.Preferences -import com.bobbyesp.utilities.preferences.PreferencesKeys.DESIRED_OVERLAY +import com.bobbyesp.utilities.preferences.PreferencesKeys.DESIRED_LAYOUT +import com.bobbyesp.utilities.preferences.PreferencesKeys.SONG_CARD_SIZE import com.bobbyesp.utilities.states.ResourceState import com.bobbyesp.utilities.ui.permission.PermissionNotGrantedDialog import com.bobbyesp.utilities.ui.permission.PermissionRequestHandler @@ -114,70 +113,99 @@ fun HomePage( var desiredLayout by remember { mutableStateOf( Preferences.Enumerations.getValue( - DESIRED_OVERLAY, LayoutType.Grid + DESIRED_LAYOUT, LayoutType.Grid ) ) } - val gridIsFirstItemVisible by remember { derivedStateOf { mediaStoreLazyGridState.firstVisibleItemIndex == 0 } } - val listIsFirstItemVisible by remember { derivedStateOf { mediaStoreLazyColumnState.firstVisibleItemIndex == 0 } } + var desiredCardSize by remember { + mutableStateOf( + Preferences.Enumerations.getValue( + SONG_CARD_SIZE, CompactCardSize.LARGE + ) + ) + } + + val gridIsFirstItemVisible by remember { + derivedStateOf { + mediaStoreLazyGridState.firstVisibleItemIndex == 0 + } + } + val listIsFirstItemVisible by remember { + derivedStateOf { + mediaStoreLazyColumnState.firstVisibleItemIndex == 0 + } + } Scaffold(modifier = modifier.fillMaxSize(), topBar = { - CenterAlignedTopAppBar( - navigationIcon = { - IconButton(onClick = { - scope.launch { - drawerState.open() - } - }) { - Icon( - imageVector = Icons.Rounded.Menu, - contentDescription = stringResource(id = R.string.open_navigation) - ) + TopAppBar(navigationIcon = { + IconButton(onClick = { + scope.launch { + drawerState.open() } - }, title = { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - ) { - Text( - text = stringResource(id = R.string.app_name).uppercase(), - fontWeight = FontWeight.SemiBold, - fontFamily = FontFamily.Monospace, - style = MaterialTheme.typography.titleLarge.copy( - letterSpacing = 4.sp, - ), - ) - AutoResizableText( - text = stringResource(id = R.string.app_desc).uppercase(), - fontWeight = FontWeight.Normal, - fontFamily = FontFamily.Monospace, - style = MaterialTheme.typography.bodySmall.copy( - letterSpacing = 2.sp, - ), - color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.7f) + }) { + Icon( + imageVector = Icons.Rounded.Menu, + contentDescription = stringResource(id = R.string.open_navigation) + ) + } + }, title = { + Column( + horizontalAlignment = Alignment.Start, + ) { + Text( + text = stringResource(id = R.string.app_name).uppercase(), + fontWeight = FontWeight.SemiBold, + fontFamily = FontFamily.Monospace, + style = MaterialTheme.typography.titleLarge.copy( + letterSpacing = 4.sp, + ), + ) + AutoResizableText( + text = stringResource(id = R.string.app_desc).uppercase(), + fontWeight = FontWeight.Normal, + fontFamily = FontFamily.Monospace, + style = MaterialTheme.typography.bodySmall.copy( + letterSpacing = 2.sp, + ), + color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.7f) + ) + } + }, actions = { + IconButton( + onClick = { navController.navigate(Route.SettingsNavigator.Settings) }) { + Icon( + imageVector = Icons.Rounded.Settings, contentDescription = stringResource( + id = R.string.settings ) - } - }, actions = { - IconButton( - onClick = { - moreOptionsVisible = !moreOptionsVisible - }) { - Icon( - imageVector = Icons.Rounded.MoreVert, contentDescription = stringResource( - id = R.string.open_more_options - ) + ) + } + IconButton( + onClick = { + moreOptionsVisible = !moreOptionsVisible + }) { + Icon( + imageVector = Icons.Rounded.MoreVert, contentDescription = stringResource( + id = R.string.open_more_options ) - } - AnimatedDropdownMenu( - expanded = moreOptionsVisible, onDismissRequest = { - moreOptionsVisible = false - }) { - DropdownMenuContent(onLayoutChanged = { + ) + } + AnimatedDropdownMenu( + expanded = moreOptionsVisible, onDismissRequest = { + moreOptionsVisible = false + }) { + DropdownMenuContent( + desiredLayout = desiredLayout, + desiredCardSize = desiredCardSize, + onLayoutChanged = { desiredLayout = it + }, + onCardSizeChanged = { + desiredCardSize = it }) - } + } - }) + }) }, floatingActionButton = { when (desiredLayout) { LayoutType.Grid -> { @@ -244,6 +272,7 @@ fun HomePage( lazyGridState = mediaStoreLazyGridState, lazyListState = mediaStoreLazyColumnState, desiredLayout = desiredLayout, + compactCardSize = desiredCardSize, onReloadMediaStore = { onEvent(MediaStorePageViewModel.Companion.Events.ReloadMediaStore) }, @@ -258,17 +287,13 @@ fun HomePage( @Composable private fun DropdownMenuContent( - onLayoutChanged: (LayoutType) -> Unit = {} + desiredLayout: LayoutType, + desiredCardSize: CompactCardSize, + onLayoutChanged: (LayoutType) -> Unit = {}, + onCardSizeChanged: (CompactCardSize) -> Unit = {} ) { val availableLayoutType = LayoutType.entries.toImmutableList() - var desiredOverlay by remember { - mutableIntStateOf( - Preferences.Enumerations.getValue( - DESIRED_OVERLAY, LayoutType.Grid - ).ordinal - ) - } Column( modifier = Modifier.padding(horizontal = 8.dp), verticalArrangement = Arrangement.spacedBy(8.dp), @@ -280,37 +305,61 @@ private fun DropdownMenuContent( color = MaterialTheme.colorScheme.primary, style = MaterialTheme.typography.labelMedium ) - DropdownItemContainer(content = { - SingleChoiceSegmentedButtonRow { - availableLayoutType.forEachIndexed { index, listType -> - SegmentedButton( - selected = desiredOverlay == listType.ordinal, - onClick = { - desiredOverlay = listType.ordinal - Preferences.Enumerations.encodeValue( - DESIRED_OVERLAY, listType + DropdownItemContainer( + content = { + SingleChoiceSegmentedButtonRow { + availableLayoutType.forEachIndexed { index, listType -> + SegmentedButton( + selected = desiredLayout.ordinal == listType.ordinal, + onClick = { + Preferences.Enumerations.encodeValue( + DESIRED_LAYOUT, listType + ) + onLayoutChanged(listType) + }, + shape = SegmentedButtonDefaults.itemShape( + index = index, count = availableLayoutType.size + ), + ) { + Icon( + imageVector = listType.icon, + contentDescription = stringResource(id = R.string.list_type) ) - onLayoutChanged(listType) - }, - shape = SegmentedButtonDefaults.itemShape( - index = index, count = availableLayoutType.size - ), - ) { - Icon( - imageVector = listType.icon, - contentDescription = stringResource(id = R.string.list_type) - ) + } } } - } - }) + }) + Text( + text = stringResource(id = R.string.card_size), + modifier = Modifier.fillMaxWidth(), + color = MaterialTheme.colorScheme.primary, + style = MaterialTheme.typography.labelMedium + ) + + DropdownItemContainer( + content = { + SingleChoiceSegmentedButtonRow { + CompactCardSize.entries.forEachIndexed { index, cardSize -> + SegmentedButton( + selected = desiredCardSize.ordinal == cardSize.ordinal, + onClick = { + Preferences.Enumerations.encodeValue( + SONG_CARD_SIZE, cardSize + ) + onCardSizeChanged(cardSize) + }, + shape = SegmentedButtonDefaults.itemShape( + index = index, count = CompactCardSize.entries.size + ), + ) { + Icon( + imageVector = Icons.Rounded.CropSquare, + contentDescription = stringResource(id = R.string.card_size) + ) + } + } + } + }) } } -enum class LayoutType(val icon: ImageVector) { - Grid(icon = Icons.Rounded.GridView), List(icon = Icons.AutoMirrored.Rounded.List); - - companion object { - fun Int.toListType(): LayoutType = entries.first { it.ordinal == this } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/bobbyesp/metadator/presentation/pages/home/LayoutType.kt b/app/src/main/java/com/bobbyesp/metadator/presentation/pages/home/LayoutType.kt new file mode 100644 index 0000000..05261ea --- /dev/null +++ b/app/src/main/java/com/bobbyesp/metadator/presentation/pages/home/LayoutType.kt @@ -0,0 +1,15 @@ +package com.bobbyesp.metadator.presentation.pages.home + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.rounded.List +import androidx.compose.material.icons.rounded.GridView +import androidx.compose.ui.graphics.vector.ImageVector + +enum class LayoutType(val icon: ImageVector) { + Grid(icon = Icons.Rounded.GridView), + List(icon = Icons.AutoMirrored.Rounded.List); + + companion object { + fun Int.toListType(): LayoutType = entries.first { it.ordinal == this } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/bobbyesp/metadator/presentation/pages/mediaplayer/player/PlayerControls.kt b/app/src/main/java/com/bobbyesp/metadator/presentation/pages/mediaplayer/player/PlayerControls.kt index b68e8b1..bbe0bc0 100644 --- a/app/src/main/java/com/bobbyesp/metadator/presentation/pages/mediaplayer/player/PlayerControls.kt +++ b/app/src/main/java/com/bobbyesp/metadator/presentation/pages/mediaplayer/player/PlayerControls.kt @@ -44,6 +44,7 @@ import com.bobbyesp.metadator.R import com.bobbyesp.metadator.presentation.components.buttons.PlayPauseAnimatedButton import com.bobbyesp.metadator.presentation.components.others.RepeatStateIcon import com.bobbyesp.metadator.presentation.components.others.ShuffleStateIcon +import com.bobbyesp.metadator.presentation.components.text.ConditionedMarqueeText import com.bobbyesp.metadator.presentation.pages.mediaplayer.MediaplayerViewModel import com.bobbyesp.ui.components.text.MarqueeText import com.bobbyesp.ui.components.text.MarqueeTextGradientOptions @@ -110,7 +111,7 @@ fun PlayerControls( text = it?.title.toString(), style = MaterialTheme.typography.titleLarge.copy(fontWeight = FontWeight.Medium) ) - MarqueeText( + ConditionedMarqueeText( text = it?.artist.toString(), style = MaterialTheme.typography.bodyLarge, customEasing = EaseInOutSine, diff --git a/app/src/main/java/com/bobbyesp/metadator/presentation/pages/mediaplayer/player/views/MiniplayerContent.kt b/app/src/main/java/com/bobbyesp/metadator/presentation/pages/mediaplayer/player/views/MiniplayerContent.kt index 4025773..f3e4012 100644 --- a/app/src/main/java/com/bobbyesp/metadator/presentation/pages/mediaplayer/player/views/MiniplayerContent.kt +++ b/app/src/main/java/com/bobbyesp/metadator/presentation/pages/mediaplayer/player/views/MiniplayerContent.kt @@ -31,6 +31,7 @@ import androidx.compose.ui.unit.sp import androidx.media3.common.MediaMetadata import com.bobbyesp.metadator.R import com.bobbyesp.metadator.presentation.components.image.AsyncImage +import com.bobbyesp.metadator.presentation.components.text.ConditionedMarqueeText import com.bobbyesp.metadator.presentation.pages.mediaplayer.player.AnimatedTextContentTransformation import com.bobbyesp.ui.components.button.DynamicButton import com.bobbyesp.ui.components.text.MarqueeText @@ -84,14 +85,14 @@ fun MiniplayerContent( ) { transition.AnimatedContent(transitionSpec = { AnimatedTextContentTransformation }) { Column { - MarqueeText( + ConditionedMarqueeText( text = it.title.toString(), style = MaterialTheme.typography.bodyLarge, fontWeight = FontWeight.Bold, fontSize = 16.sp ) - MarqueeText( + ConditionedMarqueeText( text = it.artist.toString(), style = MaterialTheme.typography.bodyMedium.copy( color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f) diff --git a/app/src/main/java/com/bobbyesp/metadator/presentation/pages/settings/modules/GeneralSettingsPage.kt b/app/src/main/java/com/bobbyesp/metadator/presentation/pages/settings/modules/GeneralSettingsPage.kt index 49c57e2..04935d0 100644 --- a/app/src/main/java/com/bobbyesp/metadator/presentation/pages/settings/modules/GeneralSettingsPage.kt +++ b/app/src/main/java/com/bobbyesp/metadator/presentation/pages/settings/modules/GeneralSettingsPage.kt @@ -28,7 +28,7 @@ fun GeneralSettingsPage() { val navController = LocalNavController.current val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior( - rememberTopAppBarState(), + state = rememberTopAppBarState(), canScroll = { true } ) diff --git a/app/src/main/java/com/bobbyesp/metadator/presentation/pages/utilities/tageditor/MetadataEditorPage.kt b/app/src/main/java/com/bobbyesp/metadator/presentation/pages/utilities/tageditor/MetadataEditorPage.kt index f74d8ab..045a6ef 100644 --- a/app/src/main/java/com/bobbyesp/metadator/presentation/pages/utilities/tageditor/MetadataEditorPage.kt +++ b/app/src/main/java/com/bobbyesp/metadator/presentation/pages/utilities/tageditor/MetadataEditorPage.kt @@ -175,7 +175,7 @@ fun MetadataEditorPage( TopAppBar( title = { Column(modifier = Modifier.fillMaxWidth()) { - MarqueeText( + Text( text = stringResource(id = R.string.viewing_metadata), style = MaterialTheme.typography.bodyLarge, fontSize = 20.sp, diff --git a/app/src/main/java/com/bobbyesp/metadator/presentation/pages/utilities/tageditor/spotify/stages/SpMetadataBsDetails.kt b/app/src/main/java/com/bobbyesp/metadator/presentation/pages/utilities/tageditor/spotify/stages/SpMetadataBsDetails.kt index 89476fc..2cc7395 100644 --- a/app/src/main/java/com/bobbyesp/metadator/presentation/pages/utilities/tageditor/spotify/stages/SpMetadataBsDetails.kt +++ b/app/src/main/java/com/bobbyesp/metadator/presentation/pages/utilities/tageditor/spotify/stages/SpMetadataBsDetails.kt @@ -43,6 +43,7 @@ import com.bobbyesp.metadator.ext.TagLib.toLocalizedName import com.bobbyesp.metadator.ext.format import com.bobbyesp.metadator.ext.formatArtists import com.bobbyesp.metadator.presentation.components.image.AsyncImage +import com.bobbyesp.metadator.presentation.components.text.ConditionedMarqueeText import com.bobbyesp.metadator.presentation.pages.utilities.tageditor.spotify.MetadataBsVM import com.bobbyesp.ui.components.button.BackButton import com.bobbyesp.ui.components.others.SelectableSurface @@ -198,7 +199,7 @@ private fun TrackInfo( .padding(8.dp) .weight(1f) ) { - MarqueeText( + ConditionedMarqueeText( text = track.name, style = MaterialTheme.typography.bodyLarge, fontWeight = FontWeight.Bold, diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 130c736..a221df9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -81,4 +81,5 @@ The lyrics retrievement process has been cancelled The lyrics sent from the provider app were null or empty Something unexpected occurred! + Card size \ No newline at end of file diff --git a/app/utilities/src/main/java/com/bobbyesp/utilities/preferences/Preferences.kt b/app/utilities/src/main/java/com/bobbyesp/utilities/preferences/Preferences.kt index 2c7ebf7..bf9d8d9 100644 --- a/app/utilities/src/main/java/com/bobbyesp/utilities/preferences/Preferences.kt +++ b/app/utilities/src/main/java/com/bobbyesp/utilities/preferences/Preferences.kt @@ -5,6 +5,7 @@ import com.bobbyesp.utilities.preferences.PreferencesKeys.DYNAMIC_COLOR import com.bobbyesp.utilities.preferences.PreferencesKeys.HIGH_CONTRAST import com.bobbyesp.utilities.preferences.PreferencesKeys.MARQUEE_TEXT import com.bobbyesp.utilities.preferences.PreferencesKeys.MMKV_PREFERENCES_NAME +import com.bobbyesp.utilities.preferences.PreferencesKeys.SONG_CARD_SIZE import com.bobbyesp.utilities.preferences.PreferencesKeys.THEME_COLOR import com.bobbyesp.utilities.preferences.settings.AppMainSettings import com.bobbyesp.utilities.theme.DarkThemePreference @@ -23,7 +24,8 @@ private val BooleanPreferenceDefaults: Map = private val IntPreferenceDefaults: Map = mapOf( - DARK_THEME_VALUE to DarkThemePreference.FOLLOW_SYSTEM + DARK_THEME_VALUE to DarkThemePreference.FOLLOW_SYSTEM, + SONG_CARD_SIZE to 2 //CompactCardSize.LARGE (com.bobbyesp.metadator.presentation.components.cards.songs.compact.CompactCardSize) ) object Preferences { diff --git a/app/utilities/src/main/java/com/bobbyesp/utilities/preferences/PreferencesKeys.kt b/app/utilities/src/main/java/com/bobbyesp/utilities/preferences/PreferencesKeys.kt index be1bd64..e9d6cbb 100644 --- a/app/utilities/src/main/java/com/bobbyesp/utilities/preferences/PreferencesKeys.kt +++ b/app/utilities/src/main/java/com/bobbyesp/utilities/preferences/PreferencesKeys.kt @@ -2,8 +2,8 @@ package com.bobbyesp.utilities.preferences object PreferencesKeys { //------------GENERAL-------------- - const val DESIRED_OVERLAY = "desired_overlay" - + const val DESIRED_LAYOUT = "desired_overlay" + const val SONG_CARD_SIZE = "song_card_size" const val MARQUEE_TEXT = "marquee_text" //------------THEME----------------