Merge pull request #6172 from Bnyro/master

feat: support for duration inputs in submit segment dialog
This commit is contained in:
Bnyro 2024-06-21 18:11:17 +02:00 committed by GitHub
commit c65a2b8454
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 67 additions and 6 deletions

View File

@ -126,6 +126,7 @@ dependencies {
implementation(libs.kotlinx.serialization)
implementation(libs.kotlinx.datetime)
implementation(libs.kotlinx.serialization.retrofit)
testImplementation(libs.testng)
/* Cronet and Coil */
coreLibraryDesugaring(libs.desugaring)

View File

@ -18,6 +18,7 @@ import com.github.libretube.extensions.TAG
import com.github.libretube.extensions.toastFromMainDispatcher
import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.util.TextUtils
import com.github.libretube.util.TextUtils.parseDurationString
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@ -52,7 +53,7 @@ class SubmitSegmentDialog : DialogFragment() {
lifecycleScope.launch { voteForSegment() }
}
binding.startTime.setText((currentPosition.toFloat() / 1000).toString())
binding.startTime.setText(DateUtils.formatElapsedTime(((currentPosition.toFloat() / 1000).toLong())))
binding.segmentCategory.items = resources.getStringArray(R.array.sponsorBlockSegmentNames).toList()
@ -72,8 +73,9 @@ class SubmitSegmentDialog : DialogFragment() {
requireDialog().hide()
val startTime = binding.startTime.text.toString().toFloatOrNull()
var endTime = binding.endTime.text.toString().toFloatOrNull()
val startTime = binding.startTime.text.toString().parseDurationString()
var endTime = binding.endTime.text.toString().parseDurationString()
if (endTime == null || startTime == null || startTime > endTime) {
context.toastFromMainDispatcher(R.string.sb_invalid_segment)
return

View File

@ -7,6 +7,7 @@ import android.os.Build
import android.text.format.DateUtils
import com.github.libretube.BuildConfig
import com.github.libretube.R
import com.google.common.math.IntMath.pow
import kotlinx.datetime.toJavaLocalDate
import java.time.Instant
import java.time.LocalDateTime
@ -49,8 +50,44 @@ object TextUtils {
* Get time in seconds from a YouTube video link.
* @return Time in seconds
*/
fun String.toTimeInSeconds(): Long? {
return toLongOrNull() ?: Duration.parseOrNull(this)?.inWholeSeconds
fun String.toTimeInSeconds(): Long? = parseTimeString(this)?.toLong()
fun String.parseDurationString(): Float? = parseTimeString(this)
private fun parseTimeString(timeString: String): Float? {
if (timeString.all { it.isDigit() }) return timeString.toLongOrNull()?.toFloat()
if (timeString.all { it.isDigit() || ",.:".contains(it) }) {
var secondsTotal = 0
var secondsScoped = 0
var milliseconds = 0
var inMillis = false
for (char in timeString) {
if (inMillis) {
if (!char.isDigit()) break
milliseconds *= 10
milliseconds += char.digitToInt()
} else if (char.isDigit()) {
secondsScoped *= 10
secondsScoped += char.digitToInt()
} else if (char == ':') {
secondsTotal += secondsScoped * 60
secondsScoped = 0
} else if (",.".contains(char)) {
secondsTotal += secondsScoped
secondsScoped = 0
inMillis = true
}
}
val millisDecimal = milliseconds.toFloat() / pow(10, milliseconds.toString().length)
return secondsTotal.toFloat() + millisDecimal
}
return Duration.parseOrNull(timeString)?.inWholeMilliseconds?.toFloat()?.div(1000)
}
/**

View File

@ -35,7 +35,8 @@
android:id="@+id/start_time"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="numberDecimal" />
android:digits="0123456789.:"
android:inputType="number" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout

View File

@ -0,0 +1,16 @@
package com.github.libretube
import com.github.libretube.util.TextUtils.parseDurationString
import com.github.libretube.util.TextUtils.toTimeInSeconds
import org.junit.Test
import org.junit.Assert.assertEquals
class TextParserTest {
@Test
fun testTimeParser() {
assertEquals(15L * 60 + 20, "15m 20s".toTimeInSeconds())
assertEquals(1520L, "1520".toTimeInSeconds())
assertEquals(15L * 60 + 20, "15:20.25".toTimeInSeconds())
assertEquals(15f * 60 + 20 + 0.25f, "15:20.25".parseDurationString())
}
}

View File

@ -2,6 +2,7 @@
appcompat = "1.7.0"
core = "1.13.1"
gradle = "8.5.0"
junit = "4.13.2"
kotlin = "1.9.24"
ksp = "1.9.24-1.0.20"
lifecycle = "2.8.2"
@ -34,6 +35,7 @@ profileinstaller = "1.3.1"
paging = "3.3.0"
collection = "1.4.0"
swiperefreshlayout = "1.1.0"
testng = "6.9.6"
[libraries]
androidx-activity = { group = "androidx.activity", name = "activity-ktx", version.ref = "activity" }
@ -42,6 +44,7 @@ androidx-core = { group = "androidx.core", name = "core", version.ref = "core" }
androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
androidx-fragment = { group = "androidx.fragment", name = "fragment-ktx", version.ref = "fragment" }
gradle = { module = "com.android.tools.build:gradle", version.ref = "gradle" }
junit = { module = "junit:junit", version.ref = "junit" }
kotlin-gradle-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
kotlin-serialization = { module = "org.jetbrains.kotlin:kotlin-serialization", version.ref = "kotlin" }
logging-interceptor = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "loggingInterceptor" }
@ -81,6 +84,7 @@ androidx-profileinstaller = { group = "androidx.profileinstaller", name = "profi
androidx-paging = { group = "androidx.paging", name = "paging-runtime-ktx", version.ref = "paging" }
androidx-collection = { group = "androidx.collection", name = "collection", version.ref = "collection" }
androidx-swiperefreshlayout = { group = "androidx.swiperefreshlayout", name = "swiperefreshlayout", version.ref = "swiperefreshlayout" }
testng = { group = "org.testng", name = "testng", version.ref = "testng" }
[plugins]
androidTest = { id = "com.android.test", version.ref = "gradle" }