diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 29a30e7dcd..cbda5c498e 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,3 +1,5 @@ +import org.jetbrains.kotlin.compose.compiler.gradle.ComposeFeatureFlag + plugins { alias(libs.plugins.android.application) alias(libs.plugins.kotlin.android) @@ -100,8 +102,10 @@ ksp { } composeCompiler { - enableStrongSkippingMode = true - enableNonSkippingGroupOptimization = true + featureFlags = setOf( + ComposeFeatureFlag.StrongSkipping, + ComposeFeatureFlag.OptimizeNonSkippingGroups + ) if (project.findProperty("enableComposeCompilerReports") == "true") { val dest = layout.buildDirectory.dir("compose_metrics") @@ -126,10 +130,12 @@ dependencies { implementation(libs.compose.ui) implementation(libs.compose.ui.util) implementation(libs.compose.shimmer) - implementation(libs.compose.coil) implementation(libs.compose.lottie) implementation(libs.compose.material3) + implementation(libs.coil.compose) + implementation(libs.coil.ktor) + implementation(libs.palette) implementation(libs.monet) runtimeOnly(projects.core.materialCompat) diff --git a/app/src/main/kotlin/app/vitune/android/Database.kt b/app/src/main/kotlin/app/vitune/android/Database.kt index 81ee55dfde..f57eb24ecd 100644 --- a/app/src/main/kotlin/app/vitune/android/Database.kt +++ b/app/src/main/kotlin/app/vitune/android/Database.kt @@ -52,12 +52,12 @@ import app.vitune.android.models.SongPlaylistMap import app.vitune.android.models.SongWithContentLength import app.vitune.android.models.SortedSongPlaylistMap import app.vitune.android.service.LOCAL_KEY_PREFIX -import app.vitune.core.ui.utils.songBundle import app.vitune.core.data.enums.AlbumSortBy import app.vitune.core.data.enums.ArtistSortBy import app.vitune.core.data.enums.PlaylistSortBy import app.vitune.core.data.enums.SongSortBy import app.vitune.core.data.enums.SortOrder +import app.vitune.core.ui.utils.songBundle import io.ktor.http.Url import kotlinx.coroutines.flow.Flow diff --git a/app/src/main/kotlin/app/vitune/android/MainApplication.kt b/app/src/main/kotlin/app/vitune/android/MainApplication.kt index 9b2bc0f139..459cf25917 100644 --- a/app/src/main/kotlin/app/vitune/android/MainApplication.kt +++ b/app/src/main/kotlin/app/vitune/android/MainApplication.kt @@ -112,10 +112,14 @@ import app.vitune.providers.innertube.Innertube import app.vitune.providers.innertube.models.bodies.BrowseBody import app.vitune.providers.innertube.requests.playlistPage import app.vitune.providers.innertube.requests.song -import coil.ImageLoader -import coil.ImageLoaderFactory -import coil.disk.DiskCache -import coil.util.DebugLogger +import coil3.ImageLoader +import coil3.PlatformContext +import coil3.SingletonImageLoader +import coil3.disk.DiskCache +import coil3.disk.directory +import coil3.memory.MemoryCache +import coil3.request.crossfade +import coil3.util.DebugLogger import com.kieronquinn.monetcompat.core.MonetActivityAccessException import com.kieronquinn.monetcompat.core.MonetCompat import com.kieronquinn.monetcompat.interfaces.MonetColorsChangedListener @@ -266,7 +270,10 @@ class MainActivity : ComponentActivity(), MonetColorsChangedListener { } val pip = isInPip( - onChanged = { if (it && vm.binder?.player?.shouldBePlaying == true) playerBottomSheetState.expandSoft() } + onChange = { + if (!it || vm.binder?.player?.shouldBePlaying != true) return@isInPip + playerBottomSheetState.expandSoft() + } ) KeyedCrossfade(state = pip) { currentPip -> @@ -378,11 +385,15 @@ class MainActivity : ComponentActivity(), MonetColorsChangedListener { MediaStore.Audio.Artists.ENTRY_CONTENT_TYPE -> extras.artist MediaStore.Audio.Albums.ENTRY_CONTENT_TYPE -> extras.album "vnd.android.cursor.item/audio" -> listOfNotNull( - extras.album, extras.artist, extras.genre, extras.title + extras.album, + extras.artist, + extras.genre, + extras.title ).joinToString(separator = " ") @Suppress("deprecation") - MediaStore.Audio.Playlists.ENTRY_CONTENT_TYPE -> extras.playlist + MediaStore.Audio.Playlists.ENTRY_CONTENT_TYPE + -> extras.playlist else -> null } @@ -421,6 +432,7 @@ class MainActivity : ComponentActivity(), MonetColorsChangedListener { } context(Context) +@Suppress("CyclomaticComplexMethod") fun handleUrl( uri: Uri, binder: PlayerService.Binder? @@ -484,7 +496,7 @@ val LocalPlayerAwareWindowInsets = compositionLocalOf { error("No player insets provided") } val LocalCredentialManager = staticCompositionLocalOf { Dependencies.credentialManager } -class MainApplication : Application(), ImageLoaderFactory, Configuration.Provider { +class MainApplication : Application(), SingletonImageLoader.Factory, Configuration.Provider { override fun onCreate() { StrictMode.setVmPolicy( VmPolicy.Builder() @@ -505,15 +517,19 @@ class MainApplication : Application(), ImageLoaderFactory, Configuration.Provide ServiceNotifications.createAll() } - override fun newImageLoader() = ImageLoader.Builder(this) + override fun newImageLoader(context: PlatformContext) = ImageLoader.Builder(this) .crossfade(true) - .respectCacheHeaders(false) - .diskCache( + .memoryCache { + MemoryCache.Builder() + .maxSizePercent(context, 0.1) + .build() + } + .diskCache { DiskCache.Builder() - .directory(cacheDir.resolve("coil")) + .directory(context.cacheDir.resolve("coil")) .maxSizeBytes(DataPreferences.coilDiskCacheMaxSize.bytes) .build() - ) + } .let { if (BuildConfig.DEBUG) it.logger(DebugLogger()) else it } .build() diff --git a/app/src/main/kotlin/app/vitune/android/preferences/UIStatePreferences.kt b/app/src/main/kotlin/app/vitune/android/preferences/UIStatePreferences.kt index 78f5c9ceec..1a66c91775 100644 --- a/app/src/main/kotlin/app/vitune/android/preferences/UIStatePreferences.kt +++ b/app/src/main/kotlin/app/vitune/android/preferences/UIStatePreferences.kt @@ -7,6 +7,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import app.vitune.android.GlobalPreferencesHolder import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList object UIStatePreferences : GlobalPreferencesHolder() { @@ -19,17 +20,22 @@ object UIStatePreferences : GlobalPreferencesHolder() { private var visibleTabs by json(mapOf>()) @Composable - fun mutableTabStateOf(key: String, default: List = listOf()): MutableState> = - remember(key, default, visibleTabs) { - mutableStateOf( - value = visibleTabs.getOrDefault(key, default).toImmutableList(), - policy = object : SnapshotMutationPolicy> { - override fun equivalent(a: ImmutableList, b: ImmutableList): Boolean { - val eq = a == b - if (!eq) visibleTabs += key to b - return eq - } + fun mutableTabStateOf( + key: String, + default: ImmutableList = persistentListOf() + ): MutableState> = remember(key, default, visibleTabs) { + mutableStateOf( + value = visibleTabs.getOrDefault(key, default).toImmutableList(), + policy = object : SnapshotMutationPolicy> { + override fun equivalent( + a: ImmutableList, + b: ImmutableList + ): Boolean { + val eq = a == b + if (!eq) visibleTabs += key to b + return eq } - ) - } + } + ) + } } diff --git a/app/src/main/kotlin/app/vitune/android/service/BitmapProvider.kt b/app/src/main/kotlin/app/vitune/android/service/BitmapProvider.kt index 42342a86cc..33f5af2e32 100644 --- a/app/src/main/kotlin/app/vitune/android/service/BitmapProvider.kt +++ b/app/src/main/kotlin/app/vitune/android/service/BitmapProvider.kt @@ -3,13 +3,14 @@ package app.vitune.android.service import android.content.Context import android.content.res.Configuration import android.graphics.Bitmap -import android.graphics.drawable.BitmapDrawable import android.net.Uri import androidx.core.graphics.applyCanvas import app.vitune.android.utils.thumbnail -import coil.imageLoader -import coil.request.Disposable -import coil.request.ImageRequest +import coil3.imageLoader +import coil3.request.Disposable +import coil3.request.ImageRequest +import coil3.request.allowHardware +import coil3.toBitmap context(Context) class BitmapProvider( @@ -84,7 +85,7 @@ class BitmapProvider( onDone(bitmap) }, onSuccess = { _, result -> - lastBitmap = (result.drawable as BitmapDrawable).bitmap + lastBitmap = result.image.run { toBitmap(width, height) } onDone(bitmap) } ) diff --git a/app/src/main/kotlin/app/vitune/android/service/PlayerService.kt b/app/src/main/kotlin/app/vitune/android/service/PlayerService.kt index d2c23f94b1..1300308a43 100644 --- a/app/src/main/kotlin/app/vitune/android/service/PlayerService.kt +++ b/app/src/main/kotlin/app/vitune/android/service/PlayerService.kt @@ -340,7 +340,9 @@ class PlayerService : InvincibleService(), Player.Listener, PlaybackStatsListene callback: (T) -> Unit ) = launch { prop.stateFlow.collectLatest { handler.post { callback(it) } } } - subscribe(AppearancePreferences.isShowingThumbnailInLockscreenProperty) { maybeShowSongCoverInLockScreen() } + subscribe(AppearancePreferences.isShowingThumbnailInLockscreenProperty) { + maybeShowSongCoverInLockScreen() + } subscribe(PlayerPreferences.bassBoostLevelProperty) { maybeBassBoost() } subscribe(PlayerPreferences.bassBoostProperty) { maybeBassBoost() } @@ -352,7 +354,9 @@ class PlayerService : InvincibleService(), Player.Listener, PlaybackStatsListene player.setPlaybackPitch(it.coerceAtLeast(0.01f)) } subscribe(PlayerPreferences.queueLoopEnabledProperty) { updateRepeatMode() } - subscribe(PlayerPreferences.resumePlaybackWhenDeviceConnectedProperty) { maybeResumePlaybackWhenDeviceConnected() } + subscribe(PlayerPreferences.resumePlaybackWhenDeviceConnectedProperty) { + maybeResumePlaybackWhenDeviceConnected() + } subscribe(PlayerPreferences.skipSilenceProperty) { player.skipSilenceEnabled = it } subscribe(PlayerPreferences.speedProperty) { player.setPlaybackSpeed(it.coerceAtLeast(0.01f)) @@ -633,7 +637,6 @@ class PlayerService : InvincibleService(), Player.Listener, PlaybackStatsListene } } - @Suppress("ReturnCount") private fun maybeNormalizeVolume() { if (!PlayerPreferences.volumeNormalization) { loudnessEnhancer?.enabled = false @@ -684,6 +687,7 @@ class PlayerService : InvincibleService(), Player.Listener, PlaybackStatsListene } } + @Suppress("CyclomaticComplexMethod") // TODO: evaluate CyclomaticComplexMethod threshold private fun maybeSponsorBlock() { poiTimestamp = null @@ -726,6 +730,7 @@ class PlayerService : InvincibleService(), Player.Listener, PlaybackStatsListene val lastSegmentEnd = segments.lastOrNull()?.end?.inWholeMilliseconds ?: return@mapCatching + @Suppress("LoopWithTooManyJumpStatements") do { if (lastSegmentEnd < posMillis()) { yield() @@ -741,7 +746,7 @@ class PlayerService : InvincibleService(), Player.Listener, PlaybackStatsListene ((nextSegment.start.inWholeMilliseconds - posMillis()) / speed().toDouble()).milliseconds ) - if (posMillis() !in nextSegment.start.inWholeMilliseconds..nextSegment.end.inWholeMilliseconds) { + if (posMillis().milliseconds !in nextSegment.start..nextSegment.end) { // Player is not in the segment for some reason, maybe the user seeked in the meantime yield() continue @@ -851,19 +856,23 @@ class PlayerService : InvincibleService(), Player.Listener, PlaybackStatsListene private fun sendOpenEqualizerIntent() = sendBroadcast( Intent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION).apply { - replaceExtras(EqualizerIntentBundleAccessor.bundle { - audioSession = player.audioSessionId - packageName = packageName - contentType = AudioEffect.CONTENT_TYPE_MUSIC - }) + replaceExtras( + EqualizerIntentBundleAccessor.bundle { + audioSession = player.audioSessionId + packageName = packageName + contentType = AudioEffect.CONTENT_TYPE_MUSIC + } + ) } ) private fun sendCloseEqualizerIntent() = sendBroadcast( Intent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION).apply { - replaceExtras(EqualizerIntentBundleAccessor.bundle { - audioSession = player.audioSessionId - }) + replaceExtras( + EqualizerIntentBundleAccessor.bundle { + audioSession = player.audioSessionId + } + ) } ) @@ -1298,6 +1307,7 @@ class PlayerService : InvincibleService(), Player.Listener, PlaybackStatsListene SimpleCache(directory, cacheEvictor, createDatabaseProvider(context)) } + @Suppress("CyclomaticComplexMethod") fun createYouTubeDataSourceResolverFactory( context: Context, cache: Cache, @@ -1326,7 +1336,7 @@ class PlayerService : InvincibleService(), Player.Listener, PlaybackStatsListene } ?: this if ( - dataSpec.isLocal || ((chunkLength != null) && cache.isCached( + dataSpec.isLocal || (chunkLength != null && cache.isCached( /* key = */ mediaId, /* position = */ dataSpec.position, /* length = */ chunkLength diff --git a/app/src/main/kotlin/app/vitune/android/service/ServiceNotifications.kt b/app/src/main/kotlin/app/vitune/android/service/ServiceNotifications.kt index 62fda8e52f..dfdf74bb2d 100644 --- a/app/src/main/kotlin/app/vitune/android/service/ServiceNotifications.kt +++ b/app/src/main/kotlin/app/vitune/android/service/ServiceNotifications.kt @@ -133,7 +133,10 @@ abstract class NotificationChannels { } inline fun readOnlyProvider( - crossinline provide: (thisRef: ThisRef, property: KProperty<*>) -> (thisRef: ThisRef, property: KProperty<*>) -> Return + crossinline provide: ( + thisRef: ThisRef, + property: KProperty<*> + ) -> (thisRef: ThisRef, property: KProperty<*>) -> Return ) = PropertyDelegateProvider> { thisRef, property -> val provider = provide(thisRef, property) ReadOnlyProperty { innerThisRef, innerProperty -> provider(innerThisRef, innerProperty) } diff --git a/app/src/main/kotlin/app/vitune/android/ui/components/BottomSheet.kt b/app/src/main/kotlin/app/vitune/android/ui/components/BottomSheet.kt index 1e0d467aa7..7fe8379c6c 100644 --- a/app/src/main/kotlin/app/vitune/android/ui/components/BottomSheet.kt +++ b/app/src/main/kotlin/app/vitune/android/ui/components/BottomSheet.kt @@ -107,8 +107,11 @@ fun BottomSheet( } } +@Suppress("TooManyFunctions") @Stable -class BottomSheetState internal constructor( +class BottomSheetState +@Suppress("LongParameterList") +internal constructor( density: Density, initialValue: Dp, private val coroutineScope: CoroutineScope, @@ -267,9 +270,9 @@ class BottomSheetState internal constructor( @Composable fun rememberBottomSheetState( - key: Any? = Unit, dismissedBound: Dp, expandedBound: Dp, + key: Any? = Unit, collapsedBound: Dp = dismissedBound, initialAnchor: BottomSheetState.Anchor = BottomSheetState.Anchor.Dismissed ): BottomSheetState { diff --git a/app/src/main/kotlin/app/vitune/android/ui/components/Menu.kt b/app/src/main/kotlin/app/vitune/android/ui/components/Menu.kt index 90b7a15cbf..cf57630597 100644 --- a/app/src/main/kotlin/app/vitune/android/ui/components/Menu.kt +++ b/app/src/main/kotlin/app/vitune/android/ui/components/Menu.kt @@ -28,7 +28,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.nestedscroll.nestedScroll -import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.times import app.vitune.android.LocalPlayerAwareWindowInsets import app.vitune.android.ui.modifiers.pressable diff --git a/app/src/main/kotlin/app/vitune/android/ui/components/themed/BigIconButton.kt b/app/src/main/kotlin/app/vitune/android/ui/components/themed/BigIconButton.kt index 52e15954a2..dbabfc1484 100644 --- a/app/src/main/kotlin/app/vitune/android/ui/components/themed/BigIconButton.kt +++ b/app/src/main/kotlin/app/vitune/android/ui/components/themed/BigIconButton.kt @@ -16,7 +16,6 @@ import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.Shape import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp -import app.vitune.android.ui.modifiers.pressable import app.vitune.core.ui.LocalAppearance import app.vitune.core.ui.utils.roundedShape diff --git a/app/src/main/kotlin/app/vitune/android/ui/components/themed/LayoutWithAdaptiveThumbnail.kt b/app/src/main/kotlin/app/vitune/android/ui/components/themed/LayoutWithAdaptiveThumbnail.kt index 0f922119b6..58a973ea08 100644 --- a/app/src/main/kotlin/app/vitune/android/ui/components/themed/LayoutWithAdaptiveThumbnail.kt +++ b/app/src/main/kotlin/app/vitune/android/ui/components/themed/LayoutWithAdaptiveThumbnail.kt @@ -19,7 +19,7 @@ import app.vitune.core.ui.LocalAppearance import app.vitune.core.ui.shimmer import app.vitune.core.ui.utils.isLandscape import app.vitune.core.ui.utils.px -import coil.compose.AsyncImage +import coil3.compose.AsyncImage import com.valentinilk.shimmer.shimmer @Composable diff --git a/app/src/main/kotlin/app/vitune/android/ui/components/themed/MediaItemMenu.kt b/app/src/main/kotlin/app/vitune/android/ui/components/themed/MediaItemMenu.kt index 6025bea4e5..1c8a82fa6b 100644 --- a/app/src/main/kotlin/app/vitune/android/ui/components/themed/MediaItemMenu.kt +++ b/app/src/main/kotlin/app/vitune/android/ui/components/themed/MediaItemMenu.kt @@ -76,7 +76,6 @@ import app.vitune.android.utils.isCached import app.vitune.android.utils.launchYouTubeMusic import app.vitune.android.utils.medium import app.vitune.android.utils.semiBold -import app.vitune.core.ui.utils.songBundle import app.vitune.android.utils.toast import app.vitune.core.data.enums.PlaylistSortBy import app.vitune.core.data.enums.SortOrder @@ -85,6 +84,7 @@ import app.vitune.core.ui.LocalAppearance import app.vitune.core.ui.favoritesIcon import app.vitune.core.ui.utils.px import app.vitune.core.ui.utils.roundedShape +import app.vitune.core.ui.utils.songBundle import app.vitune.providers.innertube.models.NavigationEndpoint import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.collectLatest diff --git a/app/src/main/kotlin/app/vitune/android/ui/components/themed/NavigationRail.kt b/app/src/main/kotlin/app/vitune/android/ui/components/themed/NavigationRail.kt index df7f4196f9..ccbdaf9d29 100644 --- a/app/src/main/kotlin/app/vitune/android/ui/components/themed/NavigationRail.kt +++ b/app/src/main/kotlin/app/vitune/android/ui/components/themed/NavigationRail.kt @@ -182,9 +182,9 @@ inline fun NavigationRail( noinline onTopIconButtonClick: () -> Unit, tabIndex: Int, crossinline onTabIndexChange: (Int) -> Unit, - modifier: Modifier = Modifier, hiddenTabs: ImmutableList, crossinline setHiddenTabs: (List) -> Unit, + modifier: Modifier = Modifier, crossinline content: TabsBuilder.() -> Unit ) { val (colorPalette, typography) = LocalAppearance.current diff --git a/app/src/main/kotlin/app/vitune/android/ui/items/AlbumItem.kt b/app/src/main/kotlin/app/vitune/android/ui/items/AlbumItem.kt index 3816f3251f..38a872c8e5 100644 --- a/app/src/main/kotlin/app/vitune/android/ui/items/AlbumItem.kt +++ b/app/src/main/kotlin/app/vitune/android/ui/items/AlbumItem.kt @@ -21,7 +21,7 @@ import app.vitune.core.ui.LocalAppearance import app.vitune.core.ui.shimmer import app.vitune.core.ui.utils.px import app.vitune.providers.innertube.Innertube -import coil.compose.AsyncImage +import coil3.compose.AsyncImage @Composable fun AlbumItem( diff --git a/app/src/main/kotlin/app/vitune/android/ui/items/ArtistItem.kt b/app/src/main/kotlin/app/vitune/android/ui/items/ArtistItem.kt index 7b4a3a51c6..2f8db847e6 100644 --- a/app/src/main/kotlin/app/vitune/android/ui/items/ArtistItem.kt +++ b/app/src/main/kotlin/app/vitune/android/ui/items/ArtistItem.kt @@ -23,7 +23,7 @@ import app.vitune.core.ui.LocalAppearance import app.vitune.core.ui.shimmer import app.vitune.core.ui.utils.px import app.vitune.providers.innertube.Innertube -import coil.compose.AsyncImage +import coil3.compose.AsyncImage @Composable fun ArtistItem( diff --git a/app/src/main/kotlin/app/vitune/android/ui/items/PlaylistItem.kt b/app/src/main/kotlin/app/vitune/android/ui/items/PlaylistItem.kt index 0495d9688e..c3a69c5c98 100644 --- a/app/src/main/kotlin/app/vitune/android/ui/items/PlaylistItem.kt +++ b/app/src/main/kotlin/app/vitune/android/ui/items/PlaylistItem.kt @@ -42,7 +42,7 @@ import app.vitune.core.ui.shimmer import app.vitune.core.ui.utils.px import app.vitune.core.ui.utils.roundedShape import app.vitune.providers.innertube.Innertube -import coil.compose.AsyncImage +import coil3.compose.AsyncImage import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flowOf diff --git a/app/src/main/kotlin/app/vitune/android/ui/items/SongItem.kt b/app/src/main/kotlin/app/vitune/android/ui/items/SongItem.kt index 9470159205..09843607fb 100644 --- a/app/src/main/kotlin/app/vitune/android/ui/items/SongItem.kt +++ b/app/src/main/kotlin/app/vitune/android/ui/items/SongItem.kt @@ -37,7 +37,7 @@ import app.vitune.core.ui.shimmer import app.vitune.core.ui.utils.px import app.vitune.core.ui.utils.songBundle import app.vitune.providers.innertube.Innertube -import coil.compose.AsyncImage +import coil3.compose.AsyncImage @Composable fun SongItem( diff --git a/app/src/main/kotlin/app/vitune/android/ui/items/VideoItem.kt b/app/src/main/kotlin/app/vitune/android/ui/items/VideoItem.kt index 75872b022d..38c37f5c6d 100644 --- a/app/src/main/kotlin/app/vitune/android/ui/items/VideoItem.kt +++ b/app/src/main/kotlin/app/vitune/android/ui/items/VideoItem.kt @@ -26,7 +26,7 @@ import app.vitune.core.ui.overlay import app.vitune.core.ui.shimmer import app.vitune.core.ui.utils.roundedShape import app.vitune.providers.innertube.Innertube -import coil.compose.AsyncImage +import coil3.compose.AsyncImage @Composable fun VideoItem( diff --git a/app/src/main/kotlin/app/vitune/android/ui/screens/home/HomeSongs.kt b/app/src/main/kotlin/app/vitune/android/ui/screens/home/HomeSongs.kt index e5275b2eb6..4942ae33e3 100644 --- a/app/src/main/kotlin/app/vitune/android/ui/screens/home/HomeSongs.kt +++ b/app/src/main/kotlin/app/vitune/android/ui/screens/home/HomeSongs.kt @@ -340,7 +340,8 @@ fun HideSongDialog( ) } -@Suppress("UnusedReceiverParameter") +// Row content, for convenience, doesn't need modifier/receiver +@Suppress("UnusedReceiverParameter", "ModifierMissing") @Composable fun RowScope.HeaderSongSortBy( sortBy: SongSortBy, diff --git a/app/src/main/kotlin/app/vitune/android/ui/screens/player/Lyrics.kt b/app/src/main/kotlin/app/vitune/android/ui/screens/player/Lyrics.kt index 9a35cdc83f..a63cdbe292 100644 --- a/app/src/main/kotlin/app/vitune/android/ui/screens/player/Lyrics.kt +++ b/app/src/main/kotlin/app/vitune/android/ui/screens/player/Lyrics.kt @@ -389,8 +389,8 @@ fun Lyrics( lyricsState.sentences?.let { SynchronizedLyrics(it.toImmutableMap()) { binder?.player?.let { player -> - player.currentPosition + UPDATE_DELAY + lyricsState.offset - (lyrics?.startTime - ?: 0L) + player.currentPosition + UPDATE_DELAY + lyricsState.offset - + (lyrics?.startTime ?: 0L) } ?: 0L } } diff --git a/app/src/main/kotlin/app/vitune/android/ui/screens/player/LyricsDialog.kt b/app/src/main/kotlin/app/vitune/android/ui/screens/player/LyricsDialog.kt index ce0c90715d..b37a114ba1 100644 --- a/app/src/main/kotlin/app/vitune/android/ui/screens/player/LyricsDialog.kt +++ b/app/src/main/kotlin/app/vitune/android/ui/screens/player/LyricsDialog.kt @@ -34,7 +34,7 @@ import app.vitune.android.utils.thumbnail import app.vitune.android.utils.windowState import app.vitune.core.ui.LocalAppearance import app.vitune.core.ui.utils.px -import coil.compose.AsyncImage +import coil3.compose.AsyncImage @Composable fun LyricsDialog( diff --git a/app/src/main/kotlin/app/vitune/android/ui/screens/player/Player.kt b/app/src/main/kotlin/app/vitune/android/ui/screens/player/Player.kt index 0a3b5bff2c..bb27d57a77 100644 --- a/app/src/main/kotlin/app/vitune/android/ui/screens/player/Player.kt +++ b/app/src/main/kotlin/app/vitune/android/ui/screens/player/Player.kt @@ -102,7 +102,7 @@ import app.vitune.core.ui.utils.px import app.vitune.core.ui.utils.roundedShape import app.vitune.core.ui.utils.songBundle import app.vitune.providers.innertube.models.NavigationEndpoint -import coil.compose.AsyncImage +import coil3.compose.AsyncImage import kotlinx.coroutines.flow.distinctUntilChanged import kotlin.math.absoluteValue diff --git a/app/src/main/kotlin/app/vitune/android/ui/screens/player/Thumbnail.kt b/app/src/main/kotlin/app/vitune/android/ui/screens/player/Thumbnail.kt index b97e561423..9db6a6050f 100644 --- a/app/src/main/kotlin/app/vitune/android/ui/screens/player/Thumbnail.kt +++ b/app/src/main/kotlin/app/vitune/android/ui/screens/player/Thumbnail.kt @@ -63,7 +63,7 @@ import app.vitune.android.utils.windowState import app.vitune.core.ui.Dimensions import app.vitune.core.ui.LocalAppearance import app.vitune.core.ui.utils.px -import coil.compose.AsyncImage +import coil3.compose.AsyncImage import kotlinx.coroutines.launch import java.net.UnknownHostException import java.nio.channels.UnresolvedAddressException diff --git a/app/src/main/kotlin/app/vitune/android/ui/screens/search/OnlineSearch.kt b/app/src/main/kotlin/app/vitune/android/ui/screens/search/OnlineSearch.kt index a677bf7b33..c56763a117 100644 --- a/app/src/main/kotlin/app/vitune/android/ui/screens/search/OnlineSearch.kt +++ b/app/src/main/kotlin/app/vitune/android/ui/screens/search/OnlineSearch.kt @@ -76,7 +76,7 @@ fun OnlineSearch( onViewPlaylist: (String) -> Unit, decorationBox: @Composable (@Composable () -> Unit) -> Unit, focused: Boolean, - modifier: Modifier = Modifier, + modifier: Modifier = Modifier ) = Box(modifier = modifier) { val (colorPalette, typography) = LocalAppearance.current @@ -104,7 +104,7 @@ fun OnlineSearch( runCatching { Url(textFieldValue.text).takeIf { it.host.endsWith("youtube.com", ignoreCase = true) && - it.pathSegments.lastOrNull()?.equals("playlist", ignoreCase = true) == true + it.segments.lastOrNull()?.equals("playlist", ignoreCase = true) == true }?.parameters?.get("list") }.getOrNull() } diff --git a/app/src/main/kotlin/app/vitune/android/ui/screens/settings/CacheSettings.kt b/app/src/main/kotlin/app/vitune/android/ui/screens/settings/CacheSettings.kt index aea4173145..2d708b9b93 100644 --- a/app/src/main/kotlin/app/vitune/android/ui/screens/settings/CacheSettings.kt +++ b/app/src/main/kotlin/app/vitune/android/ui/screens/settings/CacheSettings.kt @@ -22,10 +22,8 @@ import app.vitune.android.preferences.PlayerPreferences import app.vitune.android.ui.components.themed.LinearProgressIndicator import app.vitune.android.ui.screens.Route import app.vitune.core.data.enums.ExoPlayerDiskCacheSize -import coil.Coil -import coil.annotation.ExperimentalCoilApi +import coil3.imageLoader -@kotlin.OptIn(ExperimentalCoilApi::class) @OptIn(UnstableApi::class) @Route @Composable @@ -36,7 +34,7 @@ fun CacheSettings() = with(DataPreferences) { SettingsCategoryScreen(title = stringResource(R.string.cache)) { SettingsDescription(text = stringResource(R.string.cache_description)) - Coil.imageLoader(context).diskCache?.let { diskCache -> + context.imageLoader.diskCache?.let { diskCache -> val diskCacheSize by remember { derivedStateOf { diskCache.size } } val formattedSize = remember(diskCacheSize) { Formatter.formatShortFileSize(context, diskCacheSize) diff --git a/app/src/main/kotlin/app/vitune/android/utils/Logcat.kt b/app/src/main/kotlin/app/vitune/android/utils/Logcat.kt index f21905cf5f..fc74c9e110 100644 --- a/app/src/main/kotlin/app/vitune/android/utils/Logcat.kt +++ b/app/src/main/kotlin/app/vitune/android/utils/Logcat.kt @@ -28,12 +28,39 @@ import kotlinx.parcelize.Parcelize import kotlinx.parcelize.WriteWith import java.io.IOException +private val logcatDateTimeFormat = LocalDateTime.Format { + date( + LocalDate.Format { + year() + char('-') + monthNumber() + char('-') + dayOfMonth() + } + ) + + char(' ') + + time( + LocalTime.Format { + hour() + char(':') + minute() + char(':') + second() + char('.') + secondFraction(3) + } + ) +} + @Immutable sealed interface Logcat : Parcelable { val id: Int companion object { // @formatter:off + @Suppress("MaximumLineLength") private val regex = "^(\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}.\\d{3})\\s+(\\w)/(.+?)\\(\\s*(\\d+)\\): (.*)$".toRegex() // @formatter:on @@ -43,27 +70,10 @@ sealed interface Logcat : Parcelable { .mapNotNull { it?.value } FormattedLine( - timestamp = LocalDateTime.parse(timestamp, LocalDateTime.Format { - date(LocalDate.Format { - year() - char('-') - monthNumber() - char('-') - dayOfMonth() - }) - - char(' ') - - time(LocalTime.Format { - hour() - char(':') - minute() - char(':') - second() - char('.') - secondFraction(3) - }) - }).toInstant(TimeZone.UTC), + timestamp = LocalDateTime.parse( + input = timestamp, + format = logcatDateTimeFormat + ).toInstant(TimeZone.UTC), level = FormattedLine.Level.codeLut[level.firstOrNull()] ?: FormattedLine.Level.Unknown, tag = tag, @@ -78,12 +88,14 @@ sealed interface Logcat : Parcelable { fun logAsFlow() = flow { val proc = - Runtime.getRuntime().exec("/system/bin/logcat -v time,year --pid=${Process.myPid()}") + Runtime.getRuntime() + .exec("/system/bin/logcat -v time,year --pid=${Process.myPid()}") val reader = proc.inputStream.bufferedReader() val ctx = currentCoroutineContext() var id = 0 + @Suppress("LoopWithTooManyJumpStatements", "SwallowedException") while (ctx.isActive) { try { emit((reader.readLine() ?: break).toLine(id++)) diff --git a/app/src/main/kotlin/app/vitune/android/utils/PIP.kt b/app/src/main/kotlin/app/vitune/android/utils/PIP.kt index 1b91dbba6c..9938a4d245 100644 --- a/app/src/main/kotlin/app/vitune/android/utils/PIP.kt +++ b/app/src/main/kotlin/app/vitune/android/utils/PIP.kt @@ -116,19 +116,19 @@ private val Activity?.pip get() = if (isAtLeastAndroid7) this?.isInPictureInPict @Composable fun isInPip( - onChanged: (Boolean) -> Unit = { } + onChange: (Boolean) -> Unit = { } ): Boolean { val context = LocalContext.current val activity = remember(context) { context.findActivityNullable() } - val currentOnChanged by rememberUpdatedState(onChanged) + val currentOnChange by rememberUpdatedState(onChange) var pip by rememberSaveable { mutableStateOf(activity.pip) } - DisposableEffect(activity, currentOnChanged) { + DisposableEffect(activity, currentOnChange) { if (activity !is OnPictureInPictureModeChangedProvider) return@DisposableEffect onDispose { } val listener: (PictureInPictureModeChangedInfo) -> Unit = { pip = it.isInPictureInPictureMode - currentOnChanged(pip) + currentOnChange(pip) } activity.addOnPictureInPictureModeChangedListener(listener) @@ -168,8 +168,8 @@ fun Modifier.pip( fun Pip( numerator: Int, denominator: Int, - actions: ActionReceiver? = null, modifier: Modifier = Modifier, + actions: ActionReceiver? = null, content: @Composable BoxScope.() -> Unit ) { val context = LocalContext.current diff --git a/app/src/main/kotlin/app/vitune/android/utils/Player.kt b/app/src/main/kotlin/app/vitune/android/utils/Player.kt index d89b2f3435..30409a0863 100644 --- a/app/src/main/kotlin/app/vitune/android/utils/Player.kt +++ b/app/src/main/kotlin/app/vitune/android/utils/Player.kt @@ -67,19 +67,19 @@ fun Player.forceSeekToPrevious( seekToStart: Boolean = true ): Unit = when { seekToStart && currentPosition > maxSeekToPreviousPosition -> seekToPrevious() - hideExplicit -> { - if (mediaItemCount > 1) { - var i = currentMediaItemIndex - 1 - while ( - i !in (0 until mediaItemCount) || - getMediaItemAt(i).mediaMetadata.extras?.songBundle?.explicit == true - ) { - if (i <= 0) i = mediaItemCount - 1 else i-- - } - seekTo(i, C.TIME_UNSET) - } else forceSeekToPrevious(hideExplicit = false) - // fall back to default behavior if there is only a single song + hideExplicit -> if (mediaItemCount <= 1) forceSeekToPrevious(hideExplicit = false) + else { + var i = currentMediaItemIndex - 1 + while ( + i !in (0 until mediaItemCount) || + getMediaItemAt(i).mediaMetadata.extras?.songBundle?.explicit == true + ) { + if (i <= 0) i = mediaItemCount - 1 else i-- + } + seekTo(i, C.TIME_UNSET) } + // fall back to default behavior if there is only a single song + hasPreviousMediaItem() -> seekToPreviousMediaItem() mediaItemCount > 0 -> seekTo(mediaItemCount - 1, C.TIME_UNSET) else -> {} diff --git a/app/src/main/kotlin/app/vitune/android/utils/PlayerState.kt b/app/src/main/kotlin/app/vitune/android/utils/PlayerState.kt index cd6e9ac63c..f5d08a0211 100644 --- a/app/src/main/kotlin/app/vitune/android/utils/PlayerState.kt +++ b/app/src/main/kotlin/app/vitune/android/utils/PlayerState.kt @@ -141,13 +141,13 @@ fun rememberEqualizerLauncher( return rememberUpdatedState { try { launcher.launch( - Intent(AudioEffect.ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL).apply { - replaceExtras(EqualizerIntentBundleAccessor.bundle { + Intent(AudioEffect.ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL).replaceExtras( + EqualizerIntentBundleAccessor.bundle { audioSessionId()?.let { audioSession = it } packageName = context.packageName this.contentType = contentType - }) - } + } + ) ) } catch (e: ActivityNotFoundException) { context.toast(context.getString(R.string.no_equalizer_installed)) diff --git a/compose/routing/src/main/kotlin/app/vitune/compose/routing/GlobalRoute.kt b/compose/routing/src/main/kotlin/app/vitune/compose/routing/GlobalRoute.kt index 70bdb297b8..2f77afd2b5 100644 --- a/compose/routing/src/main/kotlin/app/vitune/compose/routing/GlobalRoute.kt +++ b/compose/routing/src/main/kotlin/app/vitune/compose/routing/GlobalRoute.kt @@ -33,6 +33,8 @@ fun CallbackPredictiveBackHandler( ) = PredictiveBackHandler(enabled = enabled) { progress -> onStart() + // The meaning of CancellationException is different here (normally CancellationExceptions should be rethrowed) + @Suppress("SwallowedException") try { progress.collect { onProgress(it.progress) diff --git a/core/data/src/main/kotlin/app/vitune/core/data/utils/CallValidator.kt b/core/data/src/main/kotlin/app/vitune/core/data/utils/CallValidator.kt index 3db49f5d9d..334acfb511 100644 --- a/core/data/src/main/kotlin/app/vitune/core/data/utils/CallValidator.kt +++ b/core/data/src/main/kotlin/app/vitune/core/data/utils/CallValidator.kt @@ -66,30 +66,32 @@ class CallValidator( else signatures.firstOrNull()?.toByteArray()?.sha256 } + @Suppress("ImplicitDefaultLocale") // not relevant private val ByteArray.sha256: String? get() = runCatching { val md = MessageDigest.getInstance("SHA256") md.update(this) md.digest() - }.getOrNull() - ?.joinToString(":") { String.format("%02x", it) } + }.getOrNull()?.joinToString(":") { String.format("%02x", it) } } @JvmInline value class Whitelist(private val map: WhitelistMap = mapOf()) { companion object { - fun parse(parser: XmlResourceParser) = Whitelist(buildMap { - runCatching { - var event = parser.next() + fun parse(parser: XmlResourceParser) = Whitelist( + buildMap { + runCatching { + var event = parser.next() - while (event != XmlResourceParser.END_DOCUMENT) { - if (event == XmlResourceParser.START_TAG && parser.name == "signature") - putV2Tag(parser) + while (event != XmlResourceParser.END_DOCUMENT) { + if (event == XmlResourceParser.START_TAG && parser.name == "signature") + putV2Tag(parser) - event = parser.next() + event = parser.next() + } } } - }) + ) private fun MutableMap>.putV2Tag(parser: XmlResourceParser) = runCatching { diff --git a/detekt.yml b/detekt.yml index 123f4584ee..db925edde9 100644 --- a/detekt.yml +++ b/detekt.yml @@ -120,5 +120,7 @@ style: active: false # Overlaps with ModifierOrdering, ktlint preferred because of auto-correct NewLineAtEndOfFile: active: false # Overlaps with FinalNewline, ktlint preferred because of auto-correct + ReturnCount: + active: false ThrowsCount: active: false diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 968edca19c..2d16d18b5e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,14 +3,15 @@ kotlin = "2.0.20" ksp = "2.0.20-1.0.24" jvm = "17" -agp = "8.6.0" +agp = "8.7.1" room = "2.6.1" media3 = "1.4.1" -ktor = "2.3.12" +ktor = "3.0.0" detekt = "1.23.6" workmanager = "2.9.1" -credentials = "1.5.0-alpha05" +credentials = "1.5.0-alpha06" +coil = "3.0.0-rc01" [plugins] android_application = { id = "com.android.application", version.ref = "agp" } @@ -27,11 +28,11 @@ detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } [libraries] core_ktx = { module = "androidx.core:core-ktx", version = "1.13.1" } -kotlin_coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version = "1.8.1" } +kotlin_coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version = "1.9.0" } kotlin_datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version = "0.6.1" } kotlin_immutable = { module = "org.jetbrains.kotlinx:kotlinx-collections-immutable", version = "0.3.7" } -compose_bom = { module = "androidx.compose:compose-bom", version = "2024.09.00" } +compose_bom = { module = "androidx.compose:compose-bom", version = "2024.10.00" } compose_animation = { module = "androidx.compose.animation:animation" } compose_foundation = { module = "androidx.compose.foundation:foundation" } compose_ui = { module = "androidx.compose.ui:ui" } @@ -39,11 +40,13 @@ compose_ui_util = { module = "androidx.compose.ui:ui-util" } compose_ui_fonts = { module = "androidx.compose.ui:ui-text-google-fonts" } compose_material3 = { module = "androidx.compose.material3:material3", version = "1.3.0" } -compose_activity = { module = "androidx.activity:activity-compose", version = "1.9.2" } +compose_activity = { module = "androidx.activity:activity-compose", version = "1.9.3" } compose_shimmer = { module = "com.valentinilk.shimmer:compose-shimmer", version = "1.3.0" } -compose_coil = { module = "io.coil-kt:coil-compose", version = "2.6.0" } compose_lottie = { module = "com.airbnb.android:lottie-compose", version = "6.4.1" } +coil_compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coil" } +coil_ktor = { module = "io.coil-kt.coil3:coil-network-ktor3", version.ref = "coil" } + room = { module = "androidx.room:room-ktx", version.ref = "room" } room_compiler = { module = "androidx.room:room-compiler", version.ref = "room" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 9355b41557..df97d72b8b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/ktor-client-brotli/src/main/kotlin/io/ktor/client/plugins/compression/Brotli.kt b/ktor-client-brotli/src/main/kotlin/io/ktor/client/plugins/compression/Brotli.kt index 558751c85f..593917017f 100644 --- a/ktor-client-brotli/src/main/kotlin/io/ktor/client/plugins/compression/Brotli.kt +++ b/ktor-client-brotli/src/main/kotlin/io/ktor/client/plugins/compression/Brotli.kt @@ -1,5 +1,5 @@ package io.ktor.client.plugins.compression -fun ContentEncoding.Config.brotli(quality: Float? = null) { +fun ContentEncodingConfig.brotli(quality: Float? = null) { customEncoder(BrotliEncoder, quality) } diff --git a/ktor-client-brotli/src/main/kotlin/io/ktor/client/plugins/compression/BrotliEncoder.kt b/ktor-client-brotli/src/main/kotlin/io/ktor/client/plugins/compression/BrotliEncoder.kt index dca2ad1d1d..c941ccfe40 100644 --- a/ktor-client-brotli/src/main/kotlin/io/ktor/client/plugins/compression/BrotliEncoder.kt +++ b/ktor-client-brotli/src/main/kotlin/io/ktor/client/plugins/compression/BrotliEncoder.kt @@ -1,17 +1,34 @@ package io.ktor.client.plugins.compression +import io.ktor.util.ContentEncoder +import io.ktor.util.Encoder import io.ktor.utils.io.ByteReadChannel +import io.ktor.utils.io.ByteWriteChannel import io.ktor.utils.io.jvm.javaio.toByteReadChannel import io.ktor.utils.io.jvm.javaio.toInputStream -import kotlinx.coroutines.CoroutineScope import org.brotli.dec.BrotliInputStream +import kotlin.coroutines.CoroutineContext -internal object BrotliEncoder : ContentEncoder { +internal object BrotliEncoder : ContentEncoder, Encoder by Brotli { override val name: String = "br" +} - override fun CoroutineScope.encode(source: ByteReadChannel) = +private object Brotli : Encoder { + private fun encode(): Nothing = error("BrotliOutputStream not available (https://github.com/google/brotli/issues/715)") - override fun CoroutineScope.decode(source: ByteReadChannel): ByteReadChannel = - BrotliInputStream(source.toInputStream()).toByteReadChannel() + override fun decode( + source: ByteReadChannel, + coroutineContext: CoroutineContext + ) = BrotliInputStream(source.toInputStream()).toByteReadChannel() + + override fun encode( + source: ByteReadChannel, + coroutineContext: CoroutineContext + ) = encode() + + override fun encode( + source: ByteWriteChannel, + coroutineContext: CoroutineContext + ) = encode() } diff --git a/providers/innertube/src/main/kotlin/app/vitune/providers/innertube/Innertube.kt b/providers/innertube/src/main/kotlin/app/vitune/providers/innertube/Innertube.kt index 553d19f106..0e458026e7 100644 --- a/providers/innertube/src/main/kotlin/app/vitune/providers/innertube/Innertube.kt +++ b/providers/innertube/src/main/kotlin/app/vitune/providers/innertube/Innertube.kt @@ -246,7 +246,6 @@ object Innertube { } } - @Suppress("ReturnCount") fun MusicNavigationButtonRenderer.toMood(): Mood.Item? { return Mood.Item( title = buttonText.runs.firstOrNull()?.text ?: return null, diff --git a/providers/kugou/src/main/kotlin/app/vitune/providers/kugou/KuGou.kt b/providers/kugou/src/main/kotlin/app/vitune/providers/kugou/KuGou.kt index da794934e2..412f0b9461 100644 --- a/providers/kugou/src/main/kotlin/app/vitune/providers/kugou/KuGou.kt +++ b/providers/kugou/src/main/kotlin/app/vitune/providers/kugou/KuGou.kt @@ -127,7 +127,6 @@ object KuGou { return "$newArtist - $newTitle" } - @Suppress("ReturnCount") private fun String.extract(startDelimiter: String, endDelimiter: Char): Pair { val startIndex = indexOf(startDelimiter).takeIf { it != -1 } ?: return this to "" val endIndex = indexOf(endDelimiter, startIndex).takeIf { it != -1 } ?: return this to "" diff --git a/providers/piped/src/main/kotlin/app/vitune/providers/piped/models/PlaylistPreview.kt b/providers/piped/src/main/kotlin/app/vitune/providers/piped/models/PlaylistPreview.kt index 09ec835926..63b0949c51 100644 --- a/providers/piped/src/main/kotlin/app/vitune/providers/piped/models/PlaylistPreview.kt +++ b/providers/piped/src/main/kotlin/app/vitune/providers/piped/models/PlaylistPreview.kt @@ -53,7 +53,7 @@ data class Playlist( val uploaderId get() = if (uploaderUrl.startsWith("/channel/")) uploaderUrl.substringAfter("/channel/") - else Url(uploaderUrl).pathSegments.lastOrNull() + else Url(uploaderUrl).segments.lastOrNull() val duration get() = durationSeconds.seconds } diff --git a/providers/sponsorblock/src/main/kotlin/app/vitune/providers/sponsorblock/models/Action.kt b/providers/sponsorblock/src/main/kotlin/app/vitune/providers/sponsorblock/models/Action.kt index 7d6983f0a7..f399d16ff2 100644 --- a/providers/sponsorblock/src/main/kotlin/app/vitune/providers/sponsorblock/models/Action.kt +++ b/providers/sponsorblock/src/main/kotlin/app/vitune/providers/sponsorblock/models/Action.kt @@ -7,12 +7,16 @@ import kotlinx.serialization.Serializable enum class Action(val serialName: String) { @SerialName("skip") Skip("skip"), + @SerialName("mute") Mute("mute"), + @SerialName("full") Full("full"), + @SerialName("poi") POI("poi"), + @SerialName("chapter") Chapter("chapter") } diff --git a/providers/sponsorblock/src/main/kotlin/app/vitune/providers/sponsorblock/models/Category.kt b/providers/sponsorblock/src/main/kotlin/app/vitune/providers/sponsorblock/models/Category.kt index 294d48704c..704bd0e0e2 100644 --- a/providers/sponsorblock/src/main/kotlin/app/vitune/providers/sponsorblock/models/Category.kt +++ b/providers/sponsorblock/src/main/kotlin/app/vitune/providers/sponsorblock/models/Category.kt @@ -7,20 +7,28 @@ import kotlinx.serialization.Serializable enum class Category(val serialName: String) { @SerialName("sponsor") Sponsor("sponsor"), + @SerialName("selfpromo") SelfPromotion("selfpromo"), + @SerialName("interaction") Interaction("interaction"), + @SerialName("intro") Intro("intro"), + @SerialName("outro") Outro("outro"), + @SerialName("preview") Preview("preview"), + @SerialName("music_offtopic") OfftopicMusic("music_offtopic"), + @SerialName("filler") Filler("filler"), + @SerialName("poi_highlight") PoiHighlight("poi_highlight") } diff --git a/providers/sponsorblock/src/main/kotlin/app/vitune/providers/sponsorblock/models/Segment.kt b/providers/sponsorblock/src/main/kotlin/app/vitune/providers/sponsorblock/models/Segment.kt index 132ed662a0..7d96ed03e6 100644 --- a/providers/sponsorblock/src/main/kotlin/app/vitune/providers/sponsorblock/models/Segment.kt +++ b/providers/sponsorblock/src/main/kotlin/app/vitune/providers/sponsorblock/models/Segment.kt @@ -16,4 +16,4 @@ data class Segment( ) { val start get() = segment.first().seconds val end get() = segment[1].seconds -} \ No newline at end of file +}