@@ -40,6 +40,7 @@ import dagger.hilt.android.AndroidEntryPoint
40
40
import kotlinx.coroutines.*
41
41
import kotlinx.coroutines.channels.Channel
42
42
import kotlinx.coroutines.flow.*
43
+ import splitties.coroutines.raceOf
43
44
import splitties.coroutines.repeatWhileActive
44
45
import splitties.experimental.ExperimentalSplittiesApi
45
46
import splitties.init.appCtx
@@ -60,6 +61,7 @@ class UploadForegroundService : ForegroundService(Companion, redeliverIntentIfKi
60
61
61
62
private val startSignal = Channel <StartUploadRequest >()
62
63
64
+ private val cancelTransferSignals = Channel <Unit >()
63
65
private val shouldRetrySignals = Channel <Boolean >()
64
66
65
67
private val pickedFilesExtractor: PickedFilesExtractor = PickedFilesExtractorImpl ().also {
@@ -123,6 +125,10 @@ class UploadForegroundService : ForegroundService(Companion, redeliverIntentIfKi
123
125
shouldRetrySignals.send(false )
124
126
}
125
127
128
+ suspend fun cancelUpload () {
129
+ cancelTransferSignals.send(Unit )
130
+ }
131
+
126
132
private fun keepServiceRunningWhileNeeded () {
127
133
val needsToKeepFileUris = isHandlingPickedFilesFlow.transformLatest { isHandlingFiles ->
128
134
if (isHandlingFiles) emit(true )
@@ -202,21 +208,24 @@ class UploadForegroundService : ForegroundService(Companion, redeliverIntentIfKi
202
208
)
203
209
204
210
// TODO[UL-retry]: Once we support resuming the upload:
205
- // 1. Remove this big `isInternetConnectedFlow` thing and move it to just the uploader part.
211
+ // 1. Remove `isInternetConnectedFlow.mapLatest` from `tryCompletingWithInternetUnlessCancelled`,
212
+ // and move it to just the uploader part.
206
213
// 2. Remove the loop (repeatWhileActive) below.
207
214
// 3. In the uploader, use a function named `uploadAllRemainingWithRetries` or something.
208
- val transferUuid = isInternetConnectedFlow.mapLatest { isInternetConnected ->
209
- if (isInternetConnected.not ()) {
215
+ val transferUuid = tryCompletingWithInternetUnlessCancelled(
216
+ valueIfCancelled = null ,
217
+ withoutInternet = {
210
218
currentState = uploadStateFlow.filterIsInstance<UploadState .Ongoing >().first().copy(
211
219
status = Status .WaitingForInternet
212
220
)
213
221
awaitCancellation()
214
222
}
223
+ ) {
215
224
repeatWhileActive retryLoop@{
216
225
val result = startUploadSession(
217
226
startRequest = startRequest,
218
227
updateState = { currentState = it }
219
- ) ? : return @mapLatest null
228
+ ) ? : return @tryCompletingWithInternetUnlessCancelled null
220
229
val uploader = TransferUploader (
221
230
uploadManager = uploadManager,
222
231
fileChunkSizeManager = fileChunkSizeManager,
@@ -242,11 +251,11 @@ class UploadForegroundService : ForegroundService(Companion, redeliverIntentIfKi
242
251
if (shouldRetry()) {
243
252
return @retryLoop
244
253
} else {
245
- return @mapLatest null
254
+ return @tryCompletingWithInternetUnlessCancelled null
246
255
}
247
- }.onSuccess { uuid -> return @mapLatest uuid }
256
+ }.onSuccess { uuid -> return @tryCompletingWithInternetUnlessCancelled uuid }
248
257
}
249
- }.first()
258
+ }
250
259
currentState = if (transferUuid != null ) {
251
260
val url = sharedApiUrlCreator.shareTransferUrl(transferUuid)
252
261
val transferType = startRequest.info.type
@@ -268,6 +277,19 @@ class UploadForegroundService : ForegroundService(Companion, redeliverIntentIfKi
268
277
}
269
278
}
270
279
280
+ private suspend fun <R > tryCompletingWithInternetUnlessCancelled (
281
+ valueIfCancelled : R ,
282
+ withoutInternet : suspend () -> R ,
283
+ block : suspend () -> R
284
+ ): R ? = raceOf({
285
+ isInternetConnectedFlow.mapLatest { isInternetConnected ->
286
+ if (isInternetConnected) block() else withoutInternet()
287
+ }.first()
288
+ }, {
289
+ cancelTransferSignals.receive()
290
+ valueIfCancelled
291
+ })
292
+
271
293
private suspend fun shouldRetry (): Boolean = shouldRetrySignals.receive()
272
294
273
295
private suspend fun startUploadSession (
0 commit comments