diff --git a/TODO.md b/TODO.md index fcb29cd9..23000fb6 100644 --- a/TODO.md +++ b/TODO.md @@ -12,6 +12,8 @@ MVP features required for first public alpha * test bt boot behavior * have the foreground service's notification show a summary of network status, add (individually maskable) notifications for received texts or new positions * add screenshots and text to play store entry +* make hackaday page +* test using firebase testlab # Medium priority Features for future builds diff --git a/app/src/main/java/com/geeksville/mesh/MainActivity.kt b/app/src/main/java/com/geeksville/mesh/MainActivity.kt index 3df06dea..7d13ab0f 100644 --- a/app/src/main/java/com/geeksville/mesh/MainActivity.kt +++ b/app/src/main/java/com/geeksville/mesh/MainActivity.kt @@ -3,11 +3,13 @@ package com.geeksville.mesh import android.Manifest import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothManager -import android.content.* +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter import android.content.pm.PackageManager import android.os.Build import android.os.Bundle -import android.os.IBinder import android.view.Menu import android.view.MenuItem import android.widget.Toast @@ -16,6 +18,7 @@ import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import androidx.ui.core.setContent import com.geeksville.android.Logging +import com.geeksville.android.ServiceClient import com.geeksville.mesh.model.MessagesState import com.geeksville.mesh.model.NodeDB import com.geeksville.mesh.model.TextMessage @@ -330,33 +333,32 @@ class MainActivity : AppCompatActivity(), Logging, } } - private var isBound = false - private var serviceConnection = object : ServiceConnection { - override fun onServiceConnected(name: ComponentName, service: IBinder) = - exceptionReporter { - val m = IMeshService.Stub.asInterface(service) - UIState.meshService = m + private val mesh = object : ServiceClient({ + com.geeksville.mesh.IMeshService.Stub.asInterface(it) + }) { + override fun onConnected(m: com.geeksville.mesh.IMeshService) { + UIState.meshService = m - // We don't start listening for packets until after we are connected to the service - registerMeshReceiver() + // We don't start listening for packets until after we are connected to the service + registerMeshReceiver() - // We won't receive a notify for the initial state of connection, so we force an update here - onMeshConnectionChanged(m.isConnected) + // We won't receive a notify for the initial state of connection, so we force an update here + onMeshConnectionChanged(m.isConnected) - debug("connected to mesh service, isConnected=${UIState.isConnected.value}") + debug("connected to mesh service, isConnected=${UIState.isConnected.value}") - // Update our nodeinfos based on data from the device - NodeDB.nodes.clear() - NodeDB.nodes.putAll( - m.nodes.map { - it.user?.id!! to it - } - ) - } + // Update our nodeinfos based on data from the device + NodeDB.nodes.clear() + NodeDB.nodes.putAll( + m.nodes.map + { + it.user?.id!! to it + } + ) + } - override fun onServiceDisconnected(name: ComponentName) { - warn("The mesh service has disconnected") + override fun onDisconnected() { unregisterMeshReceiver() UIState.meshService = null } @@ -367,11 +369,9 @@ class MainActivity : AppCompatActivity(), Logging, // we bind using the well known name, to make sure 3rd party apps could also logAssert(UIState.meshService == null) - val intent = MeshService.startService(this) - if (intent != null) { + MeshService.startService(this)?.let { intent -> // ALSO bind so we can use the api - logAssert(bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)) - isBound = true; + mesh.connect(this, intent, Context.BIND_AUTO_CREATE) } } @@ -380,8 +380,7 @@ class MainActivity : AppCompatActivity(), Logging, // it, then now is the time to unregister. // if we never connected, do nothing debug("Unbinding from mesh service!") - if (isBound) - unbindService(serviceConnection) + mesh.close() UIState.meshService = null } diff --git a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt index 8e0af4f8..80ca3b3b 100644 --- a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt +++ b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt @@ -5,7 +5,10 @@ import android.app.Notification import android.app.NotificationChannel import android.app.NotificationManager import android.app.Service -import android.content.* +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter import android.graphics.Color import android.os.Build import android.os.IBinder @@ -14,6 +17,7 @@ import androidx.annotation.RequiresApi import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat.PRIORITY_MIN import com.geeksville.android.Logging +import com.geeksville.android.ServiceClient import com.geeksville.mesh.* import com.geeksville.mesh.MeshProtos.MeshPacket import com.geeksville.mesh.MeshProtos.ToRadio @@ -86,7 +90,9 @@ class MeshService : Service(), Logging { /// A mapping of receiver class name to package name - used for explicit broadcasts private val clientPackages = mutableMapOf() - private var radioService: IRadioInterfaceService? = null + val radio = ServiceClient { + IRadioInterfaceService.Stub.asInterface(it) + } /* see com.geeksville.mesh broadcast intents @@ -225,29 +231,14 @@ class MeshService : Service(), Logging { private fun broadcastNodeChange(info: NodeInfo) { debug("Broadcasting node change $info") val intent = Intent(ACTION_NODE_CHANGE) - - /* - if (info.user == null) - info.user = MeshUser("x", "y", "z") - - if (info.position == null) - info.position = Position(1.5, 1.6, 3) - - */ - + intent.putExtra(EXTRA_NODEINFO, info) explicitBroadcast(intent) } /// Safely access the radio service, if not connected an exception will be thrown private val connectedRadio: IRadioInterfaceService - get() { - val s = radioService - if (s == null || !isConnected) - throw RadioNotConnectedException() - - return s - } + get() = (if (isConnected) 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 @@ -261,19 +252,6 @@ class MeshService : Service(), Logging { return binder } - private val radioConnection = object : ServiceConnection { - override fun onServiceConnected(name: ComponentName?, service: IBinder?) { - val m = IRadioInterfaceService.Stub.asInterface( - service - ) - radioService = m - } - - override fun onServiceDisconnected(name: ComponentName?) { - radioService = null - } - } - @RequiresApi(Build.VERSION_CODES.O) private fun createNotificationChannel(): String { val channelId = "my_service" @@ -347,7 +325,7 @@ class MeshService : Service(), Logging { // We in turn need to use the radiointerface service val intent = Intent(this, RadioInterfaceService::class.java) // intent.action = IMeshService::class.java.name - logAssert(bindService(intent, radioConnection, Context.BIND_AUTO_CREATE)) + radio.connect(this, intent, Context.BIND_AUTO_CREATE) // the rest of our init will happen once we are in radioConnection.onServiceConnected } @@ -356,12 +334,12 @@ class MeshService : Service(), Logging { override fun onDestroy() { info("Destroying mesh service") unregisterReceiver(radioInterfaceReceiver) - unbindService(radioConnection) - radioService = null + radio.close() super.onDestroy() } + /// /// BEGINNING OF MODEL - FIXME, move elsewhere ///