kopia lustrzana https://github.com/meshtastic/Meshtastic-Android
				
				
				
			Making app aware of device sleep states, Fix #4
							rodzic
							
								
									83c1bfda69
								
							
						
					
					
						commit
						f2d43332f7
					
				
							
								
								
									
										6
									
								
								TODO.md
								
								
								
								
							
							
						
						
									
										6
									
								
								TODO.md
								
								
								
								
							| 
						 | 
				
			
			@ -1,7 +1,11 @@
 | 
			
		|||
# High priority
 | 
			
		||||
Work items for soon alpha builds
 | 
			
		||||
 | 
			
		||||
* use states for meshservice: disconnected -> connected -> deviceasleep -> disconnected
 | 
			
		||||
Document the following in application behavior
 | 
			
		||||
*change ls_secs is 1 hr normally, which is fine because if there are other nodes in the mesh and they send us a packet we will wake any time during ls_secs and update app state
 | 
			
		||||
* use states for meshservice: disconnected -> connected-> devsleep -> disconnected (3 states)
 | 
			
		||||
* when device enters LS state radiointerfaceservice publishes "Broadcasting connection=false", meshservice should then enter devicesleepstate for ls_secs + 30s (to allow for some margin)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
* use compose on each page, but not for the outer wrapper
 | 
			
		||||
* one view per page: https://developer.android.com/guide/navigation/navigation-swipe-view-2
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -30,8 +30,11 @@ interface IMeshService {
 | 
			
		|||
    typ is defined in mesh.proto Data.Type.  For now juse use 0 to mean opaque bytes.
 | 
			
		||||
 | 
			
		||||
    destId can be null to indicate "broadcast message"
 | 
			
		||||
 | 
			
		||||
    Returns true if the packet has been sent into the mesh, or false if it was merely queued
 | 
			
		||||
    inside the service - and will be delivered to mesh the next time we hear from our radio.
 | 
			
		||||
    */
 | 
			
		||||
    void sendData(String destId, in byte[] payload, int typ);
 | 
			
		||||
    boolean sendData(String destId, in byte[] payload, int typ);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
    Get the IDs of everyone on the mesh.  You should also subscribe for NODE_CHANGE broadcasts.
 | 
			
		||||
| 
						 | 
				
			
			@ -47,9 +50,9 @@ interface IMeshService {
 | 
			
		|||
    void setRadioConfig(in byte []payload);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
    Is the packet radio currently connected to the phone?
 | 
			
		||||
    Is the packet radio currently connected to the phone?  Returns a ConnectionState string.
 | 
			
		||||
    */
 | 
			
		||||
    boolean isConnected();
 | 
			
		||||
    String connectionState();
 | 
			
		||||
 | 
			
		||||
    // see com.geeksville.com.geeksville.mesh broadcast intents
 | 
			
		||||
    // RECEIVED_OPAQUE  for data received from other nodes
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -311,10 +311,10 @@ class MainActivity : AppCompatActivity(), Logging,
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    /// Called when we gain/lose a connection to our mesh radio
 | 
			
		||||
    private fun onMeshConnectionChanged(connected: Boolean) {
 | 
			
		||||
    private fun onMeshConnectionChanged(connected: MeshService.ConnectionState) {
 | 
			
		||||
        UIState.isConnected.value = connected
 | 
			
		||||
        debug("connchange ${UIState.isConnected.value}")
 | 
			
		||||
        if (connected) {
 | 
			
		||||
        if (connected == MeshService.ConnectionState.CONNECTED) {
 | 
			
		||||
            // always get the current radio config when we connect
 | 
			
		||||
            readRadioConfig()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -383,7 +383,8 @@ class MainActivity : AppCompatActivity(), Logging,
 | 
			
		|||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                MeshService.ACTION_MESH_CONNECTED -> {
 | 
			
		||||
                    val connected = intent.getBooleanExtra(EXTRA_CONNECTED, false)
 | 
			
		||||
                    val connected =
 | 
			
		||||
                        MeshService.ConnectionState.valueOf(intent.getStringExtra(EXTRA_CONNECTED)!!)
 | 
			
		||||
                    onMeshConnectionChanged(connected)
 | 
			
		||||
                }
 | 
			
		||||
                else -> TODO()
 | 
			
		||||
| 
						 | 
				
			
			@ -402,7 +403,8 @@ class MainActivity : AppCompatActivity(), Logging,
 | 
			
		|||
            registerMeshReceiver()
 | 
			
		||||
 | 
			
		||||
            // We won't receive a notify for the initial state of connection, so we force an update here
 | 
			
		||||
            onMeshConnectionChanged(service.isConnected)
 | 
			
		||||
            val connectionState = MeshService.ConnectionState.valueOf(service.connectionState())
 | 
			
		||||
            onMeshConnectionChanged(connectionState)
 | 
			
		||||
 | 
			
		||||
            debug("connected to mesh service, isConnected=${UIState.isConnected.value}")
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,6 +11,7 @@ import com.geeksville.android.BuildUtils.isEmulator
 | 
			
		|||
import com.geeksville.android.Logging
 | 
			
		||||
import com.geeksville.mesh.IMeshService
 | 
			
		||||
import com.geeksville.mesh.MeshProtos
 | 
			
		||||
import com.geeksville.mesh.service.MeshService
 | 
			
		||||
import com.geeksville.mesh.ui.getInitials
 | 
			
		||||
 | 
			
		||||
/// FIXME - figure out how to merge this staate with the AppStatus Model
 | 
			
		||||
| 
						 | 
				
			
			@ -22,7 +23,7 @@ object UIState : Logging {
 | 
			
		|||
    var meshService: IMeshService? = null
 | 
			
		||||
 | 
			
		||||
    /// Are we connected to our radio device
 | 
			
		||||
    val isConnected = mutableStateOf(false)
 | 
			
		||||
    val isConnected = mutableStateOf(MeshService.ConnectionState.DISCONNECTED)
 | 
			
		||||
 | 
			
		||||
    /// various radio settings (including the channel)
 | 
			
		||||
    private val radioConfig = mutableStateOf<MeshProtos.RadioConfig?>(null)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -104,6 +104,12 @@ class MeshService : Service(), Logging {
 | 
			
		|||
        data class TextMessage(val fromId: String, val text: String)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public enum class ConnectionState {
 | 
			
		||||
        DISCONNECTED,
 | 
			
		||||
        CONNECTED,
 | 
			
		||||
        DEVICE_SLEEP // device is in LS sleep state, it will reconnected to us over bluetooth once it has data
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// A mapping of receiver class name to package name - used for explicit broadcasts
 | 
			
		||||
    private val clientPackages = mutableMapOf<String, String>()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -114,6 +120,9 @@ class MeshService : Service(), Logging {
 | 
			
		|||
    private val serviceJob = Job()
 | 
			
		||||
    private val serviceScope = CoroutineScope(Dispatchers.IO + serviceJob)
 | 
			
		||||
 | 
			
		||||
    /// The current state of our connection
 | 
			
		||||
    private var connectionState = ConnectionState.DISCONNECTED
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    see com.geeksville.mesh broadcast intents
 | 
			
		||||
    // RECEIVED_OPAQUE  for data received from other nodes
 | 
			
		||||
| 
						 | 
				
			
			@ -165,7 +174,7 @@ class MeshService : Service(), Logging {
 | 
			
		|||
                            )
 | 
			
		||||
                        } catch (ex: RadioNotConnectedException) {
 | 
			
		||||
                            warn("Lost connection to radio, stopping location requests")
 | 
			
		||||
                            onConnectionChanged(false)
 | 
			
		||||
                            onConnectionChanged(ConnectionState.DEVICE_SLEEP)
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			@ -262,7 +271,8 @@ class MeshService : Service(), Logging {
 | 
			
		|||
 | 
			
		||||
    /// Safely access the radio service, if not connected an exception will be thrown
 | 
			
		||||
    private val connectedRadio: IRadioInterfaceService
 | 
			
		||||
        get() = (if (isConnected) radio.serviceP else null) ?: throw RadioNotConnectedException()
 | 
			
		||||
        get() = (if (connectionState == ConnectionState.CONNECTED) radio.serviceP else null)
 | 
			
		||||
            ?: throw RadioNotConnectedException()
 | 
			
		||||
 | 
			
		||||
    /// Send a command/packet to our radio.  But cope with the possiblity that we might start up
 | 
			
		||||
    /// before we are fully bound to the RadioInterfaceService
 | 
			
		||||
| 
						 | 
				
			
			@ -314,11 +324,13 @@ class MeshService : Service(), Logging {
 | 
			
		|||
    /// A text message that has a arrived since the last notification update
 | 
			
		||||
    private var recentReceivedText: TextMessage? = null
 | 
			
		||||
 | 
			
		||||
    val summaryString
 | 
			
		||||
        get() = if (!isConnected)
 | 
			
		||||
            "No radio connected"
 | 
			
		||||
        else
 | 
			
		||||
            "Connected: $numOnlineNodes of $numNodes online"
 | 
			
		||||
    private val summaryString
 | 
			
		||||
        get() = when (connectionState) {
 | 
			
		||||
            ConnectionState.CONNECTED -> "Connected: $numOnlineNodes of $numNodes online"
 | 
			
		||||
            ConnectionState.DISCONNECTED -> "Disconnected"
 | 
			
		||||
            ConnectionState.DEVICE_SLEEP -> "Device sleeping"
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    override fun toString() = summaryString
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -416,8 +428,7 @@ class MeshService : Service(), Logging {
 | 
			
		|||
 | 
			
		||||
    var myNodeInfo: MyNodeInfo? = null
 | 
			
		||||
 | 
			
		||||
    /// Is our radio connected to the phone?
 | 
			
		||||
    private var isConnected = false
 | 
			
		||||
    private var radioConfig: MeshProtos.RadioConfig? = null
 | 
			
		||||
 | 
			
		||||
    /// True after we've done our initial node db init
 | 
			
		||||
    private var haveNodeDB = false
 | 
			
		||||
| 
						 | 
				
			
			@ -587,7 +598,10 @@ class MeshService : Service(), Logging {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    /// If packets arrive before we have our node DB, we delay parsing them until the DB is ready
 | 
			
		||||
    private val earlyPackets = mutableListOf<MeshPacket>()
 | 
			
		||||
    private val earlyReceivedPackets = mutableListOf<MeshPacket>()
 | 
			
		||||
 | 
			
		||||
    /// If apps try to send packets when our radio is sleeping, we queue them here instead
 | 
			
		||||
    private val offlineSentPackets = mutableListOf<MeshPacket>()
 | 
			
		||||
 | 
			
		||||
    /// Update our model and resend as needed for a MeshPacket we just received from the radio
 | 
			
		||||
    private fun handleReceivedMeshPacket(packet: MeshPacket) {
 | 
			
		||||
| 
						 | 
				
			
			@ -595,17 +609,21 @@ class MeshService : Service(), Logging {
 | 
			
		|||
            processReceivedMeshPacket(packet)
 | 
			
		||||
            onNodeDBChanged()
 | 
			
		||||
        } else {
 | 
			
		||||
            earlyPackets.add(packet)
 | 
			
		||||
            logAssert(earlyPackets.size < 128) // The max should normally be about 32, but if the device is messed up it might try to send forever
 | 
			
		||||
            earlyReceivedPackets.add(packet)
 | 
			
		||||
            logAssert(earlyReceivedPackets.size < 128) // The max should normally be about 32, but if the device is messed up it might try to send forever
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Process any packets that showed up too early
 | 
			
		||||
    private fun processEarlyPackets() {
 | 
			
		||||
        earlyPackets.forEach { processReceivedMeshPacket(it) }
 | 
			
		||||
        earlyPackets.clear()
 | 
			
		||||
        earlyReceivedPackets.forEach { processReceivedMeshPacket(it) }
 | 
			
		||||
        earlyReceivedPackets.clear()
 | 
			
		||||
 | 
			
		||||
        offlineSentPackets.forEach { sendMeshPacket(it) }
 | 
			
		||||
        offlineSentPackets.clear()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// Update our model and resend as needed for a MeshPacket we just received from the radio
 | 
			
		||||
    private fun processReceivedMeshPacket(packet: MeshPacket) {
 | 
			
		||||
        val fromNum = packet.from
 | 
			
		||||
| 
						 | 
				
			
			@ -644,6 +662,7 @@ class MeshService : Service(), Logging {
 | 
			
		|||
 | 
			
		||||
    private fun currentSecond() = (System.currentTimeMillis() / 1000).toInt()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// We are reconnecting to a radio, redownload the full state.  This operation might take hundreds of milliseconds
 | 
			
		||||
    private fun reinitFromRadio() {
 | 
			
		||||
        // Read the MyNodeInfo object
 | 
			
		||||
| 
						 | 
				
			
			@ -657,6 +676,8 @@ class MeshService : Service(), Logging {
 | 
			
		|||
 | 
			
		||||
        myNodeInfo = mi
 | 
			
		||||
 | 
			
		||||
        radioConfig = MeshProtos.RadioConfig.parseFrom(connectedRadio.readRadioConfig())
 | 
			
		||||
 | 
			
		||||
        /// Track types of devices and firmware versions in use
 | 
			
		||||
        GeeksvilleApplication.analytics.setUserInfo(
 | 
			
		||||
            DataPair("region", mi.region),
 | 
			
		||||
| 
						 | 
				
			
			@ -743,11 +764,42 @@ class MeshService : Service(), Logging {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private var sleepTimeout: Job? = null
 | 
			
		||||
 | 
			
		||||
    /// Called when we gain/lose connection to our radio
 | 
			
		||||
    private fun onConnectionChanged(c: Boolean) {
 | 
			
		||||
        debug("onConnectionChanged connected=$c")
 | 
			
		||||
        isConnected = c
 | 
			
		||||
        if (c) {
 | 
			
		||||
    private fun onConnectionChanged(c: ConnectionState) {
 | 
			
		||||
        debug("onConnectionChanged=$c")
 | 
			
		||||
 | 
			
		||||
        /// Perform all the steps needed once we start waiting for device sleep to complete
 | 
			
		||||
        fun startDeviceSleep() {
 | 
			
		||||
            // lost radio connection, therefore no need to keep listening to GPS
 | 
			
		||||
            stopLocationRequests()
 | 
			
		||||
 | 
			
		||||
            // Have our timeout fire in the approprate number of seconds
 | 
			
		||||
            sleepTimeout = serviceScope.handledLaunch {
 | 
			
		||||
                try {
 | 
			
		||||
                    // If we have a valid timeout, wait that long (+30 seconds) otherwise, just wait 30 seconds
 | 
			
		||||
                    val timeout = (radioConfig?.preferences?.lsSecs ?: 0) + 30
 | 
			
		||||
 | 
			
		||||
                    debug("Waiting for sleeping device, timeout=$timeout secs")
 | 
			
		||||
                    delay(timeout * 1000L)
 | 
			
		||||
                    warn("Device timeout out, setting disconnected")
 | 
			
		||||
                    onConnectionChanged(ConnectionState.DISCONNECTED)
 | 
			
		||||
                } catch (ex: CancellationException) {
 | 
			
		||||
                    debug("device sleep timeout cancelled")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun startDisconnect() {
 | 
			
		||||
            GeeksvilleApplication.analytics.track(
 | 
			
		||||
                "mesh_disconnect",
 | 
			
		||||
                DataPair("num_nodes", numNodes),
 | 
			
		||||
                DataPair("num_online", numOnlineNodes)
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun startConnect() {
 | 
			
		||||
            // Do our startup init
 | 
			
		||||
            try {
 | 
			
		||||
                reinitFromRadio()
 | 
			
		||||
| 
						 | 
				
			
			@ -771,20 +823,37 @@ class MeshService : Service(), Logging {
 | 
			
		|||
                // It seems that when the ESP32 goes offline it can briefly come back for a 100ms ish which
 | 
			
		||||
                // causes the phone to try and reconnect.  If we fail downloading our initial radio state we don't want to
 | 
			
		||||
                // claim we have a valid connection still
 | 
			
		||||
                isConnected = false;
 | 
			
		||||
                connectionState = ConnectionState.DEVICE_SLEEP
 | 
			
		||||
                startDeviceSleep()
 | 
			
		||||
                throw ex; // Important to rethrow so that we don't tell the app all is well
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            // lost radio connection, therefore no need to keep listening to GPS
 | 
			
		||||
            stopLocationRequests()
 | 
			
		||||
 | 
			
		||||
            GeeksvilleApplication.analytics.track(
 | 
			
		||||
                "mesh_disconnect",
 | 
			
		||||
                DataPair("num_nodes", numNodes),
 | 
			
		||||
                DataPair("num_online", numOnlineNodes)
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Cancel any existing timeouts
 | 
			
		||||
        sleepTimeout?.let {
 | 
			
		||||
            it.cancel()
 | 
			
		||||
            sleepTimeout = null
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        connectionState = c
 | 
			
		||||
        when (c) {
 | 
			
		||||
            ConnectionState.CONNECTED ->
 | 
			
		||||
                startConnect()
 | 
			
		||||
            ConnectionState.DEVICE_SLEEP ->
 | 
			
		||||
                startDeviceSleep()
 | 
			
		||||
            ConnectionState.DISCONNECTED ->
 | 
			
		||||
                startDisconnect()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // broadcast an intent with our new connection state
 | 
			
		||||
        val intent = Intent(ACTION_MESH_CONNECTED)
 | 
			
		||||
        intent.putExtra(
 | 
			
		||||
            EXTRA_CONNECTED,
 | 
			
		||||
            connectionState.toString()
 | 
			
		||||
        )
 | 
			
		||||
        explicitBroadcast(intent)
 | 
			
		||||
 | 
			
		||||
        // Update the android notification in the status bar
 | 
			
		||||
        updateNotification()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -801,12 +870,12 @@ class MeshService : Service(), Logging {
 | 
			
		|||
                when (intent.action) {
 | 
			
		||||
                    RadioInterfaceService.RADIO_CONNECTED_ACTION -> {
 | 
			
		||||
                        try {
 | 
			
		||||
                            onConnectionChanged(intent.getBooleanExtra(EXTRA_CONNECTED, false))
 | 
			
		||||
 | 
			
		||||
                            // forward the connection change message to anyone who is listening to us. but change the action
 | 
			
		||||
                            // to prevent an infinite loop from us receiving our own broadcast. ;-)
 | 
			
		||||
                            intent.action = ACTION_MESH_CONNECTED
 | 
			
		||||
                            explicitBroadcast(intent)
 | 
			
		||||
                            onConnectionChanged(
 | 
			
		||||
                                if (intent.getBooleanExtra(EXTRA_CONNECTED, false))
 | 
			
		||||
                                    ConnectionState.CONNECTED
 | 
			
		||||
                                else
 | 
			
		||||
                                    ConnectionState.DEVICE_SLEEP
 | 
			
		||||
                            )
 | 
			
		||||
                        } catch (ex: RemoteException) {
 | 
			
		||||
                            // This can happen sometimes (especially if the device is slowly dying due to killing power, don't report to crashlytics
 | 
			
		||||
                            warn("Abandoning reconnect attempt, due to errors during init: ${ex.message}")
 | 
			
		||||
| 
						 | 
				
			
			@ -870,6 +939,15 @@ class MeshService : Service(), Logging {
 | 
			
		|||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Send a mesh packet to the radio, if the radio is not currently connected this function will throw NotConnectedException
 | 
			
		||||
     */
 | 
			
		||||
    private fun sendMeshPacket(packet: MeshPacket) {
 | 
			
		||||
        sendToRadio(ToRadio.newBuilder().apply {
 | 
			
		||||
            this.packet = packet
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private val binder = object : IMeshService.Stub() {
 | 
			
		||||
        // Note: bound methods don't get properly exception caught/logged, so do that with a wrapper
 | 
			
		||||
        // per https://blog.classycode.com/dealing-with-exceptions-in-aidl-9ba904c6d63
 | 
			
		||||
| 
						 | 
				
			
			@ -900,9 +978,9 @@ class MeshService : Service(), Logging {
 | 
			
		|||
                connectedRadio.writeOwner(user.toByteArray())
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        override fun sendData(destId: String?, payloadIn: ByteArray, typ: Int) =
 | 
			
		||||
        override fun sendData(destId: String?, payloadIn: ByteArray, typ: Int): Boolean =
 | 
			
		||||
            toRemoteExceptions {
 | 
			
		||||
                info("sendData dest=$destId <- ${payloadIn.size} bytes")
 | 
			
		||||
                info("sendData dest=$destId <- ${payloadIn.size} bytes (connectionState=$connectionState)")
 | 
			
		||||
 | 
			
		||||
                // encapsulate our payload in the proper protobufs and fire it off
 | 
			
		||||
                val packet = buildMeshPacket(destId) {
 | 
			
		||||
| 
						 | 
				
			
			@ -911,24 +989,33 @@ class MeshService : Service(), Logging {
 | 
			
		|||
                        it.payload = ByteString.copyFrom(payloadIn)
 | 
			
		||||
                    }.build()
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                sendToRadio(ToRadio.newBuilder().apply {
 | 
			
		||||
                    this.packet = packet
 | 
			
		||||
                })
 | 
			
		||||
                // If radio is sleeping, queue the packet
 | 
			
		||||
                when (connectionState) {
 | 
			
		||||
                    ConnectionState.DEVICE_SLEEP ->
 | 
			
		||||
                        offlineSentPackets.add(packet)
 | 
			
		||||
                    else ->
 | 
			
		||||
                        sendMeshPacket(packet)
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                GeeksvilleApplication.analytics.track(
 | 
			
		||||
                    "data_send",
 | 
			
		||||
                    DataPair("num_bytes", payloadIn.size),
 | 
			
		||||
                    DataPair("type", typ)
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
                connectionState == ConnectionState.CONNECTED
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        override fun getRadioConfig(): ByteArray = toRemoteExceptions {
 | 
			
		||||
            connectedRadio.readRadioConfig()
 | 
			
		||||
            this@MeshService.radioConfig?.toByteArray() ?: throw RadioNotConnectedException()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        override fun setRadioConfig(payload: ByteArray) = toRemoteExceptions {
 | 
			
		||||
            // Update our device
 | 
			
		||||
            connectedRadio.writeRadioConfig(payload)
 | 
			
		||||
 | 
			
		||||
            // Update our cached copy
 | 
			
		||||
            this@MeshService.radioConfig = MeshProtos.RadioConfig.parseFrom(payload)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        override fun getNodes(): Array<NodeInfo> = toRemoteExceptions {
 | 
			
		||||
| 
						 | 
				
			
			@ -938,10 +1025,10 @@ class MeshService : Service(), Logging {
 | 
			
		|||
            r
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        override fun isConnected(): Boolean = toRemoteExceptions {
 | 
			
		||||
            val r = this@MeshService.isConnected
 | 
			
		||||
            info("in isConnected=$r")
 | 
			
		||||
            r
 | 
			
		||||
        override fun connectionState(): String = toRemoteExceptions {
 | 
			
		||||
            val r = this@MeshService.connectionState
 | 
			
		||||
            info("in connectionState=$r")
 | 
			
		||||
            r.toString()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -301,7 +301,8 @@ class RadioInterfaceService : Service(), Logging {
 | 
			
		|||
        info("Connected to radio!")
 | 
			
		||||
 | 
			
		||||
        if (!hasForcedRefresh) {
 | 
			
		||||
            hasForcedRefresh = true
 | 
			
		||||
            // FIXME - for some reason we need to refresh _everytime_.  It is almost as if we've cached wrong descriptor fieldnums forever
 | 
			
		||||
            // hasForcedRefresh = true
 | 
			
		||||
            forceServiceRefresh()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,10 +4,7 @@ import androidx.compose.Composable
 | 
			
		|||
import androidx.compose.state
 | 
			
		||||
import androidx.ui.core.ContextAmbient
 | 
			
		||||
import androidx.ui.core.Text
 | 
			
		||||
import androidx.ui.layout.Column
 | 
			
		||||
import androidx.ui.layout.Container
 | 
			
		||||
import androidx.ui.layout.LayoutSize
 | 
			
		||||
import androidx.ui.layout.Row
 | 
			
		||||
import androidx.ui.layout.*
 | 
			
		||||
import androidx.ui.material.*
 | 
			
		||||
import androidx.ui.tooling.preview.Preview
 | 
			
		||||
import androidx.ui.unit.dp
 | 
			
		||||
| 
						 | 
				
			
			@ -15,6 +12,7 @@ import com.geeksville.android.Logging
 | 
			
		|||
import com.geeksville.mesh.R
 | 
			
		||||
import com.geeksville.mesh.model.NodeDB
 | 
			
		||||
import com.geeksville.mesh.model.UIState
 | 
			
		||||
import com.geeksville.mesh.service.MeshService
 | 
			
		||||
import com.geeksville.mesh.service.RadioInterfaceService
 | 
			
		||||
import com.geeksville.mesh.service.SoftwareUpdateService
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -36,34 +34,40 @@ fun HomeContent() {
 | 
			
		|||
 | 
			
		||||
    Column {
 | 
			
		||||
        Row {
 | 
			
		||||
            fun connected() = UIState.isConnected.value != MeshService.ConnectionState.DISCONNECTED
 | 
			
		||||
            VectorImage(
 | 
			
		||||
                id = if (UIState.isConnected.value) R.drawable.cloud_on else R.drawable.cloud_off,
 | 
			
		||||
                tint = palette.onBackground // , modifier = LayoutSize(40.dp, 40.dp)
 | 
			
		||||
                id = if (connected()) R.drawable.cloud_on else R.drawable.cloud_off,
 | 
			
		||||
                tint = palette.onBackground,
 | 
			
		||||
                modifier = LayoutPadding(start = 8.dp)
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            if (UIState.isConnected.value) {
 | 
			
		||||
                Column {
 | 
			
		||||
                    Text("Connected")
 | 
			
		||||
            Column {
 | 
			
		||||
 | 
			
		||||
                    if (false) { // hide the firmware update button for now, it is kinda ugly and users don't need it yet
 | 
			
		||||
                        /// Create a software update button
 | 
			
		||||
                        val context = ContextAmbient.current
 | 
			
		||||
                        RadioInterfaceService.getBondedDeviceAddress(context)?.let { macAddress ->
 | 
			
		||||
                            Button(
 | 
			
		||||
                                onClick = {
 | 
			
		||||
                                    SoftwareUpdateService.enqueueWork(
 | 
			
		||||
                                        context,
 | 
			
		||||
                                        SoftwareUpdateService.startUpdateIntent(macAddress)
 | 
			
		||||
                                    )
 | 
			
		||||
                                }
 | 
			
		||||
                            ) {
 | 
			
		||||
                                Text(text = "Update firmware")
 | 
			
		||||
                Text(
 | 
			
		||||
                    when (UIState.isConnected.value) {
 | 
			
		||||
                        MeshService.ConnectionState.CONNECTED -> "Connected"
 | 
			
		||||
                        MeshService.ConnectionState.DISCONNECTED -> "Disconnected"
 | 
			
		||||
                        MeshService.ConnectionState.DEVICE_SLEEP -> "Power Saving"
 | 
			
		||||
                    },
 | 
			
		||||
                    modifier = LayoutPadding(start = 8.dp)
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
                if (false) { // hide the firmware update button for now, it is kinda ugly and users don't need it yet
 | 
			
		||||
                    /// Create a software update button
 | 
			
		||||
                    val context = ContextAmbient.current
 | 
			
		||||
                    RadioInterfaceService.getBondedDeviceAddress(context)?.let { macAddress ->
 | 
			
		||||
                        Button(
 | 
			
		||||
                            onClick = {
 | 
			
		||||
                                SoftwareUpdateService.enqueueWork(
 | 
			
		||||
                                    context,
 | 
			
		||||
                                    SoftwareUpdateService.startUpdateIntent(macAddress)
 | 
			
		||||
                                )
 | 
			
		||||
                            }
 | 
			
		||||
                        ) {
 | 
			
		||||
                            Text(text = "Update firmware")
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                Text("Not Connected")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Ładowanie…
	
		Reference in New Issue