Skip to content

Commit

Permalink
feat(subs/audio): add delay cards
Browse files Browse the repository at this point in the history
some minor fixes too
  • Loading branch information
abdallahmehiz committed Aug 2, 2024
1 parent b3b9017 commit 611dc8b
Show file tree
Hide file tree
Showing 24 changed files with 734 additions and 28 deletions.
17 changes: 17 additions & 0 deletions app/src/main/java/live/mehiz/mpvkt/database/Migrations.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package live.mehiz.mpvkt.database

import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase

val Migrations: Array<Migration> = arrayOf(
MIGRATION1to2
)

private object MIGRATION1to2 : Migration(1, 2) {
override fun migrate(db: SupportSQLiteDatabase) {
listOf("subDelay", "secondarySubDelay", "audioDelay").forEach {
db.execSQL("ALTER TABLE PlaybackStateEntity ADD COLUMN $it INTEGER NOT NULL DEFAULT 0")
}
db.execSQL("ALTER TABLE PlaybackStateEntity ADD COLUMN subSpeed REAL NOT NULL DEFAULT 0")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 = 1)
@Database(entities = [PlaybackStateEntity::class], version = 2)
abstract class MpvKtDatabase : RoomDatabase() {
abstract fun videoDataDao(): PlaybackStateDao
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ data class PlaybackStateEntity(
@PrimaryKey val mediaTitle: String,
val lastPosition: Int, // in seconds
val sid: Int,
val subDelay: Int,
val subSpeed: Double,
val secondarySid: Int,
val secondarySubDelay: Int,
val aid: Int,
val audioDelay: Int,
)
2 changes: 2 additions & 0 deletions app/src/main/java/live/mehiz/mpvkt/di/DatabaseModule.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package live.mehiz.mpvkt.di

import androidx.room.Room
import live.mehiz.mpvkt.database.Migrations
import live.mehiz.mpvkt.database.MpvKtDatabase
import org.koin.android.ext.koin.androidContext
import org.koin.dsl.module
Expand All @@ -9,6 +10,7 @@ val DatabaseModule = module {
single<MpvKtDatabase> {
Room
.databaseBuilder(androidContext(), MpvKtDatabase::class.java, "mpvKt.db")
.addMigrations(migrations = Migrations)
.build()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ import live.mehiz.mpvkt.preferences.preference.PreferenceStore

class AudioPreferences(preferenceStore: PreferenceStore) {
val preferredLanguages = preferenceStore.getString("audio_preferred_languages")
val defaultAudioDelay = preferenceStore.getInt("audio_delay_default")
val audioPitchCorrection = preferenceStore.getBoolean("audio_pitch_correction", true)
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ class SubtitlesPreferences(preferenceStore: PreferenceStore) {
val justification = preferenceStore.getEnum("sub_justify", SubtitleJustification.Auto)

val overrideAssSubs = preferenceStore.getBoolean("sub_override_ass")

val defaultSubDelay = preferenceStore.getInt("sub_default_delay")
val defaultSubSpeed = preferenceStore.getFloat("sub_default_speed")
val defaultSecondarySubDelay = preferenceStore.getInt("sub_default_secondary_delay")
}

enum class SubtitleJustification(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
Expand Down Expand Up @@ -65,8 +66,8 @@ fun ExpandableCard(
Icon(Icons.Default.ArrowDropDown, null)
}
}
if (isExpanded) {
content()
Box(Modifier.animateContentSize()) {
if (isExpanded) content()
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package live.mehiz.mpvkt.presentation

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AddCircle
import androidx.compose.material.icons.filled.RemoveCircle
import androidx.compose.material3.Icon
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
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.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import live.mehiz.mpvkt.R

@Composable
fun OutlinedNumericChooser(
value: Int,
onChange: (Int) -> Unit,
max: Int,
step: Int,
modifier: Modifier = Modifier,
min: Int = 0,
suffix: (@Composable () -> Unit)? = null,
label: (@Composable () -> Unit)? = null,
) {
assert(max > min) { "min can't be larger than max ($min > $max)" }
Row(
modifier = modifier,
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
RepeatingIconButton(onClick = { onChange(value - step) }) {
Icon(Icons.Filled.RemoveCircle, null)
}
var valueString by remember { mutableStateOf("$value") }
LaunchedEffect(value) {
if (valueString.isBlank()) return@LaunchedEffect
valueString = value.toString()
}
OutlinedTextField(
label = label,
value = valueString,
onValueChange = { newValue ->
if (newValue.isBlank()) {
valueString = newValue
onChange(0)
}
runCatching {
val intValue = newValue.toInt()
onChange(intValue)
valueString = newValue
}
},
isError = value > max || value < min,
supportingText = {
if (value > max) Text(stringResource(R.string.numeric_chooser_value_too_big))
if (value < min) Text(stringResource(R.string.numeric_chooser_value_too_small))
},
suffix = suffix,
modifier = Modifier.weight(1f),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
)
RepeatingIconButton(onClick = { onChange(value + step) }) {
Icon(Icons.Filled.AddCircle, null)
}
}
}

@Composable
fun OutlinedNumericChooser(
value: Float,
onChange: (Float) -> Unit,
max: Float,
step: Float,
modifier: Modifier = Modifier,
min: Float = 0f,
suffix: (@Composable () -> Unit)? = null,
label: (@Composable () -> Unit)? = null,
) {
assert(max > min) { "min can't be larger than max ($min > $max)" }
Row(
modifier,
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
RepeatingIconButton(onClick = { onChange(value - step) }) {
Icon(Icons.Filled.RemoveCircle, null)
}
var valueString by remember { mutableStateOf("$value") }
LaunchedEffect(value) {
if (valueString.isBlank()) return@LaunchedEffect
valueString = value.toString().dropLastWhile { it == '0' }.dropLastWhile { it == '.' }
}
OutlinedTextField(
value = valueString,
label = label,
onValueChange = { newValue ->
if (newValue.isBlank()) {
valueString = newValue
onChange(0f)
}
runCatching {
if (newValue.startsWith('.')) return@runCatching
val floatValue = newValue.toFloat()
onChange(floatValue)
valueString = newValue
}
},
isError = value > max || value < min,
supportingText = {
if (value > max) Text(stringResource(R.string.numeric_chooser_value_too_big))
if (value < min) Text(stringResource(R.string.numeric_chooser_value_too_small))
},
modifier = Modifier.weight(1f),
suffix = suffix,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
)
RepeatingIconButton(onClick = { onChange(value + step) }) {
Icon(Icons.Filled.AddCircle, null)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package live.mehiz.mpvkt.presentation

import android.view.MotionEvent
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.material3.IconButton
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.rememberUpdatedState
import androidx.compose.runtime.setValue
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.pointerInteropFilter
import kotlinx.coroutines.delay

@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun RepeatingIconButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
maxDelayMillis: Long = 750,
minDelayMillis: Long = 5,
delayDecayFactor: Float = .25f,
content: @Composable () -> Unit,
) {
val currentClickListener by rememberUpdatedState(onClick)
var pressed by remember { mutableStateOf(false) }

IconButton(
modifier = modifier.pointerInteropFilter {
pressed = when (it.action) {
MotionEvent.ACTION_DOWN -> true

else -> false
}

true
},
onClick = {},
enabled = enabled,
interactionSource = interactionSource,
content = content,
)

LaunchedEffect(pressed, enabled) {
var currentDelayMillis = maxDelayMillis

while (enabled && pressed) {
currentClickListener()
delay(currentDelayMillis)
currentDelayMillis =
(currentDelayMillis - (currentDelayMillis * delayDecayFactor))
.toLong().coerceAtLeast(minDelayMillis)
}
}
}
49 changes: 49 additions & 0 deletions app/src/main/java/live/mehiz/mpvkt/presentation/SliderItem.kt
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,55 @@ fun SliderItem(
}
}

@Composable
fun SliderItem(
label: String,
value: Float,
valueText: String,
onChange: (Float) -> Unit,
max: Float,
steps: Int,
modifier: Modifier = Modifier,
min: Float = 0f,
icon: @Composable () -> Unit = {},
) {
val haptic = LocalHapticFeedback.current

Row(
modifier = modifier
.fillMaxWidth()
.padding(
horizontal = 16.dp,
vertical = 8.dp,
),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(24.dp),
) {
icon()
Column(modifier = Modifier.weight(0.5f)) {
Text(
text = label,
style = MaterialTheme.typography.bodyMedium,
)
Text(valueText)
}

Slider(
value = value,
onValueChange = {
val newValue = it
if (newValue != value) {
onChange(newValue)
haptic.performHapticFeedback(HapticFeedbackType.TextHandleMove)
}
},
modifier = Modifier.weight(1.5f),
valueRange = min..max,
steps = steps,
)
}
}

@Composable
fun VerticalSliderItem(
label: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,8 @@ class CrashActivity : ComponentActivity() {
}
OutlinedButton(
onClick = {
finish()
startActivity(Intent(this@CrashActivity, MainActivity::class.java))
finishAndRemoveTask()
},
modifier = Modifier.fillMaxWidth(),
) {
Expand Down
Loading

0 comments on commit 611dc8b

Please sign in to comment.