diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index 728a6136fd..f181f73e34 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -58,7 +58,8 @@ kotlin { configurations.commonMainApi { exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-coroutines-android") } - */ + */ + } diff --git a/composeApp/src/androidMain/ic_launcher-playstore.webp b/composeApp/src/androidMain/ic_launcher-playstore.webp index dd6cd9abdd..63e6bfbc71 100644 Binary files a/composeApp/src/androidMain/ic_launcher-playstore.webp and b/composeApp/src/androidMain/ic_launcher-playstore.webp differ diff --git a/composeApp/src/androidMain/ic_logo-playstore.webp b/composeApp/src/androidMain/ic_logo-playstore.webp index 6682e6dcba..63e6bfbc71 100644 Binary files a/composeApp/src/androidMain/ic_logo-playstore.webp and b/composeApp/src/androidMain/ic_logo-playstore.webp differ diff --git a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/enums/NavRoutes.kt b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/enums/NavRoutes.kt index 107fe6f941..355a5b8e98 100644 --- a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/enums/NavRoutes.kt +++ b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/enums/NavRoutes.kt @@ -1,5 +1,7 @@ package it.fast4x.rimusic.enums +import androidx.navigation.NavController + enum class NavRoutes { home, album, @@ -21,5 +23,14 @@ enum class NavRoutes { statistics, newAlbums, moodsPage, - podcast + podcast; + + companion object { + + fun current( navController: NavController ) = navController.currentBackStackEntry?.destination?.route + } + + fun isHere( navController: NavController ) = current( navController ) == this.name + + fun isNotHere( navController: NavController ) = !isHere( navController ) } \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/service/DownloadUtil.kt b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/service/DownloadUtil.kt index 9e5192f8c2..cf5b5b0a67 100644 --- a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/service/DownloadUtil.kt +++ b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/service/DownloadUtil.kt @@ -22,6 +22,7 @@ import androidx.media3.datasource.okhttp.OkHttpDataSource import androidx.media3.exoplayer.offline.Download import androidx.media3.exoplayer.offline.DownloadManager import androidx.media3.exoplayer.offline.DownloadNotificationHelper +import androidx.media3.exoplayer.scheduler.Requirements import it.fast4x.innertube.Innertube import it.fast4x.innertube.models.bodies.PlayerBody import it.fast4x.innertube.requests.player @@ -144,9 +145,10 @@ object DownloadUtil { ?.maxByOrNull { (it.bitrate?.times( when (audioQualityFormat) { - AudioQualityFormat.Auto -> if (connectivityManager.isActiveNetworkMetered) -1 else 1 + AudioQualityFormat.Auto -> if (connectivityManager.isActiveNetworkMetered) -2 else 1 AudioQualityFormat.High -> 1 - AudioQualityFormat.Low, AudioQualityFormat.Medium -> -1 + AudioQualityFormat.Medium -> -1 + AudioQualityFormat.Low -> -2 } ) ?: -1) + (if (it.mimeType.startsWith("audio/webm")) 10240 else 0) } @@ -343,7 +345,9 @@ object DownloadUtil { getResolvingDataSourceFactory(context), Executors.newFixedThreadPool(6) ).apply { - maxParallelDownloads = 2 + maxParallelDownloads = 3 + minRetryCount = 1 + requirements = Requirements(Requirements.NETWORK) } //downloadTracker = diff --git a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/service/PlayerService.kt b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/service/PlayerService.kt index aa1f5cd28e..08efe8506f 100644 --- a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/service/PlayerService.kt +++ b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/service/PlayerService.kt @@ -55,8 +55,10 @@ import androidx.media3.common.audio.SonicAudioProcessor import androidx.media3.common.util.Log import androidx.media3.common.util.UnstableApi import androidx.media3.database.StandaloneDatabaseProvider +import androidx.media3.datasource.DataSource import androidx.media3.datasource.DataSpec import androidx.media3.datasource.DefaultDataSource +import androidx.media3.datasource.HttpDataSource.InvalidResponseCodeException import androidx.media3.datasource.ResolvingDataSource import androidx.media3.datasource.cache.Cache import androidx.media3.datasource.cache.CacheDataSource @@ -81,6 +83,7 @@ import androidx.media3.exoplayer.audio.SilenceSkippingAudioProcessor import androidx.media3.exoplayer.mediacodec.MediaCodecSelector import androidx.media3.exoplayer.offline.Download import androidx.media3.exoplayer.source.DefaultMediaSourceFactory +import androidx.media3.exoplayer.upstream.DefaultLoadErrorHandlingPolicy import androidx.media3.extractor.DefaultExtractorsFactory import androidx.media3.extractor.ExtractorsFactory import androidx.media3.extractor.mkv.MatroskaExtractor @@ -126,6 +129,7 @@ import it.fast4x.rimusic.utils.asSong import it.fast4x.rimusic.utils.broadCastPendingIntent import it.fast4x.rimusic.cleanPrefix import it.fast4x.rimusic.enums.AudioQualityFormat +import it.fast4x.rimusic.enums.PopupType import it.fast4x.rimusic.utils.audioQualityFormatKey import it.fast4x.rimusic.utils.closebackgroundPlayerKey import it.fast4x.rimusic.utils.discordPersonalAccessTokenKey @@ -140,6 +144,8 @@ import it.fast4x.rimusic.utils.forcePlayFromBeginning import it.fast4x.rimusic.utils.forceSeekToNext import it.fast4x.rimusic.utils.forceSeekToPrevious import it.fast4x.rimusic.utils.getEnum +import it.fast4x.rimusic.utils.handleCatchingErrors +import it.fast4x.rimusic.utils.handleRangeErrors import it.fast4x.rimusic.utils.intent import it.fast4x.rimusic.utils.isAtLeastAndroid10 import it.fast4x.rimusic.utils.isAtLeastAndroid12 @@ -164,6 +170,7 @@ import it.fast4x.rimusic.utils.resumePlaybackWhenDeviceConnectedKey import it.fast4x.rimusic.utils.shouldBePlaying import it.fast4x.rimusic.utils.showDownloadButtonBackgroundPlayerKey import it.fast4x.rimusic.utils.showLikeButtonBackgroundPlayerKey +import it.fast4x.rimusic.utils.skipMediaOnErrorKey import it.fast4x.rimusic.utils.skipSilenceKey import it.fast4x.rimusic.utils.startFadeAnimator import it.fast4x.rimusic.utils.thumbnail @@ -202,6 +209,7 @@ import kotlinx.coroutines.withContext import me.knighthat.appContext import okhttp3.OkHttpClient import timber.log.Timber +import java.io.IOException import java.io.ObjectInputStream import java.io.ObjectOutputStream import java.net.ConnectException @@ -543,7 +551,7 @@ class PlayerService : InvincibleService(), downloadCache = DownloadUtil.getDownloadSimpleCache(applicationContext) as SimpleCache player = ExoPlayer.Builder(this, createRendersFactory(), createMediaSourceFactory()) - .setRenderersFactory(DefaultRenderersFactory(this).setEnableDecoderFallback(true)) + //.setRenderersFactory(DefaultRenderersFactory(this).setEnableDecoderFallback(true)) .setAudioAttributes( AudioAttributes.Builder() .setContentType(C.AUDIO_CONTENT_TYPE_MUSIC) @@ -556,19 +564,19 @@ class PlayerService : InvincibleService(), .setHandleAudioBecomingNoisy(true) //.setSeekForwardIncrementMs(5000) //.setSeekBackIncrementMs(5000) - //.setUsePlatformDiagnostics(false) + .setUsePlatformDiagnostics(false) .build() + player.skipSilenceEnabled = preferences.getBoolean(skipSilenceKey, false) + player.addListener(this@PlayerService) + player.addAnalyticsListener(PlaybackStatsListener(false, this@PlayerService)) + player.repeatMode = when { preferences.getBoolean(trackLoopEnabledKey, false) -> Player.REPEAT_MODE_ONE preferences.getBoolean(queueLoopEnabledKey, false) -> Player.REPEAT_MODE_ALL else -> Player.REPEAT_MODE_OFF } - player.skipSilenceEnabled = preferences.getBoolean(skipSilenceKey, false) - player.addListener(this) - player.addAnalyticsListener(PlaybackStatsListener(false, this)) - // Build a PendingIntent that can be used to launch the UI. val sessionActivityPendingIntent = packageManager?.getLaunchIntentForPackage(packageName)?.let { sessionIntent -> @@ -1521,24 +1529,57 @@ class PlayerService : InvincibleService(), override fun onPlayerError(error: PlaybackException) { super.onPlayerError(error) Timber.e("PlayerService onPlayerError ${error.stackTraceToString()}") - //this.stopService(this.intent()) - //this.stopService(this.intent()) - //Log.d("mediaItem","onPlayerError ${error.errorCodeName}") + //println("mediaItem onPlayerError errorCode ${error.errorCode} errorCodeName ${error.errorCodeName}") + if (error.errorCode in arrayOf(416,2000)) { + //println("mediaItem onPlayerError recovered occurred errorCodeName ${error.errorCodeName}") + player.pause() + player.prepare() + player.play() + return + } + + if (!preferences.getBoolean(skipMediaOnErrorKey, false) + || !player.hasNextMediaItem()) + return + + val prev = player.currentMediaItem ?: return + player.seekToNextMediaItem() + + coroutineScope.launch(Dispatchers.Main) { + withContext(Dispatchers.Main) { + SmartMessage( + getString( + R.string.skip_media_on_error_message, + prev.mediaMetadata.title + ), type = PopupType.Success, durationLong = true ,context = this@PlayerService + ) + } + } + + } + /* override fun onPlayerErrorChanged(error: PlaybackException?) { super.onPlayerErrorChanged(error) Timber.e("PlayerService onPlayerErrorChanged ${error?.stackTraceToString()}") //this.stopService(this.intent()) //this.stopService(this.intent()) //Log.d("mediaItem","onPlayerErrorChanged ${error?.errorCodeName}") + + //onPlayerError(error!!) } + */ + + + /* override fun onPlaybackSuppressionReasonChanged(playbackSuppressionReason: Int) { super.onPlaybackSuppressionReasonChanged(playbackSuppressionReason) //Timber.e("PlayerService onPlaybackSuppressionReasonChanged $playbackSuppressionReason") //Log.d("mediaItem","onPlaybackSuppressionReasonChanged $playbackSuppressionReason") } + */ @UnstableApi override fun onIsPlayingChanged(isPlaying: Boolean) { @@ -1805,6 +1846,10 @@ class PlayerService : InvincibleService(), ), DefaultExtractorsFactory() //getExtractorsFactory() + ).setLoadErrorHandlingPolicy( + object : DefaultLoadErrorHandlingPolicy() { + override fun isEligibleForFallback(exception: IOException) = true + } ) private fun getExtractorsFactory(): ExtractorsFactory = ExtractorsFactory { @@ -1821,16 +1866,52 @@ class PlayerService : InvincibleService(), ) } + private fun createRendersFactory() = object : DefaultRenderersFactory(this) { + override fun buildAudioSink( + context: Context, + enableFloatOutput: Boolean, + enableAudioTrackPlaybackParams: Boolean + ): AudioSink { + val minimumSilenceDuration = preferences.getLong( + minimumSilenceDurationKey, 2_000_000L).coerceIn(1000L..2_000_000L) + + return DefaultAudioSink.Builder(applicationContext) + .setEnableFloatOutput(enableFloatOutput) + .setEnableAudioTrackPlaybackParams(enableAudioTrackPlaybackParams) + .setAudioOffloadSupportProvider( + DefaultAudioOffloadSupportProvider(applicationContext) + ) + .setAudioProcessorChain( + DefaultAudioProcessorChain( + arrayOf(), + SilenceSkippingAudioProcessor( + /* minimumSilenceDurationUs = */ minimumSilenceDuration, + /* silenceRetentionRatio = */ 0.01f, + /* maxSilenceToKeepDurationUs = */ minimumSilenceDuration, + /* minVolumeToKeepPercentageWhenMuting = */ 0, + /* silenceThresholdLevel = */ 256 + ), + SonicAudioProcessor() + ) + ) + .build() + .apply { + if (isAtLeastAndroid10) setOffloadMode(AudioSink.OFFLOAD_MODE_DISABLED) + } + } + } + + /* private fun createRendersFactory(): RenderersFactory { val minimumSilenceDuration = preferences.getLong( - minimumSilenceDurationKey, 2_000_000L) //PlayerPreferences.minimumSilence.coerceIn(1000L..2_000_000L) + minimumSilenceDurationKey, 2_000_000L).coerceIn(1000L..2_000_000L) val audioSink = DefaultAudioSink.Builder(applicationContext) .setEnableFloatOutput(false) .setEnableAudioTrackPlaybackParams(false) .setAudioOffloadSupportProvider(DefaultAudioOffloadSupportProvider(applicationContext)) .setAudioProcessorChain( DefaultAudioProcessorChain( - emptyArray(), + arrayOf(), SilenceSkippingAudioProcessor( /* minimumSilenceDurationUs = */ minimumSilenceDuration, /* silenceRetentionRatio = */ 0.01f, @@ -1865,11 +1946,13 @@ class PlayerService : InvincibleService(), ) } } - + */ private fun createDataSourceResolverFactory( mediaItemToPlay: (videoId: String) -> MediaItem? - ): ResolvingDataSource.Factory { + ): //ResolvingDataSource.Factory { // not required for handlers + DataSource.Factory { // required for handlers + val ringBuffer = RingBuffer?>(2) { null } val chunkLength = 512 * 1024L @@ -1975,9 +2058,10 @@ class PlayerService : InvincibleService(), ?.maxByOrNull { (it.bitrate?.times( when (audioQualityFormat) { - AudioQualityFormat.Auto -> if (connectivityManager.isActiveNetworkMetered) -1 else 1 + AudioQualityFormat.Auto -> if (connectivityManager.isActiveNetworkMetered) -2 else 1 AudioQualityFormat.High -> 1 - AudioQualityFormat.Low, AudioQualityFormat.Medium -> -1 + AudioQualityFormat.Medium -> -1 + AudioQualityFormat.Low -> -2 } ) ?: -1) + (if (it.mimeType.startsWith("audio/webm")) 10240 else 0) } @@ -2091,6 +2175,8 @@ class PlayerService : InvincibleService(), } } } + .handleCatchingErrors() // required for Datasource.Factory + .handleRangeErrors() // required for Datasource.Factory } diff --git a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/components/NavigationRail.kt b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/components/NavigationRail.kt deleted file mode 100644 index 8161a93c40..0000000000 --- a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/components/NavigationRail.kt +++ /dev/null @@ -1,304 +0,0 @@ -package it.fast4x.rimusic.ui.components - -import androidx.compose.animation.animateColor -import androidx.compose.animation.core.animateFloat -import androidx.compose.animation.core.updateTransition -import androidx.compose.foundation.Image -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.ColumnScope -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.offset -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.text.BasicText -import androidx.compose.foundation.verticalScroll -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.draw.rotate -import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.graphics.graphicsLayer -import androidx.compose.ui.layout.layout -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.unit.dp -import it.fast4x.rimusic.R -import it.fast4x.rimusic.enums.NavigationBarType -import it.fast4x.rimusic.enums.UiType -import it.fast4x.rimusic.ui.styling.Dimensions -import it.fast4x.rimusic.ui.styling.favoritesIcon -import it.fast4x.rimusic.utils.isLandscape -import it.fast4x.rimusic.utils.semiBold -import me.knighthat.colorPalette -import me.knighthat.typography - -@Composable -inline fun NavigationRail( - topIconButtonId: Int, - noinline onTopIconButtonClick: () -> Unit, - showButton1: Boolean = true, - topIconButton2Id: Int, - noinline onTopIconButton2Click: () -> Unit, - showButton2: Boolean, - bottomIconButtonId: Int? = R.drawable.search, - noinline onBottomIconButtonClick: () -> Unit, - showBottomButton: Boolean = false, - tabIndex: Int, - crossinline onTabIndexChanged: (Int) -> Unit, - content: @Composable ColumnScope.(@Composable (Int, String, Int) -> Unit) -> Unit, - hideTabs: Boolean? = false, - modifier: Modifier = Modifier -) { - val isLandscape = isLandscape - - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = modifier - .verticalScroll(rememberScrollState()) - //.padding(paddingValues) - //.border(BorderStroke(1.dp, Color.Green)) - //.fillMaxWidth() - ) { - - if (hideTabs == false) - //if(uiType == UiType.ViMusic) - Box( - contentAlignment = Alignment.TopCenter, - modifier = Modifier - .height( - if( UiType.ViMusic.isCurrent() ) - if (showButton2) - Dimensions.headerHeight - else - Dimensions.halfheaderHeight - else 0.dp - ) - /* - .size( - //width = if (isLandscape) Dimensions.navigationRailWidthLandscape else Dimensions.navigationRailWidth, - width = Dimensions.navigationRailWidth, - height = if (showButton2) Dimensions.headerHeight else Dimensions.halfheaderHeight - ) - */ - //.border(BorderStroke(1.dp, Color.Red)) - ) { - if (showButton1) - Image( - painter = painterResource(topIconButtonId), - contentDescription = null, - colorFilter = ColorFilter.tint(colorPalette().favoritesIcon), //ColorFilter.tint(colorPalette().textSecondary), - modifier = Modifier - .offset( - x = 0.dp, //if (isLandscape) 0.dp else Dimensions.navigationRailIconOffset, - y = 7.dp - ) - .clip(CircleShape) - .clickable(onClick = onTopIconButtonClick) - //.padding(all = 12.dp) - .padding(top = 12.dp, bottom = 12.dp) - .size(24.dp) - ) - if (showButton2) { - Image( - painter = painterResource(topIconButton2Id), - contentDescription = null, - colorFilter = ColorFilter.tint(colorPalette().textSecondary), - modifier = Modifier - .offset( - x = 0.dp, //if (isLandscape) 0.dp else Dimensions.navigationRailIconOffset, - y = 70.dp - ) - .clip(CircleShape) - .clickable(onClick = onTopIconButton2Click) - //.padding(all = 12.dp) - .padding(top = 12.dp, bottom = 12.dp) - .size(24.dp) - ) - } - - } - - - if (hideTabs == false) - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier - //.border(BorderStroke(1.dp,Color.Green)) - //.width(if (isLandscape) Dimensions.navigationRailWidthLandscape else Dimensions.navigationRailWidth) - //.width(Dimensions.navigationRailWidth) - //.fillMaxWidth() - //.border(BorderStroke(1.dp, Color.Magenta)) - ) { - val transition = updateTransition(targetState = tabIndex, label = null) - - content { index, text, icon -> - - val textColor by transition.animateColor(label = "") { - if (it == index) colorPalette().text else colorPalette().textDisabled - } - val dothAlpha by transition.animateFloat(label = "") { - if (it == index) 1f else 0f - } - - val textContent: @Composable () -> Unit = { - if ( NavigationBarType.IconOnly.isCurrent() ) { - /* - BasicText( - text = "", - style = typography.xs.semiBold.center.color(textColor), - modifier = Modifier - .padding(horizontal = 16.dp) - ) - */ - } else { - BasicText( - text = text, - //style = typography.xs.semiBold.center.color(textColor), - style = TextStyle( - fontSize = typography().xs.semiBold.fontSize, - fontWeight = typography().xs.semiBold.fontWeight, - color = colorPalette().text, - //textAlign = if(uiType != UiType.ViMusic) TextAlign.Center else TextAlign.End - - ), - modifier = Modifier - .vertical(enabled = !isLandscape) - .rotate(if (isLandscape) 0f else -90f) - .padding(horizontal = 16.dp) - ) - } - } - - val iconContent: @Composable () -> Unit = { - if ( NavigationBarType.IconOnly.isCurrent() ) { - Image( - painter = painterResource(icon), - contentDescription = null, - colorFilter = ColorFilter.tint(textColor), - modifier = Modifier - //.padding(all = 12.dp) - .padding(top = 12.dp, bottom = 12.dp) - .size(24.dp) - ) - } else { - Image( - painter = painterResource(icon), - contentDescription = null, - colorFilter = ColorFilter.tint(colorPalette().text), - modifier = Modifier - .vertical(enabled = !isLandscape) - .graphicsLayer { - alpha = dothAlpha - translationX = (1f - dothAlpha) * -48.dp.toPx() - rotationZ = if (isLandscape) 0f else -90f - } - .size(Dimensions.navigationRailIconOffset * 2) - ) - } - } - - /* - val dothAlpha by transition.animateFloat(label = "") { - if (it == index) 1f else 0f - } - - val textColor by transition.animateColor(label = "") { - if (it == index) colorPalette().text else colorPalette().textDisabled - } - - val iconContent: @Composable () -> Unit = { - Image( - painter = painterResource(icon), - contentDescription = null, - colorFilter = ColorFilter.tint(colorPalette().text), - modifier = Modifier - .vertical(enabled = !isLandscape) - .graphicsLayer { - alpha = dothAlpha - //translationX = (1f - dothAlpha) * -48.dp.toPx() - rotationZ = if (isLandscape) 0f else -90f - } - .size(Dimensions.navigationRailIconOffset * 2) - ) - } - - val textContent: @Composable () -> Unit = { - BasicText( - text = text, - style = typography.xs.semiBold.center.color(textColor), - modifier = Modifier - .vertical(enabled = !isLandscape) - .rotate(if (isLandscape) 0f else -90f) - .padding(horizontal = 16.dp) - ) - } - */ - val contentModifier = Modifier - .clip(RoundedCornerShape(24.dp)) - .clickable(onClick = { onTabIndexChanged(index) }) - - if (isLandscape) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = contentModifier - .padding(vertical = 8.dp) - ) { - iconContent() - textContent() - } - } else { - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = contentModifier - .padding(horizontal = 8.dp) - ) { - iconContent() - textContent() - } - } - } - } - - if (showBottomButton) - Box( - contentAlignment = Alignment.TopCenter, - modifier = Modifier - .size( - width = if (isLandscape) Dimensions.navigationRailWidthLandscape else Dimensions.navigationRailWidth, - height = Dimensions.halfheaderHeight - ) - ) { - Image( - painter = painterResource(bottomIconButtonId ?: R.drawable.search ), - contentDescription = null, - colorFilter = ColorFilter.tint(colorPalette().textSecondary), - modifier = Modifier - .clickable(onClick = onBottomIconButtonClick ) - .padding(all = 12.dp) - .size(24.dp) - ) - } - - - } -} - -fun Modifier.vertical(enabled: Boolean = true) = - if (enabled) - layout { measurable, constraints -> - val placeable = measurable.measure(constraints.copy(maxWidth = Int.MAX_VALUE)) - layout(placeable.height, placeable.width) { - placeable.place( - x = -(placeable.width / 2 - placeable.height / 2), - y = -(placeable.height / 2 - placeable.width / 2) - ) - } - } else this diff --git a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/components/NavigationRailTB.kt b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/components/NavigationRailTB.kt deleted file mode 100644 index 3805da2453..0000000000 --- a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/components/NavigationRailTB.kt +++ /dev/null @@ -1,248 +0,0 @@ -package it.fast4x.rimusic.ui.components - -import androidx.annotation.OptIn -import androidx.compose.animation.animateColor -import androidx.compose.animation.core.updateTransition -import androidx.compose.foundation.Image -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.horizontalScroll -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.ColumnScope -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.systemBars -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.text.BasicText -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.unit.dp -import androidx.media3.common.util.UnstableApi -import androidx.navigation.NavController -import it.fast4x.rimusic.R -import it.fast4x.rimusic.enums.NavigationBarPosition -import it.fast4x.rimusic.enums.NavigationBarType -import it.fast4x.rimusic.ui.styling.Dimensions -import it.fast4x.rimusic.ui.styling.favoritesIcon -import it.fast4x.rimusic.utils.semiBold -import me.knighthat.colorPalette -import me.knighthat.typography - -@OptIn(UnstableApi::class) -@Composable -inline fun NavigationRailTB( - navController: NavController, - topIconButtonId: Int, - noinline onTopIconButtonClick: () -> Unit, - showButton1: Boolean, - topIconButton2Id: Int, - noinline onTopIconButton2Click: () -> Unit, - showButton2: Boolean, - bottomIconButtonId: Int?, - noinline onBottomIconButtonClick: () -> Unit, - showBottomButton: Boolean, - tabIndex: Int, - crossinline onTabIndexChanged: (Int) -> Unit, - content: @Composable (ColumnScope.(@Composable (Int, String, Int) -> Unit) -> Unit), - hideTabs: Boolean, - modifier: Modifier = Modifier -) { - /* - val isLandscape = isLandscape - - val paddingValues = LocalPlayerAwareWindowInsets.current - .only(WindowInsetsSides.Vertical + WindowInsetsSides.Start).asPaddingValues() - - */ - - val density = LocalDensity.current - val windowsInsets = WindowInsets.systemBars - val bottomDp = with(density) { windowsInsets.getBottom(density).toDp() } - - //val localSheetState = LocalPlayerSheetState.current - /* - val bottomPadding = if (navigationBarPosition == NavigationBarPosition.Bottom) - if (localSheetState.isCollapsed) bottomDp + Dimensions.navigationBarHeight else bottomDp - else 0.dp - */ - val bottomPadding = if ( NavigationBarPosition.Bottom.isCurrent() ) bottomDp else 5.dp - - //val topPadding = if (navigationBarPosition == NavigationBarPosition.Top) 30.dp else 0.dp - val topPadding = 0.dp - Column( - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Bottom, - modifier = modifier - //.border(BorderStroke(1.dp, Color.Yellow)) - //.padding(top = 30.dp) - .padding(top = topPadding, bottom = bottomPadding) //bottom navigation - //.background(colorPalette.background0) - //.background(colorPalette.background1) - //.background(Color.Transparent) - ) { - - if (!hideTabs) - Column( - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.SpaceAround, - modifier = Modifier - .fillMaxWidth() - .height(Dimensions.navigationBarHeight-10.dp) - .padding(horizontal = 5.dp) - ) { - val transition = updateTransition(targetState = tabIndex, label = null) - - val listIconContent = mutableListOf<@Composable () -> Unit>() - - content { index, text, icon -> - - val textColor by transition.animateColor(label = "") { - if (it == index) - colorPalette().text - else - colorPalette().textDisabled - } - - val contentModifier = Modifier - .clip(RoundedCornerShape(12.dp)) - .clickable(onClick = { onTabIndexChanged(index) }) - - val itemContent: @Composable () -> Unit = { - Box( - modifier = contentModifier - ) { - if ( NavigationBarType.IconOnly.isCurrent() ) { - Image( - painter = painterResource(icon), - contentDescription = null, - colorFilter = ColorFilter.tint(textColor), - modifier = Modifier - .padding(all = 12.dp) - .size(20.dp) - ) - } else { - Column ( - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.SpaceAround, - modifier = Modifier - .padding(all = 5.dp) - .fillMaxSize() - ){ - Image( - painter = painterResource(icon), - contentDescription = null, - colorFilter = ColorFilter.tint(textColor), - modifier = Modifier - .size(Dimensions.navigationRailIconOffset * 3) - ) - Spacer(modifier = Modifier.height(5.dp)) - BasicText( - text = text, - style = TextStyle( - fontSize = typography().xs.semiBold.fontSize, - fontWeight = typography().xs.semiBold.fontWeight, - fontFamily = typography().xs.semiBold.fontFamily, - color = textColor, - ), - maxLines = 2, - overflow = TextOverflow.Ellipsis, - ) - } - } - } - } - - - listIconContent.add { itemContent() } - - } - - val scrollState = rememberScrollState() - val roundedCornerShape = if ( NavigationBarPosition.Bottom.isCurrent() ) - RoundedCornerShape(topStart = 12.dp, topEnd = 12.dp) - else RoundedCornerShape(bottomStart = 12.dp, bottomEnd = 12.dp) - Box( - modifier = Modifier - .fillMaxWidth() - .clip(roundedCornerShape) - .background(colorPalette().background1) - ) { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceEvenly, - modifier = Modifier - .fillMaxWidth() - .fillMaxSize() - .horizontalScroll(scrollState) - //.padding(horizontal = 5.dp) - //.padding(top = 4.dp, bottom = 4.dp) - //.padding(top = 1.dp, bottom = 1.dp) - - ) { - - //if (listIconContent.size > 1) //hide if only one icon is present - listIconContent.forEach { - it() - } - - if (showButton1) - Image( - painter = painterResource(topIconButtonId), - contentDescription = null, - colorFilter = ColorFilter.tint(colorPalette().favoritesIcon), //ColorFilter.tint(colorPalette().textSecondary), - modifier = Modifier - .clip(CircleShape) - .clickable(onClick = onTopIconButtonClick) - .padding(all = 4.dp) - .size(24.dp) - ) - - if (showButton2) - Image( - painter = painterResource(topIconButton2Id), - contentDescription = null, - colorFilter = ColorFilter.tint(colorPalette().textSecondary), - modifier = Modifier - .clip(CircleShape) - .clickable(onClick = onTopIconButton2Click) - .padding(all = 4.dp) - .size(24.dp) - ) - - - if (showBottomButton) - Image( - painter = painterResource(bottomIconButtonId ?: R.drawable.search), - contentDescription = null, - colorFilter = ColorFilter.tint(colorPalette().textSecondary), - modifier = Modifier - .clickable(onClick = onBottomIconButtonClick) - .padding(all = 4.dp) - .size(24.dp) - ) - } - } - - } - - - } -} diff --git a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/components/Scaffold.kt b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/components/Scaffold.kt index e810cd4857..de82d8ab9f 100644 --- a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/components/Scaffold.kt +++ b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/components/Scaffold.kt @@ -21,7 +21,6 @@ import androidx.compose.animation.shrinkOut import androidx.compose.animation.togetherWith import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize @@ -37,15 +36,17 @@ import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import androidx.navigation.NavController import it.fast4x.rimusic.R +import it.fast4x.rimusic.enums.NavRoutes import it.fast4x.rimusic.enums.NavigationBarPosition import it.fast4x.rimusic.enums.PlayerPosition import it.fast4x.rimusic.enums.TransitionEffect import it.fast4x.rimusic.enums.UiType -import it.fast4x.rimusic.ui.components.themed.AppBar import it.fast4x.rimusic.utils.playerPositionKey import it.fast4x.rimusic.utils.rememberPreference import it.fast4x.rimusic.utils.transitionEffectKey import me.knighthat.colorPalette +import me.knighthat.component.header.AppHeader +import me.knighthat.component.nav.VerticalNavigationBar @OptIn(ExperimentalMaterial3Api::class) @ExperimentalAnimationApi @@ -53,21 +54,21 @@ import me.knighthat.colorPalette fun Scaffold( navController: NavController, playerEssential: @Composable (() -> Unit)? = null, - topIconButtonId: Int, + topIconButtonId: Int = R.drawable.chevron_back, onTopIconButtonClick: () -> Unit, showButton1: Boolean = false, - topIconButton2Id: Int, + topIconButton2Id: Int = R.drawable.chevron_back, onTopIconButton2Click: () -> Unit, - showButton2: Boolean, + showButton2: Boolean = false, bottomIconButtonId: Int? = R.drawable.search, onBottomIconButtonClick: (() -> Unit)? = {}, showBottomButton: Boolean = false, hideTabs: Boolean = false, - tabIndex: Int, - onTabChanged: (Int) -> Unit, + tabIndex: Int = 0, + onTabChanged: (Int) -> Unit = {}, showTopActions: Boolean = false, - tabColumnContent: @Composable ColumnScope.(@Composable (Int, String, Int) -> Unit) -> Unit, - onHomeClick: () -> Unit, + tabColumnContent: @Composable (@Composable (Int, String, Int) -> Unit) -> Unit, + onHomeClick: () -> Unit = { navController.navigate( NavRoutes.home.name ) }, onSettingsClick: (() -> Unit)? = {}, onStatisticsClick: (() -> Unit)? = {}, onHistoryClick: (() -> Unit)? = {}, @@ -117,9 +118,7 @@ fun Scaffold( modifier = customModifier, containerColor = colorPalette().background0, topBar = { - if( UiType.RiMusic.isCurrent() ) { - AppBar(navController) - } + if( UiType.RiMusic.isCurrent() ) AppHeader( navController ).Draw() }, bottomBar = { @@ -157,26 +156,11 @@ fun Scaffold( .background( colorPalette().background0 ) .fillMaxSize() ) { - val navigationRail: @Composable () -> Unit = { - NavigationRail( - topIconButtonId = topIconButtonId, - onTopIconButtonClick = onTopIconButtonClick, - showButton1 = showButton1, - topIconButton2Id = topIconButton2Id, - onTopIconButton2Click = onTopIconButton2Click, - showButton2 = showButton2, - bottomIconButtonId = bottomIconButtonId, - onBottomIconButtonClick = onBottomIconButtonClick ?: {}, - showBottomButton = showBottomButton, - tabIndex = tabIndex, - onTabIndexChanged = onTabChanged, - content = tabColumnContent, - hideTabs = hideTabs - ) - } + val verticalNavBar = VerticalNavigationBar( tabIndex, onTabChanged, navController ) + verticalNavBar.add( tabColumnContent ) if ( NavigationBarPosition.Left.isCurrent() ) - navigationRail() + verticalNavBar.Draw() val topPadding = if ( UiType.ViMusic.isCurrent() ) 30.dp else 0.dp @@ -241,7 +225,7 @@ fun Scaffold( ) if ( NavigationBarPosition.Right.isCurrent() ) - navigationRail() + verticalNavBar.Draw() } //** diff --git a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/components/ScaffoldTB.kt b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/components/ScaffoldTB.kt index 909ee4a115..ec48c2e77d 100644 --- a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/components/ScaffoldTB.kt +++ b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/components/ScaffoldTB.kt @@ -23,7 +23,6 @@ import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.WindowInsetsSides import androidx.compose.foundation.layout.asPaddingValues @@ -46,11 +45,12 @@ import androidx.navigation.NavController import it.fast4x.rimusic.enums.NavigationBarPosition import it.fast4x.rimusic.enums.PlayerPosition import it.fast4x.rimusic.enums.TransitionEffect -import it.fast4x.rimusic.ui.components.themed.AppBar import it.fast4x.rimusic.utils.playerPositionKey import it.fast4x.rimusic.utils.rememberPreference import it.fast4x.rimusic.utils.transitionEffectKey import me.knighthat.colorPalette +import me.knighthat.component.header.AppHeader +import me.knighthat.component.nav.HorizontalNavigationBar @OptIn(ExperimentalMaterial3Api::class) @@ -77,28 +77,12 @@ fun ScaffoldTB( onSearchClick: (() -> Unit)?, tabIndex: Int, onTabChanged: (Int) -> Unit, - tabColumnContent: @Composable (ColumnScope.(@Composable (Int, String, Int) -> Unit) -> Unit), + tabColumnContent: @Composable (@Composable (Int, String, Int) -> Unit) -> Unit, modifier: Modifier = Modifier, content: @Composable AnimatedVisibilityScope.(Int) -> Unit ) { - val navigationRailTB: @Composable () -> Unit = { - NavigationRailTB( - navController = navController, - topIconButtonId = topIconButtonId, - onTopIconButtonClick = onTopIconButtonClick, - showButton1 = showButton1, - topIconButton2Id = topIconButton2Id, - onTopIconButton2Click = onTopIconButton2Click, - showButton2 = showButton2, - bottomIconButtonId = bottomIconButtonId, - onBottomIconButtonClick = onBottomIconButtonClick ?: {}, - showBottomButton = showBottomButton, - tabIndex = tabIndex, - onTabIndexChanged = onTabChanged, - content = tabColumnContent, - hideTabs = hideTabs - ) - } + val horizontalNavBar = HorizontalNavigationBar( tabIndex, onTabChanged, navController, modifier ) + horizontalNavBar.add( tabColumnContent ) //val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior() val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() @@ -114,10 +98,10 @@ fun ScaffoldTB( verticalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth() ) { - AppBar(navController) + AppHeader( navController ).Draw() if ( NavigationBarPosition.Top.isCurrent() ) - navigationRailTB() + horizontalNavBar.Draw() /* if (playerEssential != null && playerPosition == PlayerPosition.Top) { @@ -166,14 +150,17 @@ fun ScaffoldTB( */ if ( NavigationBarPosition.Bottom.isCurrent() ) - navigationRailTB() - + horizontalNavBar.Draw() //} } ) { val modifierBoxPadding = if ( NavigationBarPosition.Top.isCurrent() ) + Modifier + .padding(it) + .fillMaxSize() + else Modifier .padding(it) .padding( @@ -182,10 +169,6 @@ fun ScaffoldTB( .asPaddingValues() ) .fillMaxSize() - else - Modifier - .padding(it) - .fillMaxSize() Box( modifier = modifierBoxPadding diff --git a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/components/SimpleScaffold.kt b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/components/SimpleScaffold.kt deleted file mode 100644 index 8284072497..0000000000 --- a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/components/SimpleScaffold.kt +++ /dev/null @@ -1,62 +0,0 @@ -package it.fast4x.rimusic.ui.components - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Surface -import androidx.compose.material3.TopAppBarDefaults -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.input.nestedscroll.nestedScroll -import androidx.navigation.NavController -import it.fast4x.rimusic.enums.NavigationBarPosition -import it.fast4x.rimusic.enums.UiType -import it.fast4x.rimusic.ui.components.themed.AppBar -import it.fast4x.rimusic.ui.styling.Dimensions -import me.knighthat.colorPalette - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun SimpleScaffold( - navController: NavController, - content: @Composable () -> Unit -) { - val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() - val customModifier = if( UiType.RiMusic.isCurrent()) - Modifier.nestedScroll(scrollBehavior.nestedScrollConnection) - else Modifier - - - androidx.compose.material3.Scaffold( - modifier = customModifier, - containerColor = colorPalette().background0, - topBar = { - if( UiType.RiMusic.isCurrent()) { - AppBar(navController) - } - }, - bottomBar = {} - ) { paddingValues -> - Row( - modifier = Modifier - .padding(paddingValues) - .background(colorPalette().background0) - .fillMaxSize() - ) { - - Surface( - modifier = Modifier - .fillMaxWidth( - if( NavigationBarPosition.Right.isCurrent() ) - Dimensions.contentWidthRightBar - else - 1f - ), - content = content - ) - } - } -} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/components/themed/AppBar.kt b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/components/themed/AppBar.kt deleted file mode 100644 index bc8fe2278c..0000000000 --- a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/components/themed/AppBar.kt +++ /dev/null @@ -1,329 +0,0 @@ -package it.fast4x.rimusic.ui.components.themed - -import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.Image -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.combinedClickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.text.BasicText -import androidx.compose.material3.DropdownMenu -import androidx.compose.material3.DropdownMenuItem -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.HorizontalDivider -import androidx.compose.material3.Icon -import androidx.compose.material3.Text -import androidx.compose.material3.TopAppBar -import androidx.compose.material3.TopAppBarColors -import androidx.compose.material3.TopAppBarDefaults -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.input.nestedscroll.nestedScroll -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.res.vectorResource -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.unit.dp -import androidx.lifecycle.Lifecycle -import androidx.navigation.NavController -import it.fast4x.rimusic.R -import it.fast4x.rimusic.enums.ColorPaletteMode -import it.fast4x.rimusic.enums.NavRoutes -import it.fast4x.rimusic.enums.UiType -import it.fast4x.rimusic.extensions.games.pacman.Pacman -import it.fast4x.rimusic.ui.styling.favoritesIcon -import it.fast4x.rimusic.utils.colorPaletteModeKey -import it.fast4x.rimusic.utils.getCurrentRoute -import it.fast4x.rimusic.utils.logDebugEnabledKey -import it.fast4x.rimusic.utils.menuItemColors -import it.fast4x.rimusic.utils.parentalControlEnabledKey -import it.fast4x.rimusic.utils.rememberPreference -import it.fast4x.rimusic.utils.semiBold -import me.knighthat.colorPalette -import me.knighthat.typography - - -@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class) -@Composable -fun AppBar( - navController: NavController -) { - val colorPaletteMode by rememberPreference(colorPaletteModeKey, ColorPaletteMode.Dark) - var expanded by remember { mutableStateOf(false) } - var countForReveal by remember { mutableStateOf(0) } - var showGames by remember { mutableStateOf(false) } - //val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior() - val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() - val customModifier = if( UiType.RiMusic.isCurrent() ) - Modifier.nestedScroll(scrollBehavior.nestedScrollConnection) - else Modifier - - val context = LocalContext.current - val parentalControlEnabled by rememberPreference(parentalControlEnabledKey, false) - - if (showGames) { - Pacman() - } - - val appBar = - TopAppBar( - navigationIcon = { - //val currentRoute = navController.currentBackStackEntry?.destination?.route - //println("navController current destination and route ${navController.currentDestination} $currentRoute") - if (getCurrentRoute(navController) != NavRoutes.home.name) - androidx.compose.material3.IconButton( - onClick = { - if (navController.currentBackStackEntry?.lifecycle?.currentState == Lifecycle.State.RESUMED) - navController.popBackStack() - } - ) { - Icon( - imageVector = ImageVector.vectorResource(R.drawable.chevron_back), - tint = colorPalette().favoritesIcon, - contentDescription = null, - modifier = Modifier.size(24.dp) - ) - } - }, - title = { - Row( - horizontalArrangement = Arrangement.spacedBy(5.dp), - verticalAlignment = Alignment.CenterVertically - ) { - Image( - painter = painterResource(R.drawable.app_icon), - colorFilter = ColorFilter.tint(colorPalette().favoritesIcon), - contentDescription = null, - modifier = Modifier - .size(36.dp) - .combinedClickable( - onClick = { - countForReveal++ - - if (countForReveal == 10) { - countForReveal = 0 - navController.navigate(NavRoutes.gamePacman.name) - return@combinedClickable - } - - val message: String = when(countForReveal) { - 3 -> "Do you like clicking? Then continue..." - 6 -> "Okay, you’re looking for something, keep..." - 9 -> "You are a number one, click and enjoy the surprise" - else -> "" - } - SmartMessage( - message, - durationLong = true, - context = context - ) - }, - onLongClick = { - SmartMessage( - "You are a number one, click and enjoy the surprise", - durationLong = true, context = context - ) - navController.navigate(NavRoutes.gameSnake.name) - } - ) - ) - Image( - painter = painterResource(R.drawable.app_logo_text), - colorFilter = ColorFilter.tint( - when(colorPaletteMode) { - ColorPaletteMode.Light, ColorPaletteMode.System -> colorPalette().text - else -> Color.White - } - ), - contentDescription = null, - modifier = Modifier - .size(100.dp) - .clickable { - if (navController.currentDestination?.route != NavRoutes.home.name) - navController.navigate(NavRoutes.home.name) - } - ) - if (parentalControlEnabled) - Image( - painter = painterResource(R.drawable.shield_checkmark), - colorFilter = ColorFilter.tint( - when(colorPaletteMode) { - ColorPaletteMode.Light, ColorPaletteMode.System -> colorPalette().text - else -> Color.White - } - ), - contentDescription = null, - modifier = Modifier.size(20.dp) - ) - - val logDebugEnabled by rememberPreference(logDebugEnabledKey, false) - - if (logDebugEnabled) - BasicText( - text = stringResource(R.string.info_debug_mode_enabled), - style = TextStyle( - fontSize = typography().xxs.semiBold.fontSize, - fontWeight = typography().xxs.semiBold.fontWeight, - fontFamily = typography().xxs.semiBold.fontFamily, - color = colorPalette().red - ), - modifier = Modifier - .clickable { - SmartMessage(context.resources.getString(R.string.info_debug_mode_is_enabled), durationLong = true, context = context) - navController.navigate(NavRoutes.settings.name) - } - ) - /* - BasicText( - text = "Music", - style = TextStyle( - fontSize = typography.xxxl.semiBold.fontSize, - //fontWeight = typography.xxl.semiBold.fontWeight, - fontFamily = typography.xxxl.semiBold.fontFamily, - color = colorPalette.text - ), - modifier = Modifier - .clickable { - //onHomeClick() - if (navController.currentDestination?.route != NavRoutes.home.name) - navController.navigate(NavRoutes.home.name) - } - ) - */ - } - }, - actions = { - //if (showTopActions == true) { - //println("mediaItem nav ${navController.currentDestination?.route}") - androidx.compose.material3.IconButton( - //enabled = navController.currentDestination?.route?.startsWith(NavRoutes.search.name) == false, - onClick = { - //if (onSearchClick != null) { - //onSearchClick() - navController.navigate(NavRoutes.search.name) - //} - } - ) { - Icon( - imageVector = ImageVector.vectorResource(R.drawable.search), - contentDescription = null, - modifier = Modifier.size(24.dp) - ) - } - androidx.compose.material3.IconButton(onClick = { expanded = !expanded }) { - Icon( - imageVector = ImageVector.vectorResource(R.drawable.burger), - contentDescription = null, - modifier = Modifier.size(24.dp) - ) - } - - DropdownMenu( - expanded = expanded, - onDismissRequest = { expanded = false }, - containerColor = Color.Transparent, - modifier = Modifier.background( colorPalette().background0.copy(0.90f)) - ) { - DropdownMenuItem( - enabled = navController.currentDestination?.route != NavRoutes.history.name, - colors = menuItemColors(), - text = { Text(stringResource(R.string.history)) }, - leadingIcon = { - Icon( - imageVector = ImageVector.vectorResource(R.drawable.history), - contentDescription = null, - modifier = Modifier.size(24.dp) - ) - }, - onClick = { - expanded = false - //if (onHistoryClick != null) { - //onHistoryClick() - navController.navigate(NavRoutes.history.name) - //} - } - ) - DropdownMenuItem( - enabled = navController.currentDestination?.route != NavRoutes.statistics.name, - colors = menuItemColors(), - text = { Text(stringResource(R.string.statistics)) }, - leadingIcon = { - Icon( - imageVector = ImageVector.vectorResource(R.drawable.stats_chart), - contentDescription = null, - modifier = Modifier.size(24.dp) - ) - }, - onClick = { - expanded = false - //if (onStatisticsClick != null) { - //onStatisticsClick() - navController.navigate(NavRoutes.statistics.name) - //} - } - ) - HorizontalDivider() - DropdownMenuItem( - enabled = navController.currentDestination?.route != NavRoutes.settings.name, - colors = menuItemColors(), - text = { Text(stringResource(R.string.settings)) }, - leadingIcon = { - Icon( - imageVector = ImageVector.vectorResource(R.drawable.settings), - contentDescription = null, - modifier = Modifier.size(24.dp) - ) - }, - onClick = { - expanded = false - //if (onSettingsClick != null) { - //onSettingsClick() - navController.navigate(NavRoutes.settings.name) - //} - } - ) - } - - /* - IconButton(onClick = { }) { - Icon( - imageVector = ImageVector.vectorResource(R.drawable.stats_chart), - contentDescription = null, - modifier = Modifier.size(24.dp) - ) - } - if (onSettingsClick != null) { - IconButton(onClick = onSettingsClick) { - Icon( - imageVector = ImageVector.vectorResource(R.drawable.settings), - contentDescription = null, - modifier = Modifier.size(24.dp) - ) - } - } - */ - //} - }, - scrollBehavior = scrollBehavior, - colors = TopAppBarColors( - containerColor = colorPalette().background0, - titleContentColor = colorPalette().text, - scrolledContainerColor = colorPalette().background0, - navigationIconContentColor = colorPalette().background0, - actionIconContentColor = colorPalette().text - ) - ) - - return appBar -} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/album/AlbumScreenWithoutNavBar.kt b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/album/AlbumScreenWithoutNavBar.kt index e7937e7efb..8d762c4cfd 100644 --- a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/album/AlbumScreenWithoutNavBar.kt +++ b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/album/AlbumScreenWithoutNavBar.kt @@ -68,7 +68,6 @@ import it.fast4x.rimusic.enums.UiType import it.fast4x.rimusic.models.Album import it.fast4x.rimusic.models.SongAlbumMap import it.fast4x.rimusic.query -import it.fast4x.rimusic.ui.components.themed.AppBar import it.fast4x.rimusic.ui.components.themed.Header import it.fast4x.rimusic.ui.components.themed.HeaderIconButton import it.fast4x.rimusic.ui.components.themed.HeaderPlaceholder @@ -87,6 +86,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.combine import kotlinx.coroutines.withContext import me.knighthat.colorPalette +import me.knighthat.component.header.AppHeader @ExperimentalMaterialApi @@ -286,7 +286,7 @@ fun AlbumScreenWithoutNavBar( containerColor = colorPalette().background0, topBar = { if( UiType.RiMusic.isCurrent() ) - AppBar(navController) + AppHeader( navController ).Draw() } ) { //** diff --git a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/builtinplaylist/BuiltInPlaylistScreen.kt b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/builtinplaylist/BuiltInPlaylistScreen.kt index b2dd04aef2..f95841d47a 100644 --- a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/builtinplaylist/BuiltInPlaylistScreen.kt +++ b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/builtinplaylist/BuiltInPlaylistScreen.kt @@ -21,8 +21,6 @@ import it.fast4x.rimusic.enums.BuiltInPlaylist import it.fast4x.rimusic.enums.DeviceLists import it.fast4x.rimusic.enums.MaxTopPlaylistItems import it.fast4x.rimusic.enums.NavRoutes -import it.fast4x.rimusic.enums.UiType -import it.fast4x.rimusic.ui.components.Scaffold import it.fast4x.rimusic.ui.screens.globalRoutes import it.fast4x.rimusic.ui.screens.ondevice.DeviceListSongs import it.fast4x.rimusic.utils.MaxTopPlaylistItemsKey @@ -32,8 +30,7 @@ import it.fast4x.rimusic.utils.showDownloadedPlaylistKey import it.fast4x.rimusic.utils.showFavoritesPlaylistKey import it.fast4x.rimusic.utils.showMyTopPlaylistKey import it.fast4x.rimusic.utils.showOnDevicePlaylistKey -import it.fast4x.rimusic.utils.showSearchTabKey -import it.fast4x.rimusic.utils.showStatsInNavbarKey +import me.knighthat.Skeleton @ExperimentalMaterialApi @ExperimentalTextApi @@ -70,122 +67,53 @@ fun BuiltInPlaylistScreen( val showMyTopPlaylist by rememberPreference(showMyTopPlaylistKey, true) val showDownloadedPlaylist by rememberPreference(showDownloadedPlaylistKey, true) val showOnDevicePlaylist by rememberPreference(showOnDevicePlaylistKey, true) - val showSearchTab by rememberPreference(showSearchTabKey, false) - val showStatsInNavbar by rememberPreference(showStatsInNavbarKey, false) PersistMapCleanup(tagPrefix = "${builtInPlaylist.name}/") RouteHandler(listenToGlobalEmitter = true) { globalRoutes() - /* - searchResultRoute { query -> - SearchResultScreen( - navController = navController, - query = query, - onSearchAgain = { - searchRoute(query) - } - ) - } - - searchRoute { initialTextInput -> - val context = LocalContext.current - SearchScreen( - navController = navController, - initialTextInput = initialTextInput, - onSearch = { query -> - pop() - searchResultRoute(query) - - if (!context.preferences.getBoolean(pauseSearchHistoryKey, false)) { - query { - Database.insert(SearchQuery(query = query)) - } - } - }, - onViewPlaylist = {} - ) - } -*/ host { - Scaffold( - navController = navController, - playerEssential = playerEssential, - topIconButtonId = R.drawable.chevron_back, - onTopIconButtonClick = pop, - showButton1 = UiType.RiMusic.isNotCurrent(), - topIconButton2Id = R.drawable.chevron_back, - onTopIconButton2Click = pop, - showButton2 = if( UiType.RiMusic.isCurrent() ) false else showStatsInNavbar, - showBottomButton = if( UiType.RiMusic.isCurrent() ) false else showSearchTab, - onBottomIconButtonClick = { - //searchRoute("") - navController.navigate(NavRoutes.search.name) - }, - tabIndex = tabIndex, - onTabChanged = onTabIndexChanged, - onHomeClick = { - //homeRoute() - navController.navigate(NavRoutes.home.name) - }, - tabColumnContent = { Item -> + Skeleton( + navController, + tabIndex, + onTabIndexChanged, + playerEssential, + navBarContent = { item -> if(showFavoritesPlaylist) - Item(0, stringResource(R.string.favorites), R.drawable.heart) + item(0, stringResource(R.string.favorites), R.drawable.heart) if(showCachedPlaylist) - Item(1, stringResource(R.string.cached), R.drawable.sync) + item(1, stringResource(R.string.cached), R.drawable.sync) if(showDownloadedPlaylist) - Item(2, stringResource(R.string.downloaded), R.drawable.downloaded) + item(2, stringResource(R.string.downloaded), R.drawable.downloaded) if(showMyTopPlaylist) - Item(3, stringResource(R.string.my_playlist_top).format(maxTopPlaylistItems.number) , R.drawable.trending) + item(3, stringResource(R.string.my_playlist_top).format(maxTopPlaylistItems.number) , R.drawable.trending) if(showOnDevicePlaylist) - Item(4, stringResource(R.string.on_device), R.drawable.musical_notes) + item(4, stringResource(R.string.on_device), R.drawable.musical_notes) } ) { currentTabIndex -> saveableStateHolder.SaveableStateProvider(key = currentTabIndex) { - when (currentTabIndex) { - 0 -> BuiltInPlaylistSongs( - navController = navController, - builtInPlaylist = BuiltInPlaylist.Favorites, - onSearchClick = { - //searchRoute("") - navController.navigate(NavRoutes.search.name) - } - ) - 1 -> BuiltInPlaylistSongs( - navController = navController, - builtInPlaylist = BuiltInPlaylist.Offline, - onSearchClick = { - //searchRoute("") - navController.navigate(NavRoutes.search.name) - } - ) - 2 -> BuiltInPlaylistSongs( - navController = navController, - builtInPlaylist = BuiltInPlaylist.Downloaded, - onSearchClick = { - //searchRoute("") - navController.navigate(NavRoutes.search.name) - } - ) - 3 -> BuiltInPlaylistSongs( + val playlist: BuiltInPlaylist = + when( currentTabIndex ) { + 0 -> BuiltInPlaylist.Favorites + 1 -> BuiltInPlaylist.Offline + 2 -> BuiltInPlaylist.Downloaded + 3 -> BuiltInPlaylist.Top + else -> BuiltInPlaylist.OnDevice + } + + if( playlist == BuiltInPlaylist.OnDevice ) + DeviceListSongs( navController = navController, - builtInPlaylist = BuiltInPlaylist.Top, - onSearchClick = { - //searchRoute("") - navController.navigate(NavRoutes.search.name) - } + deviceLists = DeviceLists.LocalSongs, + onSearchClick = { navController.navigate(NavRoutes.search.name) } ) - 4 -> DeviceListSongs( + else + BuiltInPlaylistSongs( navController = navController, - deviceLists = DeviceLists.LocalSongs, - onSearchClick = { - //searchRoute("") - navController.navigate(NavRoutes.search.name) - } + builtInPlaylist = playlist, + onSearchClick = { navController.navigate(NavRoutes.search.name) } ) - - } } } } diff --git a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/history/HistoryScreen.kt b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/history/HistoryScreen.kt index 6514130026..0680ee1c5d 100644 --- a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/history/HistoryScreen.kt +++ b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/history/HistoryScreen.kt @@ -13,10 +13,8 @@ import androidx.navigation.NavController import it.fast4x.compose.persist.PersistMapCleanup import it.fast4x.compose.routing.RouteHandler import it.fast4x.rimusic.R -import it.fast4x.rimusic.enums.UiType -import it.fast4x.rimusic.ui.components.Scaffold import it.fast4x.rimusic.ui.screens.globalRoutes -import it.fast4x.rimusic.ui.screens.homeRoute +import me.knighthat.Skeleton @ExperimentalMaterialApi @ExperimentalTextApi @@ -37,26 +35,10 @@ fun HistoryScreen( globalRoutes() host { - Scaffold( - navController = navController, - playerEssential = playerEssential, - topIconButtonId = R.drawable.chevron_back, - onTopIconButtonClick = pop, - showButton1 = UiType.RiMusic.isNotCurrent(), - topIconButton2Id = R.drawable.chevron_back, - onTopIconButton2Click = pop, - showButton2 = false, - tabIndex = 0, - onTabChanged = { }, - onHomeClick = { homeRoute() }, - /* - onSettingsClick = { settingsRoute() }, - onStatisticsClick = { statisticsTypeRoute(StatisticsType.Today) }, - onHistoryClick = { historyRoute() }, - onSearchClick = { searchRoute("") }, - - */ - tabColumnContent = { item -> + Skeleton( + navController, + mediaPlayer = playerEssential, + navBarContent = { item -> item(0, stringResource(R.string.history), R.drawable.history) } ) { currentTabIndex -> diff --git a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/home/HomeScreen.kt b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/home/HomeScreen.kt index 79ac428f14..ca31bf4128 100644 --- a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/home/HomeScreen.kt +++ b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/home/HomeScreen.kt @@ -29,9 +29,7 @@ import it.fast4x.rimusic.R import it.fast4x.rimusic.enums.CheckUpdateState import it.fast4x.rimusic.enums.HomeScreenTabs import it.fast4x.rimusic.enums.NavRoutes -import it.fast4x.rimusic.enums.UiType import it.fast4x.rimusic.models.toUiMood -import it.fast4x.rimusic.ui.components.Scaffold import it.fast4x.rimusic.ui.components.themed.ConfirmationDialog import it.fast4x.rimusic.ui.screens.globalRoutes import it.fast4x.rimusic.ui.screens.searchResultRoute @@ -46,6 +44,7 @@ import it.fast4x.rimusic.utils.preferences import it.fast4x.rimusic.utils.rememberPreference import it.fast4x.rimusic.utils.showSearchTabKey import it.fast4x.rimusic.utils.showStatsInNavbarKey +import me.knighthat.Skeleton @OptIn(ExperimentalMaterial3Api::class) @@ -205,47 +204,12 @@ fun HomeScreen( if (!enableQuickPicksPage && tabIndex==0) tabIndex = 1 - Scaffold( - navController = navController, - playerEssential = playerEssential, - topIconButtonId = R.drawable.settings, - onTopIconButtonClick = { - //settingsRoute() - navController.navigate(NavRoutes.settings.name) - }, - showButton1 = UiType.RiMusic.isNotCurrent(), - topIconButton2Id = R.drawable.stats_chart, - onTopIconButton2Click = { - //statisticsTypeRoute(StatisticsType.Today) - navController.navigate(NavRoutes.statistics.name) - }, - showButton2 = if( UiType.RiMusic.isCurrent() ) false else showStatsInNavbar, - showBottomButton = if( UiType.RiMusic.isCurrent() ) false else showSearchTab, - onBottomIconButtonClick = { - //searchRoute("") - navController.navigate(NavRoutes.search.name) - }, - tabIndex = tabIndex, - onTabChanged = onTabChanged, - showTopActions = true, - onHomeClick = {}, - onSettingsClick = { - //settingsRoute() - navController.navigate(NavRoutes.settings.name) - }, - onStatisticsClick = { - //statisticsTypeRoute(StatisticsType.Today) - navController.navigate(NavRoutes.statistics.name) - }, - onHistoryClick = { - //historyRoute() - navController.navigate(NavRoutes.history.name) - }, - onSearchClick = { - //searchRoute("") - navController.navigate(NavRoutes.search.name) - }, - tabColumnContent = { Item -> + Skeleton( + navController, + tabIndex, + onTabChanged, + playerEssential, + navBarContent = { Item -> if (enableQuickPicksPage) Item(0, stringResource(R.string.quick_picks), R.drawable.sparkles) Item(1, stringResource(R.string.songs), R.drawable.musical_notes) @@ -329,13 +293,6 @@ fun HomeScreen( ) 4 -> HomeLibraryModern( - /* - onBuiltInPlaylist = { - //builtInPlaylistRoute(it) - navController.navigate(route = "${NavRoutes.builtInPlaylist.name}/${it.ordinal}") - }, - - */ onPlaylistClick = { //localPlaylistRoute(it.id) navController.navigate(route = "${NavRoutes.localPlaylist.name}/${it.id}") @@ -344,49 +301,192 @@ fun HomeScreen( //searchRoute("") navController.navigate(NavRoutes.search.name) }, - /* - onDeviceListSongsClick = { - //deviceListSongRoute("") - navController.navigate(NavRoutes.onDevice.name) - }, - onStatisticsClick = { - //statisticsTypeRoute(StatisticsType.Today) - navController.navigate(NavRoutes.statistics.name) - }, - - */ onSettingsClick = { //settingsRoute() navController.navigate(NavRoutes.settings.name) } ) - /* - 5 -> HomeDiscovery( - onMoodClick = { mood -> moodRoute(mood.toUiMood()) }, - onNewReleaseAlbumClick = { albumRoute(it) }, - onSearchClick = { searchRoute("") } - ) - */ - - //6 -> HomeEqualizer( ) - /* - 5 -> HomeStatistics( - onStatisticsType = { statisticsTypeRoute(it)}, - onBuiltInPlaylist = { builtInPlaylistRoute(it) }, - onPlaylistClick = { localPlaylistRoute(it.id) }, - onSearchClick = { searchRoute("") } - ) - */ - - /* - 6 -> HomeSearch( - onSearchType = { searchTypeRoute(it) } - ) - */ } } } + +// Scaffold( +// navController = navController, +// playerEssential = playerEssential, +// topIconButtonId = R.drawable.settings, +// onTopIconButtonClick = { +// //settingsRoute() +// navController.navigate(NavRoutes.settings.name) +// }, +// showButton1 = uiType() != UiType.RiMusic, +// topIconButton2Id = R.drawable.stats_chart, +// onTopIconButton2Click = { +// //statisticsTypeRoute(StatisticsType.Today) +// navController.navigate(NavRoutes.statistics.name) +// }, +// showButton2 = if( uiType() == UiType.RiMusic ) false else showStatsInNavbar, +// showBottomButton = if( uiType() == UiType.RiMusic ) false else showSearchTab, +// onBottomIconButtonClick = { +// //searchRoute("") +// navController.navigate(NavRoutes.search.name) +// }, +// tabIndex = tabIndex, +// onTabChanged = onTabChanged, +// showTopActions = true, +// onHomeClick = {}, +// onSettingsClick = { +// //settingsRoute() +// navController.navigate(NavRoutes.settings.name) +// }, +// onStatisticsClick = { +// //statisticsTypeRoute(StatisticsType.Today) +// navController.navigate(NavRoutes.statistics.name) +// }, +// onHistoryClick = { +// //historyRoute() +// navController.navigate(NavRoutes.history.name) +// }, +// onSearchClick = { +// //searchRoute("") +// navController.navigate(NavRoutes.search.name) +// }, +// tabColumnContent = { Item -> +// if (enableQuickPicksPage) +// Item(0, stringResource(R.string.quick_picks), R.drawable.sparkles) +// Item(1, stringResource(R.string.songs), R.drawable.musical_notes) +// Item(2, stringResource(R.string.artists), R.drawable.artists) +// Item(3, stringResource(R.string.albums), R.drawable.album) +// Item(4, stringResource(R.string.playlists), R.drawable.library) +// } +// ) { currentTabIndex -> +// saveableStateHolder.SaveableStateProvider(key = currentTabIndex) { +// when (currentTabIndex) { +// 0 -> QuickPicksModern( +// onAlbumClick = { +// //albumRoute(it) +// navController.navigate(route = "${NavRoutes.album.name}/$it") +// }, +// onArtistClick = { +// //artistRoute(it) +// navController.navigate(route = "${NavRoutes.artist.name}/$it") +// }, +// onPlaylistClick = { +// //playlistRoute(it) +// navController.navigate(route = "${NavRoutes.playlist.name}/$it") +// }, +// onSearchClick = { +// //searchRoute("") +// navController.navigate(NavRoutes.search.name) +// }, +// onMoodClick = { mood -> +// //moodRoute(mood.toUiMood()) +// navController.currentBackStackEntry?.savedStateHandle?.set("mood", mood.toUiMood()) +// navController.navigate(NavRoutes.mood.name) +// }, +// onSettingsClick = { +// //settingsRoute() +// navController.navigate(NavRoutes.settings.name) +// }, +// navController = navController +// +// ) +// +// 1 -> HomeSongsModern( +// navController = navController, +// onSearchClick = { +// //searchRoute("") +// navController.navigate(NavRoutes.search.name) +// }, +// onSettingsClick = { +// //settingsRoute() +// navController.navigate(NavRoutes.settings.name) +// } +// ) +// +// 2 -> HomeArtistsModern( +// onArtistClick = { +// //artistRoute(it.id) +// navController.navigate(route = "${NavRoutes.artist.name}/${it.id}") +// }, +// onSearchClick = { +// //searchRoute("") +// navController.navigate(NavRoutes.search.name) +// }, +// onSettingsClick = { +// //settingsRoute() +// navController.navigate(NavRoutes.settings.name) +// } +// ) +// +// 3 -> HomeAlbumsModern( +// onAlbumClick = { +// //albumRoute(it.id) +// navController.navigate(route = "${NavRoutes.album.name}/${it.id}") +// }, +// onSearchClick = { +// //searchRoute("") +// navController.navigate(NavRoutes.search.name) +// }, +// onSettingsClick = { +// //settingsRoute() +// navController.navigate(NavRoutes.settings.name) +// } +// ) +// +// 4 -> HomeLibraryModern( +// onBuiltInPlaylist = { +// //builtInPlaylistRoute(it) +// navController.navigate(route = "${NavRoutes.builtInPlaylist.name}/${it.ordinal}") +// }, +// onPlaylistClick = { +// //localPlaylistRoute(it.id) +// navController.navigate(route = "${NavRoutes.localPlaylist.name}/${it.id}") +// }, +// onSearchClick = { +// //searchRoute("") +// navController.navigate(NavRoutes.search.name) +// }, +// onDeviceListSongsClick = { +// //deviceListSongRoute("") +// navController.navigate(NavRoutes.onDevice.name) +// }, +// onStatisticsClick = { +// //statisticsTypeRoute(StatisticsType.Today) +// navController.navigate(NavRoutes.statistics.name) +// }, +// onSettingsClick = { +// //settingsRoute() +// navController.navigate(NavRoutes.settings.name) +// } +// +// ) +// /* +// 5 -> HomeDiscovery( +// onMoodClick = { mood -> moodRoute(mood.toUiMood()) }, +// onNewReleaseAlbumClick = { albumRoute(it) }, +// onSearchClick = { searchRoute("") } +// ) +// */ +// +// //6 -> HomeEqualizer( ) +// /* +// 5 -> HomeStatistics( +// onStatisticsType = { statisticsTypeRoute(it)}, +// onBuiltInPlaylist = { builtInPlaylistRoute(it) }, +// onPlaylistClick = { localPlaylistRoute(it.id) }, +// onSearchClick = { searchRoute("") } +// ) +// */ +// +// /* +// 6 -> HomeSearch( +// onSearchType = { searchTypeRoute(it) } +// ) +// */ +// } +// } +// } } } diff --git a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/localplaylist/LocalPlaylistScreenWithoutNavBar.kt b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/localplaylist/LocalPlaylistScreenWithoutNavBar.kt index f66afbf8cb..b0d7000688 100644 --- a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/localplaylist/LocalPlaylistScreenWithoutNavBar.kt +++ b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/localplaylist/LocalPlaylistScreenWithoutNavBar.kt @@ -43,12 +43,12 @@ import it.fast4x.compose.routing.RouteHandler import it.fast4x.rimusic.enums.PlayerPosition import it.fast4x.rimusic.enums.TransitionEffect import it.fast4x.rimusic.enums.UiType -import it.fast4x.rimusic.ui.components.themed.AppBar import it.fast4x.rimusic.ui.screens.globalRoutes import it.fast4x.rimusic.utils.playerPositionKey import it.fast4x.rimusic.utils.rememberPreference import it.fast4x.rimusic.utils.transitionEffectKey import me.knighthat.colorPalette +import me.knighthat.component.header.AppHeader @OptIn(KotlinCsvExperimental::class) @ExperimentalMaterialApi @@ -77,7 +77,7 @@ fun LocalPlaylistScreenWithoutNavBar( containerColor = colorPalette().background0, topBar = { if( UiType.RiMusic.isCurrent() ) - AppBar(navController) + AppHeader( navController ).Draw() } ) { //** diff --git a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/mood/MoodScreen.kt b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/mood/MoodScreen.kt index fc5cabb8bb..b9da6b53c7 100644 --- a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/mood/MoodScreen.kt +++ b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/mood/MoodScreen.kt @@ -13,11 +13,9 @@ import androidx.navigation.NavController import it.fast4x.compose.persist.PersistMapCleanup import it.fast4x.compose.routing.RouteHandler import it.fast4x.rimusic.R -import it.fast4x.rimusic.enums.NavRoutes -import it.fast4x.rimusic.enums.UiType import it.fast4x.rimusic.models.Mood -import it.fast4x.rimusic.ui.components.Scaffold import it.fast4x.rimusic.ui.screens.globalRoutes +import me.knighthat.Skeleton @ExperimentalMaterialApi @ExperimentalTextApi @@ -38,24 +36,12 @@ fun MoodScreen( RouteHandler(listenToGlobalEmitter = true) { globalRoutes() host { - Scaffold( - navController = navController, - playerEssential = playerEssential, - topIconButtonId = R.drawable.chevron_back, - showButton1 = UiType.RiMusic.isNotCurrent(), - onTopIconButtonClick = pop, - topIconButton2Id = R.drawable.chevron_back, - onTopIconButton2Click = pop, - showButton2 = false, - tabIndex = 0, - onTabChanged = { }, - onHomeClick = { - //homeRoute() - navController.navigate(NavRoutes.home.name) - }, - tabColumnContent = { item -> + Skeleton( + navController, + navBarContent = { item -> item(0, stringResource(R.string.mood), R.drawable.album) - } + }, + mediaPlayer = playerEssential ) { currentTabIndex -> saveableStateHolder.SaveableStateProvider(key = currentTabIndex) { when (currentTabIndex) { diff --git a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/mood/MoodsPageScreen.kt b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/mood/MoodsPageScreen.kt index c595bbdc5d..e944927638 100644 --- a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/mood/MoodsPageScreen.kt +++ b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/mood/MoodsPageScreen.kt @@ -13,10 +13,8 @@ import androidx.navigation.NavController import it.fast4x.compose.persist.PersistMapCleanup import it.fast4x.compose.routing.RouteHandler import it.fast4x.rimusic.R -import it.fast4x.rimusic.enums.NavRoutes -import it.fast4x.rimusic.enums.UiType -import it.fast4x.rimusic.ui.components.Scaffold import it.fast4x.rimusic.ui.screens.globalRoutes +import me.knighthat.Skeleton @ExperimentalMaterialApi @ExperimentalTextApi @@ -35,21 +33,9 @@ fun MoodsPageScreen( RouteHandler(listenToGlobalEmitter = true) { globalRoutes() host { - Scaffold( - navController = navController, - topIconButtonId = R.drawable.chevron_back, - showButton1 = UiType.RiMusic.isNotCurrent(), - onTopIconButtonClick = pop, - topIconButton2Id = R.drawable.chevron_back, - onTopIconButton2Click = pop, - showButton2 = false, - tabIndex = 0, - onTabChanged = { }, - onHomeClick = { - //homeRoute() - navController.navigate(NavRoutes.home.name) - }, - tabColumnContent = { item -> + Skeleton( + navController, + navBarContent = { item -> item(0, stringResource(R.string.moods_and_genres), R.drawable.album) } ) { currentTabIndex -> diff --git a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/newreleases/NewReleasesScreen.kt b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/newreleases/NewReleasesScreen.kt index 602bf6feda..24992f137c 100644 --- a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/newreleases/NewReleasesScreen.kt +++ b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/newreleases/NewReleasesScreen.kt @@ -17,10 +17,8 @@ import androidx.navigation.NavController import it.fast4x.compose.persist.PersistMapCleanup import it.fast4x.compose.routing.RouteHandler import it.fast4x.rimusic.R -import it.fast4x.rimusic.enums.NavRoutes -import it.fast4x.rimusic.enums.UiType -import it.fast4x.rimusic.ui.components.Scaffold import it.fast4x.rimusic.ui.screens.globalRoutes +import me.knighthat.Skeleton @ExperimentalMaterialApi @ExperimentalTextApi @@ -45,21 +43,13 @@ fun NewreleasesScreen( globalRoutes() host { - Scaffold( - navController = navController, - playerEssential = playerEssential, - topIconButtonId = R.drawable.chevron_back, - onTopIconButtonClick = pop, - showButton1 = UiType.RiMusic.isNotCurrent(), - topIconButton2Id = R.drawable.chevron_back, - onTopIconButton2Click = pop, - showButton2 = false, - tabIndex = tabIndex, + Skeleton( + navController, + tabIndex, onTabChanged = { tabIndex = it }, - onHomeClick = { navController.navigate(NavRoutes.home.name) }, - tabColumnContent = { item -> + playerEssential, + navBarContent = { item -> item(0, stringResource(R.string.new_albums), R.drawable.album) - //item(1, stringResource(R.string.new_albums_of_your_artists), R.drawable.album) } ) { currentTabIndex -> saveableStateHolder.SaveableStateProvider(key = currentTabIndex) { diff --git a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/ondevice/DeviceListSongsScreen.kt b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/ondevice/DeviceListSongsScreen.kt index 88dc263d8d..1c3373e839 100644 --- a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/ondevice/DeviceListSongsScreen.kt +++ b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/ondevice/DeviceListSongsScreen.kt @@ -20,8 +20,6 @@ import it.fast4x.rimusic.enums.BuiltInPlaylist import it.fast4x.rimusic.enums.DeviceLists import it.fast4x.rimusic.enums.MaxTopPlaylistItems import it.fast4x.rimusic.enums.NavRoutes -import it.fast4x.rimusic.enums.UiType -import it.fast4x.rimusic.ui.components.Scaffold import it.fast4x.rimusic.ui.screens.builtinplaylist.BuiltInPlaylistSongs import it.fast4x.rimusic.ui.screens.globalRoutes import it.fast4x.rimusic.utils.MaxTopPlaylistItemsKey @@ -31,7 +29,7 @@ import it.fast4x.rimusic.utils.showDownloadedPlaylistKey import it.fast4x.rimusic.utils.showFavoritesPlaylistKey import it.fast4x.rimusic.utils.showMyTopPlaylistKey import it.fast4x.rimusic.utils.showOnDevicePlaylistKey -import it.fast4x.rimusic.utils.showSearchTabKey +import me.knighthat.Skeleton @ExperimentalMaterialApi @ExperimentalTextApi @@ -52,7 +50,6 @@ fun DeviceListSongsScreen( DeviceLists.LocalSongs -> 4 }) } - val showSearchTab by rememberPreference(showSearchTabKey, false) val maxTopPlaylistItems by rememberPreference( MaxTopPlaylistItemsKey, @@ -69,115 +66,48 @@ fun DeviceListSongsScreen( RouteHandler(listenToGlobalEmitter = true) { globalRoutes() - /* - searchResultRoute { query -> - SearchResultScreen( - navController = navController, - query = query, - onSearchAgain = { - searchRoute(query) - } - ) - } - - searchRoute { initialTextInput -> - val context = LocalContext.current - SearchScreen( - navController = navController, - initialTextInput = initialTextInput, - onSearch = { query -> - pop() - searchResultRoute(query) - - if (!context.preferences.getBoolean(pauseSearchHistoryKey, false)) { - query { - Database.insert(SearchQuery(query = query)) - } - } - }, - onViewPlaylist = {} - ) - } -*/ host { - Scaffold( - navController = navController, - playerEssential = playerEssential, - topIconButtonId = R.drawable.chevron_back, - onTopIconButtonClick = pop, - showButton1 = UiType.RiMusic.isNotCurrent(), - topIconButton2Id = R.drawable.chevron_back, - onTopIconButton2Click = pop, - showButton2 = false, - showBottomButton = showSearchTab, - onBottomIconButtonClick = { - //searchRoute("") - navController.navigate(NavRoutes.search.name) - }, - tabIndex = tabIndex, - onTabChanged = onTabIndexChanged, - onHomeClick = { - //homeRoute() - navController.navigate(NavRoutes.home.name) - }, - tabColumnContent = { Item -> + Skeleton( + navController, + tabIndex, + onTabIndexChanged, + playerEssential, + navBarContent = { item -> if(showFavoritesPlaylist) - Item(0, stringResource(R.string.favorites), R.drawable.heart) + item(0, stringResource(R.string.favorites), R.drawable.heart) if(showCachedPlaylist) - Item(1, stringResource(R.string.cached), R.drawable.sync) + item(1, stringResource(R.string.cached), R.drawable.sync) if(showDownloadedPlaylist) - Item(2, stringResource(R.string.downloaded), R.drawable.downloaded) + item(2, stringResource(R.string.downloaded), R.drawable.downloaded) if(showMyTopPlaylist) - Item(3, stringResource(R.string.my_playlist_top) + " ${maxTopPlaylistItems.number}" , R.drawable.trending) + item(3, stringResource(R.string.my_playlist_top) + " ${maxTopPlaylistItems.number}" , R.drawable.trending) if(showOnDevicePlaylist) - Item(4, stringResource(R.string.on_device), R.drawable.musical_notes) + item(4, stringResource(R.string.on_device), R.drawable.musical_notes) } ) { currentTabIndex -> saveableStateHolder.SaveableStateProvider(key = currentTabIndex) { - when (currentTabIndex) { - 0 -> BuiltInPlaylistSongs( - navController = navController, - builtInPlaylist = BuiltInPlaylist.Favorites, - onSearchClick = { - //searchRoute("") - navController.navigate(NavRoutes.search.name) - } - ) - 1 -> BuiltInPlaylistSongs( - navController = navController, - builtInPlaylist = BuiltInPlaylist.Offline, - onSearchClick = { - //searchRoute("") - navController.navigate(NavRoutes.search.name) - } - ) - 2 -> BuiltInPlaylistSongs( - navController = navController, - builtInPlaylist = BuiltInPlaylist.Downloaded, - onSearchClick = { - //searchRoute("") - navController.navigate(NavRoutes.search.name) - } - ) - 3 -> BuiltInPlaylistSongs( + val builtInPlaylist: BuiltInPlaylist = + when( currentTabIndex ) { + 0 -> BuiltInPlaylist.Favorites + 1 -> BuiltInPlaylist.Offline + 2 -> BuiltInPlaylist.Downloaded + 3 -> BuiltInPlaylist.Top + else -> BuiltInPlaylist.OnDevice + } + + if( builtInPlaylist == BuiltInPlaylist.OnDevice ) + DeviceListSongs( navController = navController, - builtInPlaylist = BuiltInPlaylist.Top, - onSearchClick = { - //searchRoute("") - navController.navigate(NavRoutes.search.name) - } + deviceLists = DeviceLists.LocalSongs, + onSearchClick = { navController.navigate(NavRoutes.search.name) } ) - 4 -> DeviceListSongs( + else + BuiltInPlaylistSongs( navController = navController, - deviceLists = DeviceLists.LocalSongs, - onSearchClick = { - //searchRoute("") - navController.navigate(NavRoutes.search.name) - } + builtInPlaylist = builtInPlaylist, + onSearchClick = { navController.navigate(NavRoutes.search.name) } ) - - } } } } diff --git a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/playlist/PlaylistScreen.kt b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/playlist/PlaylistScreen.kt index 5350ae0d53..77401b9ec5 100644 --- a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/playlist/PlaylistScreen.kt +++ b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/playlist/PlaylistScreen.kt @@ -19,6 +19,7 @@ import it.fast4x.rimusic.ui.components.Scaffold import it.fast4x.rimusic.ui.screens.globalRoutes import it.fast4x.rimusic.utils.UiTypeKey import it.fast4x.rimusic.utils.rememberPreference +import me.knighthat.Skeleton @ExperimentalMaterialApi @ExperimentalTextApi @@ -40,25 +41,14 @@ fun PlaylistScreen( RouteHandler(listenToGlobalEmitter = true) { globalRoutes() - val uiType by rememberPreference(UiTypeKey, UiType.RiMusic) + host { - Scaffold( - navController = navController, - playerEssential = playerEssential, - onTopIconButtonClick = pop, - showButton1 = uiType != UiType.RiMusic, - onTopIconButton2Click = pop, - tabColumnContent = { Item -> - Item(0, stringResource(R.string.songs), R.drawable.musical_notes) - }, - onTabChanged = {}, - onHomeClick = {}, - onBottomIconButtonClick = {}, - showBottomButton = false, - tabIndex = 0, - topIconButtonId = R.drawable.ui, - topIconButton2Id = R.drawable.ui, - showButton2 = false + Skeleton( + navController, + mediaPlayer = playerEssential, + navBarContent = { item -> + item(0, stringResource(R.string.songs), R.drawable.musical_notes) + } ) { currentTabIndex -> saveableStateHolder.SaveableStateProvider(key = currentTabIndex) { when (currentTabIndex) { diff --git a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/podcast/PodcastScreen.kt b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/podcast/PodcastScreen.kt index a4bcd6959e..4d19ac4c02 100644 --- a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/podcast/PodcastScreen.kt +++ b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/podcast/PodcastScreen.kt @@ -13,10 +13,8 @@ import androidx.navigation.NavController import it.fast4x.compose.persist.PersistMapCleanup import it.fast4x.compose.routing.RouteHandler import it.fast4x.rimusic.R -import it.fast4x.rimusic.enums.NavRoutes -import it.fast4x.rimusic.enums.UiType -import it.fast4x.rimusic.ui.components.Scaffold import it.fast4x.rimusic.ui.screens.globalRoutes +import me.knighthat.Skeleton @ExperimentalMaterialApi @ExperimentalTextApi @@ -38,24 +36,13 @@ fun PodcastScreen( RouteHandler(listenToGlobalEmitter = true) { globalRoutes() + host { - Scaffold( - navController = navController, - playerEssential = playerEssential, - topIconButtonId = R.drawable.chevron_back, - onTopIconButtonClick = pop, - showButton1 = UiType.RiMusic.isNotCurrent(), - topIconButton2Id = R.drawable.chevron_back, - onTopIconButton2Click = pop, - showButton2 = false, - tabIndex = 0, - onTabChanged = { }, - onHomeClick = { - //homeRoute() - navController.navigate(NavRoutes.home.name) - }, - tabColumnContent = { Item -> - Item(0, stringResource(R.string.podcast_episodes), R.drawable.podcast) + Skeleton( + navController, + mediaPlayer = playerEssential, + navBarContent = { item -> + item(0, stringResource(R.string.podcast_episodes), R.drawable.podcast) } ) { currentTabIndex -> saveableStateHolder.SaveableStateProvider(key = currentTabIndex) { diff --git a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/search/SearchScreen.kt b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/search/SearchScreen.kt index 3e6572a020..86e3a96042 100644 --- a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/search/SearchScreen.kt +++ b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/search/SearchScreen.kt @@ -31,13 +31,12 @@ import androidx.navigation.NavController import it.fast4x.compose.persist.PersistMapCleanup import it.fast4x.compose.routing.RouteHandler import it.fast4x.rimusic.R -import it.fast4x.rimusic.enums.NavRoutes import it.fast4x.rimusic.enums.UiType -import it.fast4x.rimusic.ui.components.Scaffold import it.fast4x.rimusic.ui.components.themed.IconButton import it.fast4x.rimusic.ui.screens.globalRoutes import it.fast4x.rimusic.ui.styling.favoritesIcon import it.fast4x.rimusic.utils.secondary +import me.knighthat.Skeleton import me.knighthat.colorPalette import me.knighthat.typography @@ -82,7 +81,6 @@ fun SearchScreen( host { val decorationBox: @Composable (@Composable () -> Unit) -> Unit = { innerTextField -> - Box( contentAlignment = Alignment.CenterStart, modifier = Modifier @@ -105,10 +103,7 @@ fun SearchScreen( .size(20.dp) ) } - - } - Box( contentAlignment = Alignment.CenterStart, modifier = Modifier @@ -129,7 +124,6 @@ fun SearchScreen( ) } - innerTextField() } Box( @@ -152,34 +146,18 @@ fun SearchScreen( .size(24.dp) ) } - - } } - Scaffold( - navController = navController, - playerEssential = playerEssential, - topIconButtonId = R.drawable.chevron_back, - showButton1 = UiType.RiMusic.isNotCurrent(), - onTopIconButtonClick = { - //onGoToHome() - navController.navigate(NavRoutes.home.name) - }, - topIconButton2Id = R.drawable.chevron_back, - onTopIconButton2Click = pop, - showButton2 = false, - //hideTabs = false, - tabIndex = tabIndex, - onTabChanged = onTabChanged, - onHomeClick = { - //homeRoute() - navController.navigate(NavRoutes.home.name) - }, - tabColumnContent = { Item -> - Item(0, stringResource(R.string.online), R.drawable.globe) - Item(1, stringResource(R.string.library), R.drawable.library) - Item(2, stringResource(R.string.go_to_link), R.drawable.link) + Skeleton( + navController, + tabIndex, + onTabChanged, + playerEssential, + navBarContent = { item -> + item(0, stringResource(R.string.online), R.drawable.globe) + item(1, stringResource(R.string.library), R.drawable.library) + item(2, stringResource(R.string.go_to_link), R.drawable.link) } ) { currentTabIndex -> saveableStateHolder.SaveableStateProvider(currentTabIndex) { diff --git a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/search/SearchTypeScreen.kt b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/search/SearchTypeScreen.kt index 12881691e9..cc747be349 100644 --- a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/search/SearchTypeScreen.kt +++ b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/search/SearchTypeScreen.kt @@ -37,6 +37,7 @@ import it.fast4x.rimusic.ui.components.themed.IconButton import it.fast4x.rimusic.ui.screens.globalRoutes import it.fast4x.rimusic.ui.styling.favoritesIcon import it.fast4x.rimusic.utils.secondary +import me.knighthat.Skeleton import me.knighthat.colorPalette import me.knighthat.typography @@ -75,19 +76,7 @@ fun SearchTypeScreen( RouteHandler(listenToGlobalEmitter = true) { globalRoutes() - /* - searchResultRoute { query -> - SearchResultScreen( - navController = navController, - query = query, - onSearchAgain = { - searchRoute(query) - } - ) - } - val onGoToHome = homeRoute::global -*/ host { val decorationBox: @Composable (@Composable () -> Unit) -> Unit = { innerTextField -> Box( @@ -130,26 +119,14 @@ fun SearchTypeScreen( } } - Scaffold( - navController = navController, - topIconButtonId = R.drawable.chevron_back, - onTopIconButtonClick = { - //onGoToHome() - navController.navigate(NavRoutes.home.name) - }, - topIconButton2Id = R.drawable.chevron_back, - onTopIconButton2Click = pop, - showButton2 = false, - tabIndex = tabIndex, - onTabChanged = onTabChanged, - onHomeClick = { - //homeRoute() - navController.navigate(NavRoutes.home.name) - }, - tabColumnContent = { Item -> - Item(0, stringResource(R.string.online), R.drawable.globe) - Item(1, stringResource(R.string.library), R.drawable.library) - Item(2, stringResource(R.string.go_to_link), R.drawable.link) + Skeleton( + navController, + tabIndex, + onTabChanged, + navBarContent = { item -> + item(0, stringResource(R.string.online), R.drawable.globe) + item(1, stringResource(R.string.library), R.drawable.library) + item(2, stringResource(R.string.go_to_link), R.drawable.link) } ) { currentTabIndex -> saveableStateHolder.SaveableStateProvider(currentTabIndex) { @@ -158,20 +135,7 @@ fun SearchTypeScreen( navController = navController, textFieldValue = textFieldValue, onTextFieldValueChanged = onTextFieldValueChanged, - onSearch = { - navController.navigate("${NavRoutes.searchResults.name}/$it") - }, - /* - onSearch = { query -> - //pop() - searchResultRoute(query) - if (!preferences.getBoolean(pauseSearchHistoryKey, false)) { - transaction { - Database.insert(SearchQuery(query = query)) - } - } - }, - */ + onSearch = { navController.navigate("${NavRoutes.searchResults.name}/$it") }, decorationBox = decorationBox ) @@ -183,10 +147,7 @@ fun SearchTypeScreen( onAction1 = { onTabChanged(0) }, onAction2 = { onTabChanged(1) }, onAction3 = { onTabChanged(2) }, - onAction4 = { - //onGoToHome() - navController.navigate(NavRoutes.home.name) - } + onAction4 = { navController.navigate(NavRoutes.home.name) } ) 2 -> GoToLink( @@ -197,10 +158,7 @@ fun SearchTypeScreen( onAction1 = { onTabChanged(0) }, onAction2 = { onTabChanged(1) }, onAction3 = { onTabChanged(2) }, - onAction4 = { - //onGoToHome() - navController.navigate(NavRoutes.home.name) - } + onAction4 = { navController.navigate(NavRoutes.home.name) } ) } } diff --git a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/searchresult/SearchResultScreen.kt b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/searchresult/SearchResultScreen.kt index 7c5dd17e0c..21141b4db6 100644 --- a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/searchresult/SearchResultScreen.kt +++ b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/searchresult/SearchResultScreen.kt @@ -42,13 +42,11 @@ import it.fast4x.rimusic.EXPLICIT_PREFIX import it.fast4x.rimusic.LocalPlayerServiceBinder import it.fast4x.rimusic.R import it.fast4x.rimusic.enums.NavRoutes -import it.fast4x.rimusic.enums.UiType import it.fast4x.rimusic.models.Album import it.fast4x.rimusic.models.Song import it.fast4x.rimusic.models.SongAlbumMap import it.fast4x.rimusic.query import it.fast4x.rimusic.ui.components.LocalMenuState -import it.fast4x.rimusic.ui.components.Scaffold import it.fast4x.rimusic.ui.components.SwipeableAlbumItem import it.fast4x.rimusic.ui.components.SwipeablePlaylistItem import it.fast4x.rimusic.ui.components.themed.Header @@ -85,6 +83,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.combine import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import me.knighthat.Skeleton @ExperimentalMaterialApi @ExperimentalTextApi @@ -138,34 +137,26 @@ fun SearchResultScreen( } val emptyItemsText = stringResource(R.string.no_results_found) - Scaffold( - navController = navController, - playerEssential = playerEssential, - topIconButtonId = R.drawable.chevron_back, - onTopIconButtonClick = pop, - showButton1 = UiType.RiMusic.isNotCurrent(), - topIconButton2Id = R.drawable.chevron_back, - onTopIconButton2Click = pop, - showButton2 = false, - tabIndex = tabIndex, - onHomeClick = { - navController.navigate(NavRoutes.home.name) - }, - onTabChanged = onTabIndexChanges, - tabColumnContent = { Item -> - Item(0, stringResource(R.string.songs), R.drawable.musical_notes) - Item(1, stringResource(R.string.albums), R.drawable.album) - Item(2, stringResource(R.string.artists), R.drawable.artist) - Item(3, stringResource(R.string.videos), R.drawable.video) - Item(4, stringResource(R.string.playlists), R.drawable.playlist) - Item(5, stringResource(R.string.featured), R.drawable.featured_playlist) - Item(6, stringResource(R.string.podcasts), R.drawable.podcast) + + Skeleton( + navController, + tabIndex, + onTabIndexChanges, + playerEssential, + navBarContent = { item -> + item(0, stringResource(R.string.songs), R.drawable.musical_notes) + item(1, stringResource(R.string.albums), R.drawable.album) + item(2, stringResource(R.string.artists), R.drawable.artist) + item(3, stringResource(R.string.videos), R.drawable.video) + item(4, stringResource(R.string.playlists), R.drawable.playlist) + item(5, stringResource(R.string.featured), R.drawable.featured_playlist) + item(6, stringResource(R.string.podcasts), R.drawable.podcast) } - ) { tabIndex -> - saveableStateHolder.SaveableStateProvider(tabIndex) { - when (tabIndex) { + ) { currentTabIndex -> + saveableStateHolder.SaveableStateProvider(currentTabIndex) { + when ( currentTabIndex ) { 0 -> { - val binder = LocalPlayerServiceBinder.current + val localBinder = LocalPlayerServiceBinder.current val menuState = LocalMenuState.current val thumbnailSizeDp = Dimensions.thumbnails.song val thumbnailSizePx = thumbnailSizeDp.px @@ -198,7 +189,7 @@ fun SearchResultScreen( SwipeablePlaylistItem( mediaItem = song.asMediaItem, onSwipeToRight = { - binder?.player?.addNext(song.asMediaItem) + localBinder?.player?.addNext(song.asMediaItem) } ) { downloadState = getDownloadState(song.asMediaItem.mediaId) @@ -208,7 +199,7 @@ fun SearchResultScreen( song = song, isDownloaded = isDownloaded, onDownloadClick = { - binder?.cache?.removeResource(song.asMediaItem.mediaId) + localBinder?.cache?.removeResource(song.asMediaItem.mediaId) query { Database.insert( Song( @@ -246,9 +237,9 @@ fun SearchResultScreen( ) }, onClick = { - binder?.stopRadio() - binder?.player?.forcePlay(song.asMediaItem) - binder?.setupRadio(song.info?.endpoint) + localBinder?.stopRadio() + localBinder?.player?.forcePlay(song.asMediaItem) + localBinder?.setupRadio(song.info?.endpoint) } ) ) @@ -292,8 +283,8 @@ fun SearchResultScreen( CoroutineScope(Dispatchers.IO).launch { Database .album(album.key) - .combine(snapshotFlow { tabIndex }) { album, tabIndex -> album to tabIndex } - .collect { (currentAlbum) -> + .combine(snapshotFlow { currentTabIndex }) { album, tabIndex -> album to tabIndex } + .collect { if (albumPage == null) withContext(Dispatchers.IO) { Innertube.albumPage( @@ -305,7 +296,7 @@ fun SearchResultScreen( albumPage = currentAlbumPage - println("mediaItem success home album songsPage ${currentAlbumPage?.songsPage} description ${currentAlbumPage?.description} year ${currentAlbumPage?.year}") + println("mediaItem success home album songsPage ${currentAlbumPage.songsPage} description ${currentAlbumPage.description} year ${currentAlbumPage.year}") albumPage ?.songsPage @@ -342,8 +333,8 @@ fun SearchResultScreen( CoroutineScope(Dispatchers.IO).launch { Database .album(album.key) - .combine(snapshotFlow { tabIndex }) { album, tabIndex -> album to tabIndex } - .collect { (currentAlbum) -> + .combine(snapshotFlow { currentTabIndex }) { album, tabIndex -> album to tabIndex } + .collect { if (albumPage == null) withContext(Dispatchers.IO) { Innertube.albumPage( @@ -355,27 +346,27 @@ fun SearchResultScreen( albumPage = currentAlbumPage - println("mediaItem success home album songsPage ${currentAlbumPage?.songsPage} description ${currentAlbumPage?.description} year ${currentAlbumPage?.year}") + println("mediaItem success home album songsPage ${currentAlbumPage.songsPage} description ${currentAlbumPage.description} year ${currentAlbumPage.year}") Database.upsert( Album( id = album.key, - title = currentAlbumPage?.title, - thumbnailUrl = currentAlbumPage?.thumbnail?.url, - year = currentAlbumPage?.year, - authorsText = currentAlbumPage?.authors + title = currentAlbumPage.title, + thumbnailUrl = currentAlbumPage.thumbnail?.url, + year = currentAlbumPage.year, + authorsText = currentAlbumPage.authors ?.joinToString( "" ) { it.name ?: "" }, - shareUrl = currentAlbumPage?.url, + shareUrl = currentAlbumPage.url, timestamp = System.currentTimeMillis(), bookmarkedAt = System.currentTimeMillis() ), currentAlbumPage - ?.songsPage + .songsPage ?.items ?.map( Innertube.SongItem::asMediaItem @@ -399,8 +390,6 @@ fun SearchResultScreen( } } - - //} } } } @@ -470,7 +459,7 @@ fun SearchResultScreen( } 3 -> { - val binder = LocalPlayerServiceBinder.current + val localBinder = LocalPlayerServiceBinder.current val menuState = LocalMenuState.current val thumbnailHeightDp = 72.dp val thumbnailWidthDp = 128.dp @@ -499,7 +488,7 @@ fun SearchResultScreen( SwipeablePlaylistItem( mediaItem = video.asMediaItem, onSwipeToRight = { - binder?.player?.addNext(video.asMediaItem) + localBinder?.player?.addNext(video.asMediaItem) } ) { VideoItem( @@ -521,11 +510,11 @@ fun SearchResultScreen( ) }, onClick = { - binder?.stopRadio() + localBinder?.stopRadio() if (isVideoEnabled) - binder?.player?.playVideo(video.asMediaItem) + localBinder?.player?.playVideo(video.asMediaItem) else - binder?.player?.forcePlay(video.asMediaItem) + localBinder?.player?.forcePlay(video.asMediaItem) //binder?.setupRadio(video.info?.endpoint) } ) @@ -549,14 +538,14 @@ fun SearchResultScreen( ItemsPage( tag = "searchResults/$query/${ - when (tabIndex) { + when (currentTabIndex) { 4 -> "playlists" else -> "featured" } }", itemsPageProvider = { continuation -> if (continuation == null) { - val filter = when (tabIndex) { + val filter = when (currentTabIndex) { 4 -> Innertube.SearchFilter.CommunityPlaylist else -> Innertube.SearchFilter.FeaturedPlaylist } diff --git a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/settings/SettingsScreen.kt b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/settings/SettingsScreen.kt index 3c0a6bbde4..11aa5b9126 100644 --- a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/settings/SettingsScreen.kt +++ b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/settings/SettingsScreen.kt @@ -43,9 +43,7 @@ import androidx.media3.common.util.UnstableApi import androidx.navigation.NavController import it.fast4x.compose.routing.RouteHandler import it.fast4x.rimusic.R -import it.fast4x.rimusic.enums.UiType import it.fast4x.rimusic.enums.ValidationType -import it.fast4x.rimusic.ui.components.Scaffold import it.fast4x.rimusic.ui.components.themed.DialogColorPicker import it.fast4x.rimusic.ui.components.themed.InputTextDialog import it.fast4x.rimusic.ui.components.themed.Slider @@ -54,10 +52,10 @@ import it.fast4x.rimusic.ui.components.themed.StringListDialog import it.fast4x.rimusic.ui.components.themed.Switch import it.fast4x.rimusic.ui.components.themed.ValueSelectorDialog import it.fast4x.rimusic.ui.screens.globalRoutes -import it.fast4x.rimusic.ui.screens.homeRoute import it.fast4x.rimusic.utils.color import it.fast4x.rimusic.utils.secondary import it.fast4x.rimusic.utils.semiBold +import me.knighthat.Skeleton import me.knighthat.colorPalette import me.knighthat.typography @@ -72,8 +70,8 @@ fun SettingsScreen( navController: NavController, playerEssential: @Composable () -> Unit = {}, ) { - val saveableStateHolder = rememberSaveableStateHolder() val context = LocalContext.current + val saveableStateHolder = rememberSaveableStateHolder() val (tabIndex, onTabChanged) = rememberSaveable { mutableStateOf(0) @@ -82,25 +80,18 @@ fun SettingsScreen( RouteHandler(listenToGlobalEmitter = true) { globalRoutes() host { - Scaffold( - navController = navController, - playerEssential = playerEssential, - topIconButtonId = R.drawable.chevron_back, - onTopIconButtonClick = pop, - showButton1 = UiType.RiMusic.isNotCurrent(), - topIconButton2Id = R.drawable.chevron_back, - onTopIconButton2Click = pop, - showButton2 = false, - tabIndex = tabIndex, - onHomeClick = { homeRoute() }, - onTabChanged = onTabChanged, - tabColumnContent = { Item -> - Item(0, stringResource(R.string.ui_tab), R.drawable.ui) - Item(1, stringResource(R.string.player_appearance), R.drawable.color_palette) - Item(2, stringResource(R.string.quick_picks), R.drawable.sparkles) - Item(3, stringResource(R.string.tab_data), R.drawable.server) - Item(4, stringResource(R.string.tab_miscellaneous), R.drawable.equalizer) - Item(5, stringResource(R.string.about), R.drawable.information) + Skeleton( + navController, + tabIndex, + onTabChanged, + playerEssential, + navBarContent = { item -> + item(0, stringResource(R.string.ui_tab), R.drawable.ui) + item(1, stringResource(R.string.player_appearance), R.drawable.color_palette) + item(2, stringResource(R.string.quick_picks), R.drawable.sparkles) + item(3, stringResource(R.string.tab_data), R.drawable.server) + item(4, stringResource(R.string.tab_miscellaneous), R.drawable.equalizer) + item(5, stringResource(R.string.about), R.drawable.information) } ) { currentTabIndex -> diff --git a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/settings/UiSettings.kt b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/settings/UiSettings.kt index 77731d706d..0bf3b890f8 100644 --- a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/settings/UiSettings.kt +++ b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/settings/UiSettings.kt @@ -53,7 +53,6 @@ import it.fast4x.rimusic.R import it.fast4x.rimusic.enums.AudioQualityFormat import it.fast4x.rimusic.enums.BackgroundProgress import it.fast4x.rimusic.enums.CarouselSize -import it.fast4x.rimusic.enums.ClickLyricsText import it.fast4x.rimusic.enums.ColorPaletteMode import it.fast4x.rimusic.enums.ColorPaletteName import it.fast4x.rimusic.enums.DurationInMilliseconds @@ -217,6 +216,7 @@ import it.fast4x.rimusic.utils.showStatsListeningTimeKey import it.fast4x.rimusic.utils.showTopActionsBarKey import it.fast4x.rimusic.utils.showTotalTimeQueueKey import it.fast4x.rimusic.utils.showthumbnailKey +import it.fast4x.rimusic.utils.skipMediaOnErrorKey import it.fast4x.rimusic.utils.skipSilenceKey import it.fast4x.rimusic.utils.swipeUpQueueKey import it.fast4x.rimusic.utils.tapqueueKey @@ -253,6 +253,8 @@ fun DefaultUiSettings() { var skipSilence by rememberPreference(skipSilenceKey, false) skipSilence = false + var skipMediaOnError by rememberPreference(skipMediaOnErrorKey, false) + skipMediaOnError = false var volumeNormalization by rememberPreference(volumeNormalizationKey, false) volumeNormalization = false var recommendationsNumber by rememberPreference(recommendationsNumberKey, RecommendationsNumber.`5`) @@ -557,6 +559,7 @@ fun UiSettings( ) var skipSilence by rememberPreference(skipSilenceKey, false) + var skipMediaOnError by rememberPreference(skipMediaOnErrorKey, false) var volumeNormalization by rememberPreference(volumeNormalizationKey, false) var audioQualityFormat by rememberPreference(audioQualityFormatKey, AudioQualityFormat.Auto) @@ -1232,6 +1235,43 @@ fun UiSettings( } ) + if (filter.isNullOrBlank() || stringResource(R.string.skip_media_on_error).contains(filterCharSequence,true)) { + SwitchSettingEntry( + title = stringResource(R.string.skip_media_on_error), + text = stringResource(R.string.skip_media_on_error_description), + isChecked = skipMediaOnError, + onCheckedChange = { + skipMediaOnError = it + restartService = true + } + ) + + AnimatedVisibility(visible = restartService) { + Column( + modifier = Modifier.padding(start = 25.dp) + ) { + Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) { + SettingsDescription( + text = stringResource(R.string.minimum_silence_length_warning), + important = true, + modifier = Modifier.weight(2f) + ) + SecondaryTextButton( + text = stringResource(R.string.restart_service), + onClick = { + binder?.restartForegroundOrStop()?.let { restartService = false } + }, + modifier = Modifier + .weight(1f) + .padding(end = 24.dp) + ) + } + + } + } + + } + if (filter.isNullOrBlank() || stringResource(R.string.skip_silence).contains(filterCharSequence,true)) { SwitchSettingEntry( title = stringResource(R.string.skip_silence), diff --git a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/statistics/StatisticsScreen.kt b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/statistics/StatisticsScreen.kt index 792f997133..449c23611f 100644 --- a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/statistics/StatisticsScreen.kt +++ b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/ui/screens/statistics/StatisticsScreen.kt @@ -15,11 +15,9 @@ import androidx.navigation.NavController import it.fast4x.compose.persist.PersistMapCleanup import it.fast4x.compose.routing.RouteHandler import it.fast4x.rimusic.R -import it.fast4x.rimusic.enums.NavRoutes import it.fast4x.rimusic.enums.StatisticsType -import it.fast4x.rimusic.enums.UiType -import it.fast4x.rimusic.ui.components.Scaffold import it.fast4x.rimusic.ui.screens.globalRoutes +import me.knighthat.Skeleton @ExperimentalMaterialApi @ExperimentalTextApi @@ -44,7 +42,6 @@ fun StatisticsScreen( StatisticsType.SixMonths -> 4 StatisticsType.OneYear -> 5 StatisticsType.All -> 6 - }) } @@ -53,72 +50,34 @@ fun StatisticsScreen( RouteHandler(listenToGlobalEmitter = true) { globalRoutes() host { - Scaffold( - navController = navController, - playerEssential = playerEssential, - topIconButtonId = R.drawable.chevron_back, - onTopIconButtonClick = pop, - showButton1 = UiType.RiMusic.isNotCurrent(), - topIconButton2Id = R.drawable.chevron_back, - onTopIconButton2Click = pop, - showButton2 = false, - onBottomIconButtonClick = { - //searchRoute("") - navController.navigate(NavRoutes.search.name) - }, - tabIndex = tabIndex, - onTabChanged = onTabIndexChanged, - onHomeClick = { - //homeRoute() - navController.navigate(NavRoutes.home.name) - }, - /* - onSettingsClick = { settingsRoute() }, - onStatisticsClick = { statisticsTypeRoute(StatisticsType.Today) }, - onHistoryClick = { historyRoute() }, - onSearchClick = { searchRoute("") }, - */ - tabColumnContent = { Item -> - Item(0, stringResource(R.string.today), R.drawable.stat_today) - Item(1, stringResource(R.string._1_week), R.drawable.stat_week) - Item(2, stringResource(R.string._1_month), R.drawable.stat_month) - Item(3, stringResource(R.string._3_month), R.drawable.stat_3months) - Item(4, stringResource(R.string._6_month), R.drawable.stat_6months) - Item(5, stringResource(R.string._1_year), R.drawable.stat_year) - Item(6, stringResource(R.string.all), R.drawable.calendar_clear) + Skeleton( + navController, + tabIndex, + onTabIndexChanged, + playerEssential, + navBarContent = { item -> + item(0, stringResource(R.string.today), R.drawable.stat_today) + item(1, stringResource(R.string._1_week), R.drawable.stat_week) + item(2, stringResource(R.string._1_month), R.drawable.stat_month) + item(3, stringResource(R.string._3_month), R.drawable.stat_3months) + item(4, stringResource(R.string._6_month), R.drawable.stat_6months) + item(5, stringResource(R.string._1_year), R.drawable.stat_year) + item(6, stringResource(R.string.all), R.drawable.calendar_clear) } ) { currentTabIndex -> saveableStateHolder.SaveableStateProvider(key = currentTabIndex) { - when (currentTabIndex) { - 0 -> StatisticsPageModern( - navController = navController, - statisticsType = StatisticsType.Today - ) - 1 -> StatisticsPageModern( - navController = navController, - statisticsType = StatisticsType.OneWeek - ) - 2 -> StatisticsPageModern( - navController = navController, - statisticsType = StatisticsType.OneMonth - ) - 3 -> StatisticsPageModern( - navController = navController, - statisticsType = StatisticsType.ThreeMonths - ) - 4 -> StatisticsPageModern( - navController = navController, - statisticsType = StatisticsType.SixMonths - ) - 5 -> StatisticsPageModern( - navController = navController, - statisticsType = StatisticsType.OneYear - ) - 6 -> StatisticsPageModern( - navController = navController, - statisticsType = StatisticsType.All - ) - } + val type: StatisticsType = + when( currentTabIndex ) { + 0 -> StatisticsType.Today + 1 -> StatisticsType.OneWeek + 2 -> StatisticsType.OneMonth + 3 -> StatisticsType.ThreeMonths + 4 -> StatisticsType.SixMonths + 5 -> StatisticsType.OneYear + else -> StatisticsType.All + } + + StatisticsPageModern( navController, type ) } } } diff --git a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/utils/DataSource.kt b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/utils/DataSource.kt index 669c0bf656..ce444d2b30 100644 --- a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/utils/DataSource.kt +++ b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/utils/DataSource.kt @@ -1,12 +1,65 @@ package it.fast4x.rimusic.utils import androidx.annotation.OptIn +import androidx.media3.common.PlaybackException import androidx.media3.common.util.UnstableApi import androidx.media3.datasource.DataSource import androidx.media3.datasource.DataSpec +import androidx.media3.datasource.HttpDataSource.InvalidResponseCodeException import androidx.media3.datasource.TransferListener import androidx.media3.datasource.cache.CacheDataSource + +@UnstableApi +class RangeHandlerDataSourceFactory(private val parent: DataSource.Factory) : DataSource.Factory { + class Source(private val parent: DataSource) : DataSource by parent { + @OptIn(UnstableApi::class) + override fun open(dataSpec: DataSpec) = runCatching { + parent.open(dataSpec) + }.getOrElse { e -> + if (e.cause is InvalidResponseCodeException && (e.cause as InvalidResponseCodeException).responseCode == 416) parent.open( + dataSpec + .withRequestHeaders( + dataSpec.httpRequestHeaders.filter { + it.key.equals("range", ignoreCase = true) + } + ) + ) + else throw e + } + } + + override fun createDataSource() = Source(parent.createDataSource()) +} + +@UnstableApi +class CatchingDataSourceFactory(private val parent: DataSource.Factory) : DataSource.Factory { + class Source(private val parent: DataSource) : DataSource by parent { + @OptIn(UnstableApi::class) + override fun open(dataSpec: DataSpec) = runCatching { + parent.open(dataSpec) + }.getOrElse { + it.printStackTrace() + + if (it is PlaybackException) throw it + else throw PlaybackException( + "Unknown playback error", + it, + PlaybackException.ERROR_CODE_UNSPECIFIED + ) + } + } + + override fun createDataSource() = Source(parent.createDataSource()) +} + +@OptIn(UnstableApi::class) +fun DataSource.Factory.handleRangeErrors(): DataSource.Factory = RangeHandlerDataSourceFactory(this) + +@OptIn(UnstableApi::class) +fun DataSource.Factory.handleCatchingErrors(): DataSource.Factory = CatchingDataSourceFactory(this) + +/* @OptIn(UnstableApi::class) class ConditionalCacheDataSourceFactory( private val cacheDataSourceFactory: CacheDataSource.Factory, @@ -50,4 +103,5 @@ class ConditionalCacheDataSourceFactory( override fun getUri() = source.uri override fun close() = source.close() } -} \ No newline at end of file +} +*/ \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/utils/Preferences.kt b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/utils/Preferences.kt index 03aec10043..8770f6c6ab 100644 --- a/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/utils/Preferences.kt +++ b/composeApp/src/androidMain/kotlin/it/fast4x/rimusic/utils/Preferences.kt @@ -53,6 +53,7 @@ const val trackLoopEnabledKey = "trackLoopEnabled" const val queueLoopEnabledKey = "queueLoopEnabled" const val reorderInQueueEnabledKey = "reorderInQueueEnabled" const val skipSilenceKey = "skipSilence" +const val skipMediaOnErrorKey = "skipMediaOnError" const val volumeNormalizationKey = "volumeNormalization" const val resumePlaybackWhenDeviceConnectedKey = "resumePlaybackWhenDeviceConnected" const val persistentQueueKey = "persistentQueue" diff --git a/composeApp/src/androidMain/kotlin/me/knighthat/EffectHandler.kt b/composeApp/src/androidMain/kotlin/me/knighthat/EffectHandler.kt new file mode 100644 index 0000000000..4d66f42175 --- /dev/null +++ b/composeApp/src/androidMain/kotlin/me/knighthat/EffectHandler.kt @@ -0,0 +1,90 @@ +package me.knighthat + +import androidx.compose.animation.AnimatedContentTransitionScope +import androidx.compose.animation.ContentTransform +import androidx.compose.animation.EnterTransition +import androidx.compose.animation.ExitTransition +import androidx.compose.animation.core.LinearOutSlowInEasing +import androidx.compose.animation.core.Spring +import androidx.compose.animation.core.VisibilityThreshold +import androidx.compose.animation.core.spring +import androidx.compose.animation.core.tween +import androidx.compose.animation.expandIn +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.scaleIn +import androidx.compose.animation.scaleOut +import androidx.compose.animation.shrinkOut +import androidx.compose.animation.togetherWith +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.unit.IntOffset +import it.fast4x.rimusic.enums.TransitionEffect +import it.fast4x.rimusic.utils.rememberPreference +import it.fast4x.rimusic.utils.transitionEffectKey + +private val tween350 = tween( 350 ) + +private fun slideDirection( + transitionEffect: TransitionEffect, + targetState: Int, + initialState: Int +): AnimatedContentTransitionScope.SlideDirection { + val isSlideHorizontal = transitionEffect == TransitionEffect.SlideHorizontal + + return when ( targetState > initialState ) { + true -> + if ( isSlideHorizontal ) + AnimatedContentTransitionScope.SlideDirection.Left + else + AnimatedContentTransitionScope.SlideDirection.Up + false -> + if ( isSlideHorizontal ) + AnimatedContentTransitionScope.SlideDirection.Right + else + AnimatedContentTransitionScope.SlideDirection.Down + } +} + +private fun scale(): ContentTransform = scaleIn( tween350 ) togetherWith scaleOut( tween350 ) + +private fun fade(): ContentTransform = fadeIn( tween350 ) togetherWith fadeOut( tween350 ) + +private fun expand(): ContentTransform { + val expandIn = expandIn( + tween( 350, 0, LinearOutSlowInEasing ), + Alignment.TopStart + ) + val shrinkOut = shrinkOut( + tween( 350, 0, LinearOutSlowInEasing ), + Alignment.TopStart + ) + return expandIn togetherWith shrinkOut +} + +private fun none(): ContentTransform = EnterTransition.None togetherWith ExitTransition.None + +@Composable +fun transition(): AnimatedContentTransitionScope.() -> ContentTransform { + + val transitionEffect by rememberPreference(transitionEffectKey, TransitionEffect.Scale) + + return { + when( transitionEffect ) { + TransitionEffect.Scale -> scale() + TransitionEffect.Fade -> fade() + TransitionEffect.Expand -> expand() + TransitionEffect.None -> none() + TransitionEffect.SlideVertical, TransitionEffect.SlideHorizontal -> { + val animationSpec = spring( + dampingRatio = 0.9f, + stiffness = Spring.StiffnessLow, + visibilityThreshold = IntOffset.VisibilityThreshold + ) + val slideDirection = slideDirection( transitionEffect, targetState, initialState ) + slideIntoContainer(slideDirection, animationSpec) togetherWith slideOutOfContainer(slideDirection, animationSpec) + } + } + } +} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/me/knighthat/GlobalVars.kt b/composeApp/src/androidMain/kotlin/me/knighthat/GlobalVars.kt index 63bbd86a3c..74dd4d6884 100644 --- a/composeApp/src/androidMain/kotlin/me/knighthat/GlobalVars.kt +++ b/composeApp/src/androidMain/kotlin/me/knighthat/GlobalVars.kt @@ -1,12 +1,13 @@ package me.knighthat +import android.content.Context import androidx.compose.runtime.Composable import androidx.compose.runtime.ReadOnlyComposable import it.fast4x.rimusic.Dependencies -import it.fast4x.rimusic.enums.NavigationBarPosition -import it.fast4x.rimusic.enums.NavigationBarType -import it.fast4x.rimusic.enums.UiType import it.fast4x.rimusic.ui.styling.LocalAppearance +import it.fast4x.rimusic.utils.rememberPreference +import it.fast4x.rimusic.utils.showSearchTabKey +import it.fast4x.rimusic.utils.showStatsInNavbarKey @Composable fun typography() = LocalAppearance.current.typography @@ -18,4 +19,10 @@ fun colorPalette() = LocalAppearance.current.colorPalette @Composable fun thumbnailShape() = LocalAppearance.current.thumbnailShape -fun appContext() = Dependencies.application.applicationContext \ No newline at end of file +@Composable +fun showSearchIconInNav() = rememberPreference( showSearchTabKey, false ).value + +@Composable +fun showStatsIconInNav() = rememberPreference( showStatsInNavbarKey, false ).value + +fun appContext(): Context = Dependencies.application.applicationContext \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/me/knighthat/Skeleton.kt b/composeApp/src/androidMain/kotlin/me/knighthat/Skeleton.kt new file mode 100644 index 0000000000..a16dc49c4d --- /dev/null +++ b/composeApp/src/androidMain/kotlin/me/knighthat/Skeleton.kt @@ -0,0 +1,137 @@ +package me.knighthat + +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.AnimatedVisibilityScope +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.WindowInsetsSides +import androidx.compose.foundation.layout.asPaddingValues +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.only +import androidx.compose.foundation.layout.padding +import androidx.compose.material.BottomNavigationDefaults.windowInsets +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Scaffold +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import androidx.navigation.NavController +import it.fast4x.rimusic.enums.NavigationBarPosition +import it.fast4x.rimusic.enums.PlayerPosition +import it.fast4x.rimusic.enums.UiType +import it.fast4x.rimusic.utils.playerPositionKey +import it.fast4x.rimusic.utils.rememberPreference +import me.knighthat.component.header.AppHeader +import me.knighthat.component.nav.AbstractNavigationBar +import me.knighthat.component.nav.HorizontalNavigationBar +import me.knighthat.component.nav.VerticalNavigationBar + +// THIS IS THE SCAFFOLD +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun Skeleton( + navController: NavController, + tabIndex: Int = 0, + onTabChanged: (Int) -> Unit = {}, + mediaPlayer: @Composable (() -> Unit)? = null, + navBarContent: @Composable (@Composable (Int, String, Int) -> Unit) -> Unit, + content: @Composable AnimatedVisibilityScope.(Int) -> Unit +) { + val navigationBar: AbstractNavigationBar = + when( NavigationBarPosition.current() ) { + NavigationBarPosition.Left, NavigationBarPosition.Right -> + VerticalNavigationBar( tabIndex, onTabChanged, navController ) + NavigationBarPosition.Top, NavigationBarPosition.Bottom -> + HorizontalNavigationBar( tabIndex, onTabChanged, navController ) + } + navigationBar.add( navBarContent ) + + val appHeader: @Composable () -> Unit = { + Column( + verticalArrangement = Arrangement.SpaceBetween, + modifier = Modifier.fillMaxWidth() + ) { + if( UiType.RiMusic.isCurrent() ) + AppHeader( navController ).Draw() + + if ( NavigationBarPosition.Top.isCurrent() ) + navigationBar.Draw() + } + } + + val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() + val modifier: Modifier = + if( UiType.ViMusic.isCurrent() && navigationBar is HorizontalNavigationBar ) + Modifier + else + Modifier.nestedScroll( scrollBehavior.nestedScrollConnection ) + + Scaffold( + modifier = modifier, + containerColor = colorPalette().background0, + topBar = appHeader, + bottomBar = { + if ( NavigationBarPosition.Bottom.isCurrent() ) + navigationBar.Draw() + } + ) { + val paddingSides = WindowInsetsSides.Bottom + WindowInsetsSides.Horizontal + val innerPadding = + if( NavigationBarPosition.Top.isCurrent() ) + windowInsets.only( paddingSides ).asPaddingValues() + else + PaddingValues( Dp.Hairline ) + + Box( + Modifier + .padding(it) + .padding(innerPadding) + .fillMaxSize() + ) { + Row( + Modifier + .background(colorPalette().background0) + .fillMaxSize() + ) { + if( NavigationBarPosition.Left.isCurrent() ) + navigationBar.Draw() + + val topPadding = if ( UiType.ViMusic.isCurrent() ) 30.dp else 0.dp + AnimatedContent( + targetState = tabIndex, + transitionSpec = transition(), + content = content, + label = "", + modifier = Modifier.fillMaxHeight().padding( top = topPadding ) + ) + + if( NavigationBarPosition.Right.isCurrent() ) + navigationBar.Draw() + } + + val playerPosition by rememberPreference(playerPositionKey, PlayerPosition.Bottom) + val playerAlignment = + if (playerPosition == PlayerPosition.Top) + Alignment.TopCenter + else + Alignment.BottomCenter + + Box( + Modifier.padding( vertical = 30.dp ) + .align( playerAlignment ), + content = { mediaPlayer?.invoke() } + ) + } + } +} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/me/knighthat/button/Button.kt b/composeApp/src/androidMain/kotlin/me/knighthat/button/Button.kt new file mode 100644 index 0000000000..89c19382d5 --- /dev/null +++ b/composeApp/src/androidMain/kotlin/me/knighthat/button/Button.kt @@ -0,0 +1,34 @@ +package me.knighthat.button + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.Dp + +open class Button( + val iconId: Int, + var color: Color, + var padding: Dp, + var size: Dp, + var modifier: Modifier = Modifier +) { + fun modifier( newModifier: (Modifier) -> Modifier ) { + this.modifier = newModifier( this.modifier ) + } + + @Composable + open fun Draw() { + Image( + painter = painterResource( iconId ), + contentDescription = null, + colorFilter = ColorFilter.tint( color ), + modifier = modifier.padding( all = padding ) + .size( size ) + ) + } +} diff --git a/composeApp/src/androidMain/kotlin/me/knighthat/button/TextIconButton.kt b/composeApp/src/androidMain/kotlin/me/knighthat/button/TextIconButton.kt new file mode 100644 index 0000000000..318a7a9fe3 --- /dev/null +++ b/composeApp/src/androidMain/kotlin/me/knighthat/button/TextIconButton.kt @@ -0,0 +1,53 @@ +package me.knighthat.button + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.BasicText +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import it.fast4x.rimusic.utils.semiBold +import me.knighthat.typography + +class TextIconButton( + val text: String, + iconId: Int, + color: Color, + padding: Dp, + size: Dp, + modifier: Modifier = Modifier +): Button( iconId, color, padding, size, modifier ) { + + @Composable + override fun Draw() { + Column ( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.SpaceAround, + modifier = Modifier.padding( all = 5.dp ) + .fillMaxSize() + ){ + super.Draw() + Spacer( modifier = Modifier.height( 5.dp ) ) + BasicText( + text = text, + style = TextStyle( + fontSize = typography().xs.semiBold.fontSize, + fontWeight = typography().xs.semiBold.fontWeight, + fontFamily = typography().xs.semiBold.fontFamily, + color = color, + ), + maxLines = 2, + overflow = TextOverflow.Ellipsis + ) + } + } +} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/me/knighthat/component/header/ActionBar.kt b/composeApp/src/androidMain/kotlin/me/knighthat/component/header/ActionBar.kt new file mode 100644 index 0000000000..500dc35047 --- /dev/null +++ b/composeApp/src/androidMain/kotlin/me/knighthat/component/header/ActionBar.kt @@ -0,0 +1,80 @@ +package me.knighthat.component.header + +import androidx.compose.foundation.background +import androidx.compose.material3.HorizontalDivider +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.navigation.NavController +import it.fast4x.rimusic.R +import it.fast4x.rimusic.enums.NavRoutes +import me.knighthat.colorPalette +import me.knighthat.menu.DropdownMenu + +@Composable +private fun HamburgerMenu( + expanded: Boolean, + onItemClick: (NavRoutes) -> Unit, + onDismissRequest: () -> Unit +) { + val menu = DropdownMenu( + expanded = expanded, + modifier = Modifier.background( colorPalette().background0.copy(0.90f) ), + onDismissRequest = onDismissRequest + ) + // History button + menu.add( + DropdownMenu.Item( + R.drawable.history, + R.string.history + ) { onItemClick( NavRoutes.history ) } + ) + // Statistics button + menu.add( + DropdownMenu.Item( + R.drawable.stats_chart, + R.string.statistics + ) { onItemClick( NavRoutes.statistics ) } + ) + menu.add { HorizontalDivider() } + // Settings button + menu.add( + DropdownMenu.Item( + R.drawable.settings, + R.string.settings + ) { onItemClick( NavRoutes.settings ) } + ) + menu.Draw() +} + +// START +@Composable +fun ActionBar( + navController: NavController, +) { + var expanded by remember { mutableStateOf(false) } + + // Search Icon + HeaderIcon( R.drawable.search) { navController.navigate(NavRoutes.search.name) } + // Hamburger Icon + HeaderIcon( R.drawable.burger ) { expanded = !expanded } + + // Define actions for when item inside menu clicked, + // and when user clicks on places other than the menu (dismiss) + val onItemClick: (NavRoutes) -> Unit = { + expanded = false + navController.navigate(it.name) + } + val onDismissRequest: () -> Unit = { expanded = false } + + // Hamburger menu + HamburgerMenu( + expanded = expanded, + onItemClick = onItemClick, + onDismissRequest = onDismissRequest + ) +// END +} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/me/knighthat/component/header/AppHeader.kt b/composeApp/src/androidMain/kotlin/me/knighthat/component/header/AppHeader.kt new file mode 100644 index 0000000000..ed80649dd1 --- /dev/null +++ b/composeApp/src/androidMain/kotlin/me/knighthat/component/header/AppHeader.kt @@ -0,0 +1,74 @@ +package me.knighthat.component.header + +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarColors +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.unit.dp +import androidx.lifecycle.Lifecycle +import androidx.navigation.NavController +import it.fast4x.rimusic.R +import it.fast4x.rimusic.enums.NavRoutes +import it.fast4x.rimusic.extensions.games.pacman.Pacman +import it.fast4x.rimusic.ui.styling.favoritesIcon +import me.knighthat.button.Button +import me.knighthat.colorPalette + +class AppHeader( + val navController: NavController +) { + + companion object { + + @OptIn(ExperimentalMaterial3Api::class) + @Composable + fun colors(): TopAppBarColors = TopAppBarColors( + containerColor = colorPalette().background0, + titleContentColor = colorPalette().text, + scrolledContainerColor = colorPalette().background0, + navigationIconContentColor = colorPalette().background0, + actionIconContentColor = colorPalette().text + ) + } + + @Composable + private fun BackButton() { + if ( NavRoutes.home.isNotHere( navController ) ) + androidx.compose.material3.IconButton( + onClick = { + if (navController.currentBackStackEntry?.lifecycle?.currentState == Lifecycle.State.RESUMED) + navController.popBackStack() + } + ) { + Button( + R.drawable.chevron_back, + colorPalette().favoritesIcon, + 0.dp, + 24.dp + ).Draw() + } + } + + @OptIn(ExperimentalMaterial3Api::class) + @Composable + fun Draw() { + val showGames by remember { mutableStateOf(false) } + val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() + val context = LocalContext.current + + if (showGames) Pacman() + + TopAppBar( + title = { AppTitle( navController, context ) }, + actions = { ActionBar( navController ) }, + navigationIcon = { BackButton() }, + scrollBehavior = scrollBehavior, + colors = colors() + ) + } +} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/me/knighthat/component/header/AppTitle.kt b/composeApp/src/androidMain/kotlin/me/knighthat/component/header/AppTitle.kt new file mode 100644 index 0000000000..5a4ff4ce41 --- /dev/null +++ b/composeApp/src/androidMain/kotlin/me/knighthat/component/header/AppTitle.kt @@ -0,0 +1,143 @@ +package me.knighthat.component.header + +import android.content.Context +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.clickable +import androidx.compose.foundation.combinedClickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.text.BasicText +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableIntState +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.unit.dp +import androidx.navigation.NavController +import it.fast4x.rimusic.R +import it.fast4x.rimusic.enums.NavRoutes +import it.fast4x.rimusic.ui.components.themed.SmartMessage +import it.fast4x.rimusic.ui.styling.favoritesIcon +import it.fast4x.rimusic.utils.semiBold +import me.knighthat.button.Button +import me.knighthat.colorPalette +import me.knighthat.typography + +private fun appIconClickAction( + navController: NavController, + countToReveal: MutableIntState, + context: Context +) { + countToReveal.intValue++ + + val message: String = + when( countToReveal.intValue ) { + 10 -> { + countToReveal.intValue = 0 + navController.navigate( NavRoutes.gamePacman.name ) + "" + } + 3 -> "Do you like clicking? Then continue..." + 6 -> "Okay, you’re looking for something, keep..." + 9 -> "You are a number one, click and enjoy the surprise" + else -> "" + } + if( message.isNotEmpty() ) + SmartMessage( + message = message, + durationLong = true, + context = context + ) +} + +private fun appIconLongClickAction( + navController: NavController, + context: Context +) { + SmartMessage( + "You are a number one, click and enjoy the surprise", + durationLong = true, + context = context + ) + navController.navigate( NavRoutes.gameSnake.name ) +} + +@OptIn(ExperimentalFoundationApi::class) +@Composable +private fun AppLogo( + navController: NavController, + context: Context +) { + val countToReveal = remember { mutableIntStateOf(0) } + val modifier = Modifier.combinedClickable( + onClick = { appIconClickAction( navController, countToReveal, context ) }, + onLongClick = { appIconLongClickAction( navController, context ) } + ) + + Button( + iconId = R.drawable.app_icon, + color = colorPalette().favoritesIcon, + padding = 0.dp, + size = 36.dp, + modifier = modifier + ).Draw() +} + +@Composable +private fun AppLogoText( navController: NavController ) { + val iconTextClick: () -> Unit = { + if ( NavRoutes.home.isNotHere( navController ) ) + navController.navigate(NavRoutes.home.name) + } + + Button( + iconId = R.drawable.app_logo_text, + color = AppBar.contentColor(), + padding = 0.dp, + size = 100.dp, + modifier = Modifier.clickable { iconTextClick() } + ).Draw() +} + +// START +@Composable +fun AppTitle( + navController: NavController, + context: Context +) { + Row( + horizontalArrangement = Arrangement.spacedBy( 5.dp ), + verticalAlignment = Alignment.CenterVertically + ) { + AppLogo( navController, context ) + AppLogoText( navController ) + + if(Preference.parentalControl()) + Button( + iconId = R.drawable.shield_checkmark, + color = AppBar.contentColor(), + padding = 0.dp, + size = 20.dp + ).Draw() + + if (Preference.debugLog()) + BasicText( + text = stringResource(R.string.info_debug_mode_enabled), + style = TextStyle( + fontSize = typography().xxs.semiBold.fontSize, + fontWeight = typography().xxs.semiBold.fontWeight, + fontFamily = typography().xxs.semiBold.fontFamily, + color = colorPalette().red + ), + modifier = Modifier + .clickable { + SmartMessage(context.resources.getString(R.string.info_debug_mode_is_enabled), durationLong = true, context = context) + navController.navigate(NavRoutes.settings.name) + } + ) + } +// END +} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/me/knighthat/component/header/Utils.kt b/composeApp/src/androidMain/kotlin/me/knighthat/component/header/Utils.kt new file mode 100644 index 0000000000..0dcad4d4f0 --- /dev/null +++ b/composeApp/src/androidMain/kotlin/me/knighthat/component/header/Utils.kt @@ -0,0 +1,67 @@ +package me.knighthat.component.header + +import androidx.compose.foundation.layout.size +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.LocalContentColor +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import it.fast4x.rimusic.enums.ColorPaletteMode +import it.fast4x.rimusic.utils.colorPaletteModeKey +import it.fast4x.rimusic.utils.logDebugEnabledKey +import it.fast4x.rimusic.utils.parentalControlEnabledKey +import it.fast4x.rimusic.utils.rememberPreference +import me.knighthat.colorPalette + +@Composable +internal fun HeaderIcon( + iconId: Int, + tint: Color = LocalContentColor.current, + size: Dp = 24.dp, + onClick: () -> Unit +) { + IconButton( onClick ) { + Icon( + imageVector = ImageVector.vectorResource( iconId ), + contentDescription = null, + modifier = Modifier.size( size ), + tint = tint + ) + } +} + +internal class Preference { + + internal companion object { + + @Composable + fun parentalControl(): Boolean = + rememberPreference( parentalControlEnabledKey, false ).value + + @Composable + fun debugLog(): Boolean = + rememberPreference( logDebugEnabledKey, false ).value + + @Composable + fun colorTheme(): ColorPaletteMode = + rememberPreference( colorPaletteModeKey, ColorPaletteMode.Dark ).value + } +} + +internal class AppBar { + + internal companion object { + + @Composable + fun contentColor(): Color = + when(Preference.colorTheme()) { + ColorPaletteMode.Light, ColorPaletteMode.System -> colorPalette().text + else -> Color.White + } + } +} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/me/knighthat/component/nav/AbstractNavigationBar.kt b/composeApp/src/androidMain/kotlin/me/knighthat/component/nav/AbstractNavigationBar.kt new file mode 100644 index 0000000000..95f1948a49 --- /dev/null +++ b/composeApp/src/androidMain/kotlin/me/knighthat/component/nav/AbstractNavigationBar.kt @@ -0,0 +1,102 @@ +package me.knighthat.component.nav + +import android.annotation.SuppressLint +import androidx.compose.foundation.clickable +import androidx.compose.runtime.Composable +import androidx.compose.runtime.ReadOnlyComposable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import androidx.lifecycle.Lifecycle +import androidx.navigation.NavController +import it.fast4x.rimusic.R +import it.fast4x.rimusic.enums.NavRoutes +import it.fast4x.rimusic.ui.styling.favoritesIcon +import me.knighthat.button.Button +import me.knighthat.colorPalette + +@SuppressLint("ComposableNaming") +abstract class AbstractNavigationBar( + val navController: NavController, + val modifier: Modifier = Modifier +) { + internal val buttonList: MutableList<@Composable () -> Unit> = mutableListOf() + + @ReadOnlyComposable + @Composable + internal open fun BackButton(): NavigationButton { + val button = NavigationButton( navController, R.drawable.chevron_back, colorPalette().favoritesIcon ) + button.clickEvent { + if ( navController.currentBackStackEntry?.lifecycle?.currentState == Lifecycle.State.RESUMED ) + navController.popBackStack() + } + return button + } + + @ReadOnlyComposable + @Composable + internal open fun SettingsButton(): NavigationButton { + return NavigationButton( + navController, + R.drawable.settings, + colorPalette().favoritesIcon, + NavRoutes.settings.name + ) + } + + @ReadOnlyComposable + @Composable + internal open fun StatsButton(): NavigationButton { + return NavigationButton( + navController, + R.drawable.stats_chart, + colorPalette().textSecondary, + NavRoutes.statistics.name + ) + } + + @ReadOnlyComposable + @Composable + internal open fun SearchButton(): NavigationButton { + return NavigationButton( + navController, + R.drawable.search, + colorPalette().textSecondary, + NavRoutes.search.name + ) + } + + @Composable + abstract fun add( buttons: @Composable (@Composable (Int, String, Int) -> Unit) -> Unit ) + + @Composable + abstract fun Draw() + + @Composable + fun buttonList(): MutableList<@Composable () -> Unit> = remember { buttonList } +} + +internal class NavigationButton( + val navController: NavController, + iconId: Int, + color: Color, + val destination: String = "", + padding: Dp = 0.dp, + size: Dp = 0.dp, + modifier: Modifier = Modifier +): Button( iconId, color, padding, size, modifier ) { + + fun clickEvent( event: (NavigationButton) -> Unit ) { + modifier = modifier.clickable { event(this@NavigationButton) } + } + + @Composable + override fun Draw() { + if( destination.isNotBlank() ) + clickEvent { navController.navigate( destination ) } + + super.Draw() + } +} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/me/knighthat/component/nav/HomeNavigation.kt b/composeApp/src/androidMain/kotlin/me/knighthat/component/nav/HomeNavigation.kt new file mode 100644 index 0000000000..c479817670 --- /dev/null +++ b/composeApp/src/androidMain/kotlin/me/knighthat/component/nav/HomeNavigation.kt @@ -0,0 +1,6 @@ +package me.knighthat.component.nav + +object HomeNavigation { + + +} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/me/knighthat/component/nav/HorizontalNavigationBar.kt b/composeApp/src/androidMain/kotlin/me/knighthat/component/nav/HorizontalNavigationBar.kt new file mode 100644 index 0000000000..1c3b6be181 --- /dev/null +++ b/composeApp/src/androidMain/kotlin/me/knighthat/component/nav/HorizontalNavigationBar.kt @@ -0,0 +1,207 @@ +package me.knighthat.component.nav + +import android.annotation.SuppressLint +import androidx.compose.animation.animateColor +import androidx.compose.animation.core.updateTransition +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.horizontalScroll +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.systemBars +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import androidx.navigation.NavController +import it.fast4x.rimusic.enums.NavRoutes +import it.fast4x.rimusic.enums.NavigationBarPosition +import it.fast4x.rimusic.enums.NavigationBarType +import it.fast4x.rimusic.enums.UiType +import it.fast4x.rimusic.ui.styling.Dimensions +import it.fast4x.rimusic.utils.rememberPreference +import it.fast4x.rimusic.utils.showSearchTabKey +import it.fast4x.rimusic.utils.showStatsInNavbarKey +import me.knighthat.button.Button +import me.knighthat.button.TextIconButton +import me.knighthat.colorPalette +import me.knighthat.showSearchIconInNav +import me.knighthat.showStatsIconInNav + +// Shown when "Navigation bar position" is set to "top" or "bottom" +class HorizontalNavigationBar( + val tabIndex: Int, + val onTabChanged: (Int) -> Unit, + navController: NavController, + modifier: Modifier = Modifier +): AbstractNavigationBar( navController, modifier ) { + + private fun navButtonProperties(): Modifier { + val padding: Dp = 4.dp + val size: Dp = 24.dp + val border: Shape = CircleShape + + return Modifier.padding( all = padding ) + .size( size ) + .clip( shape = border ) + } + + @Composable + private fun addButton( button: Button, modifier: Modifier = Modifier ) = + // buttonList() duplicates button instead of updating them. + // Do NOT use it + buttonList.add { + Box( modifier ) { button.Draw() } + } + + @SuppressLint("ComposableNaming") + @Composable + private fun addButton( index: Int, button: Button, modifier: Modifier = Modifier ) = + // buttonList() duplicates button instead of updating them + // Do NOT use it + buttonList.add( index ) { + Box( modifier ) { button.Draw() } + } + + @Composable + private fun bottomPadding(): Dp { + return if ( NavigationBarPosition.Bottom.isCurrent() ) + with( LocalDensity.current ) { + WindowInsets.systemBars.getBottom( this ).toDp() + } + else + 5.dp + } + + private fun topPadding(): Dp = 0.dp + + @Composable + override fun add(buttons: @Composable (@Composable (Int, String, Int) -> Unit) -> Unit) { + val transition = updateTransition(targetState = tabIndex, label = null) + + buttons { index, text, iconId -> + + val color by transition.animateColor(label = "") { + if (it == index) colorPalette().text else colorPalette().textDisabled + } + + val button: Button = + if ( NavigationBarType.IconOnly.isCurrent() ) + Button( iconId, color, 12.dp, 20.dp ) + else + TextIconButton( text, iconId, color, 0.dp, Dimensions.navigationRailIconOffset * 3 ) + + val contentModifier = Modifier + .clip(RoundedCornerShape(12.dp)) + .clickable(onClick = { onTabChanged(index) }) + + addButton( button, contentModifier ) + } + } + + @Composable + override fun BackButton(): NavigationButton { + val button = super.BackButton() + button.modifier = this.navButtonProperties() + return button + } + + @Composable + override fun SettingsButton(): NavigationButton { + val button = super.SettingsButton() + button.modifier = this.navButtonProperties() + return button + } + + @Composable + override fun StatsButton(): NavigationButton { + val button = super.StatsButton() + button.modifier = this.navButtonProperties() + return button + } + + @Composable + override fun SearchButton(): NavigationButton { + val button = super.SearchButton() + button.modifier = this.navButtonProperties() + return button + } + + @Composable + override fun Draw() { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Bottom, + modifier = modifier.padding( top = topPadding(), bottom = bottomPadding() ) + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.SpaceAround, + modifier = Modifier + .fillMaxWidth() + .height(Dimensions.navigationBarHeight - 10.dp) + ) { + + val scrollState = rememberScrollState() + val roundedCornerShape = + if ( NavigationBarPosition.Bottom.isCurrent() ) + RoundedCornerShape(topStart = 12.dp, topEnd = 12.dp) + else + RoundedCornerShape(bottomStart = 12.dp, bottomEnd = 12.dp) + + // Settings button only visible when + // UI is not RiMusic and current location isn't home screen + if( UiType.ViMusic.isCurrent() && NavRoutes.home.isNotHere( navController ) ) + BackButton().Draw() + + Box( + modifier = Modifier + .fillMaxWidth() + .clip(roundedCornerShape) + .background(colorPalette().background1) + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceEvenly, + modifier = Modifier + .fillMaxWidth() + .fillMaxSize() + .horizontalScroll(scrollState), + content = { buttonList().forEach { it() } } + ) + } + + // Search button only visible when + // UI is not RiMusic and must be explicitly turned on + if( UiType.ViMusic.isCurrent() && showSearchIconInNav() ) + SearchButton() + + // Settings button only visible when + // UI is not RiMusic + if( UiType.ViMusic.isCurrent() ) + SettingsButton().Draw() + + // Statistics button only visible when + // UI is not RiMusic and must be explicitly turned on + if( UiType.ViMusic.isCurrent() && showStatsIconInNav() ) + StatsButton() + } + } + } +} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/me/knighthat/component/nav/VerticalNavigationBar.kt b/composeApp/src/androidMain/kotlin/me/knighthat/component/nav/VerticalNavigationBar.kt new file mode 100644 index 0000000000..f2df0790f1 --- /dev/null +++ b/composeApp/src/androidMain/kotlin/me/knighthat/component/nav/VerticalNavigationBar.kt @@ -0,0 +1,249 @@ +package me.knighthat.component.nav + +import androidx.compose.animation.animateColor +import androidx.compose.animation.core.animateFloat +import androidx.compose.animation.core.updateTransition +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.BasicText +import androidx.compose.foundation.verticalScroll +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.rotate +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.layout.layout +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.unit.Constraints +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import androidx.navigation.NavController +import it.fast4x.rimusic.enums.NavRoutes +import it.fast4x.rimusic.enums.NavigationBarType +import it.fast4x.rimusic.enums.UiType +import it.fast4x.rimusic.ui.styling.Dimensions +import it.fast4x.rimusic.utils.isLandscape +import it.fast4x.rimusic.utils.semiBold +import me.knighthat.button.Button +import me.knighthat.colorPalette +import me.knighthat.showSearchIconInNav +import me.knighthat.showStatsIconInNav +import me.knighthat.typography + +// TODO: Move this to where it belongs. Currently, UNKNOWN +fun Modifier.vertical( enabled: Boolean = true ) = + if ( enabled ) + layout { measurable, constraints -> + val c: Constraints = constraints.copy( maxWidth = Int.MAX_VALUE ) + val placeable = measurable.measure( c ) + + layout( placeable.height, placeable.width ) { + placeable.place( + x = -(placeable.width / 2 - placeable.height / 2), + y = -(placeable.height / 2 - placeable.width / 2) + ) + } + } + else this + +// Shown when "Navigation bar position" is set to "left" or "right" +class VerticalNavigationBar( + val tabIndex: Int, + val onTabChanged: (Int) -> Unit, + navController: NavController, + modifier: Modifier = Modifier +): AbstractNavigationBar( navController, modifier ) { + + @Composable + private fun addButton( component: @Composable () -> Unit ) = + // buttonList() duplicates button instead of updating them + // Do NOT use it + super.buttonList.add( component ) + + @Composable + override fun add(buttons: @Composable (@Composable (Int, String, Int) -> Unit) -> Unit ) { + val transition = updateTransition( targetState = tabIndex, label = null ) + val isLandscape: Boolean = isLandscape + + buttons { index, text, iconId -> + val textColor by transition.animateColor(label = "") { + if (it == index) + colorPalette().text + else + colorPalette().textDisabled + } + val dothAlpha by transition.animateFloat(label = "") { + if (it == index) + 1f + else + 0f + } + + val textContent: @Composable () -> Unit = { + if ( NavigationBarType.IconAndText.isCurrent() ) + BasicText( + text = text, + style = TextStyle( + fontSize = typography().xs.semiBold.fontSize, + fontWeight = typography().xs.semiBold.fontWeight, + color = colorPalette().text, + ), + modifier = Modifier.vertical( enabled = !isLandscape ) + .rotate(if (isLandscape) 0f else -90f) + .padding(horizontal = 16.dp) + ) + } + + val buttonModifier: Modifier = + if ( NavigationBarType.IconOnly.isCurrent() ) { + Modifier.padding( top = 12.dp, bottom = 12.dp ) + .size(24.dp) + } else { + Modifier.vertical( enabled = !isLandscape ) + .size( Dimensions.navigationRailIconOffset * 2 ) + .graphicsLayer { + alpha = dothAlpha + translationX = (1f - dothAlpha) * -48.dp.toPx() + rotationZ = if (isLandscape) 0f else -90f + } + } + val button = Button( iconId, textColor, 0.dp, 0.dp, buttonModifier ) + val contentModifier = Modifier.clip( RoundedCornerShape(24.dp) ) + .clickable( onClick = { onTabChanged(index) } ) + .padding( vertical = 8.dp ) + val result: @Composable () -> Unit = { + if( isLandscape ) + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = contentModifier + ) { + button.Draw() + textContent() + } + else + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = contentModifier + ) { + button.Draw() + textContent() + } + } + + addButton( result ) + } + } + + @Composable + override fun BackButton(): NavigationButton { + val button = super.BackButton() + button.modifier { + it.offset( 0.dp, 7.dp ) + .clip( CircleShape ) + .padding( top = 12.dp, bottom = 12.dp ) + .size( 24.dp ) + } + return button + } + + @Composable + override fun SettingsButton(): NavigationButton { + val button = super.SettingsButton() + button.modifier { + it.offset( 0.dp, 7.dp ) + .clip( CircleShape ) + .padding( top = 12.dp, bottom = 12.dp ) + .size( 24.dp ) + } + return button + } + + @Composable + override fun StatsButton(): NavigationButton { + val button = super.StatsButton() + button.modifier { + it.offset( 0.dp, 70.dp ) + .clip( CircleShape ) + .padding( top = 12.dp, bottom = 12.dp ) + .size( 24.dp ) + } + return button + } + + @Composable + override fun SearchButton(): NavigationButton { + val button = super.SearchButton() + button.modifier { + it.padding( all = 12.dp ) + .size( 24.dp ) + } + return button + } + + @Composable + override fun Draw() { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = modifier.verticalScroll( rememberScrollState() ) + ) { + val boxPadding: Dp = + if( UiType.ViMusic.isCurrent() ) + 30.dp + else + Dp.Hairline + Box( + contentAlignment = Alignment.TopCenter, + modifier = Modifier + .height( + if( UiType.ViMusic.isCurrent() ) + if ( showStatsIconInNav() ) + Dimensions.headerHeight + else + Dimensions.halfheaderHeight + else 0.dp + ).padding( top = boxPadding ) + ) { + // Show settings and statistics buttons in homepage + // Show back button in other screens + if( navController.currentBackStackEntry?.destination?.route == NavRoutes.home.name ) { + SettingsButton().Draw() + StatsButton().Draw() + } else + BackButton().Draw() + } + + Column( + horizontalAlignment = Alignment.CenterHorizontally, + content = { buttonList().forEach { it() } } + ) + + // Only show search icon when UI is ViMusic and + // setting is turned on + if( UiType.ViMusic.isCurrent() && showSearchIconInNav() ) { + val iconWidth: Dp = + if( isLandscape ) + Dimensions.navigationRailWidthLandscape + else + Dimensions.navigationRailWidth + val iconHeight: Dp = Dimensions.halfheaderHeight + + Box( + contentAlignment = Alignment.TopCenter, + modifier = Modifier.size( iconWidth, iconHeight ), + content = { SearchButton().Draw() } + ) + } + } + } +} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/me/knighthat/menu/DropdownMenu.kt b/composeApp/src/androidMain/kotlin/me/knighthat/menu/DropdownMenu.kt new file mode 100644 index 0000000000..05bf35fa66 --- /dev/null +++ b/composeApp/src/androidMain/kotlin/me/knighthat/menu/DropdownMenu.kt @@ -0,0 +1,93 @@ +package me.knighthat.menu + +import androidx.compose.foundation.layout.size +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.Icon +import androidx.compose.material3.MenuItemColors +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import it.fast4x.rimusic.ui.styling.favoritesIcon +import me.knighthat.colorPalette + +class DropdownMenu( + val expanded: Boolean, + val containerColor: Color = Color.Transparent, + val modifier: Modifier = Modifier, + val onDismissRequest: () -> Unit +) { + + private val _components: MutableList<@Composable () -> Unit> = mutableListOf() + + @Composable + fun components() = remember { _components } + + @Composable + fun add( item: Item ) = _components.add { item.Draw() } + + @Composable + fun add( component: @Composable () -> Unit) = _components.add( component ) + + @Composable + fun Draw() { + DropdownMenu( + expanded = expanded, + onDismissRequest = onDismissRequest, + containerColor = containerColor, + modifier = modifier, + content = { components().forEach { it() } } + ) + } + + class Item( + val iconId: Int, + val textId: Int, + val size: Dp = 24.dp, + val padding: Dp = Dp.Hairline, + val colors: MenuItemColors? = null, + val modifier: Modifier = Modifier, + val onClick: () -> Unit + ) { + + companion object { + + @Composable + fun colors(): MenuItemColors { + return MenuItemColors( + leadingIconColor = colorPalette().favoritesIcon, + trailingIconColor = colorPalette().favoritesIcon, + textColor = colorPalette().textSecondary, + disabledTextColor = colorPalette().text, + disabledLeadingIconColor = colorPalette().text, + disabledTrailingIconColor = colorPalette().text, + ) + } + } + + @Composable + fun Draw() { + val icon: @Composable () -> Unit = { + Icon( + painter = painterResource( iconId ), + contentDescription = null, + modifier = modifier.size( 24.dp ) + ) + } + + DropdownMenuItem( + enabled = true, + colors = colors ?: colors(), + text = { Text( stringResource(textId) ) }, + leadingIcon = icon, + onClick = onClick + ) + } + } +} \ No newline at end of file diff --git a/composeApp/src/androidMain/res/drawable-nodpi/banner_tv_foreground.xml b/composeApp/src/androidMain/res/drawable-nodpi/banner_tv_foreground.xml deleted file mode 100644 index b3f23302da..0000000000 --- a/composeApp/src/androidMain/res/drawable-nodpi/banner_tv_foreground.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - diff --git a/composeApp/src/androidMain/res/drawable-nodpi/ic_launcher_foreground_inverse.xml b/composeApp/src/androidMain/res/drawable-nodpi/ic_launcher_foreground_inverse.xml deleted file mode 100644 index 6ed72c25c3..0000000000 --- a/composeApp/src/androidMain/res/drawable-nodpi/ic_launcher_foreground_inverse.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - diff --git a/composeApp/src/androidMain/res/drawable-nodpi/splash_screen.xml b/composeApp/src/androidMain/res/drawable-nodpi/splash_screen.xml deleted file mode 100644 index faf6a699cf..0000000000 --- a/composeApp/src/androidMain/res/drawable-nodpi/splash_screen.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - diff --git a/composeApp/src/androidMain/res/drawable-nodpi/sync_circle.xml b/composeApp/src/androidMain/res/drawable-nodpi/sync_circle.xml deleted file mode 100644 index d9f323c643..0000000000 --- a/composeApp/src/androidMain/res/drawable-nodpi/sync_circle.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml b/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml index 06a8bbf9eb..7353dbd1fd 100644 --- a/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml b/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml index 06a8bbf9eb..7353dbd1fd 100644 --- a/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_logo_round.xml b/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_logo_round.xml deleted file mode 100644 index ed39473f7f..0000000000 --- a/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_logo_round.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher.webp b/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher.webp index a323851a7e..b50c8f4f06 100644 Binary files a/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher.webp and b/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher_round.webp b/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher_round.webp index 9a7ff64925..61405bf45f 100644 Binary files a/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher_round.webp and b/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/composeApp/src/androidMain/res/mipmap-hdpi/ic_logo.webp b/composeApp/src/androidMain/res/mipmap-hdpi/ic_logo.webp deleted file mode 100644 index 3620ae86fe..0000000000 Binary files a/composeApp/src/androidMain/res/mipmap-hdpi/ic_logo.webp and /dev/null differ diff --git a/composeApp/src/androidMain/res/mipmap-hdpi/ic_logo_round.webp b/composeApp/src/androidMain/res/mipmap-hdpi/ic_logo_round.webp deleted file mode 100644 index 2c74ce0e9f..0000000000 Binary files a/composeApp/src/androidMain/res/mipmap-hdpi/ic_logo_round.webp and /dev/null differ diff --git a/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher.webp b/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher.webp index ec6c490d44..bbc3424c1e 100644 Binary files a/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher.webp and b/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher_round.webp b/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher_round.webp index 8a8f07a405..b5d1f62a87 100644 Binary files a/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher_round.webp and b/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/composeApp/src/androidMain/res/mipmap-mdpi/ic_logo.webp b/composeApp/src/androidMain/res/mipmap-mdpi/ic_logo.webp deleted file mode 100644 index 2ec1ea53f9..0000000000 Binary files a/composeApp/src/androidMain/res/mipmap-mdpi/ic_logo.webp and /dev/null differ diff --git a/composeApp/src/androidMain/res/mipmap-mdpi/ic_logo_round.webp b/composeApp/src/androidMain/res/mipmap-mdpi/ic_logo_round.webp deleted file mode 100644 index 5e7b645d12..0000000000 Binary files a/composeApp/src/androidMain/res/mipmap-mdpi/ic_logo_round.webp and /dev/null differ diff --git a/composeApp/src/androidMain/res/mipmap-xhdpi/ic_banner.png b/composeApp/src/androidMain/res/mipmap-xhdpi/ic_banner.png deleted file mode 100644 index a774b0c251..0000000000 Binary files a/composeApp/src/androidMain/res/mipmap-xhdpi/ic_banner.png and /dev/null differ diff --git a/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher.webp b/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher.webp index 6542fdb6b8..d31c493aca 100644 Binary files a/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher.webp and b/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.webp b/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.webp index 147c456b3c..33ce23500e 100644 Binary files a/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.webp and b/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/composeApp/src/androidMain/res/mipmap-xhdpi/ic_logo.webp b/composeApp/src/androidMain/res/mipmap-xhdpi/ic_logo.webp deleted file mode 100644 index 153d2caefe..0000000000 Binary files a/composeApp/src/androidMain/res/mipmap-xhdpi/ic_logo.webp and /dev/null differ diff --git a/composeApp/src/androidMain/res/mipmap-xhdpi/ic_logo_round.webp b/composeApp/src/androidMain/res/mipmap-xhdpi/ic_logo_round.webp deleted file mode 100644 index 701248ebce..0000000000 Binary files a/composeApp/src/androidMain/res/mipmap-xhdpi/ic_logo_round.webp and /dev/null differ diff --git a/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher.webp b/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher.webp index ddf9fc32ed..cad79aba99 100644 Binary files a/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher.webp and b/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.webp b/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.webp index 999b36205d..bb116ea183 100644 Binary files a/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.webp and b/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_logo.webp b/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_logo.webp deleted file mode 100644 index dd55dd6ef9..0000000000 Binary files a/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_logo.webp and /dev/null differ diff --git a/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_logo_round.webp b/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_logo_round.webp deleted file mode 100644 index c2585a9570..0000000000 Binary files a/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_logo_round.webp and /dev/null differ diff --git a/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.webp b/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.webp index 5d2f581aa2..730a833977 100644 Binary files a/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.webp and b/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.webp b/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.webp index 0b8cc00f1b..7ffb52fe83 100644 Binary files a/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.webp and b/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_logo.webp b/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_logo.webp deleted file mode 100644 index 17f8e48080..0000000000 Binary files a/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_logo.webp and /dev/null differ diff --git a/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_logo_round.webp b/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_logo_round.webp deleted file mode 100644 index dd3d400223..0000000000 Binary files a/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_logo_round.webp and /dev/null differ diff --git a/composeApp/src/androidMain/res/values-af/strings.xml b/composeApp/src/androidMain/res/values-af/strings.xml index ed493c4b3d..a326654181 100644 --- a/composeApp/src/androidMain/res/values-af/strings.xml +++ b/composeApp/src/androidMain/res/values-af/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-ar/strings.xml b/composeApp/src/androidMain/res/values-ar/strings.xml index b73dd376e3..b93ec943bc 100644 --- a/composeApp/src/androidMain/res/values-ar/strings.xml +++ b/composeApp/src/androidMain/res/values-ar/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-ba/strings.xml b/composeApp/src/androidMain/res/values-ba/strings.xml index 2fd70de54d..67f2052c2d 100644 --- a/composeApp/src/androidMain/res/values-ba/strings.xml +++ b/composeApp/src/androidMain/res/values-ba/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-bn/strings.xml b/composeApp/src/androidMain/res/values-bn/strings.xml index 4d0f460d96..80806881d5 100644 --- a/composeApp/src/androidMain/res/values-bn/strings.xml +++ b/composeApp/src/androidMain/res/values-bn/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-ca/strings.xml b/composeApp/src/androidMain/res/values-ca/strings.xml index 06bd693e55..272090f77f 100644 --- a/composeApp/src/androidMain/res/values-ca/strings.xml +++ b/composeApp/src/androidMain/res/values-ca/strings.xml @@ -747,10 +747,13 @@ Guany de sonoritat de base Guany objectiu per a la normalització de la sonoritat Media cannot be played - Full Screen Lyrics Components - Title and Artists - Mini Queue + Components de Lletres a pantalla completa + Títol i Artistes + Mini cua Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-cs/strings.xml b/composeApp/src/androidMain/res/values-cs/strings.xml index 942e5d763b..80c68d4a2c 100644 --- a/composeApp/src/androidMain/res/values-cs/strings.xml +++ b/composeApp/src/androidMain/res/values-cs/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-da/strings.xml b/composeApp/src/androidMain/res/values-da/strings.xml index 545f2418a2..fc70b140cf 100644 --- a/composeApp/src/androidMain/res/values-da/strings.xml +++ b/composeApp/src/androidMain/res/values-da/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-de/strings.xml b/composeApp/src/androidMain/res/values-de/strings.xml index 368b24de4c..f3365f1156 100644 --- a/composeApp/src/androidMain/res/values-de/strings.xml +++ b/composeApp/src/androidMain/res/values-de/strings.xml @@ -747,10 +747,13 @@ Lautstärke Grundverstärkung Zielverstärkung für die Lautstärkenormalisierung Media cannot be played - Full Screen Lyrics Components - Title and Artists - Mini Queue - Controls + Vollbild-Songtext-Komponenten + Titel und Künstler + Mini-Warteschlange + Steuerung Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-el/strings.xml b/composeApp/src/androidMain/res/values-el/strings.xml index f4c6798eaf..9b0eb95bf8 100644 --- a/composeApp/src/androidMain/res/values-el/strings.xml +++ b/composeApp/src/androidMain/res/values-el/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-en/strings.xml b/composeApp/src/androidMain/res/values-en/strings.xml index 3be1b34a34..825fb4d268 100644 --- a/composeApp/src/androidMain/res/values-en/strings.xml +++ b/composeApp/src/androidMain/res/values-en/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-eo/strings.xml b/composeApp/src/androidMain/res/values-eo/strings.xml index b67643adfb..e844f1ca99 100644 --- a/composeApp/src/androidMain/res/values-eo/strings.xml +++ b/composeApp/src/androidMain/res/values-eo/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-es/strings.xml b/composeApp/src/androidMain/res/values-es/strings.xml index 02cc73d76f..f8df9ecd98 100644 --- a/composeApp/src/androidMain/res/values-es/strings.xml +++ b/composeApp/src/androidMain/res/values-es/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-et/strings.xml b/composeApp/src/androidMain/res/values-et/strings.xml index 5b1c3e2902..d670269ff7 100644 --- a/composeApp/src/androidMain/res/values-et/strings.xml +++ b/composeApp/src/androidMain/res/values-et/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-fa/strings.xml b/composeApp/src/androidMain/res/values-fa/strings.xml index 31abc24aec..e7a1822bc7 100644 --- a/composeApp/src/androidMain/res/values-fa/strings.xml +++ b/composeApp/src/androidMain/res/values-fa/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-fi/strings.xml b/composeApp/src/androidMain/res/values-fi/strings.xml index 040c281d4e..d0a6d303b3 100644 --- a/composeApp/src/androidMain/res/values-fi/strings.xml +++ b/composeApp/src/androidMain/res/values-fi/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-fil/strings.xml b/composeApp/src/androidMain/res/values-fil/strings.xml index c78ae443d6..853aea20ba 100644 --- a/composeApp/src/androidMain/res/values-fil/strings.xml +++ b/composeApp/src/androidMain/res/values-fil/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-fr/strings.xml b/composeApp/src/androidMain/res/values-fr/strings.xml index e7d3df266e..8ec045d59a 100644 --- a/composeApp/src/androidMain/res/values-fr/strings.xml +++ b/composeApp/src/androidMain/res/values-fr/strings.xml @@ -755,4 +755,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-ga/strings.xml b/composeApp/src/androidMain/res/values-ga/strings.xml index 14874dbc61..67db0c71e2 100644 --- a/composeApp/src/androidMain/res/values-ga/strings.xml +++ b/composeApp/src/androidMain/res/values-ga/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-gl/strings.xml b/composeApp/src/androidMain/res/values-gl/strings.xml index b71687a19d..60f6030163 100644 --- a/composeApp/src/androidMain/res/values-gl/strings.xml +++ b/composeApp/src/androidMain/res/values-gl/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-he/strings.xml b/composeApp/src/androidMain/res/values-he/strings.xml index 1bd7a34980..61f4b5ad85 100644 --- a/composeApp/src/androidMain/res/values-he/strings.xml +++ b/composeApp/src/androidMain/res/values-he/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-hi/strings.xml b/composeApp/src/androidMain/res/values-hi/strings.xml index 895fbc00df..ba161dd20f 100644 --- a/composeApp/src/androidMain/res/values-hi/strings.xml +++ b/composeApp/src/androidMain/res/values-hi/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-hu/strings.xml b/composeApp/src/androidMain/res/values-hu/strings.xml index a72103cb6e..8ed6aa6df1 100644 --- a/composeApp/src/androidMain/res/values-hu/strings.xml +++ b/composeApp/src/androidMain/res/values-hu/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-ia/strings.xml b/composeApp/src/androidMain/res/values-ia/strings.xml index e80ff1b5dc..3fe0946ea7 100644 --- a/composeApp/src/androidMain/res/values-ia/strings.xml +++ b/composeApp/src/androidMain/res/values-ia/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-id/strings.xml b/composeApp/src/androidMain/res/values-id/strings.xml index d7192a3948..bef90b063d 100644 --- a/composeApp/src/androidMain/res/values-id/strings.xml +++ b/composeApp/src/androidMain/res/values-id/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-in/strings.xml b/composeApp/src/androidMain/res/values-in/strings.xml index 118871d034..33f587bf60 100644 --- a/composeApp/src/androidMain/res/values-in/strings.xml +++ b/composeApp/src/androidMain/res/values-in/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-it/strings.xml b/composeApp/src/androidMain/res/values-it/strings.xml index dbc76d56b2..92b1442d51 100644 --- a/composeApp/src/androidMain/res/values-it/strings.xml +++ b/composeApp/src/androidMain/res/values-it/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-iw/strings.xml b/composeApp/src/androidMain/res/values-iw/strings.xml index 4c4dd35951..4532d3f1b7 100644 --- a/composeApp/src/androidMain/res/values-iw/strings.xml +++ b/composeApp/src/androidMain/res/values-iw/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-ja/strings.xml b/composeApp/src/androidMain/res/values-ja/strings.xml index 6a15653fef..37156e488b 100644 --- a/composeApp/src/androidMain/res/values-ja/strings.xml +++ b/composeApp/src/androidMain/res/values-ja/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-ko/strings.xml b/composeApp/src/androidMain/res/values-ko/strings.xml index c658f1c07b..08c44acaf1 100644 --- a/composeApp/src/androidMain/res/values-ko/strings.xml +++ b/composeApp/src/androidMain/res/values-ko/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-ml/strings.xml b/composeApp/src/androidMain/res/values-ml/strings.xml index b8fbb701db..33df5117d0 100644 --- a/composeApp/src/androidMain/res/values-ml/strings.xml +++ b/composeApp/src/androidMain/res/values-ml/strings.xml @@ -123,7 +123,7 @@ %1$s പ്ലേബാക്ക് ഇവന്റുകൾ നീക്കം ചെയ്യുക Existing data will be overwritten. %1$s will automatically close itself after restoring the database. ഫോൾഡർ കാഷെ - Attention by clicking on the button below, the cache location folder will be reset to the default + സ്ഥലം ഫോൾഡർ ആൻഡ്രോയിഡ് 10 മുതൽ ഈ സവിശേഷത ലഭ്യമല്ലാവും കാഷെ ലൊക്കേഷൻ ഫോൾഡർ പുന: ക്രമീകരിക്കുക @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-nl/strings.xml b/composeApp/src/androidMain/res/values-nl/strings.xml index 1b7f1ada6a..ecc4f82baf 100644 --- a/composeApp/src/androidMain/res/values-nl/strings.xml +++ b/composeApp/src/androidMain/res/values-nl/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-no/strings.xml b/composeApp/src/androidMain/res/values-no/strings.xml index c7500a5656..c003922d42 100644 --- a/composeApp/src/androidMain/res/values-no/strings.xml +++ b/composeApp/src/androidMain/res/values-no/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-or/strings.xml b/composeApp/src/androidMain/res/values-or/strings.xml index 0cf9fdedfa..e9d40fe156 100644 --- a/composeApp/src/androidMain/res/values-or/strings.xml +++ b/composeApp/src/androidMain/res/values-or/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-pl/strings.xml b/composeApp/src/androidMain/res/values-pl/strings.xml index c2796445ec..558da5d907 100644 --- a/composeApp/src/androidMain/res/values-pl/strings.xml +++ b/composeApp/src/androidMain/res/values-pl/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-pt-rBR/strings.xml b/composeApp/src/androidMain/res/values-pt-rBR/strings.xml index b45e57bb22..9241948a1f 100644 --- a/composeApp/src/androidMain/res/values-pt-rBR/strings.xml +++ b/composeApp/src/androidMain/res/values-pt-rBR/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-pt/strings.xml b/composeApp/src/androidMain/res/values-pt/strings.xml index e386ddf723..27142ce03b 100644 --- a/composeApp/src/androidMain/res/values-pt/strings.xml +++ b/composeApp/src/androidMain/res/values-pt/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-ro/strings.xml b/composeApp/src/androidMain/res/values-ro/strings.xml index c97ba13a15..95f46762e2 100644 --- a/composeApp/src/androidMain/res/values-ro/strings.xml +++ b/composeApp/src/androidMain/res/values-ro/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-ru/strings.xml b/composeApp/src/androidMain/res/values-ru/strings.xml index 3b0d908258..263bcf18c8 100644 --- a/composeApp/src/androidMain/res/values-ru/strings.xml +++ b/composeApp/src/androidMain/res/values-ru/strings.xml @@ -755,4 +755,7 @@ Управление Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-si/strings.xml b/composeApp/src/androidMain/res/values-si/strings.xml index 36c24f0bcb..962bce3838 100644 --- a/composeApp/src/androidMain/res/values-si/strings.xml +++ b/composeApp/src/androidMain/res/values-si/strings.xml @@ -754,4 +754,7 @@ CC-BY-SA 3.0 Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-sr-rCS/strings.xml b/composeApp/src/androidMain/res/values-sr-rCS/strings.xml index 04b07929ab..e829457ad6 100644 --- a/composeApp/src/androidMain/res/values-sr-rCS/strings.xml +++ b/composeApp/src/androidMain/res/values-sr-rCS/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-sr/strings.xml b/composeApp/src/androidMain/res/values-sr/strings.xml index 81b6aca0aa..5cf7607e46 100644 --- a/composeApp/src/androidMain/res/values-sr/strings.xml +++ b/composeApp/src/androidMain/res/values-sr/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-sv/strings.xml b/composeApp/src/androidMain/res/values-sv/strings.xml index 1c80f9f269..fb1827979a 100644 --- a/composeApp/src/androidMain/res/values-sv/strings.xml +++ b/composeApp/src/androidMain/res/values-sv/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-ta/strings.xml b/composeApp/src/androidMain/res/values-ta/strings.xml index 76596417d2..0694591281 100644 --- a/composeApp/src/androidMain/res/values-ta/strings.xml +++ b/composeApp/src/androidMain/res/values-ta/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-te/strings.xml b/composeApp/src/androidMain/res/values-te/strings.xml index 6df67687bb..fbf3164499 100644 --- a/composeApp/src/androidMain/res/values-te/strings.xml +++ b/composeApp/src/androidMain/res/values-te/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-tr/strings.xml b/composeApp/src/androidMain/res/values-tr/strings.xml index f49dc8490f..677c575b72 100644 --- a/composeApp/src/androidMain/res/values-tr/strings.xml +++ b/composeApp/src/androidMain/res/values-tr/strings.xml @@ -753,4 +753,7 @@ Kontroller Favorilere ekle Favorileri içeri aktar + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-uk/strings.xml b/composeApp/src/androidMain/res/values-uk/strings.xml index 852ff886be..6cdc2baefc 100644 --- a/composeApp/src/androidMain/res/values-uk/strings.xml +++ b/composeApp/src/androidMain/res/values-uk/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-vi/strings.xml b/composeApp/src/androidMain/res/values-vi/strings.xml index c435a8f23c..07bc6dd6a5 100644 --- a/composeApp/src/androidMain/res/values-vi/strings.xml +++ b/composeApp/src/androidMain/res/values-vi/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-zh-rCN/strings.xml b/composeApp/src/androidMain/res/values-zh-rCN/strings.xml index 03f3d4e59d..733588984c 100644 --- a/composeApp/src/androidMain/res/values-zh-rCN/strings.xml +++ b/composeApp/src/androidMain/res/values-zh-rCN/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-zh-rTW/strings.xml b/composeApp/src/androidMain/res/values-zh-rTW/strings.xml index 191f1091fc..ff87c8f70e 100644 --- a/composeApp/src/androidMain/res/values-zh-rTW/strings.xml +++ b/composeApp/src/androidMain/res/values-zh-rTW/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values-zh/strings.xml b/composeApp/src/androidMain/res/values-zh/strings.xml index 109351f18c..2604229f54 100644 --- a/composeApp/src/androidMain/res/values-zh/strings.xml +++ b/composeApp/src/androidMain/res/values-zh/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/androidMain/res/values/strings.xml b/composeApp/src/androidMain/res/values/strings.xml index 5d917ef14e..542388bfc1 100644 --- a/composeApp/src/androidMain/res/values/strings.xml +++ b/composeApp/src/androidMain/res/values/strings.xml @@ -753,4 +753,7 @@ Controls Add to favorites Import favorites + Autoskip on error + If an error occurs, the media is changed + The media %1$s went wrong diff --git a/composeApp/src/commonMain/composeResources/drawable/app_icon.xml b/composeApp/src/commonMain/composeResources/drawable/app_icon.xml index 87ab18ad6a..b1817025af 100644 --- a/composeApp/src/commonMain/composeResources/drawable/app_icon.xml +++ b/composeApp/src/commonMain/composeResources/drawable/app_icon.xml @@ -2,23 +2,8 @@ android:width="512dp" android:height="512dp" android:viewportWidth="512" - android:viewportHeight="512" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://schemas.android.com/apk/res/android "> + android:viewportHeight="512"> - - + android:pathData="M255,-0.5C113.6,-0.5 -1,114.1 -1,255.5s114.6,256 256,256a256.51,256.51 0,0 0,81.9 -13.4l-38.1,-66a30.26,30.26 0,0 1,-3.8 -11.3,29.92 29.92,0 0,1 0.9,-11.9 29.59,29.59 0,0 1,5.4 -10.6,30.62 30.62,0 0,1 9.1,-7.7c14.4,-8 32.6,-2.5 40.9,11.7L391.5,472A255.46,255.46 0,0 0,511 255.5C511,114.1 396.4,-0.5 255,-0.5ZM310.4,357.8a34.23,34.23 0,0 1,-53.8 7.1,34.48 34.48,0 0,1 -9.7,-19.7 33.9,33.9 0,0 1,4.3 -21.6,34.75 34.75,0 0,1 16.5,-14.5 34.31,34.31 0,0 1,40.2 10.8,34.19 34.19,0 0,1 7.1,20.8A35.31,35.31 0,0 1,310.4 357.8ZM426.8,282.3a57.29,57.29 0,0 1,-21.4 21.2l-33.2,19.1a30.29,30.29 0,0 1,-23 3.4,30.44 30.44,0 0,1 -10.7,-5.2 29,29 0,0 1,-7.8 -8.9,30.39 30.39,0 0,1 -3.8,-11.2 29.29,29.29 0,0 1,0.9 -11.8,31.32 31.32,0 0,1 5.4,-10.6 33.18,33.18 0,0 1,9 -7.7l11.6,-6.7a12.48,12.48 0,0 0,4.7 -4.7,14.72 14.72,0 0,0 1.8,-6.5 13.41,13.41 0,0 0,-1.7 -6.5,12.73 12.73,0 0,0 -4.7,-4.8L208.3,154.3a13.62,13.62 0,0 0,-6.5 -1.9,13.79 13.79,0 0,0 -6.6,1.7 12.31,12.31 0,0 0,-4.8 4.8,12.85 12.85,0 0,0 -1.8,6.6L188.6,390.6a30.1,30.1 0,1 1,-60.2 0v-251a58.16,58.16 0,0 1,29.3 -50.8l0.4,-0.2a56.94,56.94 0,0 1,29.6 -7.7,57.61 57.61,0 0,1 29.4,8.4L406.2,202.5a58.76,58.76 0,0 1,20.6 79.8Z" + android:fillColor="#58cc86"/> diff --git a/composeApp/src/commonMain/composeResources/drawable/app_logo.xml b/composeApp/src/commonMain/composeResources/drawable/app_logo.xml index fcbbf62372..784dc098f3 100644 --- a/composeApp/src/commonMain/composeResources/drawable/app_logo.xml +++ b/composeApp/src/commonMain/composeResources/drawable/app_logo.xml @@ -1,33 +1,54 @@ + xmlns:aapt="http://schemas.android.com/aapt" + android:width="500dp" + android:height="250dp" + android:viewportWidth="500" + android:viewportHeight="250"> + android:pathData="M305.81,87.7C307.79,84.49 308.76,80.62 308.76,76.02C308.76,71.52 307.79,67.62 305.87,64.3C303.93,60.99 301.06,58.34 297.63,56.69C294.09,54.92 289.87,54 284.97,54H258V120H273.77V97.43H281.85L293.8,120H311L297.47,94.89C301.07,93.28 303.86,90.89 305.81,87.7ZM273.77,66.93H281.56C283.93,66.93 285.91,67.26 287.54,67.95C289.05,68.53 290.33,69.59 291.2,70.97C292.05,72.31 292.43,73.99 292.43,76.02C292.43,78.05 292.01,79.72 291.2,81.03C290.32,82.36 289.03,83.37 287.54,83.92C285.94,84.54 283.96,84.83 281.56,84.83H273.77V66.93Z" + android:fillColor="#4AD17F"/> + android:pathData="M333,78H317V120H333V78Z" + android:fillColor="#4AD17F"/> + android:pathData="M325,70C323.42,70 321.87,69.53 320.56,68.65C319.24,67.77 318.22,66.53 317.61,65.06C317,63.6 316.85,61.99 317.15,60.44C317.46,58.89 318.22,57.46 319.34,56.34C320.46,55.22 321.89,54.46 323.44,54.15C324.99,53.85 326.6,54 328.06,54.61C329.52,55.21 330.77,56.24 331.65,57.56C332.53,58.87 333,60.42 333,62C333,64.12 332.15,66.15 330.65,67.65C329.16,69.15 327.12,70 325,70Z" + android:fillColor="#4AD17F"/> - + android:pathData="M263,131.84C263.99,131.84 264.96,132.14 265.78,132.7C266.61,133.25 267.25,134.04 267.63,134.96L285.92,179.87C285.95,179.97 286.02,180.05 286.1,180.11C286.18,180.17 286.28,180.2 286.38,180.2C286.48,180.2 286.58,180.17 286.66,180.11C286.74,180.05 286.8,179.97 286.83,179.87L305.12,134.96C305.56,133.88 306.36,132.99 307.38,132.44C308.4,131.89 309.58,131.71 310.72,131.94C311.85,132.16 312.88,132.77 313.61,133.67C314.35,134.57 314.75,135.7 314.76,136.86V186.77C314.76,187.64 314.41,188.47 313.8,189.09C313.19,189.71 312.36,190.05 311.49,190.05C310.63,190.05 309.79,189.71 309.18,189.09C308.57,188.47 308.23,187.64 308.23,186.77V146.16C308.23,146.07 308.19,145.99 308.13,145.93C308.07,145.87 307.98,145.83 307.9,145.83C307.83,145.84 307.77,145.86 307.71,145.9C307.66,145.93 307.61,145.98 307.57,146.03L290.75,187.13C290.4,188 289.79,188.75 289.01,189.27C288.23,189.8 287.32,190.08 286.38,190.08C285.44,190.09 284.52,189.81 283.74,189.28C282.96,188.75 282.35,188 282,187.13L265.18,146.02C265.16,145.96 265.11,145.9 265.05,145.87C264.99,145.83 264.93,145.82 264.86,145.82C264.77,145.82 264.69,145.86 264.63,145.92C264.57,145.98 264.53,146.06 264.53,146.15V186.76C264.53,187.63 264.19,188.47 263.57,189.09C262.96,189.7 262.13,190.05 261.27,190.05C260.4,190.05 259.57,189.7 258.96,189.09C258.34,188.47 258,187.63 258,186.76V136.86C258,135.53 258.53,134.25 259.46,133.31C260.4,132.37 261.67,131.84 263,131.84ZM349.69,172.21V149.74C349.72,148.87 350.08,148.04 350.7,147.43C351.32,146.82 352.16,146.48 353.03,146.48C353.89,146.48 354.73,146.82 355.35,147.43C355.97,148.04 356.33,148.87 356.36,149.74V186.73C356.33,187.6 355.97,188.43 355.35,189.04C354.73,189.65 353.89,189.99 353.03,189.99C352.16,189.99 351.32,189.65 350.7,189.04C350.08,188.43 349.72,187.6 349.69,186.73V182.69H349.24C348.18,184.96 346.55,186.91 344.5,188.34C342.35,189.88 339.64,190.64 336.37,190.64C333.66,190.64 331.24,190.05 329.12,188.83C326.95,187.57 325.22,185.67 324.16,183.38C322.95,180.95 322.36,177.87 322.36,174.15V149.74C322.36,148.85 322.71,148 323.34,147.37C323.96,146.75 324.81,146.39 325.69,146.39C326.58,146.39 327.42,146.75 328.05,147.37C328.67,148 329.02,148.85 329.02,149.74V173.69C329.02,176.88 329.91,179.41 331.67,181.31C333.43,183.21 335.75,184.17 338.53,184.17C340.29,184.14 342.02,183.7 343.59,182.88C345.36,182 346.86,180.65 347.93,178.98C349.11,177.21 349.69,174.97 349.69,172.21V172.21ZM390.25,157.2C389.61,157.37 388.93,157.34 388.31,157.11C387.69,156.87 387.16,156.44 386.79,155.88C386.6,155.59 386.37,155.26 386.14,154.96C385.36,153.95 384.35,153.13 383.2,152.57C381.96,151.94 380.36,151.61 378.43,151.61C375.79,151.61 373.57,152.24 371.8,153.45C370.04,154.66 369.19,156.21 369.19,158.08C369.19,159.76 369.78,161.07 370.99,162.02C372.2,162.97 374.09,163.79 376.64,164.45L383.07,166.03C386.95,166.98 389.83,168.43 391.76,170.36C393.68,172.3 394.6,174.8 394.6,177.85C394.62,180.26 393.87,182.61 392.44,184.55C391.03,186.52 389.04,188.07 386.5,189.22C383.95,190.37 380.98,190.92 377.61,190.92C373.17,190.92 369.52,189.97 366.61,188.04C364.83,186.87 363.37,185.25 362.4,183.35C362.18,182.91 362.06,182.43 362.06,181.94C362.05,181.45 362.15,180.96 362.35,180.52C362.56,180.07 362.86,179.68 363.23,179.37C363.61,179.06 364.05,178.83 364.52,178.72L364.65,178.68C365.37,178.51 366.12,178.59 366.79,178.9C367.46,179.21 368,179.74 368.34,180.39C368.93,181.54 369.8,182.54 370.86,183.28C372.55,184.46 374.77,185.05 377.52,185.05C380.62,185.05 383.1,184.4 384.93,183.05C386.76,181.7 387.71,180.09 387.71,178.22C387.73,177.51 387.59,176.8 387.32,176.14C387.04,175.49 386.63,174.9 386.11,174.41C385.06,173.36 383.43,172.6 381.24,172.08L373.99,170.37C370.01,169.42 367.1,167.94 365.24,165.97C363.38,164 362.46,161.43 362.46,158.45C362.43,156.1 363.15,153.81 364.52,151.91C365.92,150 367.82,148.53 370.2,147.44C372.59,146.36 375.36,145.83 378.4,145.83C382.71,145.83 386.07,146.79 388.52,148.69C389.91,149.74 391.09,151.03 392.01,152.5C392.28,152.91 392.45,153.38 392.51,153.87C392.56,154.35 392.5,154.85 392.33,155.3C392.15,155.76 391.88,156.17 391.52,156.5C391.15,156.83 390.72,157.07 390.25,157.2V157.2ZM399.07,186.73V149.74C399.06,149.29 399.13,148.85 399.3,148.43C399.46,148.01 399.7,147.63 400.01,147.31C400.32,146.99 400.69,146.74 401.1,146.56C401.51,146.39 401.95,146.3 402.4,146.3C402.85,146.3 403.29,146.39 403.7,146.56C404.11,146.74 404.48,146.99 404.79,147.31C405.1,147.63 405.34,148.01 405.5,148.43C405.67,148.85 405.74,149.29 405.73,149.74V186.73C405.74,187.18 405.67,187.63 405.5,188.04C405.34,188.46 405.1,188.84 404.79,189.16C404.48,189.48 404.11,189.74 403.7,189.91C403.29,190.09 402.85,190.17 402.4,190.17C401.95,190.17 401.51,190.09 401.1,189.91C400.69,189.74 400.32,189.48 400.01,189.16C399.7,188.84 399.46,188.46 399.3,188.04C399.13,187.63 399.06,187.18 399.07,186.73ZM402.47,139.13C401.21,139.15 400,138.66 399.1,137.79C398.67,137.37 398.32,136.87 398.09,136.32C397.85,135.76 397.73,135.17 397.73,134.57C397.73,133.96 397.85,133.37 398.09,132.82C398.32,132.26 398.67,131.76 399.1,131.35C400,130.47 401.21,129.99 402.47,130C403.08,129.99 403.7,130.11 404.27,130.34C404.84,130.57 405.36,130.91 405.8,131.35C406.24,131.76 406.59,132.26 406.83,132.81C407.08,133.36 407.2,133.96 407.2,134.57C407.2,135.17 407.08,135.77 406.83,136.32C406.59,136.88 406.24,137.38 405.8,137.79C404.9,138.66 403.71,139.14 402.47,139.13ZM430.02,191C425.94,191 422.45,190.05 419.51,188.11C416.58,186.17 414.32,183.51 412.72,180.13C411.12,176.75 410.34,172.87 410.34,168.5C410.34,164.07 411.15,160.16 412.75,156.74C414.35,153.32 416.67,150.66 419.61,148.76C422.55,146.85 425.98,145.87 429.9,145.87C432.96,145.87 435.71,146.42 438.16,147.57C441.38,149.02 444.02,151.52 445.64,154.67C445.83,155.05 445.95,155.48 445.99,155.91C446.02,156.35 445.97,156.79 445.84,157.2C445.71,157.62 445.49,158 445.21,158.33C444.93,158.66 444.59,158.94 444.2,159.13C443.82,159.33 443.39,159.45 442.96,159.49C442.53,159.52 442.09,159.47 441.68,159.34C441.27,159.2 440.89,158.99 440.56,158.71C440.23,158.42 439.95,158.08 439.76,157.69C439.09,156.35 438.16,155.17 437.02,154.21C435.29,152.66 432.93,151.88 429.99,151.88C427.56,151.83 425.16,152.54 423.14,153.91C421.12,155.35 419.55,157.33 418.6,159.63C417.52,162.06 417,164.95 417,168.24C417,171.62 417.52,174.54 418.57,177.04C419.61,179.54 421.15,181.47 423.11,182.85C425.07,184.23 427.35,184.92 429.99,184.92C431.62,184.95 433.23,184.63 434.73,184C436.12,183.42 437.35,182.52 438.32,181.38C438.9,180.71 439.39,179.95 439.76,179.14C440.03,178.58 440.45,178.11 440.98,177.78C441.5,177.45 442.11,177.27 442.73,177.28C445.11,177.28 446.71,179.77 445.67,181.91C445.27,182.72 444.82,183.5 444.3,184.24C442.77,186.32 440.77,188 438.45,189.13C436.04,190.41 433.23,191 430.02,191Z" + android:fillColor="#4AD17F"/> + + + + + + + + + + + + + + + + + + + + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/app_logo_text.xml b/composeApp/src/commonMain/composeResources/drawable/app_logo_text.xml index 9e12a4d549..284af6353d 100644 --- a/composeApp/src/commonMain/composeResources/drawable/app_logo_text.xml +++ b/composeApp/src/commonMain/composeResources/drawable/app_logo_text.xml @@ -1,13 +1,9 @@ + android:viewportWidth="1429" + android:viewportHeight="512"> + android:pathData="M113,95.3C118.5,95.3 123.9,97 128.6,100.1C133.2,103.2 136.8,107.6 138.9,112.8L241.2,364.6C241.4,365.2 241.8,365.6 242.2,366C242.7,366.3 243.2,366.5 243.8,366.5C244.4,366.5 244.9,366.3 245.4,366C245.8,365.6 246.2,365.2 246.4,364.6L348.7,112.8C351.1,106.8 355.6,101.8 361.3,98.7C367,95.6 373.6,94.6 380,95.9C386.3,97.1 392.1,100.6 396.2,105.6C400.3,110.6 402.6,117 402.6,123.5V403.3C402.6,408.2 400.7,412.8 397.2,416.3C393.8,419.7 389.2,421.7 384.3,421.7C379.5,421.7 374.8,419.7 371.4,416.3C368,412.8 366,408.2 366,403.3V175.6C366,175.1 365.8,174.7 365.5,174.3C365.2,174 364.7,173.8 364.2,173.8C363.8,173.8 363.5,173.9 363.2,174.1C362.9,174.3 362.6,174.6 362.4,174.9L268.3,405.3C266.3,410.2 262.9,414.4 258.5,417.3C254.2,420.3 249,421.8 243.8,421.8C238.5,421.9 233.4,420.3 229,417.4C224.7,414.4 221.3,410.2 219.3,405.3L125.2,174.8C125,174.5 124.8,174.2 124.5,174C124.1,173.8 123.8,173.7 123.4,173.7C122.9,173.7 122.4,173.9 122.1,174.3C121.7,174.6 121.5,175.1 121.5,175.6V403.2C121.5,408.1 119.6,412.8 116.2,416.3C112.8,419.7 108.1,421.7 103.3,421.7C98.4,421.7 93.8,419.7 90.4,416.3C86.9,412.8 85,408.1 85,403.2V123.5C85,116 88,108.8 93.2,103.5C98.4,98.3 105.6,95.3 113,95.3ZM598.1,321.6V195.7C598.2,190.8 600.3,186.1 603.7,182.7C607.2,179.3 611.9,177.4 616.7,177.4C621.6,177.4 626.3,179.3 629.7,182.7C633.2,186.1 635.3,190.8 635.4,195.7V403.1C635.3,408 633.2,412.6 629.7,416C626.3,419.4 621.6,421.3 616.7,421.3C611.9,421.3 607.2,419.4 603.7,416C600.3,412.6 598.2,408 598.1,403.1V380.4H595.5C589.6,393.1 580.5,404.1 569,412.1C557,420.7 541.8,425 523.5,425C508.4,425 494.9,421.7 483,414.9C470.8,407.8 461.1,397.1 455.2,384.3C448.5,370.7 445.2,353.4 445.2,332.5V195.7C445.2,190.7 447.1,185.9 450.6,182.4C454.1,178.9 458.9,176.9 463.8,176.9C468.7,176.9 473.5,178.9 477,182.4C480.5,185.9 482.4,190.7 482.4,195.7V330C482.4,347.8 487.4,362 497.2,372.7C507.1,383.3 520.1,388.7 535.6,388.7C545.5,388.5 555.2,386.1 563.9,381.5C573.8,376.5 582.2,369 588.2,359.6C594.8,349.7 598.1,337.1 598.1,321.7V321.6ZM825,237.5C821.5,238.5 817.7,238.3 814.2,237C810.7,235.7 807.7,233.2 805.7,230.1C804.6,228.5 803.3,226.6 802,225C797.6,219.3 792,214.7 785.6,211.5C778.6,208 769.7,206.2 758.9,206.2C744.1,206.2 731.7,209.7 721.8,216.5C711.9,223.3 707.2,231.9 707.2,242.4C707.2,251.8 710.5,259.2 717.2,264.5C724,269.9 734.6,274.5 748.9,278.1L784.8,287C806.6,292.3 822.7,300.4 833.5,311.3C844.2,322.1 849.4,336.2 849.4,353.3C849.5,366.8 845.3,380 837.3,390.8C829.4,401.9 818.3,410.6 804,417C789.8,423.5 773.2,426.6 754.3,426.6C729.5,426.6 709,421.2 692.8,410.4C682.8,403.8 674.6,394.8 669.2,384.1C668,381.6 667.3,378.9 667.3,376.2C667.2,373.4 667.8,370.7 668.9,368.2C670.1,365.7 671.8,363.5 673.9,361.8C676,360 678.4,358.8 681.1,358.1L681.8,357.9C685.8,357 690,357.4 693.8,359.2C697.5,360.9 700.5,363.8 702.4,367.5C705.8,374 710.6,379.5 716.5,383.7C726,390.3 738.4,393.7 753.8,393.7C771.1,393.7 785,390 795.3,382.4C805.5,374.9 810.8,365.8 810.8,355.4C810.9,351.4 810.2,347.4 808.6,343.7C807.1,340 804.8,336.7 801.8,334C796,328.1 786.9,323.9 774.6,320.9L734.1,311.3C711.8,306 695.5,297.7 685.1,286.7C674.7,275.6 669.5,261.2 669.5,244.5C669.3,231.3 673.4,218.5 681.1,207.8C688.9,197.2 699.5,188.9 712.9,182.8C726.2,176.7 741.7,173.8 758.7,173.8C782.8,173.8 801.7,179.1 815.4,189.8C823.1,195.7 829.7,202.9 834.9,211.2C836.4,213.5 837.4,216.1 837.7,218.8C838,221.5 837.6,224.3 836.7,226.9C835.7,229.4 834.1,231.7 832.1,233.6C830.1,235.4 827.7,236.8 825,237.5V237.5ZM874.4,403.1V195.7C874.3,193.2 874.8,190.7 875.7,188.3C876.6,186 877.9,183.9 879.7,182.1C881.4,180.3 883.5,178.8 885.8,177.9C888.1,176.9 890.5,176.4 893,176.4C895.5,176.4 898,176.9 900.3,177.9C902.6,178.8 904.6,180.3 906.4,182.1C908.1,183.9 909.5,186 910.4,188.3C911.3,190.7 911.7,193.2 911.7,195.7V403.1C911.7,405.6 911.3,408.1 910.4,410.4C909.5,412.8 908.1,414.9 906.4,416.7C904.6,418.5 902.6,419.9 900.3,420.9C898,421.9 895.5,422.4 893,422.4C890.5,422.4 888.1,421.9 885.8,420.9C883.5,419.9 881.4,418.5 879.7,416.7C877.9,414.9 876.6,412.8 875.7,410.4C874.8,408.1 874.3,405.6 874.4,403.1ZM893.4,136.2C886.4,136.3 879.6,133.6 874.6,128.7C872.1,126.3 870.2,123.5 868.9,120.4C867.6,117.3 866.9,114 866.9,110.6C866.9,107.2 867.6,103.9 868.9,100.8C870.2,97.7 872.1,94.9 874.6,92.6C879.6,87.6 886.4,84.9 893.4,85C896.8,85 900.3,85.6 903.5,86.9C906.7,88.2 909.6,90.1 912,92.6C914.5,94.9 916.5,97.6 917.8,100.8C919.2,103.9 919.9,107.2 919.9,110.6C919.9,114 919.2,117.3 917.8,120.5C916.5,123.6 914.5,126.4 912,128.7C907,133.5 900.3,136.2 893.4,136.2ZM1047.6,427C1024.8,427 1005.2,421.7 988.8,410.8C972.3,399.9 959.7,385 950.8,366C941.8,347.1 937.4,325.3 937.4,300.9C937.4,276 942,254.1 951,234.9C959.9,215.8 972.9,200.9 989.3,190.2C1005.8,179.5 1025,174 1046.9,174C1064.1,174 1079.4,177.1 1093.1,183.5C1111.2,191.6 1125.9,205.6 1135,223.3C1136.1,225.5 1136.8,227.8 1136.9,230.3C1137.1,232.7 1136.8,235.2 1136.1,237.5C1135.4,239.8 1134.2,242 1132.6,243.8C1131,245.7 1129.1,247.2 1126.9,248.3C1124.8,249.5 1122.4,250.1 1120,250.3C1117.6,250.5 1115.2,250.2 1112.8,249.5C1110.5,248.7 1108.4,247.5 1106.5,245.9C1104.7,244.4 1103.2,242.4 1102.1,240.2C1098.3,232.8 1093.1,226.1 1086.7,220.7C1077,212.1 1063.9,207.6 1047.4,207.6C1033.8,207.4 1020.4,211.4 1009.1,219.1C997.8,227.1 989,238.3 983.7,251.1C977.6,264.7 974.7,280.9 974.7,299.4C974.7,318.3 977.6,334.7 983.5,348.7C989.3,362.7 997.9,373.6 1008.9,381.3C1019.9,389.1 1032.7,392.9 1047.4,392.9C1056.5,393.1 1065.6,391.3 1073.9,387.8C1081.7,384.5 1088.6,379.5 1094,373C1097.3,369.3 1100,365.1 1102.1,360.5C1103.6,357.4 1105.9,354.7 1108.9,352.9C1111.8,351 1115.2,350 1118.7,350.1C1132,350.1 1141,364 1135.1,376C1132.9,380.6 1130.4,385 1127.5,389.1C1118.9,400.8 1107.7,410.2 1094.8,416.5C1081.2,423.7 1065.5,427 1047.6,427Z" + android:fillColor="#ffffff"/> diff --git a/composeApp/src/commonMain/composeResources/drawable/pin.webp b/composeApp/src/commonMain/composeResources/drawable/pin.webp new file mode 100644 index 0000000000..259b27b4e7 Binary files /dev/null and b/composeApp/src/commonMain/composeResources/drawable/pin.webp differ diff --git a/composeApp/src/commonMain/composeResources/drawable/piped_logo.xml b/composeApp/src/commonMain/composeResources/drawable/piped_logo.xml new file mode 100644 index 0000000000..2560b77d57 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/piped_logo.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/stat_month.xml b/composeApp/src/commonMain/composeResources/drawable/stat_month.xml new file mode 100644 index 0000000000..2f677c74c5 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/stat_month.xml @@ -0,0 +1,9 @@ + + + diff --git a/composeApp/src/desktopMain/kotlin/it/fast4x/rimusic/items/Items.kt b/composeApp/src/desktopMain/kotlin/it/fast4x/rimusic/items/Items.kt index de764b3dab..c38235c272 100644 --- a/composeApp/src/desktopMain/kotlin/it/fast4x/rimusic/items/Items.kt +++ b/composeApp/src/desktopMain/kotlin/it/fast4x/rimusic/items/Items.kt @@ -1,31 +1,56 @@ package it.fast4x.rimusic.items import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.Image +import androidx.compose.foundation.background import androidx.compose.foundation.basicMarquee +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxScope +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.requiredSize +import androidx.compose.foundation.layout.requiredWidth import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.BasicText import androidx.compose.material3.MaterialTheme.typography import androidx.compose.runtime.Composable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import coil3.compose.AsyncImage import it.fast4x.innertube.Innertube +import it.fast4x.rimusic.MONTHLY_PREFIX +import it.fast4x.rimusic.PINNED_PREFIX +import it.fast4x.rimusic.PIPED_PREFIX import it.fast4x.rimusic.cleanPrefix import it.fast4x.rimusic.enums.ThumbnailRoundness import it.fast4x.rimusic.thumbnail import it.fast4x.rimusic.utils.LoadImage +import it.fast4x.rimusic.utils.getTitleMonthlyPlaylist +import org.jetbrains.compose.resources.painterResource +import rimusic.composeapp.generated.resources.Res +import rimusic.composeapp.generated.resources.pin +import rimusic.composeapp.generated.resources.piped_logo +import rimusic.composeapp.generated.resources.stat_month @OptIn(ExperimentalFoundationApi::class) @Composable @@ -265,4 +290,327 @@ fun AlbumItem( alternative = alternative, showAuthors = showAuthors ) +} + +@OptIn(ExperimentalFoundationApi::class) +@Composable +fun ArtistItem( + thumbnailUrl: String?, + name: String?, + subscribersCount: String?, + //thumbnailSizePx: Int, + thumbnailSizeDp: Dp, + modifier: Modifier = Modifier, + alternative: Boolean = false, + showName: Boolean = true +) { + ItemContainer( + alternative = alternative, + thumbnailSizeDp = thumbnailSizeDp, + horizontalAlignment = Alignment.CenterHorizontally, + modifier = modifier + ) { + AsyncImage( + model = thumbnailUrl, + contentDescription = null, + modifier = Modifier + .clip(ThumbnailRoundness.Medium.shape()) + .requiredSize(thumbnailSizeDp) + ) + + if (showName) + ItemInfoContainer( + horizontalAlignment = if (alternative) Alignment.CenterHorizontally else Alignment.Start, + ) { + BasicText( + text = name ?: "", + style = TextStyle( + color = Color.White, + fontSize = typography.titleSmall.fontSize, + //fontWeight = typography.titleSmall.fontWeight + ), + maxLines = 1, //if (alternative) 1 else 2, + overflow = TextOverflow.Ellipsis, + modifier = Modifier + .basicMarquee(iterations = Int.MAX_VALUE) + ) + + subscribersCount?.let { + BasicText( + text = subscribersCount, + style = TextStyle( + color = Color.White, + fontSize = typography.titleSmall.fontSize, + //fontWeight = typography.titleSmall.fontWeight + ), + maxLines = 1, + overflow = TextOverflow.Ellipsis, + modifier = Modifier + .padding(top = 4.dp) + .basicMarquee(iterations = Int.MAX_VALUE) + ) + } + } + } +} + +@Composable +fun ArtistItem( + artist: Innertube.ArtistItem, + //thumbnailSizePx: Int, + thumbnailSizeDp: Dp, + modifier: Modifier = Modifier, + alternative: Boolean = false, +) { + ArtistItem( + thumbnailUrl = artist.thumbnail?.url, + name = artist.info?.name, + subscribersCount = artist.subscribersCountText, + thumbnailSizeDp = thumbnailSizeDp, + modifier = modifier, + alternative = alternative + ) +} + +@OptIn(ExperimentalFoundationApi::class) +@Composable +fun PlaylistItem( + thumbnailContent: @Composable BoxScope.( + //modifier: Modifier + ) -> Unit, + songCount: Int?, + name: String?, + channelName: String?, + thumbnailSizeDp: Dp, + modifier: Modifier = Modifier, + alternative: Boolean = false, + showName: Boolean = true, + showSongsCount: Boolean = true +) { + ItemContainer( + alternative = alternative, + thumbnailSizeDp = thumbnailSizeDp, + modifier = modifier + ) { //centeredModifier -> + Box( + modifier = Modifier + .clip(ThumbnailRoundness.Medium.shape()) + //.background(color = colorPalette().background4) + .requiredSize(thumbnailSizeDp) + ) { + thumbnailContent( + /* + modifier = Modifier + .fillMaxSize() + + */ + ) + + name?.let { + if (it.startsWith(PIPED_PREFIX,0,true)) { + Image( + painter = painterResource(Res.drawable.piped_logo), + colorFilter = ColorFilter.tint(Color.White), + modifier = Modifier + .size(40.dp) + .padding(all = 5.dp), + contentDescription = "Background Image" + ) + } + if (it.startsWith(PINNED_PREFIX,0,true)) { + Image( + painter = painterResource(Res.drawable.pin), + colorFilter = ColorFilter.tint(Color.White), + modifier = Modifier + .size(40.dp) + .padding(all = 5.dp), + contentDescription = "Background Image", + contentScale = ContentScale.Fit + ) + } + if (it.startsWith(MONTHLY_PREFIX,0,true)) { + Image( + painter = painterResource(Res.drawable.stat_month), + colorFilter = ColorFilter.tint(Color.White), + modifier = Modifier + .size(40.dp) + .padding(all = 5.dp), + contentDescription = "Background Image", + contentScale = ContentScale.Fit + ) + } + } + + + if (showSongsCount) + songCount?.let { + BasicText( + text = "$songCount", + style = TextStyle( + color = Color.White, + fontSize = typography.titleSmall.fontSize, + //fontWeight = typography.titleSmall.fontWeight + ), + maxLines = 1, + overflow = TextOverflow.Ellipsis, + modifier = Modifier + .padding(all = 4.dp) + .background(color = Color.White, shape = RoundedCornerShape(4.dp)) + .padding(horizontal = 6.dp, vertical = 6.dp) + .align(Alignment.BottomEnd) + ) + } + + } + + + ItemInfoContainer( + horizontalAlignment = if (alternative && channelName == null) Alignment.CenterHorizontally else Alignment.Start, + modifier = Modifier + .fillMaxSize() + ) { + if (showName) + if (name != null) { + BasicText( + //text = name.substringAfter(PINNED_PREFIX) ?: "", + text = if (name.startsWith(PINNED_PREFIX,0,true)) + name.substringAfter(PINNED_PREFIX) else + if (name.startsWith(MONTHLY_PREFIX,0,true)) + getTitleMonthlyPlaylist(name.substringAfter(MONTHLY_PREFIX)) else + if (name.startsWith(PIPED_PREFIX,0,true)) + name.substringAfter(PIPED_PREFIX) else name, + style = TextStyle( + color = Color.White, + fontSize = typography.titleSmall.fontSize, + //fontWeight = typography.titleSmall.fontWeight + ), + maxLines = 1, + overflow = TextOverflow.Ellipsis, + modifier = Modifier + .basicMarquee(iterations = Int.MAX_VALUE) + ) + } + + channelName?.let { + BasicText( + text = channelName, + style = TextStyle( + color = Color.White, + fontSize = typography.titleSmall.fontSize, + //fontWeight = typography.titleSmall.fontWeight + ), + maxLines = 1, + overflow = TextOverflow.Ellipsis, + modifier = Modifier + .basicMarquee(iterations = Int.MAX_VALUE) + ) + } + } + } +} + +@Composable +fun PlaylistItem( + thumbnailUrl: String?, + songCount: Int?, + name: String?, + channelName: String?, + thumbnailSizeDp: Dp, + modifier: Modifier = Modifier, + alternative: Boolean = false, + showSongsCount: Boolean = true +) { + PlaylistItem( + thumbnailContent = { + AsyncImage( + model = thumbnailUrl, + contentDescription = null, + contentScale = ContentScale.Crop + ) + }, + songCount = songCount, + showSongsCount = showSongsCount, + name = name, + channelName = channelName, + thumbnailSizeDp = thumbnailSizeDp, + modifier = modifier, + alternative = alternative, + ) +} + +@Composable +fun PlaylistItem( + playlist: Innertube.PlaylistItem, + thumbnailSizeDp: Dp, + modifier: Modifier = Modifier, + alternative: Boolean = false, + showSongsCount: Boolean = true +) { + PlaylistItem( + thumbnailUrl = playlist.thumbnail?.url, + songCount = playlist.songCount, + showSongsCount = showSongsCount, + name = playlist.info?.name, + channelName = playlist.channel?.name, + thumbnailSizeDp = thumbnailSizeDp, + modifier = modifier, + alternative = alternative + ) +} + +@Composable +fun MoodItemColored( + mood: Innertube.Mood.Item, + onClick: () -> Unit, + modifier: Modifier = Modifier +) { + /* + var thumbnailRoundness by rememberPreference( + thumbnailRoundnessKey, + ThumbnailRoundness.Heavy + ) + + */ + + val moodColor by remember { derivedStateOf { Color(mood.stripeColor) } } + + Column ( + verticalArrangement = Arrangement.SpaceAround, + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier + .fillMaxWidth() + .padding(5.dp) + .clip(ThumbnailRoundness.Medium.shape()) + .clickable { onClick() } + + ) { + Row( + horizontalArrangement = Arrangement.Start, + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .background(color = moodColor) + .padding(start = 10.dp) + .fillMaxHeight(0.9f) + ) { + Box( + modifier = Modifier + .requiredWidth(150.dp) + //.background(color = colorPalette().background4) + .fillMaxSize() + ) { + + BasicText( + text = mood.title, + style = TextStyle( + color = Color.White, + fontSize = typography.titleSmall.fontSize, + //fontWeight = typography.titleSmall.fontWeight + ), + modifier = Modifier.padding(horizontal = 10.dp).align(Alignment.CenterStart), + maxLines = 2, + + ) + } + } + } } \ No newline at end of file diff --git a/composeApp/src/desktopMain/kotlin/it/fast4x/rimusic/styling/Dimensions.kt b/composeApp/src/desktopMain/kotlin/it/fast4x/rimusic/styling/Dimensions.kt index 81c727896a..f7657cd0f3 100644 --- a/composeApp/src/desktopMain/kotlin/it/fast4x/rimusic/styling/Dimensions.kt +++ b/composeApp/src/desktopMain/kotlin/it/fast4x/rimusic/styling/Dimensions.kt @@ -7,8 +7,11 @@ object Dimensions { val layoutColumnTopPadding = 10.dp val layoutColumnBottomPadding = 40.dp val layoutColumnsHorizontalPadding = 10.dp + val layoutColumnBottomSpacer = 100.dp val itemInHorizontalGridWidth = 200.dp val albumThumbnailSize = 100.dp + val artistThumbnailSize = 100.dp + val playlistThumbnailSize = 100.dp } \ No newline at end of file diff --git a/composeApp/src/desktopMain/kotlin/it/fast4x/rimusic/ui/ThreeColumnsApp.kt b/composeApp/src/desktopMain/kotlin/it/fast4x/rimusic/ui/ThreeColumnsApp.kt index 2993dd4e18..eeebffb1a5 100644 --- a/composeApp/src/desktopMain/kotlin/it/fast4x/rimusic/ui/ThreeColumnsApp.kt +++ b/composeApp/src/desktopMain/kotlin/it/fast4x/rimusic/ui/ThreeColumnsApp.kt @@ -14,7 +14,9 @@ import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.gestures.ScrollableDefaults +import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -29,6 +31,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.requiredHeight import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyHorizontalGrid @@ -82,13 +85,19 @@ import it.fast4x.innertube.requests.player import it.fast4x.innertube.requests.relatedPage import it.fast4x.rimusic.enums.ThumbnailRoundness import it.fast4x.rimusic.items.AlbumItem +import it.fast4x.rimusic.items.ArtistItem +import it.fast4x.rimusic.items.MoodItemColored +import it.fast4x.rimusic.items.PlaylistItem import it.fast4x.rimusic.items.SongItem import it.fast4x.rimusic.styling.Dimensions.albumThumbnailSize +import it.fast4x.rimusic.styling.Dimensions.artistThumbnailSize import it.fast4x.rimusic.styling.Dimensions.itemInHorizontalGridWidth import it.fast4x.rimusic.styling.Dimensions.itemsVerticalPadding import it.fast4x.rimusic.styling.Dimensions.layoutColumnBottomPadding +import it.fast4x.rimusic.styling.Dimensions.layoutColumnBottomSpacer import it.fast4x.rimusic.styling.Dimensions.layoutColumnTopPadding import it.fast4x.rimusic.styling.Dimensions.layoutColumnsHorizontalPadding +import it.fast4x.rimusic.styling.Dimensions.playlistThumbnailSize import it.fast4x.rimusic.ui.components.Title import it.fast4x.rimusic.ui.components.Title2Actions import kotlinx.coroutines.Dispatchers @@ -103,9 +112,13 @@ import rimusic.composeapp.generated.resources.app_icon import rimusic.composeapp.generated.resources.app_logo_text import rimusic.composeapp.generated.resources.artists import rimusic.composeapp.generated.resources.library +import rimusic.composeapp.generated.resources.moods_and_genres import rimusic.composeapp.generated.resources.musical_notes import rimusic.composeapp.generated.resources.new_albums import rimusic.composeapp.generated.resources.play +import rimusic.composeapp.generated.resources.playlists_you_might_like +import rimusic.composeapp.generated.resources.related_albums +import rimusic.composeapp.generated.resources.similar_artists import vlcj.VlcjComponentController import vlcj.VlcjFrameController @@ -290,7 +303,8 @@ fun ThreeColumnsLayout( verticalArrangement = Arrangement.Top ) { FrameContainer( - Modifier.requiredHeight(200.dp).border(BorderStroke(1.dp, Color.Red)), + Modifier.requiredHeight(200.dp), + //.border(BorderStroke(1.dp, Color.Red)), frameController.size.collectAsState(null).value?.run { IntSize(first, second) } ?: IntSize.Zero, @@ -406,6 +420,9 @@ fun LeftPanelContent() { fun CenterPanelContent( onSongClick: (key: String) -> Unit = {}, onAlbumClick: (key: String) -> Unit = {}, + onArtistClick: (key: String) -> Unit = {}, + onPlaylistClick: (key: String) -> Unit = {}, + onMoodClick: (mood: Innertube.Mood.Item) -> Unit = {}, ) { val scrollState = rememberScrollState() Column( @@ -463,10 +480,12 @@ fun CenterPanelContent( ) val quickPicksLazyGridState = rememberLazyGridState() + val moodAngGenresLazyGridState = rememberLazyGridState() val endPaddingValues = windowInsets.only(WindowInsetsSides.End).asPaddingValues() val related = remember { mutableStateOf(null) } var relatedPageResult by remember { mutableStateOf?>(null) } - var discoverPage by remember { mutableStateOf?>(null) } + var discoverPageResult by remember { mutableStateOf?>(null) } + var discover = remember { mutableStateOf(null) } LaunchedEffect(Unit) { relatedPageResult = Innertube.relatedPage( @@ -475,9 +494,10 @@ fun CenterPanelContent( ) ) - discoverPage = Innertube.discoverPage() + discoverPageResult = Innertube.discoverPage() } relatedPageResult?.getOrNull().also { related.value = it } + discoverPageResult?.getOrNull().also { discover.value = it } LazyHorizontalGrid( state = quickPicksLazyGridState, @@ -515,7 +535,7 @@ fun CenterPanelContent( } } - discoverPage?.getOrNull()?.let { page -> + discover.let { page -> val showNewAlbums = true if (showNewAlbums) { Title( @@ -525,7 +545,33 @@ fun CenterPanelContent( ) LazyRow(contentPadding = endPaddingValues) { - items(items = page.newReleaseAlbums.distinctBy { it.key }, key = { it.key }) { + page.value?.newReleaseAlbums?.let { + items(items = it.distinctBy { it.key }, key = { it.key }) { + AlbumItem( + album = it, + thumbnailSizeDp = albumThumbnailSize, + alternative = true, + modifier = Modifier.clickable(onClick = { + onAlbumClick(it.key) + }) + ) + } + } + } + } + } + + related.value?.albums?.let { albums -> + val showRelatedAlbums = true + if (showRelatedAlbums) { + Title( + title = stringResource(Res.string.related_albums), + onClick = {}, + //modifier = Modifier.fillMaxWidth(0.7f) + ) + + LazyRow(contentPadding = endPaddingValues) { + items(items = albums.distinctBy { it.key }, key = { it.key }) { AlbumItem( album = it, thumbnailSizeDp = albumThumbnailSize, @@ -539,6 +585,98 @@ fun CenterPanelContent( } } + related.value?.artists?.let { artists -> + val showSimilarArtists = true + if (showSimilarArtists) { + Title( + title = stringResource(Res.string.similar_artists), + onClick = {}, + //modifier = Modifier.fillMaxWidth(0.7f) + ) + + LazyRow(contentPadding = endPaddingValues) { + items(items = artists.distinctBy { it.key }, key = { it.key }) { + ArtistItem( + artist = it, + thumbnailSizeDp = artistThumbnailSize, + alternative = true, + modifier = Modifier.clickable(onClick = { + onArtistClick(it.key) + }) + ) + + } + } + } + } + + related.value?.playlists?.let { playlists -> + val showPlaylistMightLike = true + if (showPlaylistMightLike) { + Title( + title = stringResource(Res.string.playlists_you_might_like), + onClick = {}, + //modifier = Modifier.fillMaxWidth(0.7f) + ) + + LazyRow(contentPadding = endPaddingValues) { + items(items = playlists.distinctBy { it.key }, key = { it.key }) { + PlaylistItem( + playlist = it, + thumbnailSizeDp = playlistThumbnailSize, + alternative = true, + showSongsCount = false, + modifier = Modifier.clickable(onClick = { + onPlaylistClick(it.key) + }) + ) + + } + } + } + } + + discover.let { page -> + val showNewAlbums = true + if (showNewAlbums) { + Title( + title = stringResource(Res.string.moods_and_genres), + onClick = {}, + //modifier = Modifier.fillMaxWidth(0.7f) + ) + + LazyHorizontalGrid( + state = moodAngGenresLazyGridState, + rows = GridCells.Fixed(4), + flingBehavior = ScrollableDefaults.flingBehavior(), + contentPadding = endPaddingValues, + modifier = Modifier + //.fillMaxWidth() + .height(itemsVerticalPadding * 4 * 8) + ) { + page.value?.moods?.let { + items( + items = it.sortedBy { it.title }, + key = { it.endpoint.params ?: it.title } + ) { + MoodItemColored( + mood = it, + onClick = { it.endpoint.browseId?.let { _ -> onMoodClick(it) } }, + modifier = Modifier + //.width(itemWidth) + .padding(4.dp) + ) + } + } + } + + } + } + + + Spacer(Modifier.height(layoutColumnBottomSpacer)) + + } } diff --git a/composeApp/src/desktopMain/kotlin/it/fast4x/rimusic/utils/MonthlyPlaylist.kt b/composeApp/src/desktopMain/kotlin/it/fast4x/rimusic/utils/MonthlyPlaylist.kt new file mode 100644 index 0000000000..035e4a2bac --- /dev/null +++ b/composeApp/src/desktopMain/kotlin/it/fast4x/rimusic/utils/MonthlyPlaylist.kt @@ -0,0 +1,39 @@ +package it.fast4x.rimusic.utils + +import androidx.compose.runtime.Composable +import org.jetbrains.compose.resources.stringResource +import rimusic.composeapp.generated.resources.Res +import rimusic.composeapp.generated.resources.month_april_s +import rimusic.composeapp.generated.resources.month_august_s +import rimusic.composeapp.generated.resources.month_december_s +import rimusic.composeapp.generated.resources.month_february_s +import rimusic.composeapp.generated.resources.month_january_s +import rimusic.composeapp.generated.resources.month_july_s +import rimusic.composeapp.generated.resources.month_june_s +import rimusic.composeapp.generated.resources.month_march_s +import rimusic.composeapp.generated.resources.month_may_s +import rimusic.composeapp.generated.resources.month_november_s +import rimusic.composeapp.generated.resources.month_october_s +import rimusic.composeapp.generated.resources.month_september_s + +@Composable +fun getTitleMonthlyPlaylist(playlist: String): String { + + val y = playlist.substring(0,4) + val m = playlist.substring(5,7).toInt() + return when (m) { + 1 -> stringResource(Res.string.month_january_s).format(y) + 2 -> stringResource(Res.string.month_february_s).format(y) + 3 -> stringResource(Res.string.month_march_s).format(y) + 4 -> stringResource(Res.string.month_april_s).format(y) + 5 -> stringResource(Res.string.month_may_s).format(y) + 6 -> stringResource(Res.string.month_june_s).format(y) + 7 -> stringResource(Res.string.month_july_s).format(y) + 8 -> stringResource(Res.string.month_august_s).format(y) + 9 -> stringResource(Res.string.month_september_s).format(y) + 10 -> stringResource(Res.string.month_october_s).format(y) + 11 -> stringResource(Res.string.month_november_s).format(y) + 12 -> stringResource(Res.string.month_december_s).format(y) + else -> playlist + } +} \ No newline at end of file diff --git a/customVersion/app-foss-release.apk b/customVersion/app-foss-release.apk index 94f67ba47d..b2d13c7b48 100644 Binary files a/customVersion/app-foss-release.apk and b/customVersion/app-foss-release.apk differ