https://kotlinlang.org logo
Title
u

ursus

04/18/2022, 10:01 PM
fun ProgressThingy(duration: Long, running: Flow<Boolean>): Flow<Float> {
    return callbackFlow {
        val animator = ValueAnimator.ofFloat(0F, 1F)
        animator.duration = duration
        animator.addUpdateListener {
            trySend(it.animatedFraction)
        }
        animator.addListener(onCancel = {
            cancel()
        })
        animator.start()

        running.collect {
            if (it) animator.resume() else animator.pause()
        }

        awaitClose {
            animator.cancel()
        }
    }
}
does anyone see a problem with collecting the running signal like that within the callback flow scope? it appears to work fine
ok turns out the
collect
obviosuly suspends so awaitClose is never registered since
running
never completes Any idea I could do this pattern? Rx used it
j

Joffrey

04/18/2022, 10:05 PM
You could launch the collection in a coroutine so you can reach
awaitClose
u

ursus

04/18/2022, 10:07 PM
doh.. thank you!
If I could ask, now it suspend on the awaitClose until the flow is canceled. How could I make it complete when the animator completes?
Im aware that it completes once the lambda returns, sooo idk
j

Joffrey

04/18/2022, 10:37 PM
You can make the callback flow complete by calling
close()
on the channel it gives you, but I don't know the animator API so not sure how to know it is complete
u

ursus

04/18/2022, 10:38 PM
oh, interesting that its not wrapped
j

Joffrey

04/18/2022, 10:38 PM
What do you mean?
u

ursus

04/18/2022, 10:39 PM
that I have to talk to the channel directly, not via some indirection as with trySend
hm there is a
close
in there as well
j

Joffrey

04/18/2022, 10:40 PM
trySend
is provided by the producer scope I believe, and close should be also provided the same way
u

ursus

04/18/2022, 10:40 PM
yea so why leak the channel reference like that
j

Joffrey

04/18/2022, 10:42 PM
I don't believe it's meant to be hidden. Rather, the producer scope provides convenient "shortcuts", but the basics are there if you need them (like pass the channel around etc)
u

ursus

04/18/2022, 10:42 PM
alright so the
close
is same as calling
channel.close
?
j

Joffrey

04/18/2022, 10:43 PM
I believe so yes, but I can't check because I'm afk. You should be able to check that in the IDE by navigating to the declaration. I would bet it calls channel.close()
u

ursus

04/18/2022, 10:44 PM
well it doesnt but it does behave as expected
public override fun close(cause: Throwable?): Boolean {
        val closed = Closed<E>(cause)
        /*
         * Try to commit close by adding a close token to the end of the queue.
         * Successful -> we're now responsible for closing receivers
         * Not successful -> help closing pending receivers to maintain invariant
         * "if (!close()) next send will throw"
         */
        val closeAdded = queue.addLastIfPrev(closed) { it !is Closed<*> }
        val actuallyClosed = if (closeAdded) closed else queue.prevNode as Closed<*>
        helpClose(actuallyClosed)
        if (closeAdded) invokeOnCloseHandler(cause)
        return closeAdded // true if we have closed
    }