Thursday, 13 June 2019

FCM + Coroutine

package com.example.background.fcm

import android.content.Context
import androidx.annotation.WorkerThreadimport com.google.firebase.iid.FirebaseInstanceId
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers.IOimport kotlinx.coroutines.async
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine

class TokenManager(scope: CoroutineScope = CoroutineScope(IO)) {
    private val instanceId = FirebaseInstanceId.getInstance().instanceId
    var currentToken: Deferred

    init {
        currentToken = scope.async {            instanceId.awaitTask()?.token        }    }

    @WorkerThread    internal suspend fun share(appContext: Context) {
        decorateToken {            deferredToken = currentToken            context = appContext
        }    }
}


-----------------
import com.google.android.gms.tasks.Task
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine

suspend fun <T> Task<T>.awaitTask(): T? = suspendCoroutine { continuation ->    this.addOnCompleteListener { task ->        if (task.isSuccessful) {
            continuation.resume(task.result)
        } else {
            continuation.resumeWithException(task.exception!!)
        }
    }}

------------------
import android.content.Contextimport androidx.annotation.WorkerThreadimport kotlinx.coroutines.CoroutineScopeimport kotlinx.coroutines.Deferredimport kotlinx.coroutines.Dispatchersimport kotlinx.coroutines.launch
class TokenDecorator(private val scope : CoroutineScope = CoroutineScope(Dispatchers.IO)) {
    lateinit var deferredToken : Deferred
    lateinit var context : Context

    @WorkerThread    fun decorate() {
        scope.launch {            val token = deferredToken?.await().toString()

            //share with third party
            //sendToServer(token)        }    }
}
fun decorateToken(block : TokenDecorator.() -> Unit) = TokenDecorator().apply(block).decorate()




Suspend Coroutine example

import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlin.concurrent.thread
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine

open class Result(val output: String?)

class Success(output: String) : Result(output)

class Failure : Result(null)

fun onComplete(result: Result?) {
    when (result) {
        is Success -> println("onSuccess : ${result.output}")
        is Failure -> println("onError ")
    }
}

class Callback {
    var listener: ((Result?) -> Unit)? = null
    internal fun onSuccess(result: Result?) {
        onComplete(result)
    }

    internal fun onError(result: Result?) {
        onComplete(result)
    }

    internal fun addListener(listener: ((Result?) -> Unit)?) {
        this.listener = listener
    }

    fun onComplete(result: Result?) {
        listener?.let { it(result) }    }
}

fun fakeAsyncCallback(): Callback {
    val callback = Callback()

    thread(isDaemon = false) {        try {
            println("starting fakeAsyncCallback")

            Thread.sleep(2000)

            println("done fakeAsyncCallback")

            callback.onSuccess(Success("fakeAsyncCallback completed"))
        } catch (throwable: Throwable) {
            callback.onError(Failure())
        }
    }
    return callback
}

suspend fun convertToSuspend(callback: Callback): String {
    return suspendCoroutine { continuation ->        callback.addListener { result ->            when (result) {
                is Success -> continuation.resume("onSuccess : ${result.output}")
                is Failure -> continuation.resume("onError ")
            }
        }    }}

fun main(args: Array) = runBlocking {    val callback = fakeAsyncCallback()
    val job = GlobalScope.launch {        val result = convertToSuspend(callback)
        
        println(result)
    }
    println("called fakeAsyncCallback")

    println("main done")

    job.join()
}