@@ -40,6 +40,7 @@ import dagger.hilt.android.AndroidEntryPoint
4040import kotlinx.coroutines.*
4141import kotlinx.coroutines.channels.Channel
4242import kotlinx.coroutines.flow.*
43+ import splitties.coroutines.raceOf
4344import splitties.coroutines.repeatWhileActive
4445import splitties.experimental.ExperimentalSplittiesApi
4546import splitties.init.appCtx
@@ -60,6 +61,7 @@ class UploadForegroundService : ForegroundService(Companion, redeliverIntentIfKi
6061
6162 private val startSignal = Channel <StartUploadRequest >()
6263
64+ private val cancelTransferSignals = Channel <Unit >()
6365 private val shouldRetrySignals = Channel <Boolean >()
6466
6567 private val pickedFilesExtractor: PickedFilesExtractor = PickedFilesExtractorImpl ().also {
@@ -123,6 +125,10 @@ class UploadForegroundService : ForegroundService(Companion, redeliverIntentIfKi
123125 shouldRetrySignals.send(false )
124126 }
125127
128+ suspend fun cancelUpload () {
129+ cancelTransferSignals.send(Unit )
130+ }
131+
126132 private fun keepServiceRunningWhileNeeded () {
127133 val needsToKeepFileUris = isHandlingPickedFilesFlow.transformLatest { isHandlingFiles ->
128134 if (isHandlingFiles) emit(true )
@@ -202,21 +208,24 @@ class UploadForegroundService : ForegroundService(Companion, redeliverIntentIfKi
202208 )
203209
204210 // 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.
206213 // 2. Remove the loop (repeatWhileActive) below.
207214 // 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 = {
210218 currentState = uploadStateFlow.filterIsInstance<UploadState .Ongoing >().first().copy(
211219 status = Status .WaitingForInternet
212220 )
213221 awaitCancellation()
214222 }
223+ ) {
215224 repeatWhileActive retryLoop@{
216225 val result = startUploadSession(
217226 startRequest = startRequest,
218227 updateState = { currentState = it }
219- ) ? : return @mapLatest null
228+ ) ? : return @tryCompletingWithInternetUnlessCancelled null
220229 val uploader = TransferUploader (
221230 uploadManager = uploadManager,
222231 fileChunkSizeManager = fileChunkSizeManager,
@@ -242,11 +251,11 @@ class UploadForegroundService : ForegroundService(Companion, redeliverIntentIfKi
242251 if (shouldRetry()) {
243252 return @retryLoop
244253 } else {
245- return @mapLatest null
254+ return @tryCompletingWithInternetUnlessCancelled null
246255 }
247- }.onSuccess { uuid -> return @mapLatest uuid }
256+ }.onSuccess { uuid -> return @tryCompletingWithInternetUnlessCancelled uuid }
248257 }
249- }.first()
258+ }
250259 currentState = if (transferUuid != null ) {
251260 val url = sharedApiUrlCreator.shareTransferUrl(transferUuid)
252261 val transferType = startRequest.info.type
@@ -268,6 +277,19 @@ class UploadForegroundService : ForegroundService(Companion, redeliverIntentIfKi
268277 }
269278 }
270279
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+
271293 private suspend fun shouldRetry (): Boolean = shouldRetrySignals.receive()
272294
273295 private suspend fun startUploadSession (
0 commit comments