XYOracleNetwork/sdk-ble-flutter

View on GitHub
android/src/main/kotlin/network/xyo/ble/xyo_ble/channels/bridge.kt

Summary

Maintainability
A
3 hrs
Test Coverage
package network.xyo.ble.xyo_ble.channels

import android.content.Context
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.PluginRegistry
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import network.xyo.ble.devices.xy.XY4BluetoothDevice
import network.xyo.ble.flutter.protobuf.BoundWitness
import network.xyo.ble.generic.gatt.server.XYBluetoothAdvertiser
import network.xyo.ble.generic.gatt.server.XYBluetoothGattServer
import network.xyo.ble.generic.scanner.XYSmartScan
import network.xyo.ble.xyo_ble.InteractionModel
import network.xyo.modbluetoothkotlin.advertiser.XyoBluetoothAdvertiser
import network.xyo.modbluetoothkotlin.client.*
import network.xyo.modbluetoothkotlin.server.XyoBluetoothServer
import network.xyo.sdkcorekotlin.boundWitness.XyoBoundWitness
import network.xyo.sdkcorekotlin.node.XyoNodeListener
import network.xyo.sdkobjectmodelkotlin.structure.XyoIterableStructure
import network.xyo.sdkobjectmodelkotlin.structure.XyoObjectStructure
import java.util.*
import kotlin.collections.ArrayList

@kotlin.ExperimentalUnsignedTypes
class XyoBridgeChannel(context: Context, val smartScan: XYSmartScan, registrar: PluginRegistry.Registrar, name: String): XyoNodeChannel(context, registrar, name) {

    private val server = XYBluetoothGattServer(context.applicationContext)
    private val serverHelper = XyoBluetoothServer(server)
    private val xyAdvertiser = XYBluetoothAdvertiser(context)
    private lateinit var advertiser: XyoBluetoothAdvertiser

    init {
        GlobalScope.launch {
            bridgeManager.restoreAndInitBridge().await()
            bridgeManager.bridge.addListener("bridge", onBoundWitness)
        }

        XyoSentinelX.enable(true)
        XyoBridgeX.enable(true)
        XyoIosAppX.enable(true)
        XyoAndroidAppX.enable(true)
        XyoBluetoothClient.enable(true)
        XY4BluetoothDevice.enable(true)
    }

    override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
        when (call.method) {
            "setArchivists" -> setArchivists(call, result)
            "getBlockCount" -> getBlockCount(call, result)
            "getLastBlock" -> getLastBlock(call, result)
            "selfSign" -> selfSign(call, result)
            "getBlocks" -> getBlocks(call, result)
            "initiateBoundWitness" -> initiateBoundWitness(call, result)
            else -> super.onMethodCall(call, result)
        }
    }

    private fun createNewAdvertiser(): XyoBluetoothAdvertiser {
        return XyoBluetoothAdvertiser(
                Random().nextInt(Short.MAX_VALUE + 1).toUShort(),
                Random().nextInt(Short.MAX_VALUE + 1).toUShort(),
                xyAdvertiser)
    }

    override suspend fun onStart(): XyoNodeChannelStatus {
        smartScan.addListener("bridge", bridgeManager.bridge.scanCallback)
        serverHelper.listener = bridgeManager.bridge.serverCallback
        server.startServer()
        serverHelper.initServer()
        advertiser = createNewAdvertiser()
        advertiser.configureAdvertiser()
        advertiser.startAdvertiser()
        smartScan.start()
        status = XyoNodeChannelStatus.Started
        return status
    }

    override suspend fun onStop(): XyoNodeChannelStatus {
        smartScan.removeListener("bridge")
        serverHelper.listener = null
        server.stopServer()
        status = XyoNodeChannelStatus.Stopped
        return status
    }

    private fun setArchivists(call: MethodCall, result: MethodChannel.Result) = GlobalScope.launch {
        notImplemented(result)
    }

    private fun selfSign(call: MethodCall, result: MethodChannel.Result) = GlobalScope.launch {
        bridgeManager.bridge.selfSignOriginChain().await()

        //now that we have a new last block
        getLastBlock(call, result)
    }

    private fun initiateBoundWitness(call: MethodCall, result: MethodChannel.Result) = GlobalScope.launch {
        var didInitiate = false
        val deviceId = (call.arguments as? Map<String, Any?>)?.get("deviceId") as? String
        deviceId?.let {
            val device = smartScan.deviceFromId(it) as? XyoBluetoothClient
            device?.let {
                bridgeManager.bridge.tryBoundWitnessWithDevice(device)
                didInitiate = true
            }
        }
        if (didInitiate) {
            sendResult(result, true)
        } else {
            sendError(result, "Not Found", "Device not found for connection")
        }
    }

    private fun getBlockCount(call: MethodCall, result: MethodChannel.Result) = GlobalScope.launch {
        val hashes = bridgeManager.bridge.blockRepository.getAllOriginBlockHashes().await() ?: return@launch sendError(result, "FAILED")
        val models = hashesToBoundWitnesses(hashes)

        sendResult(result, models?.count())
    }

    private fun getLastBlockData() = GlobalScope.async {
        val hash = bridgeManager.bridge.originState.previousHash ?: return@async null
        val structure = XyoIterableStructure(hash.bytesCopy, 0)

        val hashArray = arrayOf(structure[0])
        val hashArrayIterator = hashArray.iterator()
        val boundWitnesses = hashesToBoundWitnesses(hashArrayIterator)
        return@async boundWitnesses?.first()
    }

    private fun getLastBlock(call: MethodCall, result: MethodChannel.Result) = GlobalScope.launch {
        val lastModel: BoundWitness.DeviceBoundWitness? = getLastBlockData().await()
        sendResult(result, lastModel?.toByteArray())
    }

    private fun getBlocksData() = GlobalScope.async {
        val hashes = bridgeManager.blockRepo.getAllOriginBlockHashes().await()
        val iterator = hashes?.iterator()
        return@async hashesToBoundWitnesses(iterator)
    }

    private fun getBlocks(call: MethodCall, result: MethodChannel.Result) = GlobalScope.launch {
        val blocks = getBlocksData().await()
        val builder = BoundWitness.DeviceBoundWitnessList.newBuilder()
        builder.addAllBoundWitnesses(blocks?.toList())
        sendResult(result, builder.build().toByteArray())
    }

    private fun hashesToBoundWitnesses(hashes: Iterator<XyoObjectStructure>?): ArrayList<BoundWitness.DeviceBoundWitness>? {
        if (hashes == null) return null;
        val models = ArrayList<BoundWitness.DeviceBoundWitness>()

        for (hash in hashes) {
            val model = InteractionModel(bridgeManager.bridge.blockRepository, hash.bytesCopy, Date(), true)
            models.add(model.toBuffer())
        }

        return models
    }

    private fun notifyNewBoundWitness() {
        GlobalScope.launch {
            val boundWitness = getLastBlockData().await()

            if (boundWitness != null) {
                events.send(boundWitness.toByteArray())
            }
        }
    }

    private val onBoundWitness = object : XyoNodeListener() {
        override fun onBoundWitnessEndSuccess(boundWitness: XyoBoundWitness) {
            notifyNewBoundWitness()
        }
    }
}