Skip to content

Instantly share code, notes, and snippets.

@jasonostrander
Last active May 11, 2020 02:11
Show Gist options
  • Save jasonostrander/f735072b4d1b1f008b77a38db7328eb9 to your computer and use it in GitHub Desktop.
Save jasonostrander/f735072b4d1b1f008b77a38db7328eb9 to your computer and use it in GitHub Desktop.
import androidx.lifecycle.ViewModel
import kotlinx.coroutines.async
import kotlinx.coroutines.channels.ConflatedBroadcastChannel
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withContext
import java.io.File
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
interface HttpRequestListener {
fun onDone(code: Int, body: ByteArray)
fun onFailure()
}
interface HttpManager {
fun uploadFiles(archive: File, httpRequestListener: HttpRequestListener)
fun removeListener(httpRequestListener: HttpRequestListener)
}
suspend fun HttpManager.uploadFileToServer(archive: File) = suspendCancellableCoroutine<Int> { cont ->
val listener = object : HttpRequestListener {
override fun onDone(code: Int, body: ByteArray) {
cont.resume(code)
}
override fun onFailure() {
cont.resumeWithException(RuntimeException("Upload failed"))
}
}
uploadFiles(archive, listener)
cont.invokeOnCancellation { this.removeListener(listener) }
}
class UploadFilesUseCase(
val ioContext: CoroutineContext,
val httpManager: HttpManager
) {
private suspend fun <T> retry(times: Int = 3, block: suspend () -> T): T {
lateinit var exception: Exception
repeat(times) {
try {
return block()
} catch (e: Exception) {
exception = e
// add backoff here
}
}
throw exception
}
suspend fun uploadFiles(): Boolean = withContext(ioContext) {
try {
val mergedA = async { processAndMergeFilesOfTypeA() }
val mergedB = async { processAndMergeFilesOfTypeB() }
val archive = compressMergedFiles(mergedA.await(), mergedB.await())
val code = retry { httpManager.uploadFileToServer(archive) }
code / 100 == 2
} finally {
deleteTempDir()
}
}
private suspend fun processAndMergeFilesOfTypeA(): File = retry { TODO() }
private suspend fun processAndMergeFilesOfTypeB(): File = retry { TODO() }
private fun compressMergedFiles(mergedA: File, mergedB: File): File = TODO()
private fun deleteTempDir(): Unit = TODO()
}
class FileUploadViewModel(private val uploadFilesUseCase: UploadFilesUseCase) : ViewModel() {
enum class UploadFilesResult { Fail, Success }
private val ch = ConflatedBroadcastChannel<Unit>()
val result: Flow<UploadFilesResult>
get() = ch.asFlow()
.map {
try {
if (uploadFilesUseCase.uploadFiles()) {
UploadFilesResult.Success
} else {
UploadFilesResult.Fail
}
} catch (t: Throwable) {
// Log issues uploading
UploadFilesResult.Fail
}
}
fun uploadFiles() {
ch.offer(Unit)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment