Tweak code

This commit is contained in:
vfsfitvnm
2022-07-23 16:57:38 +02:00
parent 841a546c28
commit e912bfed1c
74 changed files with 1162 additions and 398 deletions

View File

@@ -7,7 +7,22 @@ import android.database.sqlite.SQLiteDatabase.CONFLICT_IGNORE
import android.os.Parcel
import androidx.core.database.getFloatOrNull
import androidx.media3.common.MediaItem
import androidx.room.*
import androidx.room.AutoMigration
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.DeleteTable
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.RenameColumn
import androidx.room.RenameTable
import androidx.room.RewriteQueriesToDropUnusedColumns
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.Transaction
import androidx.room.TypeConverter
import androidx.room.TypeConverters
import androidx.room.Update
import androidx.room.migration.AutoMigrationSpec
import androidx.room.migration.Migration
import androidx.sqlite.db.SimpleSQLiteQuery
@@ -15,11 +30,24 @@ import androidx.sqlite.db.SupportSQLiteDatabase
import it.vfsfitvnm.vimusic.enums.PlaylistSortBy
import it.vfsfitvnm.vimusic.enums.SongSortBy
import it.vfsfitvnm.vimusic.enums.SortOrder
import it.vfsfitvnm.vimusic.models.*
import it.vfsfitvnm.vimusic.models.Album
import it.vfsfitvnm.vimusic.models.Artist
import it.vfsfitvnm.vimusic.models.DetailedSong
import it.vfsfitvnm.vimusic.models.DetailedSongWithContentLength
import it.vfsfitvnm.vimusic.models.Format
import it.vfsfitvnm.vimusic.models.Playlist
import it.vfsfitvnm.vimusic.models.PlaylistPreview
import it.vfsfitvnm.vimusic.models.PlaylistWithSongs
import it.vfsfitvnm.vimusic.models.QueuedMediaItem
import it.vfsfitvnm.vimusic.models.SearchQuery
import it.vfsfitvnm.vimusic.models.Song
import it.vfsfitvnm.vimusic.models.SongAlbumMap
import it.vfsfitvnm.vimusic.models.SongArtistMap
import it.vfsfitvnm.vimusic.models.SongPlaylistMap
import it.vfsfitvnm.vimusic.models.SortedSongPlaylistMap
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
@Dao
interface Database {
companion object : Database by DatabaseInitializer.Instance.database
@@ -118,7 +146,10 @@ interface Database {
@Query("SELECT id, name, (SELECT COUNT(*) FROM SongPlaylistMap WHERE playlistId = id) as songCount FROM Playlist ORDER BY songCount ASC")
fun playlistPreviewsByDateSongCount(): Flow<List<PlaylistPreview>>
fun playlistPreviews(sortBy: PlaylistSortBy, sortOrder: SortOrder): Flow<List<PlaylistPreview>> {
fun playlistPreviews(
sortBy: PlaylistSortBy,
sortOrder: SortOrder
): Flow<List<PlaylistPreview>> {
return when (sortBy) {
PlaylistSortBy.Name -> playlistPreviewsByName()
PlaylistSortBy.DateAdded -> playlistPreviewsByDateAdded()
@@ -337,11 +368,15 @@ abstract class DatabaseInitializer protected constructor() : RoomDatabase() {
lateinit var Instance: DatabaseInitializer
context(Context)
operator fun invoke() {
operator fun invoke() {
if (!::Instance.isInitialized) {
Instance = Room
.databaseBuilder(this@Context, DatabaseInitializer::class.java, "data.db")
.addMigrations(From8To9Migration(), From10To11Migration(), From14To15Migration())
.addMigrations(
From8To9Migration(),
From10To11Migration(),
From14To15Migration()
)
.build()
}
}
@@ -355,35 +390,56 @@ abstract class DatabaseInitializer protected constructor() : RoomDatabase() {
class From8To9Migration : Migration(8, 9) {
override fun migrate(it: SupportSQLiteDatabase) {
it.query(SimpleSQLiteQuery("SELECT DISTINCT browseId, text, Info.id FROM Info JOIN Song ON Info.id = Song.albumId;")).use { cursor ->
val albumValues = ContentValues(2)
while (cursor.moveToNext()) {
albumValues.put("id", cursor.getString(0))
albumValues.put("title", cursor.getString(1))
it.insert("Album", CONFLICT_IGNORE, albumValues)
it.query(SimpleSQLiteQuery("SELECT DISTINCT browseId, text, Info.id FROM Info JOIN Song ON Info.id = Song.albumId;"))
.use { cursor ->
val albumValues = ContentValues(2)
while (cursor.moveToNext()) {
albumValues.put("id", cursor.getString(0))
albumValues.put("title", cursor.getString(1))
it.insert("Album", CONFLICT_IGNORE, albumValues)
it.execSQL("UPDATE Song SET albumId = '${cursor.getString(0)}' WHERE albumId = ${cursor.getLong(2)}")
it.execSQL(
"UPDATE Song SET albumId = '${cursor.getString(0)}' WHERE albumId = ${
cursor.getLong(
2
)
}"
)
}
}
}
it.query(SimpleSQLiteQuery("SELECT GROUP_CONCAT(text, ''), SongWithAuthors.songId FROM Info JOIN SongWithAuthors ON Info.id = SongWithAuthors.authorInfoId GROUP BY songId;")).use { cursor ->
val songValues = ContentValues(1)
while (cursor.moveToNext()) {
songValues.put("artistsText", cursor.getString(0))
it.update("Song", CONFLICT_IGNORE, songValues, "id = ?", arrayOf(cursor.getString(1)))
it.query(SimpleSQLiteQuery("SELECT GROUP_CONCAT(text, ''), SongWithAuthors.songId FROM Info JOIN SongWithAuthors ON Info.id = SongWithAuthors.authorInfoId GROUP BY songId;"))
.use { cursor ->
val songValues = ContentValues(1)
while (cursor.moveToNext()) {
songValues.put("artistsText", cursor.getString(0))
it.update(
"Song",
CONFLICT_IGNORE,
songValues,
"id = ?",
arrayOf(cursor.getString(1))
)
}
}
}
it.query(SimpleSQLiteQuery("SELECT browseId, text, Info.id FROM Info JOIN SongWithAuthors ON Info.id = SongWithAuthors.authorInfoId WHERE browseId NOT NULL;")).use { cursor ->
val artistValues = ContentValues(2)
while (cursor.moveToNext()) {
artistValues.put("id", cursor.getString(0))
artistValues.put("name", cursor.getString(1))
it.insert("Artist", CONFLICT_IGNORE, artistValues)
it.query(SimpleSQLiteQuery("SELECT browseId, text, Info.id FROM Info JOIN SongWithAuthors ON Info.id = SongWithAuthors.authorInfoId WHERE browseId NOT NULL;"))
.use { cursor ->
val artistValues = ContentValues(2)
while (cursor.moveToNext()) {
artistValues.put("id", cursor.getString(0))
artistValues.put("name", cursor.getString(1))
it.insert("Artist", CONFLICT_IGNORE, artistValues)
it.execSQL("UPDATE SongWithAuthors SET authorInfoId = '${cursor.getString(0)}' WHERE authorInfoId = ${cursor.getLong(2)}")
it.execSQL(
"UPDATE SongWithAuthors SET authorInfoId = '${cursor.getString(0)}' WHERE authorInfoId = ${
cursor.getLong(
2
)
}"
)
}
}
}
it.execSQL("INSERT INTO SongArtistMap(songId, artistId) SELECT songId, authorInfoId FROM SongWithAuthors")
@@ -417,15 +473,16 @@ abstract class DatabaseInitializer protected constructor() : RoomDatabase() {
class From14To15Migration : Migration(14, 15) {
override fun migrate(it: SupportSQLiteDatabase) {
it.query(SimpleSQLiteQuery("SELECT id, loudnessDb, contentLength FROM Song;")).use { cursor ->
val formatValues = ContentValues(3)
while (cursor.moveToNext()) {
formatValues.put("songId", cursor.getString(0))
formatValues.put("loudnessDb", cursor.getFloatOrNull(1))
formatValues.put("contentLength", cursor.getFloatOrNull(2))
it.insert("Format", CONFLICT_IGNORE, formatValues)
it.query(SimpleSQLiteQuery("SELECT id, loudnessDb, contentLength FROM Song;"))
.use { cursor ->
val formatValues = ContentValues(3)
while (cursor.moveToNext()) {
formatValues.put("songId", cursor.getString(0))
formatValues.put("loudnessDb", cursor.getFloatOrNull(1))
formatValues.put("contentLength", cursor.getFloatOrNull(2))
it.insert("Format", CONFLICT_IGNORE, formatValues)
}
}
}
it.execSQL("CREATE TABLE IF NOT EXISTS `Song_new` (`id` TEXT NOT NULL, `title` TEXT NOT NULL, `artistsText` TEXT, `durationText` TEXT NOT NULL, `thumbnailUrl` TEXT, `lyrics` TEXT, `likedAt` INTEGER, `totalPlayTimeMs` INTEGER NOT NULL, PRIMARY KEY(`id`))")
@@ -445,7 +502,6 @@ object Converters {
val parcel = Parcel.obtain()
parcel.unmarshall(byteArray, 0, byteArray.size)
parcel.setDataPosition(0)
val bundle = parcel.readBundle(MediaItem::class.java.classLoader)
parcel.recycle()
@@ -459,7 +515,6 @@ object Converters {
return mediaItem?.toBundle()?.let { persistableBundle ->
val parcel = Parcel.obtain()
parcel.writeBundle(persistableBundle)
val bytes = parcel.marshall()
parcel.recycle()

View File

@@ -1,6 +1,10 @@
package it.vfsfitvnm.vimusic
import android.content.*
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.content.SharedPreferences
import android.net.Uri
import android.os.Bundle
import android.os.IBinder
@@ -11,14 +15,27 @@ import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.RepeatMode
import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.tween
import androidx.compose.foundation.*
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.LocalOverscrollConfiguration
import androidx.compose.foundation.background
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.ripple.LocalRippleTheme
import androidx.compose.material.ripple.RippleAlpha
import androidx.compose.material.ripple.RippleTheme
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.runtime.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.neverEqualPolicy
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
@@ -41,7 +58,13 @@ import it.vfsfitvnm.vimusic.ui.styling.Appearance
import it.vfsfitvnm.vimusic.ui.styling.Dimensions
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.ui.views.PlayerView
import it.vfsfitvnm.vimusic.utils.*
import it.vfsfitvnm.vimusic.utils.colorPaletteModeKey
import it.vfsfitvnm.vimusic.utils.getEnum
import it.vfsfitvnm.vimusic.utils.intent
import it.vfsfitvnm.vimusic.utils.listener
import it.vfsfitvnm.vimusic.utils.preferences
import it.vfsfitvnm.vimusic.utils.rememberHapticFeedback
import it.vfsfitvnm.vimusic.utils.thumbnailRoundnessKey
class MainActivity : ComponentActivity() {
private val serviceConnection = object : ServiceConnection {
@@ -73,7 +96,8 @@ class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val expandPlayerBottomSheet = intent?.extras?.getBoolean("expandPlayerBottomSheet", false) ?: false
val expandPlayerBottomSheet =
intent?.extras?.getBoolean("expandPlayerBottomSheet", false) ?: false
uri = intent?.data
@@ -245,4 +269,4 @@ fun ExpandPlayerOnPlaylistChange(player: Player, expand: () -> Unit) {
}
})
}
}
}

View File

@@ -9,7 +9,6 @@ import it.vfsfitvnm.vimusic.utils.coilDiskCacheMaxSizeKey
import it.vfsfitvnm.vimusic.utils.getEnum
import it.vfsfitvnm.vimusic.utils.preferences
class MainApplication : Application(), ImageLoaderFactory {
override fun onCreate() {
super.onCreate()
@@ -22,7 +21,12 @@ class MainApplication : Application(), ImageLoaderFactory {
.diskCache(
DiskCache.Builder()
.directory(filesDir.resolve("coil"))
.maxSizeBytes(preferences.getEnum(coilDiskCacheMaxSizeKey, CoilDiskCacheMaxSize.`128MB`).bytes)
.maxSizeBytes(
preferences.getEnum(
coilDiskCacheMaxSizeKey,
CoilDiskCacheMaxSize.`128MB`
).bytes
)
.build()
)
.build()

View File

@@ -1,6 +1,5 @@
package it.vfsfitvnm.vimusic.enums
enum class BuiltInPlaylist {
Favorites,
Cached

View File

@@ -1,6 +1,5 @@
package it.vfsfitvnm.vimusic.enums
enum class CoilDiskCacheMaxSize {
`128MB`,
`256MB`,

View File

@@ -15,7 +15,6 @@ import it.vfsfitvnm.vimusic.ui.styling.DarkColorPalette
import it.vfsfitvnm.vimusic.ui.styling.LightColorPalette
import it.vfsfitvnm.vimusic.ui.styling.Typography
enum class ColorPaletteMode {
Light,
Dark,

View File

@@ -1,6 +1,5 @@
package it.vfsfitvnm.vimusic.enums
enum class ExoPlayerDiskCacheMaxSize {
`512MB`,
`1GB`,

View File

@@ -14,4 +14,4 @@ data class Artist(
val radioVideoId: String? = null,
val radioPlaylistId: String? = null,
val timestamp: Long?
)
)

View File

@@ -4,7 +4,6 @@ import androidx.room.Embedded
import androidx.room.Junction
import androidx.room.Relation
open class DetailedSong(
@Embedded val song: Song,
@Relation(

View File

@@ -2,7 +2,6 @@ package it.vfsfitvnm.vimusic.models
import androidx.room.Relation
class DetailedSongWithContentLength(
song: Song,
albumId: String?,

View File

@@ -1,8 +1,9 @@
package it.vfsfitvnm.vimusic.models
import androidx.compose.runtime.Immutable
import androidx.room.*
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey
@Immutable
@Entity(

View File

@@ -1,7 +1,8 @@
package it.vfsfitvnm.vimusic.models
import androidx.room.*
import androidx.room.Embedded
import androidx.room.Junction
import androidx.room.Relation
data class PlaylistWithSongs(
@Embedded val playlist: Playlist,

View File

@@ -1,6 +1,5 @@
package it.vfsfitvnm.vimusic.models
import androidx.media3.common.MediaItem
import androidx.room.ColumnInfo
import androidx.room.Entity
@@ -11,4 +10,4 @@ class QueuedMediaItem(
@PrimaryKey(autoGenerate = true) val id: Long = 0,
@ColumnInfo(typeAffinity = ColumnInfo.BLOB) val mediaItem: MediaItem,
var position: Long?
)
)

View File

@@ -5,7 +5,6 @@ import androidx.room.Entity
import androidx.room.Index
import androidx.room.PrimaryKey
@Immutable
@Entity(
indices = [

View File

@@ -1,7 +1,7 @@
package it.vfsfitvnm.vimusic.models
import androidx.room.*
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity
data class Song(
@@ -20,7 +20,7 @@ data class Song(
val hours = seconds / 3600
return when {
return when {
hours == 0L -> "${seconds / 60}m"
hours < 24L -> "${hours}h"
else -> "${hours / 24}d"

View File

@@ -5,7 +5,6 @@ import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.ForeignKey
@Immutable
@Entity(
primaryKeys = ["songId", "albumId"],

View File

@@ -4,7 +4,6 @@ import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.ForeignKey
@Entity(
primaryKeys = ["songId", "artistId"],
foreignKeys = [
@@ -25,4 +24,4 @@ import androidx.room.ForeignKey
data class SongArtistMap(
@ColumnInfo(index = true) val songId: String,
@ColumnInfo(index = true) val artistId: String
)
)

View File

@@ -1,12 +1,9 @@
package it.vfsfitvnm.vimusic.models
import androidx.compose.runtime.Immutable
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.ForeignKey
@Immutable
@Entity(
primaryKeys = ["songId", "playlistId"],
foreignKeys = [

View File

@@ -6,7 +6,7 @@ import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable
import android.net.Uri
import androidx.core.graphics.applyCanvas
import coil.Coil
import coil.imageLoader
import coil.request.Disposable
import coil.request.ImageRequest
import it.vfsfitvnm.vimusic.utils.thumbnail
@@ -39,9 +39,10 @@ class BitmapProvider(
lastIsSystemInDarkMode = isSystemInDarkMode
defaultBitmap = Bitmap.createBitmap(bitmapSize, bitmapSize, Bitmap.Config.ARGB_8888).applyCanvas {
drawColor(colorProvider(isSystemInDarkMode))
}
defaultBitmap =
Bitmap.createBitmap(bitmapSize, bitmapSize, Bitmap.Config.ARGB_8888).applyCanvas {
drawColor(colorProvider(isSystemInDarkMode))
}
return lastBitmap == null
}
@@ -52,7 +53,7 @@ class BitmapProvider(
lastEnqueued?.dispose()
lastUri = uri
lastEnqueued = Coil.imageLoader(applicationContext).enqueue(
lastEnqueued = applicationContext.imageLoader.enqueue(
ImageRequest.Builder(applicationContext)
.data(uri.thumbnail(bitmapSize))
.listener(
@@ -68,4 +69,4 @@ class BitmapProvider(
.build()
)
}
}
}

View File

@@ -1,10 +1,15 @@
package it.vfsfitvnm.vimusic.service
import android.os.Binder as AndroidBinder
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.*
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.SharedPreferences
import android.content.res.Configuration
import android.graphics.Color
import android.media.MediaMetadata
@@ -21,19 +26,32 @@ import androidx.core.content.ContextCompat.startForegroundService
import androidx.core.content.edit
import androidx.core.content.getSystemService
import androidx.core.net.toUri
import androidx.media3.common.*
import androidx.media3.common.AudioAttributes
import androidx.media3.common.C
import androidx.media3.common.MediaItem
import androidx.media3.common.PlaybackException
import androidx.media3.common.Player
import androidx.media3.common.Timeline
import androidx.media3.database.StandaloneDatabaseProvider
import androidx.media3.datasource.DataSource
import androidx.media3.datasource.DefaultHttpDataSource
import androidx.media3.datasource.ResolvingDataSource
import androidx.media3.datasource.cache.*
import androidx.media3.datasource.cache.Cache
import androidx.media3.datasource.cache.CacheDataSource
import androidx.media3.datasource.cache.LeastRecentlyUsedCacheEvictor
import androidx.media3.datasource.cache.NoOpCacheEvictor
import androidx.media3.datasource.cache.SimpleCache
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.exoplayer.RenderersFactory
import androidx.media3.exoplayer.analytics.AnalyticsListener
import androidx.media3.exoplayer.analytics.PlaybackStats
import androidx.media3.exoplayer.analytics.PlaybackStatsListener
import androidx.media3.exoplayer.audio.*
import androidx.media3.exoplayer.audio.AudioRendererEventListener
import androidx.media3.exoplayer.audio.DefaultAudioSink
import androidx.media3.exoplayer.audio.DefaultAudioSink.DefaultAudioProcessorChain
import androidx.media3.exoplayer.audio.MediaCodecAudioRenderer
import androidx.media3.exoplayer.audio.SilenceSkippingAudioProcessor
import androidx.media3.exoplayer.audio.SonicAudioProcessor
import androidx.media3.exoplayer.mediacodec.MediaCodecSelector
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory
import androidx.media3.exoplayer.source.MediaSource
@@ -46,17 +64,40 @@ import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.enums.ExoPlayerDiskCacheMaxSize
import it.vfsfitvnm.vimusic.models.QueuedMediaItem
import it.vfsfitvnm.vimusic.query
import it.vfsfitvnm.vimusic.utils.*
import it.vfsfitvnm.vimusic.utils.InvincibleService
import it.vfsfitvnm.vimusic.utils.RingBuffer
import it.vfsfitvnm.vimusic.utils.TimerJob
import it.vfsfitvnm.vimusic.utils.YouTubeRadio
import it.vfsfitvnm.vimusic.utils.activityPendingIntent
import it.vfsfitvnm.vimusic.utils.broadCastPendingIntent
import it.vfsfitvnm.vimusic.utils.exoPlayerDiskCacheMaxSizeKey
import it.vfsfitvnm.vimusic.utils.findNextMediaItemById
import it.vfsfitvnm.vimusic.utils.forcePlayFromBeginning
import it.vfsfitvnm.vimusic.utils.getEnum
import it.vfsfitvnm.vimusic.utils.intent
import it.vfsfitvnm.vimusic.utils.isInvincibilityEnabledKey
import it.vfsfitvnm.vimusic.utils.mediaItems
import it.vfsfitvnm.vimusic.utils.persistentQueueKey
import it.vfsfitvnm.vimusic.utils.preferences
import it.vfsfitvnm.vimusic.utils.repeatModeKey
import it.vfsfitvnm.vimusic.utils.shouldBePlaying
import it.vfsfitvnm.vimusic.utils.skipSilenceKey
import it.vfsfitvnm.vimusic.utils.timer
import it.vfsfitvnm.vimusic.utils.volumeNormalizationKey
import it.vfsfitvnm.youtubemusic.YouTube
import it.vfsfitvnm.youtubemusic.models.NavigationEndpoint
import kotlinx.coroutines.*
import kotlin.math.roundToInt
import kotlin.system.exitProcess
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.cancellable
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlin.math.roundToInt
import kotlin.system.exitProcess
import android.os.Binder as AndroidBinder
import kotlinx.coroutines.launch
import kotlinx.coroutines.plus
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
@Suppress("DEPRECATION")
class PlayerService : InvincibleService(), Player.Listener, PlaybackStatsListener.Callback,

View File

@@ -1,15 +1,32 @@
package it.vfsfitvnm.vimusic.ui.components
import androidx.activity.compose.BackHandler
import androidx.compose.animation.core.*
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.AnimationSpec
import androidx.compose.animation.core.AnimationVector1D
import androidx.compose.animation.core.SpringSpec
import androidx.compose.animation.core.VectorConverter
import androidx.compose.animation.core.tween
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.DraggableState
import androidx.compose.foundation.gestures.detectVerticalDragGestures
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.runtime.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.geometry.Offset
@@ -24,10 +41,10 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.Velocity
import androidx.compose.ui.unit.dp
import kotlin.math.absoluteValue
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import kotlin.math.absoluteValue
@Composable
fun BottomSheet(
@@ -109,7 +126,7 @@ fun BottomSheet(
class BottomSheetState(
draggableState: DraggableState,
private val coroutineScope: CoroutineScope,
private val animatable: Animatable<Dp, AnimationVector1D>,
private val animatable: Animatable<Dp, AnimationVector1D>,
private val onWasExpandedChanged: (Boolean) -> Unit,
) : DraggableState by draggableState {
val lowerBound: Dp
@@ -220,7 +237,11 @@ class BottomSheetState(
}
@Composable
fun rememberBottomSheetState(lowerBound: Dp, upperBound: Dp, isExpanded: Boolean = false): BottomSheetState {
fun rememberBottomSheetState(
lowerBound: Dp,
upperBound: Dp,
isExpanded: Boolean = false
): BottomSheetState {
val density = LocalDensity.current
val coroutineScope = rememberCoroutineScope()

View File

@@ -5,7 +5,14 @@ import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicText
import androidx.compose.material.ripple.rememberRipple
@@ -20,7 +27,7 @@ import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import it.vfsfitvnm.vimusic.R
@Composable
fun ChunkyButton(
@@ -69,7 +76,7 @@ fun ChunkyButton(
style = textStyle
)
secondaryText?.let { secondaryText ->
secondaryText?.let { secondaryText ->
BasicText(
text = secondaryText,
style = secondaryTextStyle
@@ -88,7 +95,7 @@ fun ChunkyButton(
Image(
// TODO: this is themed...
painter = painterResource(it.vfsfitvnm.vimusic.R.drawable.ellipsis_vertical),
painter = painterResource(R.drawable.ellipsis_vertical),
contentDescription = null,
colorFilter = ColorFilter.tint(rippleColor.copy(alpha = 0.6f)),
modifier = Modifier

View File

@@ -13,7 +13,7 @@ import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
@Composable
fun <T>ChipGroup(
fun <T> ChipGroup(
items: List<ChipItem<T>>,
value: T,
selectedBackgroundColor: Color,
@@ -27,7 +27,7 @@ fun <T>ChipGroup(
Row(
horizontalArrangement = Arrangement.spacedBy(16.dp),
modifier = Modifier
.horizontalScroll(rememberScrollState().also { })
.horizontalScroll(rememberScrollState())
.then(modifier)
) {
items.forEach { chipItem ->
@@ -47,4 +47,4 @@ fun <T>ChipGroup(
data class ChipItem<T>(
val text: String,
val value: T
)
)

View File

@@ -1,12 +1,22 @@
package it.vfsfitvnm.vimusic.ui.components
import androidx.activity.compose.BackHandler
import androidx.compose.animation.*
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.compositionLocalOf
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.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerInput
@@ -74,4 +84,4 @@ fun BottomSheetMenu(
) {
state.content()
}
}
}

View File

@@ -2,23 +2,18 @@ package it.vfsfitvnm.vimusic.ui.components
import androidx.compose.animation.core.Animatable
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.graphics.StrokeJoin
import androidx.compose.ui.graphics.drawscope.DrawStyle
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.launch
@@ -32,9 +27,90 @@ fun MusicBars(
) {
val animatablesWithSteps = remember {
listOf(
Animatable(0f) to listOf(0.2f, 0.8f, 0.1f, 0.1f, 0.3f, 0.1f, 0.2f, 0.8f, 0.7f, 0.2f, 0.4f, 0.9f, 0.7f, 0.6f, 0.1f, 0.3f, 0.1f, 0.4f, 0.1f, 0.8f, 0.7f, 0.9f, 0.5f, 0.6f, 0.3f, 0.1f),
Animatable(0f) to listOf(0.2f, 0.5f, 1.0f, 0.5f, 0.3f, 0.1f, 0.2f, 0.3f, 0.5f, 0.1f, 0.6f, 0.5f, 0.3f, 0.7f, 0.8f, 0.9f, 0.3f, 0.1f, 0.5f, 0.3f, 0.6f, 1.0f, 0.6f, 0.7f, 0.4f, 0.1f),
Animatable(0f) to listOf(0.6f, 0.5f, 1.0f, 0.6f, 0.5f, 1.0f, 0.6f, 0.5f, 1.0f, 0.5f, 0.6f, 0.7f, 0.2f, 0.3f, 0.1f, 0.5f, 0.4f, 0.6f, 0.7f, 0.1f, 0.4f, 0.3f, 0.1f, 0.4f, 0.3f, 0.7f)
Animatable(0f) to listOf(
0.2f,
0.8f,
0.1f,
0.1f,
0.3f,
0.1f,
0.2f,
0.8f,
0.7f,
0.2f,
0.4f,
0.9f,
0.7f,
0.6f,
0.1f,
0.3f,
0.1f,
0.4f,
0.1f,
0.8f,
0.7f,
0.9f,
0.5f,
0.6f,
0.3f,
0.1f
),
Animatable(0f) to listOf(
0.2f,
0.5f,
1.0f,
0.5f,
0.3f,
0.1f,
0.2f,
0.3f,
0.5f,
0.1f,
0.6f,
0.5f,
0.3f,
0.7f,
0.8f,
0.9f,
0.3f,
0.1f,
0.5f,
0.3f,
0.6f,
1.0f,
0.6f,
0.7f,
0.4f,
0.1f
),
Animatable(0f) to listOf(
0.6f,
0.5f,
1.0f,
0.6f,
0.5f,
1.0f,
0.6f,
0.5f,
1.0f,
0.5f,
0.6f,
0.7f,
0.2f,
0.3f,
0.1f,
0.5f,
0.4f,
0.6f,
0.7f,
0.1f,
0.4f,
0.3f,
0.1f,
0.4f,
0.3f,
0.7f
)
)
}
@@ -70,4 +146,4 @@ fun MusicBars(
}
}
}
}
}

View File

@@ -4,9 +4,16 @@ import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.calculateTargetValue
import androidx.compose.animation.splineBasedDecay
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.detectVerticalDragGestures
import androidx.compose.foundation.gestures.detectHorizontalDragGestures
import androidx.compose.runtime.*
import androidx.compose.foundation.gestures.detectVerticalDragGestures
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clipToBounds
@@ -18,9 +25,8 @@ import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.center
import androidx.compose.ui.util.lerp
import kotlinx.coroutines.launch
import kotlin.math.absoluteValue
import kotlinx.coroutines.launch
@Composable
fun Pager(
@@ -59,7 +65,10 @@ fun Pager(
{},
{
val velocity = -velocityTracker.calculateVelocity().x
val initialTargetValue = splineBasedDecay<Float>(this).calculateTargetValue(state.value, velocity)
val initialTargetValue = splineBasedDecay<Float>(this).calculateTargetValue(
state.value,
velocity
)
velocityTracker.resetTracking()
@@ -129,7 +138,10 @@ fun Pager(
}
}
state.updateBounds(lowerBound = steps.first().toFloat(), upperBound = steps.last().toFloat())
state.updateBounds(
lowerBound = steps.first().toFloat(),
upperBound = steps.last().toFloat()
)
val layoutDimension = IntSize(
width = if (constraints.minWidth > 0 || placeables.isEmpty()) {

View File

@@ -3,7 +3,11 @@ package it.vfsfitvnm.vimusic.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectHorizontalDragGestures
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -16,7 +20,6 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import kotlin.math.roundToLong
@Composable
fun SeekBar(
value: Long,
@@ -89,8 +92,9 @@ fun SeekBar(
)
if (drawSteps) {
for (i in value + 1 .. maximumValue) {
val stepPosition = (i.toFloat() - minimumValue) / (maximumValue - minimumValue) * size.width
for (i in value + 1..maximumValue) {
val stepPosition =
(i.toFloat() - minimumValue) / (maximumValue - minimumValue) * size.width
drawCircle(
color = scrubberColor,
radius = scrubberRadius.toPx() / 2,

View File

@@ -20,4 +20,4 @@ inline fun TopAppBar(
.fillMaxWidth(),
content = content
)
}
}

View File

@@ -3,18 +3,36 @@ package it.vfsfitvnm.vimusic.ui.components.themed
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.*
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
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.fillMaxWidth
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.text.BasicTextField
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.runtime.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
@@ -35,7 +53,12 @@ import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import it.vfsfitvnm.vimusic.ui.components.ChunkyButton
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.utils.*
import it.vfsfitvnm.vimusic.utils.center
import it.vfsfitvnm.vimusic.utils.color
import it.vfsfitvnm.vimusic.utils.drawCircle
import it.vfsfitvnm.vimusic.utils.medium
import it.vfsfitvnm.vimusic.utils.secondary
import it.vfsfitvnm.vimusic.utils.semiBold
import kotlinx.coroutines.delay
@Composable
@@ -228,7 +251,6 @@ inline fun DefaultDialog(
}
}
@Composable
inline fun <T> ValueSelectorDialog(
noinline onDismiss: () -> Unit,

View File

@@ -3,7 +3,15 @@ package it.vfsfitvnm.vimusic.ui.components.themed
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicText
import androidx.compose.material.ripple.rememberRipple
@@ -18,7 +26,6 @@ import androidx.compose.ui.unit.sp
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.utils.medium
@Composable
fun DropDownSection(content: @Composable ColumnScope.() -> Unit) {
val (colorPalette) = LocalAppearance.current
@@ -96,4 +103,4 @@ fun DropDownTextItem(
vertical = 8.dp
)
)
}
}

View File

@@ -1,21 +1,35 @@
package it.vfsfitvnm.vimusic.ui.components.themed
import androidx.compose.animation.core.*
import androidx.compose.animation.core.LinearOutSlowInEasing
import androidx.compose.animation.core.MutableTransitionState
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.tween
import androidx.compose.animation.core.updateTransition
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.runtime.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
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.compose.ui.graphics.TransformOrigin
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.*
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntRect
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Popup
import androidx.compose.ui.window.PopupPositionProvider
import androidx.compose.ui.window.PopupProperties
import kotlin.math.max
import kotlin.math.min
@Composable
fun DropdownMenu(
isDisplayed: Boolean,

View File

@@ -38,4 +38,4 @@ fun LoadingOrError(
}
}
}
}
}

View File

@@ -6,11 +6,21 @@ import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.with
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicText
import androidx.compose.runtime.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
@@ -20,13 +30,16 @@ import androidx.compose.ui.unit.dp
import androidx.media3.common.MediaItem
import it.vfsfitvnm.route.RouteHandler
import it.vfsfitvnm.route.empty
import it.vfsfitvnm.vimusic.*
import it.vfsfitvnm.vimusic.Database
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.enums.PlaylistSortBy
import it.vfsfitvnm.vimusic.enums.SortOrder
import it.vfsfitvnm.vimusic.models.DetailedSong
import it.vfsfitvnm.vimusic.models.Playlist
import it.vfsfitvnm.vimusic.models.SongPlaylistMap
import it.vfsfitvnm.vimusic.query
import it.vfsfitvnm.vimusic.transaction
import it.vfsfitvnm.vimusic.ui.components.ChunkyButton
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
import it.vfsfitvnm.vimusic.ui.components.Pager
@@ -34,12 +47,17 @@ import it.vfsfitvnm.vimusic.ui.screens.rememberAlbumRoute
import it.vfsfitvnm.vimusic.ui.screens.rememberArtistRoute
import it.vfsfitvnm.vimusic.ui.screens.rememberCreatePlaylistRoute
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.utils.*
import it.vfsfitvnm.vimusic.utils.addNext
import it.vfsfitvnm.vimusic.utils.asMediaItem
import it.vfsfitvnm.vimusic.utils.color
import it.vfsfitvnm.vimusic.utils.enqueue
import it.vfsfitvnm.vimusic.utils.forcePlay
import it.vfsfitvnm.vimusic.utils.semiBold
import it.vfsfitvnm.vimusic.utils.shareAsYouTubeSong
import it.vfsfitvnm.youtubemusic.models.NavigationEndpoint
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.flowOf
@ExperimentalAnimationApi
@Composable
fun InFavoritesMediaItemMenu(
@@ -533,7 +551,13 @@ fun MediaItemMenu(
MenuEntry(
icon = R.drawable.alarm,
text = "Sleep timer",
secondaryText = sleepTimerMillisLeft?.let { "${DateUtils.formatElapsedTime(it / 1000)} left" },
secondaryText = sleepTimerMillisLeft?.let {
"${
DateUtils.formatElapsedTime(
it / 1000
)
} left"
},
onClick = {
isShowingSleepTimerDialog = true
}

View File

@@ -1,11 +1,22 @@
package it.vfsfitvnm.vimusic.ui.components.themed
import androidx.annotation.DrawableRes
import androidx.compose.foundation.*
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
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.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicText
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
@@ -20,7 +31,6 @@ import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.utils.medium
import it.vfsfitvnm.vimusic.utils.secondary
@Composable
inline fun Menu(
modifier: Modifier = Modifier,

View File

@@ -19,7 +19,6 @@ import androidx.compose.ui.unit.dp
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.utils.drawCircle
@Composable
fun Switch(
isChecked: Boolean,
@@ -49,4 +48,4 @@ fun Switch(
)
}
)
}
}

View File

@@ -22,7 +22,6 @@ import it.vfsfitvnm.vimusic.utils.align
import it.vfsfitvnm.vimusic.utils.secondary
import it.vfsfitvnm.vimusic.utils.semiBold
@Composable
fun TextCard(
modifier: Modifier = Modifier,
@@ -110,4 +109,4 @@ private object IconTextCardScopeImpl : TextCardScope {
.padding(horizontal = 16.dp)
)
}
}
}

View File

@@ -25,4 +25,4 @@ fun TextPlaceholder(
.fillMaxWidth(remember { 0.25f + Random.nextFloat() * 0.5f })
.height(16.dp)
)
}
}

View File

@@ -5,7 +5,19 @@ import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.PaddingValues
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.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
@@ -32,24 +44,40 @@ import it.vfsfitvnm.vimusic.Database
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.enums.ThumbnailRoundness
import it.vfsfitvnm.vimusic.models.*
import it.vfsfitvnm.vimusic.models.Album
import it.vfsfitvnm.vimusic.models.DetailedSong
import it.vfsfitvnm.vimusic.models.Playlist
import it.vfsfitvnm.vimusic.models.SongAlbumMap
import it.vfsfitvnm.vimusic.models.SongPlaylistMap
import it.vfsfitvnm.vimusic.query
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
import it.vfsfitvnm.vimusic.ui.components.TopAppBar
import it.vfsfitvnm.vimusic.ui.components.themed.*
import it.vfsfitvnm.vimusic.ui.components.themed.LoadingOrError
import it.vfsfitvnm.vimusic.ui.components.themed.Menu
import it.vfsfitvnm.vimusic.ui.components.themed.MenuEntry
import it.vfsfitvnm.vimusic.ui.components.themed.NonQueuedMediaItemMenu
import it.vfsfitvnm.vimusic.ui.components.themed.TextPlaceholder
import it.vfsfitvnm.vimusic.ui.styling.Dimensions
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.ui.styling.px
import it.vfsfitvnm.vimusic.ui.views.SongItem
import it.vfsfitvnm.vimusic.utils.*
import it.vfsfitvnm.vimusic.utils.asMediaItem
import it.vfsfitvnm.vimusic.utils.bold
import it.vfsfitvnm.vimusic.utils.center
import it.vfsfitvnm.vimusic.utils.enqueue
import it.vfsfitvnm.vimusic.utils.forcePlayAtIndex
import it.vfsfitvnm.vimusic.utils.forcePlayFromBeginning
import it.vfsfitvnm.vimusic.utils.secondary
import it.vfsfitvnm.vimusic.utils.semiBold
import it.vfsfitvnm.vimusic.utils.thumbnail
import it.vfsfitvnm.vimusic.utils.toMediaItem
import it.vfsfitvnm.youtubemusic.YouTube
import java.text.DateFormat
import java.util.Date
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.runBlocking
import java.text.DateFormat
import java.util.*
@ExperimentalAnimationApi
@Composable
@@ -143,27 +171,29 @@ fun AlbumScreen(
onClick = {
menuState.hide()
albumResult?.getOrNull()?.let { album ->
query {
val playlistId =
Database.insert(
Playlist(
name = album.title
?: "Unknown"
albumResult
?.getOrNull()
?.let { album ->
query {
val playlistId =
Database.insert(
Playlist(
name = album.title
?: "Unknown"
)
)
)
songs.forEachIndexed { index, song ->
Database.insert(
SongPlaylistMap(
songId = song.song.id,
playlistId = playlistId,
position = index
songs.forEachIndexed { index, song ->
Database.insert(
SongPlaylistMap(
songId = song.song.id,
playlistId = playlistId,
position = index
)
)
)
}
}
}
}
}
)
@@ -194,14 +224,20 @@ fun AlbumScreen(
icon = R.drawable.download,
text = "Refetch",
secondaryText = albumResult?.getOrNull()?.timestamp?.let { timestamp ->
"Last updated on ${DateFormat.getDateTimeInstance().format(Date(timestamp))}"
"Last updated on ${
DateFormat
.getDateTimeInstance()
.format(Date(timestamp))
}"
},
isEnabled = albumResult?.getOrNull() != null,
onClick = {
menuState.hide()
query {
albumResult?.getOrNull()?.let(Database::delete)
albumResult
?.getOrNull()
?.let(Database::delete)
runBlocking(Dispatchers.IO) {
fetchAlbum(browseId)
}
@@ -275,7 +311,9 @@ fun AlbumScreen(
.clickable {
binder?.stopRadio()
binder?.player?.forcePlayFromBeginning(
songs.shuffled().map(DetailedSong::asMediaItem)
songs
.shuffled()
.map(DetailedSong::asMediaItem)
)
}
.shadow(elevation = 2.dp, shape = CircleShape)
@@ -325,7 +363,10 @@ fun AlbumScreen(
durationText = song.song.durationText,
onClick = {
binder?.stopRadio()
binder?.player?.forcePlayAtIndex(songs.map(DetailedSong::asMediaItem), index)
binder?.player?.forcePlayAtIndex(
songs.map(DetailedSong::asMediaItem),
index
)
},
startContent = {
BasicText(
@@ -350,7 +391,6 @@ fun AlbumScreen(
}
}
@Composable
private fun LoadingOrError(
errorMessage: String? = null,
@@ -419,4 +459,4 @@ private suspend fun fetchAlbum(browseId: String): Result<Album>? {
}
}
}
}
}

View File

@@ -1,11 +1,18 @@
package it.vfsfitvnm.vimusic.ui.screens
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
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.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
@@ -37,9 +44,15 @@ import it.vfsfitvnm.vimusic.ui.components.themed.InHistoryMediaItemMenu
import it.vfsfitvnm.vimusic.ui.components.themed.LoadingOrError
import it.vfsfitvnm.vimusic.ui.components.themed.TextCard
import it.vfsfitvnm.vimusic.ui.components.themed.TextPlaceholder
import it.vfsfitvnm.vimusic.ui.styling.*
import it.vfsfitvnm.vimusic.ui.styling.Dimensions
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.ui.styling.px
import it.vfsfitvnm.vimusic.ui.views.SongItem
import it.vfsfitvnm.vimusic.utils.*
import it.vfsfitvnm.vimusic.utils.asMediaItem
import it.vfsfitvnm.vimusic.utils.forcePlayAtIndex
import it.vfsfitvnm.vimusic.utils.forcePlayFromBeginning
import it.vfsfitvnm.vimusic.utils.semiBold
import it.vfsfitvnm.vimusic.utils.thumbnail
import it.vfsfitvnm.youtubemusic.YouTube
import it.vfsfitvnm.youtubemusic.models.NavigationEndpoint
import kotlinx.coroutines.Dispatchers
@@ -48,8 +61,6 @@ import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.runBlocking
@OptIn(ExperimentalFoundationApi::class)
@ExperimentalAnimationApi
@Composable
fun ArtistScreen(
@@ -337,4 +348,4 @@ private suspend fun fetchArtist(browseId: String): Result<Artist>? {
timestamp = System.currentTimeMillis()
).also(Database::upsert)
}
}
}

View File

@@ -4,7 +4,14 @@ import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
@@ -36,11 +43,15 @@ import it.vfsfitvnm.vimusic.ui.styling.Dimensions
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.ui.styling.px
import it.vfsfitvnm.vimusic.ui.views.SongItem
import it.vfsfitvnm.vimusic.utils.*
import it.vfsfitvnm.vimusic.utils.asMediaItem
import it.vfsfitvnm.vimusic.utils.enqueue
import it.vfsfitvnm.vimusic.utils.forcePlayAtIndex
import it.vfsfitvnm.vimusic.utils.forcePlayFromBeginning
import it.vfsfitvnm.vimusic.utils.secondary
import it.vfsfitvnm.vimusic.utils.semiBold
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.map
@ExperimentalAnimationApi
@Composable
fun BuiltInPlaylistScreen(
@@ -221,7 +232,10 @@ fun BuiltInPlaylistScreen(
thumbnailSize = thumbnailSize,
onClick = {
binder?.stopRadio()
binder?.player?.forcePlayAtIndex(songs.map(DetailedSong::asMediaItem), index)
binder?.player?.forcePlayAtIndex(
songs.map(DetailedSong::asMediaItem),
index
)
},
menuContent = {
when (builtInPlaylist) {

View File

@@ -1,13 +1,24 @@
package it.vfsfitvnm.vimusic.ui.screens
import android.net.Uri
import androidx.compose.animation.*
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.animateContentSize
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
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.lazy.LazyColumn
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyHorizontalGrid
@@ -17,16 +28,29 @@ import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.text.BasicText
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.runtime.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.draw.drawWithCache
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.center
import androidx.compose.ui.graphics.*
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.ClipOp
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.Shadow
import androidx.compose.ui.graphics.asAndroidPath
import androidx.compose.ui.graphics.drawscope.clipPath
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextOverflow
@@ -37,23 +61,44 @@ import it.vfsfitvnm.route.RouteHandler
import it.vfsfitvnm.vimusic.Database
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.enums.*
import it.vfsfitvnm.vimusic.enums.BuiltInPlaylist
import it.vfsfitvnm.vimusic.enums.PlaylistSortBy
import it.vfsfitvnm.vimusic.enums.SongSortBy
import it.vfsfitvnm.vimusic.enums.SortOrder
import it.vfsfitvnm.vimusic.enums.ThumbnailRoundness
import it.vfsfitvnm.vimusic.models.DetailedSong
import it.vfsfitvnm.vimusic.models.Playlist
import it.vfsfitvnm.vimusic.models.SearchQuery
import it.vfsfitvnm.vimusic.query
import it.vfsfitvnm.vimusic.ui.components.TopAppBar
import it.vfsfitvnm.vimusic.ui.components.themed.*
import it.vfsfitvnm.vimusic.ui.components.themed.DropDownSection
import it.vfsfitvnm.vimusic.ui.components.themed.DropDownSectionSpacer
import it.vfsfitvnm.vimusic.ui.components.themed.DropDownTextItem
import it.vfsfitvnm.vimusic.ui.components.themed.DropdownMenu
import it.vfsfitvnm.vimusic.ui.components.themed.InHistoryMediaItemMenu
import it.vfsfitvnm.vimusic.ui.components.themed.TextFieldDialog
import it.vfsfitvnm.vimusic.ui.styling.Dimensions
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.ui.styling.px
import it.vfsfitvnm.vimusic.ui.views.BuiltInPlaylistItem
import it.vfsfitvnm.vimusic.ui.views.PlaylistPreviewItem
import it.vfsfitvnm.vimusic.ui.views.SongItem
import it.vfsfitvnm.vimusic.utils.*
import it.vfsfitvnm.vimusic.utils.asMediaItem
import it.vfsfitvnm.vimusic.utils.center
import it.vfsfitvnm.vimusic.utils.color
import it.vfsfitvnm.vimusic.utils.drawCircle
import it.vfsfitvnm.vimusic.utils.forcePlayAtIndex
import it.vfsfitvnm.vimusic.utils.forcePlayFromBeginning
import it.vfsfitvnm.vimusic.utils.isFirstLaunchKey
import it.vfsfitvnm.vimusic.utils.playlistGridExpandedKey
import it.vfsfitvnm.vimusic.utils.playlistSortByKey
import it.vfsfitvnm.vimusic.utils.playlistSortOrderKey
import it.vfsfitvnm.vimusic.utils.rememberPreference
import it.vfsfitvnm.vimusic.utils.semiBold
import it.vfsfitvnm.vimusic.utils.songSortByKey
import it.vfsfitvnm.vimusic.utils.songSortOrderKey
import kotlinx.coroutines.Dispatchers
@ExperimentalFoundationApi
@ExperimentalAnimationApi
@Composable
@@ -243,7 +288,10 @@ fun HomeScreen() {
if (colorPalette.isDark) {
return@drawWithCache onDrawBehind {
drawPath(path = decorationPath, color = colorPalette.primaryContainer)
drawPath(
path = decorationPath,
color = colorPalette.primaryContainer
)
}
}
@@ -253,7 +301,10 @@ fun HomeScreen() {
isAntiAlias = true
textSize = typography.l.fontSize.toPx()
color = colorPalette.text.toArgb()
typeface = ResourcesCompat.getFont(context, R.font.poppins_w500)
typeface = ResourcesCompat.getFont(
context,
R.font.poppins_w500
)
textAlign = android.graphics.Paint.Align.CENTER
}
@@ -273,7 +324,10 @@ fun HomeScreen() {
onDrawWithContent {
clipPath(textPath, ClipOp.Difference) {
drawPath(path = decorationPath, color = colorPalette.primaryContainer)
drawPath(
path = decorationPath,
color = colorPalette.primaryContainer
)
}
clipPath(decorationPath, ClipOp.Difference) {

View File

@@ -5,12 +5,21 @@ import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.PaddingValues
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.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
@@ -26,7 +35,11 @@ import it.vfsfitvnm.vimusic.models.SongPlaylistMap
import it.vfsfitvnm.vimusic.transaction
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
import it.vfsfitvnm.vimusic.ui.components.TopAppBar
import it.vfsfitvnm.vimusic.ui.components.themed.*
import it.vfsfitvnm.vimusic.ui.components.themed.LoadingOrError
import it.vfsfitvnm.vimusic.ui.components.themed.Menu
import it.vfsfitvnm.vimusic.ui.components.themed.MenuEntry
import it.vfsfitvnm.vimusic.ui.components.themed.TextCard
import it.vfsfitvnm.vimusic.ui.components.themed.TextFieldDialog
import it.vfsfitvnm.vimusic.ui.styling.Dimensions
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.ui.styling.px
@@ -38,7 +51,6 @@ import it.vfsfitvnm.youtubemusic.YouTube
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@ExperimentalAnimationApi
@Composable
fun IntentUriScreen(uri: Uri) {
@@ -224,18 +236,21 @@ fun IntentUriScreen(uri: Uri) {
thumbnailSizePx = thumbnailSizePx,
onClick = {
binder?.stopRadio()
binder?.player?.forcePlayAtIndex(items.map(YouTube.Item.Song::asMediaItem), index)
binder?.player?.forcePlayAtIndex(
items.map(YouTube.Item.Song::asMediaItem),
index
)
}
)
}
}
} ?: itemsResult?.exceptionOrNull()?.let { throwable ->
item {
LoadingOrError(
errorMessage = throwable.javaClass.canonicalName,
onRetry = onLoad
)
}
item {
LoadingOrError(
errorMessage = throwable.javaClass.canonicalName,
onRetry = onLoad
)
}
} ?: item {
LoadingOrError()
}

View File

@@ -4,14 +4,26 @@ import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.text.BasicText
import androidx.compose.runtime.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
@@ -23,23 +35,34 @@ import androidx.compose.ui.unit.dp
import it.vfsfitvnm.reordering.rememberReorderingState
import it.vfsfitvnm.reordering.verticalDragAfterLongPressToReorder
import it.vfsfitvnm.route.RouteHandler
import it.vfsfitvnm.vimusic.*
import it.vfsfitvnm.vimusic.Database
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.models.DetailedSong
import it.vfsfitvnm.vimusic.models.PlaylistWithSongs
import it.vfsfitvnm.vimusic.models.SongPlaylistMap
import it.vfsfitvnm.vimusic.query
import it.vfsfitvnm.vimusic.transaction
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
import it.vfsfitvnm.vimusic.ui.components.TopAppBar
import it.vfsfitvnm.vimusic.ui.components.themed.*
import it.vfsfitvnm.vimusic.ui.components.themed.ConfirmationDialog
import it.vfsfitvnm.vimusic.ui.components.themed.InPlaylistMediaItemMenu
import it.vfsfitvnm.vimusic.ui.components.themed.Menu
import it.vfsfitvnm.vimusic.ui.components.themed.MenuEntry
import it.vfsfitvnm.vimusic.ui.components.themed.TextFieldDialog
import it.vfsfitvnm.vimusic.ui.styling.Dimensions
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.ui.styling.px
import it.vfsfitvnm.vimusic.ui.views.SongItem
import it.vfsfitvnm.vimusic.utils.*
import it.vfsfitvnm.vimusic.utils.asMediaItem
import it.vfsfitvnm.vimusic.utils.enqueue
import it.vfsfitvnm.vimusic.utils.forcePlayAtIndex
import it.vfsfitvnm.vimusic.utils.forcePlayFromBeginning
import it.vfsfitvnm.vimusic.utils.secondary
import it.vfsfitvnm.vimusic.utils.semiBold
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.map
@ExperimentalAnimationApi
@Composable
fun LocalPlaylistScreen(
@@ -152,7 +175,11 @@ fun LocalPlaylistScreen(
isEnabled = playlistWithSongs.songs.isNotEmpty(),
onClick = {
menuState.hide()
binder?.player?.enqueue(playlistWithSongs.songs.map(DetailedSong::asMediaItem))
binder?.player?.enqueue(
playlistWithSongs.songs.map(
DetailedSong::asMediaItem
)
)
}
)
@@ -217,7 +244,13 @@ fun LocalPlaylistScreen(
modifier = Modifier
.clickable {
binder?.stopRadio()
binder?.player?.forcePlayFromBeginning(playlistWithSongs.songs.map(DetailedSong::asMediaItem).shuffled())
binder?.player?.forcePlayFromBeginning(
playlistWithSongs.songs
.map(
DetailedSong::asMediaItem
)
.shuffled()
)
}
.shadow(elevation = 2.dp, shape = CircleShape)
.background(
@@ -235,7 +268,11 @@ fun LocalPlaylistScreen(
modifier = Modifier
.clickable {
binder?.stopRadio()
binder?.player?.forcePlayFromBeginning(playlistWithSongs.songs.map(DetailedSong::asMediaItem))
binder?.player?.forcePlayFromBeginning(
playlistWithSongs.songs.map(
DetailedSong::asMediaItem
)
)
}
.shadow(elevation = 2.dp, shape = CircleShape)
.background(
@@ -259,7 +296,11 @@ fun LocalPlaylistScreen(
thumbnailSize = thumbnailSize,
onClick = {
binder?.stopRadio()
binder?.player?.forcePlayAtIndex(playlistWithSongs.songs.map(DetailedSong::asMediaItem), index)
binder?.player?.forcePlayAtIndex(
playlistWithSongs.songs.map(
DetailedSong::asMediaItem
), index
)
},
menuContent = {
InPlaylistMediaItemMenu(

View File

@@ -5,13 +5,30 @@ import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.PaddingValues
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.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.text.BasicText
import androidx.compose.runtime.*
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.draw.alpha
@@ -34,17 +51,28 @@ import it.vfsfitvnm.vimusic.models.SongPlaylistMap
import it.vfsfitvnm.vimusic.transaction
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
import it.vfsfitvnm.vimusic.ui.components.TopAppBar
import it.vfsfitvnm.vimusic.ui.components.themed.*
import it.vfsfitvnm.vimusic.ui.components.themed.LoadingOrError
import it.vfsfitvnm.vimusic.ui.components.themed.Menu
import it.vfsfitvnm.vimusic.ui.components.themed.MenuEntry
import it.vfsfitvnm.vimusic.ui.components.themed.NonQueuedMediaItemMenu
import it.vfsfitvnm.vimusic.ui.components.themed.TextPlaceholder
import it.vfsfitvnm.vimusic.ui.styling.Dimensions
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.ui.styling.px
import it.vfsfitvnm.vimusic.ui.views.SongItem
import it.vfsfitvnm.vimusic.utils.*
import it.vfsfitvnm.vimusic.utils.bold
import it.vfsfitvnm.vimusic.utils.center
import it.vfsfitvnm.vimusic.utils.enqueue
import it.vfsfitvnm.vimusic.utils.forcePlayAtIndex
import it.vfsfitvnm.vimusic.utils.forcePlayFromBeginning
import it.vfsfitvnm.vimusic.utils.relaunchableEffect
import it.vfsfitvnm.vimusic.utils.secondary
import it.vfsfitvnm.vimusic.utils.semiBold
import it.vfsfitvnm.vimusic.utils.toMediaItem
import it.vfsfitvnm.youtubemusic.YouTube
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@ExperimentalAnimationApi
@Composable
fun PlaylistScreen(
@@ -126,17 +154,22 @@ fun PlaylistScreen(
text = "Enqueue",
onClick = {
menuState.hide()
playlist?.getOrNull()?.let { album ->
album.items
?.mapNotNull { song ->
song.toMediaItem(browseId, album)
}
?.let { mediaItems ->
binder?.player?.enqueue(
mediaItems
)
}
}
playlist
?.getOrNull()
?.let { album ->
album.items
?.mapNotNull { song ->
song.toMediaItem(
browseId,
album
)
}
?.let { mediaItems ->
binder?.player?.enqueue(
mediaItems
)
}
}
}
)
@@ -146,33 +179,40 @@ fun PlaylistScreen(
onClick = {
menuState.hide()
playlist?.getOrNull()?.let { album ->
transaction {
val playlistId =
Database.insert(
Playlist(
name = album.title
?: "Unknown"
)
)
album.items?.forEachIndexed { index, song ->
song
.toMediaItem(browseId, album)
?.let { mediaItem ->
Database.insert(mediaItem)
Database.insert(
SongPlaylistMap(
songId = mediaItem.mediaId,
playlistId = playlistId,
position = index
)
playlist
?.getOrNull()
?.let { album ->
transaction {
val playlistId =
Database.insert(
Playlist(
name = album.title
?: "Unknown"
)
}
)
album.items?.forEachIndexed { index, song ->
song
.toMediaItem(
browseId,
album
)
?.let { mediaItem ->
Database.insert(
mediaItem
)
Database.insert(
SongPlaylistMap(
songId = mediaItem.mediaId,
playlistId = playlistId,
position = index
)
)
}
}
}
}
}
}
)

View File

@@ -9,7 +9,6 @@ import it.vfsfitvnm.route.Route0
import it.vfsfitvnm.route.Route1
import it.vfsfitvnm.vimusic.enums.BuiltInPlaylist
@Composable
fun rememberIntentUriRoute(): Route1<Uri?> {
val uri = rememberSaveable {
@@ -97,13 +96,6 @@ fun rememberSearchResultRoute(): Route1<String> {
}
}
@Composable
fun rememberLyricsRoute(): Route0 {
return remember {
Route0("LyricsRoute")
}
}
@Composable
fun rememberSettingsRoute(): Route0 {
return remember {

View File

@@ -5,7 +5,16 @@ import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
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.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
@@ -13,7 +22,13 @@ import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicText
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.runtime.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
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.draw.alpha
@@ -40,12 +55,20 @@ import it.vfsfitvnm.vimusic.ui.styling.Dimensions
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.ui.styling.px
import it.vfsfitvnm.vimusic.ui.views.SongItem
import it.vfsfitvnm.vimusic.utils.*
import it.vfsfitvnm.vimusic.utils.asMediaItem
import it.vfsfitvnm.vimusic.utils.center
import it.vfsfitvnm.vimusic.utils.color
import it.vfsfitvnm.vimusic.utils.forcePlay
import it.vfsfitvnm.vimusic.utils.medium
import it.vfsfitvnm.vimusic.utils.relaunchableEffect
import it.vfsfitvnm.vimusic.utils.rememberPreference
import it.vfsfitvnm.vimusic.utils.searchFilterKey
import it.vfsfitvnm.vimusic.utils.secondary
import it.vfsfitvnm.vimusic.utils.semiBold
import it.vfsfitvnm.youtubemusic.YouTube
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@ExperimentalAnimationApi
@Composable
fun SearchResultScreen(
@@ -88,9 +111,7 @@ fun SearchResultScreen(
val playlistRoute = rememberPlaylistRoute()
val artistRoute = rememberArtistRoute()
RouteHandler(
listenToGlobalEmitter = true
) {
RouteHandler(listenToGlobalEmitter = true) {
albumRoute { browseId ->
AlbumScreen(
browseId = browseId ?: "browseId cannot be null"
@@ -583,4 +604,4 @@ private fun LoadingOrError(
}
}
}
}
}

View File

@@ -5,17 +5,36 @@ import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.*
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
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.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.text.BasicText
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.runtime.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
@@ -46,7 +65,6 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.withContext
@ExperimentalAnimationApi
@Composable
fun SearchScreen(
@@ -189,7 +207,10 @@ fun SearchScreen(
textFieldValue = TextFieldValue()
}
.padding(horizontal = 14.dp, vertical = 6.dp)
.background(color = colorPalette.lightBackground, shape = CircleShape)
.background(
color = colorPalette.lightBackground,
shape = CircleShape
)
.size(28.dp)
) {
Image(

View File

@@ -26,7 +26,6 @@ import it.vfsfitvnm.vimusic.ui.screens.settings.*
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.utils.*
@ExperimentalAnimationApi
@Composable
fun SettingsScreen() {
@@ -271,7 +270,7 @@ inline fun <reified T : Enum<T>> EnumValueSelectorSettingsEntry(
title = title,
selectedValue = selectedValue,
values = enumValues<T>().toList(),
onValueSelected =onValueSelected,
onValueSelected = onValueSelected,
modifier = modifier,
valueText = valueText
)
@@ -313,7 +312,6 @@ inline fun <T> ValueSelectorSettingsEntry(
)
}
@Composable
fun SwitchSettingEntry(
title: String,

View File

@@ -1,10 +1,21 @@
package it.vfsfitvnm.vimusic.ui.screens.settings
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
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.text.BasicText
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -133,4 +144,4 @@ fun AboutScreen() {
}
}
}
}
}

View File

@@ -1,9 +1,18 @@
package it.vfsfitvnm.vimusic.ui.screens.settings
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
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.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.BasicText
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
@@ -16,9 +25,17 @@ import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.enums.ColorPaletteMode
import it.vfsfitvnm.vimusic.enums.ThumbnailRoundness
import it.vfsfitvnm.vimusic.ui.components.TopAppBar
import it.vfsfitvnm.vimusic.ui.screens.*
import it.vfsfitvnm.vimusic.ui.screens.AlbumScreen
import it.vfsfitvnm.vimusic.ui.screens.ArtistScreen
import it.vfsfitvnm.vimusic.ui.screens.EnumValueSelectorSettingsEntry
import it.vfsfitvnm.vimusic.ui.screens.SettingsEntryGroupText
import it.vfsfitvnm.vimusic.ui.screens.rememberAlbumRoute
import it.vfsfitvnm.vimusic.ui.screens.rememberArtistRoute
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.utils.*
import it.vfsfitvnm.vimusic.utils.colorPaletteModeKey
import it.vfsfitvnm.vimusic.utils.rememberPreference
import it.vfsfitvnm.vimusic.utils.semiBold
import it.vfsfitvnm.vimusic.utils.thumbnailRoundnessKey
@ExperimentalAnimationApi
@Composable
@@ -45,7 +62,10 @@ fun AppearanceSettingsScreen() {
val (colorPalette, typography) = LocalAppearance.current
var colorPaletteMode by rememberPreference(colorPaletteModeKey, ColorPaletteMode.System)
var thumbnailRoundness by rememberPreference(thumbnailRoundnessKey, ThumbnailRoundness.Light)
var thumbnailRoundness by rememberPreference(
thumbnailRoundnessKey,
ThumbnailRoundness.Light
)
Column(
modifier = Modifier

View File

@@ -4,10 +4,22 @@ import android.annotation.SuppressLint
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
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.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.text.BasicText
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -21,8 +33,12 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import it.vfsfitvnm.route.RouteHandler
import it.vfsfitvnm.vimusic.*
import it.vfsfitvnm.vimusic.Database
import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.checkpoint
import it.vfsfitvnm.vimusic.internal
import it.vfsfitvnm.vimusic.path
import it.vfsfitvnm.vimusic.query
import it.vfsfitvnm.vimusic.service.PlayerService
import it.vfsfitvnm.vimusic.ui.components.TopAppBar
import it.vfsfitvnm.vimusic.ui.components.themed.ConfirmationDialog
@@ -37,10 +53,9 @@ import it.vfsfitvnm.vimusic.utils.semiBold
import java.io.FileInputStream
import java.io.FileOutputStream
import java.text.SimpleDateFormat
import java.util.*
import java.util.Date
import kotlin.system.exitProcess
@ExperimentalAnimationApi
@Composable
fun BackupAndRestoreScreen() {
@@ -113,7 +128,11 @@ fun BackupAndRestoreScreen() {
},
onConfirm = {
restoreLauncher.launch(
arrayOf("application/x-sqlite3", "application/vnd.sqlite3", "application/octet-stream")
arrayOf(
"application/x-sqlite3",
"application/vnd.sqlite3",
"application/octet-stream"
)
)
},
confirmText = "Ok"
@@ -249,4 +268,4 @@ fun BackupAndRestoreScreen() {
}
}
}
}
}

View File

@@ -2,10 +2,25 @@ package it.vfsfitvnm.vimusic.ui.screens.settings
import android.text.format.Formatter
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
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.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.BasicText
import androidx.compose.runtime.*
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.platform.LocalContext
@@ -20,7 +35,14 @@ import it.vfsfitvnm.vimusic.enums.CoilDiskCacheMaxSize
import it.vfsfitvnm.vimusic.enums.ExoPlayerDiskCacheMaxSize
import it.vfsfitvnm.vimusic.ui.components.TopAppBar
import it.vfsfitvnm.vimusic.ui.components.themed.TextCard
import it.vfsfitvnm.vimusic.ui.screens.*
import it.vfsfitvnm.vimusic.ui.screens.AlbumScreen
import it.vfsfitvnm.vimusic.ui.screens.ArtistScreen
import it.vfsfitvnm.vimusic.ui.screens.DisabledSettingsEntry
import it.vfsfitvnm.vimusic.ui.screens.EnumValueSelectorSettingsEntry
import it.vfsfitvnm.vimusic.ui.screens.SettingsEntry
import it.vfsfitvnm.vimusic.ui.screens.SettingsEntryGroupText
import it.vfsfitvnm.vimusic.ui.screens.rememberAlbumRoute
import it.vfsfitvnm.vimusic.ui.screens.rememberArtistRoute
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.utils.coilDiskCacheMaxSizeKey
import it.vfsfitvnm.vimusic.utils.exoPlayerDiskCacheMaxSizeKey
@@ -56,8 +78,14 @@ fun CacheSettingsScreen() {
val (colorPalette, typography) = LocalAppearance.current
val binder = LocalPlayerServiceBinder.current
var coilDiskCacheMaxSize by rememberPreference(coilDiskCacheMaxSizeKey, CoilDiskCacheMaxSize.`128MB`)
var exoPlayerDiskCacheMaxSize by rememberPreference(exoPlayerDiskCacheMaxSizeKey, ExoPlayerDiskCacheMaxSize.`2GB`)
var coilDiskCacheMaxSize by rememberPreference(
coilDiskCacheMaxSizeKey,
CoilDiskCacheMaxSize.`128MB`
)
var exoPlayerDiskCacheMaxSize by rememberPreference(
exoPlayerDiskCacheMaxSizeKey,
ExoPlayerDiskCacheMaxSize.`2GB`
)
val coroutineScope = rememberCoroutineScope()
@@ -170,4 +198,4 @@ fun CacheSettingsScreen() {
}
}
}
}
}

View File

@@ -9,10 +9,23 @@ import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
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.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.BasicText
import androidx.compose.runtime.*
import androidx.compose.foundation.verticalScroll
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.compose.ui.graphics.ColorFilter
import androidx.compose.ui.platform.LocalContext
@@ -22,14 +35,19 @@ import it.vfsfitvnm.route.RouteHandler
import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.ui.components.TopAppBar
import it.vfsfitvnm.vimusic.ui.components.themed.TextCard
import it.vfsfitvnm.vimusic.ui.screens.*
import it.vfsfitvnm.vimusic.ui.screens.AlbumScreen
import it.vfsfitvnm.vimusic.ui.screens.ArtistScreen
import it.vfsfitvnm.vimusic.ui.screens.SettingsEntry
import it.vfsfitvnm.vimusic.ui.screens.SettingsEntryGroupText
import it.vfsfitvnm.vimusic.ui.screens.SwitchSettingEntry
import it.vfsfitvnm.vimusic.ui.screens.rememberAlbumRoute
import it.vfsfitvnm.vimusic.ui.screens.rememberArtistRoute
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.utils.isIgnoringBatteryOptimizations
import it.vfsfitvnm.vimusic.utils.isInvincibilityEnabledKey
import it.vfsfitvnm.vimusic.utils.rememberPreference
import it.vfsfitvnm.vimusic.utils.semiBold
@ExperimentalAnimationApi
@Composable
fun OtherSettingsScreen() {
@@ -164,5 +182,3 @@ fun OtherSettingsScreen() {
}
}
}

View File

@@ -6,9 +6,18 @@ import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
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.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.BasicText
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
@@ -21,10 +30,19 @@ import it.vfsfitvnm.route.RouteHandler
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.ui.components.TopAppBar
import it.vfsfitvnm.vimusic.ui.screens.*
import it.vfsfitvnm.vimusic.ui.screens.AlbumScreen
import it.vfsfitvnm.vimusic.ui.screens.ArtistScreen
import it.vfsfitvnm.vimusic.ui.screens.SettingsEntry
import it.vfsfitvnm.vimusic.ui.screens.SettingsEntryGroupText
import it.vfsfitvnm.vimusic.ui.screens.SwitchSettingEntry
import it.vfsfitvnm.vimusic.ui.screens.rememberAlbumRoute
import it.vfsfitvnm.vimusic.ui.screens.rememberArtistRoute
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.utils.*
import it.vfsfitvnm.vimusic.utils.persistentQueueKey
import it.vfsfitvnm.vimusic.utils.rememberPreference
import it.vfsfitvnm.vimusic.utils.semiBold
import it.vfsfitvnm.vimusic.utils.skipSilenceKey
import it.vfsfitvnm.vimusic.utils.volumeNormalizationKey
@ExperimentalAnimationApi
@Composable
@@ -128,20 +146,28 @@ fun PlayerSettingsScreen() {
title = "Equalizer",
text = "Interact with the system equalizer",
onClick = {
val intent = Intent(AudioEffect.ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL).apply {
putExtra(AudioEffect.EXTRA_AUDIO_SESSION, binder?.player?.audioSessionId)
putExtra(AudioEffect.EXTRA_PACKAGE_NAME, context.packageName)
putExtra(AudioEffect.EXTRA_CONTENT_TYPE, AudioEffect.CONTENT_TYPE_MUSIC)
}
val intent =
Intent(AudioEffect.ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL).apply {
putExtra(
AudioEffect.EXTRA_AUDIO_SESSION,
binder?.player?.audioSessionId
)
putExtra(AudioEffect.EXTRA_PACKAGE_NAME, context.packageName)
putExtra(
AudioEffect.EXTRA_CONTENT_TYPE,
AudioEffect.CONTENT_TYPE_MUSIC
)
}
if (intent.resolveActivity(context.packageManager) != null) {
activityResultLauncher.launch(intent)
} else {
Toast.makeText(context, "No equalizer app found!", Toast.LENGTH_SHORT).show()
Toast.makeText(context, "No equalizer app found!", Toast.LENGTH_SHORT)
.show()
}
}
)
}
}
}
}
}

View File

@@ -9,4 +9,4 @@ data class Appearance(
val thumbnailShape: Shape
)
val LocalAppearance = staticCompositionLocalOf<Appearance> { TODO() }
val LocalAppearance = staticCompositionLocalOf<Appearance> { TODO() }

View File

@@ -1,6 +1,6 @@
package it.vfsfitvnm.vimusic.ui.styling
import androidx.compose.runtime.*
import androidx.compose.runtime.Immutable
import androidx.compose.ui.graphics.Color
@Immutable

View File

@@ -6,7 +6,6 @@ import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
@Suppress("ClassName")
object Dimensions {
object thumbnails {
@@ -19,7 +18,7 @@ object Dimensions {
val songPreview = collapsedPlayer
val song: Dp
@Composable
get() = with (LocalConfiguration.current) {
get() = with(LocalConfiguration.current) {
minOf(screenHeightDp, screenWidthDp)
}.dp
}
@@ -30,4 +29,4 @@ object Dimensions {
inline val Dp.px: Int
@Composable
inline get() = with (LocalDensity.current) { roundToPx() }
inline get() = with(LocalDensity.current) { roundToPx() }

View File

@@ -1,19 +1,7 @@
package it.vfsfitvnm.vimusic.ui.styling
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.remember
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.ExperimentalTextApi
import androidx.compose.ui.text.PlatformTextStyle
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
import it.vfsfitvnm.vimusic.R
@Immutable
data class Typography(

View File

@@ -9,24 +9,56 @@ import android.text.format.Formatter
import android.widget.Toast
import androidx.activity.compose.LocalActivityResultRegistryOwner
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.*
import androidx.compose.foundation.*
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedContentScope
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.SizeTransform
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.animation.with
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
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.Spacer
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxHeight
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.width
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.material.ripple.rememberRipple
import androidx.compose.runtime.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.*
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.input.pointer.pointerInput
@@ -51,19 +83,44 @@ import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.enums.ThumbnailRoundness
import it.vfsfitvnm.vimusic.models.Song
import it.vfsfitvnm.vimusic.query
import it.vfsfitvnm.vimusic.ui.components.*
import it.vfsfitvnm.vimusic.ui.components.themed.*
import it.vfsfitvnm.vimusic.ui.styling.*
import it.vfsfitvnm.vimusic.utils.*
import it.vfsfitvnm.vimusic.ui.components.BottomSheet
import it.vfsfitvnm.vimusic.ui.components.BottomSheetState
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
import it.vfsfitvnm.vimusic.ui.components.SeekBar
import it.vfsfitvnm.vimusic.ui.components.rememberBottomSheetState
import it.vfsfitvnm.vimusic.ui.components.themed.BaseMediaItemMenu
import it.vfsfitvnm.vimusic.ui.components.themed.LoadingOrError
import it.vfsfitvnm.vimusic.ui.components.themed.TextFieldDialog
import it.vfsfitvnm.vimusic.ui.components.themed.TextPlaceholder
import it.vfsfitvnm.vimusic.ui.styling.BlackColorPalette
import it.vfsfitvnm.vimusic.ui.styling.DarkColorPalette
import it.vfsfitvnm.vimusic.ui.styling.Dimensions
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.ui.styling.px
import it.vfsfitvnm.vimusic.utils.bold
import it.vfsfitvnm.vimusic.utils.center
import it.vfsfitvnm.vimusic.utils.color
import it.vfsfitvnm.vimusic.utils.medium
import it.vfsfitvnm.vimusic.utils.rememberError
import it.vfsfitvnm.vimusic.utils.rememberMediaItem
import it.vfsfitvnm.vimusic.utils.rememberMediaItemIndex
import it.vfsfitvnm.vimusic.utils.rememberPositionAndDuration
import it.vfsfitvnm.vimusic.utils.rememberRepeatMode
import it.vfsfitvnm.vimusic.utils.rememberShouldBePlaying
import it.vfsfitvnm.vimusic.utils.rememberVolume
import it.vfsfitvnm.vimusic.utils.seamlessPlay
import it.vfsfitvnm.vimusic.utils.secondary
import it.vfsfitvnm.vimusic.utils.semiBold
import it.vfsfitvnm.vimusic.utils.thumbnail
import it.vfsfitvnm.vimusic.utils.verticalFadingEdge
import it.vfsfitvnm.youtubemusic.YouTube
import it.vfsfitvnm.youtubemusic.models.NavigationEndpoint
import kotlin.math.absoluteValue
import kotlin.math.roundToInt
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.runBlocking
import kotlin.math.absoluteValue
import kotlin.math.roundToInt
@ExperimentalAnimationApi
@Composable
@@ -98,7 +155,8 @@ fun PlayerView(
.background(colorPalette.elevatedBackground)
.fillMaxSize()
.drawBehind {
val progress = positionAndDuration.first.toFloat() / positionAndDuration.second.absoluteValue
val progress =
positionAndDuration.first.toFloat() / positionAndDuration.second.absoluteValue
val offset = Dimensions.thumbnails.player.songPreview.toPx()
drawLine(

View File

@@ -4,7 +4,12 @@ import androidx.annotation.DrawableRes
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.text.BasicText
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
@@ -33,7 +38,6 @@ import it.vfsfitvnm.vimusic.utils.thumbnail
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.distinctUntilChanged
@Composable
fun PlaylistPreviewItem(
playlistPreview: PlaylistPreview,
@@ -168,4 +172,4 @@ fun PlaylistItem(
.padding(horizontal = 8.dp, vertical = 4.dp)
)
}
}
}

View File

@@ -5,7 +5,15 @@ import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
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.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.text.BasicText
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.runtime.Composable
@@ -31,7 +39,6 @@ import it.vfsfitvnm.vimusic.utils.secondary
import it.vfsfitvnm.vimusic.utils.semiBold
import it.vfsfitvnm.vimusic.utils.thumbnail
@ExperimentalAnimationApi
@Composable
@NonRestartableComposable
@@ -89,7 +96,6 @@ fun SongItem(
)
}
@ExperimentalAnimationApi
@Composable
@NonRestartableComposable
@@ -199,4 +205,4 @@ fun SongItem(
trailingContent?.invoke()
}
}
}

View File

@@ -9,22 +9,26 @@ import android.os.Build
import android.os.PowerManager
import androidx.core.content.getSystemService
inline fun <reified T> Context.intent(): Intent =
Intent(this@Context, T::class.java)
inline fun <reified T: BroadcastReceiver> Context.broadCastPendingIntent(
inline fun <reified T : BroadcastReceiver> Context.broadCastPendingIntent(
requestCode: Int = 0,
flags: Int = if (Build.VERSION.SDK_INT >= 23) PendingIntent.FLAG_IMMUTABLE else 0,
): PendingIntent =
PendingIntent.getBroadcast(this, requestCode, intent<T>(), flags)
inline fun <reified T: Activity> Context.activityPendingIntent(
inline fun <reified T : Activity> Context.activityPendingIntent(
requestCode: Int = 0,
flags: Int = 0,
block: Intent.() -> Unit = {},
): PendingIntent =
PendingIntent.getActivity(this, requestCode, intent<T>().apply(block), (if (Build.VERSION.SDK_INT >= 23) PendingIntent.FLAG_IMMUTABLE else 0) or flags)
PendingIntent.getActivity(
this,
requestCode,
intent<T>().apply(block),
(if (Build.VERSION.SDK_INT >= 23) PendingIntent.FLAG_IMMUTABLE else 0) or flags
)
val Context.isIgnoringBatteryOptimizations: Boolean
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

View File

@@ -24,6 +24,11 @@ fun DrawScope.drawCircle(
it.colorFilter = colorFilter
it.style = style
}.asFrameworkPaint().also {
it.setShadowLayer(shadow.blurRadius, shadow.offset.x, shadow.offset.y, shadow.color.toArgb())
it.setShadowLayer(
shadow.blurRadius,
shadow.offset.x,
shadow.offset.y,
shadow.color.toArgb()
)
}
)
)

View File

@@ -11,7 +11,6 @@ import android.os.Build
import android.os.Handler
import android.os.Looper
// https://stackoverflow.com/q/53502244/16885569
// I found four ways to make the system not kill the stopped foreground service: e.g. when
// the player is paused:

View File

@@ -4,15 +4,14 @@ import androidx.media3.common.MediaItem
import androidx.media3.common.Player
import androidx.media3.common.Timeline
val Timeline.mediaItems: List<MediaItem>
get() = (0 until windowCount).map { index ->
getWindow(index, Timeline.Window()).mediaItem
get() = List(windowCount) {
getWindow(it, Timeline.Window()).mediaItem
}
val Timeline.windows: List<Timeline.Window>
get() = (0 until windowCount).map { index ->
getWindow(index, Timeline.Window())
inline val Timeline.windows: List<Timeline.Window>
get() = List(windowCount) {
getWindow(it, Timeline.Window())
}
val Player.shouldBePlaying: Boolean

View File

@@ -1,6 +1,13 @@
package it.vfsfitvnm.vimusic.utils
import androidx.compose.runtime.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.DisposableEffectResult
import androidx.compose.runtime.DisposableEffectScope
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
import androidx.media3.common.MediaItem
import androidx.media3.common.PlaybackException
import androidx.media3.common.Player
@@ -9,7 +16,6 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
context(DisposableEffectScope)
fun Player.listener(listener: Player.Listener): DisposableEffectResult {
addListener(listener)
@@ -27,11 +33,13 @@ fun rememberMediaItemIndex(player: Player): State<Int> {
DisposableEffect(player) {
player.listener(object : Player.Listener {
override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
mediaItemIndexState.value = if (player.mediaItemCount == 0) -1 else player.currentMediaItemIndex
mediaItemIndexState.value =
if (player.mediaItemCount == 0) -1 else player.currentMediaItemIndex
}
override fun onTimelineChanged(timeline: Timeline, reason: Int) {
mediaItemIndexState.value = if (player.mediaItemCount == 0) -1 else player.currentMediaItemIndex
mediaItemIndexState.value =
if (player.mediaItemCount == 0) -1 else player.currentMediaItemIndex
}
})
}
@@ -187,6 +195,7 @@ fun rememberError(player: Player): State<PlaybackException?> {
override fun onPlaybackStateChanged(playbackState: Int) {
errorState.value = player.playerError
}
override fun onPlayerError(playbackException: PlaybackException) {
errorState.value = playbackException
}
@@ -194,4 +203,4 @@ fun rememberError(player: Player): State<PlaybackException?> {
}
return errorState
}
}

View File

@@ -2,11 +2,14 @@ package it.vfsfitvnm.vimusic.utils
import android.content.Context
import android.content.SharedPreferences
import androidx.compose.runtime.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.SnapshotMutationPolicy
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.core.content.edit
const val colorPaletteModeKey = "colorPaletteMode"
const val thumbnailRoundnessKey = "thumbnailRoundness"
const val coilDiskCacheMaxSizeKey = "coilDiskCacheMaxSize"
@@ -36,14 +39,15 @@ inline fun <reified T : Enum<T>> SharedPreferences.getEnum(
}
} ?: defaultValue
inline fun <reified T : Enum<T>> SharedPreferences.Editor.putEnum(key: String, value: T) =
inline fun <reified T : Enum<T>> SharedPreferences.Editor.putEnum(
key: String,
value: T
): SharedPreferences.Editor =
putString(key, value.name)
val Context.preferences: SharedPreferences
get() = getSharedPreferences("preferences", Context.MODE_PRIVATE)
@Composable
fun rememberPreference(key: String, defaultValue: Boolean): MutableState<Boolean> {
val context = LocalContext.current
@@ -65,7 +69,7 @@ fun rememberPreference(key: String, defaultValue: String): MutableState<String>
}
@Composable
inline fun <reified T: Enum<T>> rememberPreference(key: String, defaultValue: T): MutableState<T> {
inline fun <reified T : Enum<T>> rememberPreference(key: String, defaultValue: T): MutableState<T> {
val context = LocalContext.current
return remember {
mutableStatePreferenceOf(context.preferences.getEnum(key, defaultValue)) {
@@ -74,7 +78,10 @@ inline fun <reified T: Enum<T>> rememberPreference(key: String, defaultValue: T)
}
}
inline fun <T> mutableStatePreferenceOf(value: T, crossinline onStructuralInequality: (newValue: T) -> Unit) =
inline fun <T> mutableStatePreferenceOf(
value: T,
crossinline onStructuralInequality: (newValue: T) -> Unit
) =
mutableStateOf(
value = value,
policy = object : SnapshotMutationPolicy<T> {

View File

@@ -1,10 +1,14 @@
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
@file:OptIn(InternalComposeApi::class)
package it.vfsfitvnm.vimusic.utils
import androidx.compose.runtime.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.InternalComposeApi
import androidx.compose.runtime.LaunchedEffectImpl
import androidx.compose.runtime.NonRestartableComposable
import androidx.compose.runtime.currentComposer
import androidx.compose.runtime.remember
import kotlinx.coroutines.CoroutineScope
@Composable

View File

@@ -8,7 +8,6 @@ import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
interface TimerJob {
val millisLeft: StateFlow<Long?>
fun cancel()
@@ -16,14 +15,12 @@ interface TimerJob {
fun CoroutineScope.timer(delayMillis: Long, onCompletion: () -> Unit): TimerJob {
val millisLeft = MutableStateFlow<Long?>(delayMillis)
val job = launch {
while (isActive && millisLeft.value != null) {
delay(1000)
millisLeft.emit(millisLeft.value?.minus(1000)?.takeIf { it > 0 })
}
}
val disposableHandle = job.invokeOnCompletion {
if (it == null) {
onCompletion()

View File

@@ -5,7 +5,6 @@ import it.vfsfitvnm.youtubemusic.YouTube
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
data class YouTubeRadio(
private val videoId: String? = null,
private val playlistId: String? = null,

View File

@@ -21,4 +21,4 @@ fun rememberHapticFeedback(): HapticFeedback {
}
}
}
}
}