From 6742c885be4f0cf44c50b25876692d1d395dcb7b Mon Sep 17 00:00:00 2001 From: AbdallahMehiz Date: Tue, 20 Aug 2024 08:00:06 +0100 Subject: [PATCH] feat: playbackSpeed sheet * per video playback speed * show number of items inside directories in file picker --- .../live/mehiz/mpvkt/database/Migrations.kt | 9 +- .../mehiz/mpvkt/database/MpvKtDatabase.kt | 2 +- .../database/entities/PlaybackStateEntity.kt | 1 + .../presentation/components/SliderItem.kt | 15 ++-- .../mehiz/mpvkt/ui/home/FilePickerScreen.kt | 16 +++- .../mehiz/mpvkt/ui/player/PlayerActivity.kt | 18 ++-- .../live/mehiz/mpvkt/ui/player/PlayerEnums.kt | 1 + .../mehiz/mpvkt/ui/player/PlayerViewModel.kt | 1 + .../controls/BottomLeftPlayerControls.kt | 11 +-- .../mpvkt/ui/player/controls/PlayerSheets.kt | 5 ++ .../components/sheets/ChaptersSheet.kt | 8 +- .../components/sheets/PlaybackSpeedSheet.kt | 90 +++++++++++++++++++ app/src/main/res/values/strings.xml | 3 + 13 files changed, 151 insertions(+), 29 deletions(-) create mode 100644 app/src/main/java/live/mehiz/mpvkt/ui/player/controls/components/sheets/PlaybackSpeedSheet.kt diff --git a/app/src/main/java/live/mehiz/mpvkt/database/Migrations.kt b/app/src/main/java/live/mehiz/mpvkt/database/Migrations.kt index d94ce4b..f2c6e1e 100644 --- a/app/src/main/java/live/mehiz/mpvkt/database/Migrations.kt +++ b/app/src/main/java/live/mehiz/mpvkt/database/Migrations.kt @@ -4,7 +4,8 @@ import androidx.room.migration.Migration import androidx.sqlite.db.SupportSQLiteDatabase val Migrations: Array = arrayOf( - MIGRATION1to2 + MIGRATION1to2, + MIGRATION2to3, ) private object MIGRATION1to2 : Migration(1, 2) { @@ -15,3 +16,9 @@ private object MIGRATION1to2 : Migration(1, 2) { db.execSQL("ALTER TABLE PlaybackStateEntity ADD COLUMN subSpeed REAL NOT NULL DEFAULT 0") } } + +private object MIGRATION2to3 : Migration(2, 3) { + override fun migrate(db: SupportSQLiteDatabase) { + db.execSQL("ALTER TABLE PlaybackStateEntity ADD COLUMN playbackSpeed REAL NOT NULL DEFAULT 0") + } +} diff --git a/app/src/main/java/live/mehiz/mpvkt/database/MpvKtDatabase.kt b/app/src/main/java/live/mehiz/mpvkt/database/MpvKtDatabase.kt index 151ff64..e04ecf0 100644 --- a/app/src/main/java/live/mehiz/mpvkt/database/MpvKtDatabase.kt +++ b/app/src/main/java/live/mehiz/mpvkt/database/MpvKtDatabase.kt @@ -5,7 +5,7 @@ import androidx.room.RoomDatabase import live.mehiz.mpvkt.database.dao.PlaybackStateDao import live.mehiz.mpvkt.database.entities.PlaybackStateEntity -@Database(entities = [PlaybackStateEntity::class], version = 2) +@Database(entities = [PlaybackStateEntity::class], version = 3) abstract class MpvKtDatabase : RoomDatabase() { abstract fun videoDataDao(): PlaybackStateDao } diff --git a/app/src/main/java/live/mehiz/mpvkt/database/entities/PlaybackStateEntity.kt b/app/src/main/java/live/mehiz/mpvkt/database/entities/PlaybackStateEntity.kt index 4d5e847..b19bc97 100644 --- a/app/src/main/java/live/mehiz/mpvkt/database/entities/PlaybackStateEntity.kt +++ b/app/src/main/java/live/mehiz/mpvkt/database/entities/PlaybackStateEntity.kt @@ -7,6 +7,7 @@ import androidx.room.PrimaryKey data class PlaybackStateEntity( @PrimaryKey val mediaTitle: String, val lastPosition: Int, // in seconds + val playbackSpeed: Double, val sid: Int, val subDelay: Int, val subSpeed: Double, diff --git a/app/src/main/java/live/mehiz/mpvkt/presentation/components/SliderItem.kt b/app/src/main/java/live/mehiz/mpvkt/presentation/components/SliderItem.kt index 782e052..86235bf 100644 --- a/app/src/main/java/live/mehiz/mpvkt/presentation/components/SliderItem.kt +++ b/app/src/main/java/live/mehiz/mpvkt/presentation/components/SliderItem.kt @@ -22,6 +22,7 @@ import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.dp +import live.mehiz.mpvkt.ui.theme.spacing @Composable fun SliderItem( @@ -78,8 +79,8 @@ fun SliderItem( valueText: String, onChange: (Float) -> Unit, max: Float, - steps: Int, modifier: Modifier = Modifier, + steps: Int = 0, min: Float = 0f, icon: @Composable () -> Unit = {}, ) { @@ -89,11 +90,11 @@ fun SliderItem( modifier = modifier .fillMaxWidth() .padding( - horizontal = 16.dp, - vertical = 8.dp, + horizontal = MaterialTheme.spacing.medium, + vertical = MaterialTheme.spacing.smaller, ), verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(24.dp), + horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.large), ) { icon() Column(modifier = Modifier.weight(0.5f)) { @@ -137,10 +138,10 @@ fun VerticalSliderItem( modifier = modifier .fillMaxHeight() .padding( - horizontal = 16.dp, - vertical = 8.dp, + horizontal = MaterialTheme.spacing.medium, + vertical = MaterialTheme.spacing.smaller, ), - verticalArrangement = Arrangement.spacedBy(24.dp), + verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.larger), horizontalAlignment = Alignment.CenterHorizontally, ) { icon() diff --git a/app/src/main/java/live/mehiz/mpvkt/ui/home/FilePickerScreen.kt b/app/src/main/java/live/mehiz/mpvkt/ui/home/FilePickerScreen.kt index 2bfbefd..f86abf3 100644 --- a/app/src/main/java/live/mehiz/mpvkt/ui/home/FilePickerScreen.kt +++ b/app/src/main/java/live/mehiz/mpvkt/ui/home/FilePickerScreen.kt @@ -129,7 +129,7 @@ data class FilePickerScreen(val uri: String) : Screen() { MaterialTheme.colorScheme.surfaceContainerHigh }, ), - items = if(fileManager.isDirectory(file)) fileManager.listFiles(file).size else null, + items = if (fileManager.isDirectory(file)) fileManager.listFiles(file).size else null, onClick = { onNavigate(file) }, ) } @@ -142,9 +142,9 @@ data class FilePickerScreen(val uri: String) : Screen() { isDirectory: Boolean, lastModified: Long?, length: Long?, - items: Int? = null, onClick: () -> Unit, modifier: Modifier = Modifier, + items: Int? = null, ) { var size: String? by remember { mutableStateOf(null) } var time: String? by remember { mutableStateOf(null) } @@ -187,9 +187,17 @@ data class FilePickerScreen(val uri: String) : Screen() { color = MaterialTheme.colorScheme.onSurfaceVariant, style = MaterialTheme.typography.bodyMedium, ) - if ((size != null && !isDirectory) || (items != null && isDirectory)) { + if (size != null || items != null) { Text( - text = if(isDirectory) pluralStringResource(id = R.plurals.plural_items, count = items!!, items) else size!!, + text = if (isDirectory) { + pluralStringResource( + id = R.plurals.plural_items, + count = items!!, + items + ) + } else { + size!! + }, color = MaterialTheme.colorScheme.onSurfaceVariant, style = MaterialTheme.typography.bodyMedium, ) diff --git a/app/src/main/java/live/mehiz/mpvkt/ui/player/PlayerActivity.kt b/app/src/main/java/live/mehiz/mpvkt/ui/player/PlayerActivity.kt index bb85358..43e9ba8 100644 --- a/app/src/main/java/live/mehiz/mpvkt/ui/player/PlayerActivity.kt +++ b/app/src/main/java/live/mehiz/mpvkt/ui/player/PlayerActivity.kt @@ -404,8 +404,8 @@ class PlayerActivity : AppCompatActivity() { internal fun onObserverEvent(property: String, value: Long) { when (property) { "time-pos" -> viewModel.updatePlayBackPos(value.toFloat()) - "duration" -> viewModel.duration.update { value.toFloat() } "demuxer-cache-time" -> viewModel.updateReadAhead(value = value) + "duration" -> viewModel.duration.update { value.toFloat() } } } @@ -440,8 +440,10 @@ class PlayerActivity : AppCompatActivity() { } } - @Suppress("EmptyFunctionBlock", "UnusedParameter") internal fun onObserverEvent(property: String, value: String) { + when (property) { + "speed" -> viewModel.playbackSpeed.update { value.toFloat() } + } } internal fun event(eventId: Int) { @@ -450,11 +452,7 @@ class PlayerActivity : AppCompatActivity() { getFileName(intent)?.let { fileName = it } viewModel.mediaTitle.update { val mediaTitle = MPVLib.getPropertyString("media-title") - if (mediaTitle.isBlank() || mediaTitle.isDigitsOnly()) { - fileName - } else { - mediaTitle - } + if (mediaTitle.isBlank() || mediaTitle.isDigitsOnly()) fileName else mediaTitle } CoroutineScope(Dispatchers.IO).launch { loadVideoPlaybackState(fileName) @@ -482,8 +480,9 @@ class PlayerActivity : AppCompatActivity() { if (!::fileName.isInitialized) return mpvKtDatabase.videoDataDao().upsert( PlaybackStateEntity( - fileName, - if (playerPreferences.savePositionOnQuit.get()) player.timePos ?: 0 else 0, + mediaTitle = fileName, + lastPosition = if (playerPreferences.savePositionOnQuit.get()) player.timePos ?: 0 else 0, + playbackSpeed = player.playbackSpeed ?: playerPreferences.defaultSpeed.get().toDouble(), sid = player.sid, subDelay = (MPVLib.getPropertyDouble("sub-delay") * 1000).toInt(), subSpeed = MPVLib.getPropertyDouble("sub-speed"), @@ -509,6 +508,7 @@ class PlayerActivity : AppCompatActivity() { player.aid = it.aid player.subDelay = subDelay player.secondarySubDelay = secondarySubDelay + player.playbackSpeed = it.playbackSpeed MPVLib.setPropertyDouble("audio-delay", audioDelay) } player.timePos = if (playerPreferences.savePositionOnQuit.get()) state?.lastPosition ?: 0 else 0 diff --git a/app/src/main/java/live/mehiz/mpvkt/ui/player/PlayerEnums.kt b/app/src/main/java/live/mehiz/mpvkt/ui/player/PlayerEnums.kt index 2693e55..201a44b 100644 --- a/app/src/main/java/live/mehiz/mpvkt/ui/player/PlayerEnums.kt +++ b/app/src/main/java/live/mehiz/mpvkt/ui/player/PlayerEnums.kt @@ -42,6 +42,7 @@ enum class Debanding { enum class Sheets { None, + PlaybackSpeed, SubtitleTracks, AudioTracks, Chapters, diff --git a/app/src/main/java/live/mehiz/mpvkt/ui/player/PlayerViewModel.kt b/app/src/main/java/live/mehiz/mpvkt/ui/player/PlayerViewModel.kt index c608bfe..f10e609 100644 --- a/app/src/main/java/live/mehiz/mpvkt/ui/player/PlayerViewModel.kt +++ b/app/src/main/java/live/mehiz/mpvkt/ui/player/PlayerViewModel.kt @@ -30,6 +30,7 @@ class PlayerViewModel( val mediaTitle = MutableStateFlow("") val isLoading = MutableStateFlow(true) + val playbackSpeed = MutableStateFlow(0f) private val _subtitleTracks = MutableStateFlow>(emptyList()) val subtitleTracks = _subtitleTracks.asStateFlow() diff --git a/app/src/main/java/live/mehiz/mpvkt/ui/player/controls/BottomLeftPlayerControls.kt b/app/src/main/java/live/mehiz/mpvkt/ui/player/controls/BottomLeftPlayerControls.kt index 6308c3c..8e7ad30 100644 --- a/app/src/main/java/live/mehiz/mpvkt/ui/player/controls/BottomLeftPlayerControls.kt +++ b/app/src/main/java/live/mehiz/mpvkt/ui/player/controls/BottomLeftPlayerControls.kt @@ -18,7 +18,6 @@ import `is`.xyz.mpv.MPVLib import kotlinx.coroutines.flow.update import live.mehiz.mpvkt.R import live.mehiz.mpvkt.preferences.PlayerPreferences -import live.mehiz.mpvkt.preferences.preference.collectAsState import live.mehiz.mpvkt.ui.player.PlayerViewModel import live.mehiz.mpvkt.ui.player.Sheets import live.mehiz.mpvkt.ui.player.controls.components.ControlsButton @@ -42,14 +41,16 @@ fun BottomLeftPlayerControls(modifier: Modifier = Modifier) { icon = Icons.Default.ScreenRotation, onClick = { viewModel.cycleScreenRotations() } ) - val defaultSpeed by playerPreferences.defaultSpeed.collectAsState() + val currentSpeed by viewModel.playbackSpeed.collectAsState() ControlsButton( - text = stringResource(R.string.player_speed, defaultSpeed), + text = stringResource(R.string.player_speed, currentSpeed), onClick = { - val newSpeed = if (defaultSpeed >= 2) 0.25f else defaultSpeed + 0.25f + val newSpeed = if (currentSpeed >= 2) 0.25f else currentSpeed + 0.25f + viewModel.playbackSpeed.update { newSpeed } MPVLib.setPropertyDouble("speed", newSpeed.toDouble()) playerPreferences.defaultSpeed.set(newSpeed) - } + }, + onLongClick = { viewModel.sheetShown.update { Sheets.PlaybackSpeed } } ) AnimatedVisibility( currentChapter != null && playerPreferences.currentChaptersIndicator.get(), diff --git a/app/src/main/java/live/mehiz/mpvkt/ui/player/controls/PlayerSheets.kt b/app/src/main/java/live/mehiz/mpvkt/ui/player/controls/PlayerSheets.kt index 445e86c..8b059b8 100644 --- a/app/src/main/java/live/mehiz/mpvkt/ui/player/controls/PlayerSheets.kt +++ b/app/src/main/java/live/mehiz/mpvkt/ui/player/controls/PlayerSheets.kt @@ -14,6 +14,7 @@ import live.mehiz.mpvkt.ui.player.controls.components.sheets.AudioTracksSheet import live.mehiz.mpvkt.ui.player.controls.components.sheets.ChaptersSheet import live.mehiz.mpvkt.ui.player.controls.components.sheets.DecodersSheet import live.mehiz.mpvkt.ui.player.controls.components.sheets.MoreSheet +import live.mehiz.mpvkt.ui.player.controls.components.sheets.PlaybackSpeedSheet import live.mehiz.mpvkt.ui.player.controls.components.sheets.SubtitlesSheet import org.koin.compose.koinInject @@ -107,5 +108,9 @@ fun PlayerSheets() { } ) } + + Sheets.PlaybackSpeed -> { + PlaybackSpeedSheet(onDismissRequest = onDismissRequest) + } } } diff --git a/app/src/main/java/live/mehiz/mpvkt/ui/player/controls/components/sheets/ChaptersSheet.kt b/app/src/main/java/live/mehiz/mpvkt/ui/player/controls/components/sheets/ChaptersSheet.kt index cfa7e7f..dfc99d1 100644 --- a/app/src/main/java/live/mehiz/mpvkt/ui/player/controls/components/sheets/ChaptersSheet.kt +++ b/app/src/main/java/live/mehiz/mpvkt/ui/player/controls/components/sheets/ChaptersSheet.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -12,11 +13,11 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.unit.dp import `is`.xyz.mpv.MPVView import `is`.xyz.mpv.Utils import kotlinx.collections.immutable.ImmutableList import live.mehiz.mpvkt.R +import live.mehiz.mpvkt.ui.theme.spacing @Composable fun ChaptersSheet( @@ -24,6 +25,7 @@ fun ChaptersSheet( currentChapter: Int, onClick: (Int) -> Unit, onDismissRequest: () -> Unit, + modifier: Modifier = Modifier, ) { GenericTracksSheet( chapters, @@ -37,6 +39,8 @@ fun ChaptersSheet( ) }, onDismissRequest = onDismissRequest, + modifier = modifier + .padding(vertical = MaterialTheme.spacing.medium) ) } @@ -53,7 +57,7 @@ fun ChapterTrack( modifier = modifier .fillMaxWidth() .clickable(onClick = onClick) - .padding(vertical = 8.dp, horizontal = 16.dp), + .padding(vertical = MaterialTheme.spacing.smaller, horizontal = MaterialTheme.spacing.medium), horizontalArrangement = Arrangement.SpaceBetween, ) { Text( diff --git a/app/src/main/java/live/mehiz/mpvkt/ui/player/controls/components/sheets/PlaybackSpeedSheet.kt b/app/src/main/java/live/mehiz/mpvkt/ui/player/controls/components/sheets/PlaybackSpeedSheet.kt new file mode 100644 index 0000000..e77eca3 --- /dev/null +++ b/app/src/main/java/live/mehiz/mpvkt/ui/player/controls/components/sheets/PlaybackSpeedSheet.kt @@ -0,0 +1,90 @@ +package live.mehiz.mpvkt.ui.player.controls.components.sheets + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.RestartAlt +import androidx.compose.material3.Button +import androidx.compose.material3.FilledIconButton +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import `is`.xyz.mpv.MPVLib +import kotlinx.coroutines.flow.update +import live.mehiz.mpvkt.R +import live.mehiz.mpvkt.preferences.AudioPreferences +import live.mehiz.mpvkt.preferences.PlayerPreferences +import live.mehiz.mpvkt.preferences.preference.collectAsState +import live.mehiz.mpvkt.presentation.components.PlayerSheet +import live.mehiz.mpvkt.presentation.components.SliderItem +import live.mehiz.mpvkt.ui.player.PlayerViewModel +import live.mehiz.mpvkt.ui.theme.spacing +import me.zhanghai.compose.preference.ProvidePreferenceLocals +import me.zhanghai.compose.preference.SwitchPreference +import org.koin.compose.koinInject + +@Composable +fun PlaybackSpeedSheet( + onDismissRequest: () -> Unit, + modifier: Modifier = Modifier, +) { + val preferences = koinInject() + val viewModel = koinInject() + val currentSpeed by viewModel.playbackSpeed.collectAsState() + PlayerSheet(onDismissRequest = onDismissRequest) { + Column( + modifier.padding(MaterialTheme.spacing.medium), + ) { + SliderItem( + label = stringResource(id = R.string.player_sheets_speed_slider_label), + value = currentSpeed, + valueText = stringResource(id = R.string.player_speed, currentSpeed), + onChange = { newSpeed -> + viewModel.playbackSpeed.update { newSpeed } + MPVLib.setPropertyDouble("speed", newSpeed.toDouble()) + }, + max = 6f, + min = 0.01f, + ) + ProvidePreferenceLocals { + val audioPreferences = koinInject() + val pitchCorrection by audioPreferences.audioPitchCorrection.collectAsState() + SwitchPreference( + value = pitchCorrection, + onValueChange = { audioPreferences.audioPitchCorrection.set(it) }, + title = { Text(text = stringResource(id = R.string.pref_audio_pitch_correction_title)) }, + summary = { Text(text = stringResource(id = R.string.pref_audio_pitch_correction_summary)) }, + ) + } + Row( + horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.smaller), + ) { + Button( + modifier = Modifier.weight(1f), + onClick = { + preferences.defaultSpeed.set(currentSpeed) + }, + ) { + Text(text = stringResource(id = R.string.player_sheets_speed_make_default)) + } + FilledIconButton( + onClick = { + preferences.defaultSpeed.delete() + viewModel.playbackSpeed.update { 1f } + MPVLib.setPropertyDouble("speed", 1.0) + }, + ) { + Icon(imageVector = Icons.Default.RestartAlt, contentDescription = null) + } + } + } + } +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 659c24f..e00ed30 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -127,6 +127,9 @@ Hue Some filters may not work on your current video driver + Speed + Make default speed + Value too big Value too small