New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Show recording duration and waveform #4241
Changes from all commits
dd12480
4334767
0f530ac
abdd668
be82041
198c071
81d75eb
8cbc842
248a12f
788a0b8
ef1f27d
c70687c
d1506a4
007b6e1
71fc627
74dca6c
90a78f5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package org.odk.collect.async | ||
|
||
import kotlinx.coroutines.CoroutineScope | ||
import kotlinx.coroutines.delay | ||
import kotlinx.coroutines.isActive | ||
import kotlinx.coroutines.launch | ||
import kotlinx.coroutines.withContext | ||
import java.util.function.Consumer | ||
import java.util.function.Supplier | ||
import kotlin.coroutines.CoroutineContext | ||
|
||
open class CoroutineScheduler(private val foregroundContext: CoroutineContext, private val backgroundContext: CoroutineContext) : Scheduler { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We won't always need deferred tasks so thought I'd split the implementations. I think long term it might make sense to split the interfaces but lets see how it evolves. |
||
|
||
override fun <T> immediate(background: Supplier<T>, foreground: Consumer<T>) { | ||
CoroutineScope(foregroundContext).launch { | ||
val result = withContext(backgroundContext) { background.get() } | ||
foreground.accept(result) | ||
} | ||
} | ||
|
||
override fun immediate(foreground: Runnable) { | ||
CoroutineScope(foregroundContext).launch { | ||
foreground.run() | ||
} | ||
} | ||
|
||
override fun repeat(foreground: Runnable, repeatPeriod: Long): Cancellable { | ||
val repeatScope = CoroutineScope(foregroundContext) | ||
|
||
repeatScope.launch { | ||
while (isActive) { | ||
foreground.run() | ||
delay(repeatPeriod) | ||
} | ||
} | ||
|
||
return ScopeCancellable(repeatScope) | ||
} | ||
|
||
override fun networkDeferred(tag: String, spec: TaskSpec) { | ||
throw UnsupportedOperationException() | ||
} | ||
|
||
override fun networkDeferred(tag: String, spec: TaskSpec, repeatPeriod: Long) { | ||
throw UnsupportedOperationException() | ||
} | ||
|
||
override fun cancelDeferred(tag: String) { | ||
throw UnsupportedOperationException() | ||
} | ||
|
||
override fun isRunning(tag: String): Boolean { | ||
throw UnsupportedOperationException() | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package org.odk.collect.async | ||
|
||
import kotlinx.coroutines.CoroutineScope | ||
import kotlinx.coroutines.cancel | ||
|
||
internal class ScopeCancellable(private val scope: CoroutineScope) : Cancellable { | ||
|
||
override fun cancel(): Boolean { | ||
scope.cancel() | ||
return true | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,12 +7,12 @@ import java.io.File | |
|
||
/** | ||
* Interface for a ViewModel that records audio. Can only record once session | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we're now in a better place with this abstraction. Adding time/amplitude helped it make more sense as it forced the key data exposed to be the current "session" details. |
||
* at a time but supports cases where multiple views can start/playback different | ||
* recordings through a `sessionsId` passed to `start` and `getRecording`. | ||
* at a time. | ||
*/ | ||
abstract class AudioRecorderViewModel : ViewModel() { | ||
abstract fun isRecording(): LiveData<Boolean> | ||
abstract fun getRecording(sessionId: String): LiveData<File?> | ||
abstract fun isRecording(): Boolean | ||
abstract fun getCurrentSession(): LiveData<RecordingSession?> | ||
|
||
abstract fun start(sessionId: String, output: Output) | ||
abstract fun stop() | ||
|
||
|
@@ -22,3 +22,5 @@ abstract class AudioRecorderViewModel : ViewModel() { | |
*/ | ||
abstract fun cleanUp() | ||
} | ||
|
||
data class RecordingSession(val id: String, val file: File?, val duration: Long, val amplitude: Int) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that we have a test file in
strings
we should really be running it on CI.