Skip to content

Commit 1c7bb5b

Browse files
authored
Adding progress listener to kotlin WpRequestExecutor (#816)
* Adding progress lsitener * detekt * Some refactoring
1 parent 92eb79b commit 1c7bb5b

File tree

2 files changed

+81
-2
lines changed

2 files changed

+81
-2
lines changed
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package rs.wordpress.api.kotlin
2+
3+
import okhttp3.MediaType
4+
import okhttp3.RequestBody
5+
import okio.Buffer
6+
import okio.BufferedSink
7+
import okio.ForwardingSink
8+
import okio.Sink
9+
import okio.buffer
10+
import java.io.IOException
11+
12+
private const val PROGRESS_THROTTLE_MS = 500
13+
class ProgressRequestBody(
14+
private val delegate: RequestBody,
15+
private val progressListener: ProgressListener
16+
) : RequestBody() {
17+
interface ProgressListener {
18+
fun onProgress(bytesWritten: Long, contentLength: Long)
19+
}
20+
21+
override fun contentType(): MediaType? = delegate.contentType()
22+
23+
override fun contentLength(): Long = delegate.contentLength()
24+
25+
@Throws(IOException::class)
26+
override fun writeTo(sink: BufferedSink) {
27+
val contentLength = contentLength()
28+
val progressSink = ProgressSink(sink, contentLength, progressListener)
29+
val bufferedSink = progressSink.buffer()
30+
31+
delegate.writeTo(bufferedSink)
32+
bufferedSink.flush()
33+
}
34+
35+
private class ProgressSink(
36+
delegate: Sink,
37+
private val contentLength: Long,
38+
private val listener: ProgressListener
39+
) : ForwardingSink(delegate) {
40+
private var totalBytesWritten = 0L
41+
private var lastProgressTime = 0L
42+
43+
@Throws(IOException::class)
44+
override fun write(source: Buffer, byteCount: Long) {
45+
super.write(source, byteCount)
46+
totalBytesWritten += byteCount
47+
val currentTime = System.currentTimeMillis()
48+
val isComplete = totalBytesWritten >= contentLength
49+
// Always send progress for completion, or if enough time has passed
50+
if (isComplete || currentTime - lastProgressTime >= PROGRESS_THROTTLE_MS) {
51+
listener.onProgress(totalBytesWritten, contentLength)
52+
lastProgressTime = currentTime
53+
}
54+
}
55+
}
56+
}

native/kotlin/api/kotlin/src/main/kotlin/rs/wordpress/api/kotlin/WpRequestExecutor.kt

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import okhttp3.MediaType.Companion.toMediaType
99
import okhttp3.MultipartBody
1010
import okhttp3.OkHttp
1111
import okhttp3.Request
12+
import okhttp3.RequestBody
1213
import okhttp3.RequestBody.Companion.asRequestBody
1314
import okhttp3.RequestBody.Companion.toRequestBody
1415
import uniffi.wp_api.InvalidSslErrorReason
@@ -33,7 +34,8 @@ const val USER_AGENT_HEADER_NAME = "User-Agent"
3334
class WpRequestExecutor(
3435
private val httpClient: WpHttpClient = WpHttpClient.DefaultHttpClient(),
3536
private val dispatcher: CoroutineDispatcher = Dispatchers.IO,
36-
private val fileResolver: FileResolver = DefaultFileResolver()
37+
private val fileResolver: FileResolver = DefaultFileResolver(),
38+
private val uploadProgressListener: ((uploadedBytes: Long, totalBytes: Long) -> Unit)? = null
3739
) : RequestExecutor {
3840
override suspend fun execute(request: WpNetworkRequest): WpNetworkResponse =
3941
withContext(dispatcher) {
@@ -93,10 +95,11 @@ class WpRequestExecutor(
9395
if (file == null || !file.canBeUploaded()) {
9496
throw MediaUploadRequestExecutionException.MediaFileNotFound(mediaUploadRequest.filePath())
9597
}
98+
val progressRequestBody = getRequestBody(file, mediaUploadRequest, uploadProgressListener)
9699
multipartBodyBuilder.addFormDataPart(
97100
name = "file",
98101
filename = file.name,
99-
body = file.asRequestBody(mediaUploadRequest.fileContentType().toMediaType())
102+
body = progressRequestBody
100103
)
101104
requestBuilder.method(
102105
method = mediaUploadRequest.method().toString(),
@@ -119,6 +122,26 @@ class WpRequestExecutor(
119122
}
120123
}
121124

125+
private fun getRequestBody(
126+
file: File,
127+
mediaUploadRequest: MediaUploadRequest,
128+
uploadProgressListener: ((uploadedBytes: Long, totalBytes: Long) -> Unit)?
129+
): RequestBody {
130+
val fileRequestBody = file.asRequestBody(mediaUploadRequest.fileContentType().toMediaType())
131+
return if (uploadProgressListener != null) {
132+
ProgressRequestBody(
133+
delegate = fileRequestBody,
134+
progressListener = object : ProgressRequestBody.ProgressListener {
135+
override fun onProgress(bytesWritten: Long, contentLength: Long) {
136+
uploadProgressListener.invoke(bytesWritten, contentLength)
137+
}
138+
}
139+
)
140+
} else {
141+
fileRequestBody
142+
}
143+
}
144+
122145
override suspend fun sleep(millis: ULong) {
123146
delay(millis.toLong())
124147
}

0 commit comments

Comments
 (0)