diff --git a/TODO.md b/TODO.md
index d0aab1bce..26774324b 100644
--- a/TODO.md
+++ b/TODO.md
@@ -1,11 +1,13 @@
# High priority
MVP features required for first public alpha
+* start bt receive on boot
* when a text arrives, move that node info card to the bottom on the window - put the text to the left of the card. with a small arrow/distance/shortname
* let the user type texts somewhere
* include a background behind our cloud graphics, so redraws work properly
* show direction and distance on the nodeinfo cards
* show radio config screen, it shows past channels (and the current one)
+* use this for preferences? https://developer.android.com/guide/topics/ui/settings/
* do setOwner every time we connect to the radio, use our settings, radio should ignore if unchanged
* send location data for devices that don't have a GPS - https://developer.android.com/training/location/change-location-settings
* make nodeinfo card not look like ass
diff --git a/app/build.gradle b/app/build.gradle
index 5f87b8687..e7da61068 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -75,7 +75,7 @@ dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.1.0'
- implementation 'androidx.core:core-ktx:1.1.0'
+ implementation 'androidx.core:core-ktx:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'com.google.android.material:material:1.0.0'
testImplementation 'junit:junit:4.12'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index a805bd080..327d85170 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -33,6 +33,9 @@
+
+
+
@@ -108,7 +111,15 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/java/com/geeksville/mesh/MainActivity.kt b/app/src/main/java/com/geeksville/mesh/MainActivity.kt
index c0cf3bff4..4cca184d3 100644
--- a/app/src/main/java/com/geeksville/mesh/MainActivity.kt
+++ b/app/src/main/java/com/geeksville/mesh/MainActivity.kt
@@ -6,9 +6,7 @@ import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothManager
import android.content.*
import android.content.pm.PackageManager
-import android.os.Build
import android.os.Bundle
-import android.os.Debug
import android.os.IBinder
import android.provider.ContactsContract
import android.provider.ContactsContract.CommonDataKinds.Phone
@@ -25,7 +23,6 @@ import com.geeksville.mesh.ui.MeshApp
import com.geeksville.mesh.ui.TextMessage
import com.geeksville.mesh.ui.UIState
import com.geeksville.util.exceptionReporter
-import com.google.firebase.crashlytics.FirebaseCrashlytics
import java.nio.charset.Charset
import java.util.*
@@ -157,11 +154,6 @@ class MainActivity : AppCompatActivity(), Logging,
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- // We default to off in the manifest, FIXME turn on only if user approves
- // leave off when running in the debugger
- if (false && !Debug.isDebuggerConnected())
- FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(true)
-
setContent {
MeshApp()
}
@@ -177,6 +169,11 @@ class MainActivity : AppCompatActivity(), Logging,
Toast.makeText(this, "Error - this app requires bluetooth", Toast.LENGTH_LONG).show()
}
+ /* Do this better FIXME */
+ val usetbeam = false
+ val address = if (usetbeam) "B4:E6:2D:EA:32:B7" else "24:6F:28:96:C9:2A"
+ RadioInterfaceService.setBondedDeviceAddress(this, address)
+
requestPermission()
}
@@ -292,26 +289,12 @@ class MainActivity : AppCompatActivity(), Logging,
// we bind using the well known name, to make sure 3rd party apps could also
logAssert(meshService == null)
- // bind to our service using the same mechanism an external client would use (for testing coverage)
- // The following would work for us, but not external users
- //val intent = Intent(this, MeshService::class.java)
- //intent.action = IMeshService::class.java.name
- val intent = Intent()
- intent.setClassName("com.geeksville.mesh", "com.geeksville.mesh.service.MeshService")
-
- // Before binding we want to explicitly create - so the service stays alive forever (so it can keep
- // listening for the bluetooth packets arriving from the radio. And when they arrive forward them
- // to Signal or whatever.
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- startForegroundService(intent)
- } else {
- startService(intent)
+ val intent = MeshService.startService(this)
+ if (intent != null) {
+ // ALSO bind so we can use the api
+ logAssert(bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE))
+ isBound = true;
}
-
- // ALSO bind so we can use the api
- logAssert(bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE))
- isBound = true;
}
private fun unbindMeshService() {
diff --git a/app/src/main/java/com/geeksville/mesh/MeshUtilApplication.kt b/app/src/main/java/com/geeksville/mesh/MeshUtilApplication.kt
index 36fff96a5..264ebbb80 100644
--- a/app/src/main/java/com/geeksville/mesh/MeshUtilApplication.kt
+++ b/app/src/main/java/com/geeksville/mesh/MeshUtilApplication.kt
@@ -1,7 +1,19 @@
package com.geeksville.mesh
+import android.os.Debug
import com.geeksville.android.GeeksvilleApplication
+import com.google.firebase.crashlytics.FirebaseCrashlytics
class MeshUtilApplication : GeeksvilleApplication(null, "58e72ccc361883ea502510baa46580e3") {
+
+ override fun onCreate() {
+ super.onCreate()
+
+ // We default to off in the manifest, FIXME turn on only if user approves
+ // leave off when running in the debugger
+ if (false && !Debug.isDebuggerConnected())
+ FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(true)
+
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/geeksville/mesh/service/BootCompleteReceiver.kt b/app/src/main/java/com/geeksville/mesh/service/BootCompleteReceiver.kt
index 7541c857b..0230e18c3 100644
--- a/app/src/main/java/com/geeksville/mesh/service/BootCompleteReceiver.kt
+++ b/app/src/main/java/com/geeksville/mesh/service/BootCompleteReceiver.kt
@@ -3,12 +3,13 @@ package com.geeksville.mesh.service
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
+import com.geeksville.android.Logging
-class BootCompleteReceiver : BroadcastReceiver() {
+class BootCompleteReceiver : BroadcastReceiver(), Logging {
override fun onReceive(mContext: Context, intent: Intent) {
- if (intent.action == Intent.ACTION_BOOT_COMPLETED) {
- // FIXME - start listening for bluetooth messages from our device
- }
+ // FIXME - start listening for bluetooth messages from our device
+ info("Received boot complete announcement, starting mesh service")
+ MeshService.startService(mContext)
}
}
\ No newline at end of file
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 026e72d40..32bd6cd0b 100644
--- a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt
+++ b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt
@@ -33,7 +33,7 @@ class RadioNotConnectedException() : Exception("Can't find radio")
*/
class MeshService : Service(), Logging {
- companion object {
+ companion object : Logging {
/// Intents broadcast by MeshService
const val ACTION_RECEIVED_DATA = "$prefix.RECEIVED_DATA"
@@ -50,7 +50,38 @@ class MeshService : Service(), Logging {
/// If the radio hasn't yet joined a mesh (i.e. no nodenum assigned)
private const val NODE_NUM_NO_MESH = -1
+ /// Helper function to start running our service, returns the intent used to reach it
+ /// or null if the service could not be started (no bluetooth or no bonded device set)
+ fun startService(context: Context): Intent? {
+ if (RadioInterfaceService.getBondedDeviceAddress(context) == null) {
+ warn("No mesh radio is bonded, not starting service")
+ return null
+ } else {
+ // bind to our service using the same mechanism an external client would use (for testing coverage)
+ // The following would work for us, but not external users
+ //val intent = Intent(this, MeshService::class.java)
+ //intent.action = IMeshService::class.java.name
+ val intent = Intent()
+ intent.setClassName(
+ "com.geeksville.mesh",
+ "com.geeksville.mesh.service.MeshService"
+ )
+ // Before binding we want to explicitly create - so the service stays alive forever (so it can keep
+ // listening for the bluetooth packets arriving from the radio. And when they arrive forward them
+ // to Signal or whatever.
+
+ logAssert(
+ (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ context.startForegroundService(intent)
+ } else {
+ context.startService(intent)
+ }) != null
+ )
+
+ return intent
+ }
+ }
}
/// A mapping of receiver class name to package name - used for explicit broadcasts
diff --git a/app/src/main/java/com/geeksville/mesh/service/RadioInterfaceService.kt b/app/src/main/java/com/geeksville/mesh/service/RadioInterfaceService.kt
index 4c50ec2b5..718ac932e 100644
--- a/app/src/main/java/com/geeksville/mesh/service/RadioInterfaceService.kt
+++ b/app/src/main/java/com/geeksville/mesh/service/RadioInterfaceService.kt
@@ -7,6 +7,7 @@ import android.bluetooth.BluetoothManager
import android.content.Context
import android.content.Intent
import android.os.IBinder
+import androidx.core.content.edit
import com.geeksville.android.BinaryLogFile
import com.geeksville.android.Logging
import com.geeksville.concurrent.DeferredExecution
@@ -126,6 +127,15 @@ class RadioInterfaceService : Service(), Logging {
intent.putExtra(EXTRA_PAYLOAD, payload)
context.sendBroadcast(intent)
}
+
+ private fun getPrefs(context: Context) =
+ context.getSharedPreferences("radio-prefs", Context.MODE_PRIVATE)
+
+ /// Return the device we are configured to use, or null for none
+ fun getBondedDeviceAddress(context: Context) = getPrefs(context).getString("devAddr", null)
+
+ fun setBondedDeviceAddress(context: Context, addr: String) =
+ getPrefs(context).edit(commit = true) { putString("devAddr", addr) }
}
private val bluetoothAdapter: BluetoothAdapter? by lazy(LazyThreadSafetyMode.NONE) {
@@ -244,30 +254,32 @@ class RadioInterfaceService : Service(), Logging {
// FIXME, let user GUI select which device we are talking to
- // Note: this call does no comms, it just creates the device object (even if the
- // device is off/not connected)
- val usetbeam = false
- val address = if (usetbeam) "B4:E6:2D:EA:32:B7" else "24:6F:28:96:C9:2A"
+ val address = getBondedDeviceAddress(this)
+ if (address == null)
+ error("No bonded mesh radio, can't create service")
+ else {
+ // Note: this call does no comms, it just creates the device object (even if the
+ // device is off/not connected)
+ val device = bluetoothAdapter?.getRemoteDevice(address)
+ if (device != null) {
+ info("Creating radio interface service. device=$address")
- val device = bluetoothAdapter?.getRemoteDevice(address)
- if (device != null) {
- info("Creating radio interface service. device=$address")
+ // Note this constructor also does no comm
+ val s = SafeBluetooth(this, device)
+ safe = s
- // Note this constructor also does no comm
- val s = SafeBluetooth(this, device)
- safe = s
+ // FIXME, pass in true for autoconnect - so we will autoconnect whenever the radio
+ // comes in range (even if we made this connect call long ago when we got powered on)
+ // see https://stackoverflow.com/questions/40156699/which-correct-flag-of-autoconnect-in-connectgatt-of-ble for
+ // more info
+ s.asyncConnect(true, ::onConnect, ::onDisconnect)
+ } else {
+ error("Bluetooth adapter not found, assuming running on the emulator!")
+ }
- // FIXME, pass in true for autoconnect - so we will autoconnect whenever the radio
- // comes in range (even if we made this connect call long ago when we got powered on)
- // see https://stackoverflow.com/questions/40156699/which-correct-flag-of-autoconnect-in-connectgatt-of-ble for
- // more info
- s.asyncConnect(true, ::onConnect, ::onDisconnect)
- } else {
- error("Bluetooth adapter not found, assuming running on the emulator!")
+ if (logSends)
+ sentPacketsLog = BinaryLogFile(this, "sent_log.pb")
}
-
- if (logSends)
- sentPacketsLog = BinaryLogFile(this, "sent_log.pb")
}
override fun onDestroy() {