Tweak code
This commit is contained in:
@@ -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()
|
||||
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package it.vfsfitvnm.vimusic.enums
|
||||
|
||||
|
||||
enum class BuiltInPlaylist {
|
||||
Favorites,
|
||||
Cached
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package it.vfsfitvnm.vimusic.enums
|
||||
|
||||
|
||||
enum class CoilDiskCacheMaxSize {
|
||||
`128MB`,
|
||||
`256MB`,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package it.vfsfitvnm.vimusic.enums
|
||||
|
||||
|
||||
enum class ExoPlayerDiskCacheMaxSize {
|
||||
`512MB`,
|
||||
`1GB`,
|
||||
|
||||
@@ -14,4 +14,4 @@ data class Artist(
|
||||
val radioVideoId: String? = null,
|
||||
val radioPlaylistId: String? = null,
|
||||
val timestamp: Long?
|
||||
)
|
||||
)
|
||||
|
||||
@@ -4,7 +4,6 @@ import androidx.room.Embedded
|
||||
import androidx.room.Junction
|
||||
import androidx.room.Relation
|
||||
|
||||
|
||||
open class DetailedSong(
|
||||
@Embedded val song: Song,
|
||||
@Relation(
|
||||
|
||||
@@ -2,7 +2,6 @@ package it.vfsfitvnm.vimusic.models
|
||||
|
||||
import androidx.room.Relation
|
||||
|
||||
|
||||
class DetailedSongWithContentLength(
|
||||
song: Song,
|
||||
albumId: String?,
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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?
|
||||
)
|
||||
)
|
||||
|
||||
@@ -5,7 +5,6 @@ import androidx.room.Entity
|
||||
import androidx.room.Index
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
|
||||
@Immutable
|
||||
@Entity(
|
||||
indices = [
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -5,7 +5,6 @@ import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
|
||||
|
||||
@Immutable
|
||||
@Entity(
|
||||
primaryKeys = ["songId", "albumId"],
|
||||
|
||||
@@ -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
|
||||
)
|
||||
)
|
||||
|
||||
@@ -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 = [
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
)
|
||||
)
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -20,4 +20,4 @@ inline fun TopAppBar(
|
||||
.fillMaxWidth(),
|
||||
content = content
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -38,4 +38,4 @@ fun LoadingOrError(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,4 +25,4 @@ fun TextPlaceholder(
|
||||
.fillMaxWidth(remember { 0.25f + Random.nextFloat() * 0.5f })
|
||||
.height(16.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>? {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -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 {
|
||||
@@ -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(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,4 +9,4 @@ data class Appearance(
|
||||
val thumbnailShape: Shape
|
||||
)
|
||||
|
||||
val LocalAppearance = staticCompositionLocalOf<Appearance> { TODO() }
|
||||
val LocalAppearance = staticCompositionLocalOf<Appearance> { TODO() }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() }
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -21,4 +21,4 @@ fun rememberHapticFeedback(): HapticFeedback {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user